Skip to content

Optimization

The Optimization client uses the backward chaining engine’s embedded CLP(Q) simplex solver to solve linear programs. Unlike standalone solvers, optimization here is tightly integrated with the knowledge base — you can formulate problems directly from existing sorts and terms.

LP builder

The LP namespace provides builder functions for constructing linear program definitions.

import { LP } from '@kortexya/reasoninglayer';
// Objective function
const objective = LP.maximize({ chairs: 3, tables: 5 });
// or
const minObjective = LP.minimize({ cost: 2, waste: 1 });
// Constraints
const woodConstraint = LP.constraint({ chairs: 1, tables: 3 }, '<=', 12);
const laborConstraint = LP.constraint({ chairs: 2, tables: 1 }, '<=', 8, 'labor');
// Variable bounds
const bounds = LP.nonNegative('chairs', 'tables');
// { chairs: { min: 0 }, tables: { min: 0 } }
// Custom bounds
const customBounds = {
x: LP.bounds(0, 100), // 0 <= x <= 100
y: LP.bounds(0), // y >= 0
z: LP.bounds(undefined, 50), // z <= 50
};

Builder reference

MethodDescriptionReturns
LP.maximize(coefficients)Maximize a linear expressionObjectiveFunction
LP.minimize(coefficients)Minimize a linear expressionObjectiveFunction
LP.constraint(coefficients, op, rhs, label?)Create a linear constraintLinearConstraint
LP.nonNegative(...vars)Create non-negativity bounds for variablesRecord<string, VariableBounds>
LP.bounds(min?, max?)Create custom variable boundsVariableBounds

Constraint operators

OperatorMeaning
'<='Less than or equal
'>='Greater than or equal
'='Equality

Solving an LP directly

Use client.optimize.solve() with a LinearProgramDefinition:

import { LP } from '@kortexya/reasoninglayer';
const result = await client.optimize.solve({
objective: LP.maximize({ chairs: 3, tables: 5 }),
constraints: [
LP.constraint({ chairs: 1, tables: 3 }, '<=', 12, 'wood'),
LP.constraint({ chairs: 2, tables: 1 }, '<=', 8, 'labor'),
],
bounds: LP.nonNegative('chairs', 'tables'),
});
if (result.status === 'optimal') {
console.log(result.variables); // { chairs: 2.4, tables: 3.2 }
console.log(result.objectiveValue); // 23.2
console.log(result.solveTimeMs); // solve time in milliseconds
} else {
// result.status === 'infeasible'
console.log('No feasible solution exists');
}

Solver options

Pass a second argument to configure the solver:

const result = await client.optimize.solve(problem, {
timeoutMs: 60000, // solver timeout (default: 30000)
maxDepth: 100, // backward chaining depth limit (default: 50)
cleanup: false, // keep temporary sort and rule (default: true)
});

KB-driven optimization

The real power is formulating LPs from existing knowledge base data. Instead of specifying coefficients manually, fromKnowledgeBase() queries your sorts and terms to build the problem automatically.

Example: product mix optimization

Given products stored as terms with features like profit, wood_cost, and labor_cost:

// Products already exist in the KB as terms of sort "product"
// Each product has features: name, profit, wood_cost, labor_cost
const result = await client.optimize.fromKnowledgeBase({
variables: {
sort: 'product', // which sort to query
nameFeature: 'name', // feature used as variable name
},
objective: {
direction: 'maximize',
coefficientFeature: 'profit', // feature used as objective coefficient
},
resourceConstraints: [
{ costFeature: 'wood_cost', capacity: 12, label: 'wood' },
{ costFeature: 'labor_cost', capacity: 8, label: 'labor' },
],
nonNegative: true,
});
if (result.result.status === 'optimal') {
console.log(result.result.variables); // { chair: 2.4, table: 3.2 }
console.log(result.result.objectiveValue);
}
// Inspect the auto-generated LP
console.log(result.generatedProblem);
// See the terms that were used
console.log(result.discoveredTerms);

How it works

  1. Resolves the sort — finds the sort by name in the tenant’s sort list
  2. Queries all terms — retrieves all terms of that sort
  3. Extracts coefficients — reads the named features from each term to build the objective and constraint coefficient maps
  4. Builds the LP — constructs a LinearProgramDefinition from the extracted data
  5. Solves — passes the problem to solve() and returns the result plus the generated problem for inspection

Additional constraints

You can add extra constraints beyond what’s derived from the KB:

const result = await client.optimize.fromKnowledgeBase({
variables: { sort: 'product', nameFeature: 'name' },
objective: { direction: 'maximize', coefficientFeature: 'profit' },
resourceConstraints: [
{ costFeature: 'wood_cost', capacity: 12 },
],
additionalConstraints: [
LP.constraint({ chair: 1, table: 1 }, '>=', 2, 'minimum_production'),
],
nonNegative: true,
});

Custom constraint operators

Resource constraints default to <= but you can specify the operator:

resourceConstraints: [
{ costFeature: 'wood_cost', capacity: 12, op: '<=' }, // default
{ costFeature: 'min_demand', capacity: 5, op: '>=' }, // minimum demand
],

Result types

The OptimizationResult is a discriminated union:

type OptimizationResult =
| { status: 'optimal'; variables: Record<string, number>; objectiveValue: number; solveTimeMs: number }
| { status: 'infeasible'; solveTimeMs: number };

Always check result.status before accessing variables or objectiveValue.

How it works internally

The optimization client compiles LP problems into the engine’s native constraint sort vocabulary:

  1. Variables become logic variables (?chairs, ?tables)
  2. Coefficients become real_times_constraint antecedents (optimized away when coefficient is 1)
  3. Linear expressions are chained with real_plus_constraint nodes
  4. Inequality constraints become real_le_constraint, real_ge_constraint, or real_eq_constraint
  5. Bounds become real_ge_constraint, real_le_constraint, or real_between_constraint
  6. The objective becomes a maximize_objective or minimize_objective
  7. A real_labeling_constraint triggers the CLP(Q) simplex solver

The compiled rule is submitted via addRule, then backwardChain triggers the solver. Solutions come back as variable bindings with exact rational arithmetic (Rational64), converted to JavaScript numbers.

Users never need to interact with the constraint sort vocabulary directly — the LP builder and OptimizeClient handle everything.