solve_ortools_problem
Solve constraint programming problems with Google OR-Tools by defining variables, constraints, and objectives. Returns solutions for problems like nurse scheduling, routing, or resource allocation.
Instructions
Solve a constraint programming problem using Google OR-Tools.
This tool takes a constraint programming problem defined with variables,
constraints, and an optional objective, and returns a solution if one exists.
Important Note:
Each constraint expression must be a single evaluable Python statement.
You cannot use Python control flow (loops, if statements) in the expressions.
Instead, you need to generate separate constraints for each case.
Example:
Nurse Scheduling Problem:
```python
# Schedule 4 nurses across 3 shifts over 3 days
shifts_var = Variable(
name="shifts_var",
type=VariableType.BOOLEAN,
shape=[4, 3, 3], # [nurses, days, shifts]
description="Binary variable indicating if a nurse works a shift",
)
constraints = []
# INCORRECT - This will fail:
# Constraint(
# expression=(
# "for d in range(3): for s in range(3): "
# "model.add(sum([shifts_var[n][d][s] for n in range(4)]) == 1)"
# )
# )
# CORRECT - Add each constraint separately:
# Each shift must have exactly one nurse
for d in range(3):
for s in range(3):
constraints.append(
Constraint(
expression=f"model.add(sum([shifts_var[n][{d}][{s}] for n in range(4)]) == 1)",
description=f"One nurse for day {d}, shift {s}",
)
)
# Each nurse works at most one shift per day
for n in range(4):
for d in range(3):
constraints.append(
Constraint(
expression=f"model.add(sum([shifts_var[{n}][{d}][s] for s in range(3)]) <= 1)",
description=f"Max one shift for nurse {n} on day {d}",
)
)
# Each nurse works 2-3 shifts total
for n in range(4):
constraints.append(
Constraint(
expression=f"model.add(sum([shifts_var[{n}][d][s] for d in range(3) for s in range(3)]) >= 2)",
description=f"Min shifts for nurse {n}",
)
)
problem = Problem(
variables=[shifts_var],
constraints=constraints,
description="Hospital nurse scheduling problem",
)
```
Args:
problem: The problem definition with variables, constraints, and optional objective
Returns:
A list of TextContent containing the solution or an error message
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| problem | Yes |
Implementation Reference
- usolver_mcp/server/main.py:567-672 (handler)The FastMCP tool handler for 'solve_ortools_problem'. It receives an ORToolsProblem, invokes the OR-Tools solver via solve_ortools_problem, and formats the result as TextContent list.@app.tool("solve_ortools_problem") async def solve_ortools_problem_tool( problem: ORToolsProblem, ) -> list[TextContent]: """Solve a constraint programming problem using Google OR-Tools. This tool takes a constraint programming problem defined with variables, constraints, and an optional objective, and returns a solution if one exists. Important Note: Each constraint expression must be a single evaluable Python statement. You cannot use Python control flow (loops, if statements) in the expressions. Instead, you need to generate separate constraints for each case. Example: Nurse Scheduling Problem: ```python # Schedule 4 nurses across 3 shifts over 3 days shifts_var = Variable( name="shifts_var", type=VariableType.BOOLEAN, shape=[4, 3, 3], # [nurses, days, shifts] description="Binary variable indicating if a nurse works a shift", ) constraints = [] # INCORRECT - This will fail: # Constraint( # expression=( # "for d in range(3): for s in range(3): " # "model.add(sum([shifts_var[n][d][s] for n in range(4)]) == 1)" # ) # ) # CORRECT - Add each constraint separately: # Each shift must have exactly one nurse for d in range(3): for s in range(3): constraints.append( Constraint( expression=f"model.add(sum([shifts_var[n][{d}][{s}] for n in range(4)]) == 1)", description=f"One nurse for day {d}, shift {s}", ) ) # Each nurse works at most one shift per day for n in range(4): for d in range(3): constraints.append( Constraint( expression=f"model.add(sum([shifts_var[{n}][{d}][s] for s in range(3)]) <= 1)", description=f"Max one shift for nurse {n} on day {d}", ) ) # Each nurse works 2-3 shifts total for n in range(4): constraints.append( Constraint( expression=f"model.add(sum([shifts_var[{n}][d][s] for d in range(3) for s in range(3)]) >= 2)", description=f"Min shifts for nurse {n}", ) ) problem = Problem( variables=[shifts_var], constraints=constraints, description="Hospital nurse scheduling problem", ) ``` Args: problem: The problem definition with variables, constraints, and optional objective Returns: A list of TextContent containing the solution or an error message """ result = solve_ortools_problem(problem) match result: case Success(solution): return [ TextContent( type="text", text=json.dumps( { "values": solution.values, "is_feasible": solution.is_feasible, "status": solution.status, "objective_value": solution.objective_value, "statistics": solution.statistics, } ), ) ] case Failure(error): return [TextContent(type="text", text=f"Error solving problem: {error}")] case _: return [ TextContent( type="text", text="Unexpected error in solve_ortools_problem_tool", ) ]
- Pydantic BaseModel defining the input schema ORToolsProblem (imported as Problem). Includes variables, constraints, optional objective and parameters.class Problem(BaseModel): """Model representing a complete OR-Tools constraint programming problem.""" variables: list[Variable] constraints: list[Constraint] objective: Objective | None = None parameters: dict[str, Any] = Field(default_factory=dict) description: str = ""
- usolver_mcp/server/main.py:567-567 (registration)FastMCP decorator registering the solve_ortools_problem_tool as an MCP tool.@app.tool("solve_ortools_problem")
- Main solver function (exported as solve_ortools_problem) that constructs CpModel from problem spec, adds vars/constraints/objective, solves with CpSolver, extracts solution values and stats.def solve_problem(problem: Problem) -> Result[Solution, str]: """Solve an OR-Tools problem and return the solution. Args: problem: The problem definition Returns: Result containing a Solution or an error message """ try: # Create model model = cp_model.CpModel() # Create variables vars_result = create_variables(model, problem.variables) if isinstance(vars_result, Failure): return vars_result variables_dict, globals_dict = vars_result.unwrap() # Create constraints constraints_result = create_constraints( model, problem.constraints, variables_dict, globals_dict ) if isinstance(constraints_result, Failure): return constraints_result # Add objective objective_result = add_objective( model, problem.objective, variables_dict, globals_dict ) if isinstance(objective_result, Failure): return objective_result # Create solver solver = cp_model.CpSolver() # Add parameters if provided if problem.parameters: for key, value in problem.parameters.items(): if hasattr(solver.parameters, key): setattr(solver.parameters, key, value) # Solve the problem status = solver.Solve(model) # Extract solution is_feasible = status in [ cp_model.OPTIMAL, cp_model.FEASIBLE, ] values = ( extract_solution(solver, variables_dict, problem.variables) if is_feasible else {} ) return Success( Solution( values=values, is_feasible=is_feasible, status=solver.StatusName(status), objective_value=( solver.ObjectiveValue() if is_feasible and problem.objective else None ), statistics={ "num_conflicts": solver.NumConflicts(), "num_branches": solver.NumBranches(), "wall_time": solver.WallTime(), }, ) ) except Exception as e: return Failure(f"Error solving problem: {e!s}")
- Pydantic model for the solver output Solution.class Solution(BaseModel): """Model representing a solution to an OR-Tools problem.""" values: dict[str, Any] is_feasible: bool status: str objective_value: float | None = None statistics: dict[str, Any] = Field(default_factory=dict)