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
- alternative: an alternative between multiple interval variables.
- span: span (cover) of a set of interval variables.
- endBeforeEnd, endBeforeStart, startBeforeEnd, startBeforeStart, endAtStart, startAtEnd: precedence constraints.
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
- cumulNeg: negation.
- cumulPlus: addition.
- cumulMinus: subtraction.
- cumulSum: sum of multiple expressions.
Constraints on cumulative expressions
Objective
Example
Our goal is to schedule a set of tasks such that it is finished as soon as possible (i.e. the makespan is minimized). Each task has a fixed duration and 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
Parameter | Type | Description |
---|---|---|
name ? | string | Name of the model. |
Returns
Methods
abs()
Creates an integer expression which is absolute value of arg
.
Parameters
Parameter | Type |
---|---|
arg | number | IntExpr |
Returns
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
Parameter | Type | Description |
---|---|---|
main | IntervalVar | The main interval variable. |
options | IntervalVar [] | 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:
- Interval
main
is absent and alloptions[i]
are absent too. - Interval
main
is present and exactly one ofoptions[i]
is present (the remaining options are absent). Letk
be the index of the present option. Thenmain.start() == options[k].start()
andmain.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
Parameter | Type |
---|---|
arg1 | boolean | BoolExpr |
arg2 | boolean | BoolExpr |
Returns
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
Parameter | Type | Description |
---|---|---|
constraint | boolean | BoolExpr | The 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:
endsBefore50
is true ifx
is present and its end is less than or equal to 50.endsBefore50
is false ifx
is present and its end is greater than 50.endsBefore50
is absent ifx
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
Parameter | Type |
---|---|
cumul | CumulExpr |
minCapacity | number |
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
- CumulExpr.cumulGe for the equivalent function on CumulExpr.
- Model.cumulLe for the opposite constraint.
cumulLe()
cumulLe(
cumul
:CumulExpr
,maxCapacity
:number
):void
Constrains cumulative function cumul
to be everywhere less or equal to maxCapacity
.
Parameters
Parameter | Type |
---|---|
cumul | CumulExpr |
maxCapacity | number |
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
- CumulExpr.cumulLe for the equivalent function on CumulExpr.
- Model.cumulGe for the opposite constraint.
cumulMinus()
Subtraction of two cumulative expressions.
Parameters
Parameter | Type |
---|---|
lhs | CumulExpr |
rhs | CumulExpr |
Returns
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
- CumulExpr.cumulMinus for the equivalent function on CumulExpr.
- cumulSum, cumulPlus, cumulNeg for other ways to combine cumulative functions.
cumulNeg()
Negation of a cumulative expression.
Parameters
Parameter | Type |
---|---|
arg | CumulExpr |
Returns
Remarks
Computes negation of a cumulative function. That is, the resulting function has the opposite values.
See
- CumulExpr.cumulNeg for the equivalent function on CumulExpr.
- cumulSum, cumulPlus, cumulMinus for other ways to combine cumulative functions.
cumulPlus()
Addition of two cumulative expressions.
Parameters
Parameter | Type |
---|---|
lhs | CumulExpr |
rhs | CumulExpr |
Returns
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
- CumulExpr.cumulPlus for the equivalent function on CumulExpr.
- cumulSum, cumulMinus, cumulNeg for other ways to combine cumulative functions.
cumulSum()
Sum of cumulative expressions.
Parameters
Parameter | Type |
---|---|
array | CumulExpr [] |
Returns
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.endAtEnd is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.eq
endAtStart()
endAtStart(
predecessor
:IntervalVar
,successor
:IntervalVar
,delay
:number
|IntExpr
):void
Creates a precedence constraint between two interval variables.
Parameters
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.endAtStart is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.eq
endBeforeEnd()
endBeforeEnd(
predecessor
:IntervalVar
,successor
:IntervalVar
,delay
:number
|IntExpr
):void
Creates a precedence constraint between two interval variables.
Parameters
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.endBeforeEnd is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.le
endBeforeStart()
endBeforeStart(
predecessor
:IntervalVar
,successor
:IntervalVar
,delay
:number
|IntExpr
):void
Creates a precedence constraint between two interval variables.
Parameters
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.endBeforeStart is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.le
endOf()
endOf(
interval
:IntervalVar
):IntExpr
Creates an integer expression for the end of an interval variable.
Parameters
Parameter | Type |
---|---|
interval | IntervalVar |
Returns
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
- IntervalVar.end is equivalent function on IntervalVar.
- Function Model.endOr is a similar function that replaces value absent by a constant.
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
Parameter | Type |
---|---|
interval | IntervalVar |
absentValue | number |
Returns
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
Parameter | Type |
---|---|
interval | IntervalVar |
func | IntStepFunction |
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
- IntervalVar.forbidEnd for the equivalent function on IntervalVar.
- Model.forbidStart for similar function that constrains start an interval variable.
- Model.stepFunctionEval for evaluation of a step function.
forbidExtent()
forbidExtent(
interval
:IntervalVar
,func
:IntStepFunction
):void
Forbid the interval variable to overlap with segments of the function where the value is zero.
Parameters
Parameter | Type |
---|---|
interval | IntervalVar |
func | IntStepFunction |
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 is a segment of the step function where the value is zero then the interval variable either ends before () or starts after (.
See
- IntervalVar.forbidExtent for the equivalent function on IntervalVar.
- Model.forbidStart, Model.forbidEnd for similar functions that constrain start/end of an interval variable.
- Model.stepFunctionEval for evaluation of a step function.
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
Parameter | Type |
---|---|
interval | IntervalVar |
func | IntStepFunction |
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
- IntervalVar.forbidStart for the equivalent function on IntervalVar.
- Model.forbidEnd for similar function that constrains end an interval variable.
- Model.stepFunctionEval for evaluation of a step function.
ge()
ge(
arg1
:number
|IntExpr
,arg2
:number
|IntExpr
):BoolExpr
Creates Boolean expression arg1
≥ arg2
.
Parameters
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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()
Creates an expression that replaces value absent by a constant.
Parameters
Parameter | Type | Default value |
---|---|---|
arg | number | IntExpr | undefined |
absentValue | number | 0 |
Returns
Remarks
The resulting expression is:
- equal to
arg
ifarg
is present - and equal to
absentValue
otherwise (i.e. whenarg
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | 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
Parameter | Type |
---|---|
arg1 | boolean | BoolExpr |
arg2 | boolean | BoolExpr |
Returns
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 lb
≤ arg
≤ ub
.
Parameters
Parameter | Type |
---|---|
arg | number | IntExpr |
lb | number |
ub | number |
Returns
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
Parameter | Type | Description |
---|---|---|
params | object | - |
params.end ? | number | [number? , number? ] | Constraints the end of the interval. |
params.length ? | number | [number? , number? ] | Constraints the length of the interval. |
params.name ? | string | The name of the interval variable. The default is undefined . |
params.optional ? | boolean | Whether 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
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(name)
intervalVar(
name
?:string
):IntervalVar
Creates a new present interval variable with the given name and adds it to the model.
Parameters
Parameter | Type | Description |
---|---|---|
name ? | string | The name of the interval variable. |
Returns
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
- IntervalVar
- IntervalVar.makeOptional, IntervalVar.makePresent and IntervalVar.makeAbsent for changing the presence of the interval.
- IntervalVar.setStart, IntervalVar.setStartMin and IntervalVar.setStartMax for changing the start of the interval.
- IntervalVar.setEnd, IntervalVar.setEndMin and IntervalVar.setEndMax for changing the end of the interval.
- IntervalVar.setLength, IntervalVar.setLengthMin and IntervalVar.setLengthMax for changing the length of the interval.
le()
le(
arg1
:number
|IntExpr
,arg2
:number
|IntExpr
):BoolExpr
Creates Boolean expression arg1
≤ arg2
.
Parameters
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
Parameter | Type |
---|---|
interval | IntervalVar |
Returns
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
- IntervalVar.length is equivalent function on IntervalVar.
- Function Model.lengthOr is a similar function that replaces value absent by a constant.
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
Parameter | Type |
---|---|
interval | IntervalVar |
absentValue | number |
Returns
Remarks
This function is equivalent to lengthOf(interval).guard(absentValue)
.
See
lexGe()
lexGe(
lhs
: (number
|IntExpr
)[],rhs
: (number
|IntExpr
)[]):void
Parameters
Parameter | Type |
---|---|
lhs | (number | IntExpr )[] |
rhs | (number | IntExpr )[] |
Returns
void
lexGt()
lexGt(
lhs
: (number
|IntExpr
)[],rhs
: (number
|IntExpr
)[]):void
Parameters
Parameter | Type |
---|---|
lhs | (number | IntExpr )[] |
rhs | (number | IntExpr )[] |
Returns
void
lexLe()
lexLe(
lhs
: (number
|IntExpr
)[],rhs
: (number
|IntExpr
)[]):void
Parameters
Parameter | Type |
---|---|
lhs | (number | IntExpr )[] |
rhs | (number | IntExpr )[] |
Returns
void
lexLt()
lexLt(
lhs
: (number
|IntExpr
)[],rhs
: (number
|IntExpr
)[]):void
Parameters
Parameter | Type |
---|---|
lhs | (number | IntExpr )[] |
rhs | (number | IntExpr )[] |
Returns
void
lt()
lt(
arg1
:number
|IntExpr
,arg2
:number
|IntExpr
):BoolExpr
Creates Boolean expression arg1
< arg2
.
Parameters
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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()
Creates an integer expression for the maximum of the arguments.
Parameters
Parameter | Type |
---|---|
args | (number | IntExpr )[] |
Returns
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
Parameter | Type | Description |
---|---|---|
expr | number | IntExpr | The 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
min()
Creates an integer expression for the minimum of the arguments.
Parameters
Parameter | Type |
---|---|
args | (number | IntExpr )[] |
Returns
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
Parameter | Type | Description |
---|---|---|
expr | number | IntExpr | The 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
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
ne()
ne(
arg1
:number
|IntExpr
,arg2
:number
|IntExpr
):BoolExpr
Creates Boolean expression arg1
≠ arg2
.
Parameters
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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()
Creates negation of the integer expression, i.e. -arg
.
Parameters
Parameter | Type |
---|---|
arg | number | IntExpr |
Returns
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
Parameter | Type | Description |
---|---|---|
intervals | IntervalVar [] | 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:
- Interval variable
x
ory
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. - Interval variable
x
is beforey
, that is,x.end()
is less than or equal toy.start()
. - Interval variable
y
is beforex
, that is,y.end()
is less than or equal tox.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:
x.end() + transition[i][j]
is less than or equal toy.start()
.y.end() + transition[j][i]
is less than or equal tox.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
Parameter | Type | Description |
---|---|---|
sequence | SequenceVar | A 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()
Negation of the boolean expression arg
.
Parameters
Parameter | Type |
---|---|
arg | boolean | BoolExpr |
Returns
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
Parameter | Type |
---|---|
arg1 | boolean | BoolExpr |
arg2 | boolean | BoolExpr |
Returns
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
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
Parameter | Type | Description |
---|---|---|
arg | number | boolean | IntExpr | IntervalVar | The argument to check for presence in the solution. |
Returns
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
Parameter | Type |
---|---|
interval | IntervalVar |
height | number | IntExpr |
Returns
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
beforeinterval.start()
,height
betweeninterval.start()
andinterval.end()
,0
afterinterval.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.
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
- IntervalVar.pulse is equivalent function on IntervalVar.
- Model.stepAtStart, Model.stepAtEnd, Model.stepAt for other basic cumulative functions.
- Model.cumulLe and Model.cumulGe for constraints on cumulative functions.
sequenceVar()
sequenceVar(
intervals
:IntervalVar
[],types
?:number
[]):SequenceVar
Creates a sequence variable from the provided set of interval variables.
Parameters
Parameter | Type | Description |
---|---|---|
intervals | IntervalVar [] | Interval variables that will form the sequence in the solution. |
types ? | number [] | Types of the intervals, used in particular for transition times. |
Returns
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.
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
- SequenceVar.noOverlap for an example of sequenceVar usage with transition times.
- SequenceVar
- Model.noOverlap
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
Parameter | Type |
---|---|
name | string |
Returns
void
span()
span(
main
:IntervalVar
,covered
:IntervalVar
[]):void
Constraints an interval variable to span (cover) a set of other interval variables.
Parameters
Parameter | Type | Description |
---|---|---|
main | IntervalVar | The spanning interval variable. |
covered | IntervalVar [] | 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 incovered
are absent too. -
Interval variable
main
is present, at least on interval incovered
is present and:main.start()
is equal to the minimum of starting times of all present intervals incovered
.main.end()
is equal to the maximum of ending times of all present intervals incovered
.
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
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.startAtEnd is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.eq
startAtStart()
startAtStart(
predecessor
:IntervalVar
,successor
:IntervalVar
,delay
:number
|IntExpr
):void
Creates a precedence constraint between two interval variables.
Parameters
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.startAtStart is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.eq
startBeforeEnd()
startBeforeEnd(
predecessor
:IntervalVar
,successor
:IntervalVar
,delay
:number
|IntExpr
):void
Creates a precedence constraint between two interval variables.
Parameters
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.startBeforeEnd is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.le
startBeforeStart()
startBeforeStart(
predecessor
:IntervalVar
,successor
:IntervalVar
,delay
:number
|IntExpr
):void
Creates a precedence constraint between two interval variables.
Parameters
Parameter | Type | Default value |
---|---|---|
predecessor | IntervalVar | undefined |
successor | IntervalVar | undefined |
delay | number | IntExpr | 0 |
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
- IntervalVar.startBeforeStart is equivalent function on IntervalVar.
- Model.constraint
- IntervalVar.start, IntervalVar.end
- IntExpr.le
startOf()
startOf(
interval
:IntervalVar
):IntExpr
Creates an integer expression for the start of an interval variable.
Parameters
Parameter | Type |
---|---|
interval | IntervalVar |
Returns
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
- IntervalVar.start is equivalent function on IntervalVar.
- Function Model.startOr is a similar function that replaces value absent by a constant.
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
Parameter | Type |
---|---|
interval | IntervalVar |
absentValue | number |
Returns
Remarks
This function is equivalent to startOf(interval).guard(absentValue)
.
See
stepAt()
Creates cumulative function (expression) that changes value at x
by the given height
.
Parameters
Parameter | Type |
---|---|
x | number |
height | number | IntExpr |
Returns
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
afterx
.
See
- Model.stepAtStart, Model.stepAtEnd for an example with
stepAt
. - Model.cumulLe and Model.cumulGe for constraints on cumulative functions.
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
Parameter | Type |
---|---|
interval | IntervalVar |
height | number | IntExpr |
Returns
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
beforeinterval.end()
,height
afterinterval.end()
.
If interval
is absent then the created cumulative function is 0
everywhere.
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
- IntervalVar.stepAtEnd is equivalent function on IntervalVar.
- Model.stepAtStart, Model.stepAt, Model.pulse for other basic cumulative functions.
- Model.cumulLe and Model.cumulGe for constraints on cumulative functions.
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
Parameter | Type |
---|---|
interval | IntervalVar |
height | number | IntExpr |
Returns
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
beforeinterval.start()
,height
afterinterval.start()
.
If interval
is absent then the created cumulative function is 0
everywhere.
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
- IntervalVar.stepAtStart is equivalent function on IntervalVar.
- Model.stepAtEnd, Model.stepAt, Model.pulse for other basic cumulative functions.
- Model.cumulLe and Model.cumulGe for constraints on cumulative functions.
stepFunction()
stepFunction(
values
:number
[][]):IntStepFunction
Creates a new IntStepFunction.
Parameters
Parameter | Type | Description |
---|---|---|
values | number [][] | An array of points defining the step function in the form , where and are integers. The array must be sorted by . |
Returns
Remarks
Integer step function is a piecewise constant function defined on integer values in range IntVarMin to IntVarMax. The function is defined as follows:
- for ,
- for
- for .
Step functions can be used following ways:
- Function Model.stepFunctionEval evaluates the function at the given point (given as IntExpr).
- Function Model.stepFunctionSum computes a sum (integral) of the function over an IntervalVar.
- Constraints Model.forbidStart and Model.forbidEnd forbid the start/end of an IntervalVar to be in a zero-value interval of the function.
- Constraint Model.forbidExtent forbids the extent of an IntervalVar to be in a zero-value interval of the function.
stepFunctionEval()
stepFunctionEval(
func
:IntStepFunction
,arg
:number
|IntExpr
):IntExpr
Evaluates a step function at a given point.
Parameters
Parameter | Type |
---|---|
func | IntStepFunction |
arg | number | IntExpr |
Returns
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
- IntStepFunction.stepFunctionEval for the equivalent function on IntStepFunction.
- Model.forbidStart, Model.forbidEnd are convenience functions built on top of
stepFunctionEval
.
stepFunctionSum()
stepFunctionSum(
func
:IntStepFunction
,interval
:IntervalVar
):IntExpr
Computes sum of values of the step function func
over the interval interval
.
Parameters
Parameter | Type |
---|---|
func | IntStepFunction |
interval | IntervalVar |
Returns
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()
Creates in integer expression for the sum of the arguments.
Parameters
Parameter | Type |
---|---|
args | (number | IntExpr )[] |
Returns
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
Parameter | Type |
---|---|
arg1 | number | IntExpr |
arg2 | number | IntExpr |
Returns
Remarks
If one of the arguments has value absent then the resulting expression has also value absent.
Same as IntExpr.times.