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

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 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: 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 has also 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, 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: 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 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 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 then the resulting expression has also 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 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


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 has also 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));

That is, 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.intStepFunctionEval(func, interval.start()), 0));

That is, 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 has also 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, 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 has also 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 has also 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.


intervalVar()

intervalVar(params)

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 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
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:
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(name?: string): IntervalVar

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

Parameters
ParameterTypeDescription
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: 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 has also 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 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


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


lexGe()

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

Parameters

ParameterType
lhs(number | IntExpr)[]
rhs(number | IntExpr)[]

Returns

void


lexGt()

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

Parameters

ParameterType
lhs(number | IntExpr)[]
rhs(number | IntExpr)[]

Returns

void


lexLe()

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

Parameters

ParameterType
lhs(number | IntExpr)[]
rhs(number | IntExpr)[]

Returns

void


lexLt()

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

Parameters

ParameterType
lhs(number | IntExpr)[]
rhs(number | IntExpr)[]

Returns

void


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 has also 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 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 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 has also 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) then 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 has also 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 has also 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 has also 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 to not 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 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)

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

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

Parameters
ParameterTypeDescription
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 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 has also value absent.

Same as BoolExpr.or.


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 has also value absent.

Same as IntExpr.plus.


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 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: 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 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 Model.cumulPlus, Model.cumulMinus, Model.cumulNeg and Model.cumulSum. The minimum and the maximum height of a cumulative function can be constrained using Model.cumulLe and Model.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: 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 to further constrain this sequence, for example by specifying sequence-dependent minimum transition times between>

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

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


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.

Parameters

ParameterType
xnumber
heightnumber | IntExpr

Returns

CumulExpr

Remarks

Function stepAt is functionally the same as Model.stepAtStart and Model.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: 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 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 Model.cumulPlus, Model.cumulMinus, Model.cumulNeg and Model.cumulSum. The minimum and the maximum height of a cumulative function 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 interval is absent then 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

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: 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 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 Model.cumulPlus, Model.cumulMinus, Model.cumulNeg and Model.cumulSum. The minimum and the maximum height of a cumulative function 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 interval is absent then 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

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


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 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. That is, 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 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: 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 has also value absent.

Same as IntExpr.times.