Install
$ agentstack add skill-relationalai-rai-agent-skills-rai-prescriptive-solver-management ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Solver Management
> Requires relationalai>=1.11.0. Requesting sensitivity and conflict / IIS diagnostics on solve() requires the dual-bearing solver; earlier versions reject sensitivity=True as an unsupported solver attribute. See rai-setup.
Summary
What: Solver lifecycle — selection, creation, formulation inspection, execution, and diagnostics.
When to use:
- Choosing which solver to use for a problem (HiGHS vs Gurobi vs MiniZinc vs Ipopt)
- Setting up Problem and Solver instances
- Inspecting formulation before solving (problem.display(), variable/constraint counts)
- Tuning solver parameters (time limits, MIP gap, presolve)
- Diagnosing solver-level failures (crashes, numerical instability, Big-M sizing)
- Understanding solver performance (slow convergence, presolve tuning)
- Running parametric/scenario solves
When NOT to use:
- Post-solve result interpretation and communication — presenting OPTIMAL/INFEASIBLE/DUALINFEASIBLE/TIMELIMIT status to users, solution quality assessment, trivial solution detection, sensitivity analysis, and root-cause diagnosis of infeasibility/unboundedness — see
rai-prescriptive-results-interpretation. - Variable/constraint/objective formulation patterns — see
rai-prescriptive-problem-formulation - PyRel syntax (imports, types, properties) — see
rai-pyrel-coding
Overview:
- Understand the optimization goal — what decisions are being made, what does success look like?
- Classify the problem type (LP / MILP / QP / NLP / CSP)
- Select a solver (decision rules based on variable types, nonlinearity, and license availability)
- Create Problem and Solver instances
- Validate formulation pre-solve (problem.display(), count checks)
- Execute solve (parameters, time limits, warm starting)
- Diagnose solver-level issues (crashes, numerical instability, performance)
Quick Reference
from relationalai.semantics import Float, Integer, Model, sum
from relationalai.semantics.reasoners.prescriptive import Problem
# 1. Create Problem (Float for LP/MILP/NLP, Integer for CP)
problem = Problem(model, Float)
# 2. Register variables (type: "cont", "int", "bin"; bounds; naming)
problem.solve_for(Route.x_flow, type="cont", lower=0, upper=Route.capacity,
name=["flow", Route.origin, Route.dest])
problem.solve_for(Facility.x_open, type="bin", name=["open", Facility.id])
# 3. Add constraints (model.require inside problem.satisfy)
problem.satisfy(model.require(sum(Route.x_flow).per(Customer) >= Customer.demand))
problem.satisfy(model.require(Route.x_flow 0)
model.require(problem.num_constraints() > 0)
# 6. Solve — solver choice depends on problem type and user license
# See "Solver Selection" section for decision rules
problem.solve(solver_name, time_limit_sec=120)
model.require(problem.termination_status() == "OPTIMAL")
problem.solve_info().display()
# Solvers: "highs" (LP/MILP, open-source), "gurobi" (LP/MILP/QP/QCP, license required),
# "minizinc" (CP, open-source), "ipopt" (NLP, open-source)
# Check: problem.termination_status() → "OPTIMAL" | "INFEASIBLE" | "DUAL_INFEASIBLE" | "TIME_LIMIT"
Post-Solve API
problem.solve() returns None -- do NOT assign its return value. Access solve results through separate methods:
- Status summary:
problem.solve_info().display()prints a human-readable solve summary. - Status properties:
problem.solve_info().termination_status(str),problem.solve_info().objective_value(float). - Solution values (populate=True): Query via
model.select()— solved values are written back into model properties. - Solution values (populate=False): Use
Variable.values(sol_index, value_ref)on theProblemVariablereturned bysolve_for().
# CORRECT usage
problem.solve("highs", time_limit_sec=60)
si = problem.solve_info()
si.display() # Print status summary
print(si.termination_status) # "OPTIMAL", "INFEASIBLE", etc.
# Always check termination_status before reading objective_value or extracting
# results — si.objective_value is None on infeasible / unbounded solves and will
# silently render as "None" in print() / math / comparisons otherwise.
if si.termination_status in ("OPTIMAL", "LOCALLY_SOLVED"):
print(si.objective_value) # Objective function value
# For populate=False workflows, use Variable.values() (gate the extraction on
# termination_status for the same reason):
assign_var = problem.solve_for(Assignment.x, type="bin", populate=False)
problem.solve("highs")
si = problem.solve_info()
if si.termination_status in ("OPTIMAL", "LOCALLY_SOLVED"):
value_ref = Float.ref()
df = model.select(
assign_var.assignment.worker.name.alias("worker"),
value_ref.alias("value"),
).where(assign_var.values(0, value_ref), value_ref > 0.5).to_df()
> Warning: result = problem.solve(...); result.status fails because solve() returns None regardless of solver. Accessing any attribute on None raises AttributeError. Always call problem.solve() on its own line, then use problem.solve_info() separately.
Read the Formulation
Before classifying or configuring, read the existing formulation (built in rai-prescriptive-problem-formulation) to extract solver-relevant characteristics:
- Variable types — Are there integer or binary variables? (determines solver compatibility)
- Objective direction — Minimize or maximize? Is feasibility the primary concern or optimality? (determines parameter tuning)
- Problem structure — Are there nonlinear terms? How many entities and constraints? (determines solver choice and time limits)
Problem Type Classification
Identify the problem type before choosing a solver:
Linear Programming (LP): All variables continuous, objective and constraints all linear. No products of variables, no nonlinear functions.
Mixed-Integer Linear Programming (MILP): Some variables integer or binary, but objective and constraints remain linear. No products of integer variables (that makes it nonlinear). Keep Big-M tight.
Quadratic Programming (QP): Quadratic terms in objective only (e.g., risk minimization with covariance). Constraints remain linear. Check convexity (Q matrix positive semi-definite) for global optimum.
Quadratically Constrained Programming (QCP): Quadratic terms in constraints (e.g., norm constraints). More restrictive solver requirements than QP. Check if constraints are convex.
Nonlinear Programming (NLP): Nonlinear functions (exp, log, sqrt, sin, cos). Integer + NLP is very difficult (MINLP). May have local optima -- solution may not be global.
Constraint Satisfaction Problem (CSP): No meaningful objective function. Goal is finding any feasible solution. Often discrete variables with combinatorial constraints. Benefits from global constraints.
Solver Selection
Choose the solver based on variable types and objective/constraint structure:
Support Matrix
| Problem Type | Gurobi | HiGHS | Ipopt | MiniZinc | |---|---|---|---|---| | Linear Programs (LP) | YES | YES | YES | NO | | Mixed-Integer Linear (MILP) | YES | YES | NO | NO | | Quadratic Programs (QP) | YES (convex obj only) | YES | YES | NO | | Quadratically Constrained (QCP) | YES | NO | YES | NO | | Nonlinear Programs (NLP) | YES | NO | YES | NO | | Constraint Programming (CP) | NO | NO | NO | YES | | Discrete Variables (int/bin) | YES | YES | NO | YES | | Continuous Variables | YES | YES | YES | NO |
Solver Profiles
HiGHS (solver="highs"): Open-source. Best for LP, MILP, convex QP objectives. Fast simplex/IPM LP solver, good MILP for moderate problems. Params: time_limit, mip_rel_gap, presolve ("choose"/"on"/"off"), threads.
HiGHS limitations (specific):
- No indicator constraints —
implies()will fail. Use Big-M reformulation instead. - No SOS constraints —
special_ordered_set_type_1()/special_ordered_set_type_2()will fail. Use explicit binary variable formulations instead. - No quadratic constraints (QCP) — quadratic terms in constraints will fail. Only convex quadratic objectives (QP) are supported.
- No nonlinear functions (
math.exp,math.log,math.sqrt,x**n) — use Gurobi (preferred when licensed) or Ipopt.
Gurobi (solver="gurobi"): Commercial (available via RAI). Best for large-scale MILP, QP, QCP, and nonlinear problems. Industry-leading MILP performance, discrete + continuous + quadratic + nonlinear (math.exp, math.log, math.sqrt, x ** n), excellent diagnostics, multi-objective support. Params: TimeLimit, MIPGap, MIPFocus (0=balanced, 1=feasibility, 2=optimality, 3=bound), Presolve (2 for aggressive), Threads (0 for auto). License required: Gurobi requires a named prescriptive engine with a Snowflake secret and external access integration configured in raiconfig.yaml. See [rai-setup](../rai-setup/SKILL.md) for setup. If unavailable, fall back to HiGHS (LP/MILP) or Ipopt (NLP). Large MIP problems may solve significantly faster with Gurobi than HiGHS.
MiniZinc (solver="minizinc"): Open-source (Chuffed backend). Best for CP, combinatorial, constraint satisfaction. Powerful propagation, native support for all_different and implies, multiple solutions via solution_limit. Params: time_limit_sec, solution_limit. Cannot handle continuous variables, LP, QP, NLP, or SOS constraints. MiniZinc's native global-constraint library is far richer (circuit, cumulative, element, ...) but only all_different and implies are exposed from PyRel to MiniZinc today; SOS1/SOS2 are PyRel-exposed but Gurobi-only.
MiniZinc-specific requirements and behavior:
- Requires
Problem(model, Integer)— a single Float decision variable or Float data coerces the problem to MIP and MiniZinc will reject it. solution_limit=Kenables multi-solution enumeration. Termination statusSOLUTION_LIMIT(notOPTIMAL) when search stopped at K with more feasible remaining;OPTIMALmeans search exhausted with ≤ K found.- Chuffed is the only backend exposed today; Gecode / CP-SAT are not selectable.
- Native MiniZinc logging is NOT captured into LogMessage events. Use
log_to_console=Trueto stream logs.
For the full CSP-style formulation guide, see [csp-formulation.md](../rai-prescriptive-problem-formulation/references/csp-formulation.md).
Ipopt (solver="ipopt"): Open-source. Best for continuous nonlinear optimization. Interior-point for smooth NLP, handles nonlinear objectives AND constraints. Finds local optima only. Params: max_iter, max_wall_time, tol (e.g. 1e-8), print_level, mu_strategy. Cannot handle integer or binary variables -- will FAIL.
Decision Rules
Use these rules in order to pick a solver. Gurobi outperforms open-source solvers (HiGHS, Ipopt) on every problem type it supports — faster solve times, tighter MIP gaps, better scaling. Always prefer Gurobi when the user has a license. Only recommend open-source when Gurobi is unavailable or the problem type requires it (CSP → MiniZinc, smooth NLP → Ipopt).
- Check variable types first.
- Any integer/binary variable? Ipopt is invalid. Gurobi preferred; HiGHS if no license.
- MiniZinc only if the problem is pure constraint satisfaction with discrete variables.
- Check for nonlinearity.
math.exp(x),math.log(x),math.sqrt(x), orx ** n?
HiGHS and MiniZinc are invalid.
- Continuous-only NLP: Gurobi (preferred when licensed) or Ipopt (best for smooth local NLP).
- Discrete + nonlinear: Gurobi only.
- Trig (
math.sin,math.cos, etc.) and division between two decision variables are not lowered to the solver by the prescriptive library — reformulate as piecewise-linear approximations or via auxiliary variables.
- Check for quadratic constraints.
- Quadratic terms in constraints (not just objective): HiGHS is invalid.
- Gurobi preferred; Ipopt if continuous-only and no Gurobi license.
- No objective (constraint satisfaction)?
- Discrete: MiniZinc (purpose-built for CSP).
- Otherwise Gurobi or HiGHS can find feasibility.
Quick reference:
| Your problem has... | Gurobi available | No Gurobi license | |---|---|---| | Binary/integer + linear (MILP) | Gurobi | HiGHS | | Binary/integer + quadratic (MIQP) | Gurobi | (no open-source alternative) | | Continuous + linear (LP) | Gurobi | HiGHS | | Continuous + quadratic objective (QP) | Gurobi | HiGHS | | Continuous + quadratic constraints (QCP) | Gurobi | Ipopt | | Continuous + nonlinear (NLP) | Gurobi or Ipopt | Ipopt | | Discrete + constraint satisfaction (CSP) | MiniZinc | MiniZinc | | Need multiple solutions | MiniZinc (best) | MiniZinc |
Problem size guidelines (small/medium/large thresholds) and Problem initialization patterns (Problem(model, Float) vs Integer, solver string names) are in [solver-details.md](references/solver-details.md). Always select solver based on problem type and confirm license availability.
Global Constraints
Global constraints (all_different, implies, SOS1, SOS2) provide solver-exploitable combinatorial structure. Each has specific solver requirements:
| Constraint | Requires | Alternatives | |---|---|---| | all_different | MiniZinc | O(n^2) pairwise inequalities in MIP | | implies | Gurobi or MiniZinc | Big-M reformulation for HiGHS | | special_ordered_set_type_1 | Gurobi | Binary variables + sum constraints | | special_ordered_set_type_2 | Gurobi | Binary segment selection variables |
For syntax, code examples, and a CP vs MIP decision guide, see [global-constraints.md](../rai-prescriptive-problem-formulation/references/global-constraints.md).
Formulation Display
Use problem.display() to inspect variables, objectives, and constraints before solving. Check problem.num_variables(), problem.num_constraints(), and problem.num_min_objectives() / problem.num_max_objectives() against expectations (these are Relationships — use in model.require() or model.select()). Use problem.display(part) for targeted inspection of a single constraint or objective; for variables, query rows via model.select(var_ref.name, var_ref.lower, var_ref.upper).to_df(). Use problem.printed_model() (Relationship, with print_format= on problem.solve()) to get LP/MPS/LaTeX text representations. The same Problem can be re-solved multiple times — constraints accumulate across calls.
See [formulation-display.md](references/formulation-display.md) for display output structure, diagnostic tables, and targeted inspection patterns.
Pre-Solve Validation
Run five checks before calling problem.solve(): (1) entity population — problem.num_variables() > 0; (2) constraint population — problem.num_constraints() > 0 with at least one forcing constraint; (3) objective — exactly one minimize/maximize for an optimization (or sensitivity=True) solve, zero for a pure feasibility / CSP solve (conflict=True is objective-agnostic: it needs none, but tolerates the objective of an infeasible optimization model you're diagnosing); (4) data integrity — no nulls, no negatives in costs/capacities, total capacity >= total demand; (5) formulation structure via problem.display().
# Minimum pre-solve checklist
problem.display()
model.require(problem.num_variables() > 0)
model.require(problem.num_constraints() > 0)
model.require(problem.num_min_objectives() + problem.num_max_objectives() == 1) # optimization/sensitivity; use == 0 for pure feasibility/CSP (conflict=True works with either)
See [pre-solve-validation.md](references/pre-solve-validation.md) for full checks, diagnostic queries, and data integrity patterns.
Common Compilation Errors
For prescriptive-context compile errors (entity reference passed as scalar, zero entities, type mismatch, undefined concept), see [compilation-errors.md](references/compilation-errors.md). G
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: RelationalAI
- Source: RelationalAI/rai-agent-skills
- License: Apache-2.0
- Homepage: https://relational.ai
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.