Solutions
The Solution class represents a complete assignment of values to variables. Solutions are returned from the solver and can also be built manually for warm starting or external solution injection.
Accessing Solution Values
In Preview edition, all variables in solutions are reported as absent. The objective value is correct. Contact us for Academic or Full edition for actual solution values.
After solving, access variable values through the Solution object in result.solution. Values are None / null for absent variables.
Interval Variables
- Python
- TypeScript
result = model.solve()
if result.solution is not None:
solution = result.solution
# Get start and end times
start = solution.get_start(interval_var) # int | None
end = solution.get_end(interval_var) # int | None
# Get both as tuple (convenience)
value = solution.get_value(interval_var) # tuple[int, int] | None
# Check presence
if solution.is_present(interval_var):
print(f"Task runs from {start} to {end}")
else:
print("Task is absent")
const result = await model.solve();
if (result.solution !== undefined) {
const solution = result.solution;
// Get start and end times
const start = solution.getStart(intervalVar); // number | null
const end = solution.getEnd(intervalVar); // number | null
// Get both as object (convenience)
const value = solution.getValue(intervalVar); // { start: number; end: number } | null
// Check presence
if (solution.isPresent(intervalVar)) {
console.log(`Task runs from ${start} to ${end}`);
} else {
console.log("Task is absent");
}
}
Integer and Boolean Variables
- Python
- TypeScript
# IntVar
int_value = solution.get_value(int_var) # int | None
# BoolVar
bool_value = solution.get_value(bool_var) # bool | None
# Check presence (for optional variables)
if solution.is_present(int_var):
print(f"Value: {int_value}")
// IntVar
const intValue = solution.getValue(intVar); // number | null
// BoolVar
const boolValue = solution.getValue(boolVar); // boolean | null
// Check presence (for optional variables)
if (solution.isPresent(intVar)) {
console.log(`Value: ${intValue}`);
}
Objective Value
- Python
- TypeScript
objective = solution.get_objective()
# Returns:
# int - the objective value
# None - satisfaction problem (no objective) or objective is absent
const objective = solution.getObjective();
// Returns:
// number - the objective value
// undefined - satisfaction problem (no objective)
// null - objective expression evaluated to absent
Presence Functions
All variables (including non-optional ones) have presence information:
- Python
- TypeScript
# Check if variable is present in solution
solution.is_present(var) # bool
# Check if variable is absent in solution
solution.is_absent(var) # bool
// Check if variable is present in solution
solution.isPresent(var); // boolean
// Check if variable is absent in solution
solution.isAbsent(var); // boolean
Non-optional variables are always present. Optional variables may be present or absent depending on the solution.
Iterating Over Model Variables
Iterate over all variables in the model:
- Python
- TypeScript
# Get all variables
interval_vars = model.get_interval_vars()
int_vars = model.get_int_vars()
bool_vars = model.get_bool_vars()
# Print all interval times
# Note: var.name is None if no name was provided
for var in interval_vars:
if solution.is_present(var):
start = solution.get_start(var)
end = solution.get_end(var)
print(f"{var.name}: [{start}, {end})")
else:
print(f"{var.name}: absent")
// Get all variables
const intervalVars = model.getIntervalVars();
const intVars = model.getIntVars();
const boolVars = model.getBoolVars();
// Print all interval times
// Note: name property is undefined if no name was provided
for (const varObj of intervalVars) {
if (solution.isPresent(varObj)) {
const start = solution.getStart(varObj);
const end = solution.getEnd(varObj);
console.log(`${varObj.name}: [${start}, ${end})`);
} else {
console.log(`${varObj.name}: absent`);
}
}
Solution History
The SolveResult tracks the objective values of all solutions found during search. Only the best solution is retained in result.solution—for other solutions, only the objective value and timing information is recorded.
- Python
- TypeScript
result = model.solve()
# Iterate through all solutions found
for entry in result.objective_history:
print(f"Time: {entry.solve_time:.2f}s")
print(f"Objective: {entry.objective}")
print(f"Valid: {entry.valid}")
print()
# Best solution (last entry)
if result.objective_history:
best = result.objective_history[-1]
print(f"Best solution found at {best.solve_time:.2f}s")
const result = await model.solve();
// Iterate through all solutions found
for (const entry of result.objectiveHistory) {
console.log(`Time: ${entry.solveTime.toFixed(2)}s`);
console.log(`Objective: ${entry.objective}`);
console.log(`Valid: ${entry.valid}`);
console.log();
}
// Best solution (last entry)
if (result.objectiveHistory.length > 0) {
const best = result.objectiveHistory[result.objectiveHistory.length - 1];
console.log(`Best solution found at ${best.solveTime.toFixed(2)}s`);
}
ObjectiveEntry
Each entry in objective_history / objectiveHistory represents a solution found during search:
- Python
- TypeScript
class ObjectiveEntry:
solve_time: float # Time when solution was found (seconds)
objective: int | None # Objective value (None if absent)
valid: bool | None # Verification result (see below)
interface ObjectiveEntry {
solveTime: number; // Time when solution was found (seconds)
objective: number | null; // Objective value (null if absent)
valid: boolean | undefined; // Verification result (see below)
}
The valid field is populated when the verifySolution parameter is enabled (not the default). Each solution is verified against the input model by an independent algorithm. The value is true if verification passed, or None / undefined if verification was not enabled. The value is never false—if verification fails, the solver stops with an error.
Objective Bound History
Track improvements to the objective bound (lower bound for minimization, upper bound for maximization):
- Python
- TypeScript
result = model.solve()
# Iterate through bound improvements
for entry in result.objective_bound_history:
print(f"Time: {entry.solve_time:.2f}s")
print(f"Bound: {entry.value}")
print()
# Best bound (last entry)
if result.objective_bound_history:
best_bound = result.objective_bound_history[-1]
print(f"Best bound: {best_bound.value} at {best_bound.solve_time:.2f}s")
const result = await model.solve();
// Iterate through bound improvements
for (const entry of result.objectiveBoundHistory) {
console.log(`Time: ${entry.solveTime.toFixed(2)}s`);
console.log(`Bound: ${entry.value}`);
console.log();
}
// Best bound (last entry)
if (result.objectiveBoundHistory.length > 0) {
const bestBound = result.objectiveBoundHistory[result.objectiveBoundHistory.length - 1];
console.log(`Best bound: ${bestBound.value} at ${bestBound.solveTime.toFixed(2)}s`);
}
ObjectiveBoundEntry
Each entry in objective_bound_history / objectiveBoundHistory represents an improvement to the proved bound (lower bound for minimization, upper bound for maximization):
- Python
- TypeScript
class ObjectiveBoundEntry:
solve_time: float # Time when bound was proved (seconds)
value: int # Bound value
interface ObjectiveBoundEntry {
solveTime: number; // Time when bound was proved (seconds)
value: number; // Bound value
}
Building Solutions
See External Solutions for building solutions manually for warm starting or solution injection.
Complete Example
- Python
- TypeScript
import optalcp as cp
model = cp.Model()
# Create tasks
cut = model.interval_var(length=30, name="cut")
sand = model.interval_var(length=20, name="sand")
assemble = model.interval_var(length=45, name="assemble")
# Optional quality check
quality_check = model.interval_var(
length=15,
optional=True,
name="quality_check"
)
# Dependencies
cut.end_before_start(sand)
sand.end_before_start(assemble)
# Quality check must be after assembly if present
assemble.end_before_start(quality_check)
# Minimize completion time
model.minimize(model.max([assemble.end(), quality_check.end()]))
# Solve
result = model.solve()
if result.solution is not None:
solution = result.solution
print(f"Objective: {solution.get_objective()}")
print(f"Solutions found: {result.nb_solutions}")
print()
# Print all interval variables
for var in model.get_interval_vars():
if solution.is_present(var):
start = solution.get_start(var)
end = solution.get_end(var)
print(f"{var.name}: {start} -> {end} (length {end - start})")
else:
print(f"{var.name}: absent")
# Show solution history
print(f"\nSolution history:")
for i, entry in enumerate(result.objective_history):
print(f" Solution {i+1}: objective={entry.objective} at {entry.solve_time:.2f}s")
import * as CP from '@scheduleopt/optalcp';
const model = new CP.Model();
// Create tasks
const cut = model.intervalVar({ length: 30, name: "cut" });
const sand = model.intervalVar({ length: 20, name: "sand" });
const assemble = model.intervalVar({ length: 45, name: "assemble" });
// Optional quality check
const qualityCheck = model.intervalVar({
length: 15,
optional: true,
name: "quality_check"
});
// Dependencies
cut.endBeforeStart(sand);
sand.endBeforeStart(assemble);
// Quality check must be after assembly if present
assemble.endBeforeStart(qualityCheck);
// Minimize completion time
model.minimize(model.max([assemble.end(), qualityCheck.end()]));
// Solve
const result = await model.solve();
if (result.solution !== undefined) {
const solution = result.solution;
console.log(`Objective: ${solution.getObjective()}`);
console.log(`Solutions found: ${result.nbSolutions}`);
console.log();
// Print all interval variables
for (const varObj of model.getIntervalVars()) {
if (solution.isPresent(varObj)) {
const start = solution.getStart(varObj);
const end = solution.getEnd(varObj);
console.log(`${varObj.name}: ${start} -> ${end} (length ${end! - start!})`);
} else {
console.log(`${varObj.name}: absent`);
}
}
// Show solution history
console.log(`\nSolution history:`);
result.objectiveHistory.forEach((entry, i) => {
console.log(` Solution ${i+1}: objective=${entry.objective} at ${entry.solveTime.toFixed(2)}s`);
});
}
See Also
- Solving Basics - Result interpretation and solving workflow
- External Solutions - Warm starting and solution injection
- Async Solving - Receiving solutions through callbacks
- Model Export - Serializing solutions with models