Skip to content

Constrained Variables

Variables in inference goals and rules can carry constraints that restrict their possible values. The SDK provides the guard() builder for arithmetic constraints and the allen() builder for temporal interval relations.

The guard() builder

The guard() function creates a constraint as a TermInputDto representing a guard sort with op and right features.

import { guard, psi, constrained } from '@kortexya/reasoninglayer';
// Find employees with salary > 100000
const goal = psi('employee', {
name: '?Name',
salary: constrained('?Salary', guard('gt', 100000)),
});
const result = await client.inference.backwardChain({ goal });

The guard() call produces this JSON:

{
"sortName": "guard_constraint",
"features": { "op": "gt", "right": 100000 }
}

Guard operators

All six comparison operators are supported via the GuardOp type:

OperatorMeaningExample
ltLess thanguard('lt', 50)
lteLess than or equalguard('lte', 50)
gtGreater thanguard('gt', 100)
gteGreater than or equalguard('gte', 18)
eqEqualguard('eq', 42)
neNot equalguard('ne', 0)

The right operand can be a number, string, or boolean:

// Numeric constraints
guard('gt', 100); // > 100
guard('lte', 1000.50); // <= 1000.50
// String constraints
guard('eq', 'active'); // == "active"
guard('ne', 'deleted'); // != "deleted"
// Boolean constraints
guard('eq', true); // == true

Constrained variables

Use constrained() to attach a constraint to a variable. The first argument is the variable name, the second is the constraint as a TermInputDto.

// Variable with a guard constraint
const salary = constrained('?Salary', guard('gt', 50000));
// Variable with a custom constraint sort
const rating = constrained(
'?Rating',
psi('range_check', {
min: 1,
max: 5,
}),
);

Unconstrained variables

Use a plain string for variables without constraints:

const name = '?Name'; // matches anything

Combining constraints in a goal

Build complex query goals with multiple constrained variables:

const goal = psi('employee', {
name: '?Name',
department: 'engineering',
salary: constrained('?Salary', guard('gte', 80000)),
age: constrained('?Age', guard('lt', 40)),
active: true,
});
const result = await client.inference.backwardChain({
goal,
maxSolutions: 20,
});
for (const solution of result.solutions) {
for (const binding of solution.substitution.bindings) {
console.log(`${binding.variableName} = ${binding.boundToDisplay}`);
}
}

Allen temporal relations

The allen() builder creates temporal interval constraints based on Allen’s interval algebra. This is used for reasoning about time relationships between events.

import { allen } from '@kortexya/reasoninglayer';
const constraint = allen('before', 'event_a_time', 'event-b-term-uuid');

This produces:

{
"type": "Allen",
"intervalA": "event_a_time",
"intervalBTermId": "event-b-term-uuid",
"relation": "before"
}

The 13 Allen relations

RelationMeaningDiagram
beforeA ends before B startsAAA...BBB
afterA starts after B endsBBB...AAA
meetsA ends exactly when B startsAAABBB
met_byA starts exactly when B endsBBBAAA
overlapsA starts before B and ends during BAABB with overlap
overlapped_byB starts before A and ends during ABBAA with overlap
duringA is contained within BB[A]B
containsB is contained within AA[B]A
startsA starts at the same time as B, ends earlier[AB]B
started_byB starts at the same time as A, ends earlier[BA]A
finishesA ends at the same time as B, starts laterB[BA]
finished_byB ends at the same time as A, starts laterA[AB]
equalsA and B have the same start and end[AB]

Using Allen constraints with backward chaining

Allen constraints are passed in the constraints field of a backward chain request:

const result = await client.inference.backwardChain({
goal: psi('schedule_conflict', {
event: '?Event',
}),
constraints: [
allen('overlaps', 'meeting_time', meetingTermId),
allen('during', 'lunch_time', workdayTermId),
],
});

Practical example: event scheduling

// Find events that must happen before a deadline
const result = await client.inference.backwardChain({
goal: psi('task', {
name: '?TaskName',
due: '?Due',
}),
constraints: [
allen('before', '?Due', deadlineTermId),
],
maxSolutions: 50,
});

Other constraint types

The ConstraintInputDto type also supports equality and disequality constraints, which can be constructed as plain objects:

// Equality constraint: two variables must be the same
const equality = { type: 'Equality' as const, var1: '?X', var2: '?Y' };
// Disequality constraint: two variables must differ
const disequality = { type: 'Disequality' as const, var1: '?A', var2: '?B' };
const result = await client.inference.backwardChain({
goal: psi('pair', {
first: '?X',
second: '?Y',
}),
constraints: [disequality],
});