Skip to main content

Class: Model

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

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

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

Available modeling elements

Variables

Interval variables can be created by function intervalVar.

Basic integer expressions

  • startOf: start of an interval variable (optional integer expression).
  • startOr: start of an interval variable or a constant when it is absent.
  • endOf: end of an interval variable (optional integer expression).
  • endOr: end of an interval variable or a constant when it is absent.
  • lengthOf: length of an interval variable (optional integer expression).
  • lengthOr: length of an interval variable or a constant when it is absent.
  • guard: replaces absent value by a constant.

Integer arithmetics

  • plus: addition.
  • minus: subtraction.
  • neg: negation (changes sign).
  • times: multiplication.
  • div: division (rounds to zero).
  • abs: absolute value.
  • min2: minimum of two integer expressions.
  • min: minimum of an array of integer expressions.
  • max2: maximum of two integer expressions.
  • max: maximum of an array of integer expressions.
  • sum: sum of an array of integer expressions.

Comparison operators for integer expressions

  • eq: equality.
  • ne: inequality.
  • lt: less than.
  • le: less than or equal to.
  • gt: greater than.
  • ge: greater than or equal to.
  • identity: constraints two integer expressions to be equal including the presence status.

Boolean operators

Functions returning BoolExpr

  • presenceOf: whether the argument is present or absent.
  • inRange: whether an integer expression is within the given range

Basic constraints on interval variables

Disjunction (noOverlap)

  • sequenceVar: sequence variable over a set of interval variables.
  • noOverlap: constraints a set of interval variables to not overlap (possibly with transition times).

Basic cumulative expressions

  • pulse: changes value during the interval variable.
  • stepAtStart: changes value at the start of the interval variable.
  • stepAtEnd: changes value at the end of the interval variable.
  • stepAt: changes value at a given time.

Combining cumulative expressions

Constraints on cumulative expressions

  • cumulGe: greater than or equal to a constant.
  • cumulLe: less than or equal to a constant.

Objective

  • minimize: minimize an integer expression.
  • maximize: maximize an integer expression.

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 it cannot be interrupted. Moreover, each tasks 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.cumulSum(workerUsage).cumulLe(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 CP.solve(model, {
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.bestSolution!;
// Note that in evaluation version of the solver, the variable values //
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.getName() + " starts at " + );
else
console.log("Task " + task.getName() + " is absent (not scheduled).")
}
}

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

See

Constructors

constructor

new Model(name?)

Creates a new empty model.

Naming the model is optional. The main purpose of the name is to distinguish between different models during benchmarking (see benchmark).

Parameters

NameTypeDescription
name?stringName of the model.

Methods

abs

abs(arg): IntExpr

Creates an integer expression which is absolute value of arg.

Parameters

NameType
argnumber | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.abs.


alternative

alternative(main, options): void

Creates alternative constraint between interval variables.

Parameters

NameTypeDescription
mainIntervalVarThe main interval variable.
optionsIntervalVar[]Array of optional interval variables to chose from.

Returns

void

Remarks

Alternative constraint is a way to model various kind of alternative 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 a task T that can be done by worker 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. The remaining tasks are omitted in the model below though. 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(arg1, arg2): BoolExpr

Logical AND of boolean expressions arg1 and arg2.

Parameters

NameType
arg1boolean | BoolExpr
arg2boolean | BoolExpr

Returns

BoolExpr

Remarks

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

Same as BoolExpr.and.


constraint

constraint(constraint): void

Creates a constraint from a boolean expression that must be satisfied in the solution.

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

Parameters

NameTypeDescription
constraintboolean | BoolExprThe boolean expression to turn into a constraint.

Returns

void

A constraint that must be satisfied in the solution.

Remarks

A boolean expression that is not turned into a constraint can have arbitrary value in the solution.

Example

In the following example, we create a boolean expression endsBefore50 that checks whether the end of interval variable x is before 50. We don't turn it into a constraint yet:

let model = new CP.Model();
let x = model.intervalVar({ name: "x", length: 10, optional: true });
let endsBefore50 = x.end().le(50);

Because endsBefore50 is not a constraint, it can take arbitrary value in a solution, in particular:

  1. endsBefore50 is true if x is present and its end is less than or equal to 50.
  2. endsBefore50 is false if x is present and its end is greater than 50.
  3. endsBefore50 is absent if x is absent.

Now we turn endsBefore50 into a constraint:

model.constraint(endsBefore50);

When endsBefore50 is a constraint, it can only be true or absent in the solution. Therefore, cases 1 and 3 above can happen but case 2 cannot.

Difference between constraints and boolean expressions

Boolean expressions can take arbitrary value (true, false, or absent) and can be combined into composed expressions (e.g. using and or or).

Constraints can only be true or absent (in a solution) and cannot be combined into composed expressions.

Some functions create constraints directly, e.g. noOverlap. Then, it is not necessary to to pass them to function constraint. It is also not possible to combine constraints into composed expressions such as or(noOverlap(..), noOverlap(..)).


cumulGe

cumulGe(cumul, minCapacity): void

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

Parameters

NameType
cumulCumulExpr
minCapacitynumber

Returns

void

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 stepAtStart for an example with cumulGe.

See


cumulLe

cumulLe(cumul, maxCapacity): void

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

Parameters

NameType
cumulCumulExpr
maxCapacitynumber

Returns

void

Remarks

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

See


cumulMinus

cumulMinus(lhs, rhs): CumulExpr

Subtraction of two cumulative expressions.

Parameters

NameType
lhsCumulExpr
rhsCumulExpr

Returns

CumulExpr

Remarks

Computes subtraction of two cumulative functions.

Formal definition

Let result = cumulMinus(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.

cumulMinus(lhs, rhs) is the same as cumulSum([lhs, cumulNeg(rhs)]).

See


cumulNeg

cumulNeg(arg): CumulExpr

Negation of a cumulative expression.

Parameters

NameType
argCumulExpr

Returns

CumulExpr

Remarks

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

See


cumulPlus

cumulPlus(lhs, rhs): CumulExpr

Addition of two cumulative expressions.

Parameters

NameType
lhsCumulExpr
rhsCumulExpr

Returns

CumulExpr

Remarks

Computes addition of two cumulative functions.

Formal definition

Let result = cumulPlus(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.

cumulPlus(lhs, rhs) is the same as cumulSum([lhs, rhs]).

See


cumulSum

cumulSum(array): CumulExpr

Sum of cumulative expressions.

Parameters

NameType
arrayCumulExpr[]

Returns

CumulExpr

Remarks

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

See

cumulPlus, cumulMinus, cumulNeg for other ways to combine cumulative functions.


div

div(arg1, arg2): IntExpr

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

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.div.


endAtEnd

endAtEnd(predecessor, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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


endOf

endOf(interval): IntExpr

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

Parameters

NameType
intervalIntervalVar

Returns

IntExpr

Remarks

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

Example

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

let model = new CP.Model;
let x = model.intervalVar({ name: "x", ... });
let y = model.intervalVar({ name: "y", ... });
model.constraint(model.endOf(x).plus(10).le(model.startOf(y)));
model.constraint(model.lengthOf(x).le(model.lengthOf(y)));

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

See

  • end is equivalent function on IntervalVar.
  • Function endOr is a similar function that replaces value absent by a constant.

endOr

endOr(interval, absentValue): IntExpr

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

Parameters

NameType
intervalIntervalVar
absentValuenumber

Returns

IntExpr

Remarks

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

See


eq

eq(arg1, arg2): BoolExpr

Creates Boolean expression arg1 = arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.eq.


ge

ge(arg1, arg2): BoolExpr

Creates Boolean expression arg1 arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks If

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.ge.


getIntervalVars

getIntervalVars(): IntervalVar[]

Returns an array of all interval variables in the model.

Returns

IntervalVar[]


getName

getName(): undefined | string

Returns the name of the model. When no name was set, returns undefined.

Returns

undefined | string


gt

gt(arg1, arg2): BoolExpr

Creates Boolean expression arg1 > arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.gt.


guard

guard(arg, absentValue?): IntExpr

Creates an expression that replaces value absent by a constant.

Parameters

NameTypeDefault value
argnumber | IntExprundefined
absentValuenumber0

Returns

IntExpr

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(arg1, arg2): void

Constraints arg1 and arg2 to be identical, including their presence status.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

void

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(arg1, arg2): BoolExpr

Logical implication of two boolean expressions, that is arg1 implies arg2.

Parameters

NameType
arg1boolean | BoolExpr
arg2boolean | BoolExpr

Returns

BoolExpr

Remarks

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

Same as BoolExpr.implies.


inRange

inRange(arg, lb, ub): BoolExpr

Creates Boolean expression lb arg ub.

Parameters

NameType
argnumber | IntExpr
lbnumber
ubnumber

Returns

BoolExpr

Remarks

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.inRange.


intervalVar

intervalVar(params): IntervalVar

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

An interval variable represents an unknown interval (a task, operation, action) that the solver assign a value in such a way 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

NameTypeDescription
paramsObject-
params.end?number | [number?, number?]Constraints the end of the interval.
params.length?number | [number?, number?]Constraints the length of the interval.
params.name?stringThe name of the interval variable. The default is undefined.
params.optional?booleanWhether the interval variable is optional (can take value absent). The default is false.
params.start?number | [number?, number?]Constraints the start of the interval.

Returns

IntervalVar

The created interval variable.

Remarks

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:
const x = model.intervalVar({ start: 0, length: [10, 20], name: "x" });

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

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

See

IntervalVar

intervalVar(name?): IntervalVar

Creates a new present interval variable with the given name and adds it to the model.

Parameters

NameTypeDescription
name?stringThe name of the interval variable.

Returns

IntervalVar

The created interval variable.

Remarks

The default range for start, end and length is 0 to IntervalMax.

Example

const model = new CP.Model();

// Create an interval variable with a name:
const x = model.intervalVar("x");

// Create unnamed interval variable (the name will be undefined):
const interval2 = model.intervalVar();

See


le

le(arg1, arg2): BoolExpr

Creates Boolean expression arg1 arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.le.


lengthOf

lengthOf(interval): IntExpr

Creates an integer expression for the length of an interval variable.

Parameters

NameType
intervalIntervalVar

Returns

IntExpr

Remarks

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

Example

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

let model = new CP.Model;
let x = model.intervalVar({ name: "x", ... });
let y = model.intervalVar({ name: "y", ... });
model.constraint(model.endOf(x).plus(10).le(model.startOf(y)));
model.constraint(model.lengthOf(x).le(model.lengthOf(y)));

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

See

  • length is equivalent function on IntervalVar.
  • Function lengthOr is a similar function that replaces value absent by a constant.

lengthOr

lengthOr(interval, absentValue): IntExpr

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

Parameters

NameType
intervalIntervalVar
absentValuenumber

Returns

IntExpr

Remarks

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

See


lt

lt(arg1, arg2): BoolExpr

Creates Boolean expression arg1 < arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.lt.


max

max(args): IntExpr

Creates an integer expression for the maximum of the arguments.

Parameters

NameType
args(number | IntExpr)[]

Returns

IntExpr

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 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 not included).

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 max2.
  • Function span constraints interval variable to start and end at minimum and maximum of the given set of intervals.

max2

max2(arg1, arg2): IntExpr

Creates an integer expression which is the maximum of arg1 and arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

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


maximize

maximize(arg): void

Maximize the provided expression. I.e. search for a solution that achieves the maximal value of the expression.

Parameters

NameType
argnumber | IntExpr

Returns

void

Remarks

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 CP.solve(model);

See

minimize


min

min(args): IntExpr

Creates an integer expression for the minimum of the arguments.

Parameters

NameType
args(number | IntExpr)[]

Returns

IntExpr

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 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) then its end time is absent. And therefore the absent task is not included in the minimum.

See

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

min2

min2(arg1, arg2): IntExpr

Creates an integer expression which is the minimum of arg1 and arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

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


minimize

minimize(arg): void

Minimize the provided expression. I.e. search for a solution that achieves the minimal value of the expression.

Parameters

NameType
argnumber | IntExpr

Returns

void

Remarks

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 CP.solve(model);

See

maximize


minus

minus(arg1, arg2): IntExpr

Creates a subtraction of the two integer expressions, i.e. arg1 + arg2.@remarks

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

Same as IntExpr.minus.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr


ne

ne(arg1, arg2): BoolExpr

Creates Boolean expression arg1 arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function constraint to create a constraint from this expression.

Same as IntExpr.ne.


neg

neg(arg): IntExpr

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

Parameters

NameType
argnumber | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.neg.


noOverlap

noOverlap(intervals, transitions?): void

Constrain a set of interval variables to not overlap.

Parameters

NameTypeDescription
intervalsIntervalVar[]An array of interval variables to constrain.
transitions?number[][]A 2D square array of minimum transition times between the intervals. The size of the array must be equal to the number of interval variables.

Returns

void

Remarks

This function constrains a set of interval variables to 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) and therefore 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. 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() + transition[i][j] is less than or equal to y.start().
  2. y.end() + transition[j][i] is less than or equal to x.start().

Where i and j are indices of x and y in the array of interval variables.

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

Functionally this constraint is the same as SequenceVar.noOverlap and Model.noOverlap(SequenceVar, ...). The difference is that this function takes an array of interval variables as an argument instead of a sequence variable.

Example

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

Let's consider a set of tasks that must be performed by a single machine. The machine is able to 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 such that the number of missed deadlines is minimized.

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

let model = new CP.Model;

// An interval variable for each task. Begin with an empty array:
let taskVars: CP.IntervalVar[] = [];
// A boolean expression that is true if the task is late:
let isLate: CP.BoolExpr[] = [];

// Fill the arrays:
for (let i = 0; i < tasks.length; i++) {
let task = tasks[i];
let taskVar = model.intervalVar({ name: "Task" + i, length: task.length});
taskVars.push(taskVar);
isLate.push(model.ge(taskVar.end(), task.deadline));
}

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

await CP.solve(model, { searchType: "FDS" });

noOverlap(sequence, transitions?): void

Constrain a set of interval variables (forming a sequence variable) to not overlap.

Parameters

NameTypeDescription
sequenceSequenceVarA sequence variable to constrain. The sequence is formed by set of interval variables.
transitions?number[][]A 2D array of minimum transition times between the intervals.

Returns

void

Remarks

This function is the same as noOverlap, only sequence variable is passed as an argument instead. See the documentation for noOverlap for details.

There is also Model.noOverlap(IntervalVar[], ...) that takes an array of interval variables instead of a sequence variable.


not

not(arg): BoolExpr

Negation of the boolean expression arg.

Parameters

NameType
argboolean | BoolExpr

Returns

BoolExpr

Remarks

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

Same as BoolExpr.not.


or

or(arg1, arg2): BoolExpr

Logical OR of boolean expressions arg1 and arg2.

Parameters

NameType
arg1boolean | BoolExpr
arg2boolean | BoolExpr

Returns

BoolExpr

Remarks

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

Same as BoolExpr.or.


plus

plus(arg1, arg2): IntExpr

Creates an addition of the two integer expressions, i.e. arg1 + arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.plus.


presenceOf

presenceOf(arg): BoolExpr

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

Parameters

NameTypeDescription
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.constraint(model.presenceOf(x).eq(model.presenceOf(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.constraint(model.presenceOf(x).eq(model.presenceOf(y)));
// x.end <= y.start:
let isBefore = x.end().le(y.start());
model.constraint(isBefore);

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 presenceOf, the solver could not propagate that because in this case one of the intervals can be absent and the other one present (and so the value of isBefore would be absent and the constraint would be satisfied).

In order to achieve good propagation, it is recommended to use binary constraints over presenceOf when possible. E.g. use multiple binary constraints instead of a single big constraint.


pulse

pulse(interval, height): CumulExpr

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

Parameters

NameType
intervalIntervalVar
heightnumber | IntExpr

Returns

CumulExpr

Remarks

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

Formal definition

Pulse creates a cumulative function which have the value:

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

If interval is absent then the pulse is 0 everywhere.

Cumulative functions can be combined together using cumulPlus, cumulMinus, cumulNeg and cumulSum. The minimum and the maximum height of a cumulative function can be constrained using cumulLe and cumulGe.

info

Pulses with variable heights (i.e. with height given as IntExpr) are not supported yet.

Example

Lets consider a set of tasks and a group of 3 workers. Each task requires certain number of workers (nbWorkersNeeded). 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, nbWorkersNeeded: 3},
{ length: 20, nbWorkersNeeded: 2},
{ length: 15, nbWorkersNeeded: 1},
{ length: 30, nbWorkersNeeded: 2},
{ length: 20, nbWorkersNeeded: 1},
{ length: 25, nbWorkersNeeded: 2},
{ length: 10, nbWorkersNeeded: 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].nbWorkersNeeded));
// Store the end of the task:
ends.push(task.end());
}

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

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

See


sequenceVar

sequenceVar(intervals, types?): SequenceVar

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

Parameters

NameTypeDescription
intervalsIntervalVar[]Interval variables that will form the sequence in the solution.
types?number[]Types of the intervals, used in particular for transition times.

Returns

SequenceVar

Remarks

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

Types can be used to mark intervals that have similar properties, in particular they behave the same from the point of view 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.

Types

Length of the array types must 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


setName

setName(name): void

Assigns a name to the model. It overwrites any name that was previously set, e.g. in the Model constructor.

Naming the model is optional. The main purpose of the name is to distinguish between different models during benchmarking (see benchmark).

Parameters

NameType
namestring

Returns

void


span

span(main, covered): void

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

Parameters

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

Returns

void

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 on interval in covered is present and:

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

Example

Lets consider composite task T that 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.


startAtEnd

startAtEnd(predecessor, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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, successor, delay?): void

Creates a precedence constraint between two interval variables.

Parameters

NameTypeDefault value
predecessorIntervalVarundefined
successorIntervalVarundefined
delaynumber | IntExpr0

Returns

void

Remarks

Same as:

model.constraint(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


startOf

startOf(interval): IntExpr

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

Parameters

NameType
intervalIntervalVar

Returns

IntExpr

Remarks

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

Example

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

let model = new CP.Model;
let x = model.intervalVar({ name: "x", ... });
let y = model.intervalVar({ name: "y", ... });
model.constraint(model.endOf(x).plus(10).le(model.startOf(y)));
model.constraint(model.lengthOf(x).le(model.lengthOf(y)));

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

See

  • start is equivalent function on IntervalVar.
  • Function startOr is a similar function that replaces value absent by a constant.

startOr

startOr(interval, absentValue): IntExpr

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

Parameters

NameType
intervalIntervalVar
absentValuenumber

Returns

IntExpr

Remarks

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

See


stepAt

stepAt(x, height): CumulExpr

Creates cumulative function (expression) that changes value at x by the given height.

Parameters

NameType
xnumber
heightnumber | IntExpr

Returns

CumulExpr

Remarks

Function stepAt is functionally the same as stepAtStart and stepAtEnd, but the time of the change is given by parameter x instead of by start/end of an interval variable.

Formal definition

stepAt creates a cumulative function which has the value:

  • 0 before x,
  • height after x.

See


stepAtEnd

stepAtEnd(interval, height): CumulExpr

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

Parameters

NameType
intervalIntervalVar
heightnumber | IntExpr

Returns

CumulExpr

Remarks

Cumulative step functions could be used to model a resource that is consumed or produced and so its amount is changing over time. Example of such resource is a battery, an account balance, a stock of a product, etc.

A stepAtEnd can be used to change the amount of such resource at the end of a given variable. The amount is changed by the given height.

Cumulative steps could be combined together using cumulPlus, cumulMinus, cumulNeg and cumulSum. The minimum and the maximum height of a cumulative function can be constrained using cumulLe and cumulGe.

Formal definition

stepAtEnd creates a cumulative function which has the value:

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

If interval is absent then the created cumulative function is 0 everywhere.

info

Combining pulses (pulse) and steps (stepAtStart, stepAtEnd, stepAt) is not supported yet.

Example

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

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

// The input data:
const initialMoney = 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 money increases the cumul at time 0:
steps.push(model.stepAt(0, initialMoney));
// The money must be non-negative at any time:
model.cumulGe(model.cumulSum(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 CP.solve(model, { searchType: "FDS"});

See


stepAtStart

stepAtStart(interval, height): CumulExpr

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

Parameters

NameType
intervalIntervalVar
heightnumber | IntExpr

Returns

CumulExpr

Remarks

Cumulative step functions could be used to model a resource that is consumed or produced and so its amount is changing over time. Example of such resource is a battery, an account balance, a stock of a product, etc.

A stepAtStart can be used to change the amount of such resource at the start of a given variable. The amount is changed by the given height.

Cumulative steps could be combined together using cumulPlus, cumulMinus, cumulNeg and cumulSum. The minimum and the maximum height of a cumulative function can be constrained using cumulLe and cumulGe.

Formal definition

stepAtStart creates a cumulative function which has the value:

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

If interval is absent then the created cumulative function is 0 everywhere.

info

Combining pulses (pulse) and steps (stepAtStart, stepAtEnd, stepAt) is not supported yet.

Example

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

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

// The input data:
const initialMoney = 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 money increases the cumul at time 0:
steps.push(model.stepAt(0, initialMoney));
// The money must be non-negative at any time:
model.cumulGe(model.cumulSum(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 CP.solve(model, { searchType: "FDS"});

See


sum

sum(args): IntExpr

Creates in integer expression for the sum of the arguments.

Parameters

NameType
args(number | IntExpr)[]

Returns

IntExpr

Remarks

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

Note that binary function 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 of 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 CP.solve(model, { searchType: "FDS" });

times

times(arg1, arg2): IntExpr

Creates a multiplication of the two integer expressions, i.e. arg1 * arg2.

Parameters

NameType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.times.