Integer and Boolean Variables
While OptalCP excels at scheduling with interval variables, IntVar and BoolVar let you model decisions beyond time: resource levels, machine assignments, selection choices, counts, and general integer optimization problems.
Common uses in scheduling:
- Variable resource demands (task needs 1-3 workers)
- Machine or mode selection
- Counting selected optional tasks
- Linking interval presence to other decisions
Non-scheduling problems: OptalCP can solve any problem expressible with integer variables, boolean logic, and arithmetic constraints—assignment, packing, allocation, etc. OptalCP is optimized for scheduling and does not include specialized global constraints like all-different or circuit.
Creating Integer Variables
- Python
- TypeScript
import optalcp as cp
model = cp.Model()
# Variable in range [0, 100]
x = model.int_var(min=0, max=100, name="x")
# Variable with default bounds [0, IntVarMax]
y = model.int_var(name="y")
# Optional integer variable (can be absent)
opt = model.int_var(min=0, max=50, optional=True, name="opt")
import * as CP from '@scheduleopt/optalcp';
const model = new CP.Model();
// Variable in range [0, 100]
const x = model.intVar({ min: 0, max: 100, name: "x" });
// Variable with default bounds [0, IntVarMax]
const y = model.intVar({ name: "y" });
// Optional integer variable (can be absent)
const opt = model.intVar({ min: 0, max: 50, optional: true, name: "opt" });
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| min | int | 0 | Minimum value |
| max | int | IntVarMax | Maximum value |
| optional | bool | false | Whether variable can be absent |
| name | string | auto-generated | Identifier for debugging |
Creating Boolean Variables
- Python
- TypeScript
# Simple boolean variable
is_selected = model.bool_var(name="is_selected")
# Optional boolean variable
opt_flag = model.bool_var(optional=True, name="opt_flag")
// Simple boolean variable
const isSelected = model.boolVar({ name: "is_selected" });
// Optional boolean variable
const optFlag = model.boolVar({ optional: true, name: "opt_flag" });
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| optional | bool | false | Whether variable can be absent |
| name | string | auto-generated | Identifier for debugging |
Using Variables in Constraints
Variables can be used in arithmetic expressions and constraints:
- Python
- TypeScript
x = model.int_var(min=0, max=100, name="x")
y = model.int_var(min=0, max=100, name="y")
b = model.bool_var(name="b")
# Arithmetic constraints
model.enforce(x + y <= 50)
model.enforce(x * 2 == y)
# Boolean in constraints
model.enforce(b == (x > 10)) # b is true iff x > 10
# Variable resource demand
task = model.interval_var(length=10, name="task")
demand = model.int_var(min=1, max=3, name="demand")
model.enforce(model.sum([model.pulse(task, demand)]) <= 5)
# Minimize/maximize
model.minimize(x + y)
const x = model.intVar({ min: 0, max: 100, name: "x" });
const y = model.intVar({ min: 0, max: 100, name: "y" });
const b = model.boolVar({ name: "b" });
// Arithmetic constraints
model.enforce(x.plus(y).le(50));
model.enforce(x.times(2).eq(y));
// Boolean in constraints
model.enforce(b.eq(x.gt(10))); // b is true iff x > 10
// Variable resource demand
const task = model.intervalVar({ length: 10, name: "task" });
const demand = model.intVar({ min: 1, max: 3, name: "demand" });
model.enforce(model.sum([model.pulse(task, demand)]).le(5));
// Minimize/maximize
model.minimize(x.plus(y));
Don't create variables to "store" expressions—expressions can be reused directly multiple times.
- Python
- TypeScript
# DON'T: Create a variable to store an expression
the_end = model.int_var(min=0, max=1000, name="the_end")
model.enforce(the_end == task1.end())
model.enforce(the_end <= task2.start())
model.enforce(the_end <= task3.start())
# DO: Use the expression directly
model.enforce(task1.end() <= task2.start())
model.enforce(task1.end() <= task3.start())
# DON'T: Create a boolean variable for presence
overtime_needed = model.bool_var(name="overtime_needed")
model.enforce(overtime_needed == overtime.presence())
# DO: Use presence() directly
model.enforce(overtime.presence().implies(manager.presence()))
# This also applies to composed expressions
# DON'T:
total_length = model.int_var(min=0, max=1000, name="total_length")
model.enforce(total_length == task1.length() + task2.length())
# DO:
model.minimize(task1.length() + task2.length())
# Common case: lateness/tardiness
# DON'T:
deadline = 100
lateness = model.int_var(min=0, max=100, name="lateness")
model.enforce(task.end() <= deadline + lateness)
model.minimize(lateness * 10)
# DO:
deadline = 100
lateness = (task.end() - deadline).max2(0) # Python variable, not model variable
model.minimize(lateness * 10)
// DON'T: Create a variable to store an expression
const theEnd = model.intVar({ min: 0, max: 1000, name: "the_end" });
model.enforce(theEnd.eq(task1.end()));
model.enforce(theEnd.le(task2.start()));
model.enforce(theEnd.le(task3.start()));
// DO: Use the expression directly
model.enforce(task1.end().le(task2.start()));
model.enforce(task1.end().le(task3.start()));
// DON'T: Create a boolean variable for presence
const overtimeNeeded = model.boolVar({ name: "overtime_needed" });
model.enforce(overtimeNeeded.eq(overtime.presence()));
// DO: Use presence() directly
model.enforce(overtime.presence().implies(manager.presence()));
// This also applies to composed expressions
// DON'T:
const totalLength = model.intVar({ min: 0, max: 1000, name: "total_length" });
model.enforce(totalLength.eq(task1.length().plus(task2.length())));
// DO:
model.minimize(task1.length().plus(task2.length()));
// Common case: lateness/tardiness
// DON'T:
const deadline = 100;
const lateness = model.intVar({ min: 0, max: 100, name: "lateness" });
model.enforce(task.end().le(deadline + lateness));
model.minimize(lateness.times(10));
// DO:
const deadline = 100;
const lateness = task.end().minus(deadline).max2(0); // JS variable, not model variable
model.minimize(lateness.times(10));
Storing an expression in a Python/JavaScript variable is fine—it's just a reference to the expression, not a new solver variable. The problem is model.intVar()/model.int_var() which creates a solver variable that must be assigned a value during search.
The solver handles this better because:
- Expression recognition: The solver detects when the same expression appears multiple times and can optimize accordingly.
- Automatic auxiliary variables: The solver creates internal auxiliary variables only when beneficial—some constraints may be eliminated during presolve or specialized (e.g., into precedence constraints).
- Better search: User-created variables are branching candidates. Auxiliary variables the solver creates are "derived" and not branched on, leading to more efficient search.
- Preserves problem structure: User-created "alias" variables hide the mathematical structure of the problem, potentially confusing search heuristics.
Optional Variables
Like intervals, integer and boolean variables can be optional. This is especially useful for variable-height pulses over optional intervals—when the interval is absent, its resource demand should also be absent.
- Python
- TypeScript
# Optional task with optional variable demand
task = model.interval_var(length=10, optional=True, name="task")
demand = model.int_var(min=1, max=3, optional=True, name="demand")
# Synchronize presence: both present or both absent
model.enforce(demand.presence() == task.presence())
# Use in cumulative constraint
model.enforce(model.sum([model.pulse(task, demand)]) <= 5)
// Optional task with optional variable demand
const task = model.intervalVar({ length: 10, optional: true, name: "task" });
const demand = model.intVar({ min: 1, max: 3, optional: true, name: "demand" });
// Synchronize presence: both present or both absent
model.enforce(demand.presence().eq(task.presence()));
// Use in cumulative constraint
model.enforce(model.sum([model.pulse(task, demand)]).le(5));
When an optional variable is absent:
- Expressions involving the variable become absent
- The variable has no value in the solution
Querying and Modifying Bounds
Each variable has a domain—the set of possible values defined in the model. These properties let you query and modify the domain during model construction:
- Python
- TypeScript
x = model.int_var(min=0, max=100, optional=True, name="x")
# Presence status
x.optional == True # True: can be present or absent in solution
x.optional == False # True: fixed to be present
x.optional is None # True: fixed to be absent
x.optional = True # Allow both present and absent
x.optional = False # Fix to be present
x.optional = None # Fix to be absent
# Value bounds
x.min # Query minimum value
x.max # Query maximum value
x.min = 10 # Set minimum value
x.max = 90 # Set maximum value
const x = model.intVar({ min: 0, max: 100, optional: true, name: "x" });
// Presence status
x.optional === true // true: can be present or absent in solution
x.optional === false // true: fixed to be present
x.optional === null // true: fixed to be absent
x.optional = true; // Allow both present and absent
x.optional = false; // Fix to be present
x.optional = null; // Fix to be absent
// Value bounds
x.min // Query minimum value
x.max // Query maximum value
x.min = 10; // Set minimum value
x.max = 90; // Set maximum value
Constants
- Python
- TypeScript
import optalcp as cp
cp.IntVarMin # -1073741823 (minimum for IntVar)
cp.IntVarMax # 1073741823 (maximum for IntVar)
import * as CP from '@scheduleopt/optalcp';
CP.IntVarMin // -1073741823 (minimum for IntVar)
CP.IntVarMax // 1073741823 (maximum for IntVar)
Reading Solution Values
- Python
- TypeScript
x = model.int_var(min=0, max=100, name="x")
b = model.bool_var(name="b")
result = model.solve()
if result.solution:
sol = result.solution
# Read values
x_val = sol.get_value(x) # int or None if absent
b_val = sol.get_value(b) # bool or None if absent
# Check presence (for optional variables)
if sol.is_present(x):
print(f"x = {x_val}")
else:
print("x is absent")
const x = model.intVar({ min: 0, max: 100, name: "x" });
const b = model.boolVar({ name: "b" });
const result = await model.solve();
if (result.solution) {
const sol = result.solution;
// Read values
const xVal = sol.getValue(x); // number or null if absent
const bVal = sol.getValue(b); // boolean or null if absent
// Check presence (for optional variables)
if (sol.isPresent(x)) {
console.log(`x = ${xVal}`);
} else {
console.log("x is absent");
}
}
Complete Example
- Python
- TypeScript
import optalcp as cp
model = cp.Model()
# Task with variable resource requirement
task = model.interval_var(length=10, name="task")
demand = model.int_var(min=1, max=3, name="demand")
# Resource capacity constraint
model.enforce(model.sum([model.pulse(task, demand)]) <= 5)
# Minimize resource usage
model.minimize(demand)
result = model.solve()
if result.solution:
print(f"Task: {result.solution.get_start(task)}-{result.solution.get_end(task)}")
print(f"Demand: {result.solution.get_value(demand)}")
import * as CP from '@scheduleopt/optalcp';
const model = new CP.Model();
// Task with variable resource requirement
const task = model.intervalVar({ length: 10, name: "task" });
const demand = model.intVar({ min: 1, max: 3, name: "demand" });
// Resource capacity constraint
model.enforce(model.sum([model.pulse(task, demand)]).le(5));
// Minimize resource usage
model.minimize(demand);
const result = await model.solve();
if (result.solution) {
console.log(`Task: ${result.solution.getStart(task)}-${result.solution.getEnd(task)}`);
console.log(`Demand: ${result.solution.getValue(demand)}`);
}
See Also
- Intervals - IntervalVar for scheduling tasks
- Expressions - Using variables in expressions
- Constraints - Constraint types
- Resources/Cumulative - Variable resource heights