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 must 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, integer variables by function intVar.

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).
  • position: returns the position of an interval variable in a sequence.

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.

Mapping/batching

  • itvMapping: map tasks (interval variables) to slots (other interval variables).

Constraints on integer variables/expressions

  • pack: pack items of various sizes into a set of bins.

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 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.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 the evaluation 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.getName() + " starts at " + );
else
console.log("Task " + task.getName() + " is absent (not scheduled).")
}
}

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

See

Constructors

new Model()

new Model(name?: string): Model

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

ParameterTypeDescription
name?stringName of the model.

Returns

Model

Methods

abs()

abs(arg: number | IntExpr): IntExpr

Creates an integer expression which is absolute value of arg.

Parameters

ParameterType
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: IntervalVar, options: IntervalVar[]): void

Creates alternative constraint between interval variables.

Parameters

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

Returns

void

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

Logical AND of boolean expressions arg1 and arg2.

Parameters

ParameterType
arg1boolean | BoolExpr
arg2boolean | BoolExpr

Returns

BoolExpr

Remarks

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

Same as BoolExpr.and.


constraint()

constraint(constraint: boolean | BoolExpr): 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

ParameterTypeDescription
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, passing them to function constraint is unnecessary. It is also not possible to combine constraints into composed expressions such as or(noOverlap(..), noOverlap(..)).


cumulGe()

cumulGe(cumul: CumulExpr, minCapacity: number): void

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

Parameters

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

See


cumulLe()

cumulLe(cumul: CumulExpr, maxCapacity: number): void

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

Parameters

ParameterType
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 the number of workers working simultaneously, limit the maximum amount of material on stock, etc. See Model.pulse for an example with cumulLe.

See


cumulMinus()

cumulMinus(lhs: CumulExpr, rhs: CumulExpr): CumulExpr

Subtraction of two cumulative expressions.

Parameters

ParameterType
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): CumulExpr

Negation of a cumulative expression.

Parameters

ParameterType
argCumulExpr

Returns

CumulExpr

Remarks

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

See


cumulPlus()

cumulPlus(lhs: CumulExpr, rhs: CumulExpr): CumulExpr

Addition of two cumulative expressions.

Parameters

ParameterType
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[]): CumulExpr

Sum of cumulative expressions.

Parameters

ParameterType
arrayCumulExpr[]

Returns

CumulExpr

Remarks

Computes the 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: number | IntExpr, arg2: number | IntExpr): IntExpr

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

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.div.


endAtEnd()

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

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar, successor: IntervalVar, delay: number | IntExpr): void

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar, successor: IntervalVar, delay: number | IntExpr): void

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar, successor: IntervalVar, delay: number | IntExpr): void

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar): IntExpr

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

Parameters

ParameterType
intervalIntervalVar

Returns

IntExpr

Remarks

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

Example

In the following example, we constraint interval variable y to start after the end of y 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.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


endOr()

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

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

Parameters

ParameterType
intervalIntervalVar
absentValuenumber

Returns

IntExpr

Remarks

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

See


eq()

eq(arg1: number | IntExpr, arg2: number | IntExpr): BoolExpr

Creates Boolean expression arg1 = arg2.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function Model.constraint to create a constraint from this expression.

Same as IntExpr.eq.


forbidEnd()

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

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

Parameters

ParameterType
intervalIntervalVar
funcIntStepFunction

Returns

void

Remarks

This function is equivalent to:

  model.constraint(model.ne(model.stepFunctionEval(func, interval.end()), 0));

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

See


forbidExtent()

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

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

Parameters

ParameterType
intervalIntervalVar
funcIntStepFunction

Returns

void

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

See


forbidStart()

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

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

Parameters

ParameterType
intervalIntervalVar
funcIntStepFunction

Returns

void

Remarks

This function is equivalent to:

  model.constraint(model.ne(model.stepFunctionEval(func, interval.start()), 0));

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

See


ge()

ge(arg1: number | IntExpr, arg2: number | IntExpr): BoolExpr

Creates Boolean expression arg1arg2.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function Model.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, it returns undefined.

Returns

undefined | string


gt()

gt(arg1: number | IntExpr, arg2: number | IntExpr): BoolExpr

Creates Boolean expression arg1 > arg2.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function Model.constraint to create a constraint from this expression.

Same as IntExpr.gt.


guard()

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

Creates an expression that replaces value absent by a constant.

Parameters

ParameterTypeDefault 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: number | IntExpr, arg2: number | IntExpr): void

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

Parameters

ParameterType
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: boolean | BoolExpr, arg2: boolean | BoolExpr): BoolExpr

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

Parameters

ParameterType
arg1boolean | BoolExpr
arg2boolean | BoolExpr

Returns

BoolExpr

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

ParameterType
argnumber | IntExpr
lbnumber
ubnumber

Returns

BoolExpr

Remarks

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

Use function Model.constraint to create a constraint from this expression.

Same as IntExpr.inRange.


intVar()

intVar(params: {name: string;optional: boolean;range: number | [number?, number?]; }): IntVar

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

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.

Parameters

ParameterTypeDescription
paramsobject-
params.name?stringThe name of the variable. The default is undefined.
params.optional?booleanWhether the variable is optional (can take value absent). The default is false.
params.range?number | [number?, number?]Constraints the variable's value to be in the given range.

Returns

IntVar

The created integer variable.

Remarks

The parameter params.range 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 is 0 to IntVarMax. If a range is specified but one of the values is undefined (e.g., range: [, 100]), then the default value is used instead (in our case, 0).

Example

let model = new CP.Model();

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

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

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

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.

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

ParameterTypeDescription
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:
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


itvMapping()

itvMapping(tasks: IntervalVar[], slots: IntervalVar[], indices: (number | IntExpr)[]): void

Map tasks on slots according to the indices array.

Parameters

ParameterTypeDescription
tasksIntervalVar[]Array of interval variables to map.
slotsIntervalVar[]Array of interval variables to map to.
indices(number | IntExpr)[]Array of integer expressions that specify the mapping (of the same length as tasks).

Returns

void

Remarks

Each task is synchronized with the slot it is assigned to. Multiple tasks can be assigned to the same slot. A slot without any task is absent. Absent tasks are not assigned to any slot (their index value is absent). Slots are sorted by both start and end. Absent slots are at the end of the array.

The constraint can be used to form batches of synchronized tasks (so-called p-batching). In this case slots corresponds to batches. The size of the batches can be limited using, e.g., Model.pack constraint.

Formal definition

Let TT be the number of tasks (the length of the array tasks). The number of the indices must also be TT (arrays tasks and indices must have the same length). Let tasks[t] be one of the tasks, i.e., t0,1,T1\mathtt{t} \in {0,1,\dots T-1}. Then indices[t] is the index of the slot the task tasks[t] is assigned to. Only present tasks are assigned:

t0,,T1:presenceOf(tasks[t])presenceOf(indices[t])\mathtt{ \forall t \in \mathrm{0,\dots,T-1:} \quad presenceOf(tasks[t]) \,\Leftrightarrow\, presenceOf(indices[t]) }

Each task is synchronized with the slot to which it is assigned:

t0,,T1 such that tasks[t]absent:slots[indices[t]]absentstartOf(tasks[t])=startOf(slots[indices[t]])endOf(tasks[t])=endOf(slots[indices[t]])\begin{aligned} \mathtt{\forall t \in \mathrm{0,\dots,T-1} \text{ such that } tasks[t] \ne \text{absent:}} \\ \mathtt{slots[indices[t]]} &\ne \textrm{absent} \\ \mathtt{startOf(tasks[t])} &= \mathtt{startOf(slots[indices[t]]) }\\ \mathtt{endOf(tasks[t])} &= \mathtt{endOf(slots[indices[t]])} \end{aligned}

A slot is present if and only if there is a task assigned to it:

s0,,S1:  presenceOf(tasks[s])    (t0,,T1:indices[t]=s)\forall \mathtt{s} \in 0,\dots,S-1:\; \mathtt{presenceOf(tasks[s])} \;\Leftrightarrow\; (\exists \mathtt{t} \in 0,\dots,T-1: \mathtt{indices[t]=s})

Absent slots are positioned at the end of the array:

s1,,S1:presenceOf(slots[s])presenceOf(slots[s1])\mathtt{ \forall s \in \mathrm{1,\dots,S-1}:\, presenceOf(slots[s]) \Rightarrow presenceOf(slots[s-1]) }

Present slots are sorted by both start and end:

s1,,S1 such that slots[s]absent:startOf(slots[s1])startOf(slots[s])endOf(slots[s1])endOf(slots[s])\begin{aligned} \mathtt{\forall s \in \mathrm{1,\dots,S-1} \text{ such that } slots[s] \ne \text{absent:}} \\ \mathtt{startOf(slots[s-1])} &\le \mathtt{startOf(slots[s]) } \\ \mathtt{endOf(slots[s-1])} &\le \mathtt{endOf(slots[s])} \end{aligned}

The amount of the propagation for this constraint can be controlled by parameter Parameters.packPropagationLevel.

See

Model.pack for limiting the amount of tasks assigned to a slot.


le()

le(arg1: number | IntExpr, arg2: number | IntExpr): BoolExpr

Creates Boolean expression arg1arg2.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function Model.constraint to create a constraint from this expression.

Same as IntExpr.le.


lengthOf()

lengthOf(interval: IntervalVar): IntExpr

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

Parameters

ParameterType
intervalIntervalVar

Returns

IntExpr

Remarks

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

Example

In the following example, we constraint interval variable y to start after the end of y 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.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


lengthOr()

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

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

Parameters

ParameterType
intervalIntervalVar
absentValuenumber

Returns

IntExpr

Remarks

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

See


lt()

lt(arg1: number | IntExpr, arg2: number | IntExpr): BoolExpr

Creates Boolean expression arg1 < arg2.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function Model.constraint to create a constraint from this expression.

Same as IntExpr.lt.


max()

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

Creates an integer expression for the maximum of the arguments.

Parameters

ParameterType
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 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(arg1: number | IntExpr, arg2: number | IntExpr): IntExpr

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

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

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): void

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

Parameters

ParameterTypeDescription
exprnumber | IntExprThe expression to maximize.

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: (number | IntExpr)[]): IntExpr

Creates an integer expression for the minimum of the arguments.

Parameters

ParameterType
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 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(arg1: number | IntExpr, arg2: number | IntExpr): IntExpr

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

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

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): void

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

Parameters

ParameterTypeDescription
exprnumber | IntExprThe expression to minimize.

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: number | IntExpr, arg2: number | IntExpr): 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 also has value absent.

Same as IntExpr.minus.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr


ne()

ne(arg1: number | IntExpr, arg2: number | IntExpr): BoolExpr

Creates Boolean expression arg1arg2.

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

BoolExpr

Remarks

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

Use function Model.constraint to create a constraint from this expression.

Same as IntExpr.ne.


neg()

neg(arg: number | IntExpr): IntExpr

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

Parameters

ParameterType
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)

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

Constrain a set of interval variables not to overlap.

Parameters
ParameterTypeDescription
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 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() + 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 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.

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)

noOverlap(sequence: SequenceVar, transitions?: number[][]): void

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

Parameters
ParameterTypeDescription
sequenceSequenceVarA sequence variable to constrain. The sequence is formed by a 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 SequenceVar.noOverlap, only sequence variable is passed as an argument instead. See the documentation for SequenceVar.noOverlap for details.

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


not()

not(arg: boolean | BoolExpr): BoolExpr

Negation of the boolean expression arg.

Parameters

ParameterType
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: boolean | BoolExpr, arg2: boolean | BoolExpr): BoolExpr

Logical OR of boolean expressions arg1 and arg2.

Parameters

ParameterType
arg1boolean | BoolExpr
arg2boolean | BoolExpr

Returns

BoolExpr

Remarks

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

Same as BoolExpr.or.


pack()

pack(load: (number | IntExpr)[], where: (number | IntExpr)[], sizes: number[]): void

TODO:0

Parameters

ParameterType
load(number | IntExpr)[]
where(number | IntExpr)[]
sizesnumber[]

Returns

void


plus()

plus(arg1: number | IntExpr, arg2: number | IntExpr): IntExpr

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

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.plus.


position()

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

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

Parameters

ParameterType
intervalIntervalVar
sequenceSequenceVar

Returns

IntExpr

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


presenceOf()

presenceOf(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.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 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).

To achieve good propagation, it is recommended to use binary constraints over presenceOf 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

ParameterType
intervalIntervalVar
heightnumber | IntExpr

Returns

CumulExpr

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

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 Model.cumulPlus, Model.cumulMinus, Model.cumulNeg and Model.cumulSum. A cumulative function's minimum and maximum height can be constrained using Model.cumulLe and Model.cumulGe.

Examples

Let us consider a set of tasks and a group of 3 workers. Each task requires a 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" });

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 workersX, workersY, and workersZ.

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 workersZ 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 workersX = model.intVar({ range: [1, 5], name: "workersX" });
let workersY = model.intVar({ range: [1, 5], name: "workersY" });
let workersZ = model.intVar({ range: [1, 5], name: "workersZ", optional: true });

// workersZ is present if an only if z is present:
model.constraint(z.presence().eq(workersZ.presence()));

let pulseX = model.pulse(x, workersX);
let pulseY = model.pulse(y, workersY);
let pulseZ = model.pulse(z, workersZ);

// There are at most 7 workers at any time:
model.cumulSum([pulseX, pulseY, pulseZ]).cumulLe(7);

// Length of the task depends on the number of workers using the following formula:
// length * workersX = 12
model.constraint(x.length().times(workersX).eq(12));
model.constraint(y.length().times(workersY).eq(12));
model.constraint(z.length().times(workersZ).eq(12));

See


sequenceVar()

sequenceVar(intervals: IntervalVar[], types?: number[]): 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.

Returns

SequenceVar

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.

Types

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


setName()

setName(name: string): void

Assign 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 primary purpose of the name is to distinguish between different models during benchmarking (see benchmark).

Parameters

ParameterType
namestring

Returns

void


span()

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

Constraints 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

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 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.


startAtEnd()

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

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar, successor: IntervalVar, delay: number | IntExpr): void

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar, successor: IntervalVar, delay: number | IntExpr): void

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar, successor: IntervalVar, delay: number | IntExpr): void

Creates a precedence constraint between two interval variables.

Parameters

ParameterTypeDefault 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: IntervalVar): IntExpr

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

Parameters

ParameterType
intervalIntervalVar

Returns

IntExpr

Remarks

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

Example

In the following example, we constraint interval variable y to start after the end of y 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.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


startOr()

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

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

Parameters

ParameterType
intervalIntervalVar
absentValuenumber

Returns

IntExpr

Remarks

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

See


stepAt()

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

Creates cumulative function (expression) that changes value at x by the given height. The height can be positive or negative, and it can be given by a constant or an expression (for example, by Model.intVar).

Parameters

ParameterType
xnumber
heightnumber | IntExpr

Returns

CumulExpr

Remarks

Function stepAt is functionally the same as Model.stepAtStart and Model.stepAtEnd. However, the time of the change is given by the constant value x instead of by the 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: IntervalVar, height: number | IntExpr): CumulExpr

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

Parameters

ParameterType
intervalIntervalVar
heightnumber | IntExpr

Returns

CumulExpr

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 Model.cumulPlus,Model.cumulMinus, Model.cumulNeg and Model.cumulSum. A cumulative function's minimum and maximum height can be constrained using Model.cumulLe and Model.cumulGe.

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.

info

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

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 amount of money initialMoney, 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 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: IntervalVar, height: number | IntExpr): CumulExpr

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

Parameters

ParameterType
intervalIntervalVar
heightnumber | IntExpr

Returns

CumulExpr

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 Model.cumulPlus,Model.cumulMinus, Model.cumulNeg and Model.cumulSum. A cumulative function's minimum and maximum height can be constrained using Model.cumulLe and Model.cumulGe.

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.

info

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

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 amount of money initialMoney, 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 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


stepFunction()

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

Creates a new IntStepFunction.

Parameters

ParameterTypeDescription
valuesnumber[][]An array of points defining the step function in the form [[x0,y0],[x1,y1],,[xn,yn]][[x_0, y_0], [x_1, y_1], \dots, [x_n, y_n]], where xix_i and yiy_i are integers. The array must be sorted by xix_i.

Returns

IntStepFunction

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:


stepFunctionEval()

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

Evaluates a step function at a given point.

Parameters

ParameterType
funcIntStepFunction
argnumber | IntExpr

Returns

IntExpr

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


stepFunctionSum()

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

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

Parameters

ParameterType
funcIntStepFunction
intervalIntervalVar

Returns

IntExpr

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.stepFunctionSum for the equivalent function on IntStepFunction.


sum()

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

Creates in integer expression for the sum of the arguments.

Parameters

ParameterType
args(number | IntExpr)[]

Returns

IntExpr

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

times()

times(arg1: number | IntExpr, arg2: number | IntExpr): IntExpr

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

Parameters

ParameterType
arg1number | IntExpr
arg2number | IntExpr

Returns

IntExpr

Remarks

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

Same as IntExpr.times.