Interval Variables
IntervalVar represents a scheduled task or activity with a start time, end time, and length. Interval variables are the fundamental building blocks for scheduling problems in OptalCP.
Creating Interval Variables
Create an interval variable using model.interval_var():
- Python
- TypeScript
import optalcp as cp
model = cp.Model()
# Fixed interval: start=10, end=20, length=10
task1 = model.interval_var(start=10, end=20, name="task1")
# Variable start/end, fixed length
task2 = model.interval_var(length=15, name="task2")
# Restrict start and end ranges
task3 = model.interval_var(
start=(0, 100), # start in [0, 100]
end=(50, 200), # end in [50, 200]
name="task3"
)
# Variable length
task4 = model.interval_var(
length=(10, 30), # length in [10, 30]
name="task4"
)
import * as CP from '@scheduleopt/optalcp';
const model = new CP.Model();
// Fixed interval: start=10, end=20, length=10
const task1 = model.intervalVar({ start: 10, end: 20, name: "task1" });
// Variable start/end, fixed length
const task2 = model.intervalVar({ length: 15, name: "task2" });
// Restrict start and end ranges
const task3 = model.intervalVar({
start: [0, 100], // start in [0, 100]
end: [50, 200], // end in [50, 200]
name: "task3"
});
// Variable length
const task4 = model.intervalVar({
length: [10, 30], // length in [10, 30]
name: "task4"
});
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| start | int or [min, max] | [0, IntervalMax] | Start time or range |
| end | int or [min, max] | [0, IntervalMax] | End time or range |
| length | int or [min, max] | [0, LengthMax] | Duration or range |
| optional | bool | false | Whether interval can be absent |
| name | string | auto-generated | Identifier for debugging |
Implicit Constraint
The solver enforces: length = end - start
This means you don't need to specify all three—the solver infers missing bounds.
Expressions for Constraints
Use these functions to get expressions for constraints and objectives:
- Python
- TypeScript
# Expressions for start, end, length (IntExpr)
start_expr = task.start()
end_expr = task.end()
length_expr = task.length()
# Expression for presence (BoolExpr)
presence_expr = task.presence()
# Use in constraints
model.enforce(task1.end() <= task2.start())
model.minimize(task.end())
# Use presence in constraints
model.enforce(task1.presence() == task2.presence())
// Expressions for start, end, length (IntExpr)
const startExpr = task.start();
const endExpr = task.end();
const lengthExpr = task.length();
// Expression for presence (BoolExpr)
const presenceExpr = task.presence();
// Use in constraints
model.enforce(task1.end().le(task2.start()));
model.minimize(task.end());
// Use presence in constraints
model.enforce(task1.presence().eq(task2.presence()));
Expression Functions
| Function | Returns | Description |
|---|---|---|
start() | IntExpr | Start time of the interval |
end() | IntExpr | End time of the interval |
length() | IntExpr | Duration of the interval |
presence() | BoolExpr | True if present, false if absent |
Optional Intervals
Intervals can be optional, meaning they may or may not appear in the solution:
- Python
- TypeScript
# Create optional interval
task_a = model.interval_var(length=10, optional=True, name="task_a")
task_b = model.interval_var(length=20, optional=True, name="task_b")
# Link presence: both present or both absent
model.enforce(task_a.presence() == task_b.presence())
// Create optional interval
const taskA = model.intervalVar({ length: 10, optional: true, name: "task_a" });
const taskB = model.intervalVar({ length: 20, optional: true, name: "task_b" });
// Link presence: both present or both absent
model.enforce(taskA.presence().eq(taskB.presence()));
Absent Semantics
When an optional interval is absent in a solution:
- Expressions involving the interval become absent: Operations on absent intervals propagate the absent value
- Precedence constraints are satisfied trivially: If either interval is absent, the constraint is automatically satisfied
- Resource usage is zero: Absent intervals do not consume resources
- Python
- TypeScript
# Example: optional maintenance task
machine_a = model.interval_var(length=100, name="machine_a")
maintenance = model.interval_var(
length=30,
optional=True,
name="maintenance"
)
# If maintenance is present, it must be after machine_a
# Maintenance absence doesn't violate the precedence
machine_a.end_before_start(maintenance)
// Example: optional maintenance task
const machineA = model.intervalVar({ length: 100, name: "machine_a" });
const maintenance = model.intervalVar({
length: 30,
optional: true,
name: "maintenance"
});
// If maintenance is present, it must be after machine_a
// Maintenance absence doesn't violate the precedence
machineA.endBeforeStart(maintenance);
Querying and Modifying Bounds
Each interval variable has a domain—the set of possible values defined in the model. The domain includes bounds for start, end, and length, plus whether the interval can be present or absent.
These properties let you query and modify the domain during model construction:
- Python
- TypeScript
task = model.interval_var(length=10, optional=True, name="task")
# Presence status (check and set using `optional` property)
task.optional == True # True: can be present or absent in solution
task.optional == False # True: fixed to be present
task.optional is None # True: fixed to be absent
task.optional = True # Allow both present and absent
task.optional = False # Fix to be present
task.optional = None # Fix to be absent
# Time bounds (use properties with snake_case)
task.start_min # Query minimum start
task.start_max # Query maximum start
task.start_min = 0 # Set minimum start
task.start_max = 100 # Set maximum start
# Similarly for end and length:
# task.end_min, task.end_max, task.length_min, task.length_max
const task = model.intervalVar({ length: 10, optional: true, name: "task" });
// Presence status (check and set using `optional` property)
task.optional === true // true: can be present or absent in solution
task.optional === false // true: fixed to be present
task.optional === null // true: fixed to be absent
task.optional = true; // Allow both present and absent
task.optional = false; // Fix to be present
task.optional = null; // Fix to be absent
// Time bounds (use properties with camelCase)
task.startMin; // Query minimum start
task.startMax; // Query maximum start
task.startMin = 0; // Set minimum start
task.startMax = 100; // Set maximum start
// Similarly for end and length:
// task.endMin, task.endMax, task.lengthMin, task.lengthMax
Constants
OptalCP defines constants for interval bounds:
- Python
- TypeScript
import optalcp as cp
# Constants for interval bounds
cp.IntervalMin # -715827882 (minimum start/end value)
cp.IntervalMax # 715827882 (maximum start/end value)
cp.LengthMax # 1431655764 (maximum length = IntervalMax - IntervalMin)
# Example usage
task = model.interval_var(
start=(0, cp.IntervalMax),
length=(1, cp.LengthMax),
name="unbounded_task"
)
import * as CP from '@scheduleopt/optalcp';
// Constants for interval bounds
CP.IntervalMin // -715827882 (minimum start/end value)
CP.IntervalMax // 715827882 (maximum start/end value)
CP.LengthMax // 1431655764 (maximum length = IntervalMax - IntervalMin)
// Example usage
const task = model.intervalVar({
start: [0, CP.IntervalMax],
length: [1, CP.LengthMax],
name: "unbounded_task"
});
Reading Solution Values
After solving, read actual values from the solution (not the domain):
- Python
- TypeScript
result = model.solve()
if result.solution:
sol = result.solution
# Read interval values
start = sol.get_start(task) # Actual start time
end = sol.get_end(task) # Actual end time
length = sol.get_length(task) # Actual length
# Check presence (for optional intervals)
if sol.is_present(task):
print(f"Task scheduled at {start}-{end}")
else:
print("Task is absent")
const result = await model.solve();
if (result.solution) {
const sol = result.solution;
// Read interval values
const start = sol.getStart(task); // Actual start time
const end = sol.getEnd(task); // Actual end time
const length = sol.getLength(task); // Actual length
// Check presence (for optional intervals)
if (sol.isPresent(task)) {
console.log(`Task scheduled at ${start}-${end}`);
} else {
console.log("Task is absent");
}
}
Don't confuse presence() (expression for constraints) with the optional property (queries presence status during modeling):
- Python
- TypeScript
# WRONG: optional property returns bool/None, evaluates immediately
model.enforce(a.optional == b.optional) # Compiles but wrong!
# CORRECT: presence() returns BoolExpr for constraints
model.enforce(a.presence() == b.presence())
# WRONG: optional property checks if fixed, not solution value
if task.optional == False: # True only if interval cannot be absent
print("scheduled")
# CORRECT: check the actual solution
if solution.is_present(task):
print("scheduled")
// WRONG: optional property returns bool/null, evaluates immediately
model.enforce(a.optional === b.optional); // Compiles but wrong!
// CORRECT: presence() returns BoolExpr for constraints
model.enforce(a.presence().eq(b.presence()));
// WRONG: optional property checks if fixed, not solution value
if (task.optional === false) { // true only if interval cannot be absent
console.log("scheduled");
}
// CORRECT: check the actual solution
if (solution.isPresent(task)) {
console.log("scheduled");
}
Complete Example
- Python
- TypeScript
import optalcp as cp
model = cp.Model()
# Create tasks with dependencies
cut = model.interval_var(length=30, name="cut")
sand = model.interval_var(length=20, name="sand")
assemble = model.interval_var(length=45, name="assemble")
# Precedence constraints
cut.end_before_start(sand)
sand.end_before_start(assemble)
# Minimize completion time
model.minimize(assemble.end())
# Solve
result = model.solve()
if result.solution:
print(f"Cut: {result.solution.get_start(cut)}-{result.solution.get_end(cut)}")
print(f"Sand: {result.solution.get_start(sand)}-{result.solution.get_end(sand)}")
print(f"Assemble: {result.solution.get_start(assemble)}-{result.solution.get_end(assemble)}")
print(f"Makespan: {result.objective}")
import * as CP from '@scheduleopt/optalcp';
const model = new CP.Model();
// Create tasks with dependencies
const cut = model.intervalVar({ length: 30, name: "cut" });
const sand = model.intervalVar({ length: 20, name: "sand" });
const assemble = model.intervalVar({ length: 45, name: "assemble" });
// Precedence constraints
cut.endBeforeStart(sand);
sand.endBeforeStart(assemble);
// Minimize completion time
model.minimize(assemble.end());
// Solve
const result = await model.solve();
if (result.solution) {
console.log(`Cut: ${result.solution.getStart(cut)}-${result.solution.getEnd(cut)}`);
console.log(`Sand: ${result.solution.getStart(sand)}-${result.solution.getEnd(sand)}`);
console.log(`Assemble: ${result.solution.getStart(assemble)}-${result.solution.getEnd(assemble)}`);
console.log(`Makespan: ${result.objective}`);
}
See Also
- Constraints - Precedence functions and constraint types
- Variables - IntVar and BoolVar for auxiliary decisions
- Expressions - Using interval properties in expressions
- Alternative - Choosing between optional interval variants
- Span - Grouping intervals with a parent interval