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() 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 }
}

All six comparison operators are supported via the GuardOp type:

Operator Meaning Example
lt Less than guard('lt', 50)
lte Less than or equal guard('lte', 50)
gt Greater than guard('gt', 100)
gte Greater than or equal guard('gte', 18)
eq Equal guard('eq', 42)
ne Not equal guard('ne', 0)

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

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

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,
}),
);

Use a plain string for variables without constraints:

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

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}`);
}
}

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"
}
Relation Meaning Diagram
before A ends before B starts AAA...BBB
after A starts after B ends BBB...AAA
meets A ends exactly when B starts AAABBB
met_by A starts exactly when B ends BBBAAA
overlaps A starts before B and ends during B AABB with overlap
overlapped_by B starts before A and ends during A BBAA with overlap
during A is contained within B B[A]B
contains B is contained within A A[B]A
starts A starts at the same time as B, ends earlier [AB]B
started_by B starts at the same time as A, ends earlier [BA]A
finishes A ends at the same time as B, starts later B[BA]
finished_by B ends at the same time as A, starts later A[AB]
equals A and B have the same start and end [AB]

Using Allen constraints with backward chaining

Section titled “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),
],
});
// 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,
});

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],
});