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 functionconst objective = LP.maximize({ chairs: 3, tables: 5 });// orconst minObjective = LP.minimize({ cost: 2, waste: 1 });
// Constraintsconst woodConstraint = LP.constraint({ chairs: 1, tables: 3 }, '<=', 12);const laborConstraint = LP.constraint({ chairs: 2, tables: 1 }, '<=', 8, 'labor');
// Variable boundsconst bounds = LP.nonNegative('chairs', 'tables');// { chairs: { min: 0 }, tables: { min: 0 } }
// Custom boundsconst customBounds = { x: LP.bounds(0, 100), // 0 <= x <= 100 y: LP.bounds(0), // y >= 0 z: LP.bounds(undefined, 50), // z <= 50};Builder reference
| Method | Description | Returns |
|---|---|---|
LP.maximize(coefficients) | Maximize a linear expression | ObjectiveFunction |
LP.minimize(coefficients) | Minimize a linear expression | ObjectiveFunction |
LP.constraint(coefficients, op, rhs, label?) | Create a linear constraint | LinearConstraint |
LP.nonNegative(...vars) | Create non-negativity bounds for variables | Record<string, VariableBounds> |
LP.bounds(min?, max?) | Create custom variable bounds | VariableBounds |
Constraint operators
| Operator | Meaning |
|---|---|
'<=' | 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 LPconsole.log(result.generatedProblem);
// See the terms that were usedconsole.log(result.discoveredTerms);How it works
- Resolves the sort — finds the sort by name in the tenant’s sort list
- Queries all terms — retrieves all terms of that sort
- Extracts coefficients — reads the named features from each term to build the objective and constraint coefficient maps
- Builds the LP — constructs a
LinearProgramDefinitionfrom the extracted data - 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:
- Variables become logic variables (
?chairs,?tables) - Coefficients become
real_times_constraintantecedents (optimized away when coefficient is 1) - Linear expressions are chained with
real_plus_constraintnodes - Inequality constraints become
real_le_constraint,real_ge_constraint, orreal_eq_constraint - Bounds become
real_ge_constraint,real_le_constraint, orreal_between_constraint - The objective becomes a
maximize_objectiveorminimize_objective - A
real_labeling_constrainttriggers 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.