Skip to main content

Class: Model

Remarks

Model captures the problem to be solved. It contains variables, constraints and objective function.

To create an optimization model, you must first create a Model object. Then you can use the methods of the Model to create variables (e.g. Model.intervalVar), the objective function (Model.minimize or Model.maximize) and constraints (e.g. Model.noOverlap). Note that a boolean expression becomes a constraint only by passing it to Model.enforce; otherwise, it is not enforced.

To solve a model, pass it to function Model.solve or to Solver class.

Available modeling elements

Variables

Interval variables can be created by function Model.intervalVar, integer variables by function Model.intVar.

Basic integer expressions

  • Model.start: start of an interval variable (optional integer expression).
  • Model.end: end of an interval variable (optional integer expression).
  • Model.length: length of an interval variable (optional integer expression).
  • Model.guard: replaces absent value by a constant.

Integer arithmetics

Comparison operators for integer expressions

Boolean operators

Functions returning BoolExpr

Basic constraints on interval variables

Disjunction (noOverlap)

  • Model.sequenceVar: sequence variable over a set of interval variables.
  • Model.noOverlap: constraints a set of interval variables to not overlap (possibly with transition times).
  • Model.position: returns the position of an interval variable in a sequence.

Basic cumulative expressions

Combining cumulative expressions

Constraints on cumulative expressions

Objective

Example

Our goal is to schedule a set of tasks such that it is finished as soon as possible (i.e., the makespan is minimized). Each task has a fixed duration, and cannot be interrupted. Moreover, each task needs a certain number of workers to be executed, and the total number of workers is limited. The input data are generated randomly.

import * as CP from '@scheduleopt/optalcp';

// Constants for random problem generation:
const nbTasks = 100;
const nbWorkers = 5;
const maxDuration = 100;

// Start by creating the model:
let model = new CP.Model();

// For each task we will have an interval variable and a cumulative expression:
let tasks : CP.IntervalVar[] = [];
let workerUsage: CP.CumulExpr[] = [];

// Loop over the tasks:
for (let i = 0; i < nbTasks; i++) {
// Generate random task length:
const taskLength = 1 + Math.floor(Math.random() * (maxDuration - 1));
// Create the interval variable for the task:
let task = model.intervalVar({ name: "Task" + (i + 1), length: taskLength });
// And store it in the array:
tasks.push(task);
// Generate random number of workers needed for the task:
const workersNeeded = 1 + Math.floor(Math.random() * (nbWorkers - 1));
// Create the pulse that increases the number of workers used during the task:
workerUsage.push(task.pulse(workersNeeded));
}

// Limit the sum of the pulses to the number of workers available:
model.sum(workerUsage).le(nbWorkers);
// From an array of tasks, create an array of their ends:
let ends = tasks.map(t => t.end());
// And minimize the maximum of the ends:
model.max(ends).minimize();

try {
// Solve the model with the provided parameters:
let result = await model.solve({
timeLimit: 3, // Stop after 3 seconds
nbWorkers: 4, // Use for CPU threads
});

if (result.nbSolutions == 0)
console.log("No solution found.");
else {
const solution = result.solution!;
// Note that in the preview version of the solver, the variable values in
// the solution are masked, i.e. they are all *absent* (`null` in JavaScript).
// Objective value is not masked though.
console.log("Solution found with makespan " + solution.getObjective());
for (let task of tasks) {
let start = solution.getStart(task);
if (start !== null)
console.log("Task " + task.name + " starts at " + start);
else
console.log("Task " + task.name + " is absent (not scheduled).")
}
}

} catch (e) {
// In case of error, model.solve returns a rejected promise.
// Therefore, "await model.solve" throws an exception.
console.log("Error: " + (e as Error).message);
}

See

Constructors

new Model()

new Model(name?: string): Model

Creates an empty optimization model.

Parameters

ParameterTypeDescription
name?stringOptional name for the model

Returns

Model

Remarks

Creates an empty model with no variables, constraints, or objective.

The optional name parameter can be used to identify the model in logs, debugging output, and benchmarking reports. When not specified, the model remains unnamed.

After creating a model, use its methods to define:

import * as CP from "optalcp";

// Create an unnamed model
const model = new CP.Model();

// Create a named model (useful for debugging)
const namedModel = new CP.Model("JobShop");

// Add variables and constraints
const task = model.intervalVar({ length: 10, name: "task" });
model.minimize(task.end());

// Solve
const result = await model.solve();

See

Solving

solve()

solve(parameters?: Parameters, warmStart?: Solution): Promise<SolveResult>

Solves the model and returns the result.

Parameters

ParameterTypeDescription
parameters?ParametersThe parameters for solving
warmStart?SolutionThe solution to start with

Returns

Promise<SolveResult>

The result of the solve.

Remarks

Solves the model using the OptalCP solver and returns the result. This is the main entry point for solving constraint programming models.

The solver searches for solutions that satisfy all constraints in the model. If an objective was specified (using Model.minimize or Model.maximize), the solver searches for optimal or near-optimal solutions within the given time limit.

The returned SolveResult contains:

  • solution - The best solution found, or None if no solution was found. Use this to query variable values via methods like get_start(), get_end(), and get_value().
  • objective - The objective value of the best solution (if an objective was specified).
  • nb_solutions - The total number of solutions found during the search.
  • proof - Whether the solver proved optimality or infeasibility.
  • duration - The total time spent solving.
  • Statistics like nb_branches, nb_fails, and nb_restarts.

When an error occurs (e.g., invalid model, solver not found), the function raises an exception.

Parameters

Solver behavior can be controlled via the parameters argument. Common parameters include:

  • timeLimit - Maximum solving time in seconds.
  • solutionLimit - Stop after finding this many solutions.
  • nbWorkers - Number of parallel threads to use.
  • searchType - Search strategy ("LNS", "FDS", etc.).

See Parameters for the complete list.

Warm start

If the warm_start parameter is specified, the solver will start with the given solution. The solution must be compatible with the model; otherwise, an error will be raised. The solver will take advantage of the solution to speed up the search: it will search only for better solutions (if it is a minimization or maximization problem). The solver may also try to improve the provided solution by Large Neighborhood Search.

Advanced usage

This is a simple blocking function for basic usage. For advanced features like event callbacks, progress monitoring, or async support, use the Solver class instead.

This method works seamlessly in both regular Python scripts and Jupyter notebooks. In Jupyter (where an event loop is already running), it automatically handles nested event loops.

import * as CP from "optalcp";

const model = new CP.Model();
const x = model.intervalVar({ length: 10, name: "task_x" });
const y = model.intervalVar({ length: 20, name: "task_y" });
x.endBeforeStart(y);
model.minimize(y.end());

// Basic solve
let result = await model.solve();
console.log(`Objective: ${result.objective}`);

// Solve with parameters
const params = { timeLimit: 60, searchType: "LNS" };
result = await model.solve(params);

// Solve with warm start
if (result.solution) {
const result2 = await model.solve(params, result.solution);
}

See

Model exporting

toJS()

toJS(parameters?: Parameters, warmStart?: Solution): Promise<undefined | string>

Converts the model to equivalent JavaScript code.

Parameters

ParameterTypeDescription
parameters?ParametersOptional solver parameters (included in generated code)
warmStart?SolutionOptional initial solution to include

Returns

Promise<undefined | string>

JavaScript code representing the model.

Remarks

The output is human-readable, executable with Node.js, and can be stored in a file. It is meant as a way to export a model to a format that is executable, human-readable, editable, and independent of other libraries.

This feature is experimental and the result is not guaranteed to be valid in all cases.

import * as CP from "optalcp";
import * as fs from "fs";

const model = new CP.Model();
const x = model.intervalVar({ length: 10, name: "task_x" });
const y = model.intervalVar({ length: 20, name: "task_y" });
x.endBeforeStart(y);
model.minimize(y.end());

// Convert to JavaScript code
const jsCode = model.toJS();
console.log(jsCode);

// Save to file
fs.writeFileSync("model.js", jsCode);

See


toJSON()

toJSON(parameters?: Parameters, warmStart?: Solution): string

Exports the model to JSON format.

Parameters

ParameterTypeDescription
parameters?ParametersOptional solver parameters to include
warmStart?SolutionOptional initial solution to include

Returns

string

A string containing the model in JSON format.

Remarks

The result can be stored in a file for later use. The model can be converted back from JSON format using Model.fromJSON.

import * as CP from "optalcp";
import * as fs from "fs";

const model = new CP.Model();
const x = model.intervalVar({ length: 10, name: "task_x" });
const y = model.intervalVar({ length: 20, name: "task_y" });
x.endBeforeStart(y);
model.minimize(y.end());

// Export to JSON
const jsonStr = model.toJSON();

// Save to file
fs.writeFileSync("model.json", jsonStr);

// Later, load from JSON
const { model: model2, parameters: params2, warmStart: warmStart2 } =
CP.Model.fromJSON(jsonStr);

See


toText()

toText(parameters?: Parameters, warmStart?: Solution): Promise<undefined | string>

Converts the model to text format similar to IBM CP Optimizer file format.

Parameters

ParameterTypeDescription
parameters?ParametersOptional solver parameters (mostly unused)
warmStart?SolutionOptional initial solution to include

Returns

Promise<undefined | string>

Text representation of the model.

Remarks

The output is human-readable and can be stored in a file. Unlike JSON format, there is no way to convert the text format back into a Model.

The result is so similar to the file format used by IBM CP Optimizer that, under some circumstances, the result can be used as an input file for CP Optimizer. However, some differences between OptalCP and CP Optimizer make it impossible to guarantee the result is always valid for CP Optimizer.

Known issues:

  • OptalCP supports optional integer expressions, while CP Optimizer does not. If the model contains optional integer expressions, the result will not be valid for CP Optimizer or may be badly interpreted. For example, to get a valid CP Optimizer file, don't use interval.start(), use interval.start_or(default) instead.
  • For the same reason, prefer precedence constraints such as end_before_start() over model.enforce(x.end() <= y.start()).
  • Negative heights in cumulative expressions (e.g., in step_at_start()) are not supported by CP Optimizer.
import * as CP from "optalcp";
import * as fs from "fs";

const model = new CP.Model();
const x = model.intervalVar({ length: 10, name: "task_x" });
const y = model.intervalVar({ length: 20, name: "task_y" });
x.endBeforeStart(y);
model.minimize(y.end());

// Convert to text format
const text = model.toText();
console.log(text);

// Save to file
fs.writeFileSync("model.txt", text);

See


fromJSON()

static fromJSON(jsonStr: string): { model: Model; parameters: Parameters; warmStart: Solution; }

Creates a model from JSON format.

Parameters

ParameterTypeDescription
jsonStrstringA string containing the model in JSON format

Returns

{ model: Model; parameters: Parameters; warmStart: Solution; }

A tuple containing the model, optional parameters, and optional warm start solution.

model

model: Model

parameters?

optional parameters: Parameters

warmStart?

optional warmStart: Solution

Remarks

Creates a new Model instance from a JSON string that was previously exported using Model.toJSON.

The method returns a tuple with three elements:

  1. The reconstructed Model
  2. Parameters (if they were included in the JSON), or None
  3. Warm start Solution (if it was included in the JSON), or None

Variables in the new model can be accessed using methods like Model.getIntervalVars, Model.getIntVars, etc.

import * as CP from "optalcp";
import * as fs from "fs";

// Create and export a model
const model = new CP.Model();
const x = model.intervalVar({ length: 10, name: "task_x" });
model.minimize(x.end());

const params: CP.Parameters = { timeLimit: 60 };
const jsonStr = model.toJSON(params);

// Save to file
fs.writeFileSync("model.json", jsonStr);

// Later, load from file
const loadedJson = fs.readFileSync("model.json", "utf-8");

// Restore model (returns Model directly in TypeScript)
const model2 = CP.Model.fromJSON(loadedJson);

// Access variables
const intervalVars = model2.getIntervalVars();
console.log(`Loaded model with ${intervalVars.length} interval variables`);

// Solve with restored parameters
const result = await model2.solve(params);

See

Model.toJSON to export to JSON.

Other

name

Get Signature

get name(): undefined | string

The name of the model.

Remarks

The name is optional and primarily useful for distinguishing between different models during debugging and benchmarking. When set, it helps identify the model in logs and benchmark results.

The name can be set either in the constructor or by assigning to this property. Assigning overwrites any name that was previously set.

import * as CP from "optalcp";

// Set name in constructor
let model = new CP.Model({ name: "MySchedulingProblem" });
console.log(model.name); // "MySchedulingProblem"

// Or set name later
model = new CP.Model();
model.name = "JobShop";
console.log(model.name); // "JobShop"
Returns

undefined | string

Set Signature

set name(value: string): void

Parameters
ParameterType
valuestring
Returns

void


abs()

abs(arg: number | IntExpr): IntExpr

Creates an integer expression which is absolute value of arg.

Parameters

ParameterTypeDescription
argnumber | IntExprThe integer expression.

Returns

IntExpr

The resulting integer expression

Remarks

If arg has value absent then the resulting expression has also value absent.

Same as IntExpr.abs.


alternative()

alternative(main: IntervalVar, options: IntervalVar[]): Constraint

Alternative constraint models a choice between different ways to execute an interval.

Parameters

ParameterTypeDescription
mainIntervalVarThe main interval variable.
optionsIntervalVar[]Array of optional interval variables to choose from.

Returns

Constraint

The alternative constraint.

Remarks

Alternative constraint is a way to model various kinds of choices. For example, we can model a task that could be done by worker A, B, or C. To model such alternative, we use interval variable main that represents the task regardless the chosen worker and three interval variables options = [A, B, C] that represent the task when done by worker A, B, or C. Interval variables A, B, and C should be optional. This way, if e.g. option B is chosen, then B will be present and equal to main (they will start at the same time and end at the same time), the remaining options, A and C, will be absent.

We may also decide not to execute the main task at all (if it is optional). Then main will be absent and all options A, B and C will be absent too.

Formal definition

The constraint alternative(main, options) is satisfied in the following two cases:

  1. Interval main is absent and all options[i] are absent too.
  2. Interval main is present and exactly one of options[i] is present (the remaining options are absent). Let k be the index of the present option. Then main.start() == options[k].start() and main.end() == options[k].end().

Example

Let's consider task T, which can be done by workers A, B, or C. The length of the task and a cost associated with it depends on the chosen worker:

  • If done by worker A, then its length is 10, and the cost is 5.
  • If done by worker B, then its length is 20, and the cost is 2.
  • If done by worker C, then its length is 3, and the cost is 10.

Each worker can execute only one task at a time. However, the remaining tasks are omitted in the model below. The objective could be, e.g., to minimize the total cost (also omitted in the model).

let model = new CP.Model;

let T = model.intervalVar({ name: "T" });
let T_A = model.intervalVar({ name: "T_A", optional: true, length: 10 });
let T_B = model.intervalVar({ name: "T_B", optional: true, length: 20 });
let T_C = model.intervalVar({ name: "T_C", optional: true, length: 3 });

// T_A, T_B and T_C are different ways to execute task T:
model.alternative(T, [T_A, T_B, T_C]);
// The cost depends on the chosen option:
let costOfT = model.sum([
T_A.presence().times(5),
T_B.presence().times(2),
T_C.presence().times(10)
]);

// Each worker A can perform only one task at a time:
model.noOverlap([T_A, ...]); // Worker A
model.noOverlap([T_B, ...]); // Worker B
model.noOverlap([T_C, ...]); // Worker C

// Minimize the total cost:
model.sum([costOfT, ...]).minimize();

and()

and(lhs: boolean | BoolExpr, rhs: boolean | BoolExpr): BoolExpr

Logical AND of boolean expressions lhs and rhs.

Parameters

ParameterTypeDescription
lhsboolean | BoolExprThe first boolean expression.
rhsboolean | BoolExprThe second boolean expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as BoolExpr.and.


boolVar()

boolVar(params: { name: string; optional: boolean; }): BoolVar

Creates a new boolean variable and adds it to the model.

Parameters

ParameterTypeDescription
params{ name: string; optional: boolean; }-
params.name?stringName for the variable (useful for debugging)
params.optional?booleanIf true, the variable can be absent in a solution (default false)

Returns

BoolVar

The created boolean variable.

Remarks

A boolean variable represents an unknown truth value (true or false) that the solver must find. Boolean variables are useful for modeling decisions, choices, or logical conditions in your problem.

By default, a boolean variable must be assigned a value (true or false) in every solution. When optional: true, the variable can also be absent, meaning the solution does not use the variable at all. This is useful when the variable represents a decision that may not apply in all scenarios.

Boolean variables support logical operations via methods:

See

Example

Create boolean variables to model decisions:

let model = new CP.Model();
let useMachineA = model.boolVar({ name: "useMachineA" });
let useMachineB = model.boolVar({ name: "useMachineB" });

// Constraint: must use at least one machine
model.enforce(useMachineA.or(useMachineB));

// Constraint: cannot use both machines
model.enforce(useMachineA.and(useMachineB).not());

div()

div(lhs: number | IntExpr, rhs: number | IntExpr): IntExpr

Creates an integer division of the two integer expressions, i.e. lhs div rhs. The division rounds towards zero.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

IntExpr

The resulting integer expression

Remarks

If one of the arguments has value absent, the resulting expression also has value absent.

Same as IntExpr.div.


end()

end(interval: IntervalVar): IntExpr

Creates an integer expression for the end time of an interval variable.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.

Returns

IntExpr

The resulting integer expression

Remarks

If the interval is absent, the resulting expression is also absent.

Example

In the following example, we constrain interval variable y to start after the end of x with a delay of at least 10. In addition, we constrain the length of x to be less or equal to the length of y.

let model = new CP.Model;
let x = model.intervalVar({ name: "x", ... });
let y = model.intervalVar({ name: "y", ... });
model.enforce(model.end(x).plus(10).le(model.start(y)));
model.enforce(model.length(x).le(model.length(y)));

When x or y is absent then value of both constraints above is absent and therefore they are satisfied.

See

IntervalVar.end is equivalent function on IntervalVar.


endAtEnd()

endAtEnd(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.end().plus(delay).eq(successor.end())).

In other words, end of predecessor plus delay must be equal to end of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


endAtStart()

endAtStart(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.end().plus(delay).eq(successor.start())).

In other words, end of predecessor plus delay must be equal to start of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


endBeforeEnd()

endBeforeEnd(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.end().plus(delay).le(successor.end())).

In other words, end of predecessor plus delay must be less than or equal to end of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


endBeforeStart()

endBeforeStart(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.end().plus(delay).le(successor.start())).

In other words, end of predecessor plus delay must be less than or equal to start of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


endOr()

endOr(interval: IntervalVar, absentValue: number): IntExpr

Creates an integer expression for the end time of the interval variable. If the interval is absent, then its value is absentValue.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
absentValuenumberThe value to use when the interval is absent.

Returns

IntExpr

The resulting integer expression

Remarks

This function is equivalent to endOr(interval).guard(absentValue).

See


enforce()

enforce(constraint: boolean | Constraint | BoolExpr | Iterable<boolean | Constraint | BoolExpr>): void

Enforces a boolean expression as a constraint in the model.

Parameters

ParameterTypeDescription
constraintboolean | Constraint | BoolExpr | Iterable<boolean | Constraint | BoolExpr>The constraint, boolean expression, or iterable of these to enforce in the model

Returns

void

Remarks

This is the primary method for enforcing boolean expressions as constraints in the model.

A constraint is satisfied if it is not false. In other words, a constraint is satisfied if it is true or absent.

A boolean expression that is not enforced as a constraint can have arbitrary value in a solution (true, false, or absent). Once enforced as a constraint, it can only be true or absent in the solution.

Note: Constraint objects are automatically registered when created. Passing them to enforce() is accepted but does nothing. For cumulative constraints (cumul <= capacity, cumul >= min_level), using enforce() is recommended for code clarity even though it's not required.

Accepted argument types

The enforce method accepts several types of arguments:

  1. BoolExpr objects: Boolean expressions created from comparisons (e.g., x <= 5, a == b) or logical operations (e.g., a & b, ~c).
  2. bool values: Python boolean constants True or False.
  3. Constraint objects: Accepted but does nothing (constraints auto-register). Use with cumulative constraints for clarity: model.enforce(cumul <= capacity).
  4. Iterables: Lists, tuples, or generators of the above types.

Examples

Basic usage with a boolean expression:

import * as CP from "optalcp";

const model = new CP.Model();
const x = model.intervalVar({ length: 10, name: "x" });

// Enforce boolean expressions as constraints
model.enforce(x.start().ge(0));
model.enforce(x.end().le(100));

Enforcing multiple constraints at once using an iterable:

const model = new CP.Model();
const tasks = Array.from({ length: 5 }, (_, i) =>
model.intervalVar({ length: 10, name: `task_${i}` })
);

// Enforce multiple constraints at once
const constraints = tasks.map(task => task.start().ge(0));
model.enforce(constraints);

// Or inline
model.enforce(tasks.map(task => task.end().le(100)));

Enforcing multiple boolean expressions:

const model = new CP.Model();
const x = model.intVar(0, 100, "x");
const y = model.intVar(0, 100, "y");

// Enforce various boolean expressions
model.enforce([
x.plus(y).le(50), // From comparison
x.ge(10), // From comparison
true, // Trivially satisfied constraint
]);

See


eq()

eq(lhs: number | IntExpr, rhs: number | IntExpr): BoolExpr

Creates Boolean expression lhs = rhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.eq.


eval()

eval(func: IntStepFunction, arg: number | IntExpr): IntExpr

Evaluates a step function at a given point.

Parameters

ParameterTypeDescription
funcIntStepFunctionThe step function.
argnumber | IntExprThe point at which to evaluate the step function.

Returns

IntExpr

The resulting integer expression

Remarks

The result is the value of the step function func at the point arg. If the value of arg is absent, then the result is also absent.

By constraining the returned value, it is possible to limit arg to be only within certain segments of the segmented function. In particular, functions Model.forbidStart and Model.forbidEnd work that way.

See


forbidEnd()

forbidEnd(interval: IntervalVar, func: IntStepFunction): Constraint

Constrains the end of the interval variable to be outside of the zero-height segments of the step function.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
funcIntStepFunctionThe step function.

Returns

Constraint

The constraint forbidding the end point.

Remarks

This function is equivalent to:

  model.enforce(model.ne(model.eval(func, interval.end()), 0));

I.e., the function value at the end of the interval variable cannot be zero.

Example

A delivery task that must complete during business hours (not during lunch break):

See


forbidExtent()

forbidExtent(interval: IntervalVar, func: IntStepFunction): Constraint

Forbid the interval variable to overlap with segments of the function where the value is zero.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
funcIntStepFunctionThe step function.

Returns

Constraint

The constraint forbidding the extent (entire interval).

Remarks

This function prevents the specified interval variable from overlapping with segments of the step function where the value is zero. That is, if [s,e)[s, e) is a segment of the step function where the value is zero, then the interval variable either ends before ss (interval.end()s\mathtt{interval.end()} \le s) or starts after ee (einterval.start()e \le \mathtt{interval.start()}).

Example

A production task that cannot overlap with scheduled maintenance windows:

import * as CP from "optalcp";

const model = new CP.Model();

// A 3-hour production run
const production = model.intervalVar({ length: 3, name: "production" });

// Machine availability: 1 = available, 0 = under maintenance
const availability = model.stepFunction([
[0, 1], // Initially available
[8, 0], // 8h: maintenance starts
[10, 1], // 10h: maintenance ends
]);

// Production cannot overlap periods where availability is 0
model.forbidExtent(production, availability);
model.minimize(production.end());

const result = await model.solve();
// Production runs [0, 3) - finishes before maintenance window

See


forbidStart()

forbidStart(interval: IntervalVar, func: IntStepFunction): Constraint

Constrains the start of the interval variable to be outside of the zero-height segments of the step function.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
funcIntStepFunctionThe step function.

Returns

Constraint

The constraint forbidding the start point.

Remarks

This function is equivalent to:

  model.enforce(model.ne(model.eval(func, interval.start()), 0));

I.e., the function value at the start of the interval variable cannot be zero.

Example

A factory task that can only start during work hours (excluding breaks):

See


ge()

Call Signature

ge(lhs: number | IntExpr, rhs: number | IntExpr): BoolExpr

Creates Boolean expression lhsrhs.

Parameters
ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.
Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.ge.

Call Signature

ge(cumul: CumulExpr, minCapacity: number): Constraint

Constrains cumulative function cumul to be everywhere greater or equal to minCapacity.

Parameters
ParameterTypeDescription
cumulCumulExprThe cumulative expression.
minCapacitynumberThe minimum capacity value.
Returns

Constraint

The constraint object

Remarks

This function can be used to specify the minimum limit of resource usage at any time. For example to make sure that there is never less than zero material on stock. See Model.stepAtStart for an example with ge.

See

getBoolVars()

getBoolVars(): BoolVar[]

Returns a list of all boolean variables in the model.

Returns

BoolVar[]

A list of all boolean variables in the model

Remarks

Returns a copy of the list containing all boolean variables that have been created in this model using Model.boolVar.

See

Model.getIntervalVars, Model.getIntVars.

Example

import * as CP from "optalcp";

const model = new CP.Model();
const useMachineA = model.boolVar("use_machine_a");
const useMachineB = model.boolVar("use_machine_b");

const boolVars = model.getBoolVars();
console.log(boolVars.length); // 2
for (const bv of boolVars) {
console.log(bv.name); // "use_machine_a", "use_machine_b"
}

getIntervalVars()

getIntervalVars(): IntervalVar[]

Returns a list of all interval variables in the model.

Returns

IntervalVar[]

A list of all interval variables in the model

Remarks

Returns a copy of the list containing all interval variables that have been created in this model using Model.intervalVar.

See

Model.getIntVars, Model.getBoolVars.

Example

import * as CP from "optalcp";

const model = new CP.Model();
const task1 = model.intervalVar({ length: 10, name: "task1" });
const task2 = model.intervalVar({ length: 20, name: "task2" });

const intervals = model.getIntervalVars();
console.log(intervals.length); // 2
for (const iv of intervals) {
console.log(iv.name); // "task1", "task2"
}

getIntVars()

getIntVars(): IntVar[]

Returns a list of all integer variables in the model.

Returns

IntVar[]

A list of all integer variables in the model

Remarks

Returns a copy of the list containing all integer variables that have been created in this model using Model.intVar.

See

Model.getIntervalVars, Model.getBoolVars.

Example

import * as CP from "optalcp";

const model = new CP.Model();
const x = model.intVar(0, 10, "x");
const y = model.intVar(0, 100, "y");

const intVars = model.getIntVars();
console.log(intVars.length); // 2
for (const iv of intVars) {
console.log(iv.name); // "x", "y"
}

gt()

gt(lhs: number | IntExpr, rhs: number | IntExpr): BoolExpr

Creates Boolean expression lhs > rhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.gt.


guard()

guard(arg: number | IntExpr, absentValue: number): IntExpr

Creates an expression that replaces value absent by a constant.

Parameters

ParameterTypeDefault valueDescription
argnumber | IntExprundefinedThe integer expression to guard.
absentValuenumber0The value to use when the expression is absent.

Returns

IntExpr

The resulting integer expression

Remarks

The resulting expression is:

  • equal to arg if arg is present
  • and equal to absentValue otherwise (i.e. when arg is absent).

The default value of absentValue is 0.

The resulting expression is never absent.

Same as IntExpr.guard.


identity()

identity(lhs: number | IntExpr, rhs: number | IntExpr): Constraint

Constrains lhs and rhs to be identical, including their presence status.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

Constraint

The identity constraint.

Remarks

Identity is different than equality. For example, if x is absent, then eq(x, 0) is absent, but identity(x, 0) is false.

Same as IntExpr.identity.


implies()

implies(lhs: boolean | BoolExpr, rhs: boolean | BoolExpr): BoolExpr

Logical implication of two boolean expressions, that is lhs implies rhs.

Parameters

ParameterTypeDescription
lhsboolean | BoolExprThe first boolean expression.
rhsboolean | BoolExprThe second boolean expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as BoolExpr.implies.


inRange()

inRange(arg: number | IntExpr, lb: number, ub: number): BoolExpr

Creates Boolean expression lbargub.

Parameters

ParameterTypeDescription
argnumber | IntExprThe integer expression to check.
lbnumberThe lower bound of the range.
ubnumberThe upper bound of the range.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If arg has value absent then the resulting expression has also value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.inRange.


integral()

integral(func: IntStepFunction, interval: IntervalVar): IntExpr

Computes sum of values of the step function func over the interval interval.

Parameters

ParameterTypeDescription
funcIntStepFunctionThe step function.
intervalIntervalVarThe interval variable.

Returns

IntExpr

The resulting integer expression

Remarks

The sum is computed over all points in range interval.start() .. interval.end()-1. The sum includes the function value at the start time but not the value at the end time. If the interval variable has zero length, then the result is 0. If the interval variable is absent, then the result is absent.

Requirement: The step function func must be non-negative.

See

IntStepFunction.integral for the equivalent function on IntStepFunction.


intervalVar()

intervalVar(params: { end: number | [number?, number?]; length: number | [number?, number?]; name: string; optional: boolean; start: number | [number?, number?]; }): IntervalVar

Creates a new interval variable and adds it to the model.

Parameters

ParameterTypeDescription
params{ end: number | [number?, number?]; length: number | [number?, number?]; name: string; optional: boolean; start: number | [number?, number?]; }-
params.end?number | [number?, number?]Fixed end time or range [min, max] (default [0, IntervalMax])
params.length?number | [number?, number?]Fixed length or range [min, max] (default [0, IntervalMax])
params.name?stringName of the interval for debugging and display
params.optional?booleanWhether the interval is optional (default false)
params.start?number | [number?, number?]Fixed start time or range [min, max] (default [0, IntervalMax])

Returns

IntervalVar

The created interval variable.

Remarks

An interval variable represents an unknown interval (a task, operation, action) that the solver assigns a value in such a way as to satisfy all constraints. An interval variable has a start, end, and length. In a solution, start ≤ end and length = end - start.

The interval variable can be optional. In this case, its value in a solution could be absent, meaning that the task/operation is not performed.

Parameters params.start, params.end, and params.length can be either a number or a tuple of two numbers. If a number is given, it represents a fixed value. If a tuple is given, it represents a range of possible values. The default range for start, end and length is 0 to IntervalMax. If a range is specified but one of the values is undefined (e.g. start: [, 100]) then the default value is used instead (in our case 0).

Example

let model = new CP.Model();

// Create a present interval variable with a fixed start but unknown length:
let x = model.intervalVar({ start: 0, length: [10, 20], name: "x" });

// Create an interval variable with a start and end ranges:
let y = model.intervalVar({ start: [0, 5], end: [10, 15], name: "y" });

// Create an optional interval variable with a length interval 5..10:
let z = model.intervalVar({ length: [5, 10], optional: true, name: "z" });

See

IntervalVar.


intVar()

intVar(params?: { max: number; min: number; name: string; optional: boolean; }): IntVar

Creates a new integer variable and adds it to the model.

Parameters

ParameterTypeDescription
params?{ max: number; min: number; name: string; optional: boolean; }-
params.max?numberMaximum value of the variable (default IntVarMax)
params.min?numberMinimum value of the variable (default 0)
params.name?stringName of the variable for debugging and display
params.optional?booleanWhether the variable is optional (default false)

Returns

IntVar

The created integer variable.

Remarks

An integer variable represents an unknown value the solver must find. The variable can be optional. In this case, its value in a solution could be absent, meaning that the solution does not use the variable at all.

Example

let model = new CP.Model();

// Create a present integer variable with possible values 1..10:
let x = model.intVar({ min: 1, max: 10, name: "x" });

// Create an optional integer variable with possible values 5..IntVarMax:
let y = model.intVar({ min: 5, optional: true, name: "y" });

// Create an integer variable with a fixed value 10 (optional):
let z = model.intVar({ min: 10, max: 10, optional: true, name: "z" });

le()

Call Signature

le(lhs: number | IntExpr, rhs: number | IntExpr): BoolExpr

Creates Boolean expression lhsrhs.

Parameters
ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.
Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.le.

Call Signature

le(cumul: CumulExpr, maxCapacity: number): Constraint

Constrains cumulative function cumul to be everywhere less or equal to maxCapacity.

Parameters
ParameterTypeDescription
cumulCumulExprThe cumulative expression.
maxCapacitynumberThe maximum capacity value.
Returns

Constraint

The constraint object

Remarks

This function can be used to specify the maximum limit of resource usage at any time. For example, to limit the number of workers working simultaneously, limit the maximum amount of material on stock, etc. See Model.pulse for an example with le.

See

length()

length(interval: IntervalVar): IntExpr

Creates an integer expression for the duration (end - start) of an interval variable.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.

Returns

IntExpr

The resulting integer expression

Remarks

If the interval is absent, the resulting expression is also absent.

Example

In the following example, we constrain interval variable y to start after the end of x with a delay of at least 10. In addition, we constrain the length of x to be less or equal to the length of y.

let model = new CP.Model;
let x = model.intervalVar({ name: "x", ... });
let y = model.intervalVar({ name: "y", ... });
model.enforce(model.end(x).plus(10).le(model.start(y)));
model.enforce(model.length(x).le(model.length(y)));

When x or y is absent then value of both constraints above is absent and therefore they are satisfied.

See

IntervalVar.length is equivalent function on IntervalVar.


lengthOr()

lengthOr(interval: IntervalVar, absentValue: number): IntExpr

Creates an integer expression for the duration (end - start) of the interval variable. If the interval is absent, then its value is absentValue.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
absentValuenumberThe value to use when the interval is absent.

Returns

IntExpr

The resulting integer expression

Remarks

This function is equivalent to lengthOr(interval).guard(absentValue).

See


lexGe()

lexGe(lhs: (number | IntExpr)[], rhs: (number | IntExpr)[]): Constraint

Lexicographic greater than or equal constraint: lhsrhs.

Parameters

ParameterTypeDescription
lhs(number | IntExpr)[]The left-hand side array of integer expressions.
rhs(number | IntExpr)[]The right-hand side array of integer expressions.

Returns

Constraint

The lexicographic constraint.

Remarks

Constrains lhs to be lexicographically greater than or equal rhs.

Both arrays must have the same length, and the length must be at least 1.

Lexicographic ordering compares arrays element by element from the first position. The comparison lhsrhs holds if and only if:

  • all elements are equal (lhs[i] == rhs[i] for all i), or
  • there exists a position k where lhs[k] > rhs[k] and all preceding elements are equal (lhs[i] == rhs[i] for all i < k)

Lexicographic constraints are useful for symmetry breaking. For example, when you have multiple equivalent solutions that differ only in the ordering of symmetric variables, adding a lexicographic constraint can eliminate redundant solutions.

Example

const model = new CP.Model();

// Variables for a 3x3 matrix where rows should be lexicographically ordered
const rows = Array.from({ length: 3 }, (_, i) =>
Array.from({ length: 3 }, (_, j) => model.intVar(0, 9, `x_${i}_${j}`))
);

// Break row symmetry: row[0] ≥ row[1] ≥ row[2] lexicographically
model.lexGe(rows[0], rows[1]);
model.lexGe(rows[1], rows[2]);

See

Model.lexLe, Model.lexLt, Model.lexGt for other lexicographic comparisons.


lexGt()

lexGt(lhs: (number | IntExpr)[], rhs: (number | IntExpr)[]): Constraint

Lexicographic strictly greater than constraint: lhs > rhs.

Parameters

ParameterTypeDescription
lhs(number | IntExpr)[]The left-hand side array of integer expressions.
rhs(number | IntExpr)[]The right-hand side array of integer expressions.

Returns

Constraint

The lexicographic constraint.

Remarks

Constrains lhs to be lexicographically strictly greater than rhs.

Both arrays must have the same length, and the length must be at least 1.

Lexicographic ordering compares arrays element by element from the first position. The comparison lhs > rhs holds if and only if: there exists a position k where lhs[k] > rhs[k] and all preceding elements are equal (lhs[i] == rhs[i] for all i < k)

Lexicographic constraints are useful for symmetry breaking. For example, when you have multiple equivalent solutions that differ only in the ordering of symmetric variables, adding a lexicographic constraint can eliminate redundant solutions.

Example

const model = new CP.Model();

// Variables for a 3x3 matrix where rows should be lexicographically ordered
const rows = Array.from({ length: 3 }, (_, i) =>
Array.from({ length: 3 }, (_, j) => model.intVar(0, 9, `x_${i}_${j}`))
);

// Break row symmetry: row[0] > row[1] > row[2] lexicographically
model.lexGt(rows[0], rows[1]);
model.lexGt(rows[1], rows[2]);

See

Model.lexLe, Model.lexLt, Model.lexGe for other lexicographic comparisons.


lexLe()

lexLe(lhs: (number | IntExpr)[], rhs: (number | IntExpr)[]): Constraint

Lexicographic less than or equal constraint: lhsrhs.

Parameters

ParameterTypeDescription
lhs(number | IntExpr)[]The left-hand side array of integer expressions.
rhs(number | IntExpr)[]The right-hand side array of integer expressions.

Returns

Constraint

The lexicographic constraint.

Remarks

Constrains lhs to be lexicographically less than or equal rhs.

Both arrays must have the same length, and the length must be at least 1.

Lexicographic ordering compares arrays element by element from the first position. The comparison lhsrhs holds if and only if:

  • all elements are equal (lhs[i] == rhs[i] for all i), or
  • there exists a position k where lhs[k] < rhs[k] and all preceding elements are equal (lhs[i] == rhs[i] for all i < k)

Lexicographic constraints are useful for symmetry breaking. For example, when you have multiple equivalent solutions that differ only in the ordering of symmetric variables, adding a lexicographic constraint can eliminate redundant solutions.

Example

const model = new CP.Model();

// Variables for a 3x3 matrix where rows should be lexicographically ordered
const rows = Array.from({ length: 3 }, (_, i) =>
Array.from({ length: 3 }, (_, j) => model.intVar(0, 9, `x_${i}_${j}`))
);

// Break row symmetry: row[0] ≤ row[1] ≤ row[2] lexicographically
model.lexLe(rows[0], rows[1]);
model.lexLe(rows[1], rows[2]);

See

Model.lexLt, Model.lexGe, Model.lexGt for other lexicographic comparisons.


lexLt()

lexLt(lhs: (number | IntExpr)[], rhs: (number | IntExpr)[]): Constraint

Lexicographic strictly less than constraint: lhs < rhs.

Parameters

ParameterTypeDescription
lhs(number | IntExpr)[]The left-hand side array of integer expressions.
rhs(number | IntExpr)[]The right-hand side array of integer expressions.

Returns

Constraint

The lexicographic constraint.

Remarks

Constrains lhs to be lexicographically strictly less than rhs.

Both arrays must have the same length, and the length must be at least 1.

Lexicographic ordering compares arrays element by element from the first position. The comparison lhs < rhs holds if and only if: there exists a position k where lhs[k] < rhs[k] and all preceding elements are equal (lhs[i] == rhs[i] for all i < k)

Lexicographic constraints are useful for symmetry breaking. For example, when you have multiple equivalent solutions that differ only in the ordering of symmetric variables, adding a lexicographic constraint can eliminate redundant solutions.

Example

const model = new CP.Model();

// Variables for a 3x3 matrix where rows should be lexicographically ordered
const rows = Array.from({ length: 3 }, (_, i) =>
Array.from({ length: 3 }, (_, j) => model.intVar(0, 9, `x_${i}_${j}`))
);

// Break row symmetry: row[0] < row[1] < row[2] lexicographically
model.lexLt(rows[0], rows[1]);
model.lexLt(rows[1], rows[2]);

See

Model.lexLe, Model.lexGe, Model.lexGt for other lexicographic comparisons.


lt()

lt(lhs: number | IntExpr, rhs: number | IntExpr): BoolExpr

Creates Boolean expression lhs < rhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.lt.


max()

max(args: (number | IntExpr)[]): IntExpr

Creates an integer expression for the maximum of the arguments.

Parameters

ParameterTypeDescription
args(number | IntExpr)[]Array of integer expressions to compute maximum of.

Returns

IntExpr

The resulting integer expression

Remarks

Absent arguments are ignored as if they were not specified in the input array args. Maximum of an empty set (i.e. max([]) is absent. The maximum is absent also if all arguments are absent.

Note that binary function Model.max2 handles absent values differently. For example, when x is absent then:

  • max2(x, 5) is absent.
  • max([x, 5]) is 5.
  • max([x]) is absent.

Example

A common use case is to compute makespan of a set of tasks, i.e. the time when the last task finishes. In the following example, we minimize the makespan of a set of tasks (other parts of the model are omitted).

let model = new CP.Model;
let tasks: CP.IntervalVar[] = ...;
...
// Create an array of end times of the tasks:
let endTimes = tasks.map(task => task.end());
let makespan = model.max(endTimes);
model.minimize(makespan);

Notice that when a task is absent (not executed), then its end time is absent. And therefore, the absent task is not included in the maximum.

See

  • Binary Model.max2.
  • Function Model.span constraints interval variable to start and end at minimum and maximum of the given set of intervals.

max2()

max2(lhs: number | IntExpr, rhs: number | IntExpr): IntExpr

Creates an integer expression which is the maximum of lhs and rhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

IntExpr

The resulting integer expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as IntExpr.max2. See Model.max for n-ary maximum.


maximize()

maximize(expr: number | IntExpr): Objective

Creates a maximization objective for the provided expression.

Parameters

ParameterTypeDescription
exprnumber | IntExprThe expression to maximize

Returns

Objective

An Objective that maximizes the expression.

Remarks

Creates an Objective to maximize the given expression. A model can have at most one objective. New objective replaces the old one.

Equivalent of function IntExpr.maximize.

Example

In the following model, we search for a solution that maximizes the length of the interval variable x:

let model = new CP.Model;
let x = model.intervalVar({ length: [10, 20], name: "x" });
model.maximize(x.length());
let result = await model.solve();

See


min()

min(args: (number | IntExpr)[]): IntExpr

Creates an integer expression for the minimum of the arguments.

Parameters

ParameterTypeDescription
args(number | IntExpr)[]Array of integer expressions to compute minimum of.

Returns

IntExpr

The resulting integer expression

Remarks

Absent arguments are ignored as if they were not specified in the input array args. Minimum of an empty set (i.e. min([])) is absent. The minimum is absent also if all arguments are absent.

Note that binary function Model.min2 handles absent values differently. For example, when x is absent then:

  • min2(x, 5) is absent.
  • min([x, 5]) is 5.
  • min([x]) is absent.

Example

In the following example, we compute the time when the first task of tasks starts, i.e. the minimum of the starting times.

let model = new CP.Model;
let tasks: CP.IntervalVar[] = ...;
...
// Create an array of start times of the tasks:
let startTimes = tasks.map(task => task.start());
let firstStartTime = model.min(startTimes);

Notice that when a task is absent (not executed), its end time is absent. And therefore, the absent task is not included in the minimum.

See

  • Binary Model.min2.
  • Function Model.span constraints interval variable to start and end at minimum and maximum of the given set of intervals.

min2()

min2(lhs: number | IntExpr, rhs: number | IntExpr): IntExpr

Creates an integer expression which is the minimum of lhs and rhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

IntExpr

The resulting integer expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as IntExpr.min2. See Model.min for n-ary minimum.


minimize()

minimize(expr: number | IntExpr): Objective

Creates a minimization objective for the provided expression.

Parameters

ParameterTypeDescription
exprnumber | IntExprThe expression to minimize

Returns

Objective

An Objective that minimizes the expression.

Remarks

Creates an Objective to minimize the given expression. A model can have at most one objective. New objective replaces the old one.

Equivalent of function IntExpr.minimize.

Example

In the following model, we search for a solution that minimizes the maximum end of the two intervals x and y:

let model = new CP.Model();
let x = model.intervalVar({ length: 10, name: "x" });
let y = model.intervalVar({ length: 20, name: "y" });
model.minimize(model.max2(x.end(), y.end()));
let result = await model.solve();

See


minus()

Call Signature

minus(lhs: number | IntExpr, rhs: number | IntExpr): IntExpr

Creates a subtraction of the two integer expressions, i.e. lhs - rhs.

Parameters
ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.
Returns

IntExpr

The resulting integer expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as IntExpr.minus.

Call Signature

minus(lhs: CumulExpr, rhs: CumulExpr): CumulExpr

Subtraction of two cumulative expressions.

Parameters
ParameterTypeDescription
lhsCumulExprThe left-hand side cumulative expression.
rhsCumulExprThe right-hand side cumulative expression.
Returns

CumulExpr

The resulting cumulative expression

Remarks

Computes subtraction of two cumulative functions.

Formal definition

Let result = minus(lhs, rhs). Then for any number x in range IntervalMin..IntervalMax the value of result at x is equal to lhs at x minus rhs at x.

minus(lhs, rhs) is the same as sum([lhs, neg(rhs)]).

See

ne()

ne(lhs: number | IntExpr, rhs: number | IntExpr): BoolExpr

Creates Boolean expression lhsrhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Use Model.enforce to add this expression as a constraint to the model.

Same as IntExpr.ne.


neg()

Call Signature

neg(arg: number | IntExpr): IntExpr

Creates negation of the integer expression, i.e. -arg.

Parameters
ParameterTypeDescription
argnumber | IntExprThe integer expression.
Returns

IntExpr

The resulting integer expression

Remarks

If the value of arg has value absent then the resulting expression has also value absent.

Same as IntExpr.neg.

Call Signature

neg(arg: CumulExpr): CumulExpr

Negation of a cumulative expression.

Parameters
ParameterTypeDescription
argCumulExprThe cumulative expression.
Returns

CumulExpr

The resulting cumulative expression

Remarks

Computes negation of a cumulative function. That is, the resulting function has the opposite values.

See

noOverlap()

noOverlap(intervals: IntervalVar[] | SequenceVar, transitions?: number[][]): void

Constrain a set of interval variables not to overlap.

Parameters

ParameterTypeDescription
intervalsIntervalVar[] | SequenceVarAn array of interval variables or a sequence variable to constrain
transitions?number[][]A 2D square array of minimum transition times between the intervals

Returns

void

The no-overlap constraint.

Remarks

This function constrains a set of interval variables so they do not overlap. That is, for each pair of interval variables x and y, one of the following must hold:

  1. Interval variable x or y is absent. In this case, the absent interval is not scheduled (the task is not performed), so it cannot overlap with any other interval. Only optional interval variables can be absent.
  2. Interval variable x is before y, that is, x.end() is less than or equal to y.start().
  3. The interval variable y is before x. That is, y.end() is less than or equal to x.start().

The function can also take a square array transitions of minimum transition times between the intervals. The transition time is the time that must elapse between the end of the first interval and the start of the second interval. The transition time cannot be negative. When transition times are specified, the above conditions 2 and 3 are modified as follows:

  1. x.end() + transitions[i][j] is less than or equal to y.start().
  2. y.end() + transitions[j][i] is less than or equal to x.start().

Where i and j are types of x and y. When an array of intervals is passed, the type of each interval is its index in the array.

Note that minimum transition times are enforced between all pairs of intervals, not only between direct neighbors.

Instead of an array of interval variables, a SequenceVar can be passed. See Model.sequenceVar for how to assign types to intervals in a sequence.

Example

The following example does not use transition times. For example with transition times see SequenceVar.noOverlap.

Let's consider a set of tasks that must be performed by a single machine. The machine can handle only one task at a time. Each task is characterized by its length and a deadline. The goal is to schedule the tasks on the machine so that the number of missed deadlines is minimized.

import * as CP from "optalcp";

const tasks = [
{ length: 10, deadline: 70 },
{ length: 20, deadline: 50 },
{ length: 15, deadline: 50 },
{ length: 30, deadline: 100 },
{ length: 20, deadline: 120 },
{ length: 25, deadline: 90 },
{ length: 30, deadline: 80 },
{ length: 10, deadline: 40 },
{ length: 20, deadline: 60 },
{ length: 25, deadline: 150 },
];

const model = new CP.Model();

// An interval variable for each task:
const taskVars = [];
// A boolean expression that is true if the task is late:
const isLate = [];

for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
const taskVar = model.intervalVar({ name: `Task${i}`, length: task.length });
taskVars.push(taskVar);
isLate.push(taskVar.end().ge(task.deadline));
}

// Tasks cannot overlap:
model.noOverlap(taskVars);
// Minimize the number of late tasks:
model.minimize(model.sum(isLate));

const result = await model.solve();

See

SequenceVar.noOverlap is the equivalent method on SequenceVar.


not()

not(arg: boolean | BoolExpr): BoolExpr

Negation of the boolean expression arg.

Parameters

ParameterTypeDescription
argboolean | BoolExprThe boolean expression to negate.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If the argument has value absent then the resulting expression has also value absent.

Same as BoolExpr.not.


or()

or(lhs: boolean | BoolExpr, rhs: boolean | BoolExpr): BoolExpr

Logical OR of boolean expressions lhs and rhs.

Parameters

ParameterTypeDescription
lhsboolean | BoolExprThe first boolean expression.
rhsboolean | BoolExprThe second boolean expression.

Returns

BoolExpr

The resulting Boolean expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as BoolExpr.or.


plus()

Call Signature

plus(lhs: number | IntExpr, rhs: number | IntExpr): IntExpr

Creates an addition of the two integer expressions, i.e. lhs + rhs.

Parameters
ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.
Returns

IntExpr

The resulting integer expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as IntExpr.plus.

Call Signature

plus(lhs: CumulExpr, rhs: CumulExpr): CumulExpr

Addition of two cumulative expressions.

Parameters
ParameterTypeDescription
lhsCumulExprThe left-hand side cumulative expression.
rhsCumulExprThe right-hand side cumulative expression.
Returns

CumulExpr

The resulting cumulative expression

Remarks

Computes addition of two cumulative functions.

Formal definition

Let result = plus(lhs, rhs). Then for any number x in range IntervalMin..IntervalMax the value of result at x is equal to lhs at x plus rhs at x.

plus(lhs, rhs) is the same as sum([lhs, rhs]).

See

position()

position(interval: IntervalVar, sequence: SequenceVar): IntExpr

Creates an expression equal to the position of the interval on the sequence.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
sequenceSequenceVarThe sequence variable.

Returns

IntExpr

The resulting integer expression

Remarks

In the solution, the interval which is scheduled first has position 0, the second interval has position 1, etc. The position of an absent interval is absent.

The position expression cannot be used with interval variables of possibly zero length (because the position of two simultaneous zero-length intervals would be undefined). Also, position cannot be used in case of Model.noOverlap constraint with transition times.

See


presence()

presence(arg: number | boolean | IntExpr | IntervalVar): BoolExpr

Creates a boolean expression that is true if the given argument is present in the solution.

Parameters

ParameterTypeDescription
argnumber | boolean | IntExpr | IntervalVarThe argument to check for presence in the solution

Returns

BoolExpr

A boolean expression that is true if the argument is present in the solution.

Remarks

The value of the expression remains unknown until a solution is found. The expression can be used in a constraint to restrict possible solutions.

The function is equivalent to IntervalVar.presence and IntExpr.presence.

Example

In the following example, interval variables x and y must have the same presence status. I.e. they must either be both present or both absent.

const model = new CP.Model();

let x = model.intervalVar({ name: "x", optional: true, length: 10, start: [0, 100] });
let y = model.intervalVar({ name: "y", optional: true, length: 10, start: [0, 100] });
model.enforce(model.presence(x).eq(model.presence(y)));

Simple constraints over presence

The solver treats binary constraints over presence in a special way: it uses them to better propagate other constraints over the same pairs of variables. Let's extend the previous example by a constraint that x must end before y starts:

let x = model.intervalVar({ name: "x", optional: true, length: 10, start: [0, 100] });
let y = model.intervalVar({ name: "y", optional: true, length: 10, start: [0, 100] });
model.enforce(model.presence(x).eq(model.presence(y)));
// x.end <= y.start:
let precedence = x.end().le(y.start());
model.enforce(precedence);

In this example, the solver sees (propagates) that the minimum start time of y is 10 and maximum end time of x is 90. Without the constraint over presence, the solver could not propagate that because one of the intervals can be absent and the other one present (and so the value of precedence would be absent and the constraint would be satisfied).

To achieve good propagation, it is recommended to use binary constraints over presence when possible. For example, multiple binary constraints can be used instead of a single complicated constraint.


pulse()

pulse(interval: IntervalVar, height: number | IntExpr): CumulExpr

Creates cumulative function (expression) pulse for the given interval variable and height.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
heightnumber | IntExprThe height value.

Returns

CumulExpr

The resulting cumulative expression

Remarks

Pulse can be used to model a resource requirement during an interval variable. The given amount height of the resource is used throughout the interval (from start to end).

Limitation: The height must be non-negative. Pulses with negative height are not supported. If you need negative contributions, use step functions instead (see Model.stepAtStart and Model.stepAtEnd).

Formal definition

Pulse creates a cumulative function which has the value:

  • 0 before interval.start(),
  • height between interval.start() and interval.end(),
  • 0 after interval.end()

If interval is absent, the pulse is 0 everywhere.

The height can be a constant value or an expression. In particular, the height can be given by an IntVar. In such a case, the height is unknown at the time of the model creation but is determined during the search.

Note that the interval and the height may have different presence statuses (when the height is given by a variable or an expression). In this case, the pulse is present only if both the interval and the height are present. Therefore, it is helpful to constrain the height to have the same presence status as the interval.

Cumulative functions can be combined using CumulExpr.plus, CumulExpr.minus, CumulExpr.neg and Model.sum. A cumulative function's minimum and maximum height can be constrained using CumulExpr.le and CumulExpr.ge.

Examples

Let us consider a set of tasks and a group of 3 workers. Each task requires a certain number of workers (demand). Our goal is to schedule the tasks so that the length of the schedule (makespan) is minimal.

// The input data:
const nbWorkers = 3;
const tasks = [
{ length: 10, demand: 3},
{ length: 20, demand: 2},
{ length: 15, demand: 1},
{ length: 30, demand: 2},
{ length: 20, demand: 1},
{ length: 25, demand: 2},
{ length: 10, demand: 1},
];

let model = new CP.Model;
// A set of pulses, one for each task:
let pulses: CP.CumulExpr[] = [];
// End times of the tasks:
let ends: CP.IntExpr[] = [];

for (let i = 0; i < tasks.length; i++) {
// Create a task:
let task = model.intervalVar({ name: "T" + (i+1), length: tasks[i].length} );
// Create a pulse for the task:
pulses.push(model.pulse(task, tasks[i].demand));
// Store the end of the task:
ends.push(task.end());
}

// The number of workers used at any time cannot exceed nbWorkers:
model.le(model.sum(pulses), nbWorkers);
// Minimize the maximum of the ends (makespan):
model.minimize(model.max(ends));

let result = await model.solve({ searchType: "FDS" });

In the following example, we create three interval variables x, y, and z that represent some tasks. Variables x and y are present, but variable z is optional. Each task requires a certain number of workers. The length of the task depends on the assigned number of workers. The number of assigned workers is modeled using integer variables wx, wy, and wz.

There are 7 workers. Therefore, at any time, the sum of the workers assigned to the running tasks must be less or equal to 7.

If the task z is absent, then the variable wz has no meaning, and therefore, it should also be absent.

let model = CP.Model;
let x = model.intervalVar({ name: "x" });
let y = model.intervalVar({ name: "y" });
let z = model.intervalVar({ name: "z", optional: true });

let wx = model.intVar({ range: [1, 5], name: "wx" });
let wy = model.intVar({ range: [1, 5], name: "wy" });
let wz = model.intVar({ range: [1, 5], name: "wz", optional: true });

// wz is present if an only if z is present:
model.enforce(z.presence().eq(wz.presence()));

let px = model.pulse(x, wx);
let py = model.pulse(y, wy);
let pz = model.pulse(z, wz);

// There are at most 7 workers at any time:
model.sum([px, py, pz]).le(7);

// Length of the task depends on the number of workers using the following formula:
// length * wx = 12
model.enforce(x.length().times(wx).eq(12));
model.enforce(y.length().times(wy).eq(12));
model.enforce(z.length().times(wz).eq(12));

See


sequenceVar()

sequenceVar(intervals: IntervalVar[], types?: number[], name?: string): SequenceVar

Creates a sequence variable from the provided set of interval variables.

Parameters

ParameterTypeDescription
intervalsIntervalVar[]Interval variables that will form the sequence in the solution
types?number[]Types of the intervals, used in particular for transition times
name?stringName assigned to the sequence variable

Returns

SequenceVar

The created sequence variable

Remarks

Sequence variable is used together with SequenceVar.noOverlap constraint to model a set of intervals that cannot overlap and so they form a sequence in the solution. Sequence variable allows us to constrain the sequence further. For example, by specifying sequence-dependent minimum transition times.

Types can be used to mark intervals with similar properties. In particular, they behave similarly in terms of transition times. Interval variable intervals[0] will have type type[0], intervals[1] will have type type[1] and so on.

If types are not specified then intervals[0] will have type 0, intervals[1] will have type 1, and so on.

The length of the array types must be the same as the length of the array intervals. Types should be integer numbers in the range 0 to n-1 where n is the number of types.

See


span()

span(main: IntervalVar, covered: IntervalVar[]): Constraint

Constrains an interval variable to span (cover) a set of other interval variables.

Parameters

ParameterTypeDescription
mainIntervalVarThe spanning interval variable.
coveredIntervalVar[]The set of interval variables to cover.

Returns

Constraint

The span constraint.

Remarks

Span constraint can be used to model, for example, a composite task that consists of several subtasks.

The constraint makes sure that interval variable main starts with the first interval in covered and ends with the last interval in covered. Absent interval variables in covered are ignored.

Formal definition

Span constraint is satisfied in one of the following two cases:

  • Interval variable main is absent and all interval variables in covered are absent too.

  • Interval variable main is present, at least one interval in covered is present and:

    • main.start() is equal to the minimum starting time of all present intervals in covered.
    • main.end() is equal to the maximum ending time of all present intervals in covered.

Example

Let's consider composite task T, which consists of 3 subtasks: T1, T2, and T3. Subtasks are independent, could be processed in any order, and may overlap. However, task T is blocking a particular location, and no other task can be processed there. The location is blocked as soon as the first task from T1, T2, T3 starts, and it remains blocked until the last one of them finishes.

let model = new CP.Model;

// Subtasks have known lengths:
let T1 = model.intervalVar({ name: "T1", length: 10 });
let T2 = model.intervalVar({ name: "T2", length: 5 });
let T3 = model.intervalVar({ name: "T3", length: 15 });
// The main task has unknown length though:
let T = model.intervalVar({ name: "T" });

// T spans/covers T1, T2 and T3:
model.span(T, [T1, T2, T3]);

// Tasks requiring the same location cannot overlap.
// Other tasks are not included in the example, therefore '...' below:
model.noOverlap([T, ...]);

See

IntervalVar.span is equivalent function on IntervalVar.


start()

start(interval: IntervalVar): IntExpr

Creates an integer expression for the start time of an interval variable.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.

Returns

IntExpr

The resulting integer expression

Remarks

If the interval is absent, the resulting expression is also absent.

Example

In the following example, we constrain interval variable y to start after the end of x with a delay of at least 10. In addition, we constrain the length of x to be less or equal to the length of y.

let model = new CP.Model;
let x = model.intervalVar({ name: "x", ... });
let y = model.intervalVar({ name: "y", ... });
model.enforce(model.end(x).plus(10).le(model.start(y)));
model.enforce(model.length(x).le(model.length(y)));

When x or y is absent then value of both constraints above is absent and therefore they are satisfied.

See

IntervalVar.start is equivalent function on IntervalVar.


startAtEnd()

startAtEnd(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.start().plus(delay).eq(successor.end())).

In other words, start of predecessor plus delay must be equal to end of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


startAtStart()

startAtStart(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.start().plus(delay).eq(successor.start())).

In other words, start of predecessor plus delay must be equal to start of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


startBeforeEnd()

startBeforeEnd(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.start().plus(delay).le(successor.end())).

In other words, start of predecessor plus delay must be less than or equal to end of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


startBeforeStart()

startBeforeStart(predecessor: IntervalVar, successor: IntervalVar, delay: number | IntExpr): Constraint

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault valueDescription
predecessorIntervalVarundefinedThe predecessor interval variable.
successorIntervalVarundefinedThe successor interval variable.
delaynumber | IntExpr0The minimum delay between intervals.

Returns

Constraint

The precedence constraint.

Remarks

Same as:

model.enforce(predecessor.start().plus(delay).le(successor.start())).

In other words, start of predecessor plus delay must be less than or equal to start of successor.

When one of the two interval variables is absent, then the constraint is satisfied.

See


startOr()

startOr(interval: IntervalVar, absentValue: number): IntExpr

Creates an integer expression for the start time of the interval variable. If the interval is absent, then its value is absentValue.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
absentValuenumberThe value to use when the interval is absent.

Returns

IntExpr

The resulting integer expression

Remarks

This function is equivalent to startOr(interval).guard(absentValue).

See


stepAt()

stepAt(x: number, height: number | IntExpr): CumulExpr

Creates a cumulative function that changes value at a given point.

Parameters

ParameterTypeDescription
xnumberThe point at which the cumulative function changes value.
heightnumber | IntExprThe height value (can be positive, negative, constant, or expression).

Returns

CumulExpr

The resulting cumulative expression

Remarks

This function is similar to Model.stepAtStart and Model.stepAtEnd, but the time of the change is given by the constant value x instead of by the start/end of an interval variable. The height can be a constant or an expression (e.g., created by Model.intVar).

Formal definition

stepAt creates a cumulative function which has the value:

  • 0 before x,
  • height after x.

See


stepAtEnd()

stepAtEnd(interval: IntervalVar, height: number | IntExpr): CumulExpr

Creates cumulative function (expression) that changes value at end of the interval variable by the given height.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
heightnumber | IntExprThe height value.

Returns

CumulExpr

The resulting cumulative expression

Remarks

Cumulative step functions could be used to model a resource that is consumed or produced and, therefore, changes in amount over time. Examples of such a resource are a battery, an account balance, a product's stock, etc.

A stepAtEnd can change the amount of such resource at the end of a given variable. The amount is changed by the given height, which can be positive or negative.

The height can be a constant value or an expression. In particular, the height can be given by an IntVar. In such a case, the height is unknown at the time of the model creation but is determined during the search.

Note that the interval and the height may have different presence statuses (when the height is given by a variable or an expression). In this case, the step is present only if both the interval and the height are present. Therefore, it is helpful to constrain the height to have the same presence status as the interval.

Cumulative steps could be combined using CumulExpr.plus, CumulExpr.minus, CumulExpr.neg and Model.sum. A cumulative function's minimum and maximum height can be constrained using CumulExpr.le and CumulExpr.ge.

Formal definition

stepAtEnd creates a cumulative function which has the value:

  • 0 before interval.end(),
  • height after interval.end().

If the interval or the height is absent, the created cumulative function is 0 everywhere.

Example

Let us consider a set of tasks. Each task either costs a certain amount of money or makes some money. Money is consumed at the start of a task and produced at the end. We have an initial budget, and we want to schedule the tasks so that we do not run out of money (i.e., the amount is always non-negative).

Tasks cannot overlap. Our goal is to find the shortest schedule possible.

// The input data:
const budget = 100;
const tasks = [
{ length: 10, money: -150 },
{ length: 20, money: 40 },
{ length: 15, money: 20 },
{ length: 30, money: -10 },
{ length: 20, money: 30 },
{ length: 25, money: -20 },
{ length: 10, money: 10 },
{ length: 20, money: 50 },
];

let model = new CP.Model;
let taskVars: CP.IntervalVar[] = [];
// A set of steps, one for each task:
let steps: CP.CumulExpr[] = [];

for (let i = 0; i < tasks.length; i++) {
let interval = model.intervalVar({ name: "T" + (i+1), length: tasks[i].length} );
taskVars.push(interval);
if (tasks[i].money < 0) {
// Task costs some money:
steps.push(model.stepAtStart(interval, tasks[i].money));
} else {
// Tasks makes some money:
steps.push(model.stepAtEnd(interval, tasks[i].money));
}
}

// The initial budget increases the cumul at time 0:
steps.push(model.stepAt(0, budget));
// The money must be non-negative at any time:
model.ge(model.sum(steps), 0);
// Only one task at a time:
model.noOverlap(taskVars);

// Minimize the maximum of the ends (makespan):
model.max(taskVars.map(t => t.end())).minimize();

let result = await model.solve({ searchType: "FDS"});

See


stepAtStart()

stepAtStart(interval: IntervalVar, height: number | IntExpr): CumulExpr

Creates cumulative function (expression) that changes value at start of the interval variable by the given height.

Parameters

ParameterTypeDescription
intervalIntervalVarThe interval variable.
heightnumber | IntExprThe height value.

Returns

CumulExpr

The resulting cumulative expression

Remarks

Cumulative step functions could be used to model a resource that is consumed or produced and, therefore, changes in amount over time. Examples of such a resource are a battery, an account balance, a product's stock, etc.

A stepAtStart can change the amount of such resource at the start of a given variable. The amount is changed by the given height, which can be positive or negative.

The height can be a constant value or an expression. In particular, the height can be given by an IntVar. In such a case, the height is unknown at the time of the model creation but is determined during the search.

Note that the interval and the height may have different presence statuses (when the height is given by a variable or an expression). In this case, the step is present only if both the interval and the height are present. Therefore, it is helpful to constrain the height to have the same presence status as the interval.

Cumulative steps could be combined using CumulExpr.plus, CumulExpr.minus, CumulExpr.neg and Model.sum. A cumulative function's minimum and maximum height can be constrained using CumulExpr.le and CumulExpr.ge.

Formal definition

stepAtStart creates a cumulative function which has the value:

  • 0 before interval.start(),
  • height after interval.start().

If the interval or the height is absent, the created cumulative function is 0 everywhere.

Example

Let us consider a set of tasks. Each task either costs a certain amount of money or makes some money. Money is consumed at the start of a task and produced at the end. We have an initial budget, and we want to schedule the tasks so that we do not run out of money (i.e., the amount is always non-negative).

Tasks cannot overlap. Our goal is to find the shortest schedule possible.

// The input data:
const budget = 100;
const tasks = [
{ length: 10, money: -150 },
{ length: 20, money: 40 },
{ length: 15, money: 20 },
{ length: 30, money: -10 },
{ length: 20, money: 30 },
{ length: 25, money: -20 },
{ length: 10, money: 10 },
{ length: 20, money: 50 },
];

let model = new CP.Model;
let taskVars: CP.IntervalVar[] = [];
// A set of steps, one for each task:
let steps: CP.CumulExpr[] = [];

for (let i = 0; i < tasks.length; i++) {
let interval = model.intervalVar({ name: "T" + (i+1), length: tasks[i].length} );
taskVars.push(interval);
if (tasks[i].money < 0) {
// Task costs some money:
steps.push(model.stepAtStart(interval, tasks[i].money));
} else {
// Tasks makes some money:
steps.push(model.stepAtEnd(interval, tasks[i].money));
}
}

// The initial budget increases the cumul at time 0:
steps.push(model.stepAt(0, budget));
// The money must be non-negative at any time:
model.ge(model.sum(steps), 0);
// Only one task at a time:
model.noOverlap(taskVars);

// Minimize the maximum of the ends (makespan):
model.max(taskVars.map(t => t.end())).minimize();

let result = await model.solve({ searchType: "FDS"});

See


stepFunction()

stepFunction(values: number[][]): IntStepFunction

Creates a new integer step function.

Parameters

ParameterTypeDescription
valuesnumber[][]An array of points defining the step function in the form [[x0, y0], [x1, y1], ..., [xn, yn]], where xi and yi are integers. The array must be sorted by xi

Returns

IntStepFunction

The created step function

Remarks

Integer step function is a piecewise constant function defined on integer values in range IntVarMin to IntVarMax. The function is defined as follows:

  • f(x)=0f(x) = 0 for x<x0x < x_0,
  • f(x)=yif(x) = y_i for xix<xi+1x_i \leq x < x_{i+1}
  • f(x)=ynf(x) = y_n for xxnx \geq x_n.

Step functions can be used in the following ways:


sum()

Call Signature

sum(args: (number | IntExpr)[]): IntExpr

Creates an integer expression for the sum of the arguments.

Parameters
ParameterTypeDescription
args(number | IntExpr)[]Array of integer expressions to sum.
Returns

IntExpr

The resulting integer expression

Remarks

Absent arguments are ignored (treated as zeros). Therefore, the resulting expression is never absent.

Note that binary function Model.plus handles absent values differently. For example, when x is absent then:

  • plus(x, 3) is absent.
  • sum([x, 3]) is 3.
Example

Let's consider a set of optional tasks. Due to limited resources and time, only some of them can be executed. Every task has a profit, and we want to maximize the total profit from the executed tasks.

// Lengths and profits of the tasks:
const lengths = [10, 20, 15, 30, 20, 25, 30, 10, 20, 25];
const profits = [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];

let model = new CP.Model;
let tasks: CP.IntervalVar[] = [];
// Profits of individual tasks. The value will be zero if the task is not executed.
let taskProfits: CP.IntExpr[] = [];

for (let i = 0; i < lengths.length; i++) {
// All tasks must finish before time 100:
let task = model.intervalVar({ name: "Task" + i, optional: true, length: lengths[i], end: [, 100]});
tasks.push(task);
taskProfits.push(model.times(task.presence(), profits[i]));
}
model.sum(taskProfits).maximize();
// Tasks cannot overlap:
model.noOverlap(tasks);

let result = await model.solve({ searchType: "FDS" });

Call Signature

sum(args: CumulExpr[]): CumulExpr

Sum of cumulative expressions.

Parameters
ParameterTypeDescription
argsCumulExpr[]Array of cumulative expressions to sum.
Returns

CumulExpr

The resulting cumulative expression

Remarks

Computes the sum of cumulative functions. The sum can be used, e.g., to combine contributions of individual tasks to total resource consumption.

Limitation: Currently, pulse-based and step-based cumulative expressions cannot be mixed. All expressions in the sum must be either pulse-based or step-based.


times()

times(lhs: number | IntExpr, rhs: number | IntExpr): IntExpr

Creates a multiplication of the two integer expressions, i.e. lhs * rhs.

Parameters

ParameterTypeDescription
lhsnumber | IntExprThe first integer expression.
rhsnumber | IntExprThe second integer expression.

Returns

IntExpr

The resulting integer expression

Remarks

If one of the arguments has value absent, then the resulting expression also has value absent.

Same as IntExpr.times.