Problem
Problem(model: b.Model, numeric_type: b.Concept)Define and solve a decision problem on a model.
Use Problem.solve_for to declare decision variables, Problem.minimize/
Problem.maximize to add objectives, and Problem.satisfy to add constraints.
Then call Problem.solve and read results via populated properties (the
default — e.g. model.select(X.v)), the Variable.values Property
for engine-side queries (model.select(sol_idx, val).where(var.values(sol_idx, val))),
or Problem.solve_info for a Python-side metadata snapshot. Pass
solve(sensitivity=True) for post-solve duals (reduced costs, shadow
prices, basis statuses) or solve(conflict=True) to diagnose an infeasible
model (an irreducible infeasible subsystem); see Problem.solve and the
Variable / Constraint accessors below.
Parameters
Section titled “Parameters”
(modelModel) - TheModelto attach solver constructs (variables, objectives, constraints, and results) to.
(numeric_typeConcept) - Numeric type used for bounds, solution values, and numeric literals. Usesemantics.frontend.core.Floatfor HiGHS/Gurobi/Ipopt (even for MIPs), andsemantics.frontend.core.Integerfor MiniZinc.
Examples
Section titled “Examples”Declare a variable and objective:
from relationalai.semantics import Float, Modelfrom relationalai.semantics.reasoners.prescriptive import Problem
m = Model("demo")x = m.Relationship(f"{Float:x}")problem = Problem(m, Float)problem.solve_for(x, name="x", lower=0)problem.minimize(x)Calling Problem.solve invalidates the Python-side solve_info() cache.
Result accessors like termination_status() return engine-side
Relationships that reflect the most recent successful solve.
Attributes
Section titled “Attributes”.numeric_type
Section titled “.numeric_type”Problem.numeric_type: Conceptsemantics.frontend.core.Float or semantics.frontend.core.Integer.
.variables
Section titled “.variables”Problem.variables: list[ProblemVariable]Variable components registered via Problem.solve_for.
.objectives
Section titled “.objectives”Problem.objectives: list[ProblemObjective]Objective components registered via Problem.minimize / Problem.maximize.
.constraints
Section titled “.constraints”Problem.constraints: list[ProblemConstraint]Constraint components registered via Problem.satisfy.
.Variable
Section titled “.Variable”Problem.Variable: ConceptEngine-side aggregate Concept covering all declared decision variables.
Use it to query across all variables of this problem — for example,
model.select(p.Variable.name, p.Variable.lower).where(p.Variable)
to list every variable’s name and lower bound. After
solve(sensitivity=True) / solve(conflict=True) it also carries
reduced_cost, basis_status, and the *_in_conflict predicates
(see ProblemVariable).
.Objective
Section titled “.Objective”Problem.Objective: ConceptEngine-side aggregate Concept covering all objectives. Each
ProblemObjective extends this Concept; use it to query
names, types, and (in future) duals or printed expressions.
.Constraint
Section titled “.Constraint”Problem.Constraint: ConceptEngine-side aggregate Concept covering all constraints. Each
ProblemConstraint extends this Concept; use it to query names
and types, and — after solve(sensitivity=True) /
solve(conflict=True) — shadow_price, basis_status, and
in_conflict (see ProblemConstraint).
Methods
Section titled “Methods”.solve_for()
Section titled “.solve_for()”Problem.solve_for( expr: b.Relationship | b.Chain | b.Expression, where: Optional[list[Any]] = None, populate: bool = True, name: Optional[Any | list[Any]] = None, type: Optional[str] = None, lower: Optional[std.NumberValue] = None, upper: Optional[std.NumberValue] = None, start: Optional[std.NumberValue] = None,) -> "ProblemVariable"Declare decision variables for the problem.
Call this before adding objectives or constraints. The returned
ProblemVariable IS a Concept — use it directly with
model.define(), model.select(), and .ref() to annotate and
query declared variables.
Parameters:
(exprRelationship or Chain or Expression) - Expression describing the variable(s) to create (for example, a scalar relationship likexor an indexed property likeItem.cost).
(wherelist[Any], default:None) - Optional conditions restricting which variable instances are created.
(populatebool, default:True) - If True (default), write solved values back to the original relationship/property afterProblem.solve. Set to False when you create multipleProbleminstances that solve for the same relationship on the same model.
(nameAny or list[Any], default:None) - Display name for variables. Use a string for scalars or a list pattern for indexed variables (for example,["x", Item.i]).
(typestr, default:None) - Variable type:"cont"(default forFloat),"int"(default forInteger), or"bin"(binary 0/1).
(lowerVariable or float or int or Decimal, default:None) - Lower/upper bounds and an optional initial value hint.
(upperVariable or float or int or Decimal, default:None) - Lower/upper bounds and an optional initial value hint.
(startVariable or float or int or Decimal, default:None) - Lower/upper bounds and an optional initial value hint.
Returns:
-
ProblemVariable- The variable subconcept. Annotate viamodel.define(var.lower(0)), query values after solve viamodel.select(sol_idx, val).where(var.values(sol_idx, val)).Back-pointer fields. The subconcept exposes one property per non-value field of the underlying relationship, named after the field’s own name (the explicit
:namefrom the format string, or the lowercased type name if no explicit name was given). Use these to query “which domain entity does this variable represent?”. Examples for the common shapes:# Entity property: f"{Queen} is in {Integer:column}"# → var.queen (back to the Queen instance)var = p.solve_for(Queen.column, ...)model.select(sol_idx, var.queen.row, val).where(var.values(sol_idx, val))# Explicit field name: f"{Edge:e} has {Float:flow}"# → var.e (uses the explicit name, not "edge")var = p.solve_for(Edge.flow, ...)model.select(sol_idx, var.e.id, val).where(var.values(sol_idx, val))# Multi-arity entity property:# f"{Player} in {Integer:week} is in {Integer:group}"# → var.player + var.weekvar = p.solve_for(Player.assign(w, x), ...)model.select(sol_idx, var.player.p, var.week, val).where(var.values(sol_idx, val))# Bare multi-arity (no entity):# f"cell {Integer:i} {Integer:j} is {Integer:x}"# → var.i + var.jvar = p.solve_for(cell(i, j, x), ...)model.select(sol_idx, var.i, var.j, val).where(var.values(sol_idx, val))# Bare scalar relationship: f"{Float:x}" — no back-pointer.# Query var.values directly.var = p.solve_for(x, name="x", ...)model.select(sol_idx, val).where(var.values(sol_idx, val))
Raises:
ValueError- If variables are already defined for this relationship, if an argument has an invalid value (for example, an unknowntype), or if a non-value field name would shadow an intrinsic attribute on the Variable subconcept. Shadow categories include engine-side Properties (name,type,lower,upper,start,values), Python@propertydescriptors (dsl_expr,concept_name,property_name,var_type,var_where,populate), andConcept.RESERVED_NAMESmethods (ref,new,alias,where,select,define,require, etc.). SeeProblem._reserved_variable_field_namesfor the authoritative list — the error message lists the full set. Rename the conflicting field in the relationship’s format string. A ValueError is also raised if the value field’s declared type (Integer/Float) does not match the Problem’s numeric type: a decision variable’s value field must matchProblem(model, …); set integrality withtype='int'/'bin', not the property type.TypeError- If an argument has an invalid type.
Referenced By:
RelationalAI Documentation ├── Build With RelationalAI │ └── Understand how PyRel works > Use advanced reasoning > Prescriptive reasoning > Solve a decision problem │ ├── Overview │ │ └── How solving a decision problem works │ ├── Create a Problem object │ │ └── Choose a default numeric type │ ├── Add decision variables │ │ ├── Declare decision variables │ │ └── Choose variable types and bounds │ ├── Solve a decision problem │ │ └── Avoid common pitfalls │ └── Work with solutions │ └── Determine how to access results └── Release Notes └── Python API Release Notes └── What’s New in Version 1.2.0 └── Bug Fixes
.minimize()
Section titled “.minimize()”Problem.minimize( expr: b.Variable | float | int | b.Fragment, name: Optional[Any | list[Any]] = None) -> "ProblemObjective"Add a minimization objective.
The expression must reference at least one decision variable declared via
Problem.solve_for.
Parameters:
(exprVariable or float or int or Fragment) - Objective expression to minimize.
(nameAny or list[Any], default:None) - Optional objective name (string for scalar, or a list pattern for indexed objectives).
Returns:
ProblemObjective- The objective subconcept (IS a Concept).
Raises:
ValueError- If the objective does not reference any declared decision variables.
Notes:
Under PyRel relational semantics, an objective expression whose body evaluates to an empty relation produces no row, so no objective is registered for that call. The behavior is intentional but can be surprising when the modeler expected the objective to apply. It covers:
sum(X.v).where(<filter matching no rows>)sum(select(X.v).where(<empty>))— filter encapsulated inside the aggregate bodysum(model.union(<all-empty branches>))- mixed shapes like
sum(<empty>) + sum(<populated>)— when any sub-aggregate’s body is empty, the arithmetic produces no row
For per-grouping objectives (...per(X.group).where(...)) the
behavior is per-grouping: the objective registers for groupings
whose body has rows; groupings whose body is empty produce no row,
so no per-grouping objective registers for them.
Problem.num_min_objectives returns the count as a Relationship. To
read it as a Python scalar, query the model:
n = model.select(p.num_min_objectives()).to_df().iloc[0, 0].
.maximize()
Section titled “.maximize()”Problem.maximize( expr: b.Variable | float | int | b.Fragment, name: Optional[Any | list[Any]] = None) -> "ProblemObjective"Add a maximization objective.
The expression must reference at least one decision variable declared via
Problem.solve_for.
Parameters:
(exprVariable or float or int or Fragment) - Objective expression to maximize.
(nameAny or list[Any], default:None) - Optional objective name (string for scalar, or a list pattern for indexed objectives).
Returns:
ProblemObjective- The objective subconcept (IS a Concept).
Raises:
ValueError- If the objective does not reference any declared decision variables.
Notes:
See Problem.minimize Notes — empty-body relational semantics apply
identically here. Problem.num_max_objectives returns the count as
a Relationship; query as a scalar via
model.select(p.num_max_objectives()).to_df().iloc[0, 0].
.satisfy()
Section titled “.satisfy()”Problem.satisfy( expr: b.Fragment, name: Optional[Any | list[Any]] = None, keyed_by: Optional[dict[str, Any]] = None,) -> "ProblemConstraint"Add constraints from a model.require(...) fragment.
Use this to turn a require-clause fragment into solver constraints.
The returned ProblemConstraint IS a Concept — annotate via
model.define(constr.name("budget")), query via
model.select(constr.name).where(constr).
Passing a model.require(...) fragment to satisfy detaches it
from the model’s active integrity constraints — the solver enforces
it instead, and it no longer fires engine-side. To also check it
engine-side against the current solution after Problem.solve, call
Problem.verify — it temporarily reinstalls the fragment as an IC,
evaluates it, and removes it (one-shot).
.. note:
LP and MIP solvers return floating-point solutions that satisfyconstraints within solver tolerance (e.g. ``1e-8``), but engineICs check exact inequality. For continuous-variable constraints,use a tolerant ``model.require()`` post-solve instead(e.g. ``model.require(x <= bound + 1e-6)``).Parameters:
(exprFragment) - A fragment created byModel.require(optionally scoped withModel.where).
(nameAny or list[Any], default:None) - Optional constraint name (string for scalar, or a list pattern for indexed constraints).
(keyed_bydict[str,ref], default:None) - Maps a back-pointer name to the grounding reference each constraint instance is 1:1 with. The reference is an entity ({"shift": Shift}, socon.shiftjoins to the shift’s data) or a value such as an identifier ({"i": X.i}, socon.iis the scalar index); a bare primitive type is rejected. Each entry becomes an identifying Property on the constraint, so the instance is identified by — and joins to the model by — its key (con.shift.min_coverage), the same way a variable points back to its entity (var.food). The keys must uniquely determine the constraint: a key set that does not (including two conjuncts of a singlerequire(A, B)sharing keys) makes two instances collide on one identity and raises at solve (an engine-side functional-dependency error).name=plays no part in this identity — it is a display label only; a family is read back by key only ifkeyed_bydeclares one. Key names must be valid identifiers and must not clash with the managed constraint properties (root,name,type,shadow_price,basis_status,in_conflict,id). Omit it for constraints whose marginals you do not read back by key.
Returns:
ProblemConstraint- The constraint subconcept (IS a Concept).
Raises:
TypeError- Ifexpris not a fragment.ValueError- If the fragment has no require clause, or if it includes select/define clauses; if akeyed_byname is not an identifier, clashes with a managed constraint property, or its reference is neither an entity nor a value reference; or, at solve, if thekeyed_bykey set is not 1:1 with the constraint instances (an engine-side functional-dependency error).
Notes:
Under PyRel relational semantics, a require clause whose body evaluates to an empty relation produces no row, so no constraint is registered for that call. The behavior is intentional but can be surprising when the modeler expected the constraint to apply. It covers:
sum(X.v) <= sum(X.v).where(<empty filter>)- filter-encapsulated empties like
sum(select(X.v).where(<empty>)) <= 5 - all-empty-branch unions like
sum(model.union(<empty>, <empty>)) <= 5 - any arithmetic combination where one operand’s body is empty
For per-grouping constraints (...per(X.group).where(...)) the
behavior is per-grouping: the constraint registers for groupings
whose body has rows; groupings whose body is empty produce no row,
so no per-grouping constraint registers for them.
Problem.num_constraints returns the count as a Relationship. To read
it as a Python scalar, query the model:
n = model.select(p.num_constraints()).to_df().iloc[0, 0].
.verify()
Section titled “.verify()”Problem.verify(*fragments: b.Fragment) -> NoneOne-shot constraint verification against the current solution.
Temporarily installs each fragment as an integrity constraint,
triggers a model query to evaluate them, then removes them.
A ModelWarning is raised if
any constraint is violated.
Emits a UserWarning and returns without checking if the
most recent solve did not produce a successful solution (i.e.
termination_status is not OPTIMAL, LOCALLY_SOLVED,
or SOLUTION_LIMIT).
.. note:
LP and MIP solvers return floating-point solutions that satisfyconstraints within solver tolerance (e.g. ``1e-8``), but engineICs check exact inequality. For continuous-variable constraints,use a tolerant ``model.require()`` post-solve instead(e.g. ``model.require(x <= bound + 1e-6)``).Parameters:
(*fragmentsFragment, default:()) - One or more fragments previously passed toProblem.satisfy.
.num_variables()
Section titled “.num_variables()”Problem.num_variables() -> b.RelationshipNumber of declared decision variables. Usable in rules and ICs.
Returns:
Relationship- An Integer Relationship counting the declared variables.
.num_constraints()
Section titled “.num_constraints()”Problem.num_constraints() -> b.RelationshipNumber of declared constraints. Usable in rules and ICs.
Returns:
Relationship- An Integer Relationship counting the declared constraints.
.num_min_objectives()
Section titled “.num_min_objectives()”Problem.num_min_objectives() -> b.RelationshipNumber of minimization objectives. Usable in rules and ICs.
Returns:
Relationship- An Integer Relationship counting the minimization objectives.
.num_max_objectives()
Section titled “.num_max_objectives()”Problem.num_max_objectives() -> b.RelationshipNumber of maximization objectives. Usable in rules and ICs.
Returns:
Relationship- An Integer Relationship counting the maximization objectives.
.termination_status()
Section titled “.termination_status()”Problem.termination_status() -> b.RelationshipSolver termination status (e.g. "OPTIMAL"). Usable in rules and ICs.
Returns:
Relationship- A String Relationship containing the termination status.
.objective_value()
Section titled “.objective_value()”Problem.objective_value() -> b.RelationshipObjective value reported by the solver. Usable in rules and ICs.
Solvers report a single objective per solve (the primary/best solution), not per-point objectives. This Relationship reflects the solver-reported value.
Returns:
Relationship- A numeric Relationship containing the objective value.
.solve_time_sec()
Section titled “.solve_time_sec()”Problem.solve_time_sec() -> b.RelationshipSolve time in seconds (Float Relationship). Usable in rules and ICs.
Returns:
Relationship- A Float Relationship containing the solve time in seconds.
.num_points()
Section titled “.num_points()”Problem.num_points() -> b.RelationshipNumber of solution points. Usable in rules and ICs.
Returns:
Relationship- An Integer Relationship counting the solution points.
.solver_version()
Section titled “.solver_version()”Problem.solver_version() -> b.RelationshipSolver version string. Usable in rules and ICs.
Returns:
Relationship- A String Relationship containing the solver version.
.printed_model()
Section titled “.printed_model()”Problem.printed_model() -> b.RelationshipSolver-provided text representation of the problem. Usable in rules and ICs.
Returns:
Relationship- A String Relationship containing the printed model text.
.error()
Section titled “.error()”Problem.error() -> b.RelationshipSolver error message(s). Usable in rules and ICs.
A message here is not necessarily a whole-request failure: a successful
solve whose secondary step failed (e.g. conflict/IIS extraction, with
conflict_status == "FAILED") reports its reason here alongside a valid
result. Read a failure’s scope from the accompanying status, not from the
mere presence of a message.
Returns:
Relationship- A String Relationship containing solver error messages.
.display()
Section titled “.display()”Problem.display( part: Optional[b.Concept] = None, *, where: Optional[Any] = None, limit: Optional[int] = None, print_output: bool = True) -> strPrint and return a human-readable summary of the problem.
With no arguments, renders a count summary and full tables of variables, objectives, and constraints.
Pass a subconcept returned by Problem.minimize, Problem.maximize, or
Problem.satisfy to render only that component. Pass the umbrella
concept problem.Constraint (on the Problem instance) to render
every registered constraint, or problem.Objective to render
every registered objective. Pass where= with a DSL predicate to
scope rendering to filter-matching rows; where requires
part. Variable subconcepts raise ValueError; query variable
rows directly via the DSL — for example
model.select(var.name, var.lower, var.upper).where(...).
Pass limit=N to cap each rendered table to its top-N rows.
Parameters:
(partConcept, default:None) - A registered constraint or objective subconcept returned byProblem.satisfy,Problem.minimize, orProblem.maximize, or the umbrella conceptproblem.Constraintorproblem.Objectiveto render every registered constraint or every registered objective.
(wherekeyword - only, default:None) - A DSL predicate (typically referencingpart’s properties, e.g.part.name == "cap_3") that further scopes the rendered rows. Requirespart.
(limit(int, keyword - only), default:None) - Positive integer cap on each rendered table’s row count, with rows selected as the top-N by.name(natural-sort). Forpartrendering, truncated output appends"(showing N of M)"where M is the count afterwhere=filtering; for full-problem rendering, the summary header already shows the totals so the per-section annotation is omitted.
(print_output(bool, keyword - only), default:True) - IfTrue, print the formatted output to stdout. Set toFalseto receive the string without printing.
Returns:
str- The formatted summary.
Raises:
ValueError- Ifpartis a variable subconcept or an unregistered Concept, ifwhereis passed withoutpart, or iflimitis not a positive integer.TypeError- Ifpartis not a Concept.
Examples:
Where cap is a constraint subconcept returned by
Problem.satisfy:
problem.display()problem.display(cap)problem.display(cap, where=cap.name == "cap_3")problem.display(cap, limit=5)Referenced By:
RelationalAI Documentation ├── Build With RelationalAI │ └── Understand how PyRel works > Use advanced reasoning > Prescriptive reasoning > Solve a decision problem │ ├── Create a Problem object │ │ └── Inspect a Problem withdisplay()│ ├── Add constraints │ │ └── InspectProblemconstraints │ └── Solve a decision problem │ └── Avoid common pitfalls └── Release Notes └── Python API Release Notes └── What’s New in Version 1.3.0 └── New Features and Enhancements
.solve()
Section titled “.solve()”Problem.solve( solver: str, *, time_limit_sec: float | None = None, silent: bool | None = None, solution_limit: int | None = None, relative_gap_tolerance: float | None = None, absolute_gap_tolerance: float | None = None, log_to_console: bool = False, print_only: bool = False, print_format: str | None = None, sensitivity: bool = False, conflict: bool = False, **solver_params: int | float | str | bool) -> NoneSolve the decision problem using a solver backend.
Declare decision variables first with Problem.solve_for. After solving,
read back solution values via the populated properties (e.g.
model.select(X.v)) or the Variable.values Property for engine-side
queries, result accessors such as Problem.termination_status and
Problem.objective_value, or Problem.solve_info for a Python-side snapshot.
Parameters:
(solverstr) - Solver name (for example"highs","minizinc","ipopt").
(time_limit_secfloat, default:None) - Maximum solve time in seconds. The solver service defaults to 300s if not provided.
(silentbool, default:None) - Whether to suppress solver output.
(solution_limitint, default:None) - Maximum number of solutions to return (when supported).
(relative_gap_tolerancefloat, default:None) - Relative optimality gap tolerance in[0, 1].
(absolute_gap_tolerancefloat, default:None) - Absolute optimality gap tolerance (>= 0).
(log_to_consolebool, default:False) - Whether to stream solver logs to stdout while the job runs.
(print_onlybool, default:False) - If True, request a text representation without solving. Results such asProblem.printed_modelare still accessible afterward.
(print_formatstr, default:None) - Text format for the printed model. Supported formats:"moi"(MOI text),"latex","mof"(MOI JSON),"lp","mps","nl"(AMPL).
(sensitivitybool, default:False) - Request post-solve sensitivity analysis — reduced costs, shadow prices, and basis statuses — exposed viaVariable.reduced_cost,Constraint.shadow_price, and.basis_status. Requires an objective and is incompatible withsolution_limit(sensitivity is defined for the single optimal point). Meaningful for continuous (LP/QP) solves; integer/MIP models have no meaningful duals, so the accessors come back empty (a warning is emitted). Coefficient/RHS ranging (allowable increase/decrease) is not provided. Uses wire schema version 2. Read them straight off the returned variable/constraint, the same attribute style as.name/.lower— e.g.model.select(var.name, var.reduced_cost); seeProblemVariable/ProblemConstraintfor the query idiom and the dual sign convention (it depends on the objective sense and the constraint direction / active bound — not the objective sense alone).
(conflictbool, default:False) - For an infeasible model, request a conflict / irreducible infeasible subsystem (IIS) — the constraints and variable bounds that cannot all be satisfied — exposed viaConstraint.in_conflictandVar.{lower,upper,integrality}_in_conflict, with the overall outcome insolve_info().conflict_status. Needs no objective (unlikesensitivity), so a pure feasibility problem can be diagnosed too. Uses wire schema version 2. Read membership with the bare predicate, e.g.where(con.in_conflict)(seeProblemConstraint).
(**solver_paramsint or float or str or bool, default:{}) - Raw solver-specific parameters passed through to the solver service.
Raises:
ValueError- If no decision variables have been declared viaProblem.solve_for; ifprint_only=Trueis combined withsensitivityorconflict; ifsensitivity=Trueis combined with a satisfaction problem (no objective) or withsolution_limit; if the Problem was already solved under a different result schema (re-solving the same Problem across a plain solve and a sensitivity/conflict solve is not supported — create a new Problem); or ifsensitivity/conflictis requested against a solver service too old to support wire schema version 2.TypeError- If any solver-specific parameter value is not anint,float,str, orbool, or ifnumeric_typeis passed via**solver_params(declare it viaProblem(model, Float)orProblem(model, Integer)instead).RuntimeError- If the solver job fails.TimeoutError- If the solver job does not reach a terminal state in time.
Notes:
Passing **solver_params emits a warning because options may not be
portable across solvers.
A failed solve leaves prior results intact. If a solve raises — a rejected
request (ValueError), a failed job (RuntimeError), or a timeout
(TimeoutError) — the Problem’s result state is unchanged: the last
successful solve’s values, Problem.solve_info, and any sensitivity /
conflict data all remain queryable, and solve_info() continues to
describe that last successful solve. The failed attempt’s partial output is
never imported. A fresh successful solve replaces the prior results.
Referenced By:
RelationalAI Documentation
├── Build With RelationalAI
│ └── Understand how PyRel works > Use advanced reasoning > Prescriptive reasoning
│ ├── Choose a backend
│ │ ├── Use Gurobi
│ │ │ └── Example
│ │ ├── Use Ipopt
│ │ │ └── Example
│ │ └── Use MiniZinc
│ │ └── Example
│ └── Solve a decision problem
│ ├── Overview
│ ├── Solve a decision problem
│ │ ├── What happens when you solve a problem
│ │ ├── Solve for feasibility
│ │ ├── Solve optimally
│ │ ├── Set a time limit
│ │ ├── Accept a near-optimal solution
│ │ ├── Tune solver backend behavior
│ │ ├── Print the translated solver model
│ │ ├── Check error details when termination status is not OPTIMAL
│ │ └── Handle solve failures
│ └── Work with solutions
└── Release Notes
└── Python API Release Notes
├── What’s New in Version 1.0.16
│ └── Performance and Reliability
└── What’s New in Version 1.4.1
└── Bug Fixes.variable_values()
Section titled “.variable_values()”Problem.variable_values(multiple: bool = False) -> b.FragmentReturn decision variable values from the first solution.
.. deprecated:
Use ``Variable.values`` instead for composable engine-side queries::
Var = problem.Variable val = Float.ref() # or Integer.ref() for Integer-typed problems model.select(Var.name, val).where(Var.values(0, val)) # first solution
# Multi-solution form (matches multiple=True): sol_idx = Integer.ref() model.select(sol_idx, Var.name, val).where(Var.values(sol_idx, val))Use this after Problem.solve to read variable values as a fragment you
can materialize with .to_df() or print with .inspect(). Without
multiple, the returned fragment reflects the first solution only.
Parameters:
(multiplebool, default:False) - If True, return values for all solutions from the most recentProblem.solvecall and include a 0-basedsol_indexcolumn.
Returns:
Fragment- A fragment with columnsnameandvalue(andsol_indexif multiple is True). Returns an empty DataFrame ifProblem.solvehas not been called.
Referenced By:
RelationalAI Documentation ├── Build With RelationalAI │ └── Understand how PyRel works > Use advanced reasoning > Prescriptive reasoning > Solve a decision problem │ ├── Add decision variables │ │ └── Declare decision variables │ └── Work with solutions │ ├── Determine how to access results │ └── Read solver-level variable values └── Release Notes └── Python API Release Notes └── What’s New in Version 1.0.13 └── Upgrade Notes
.load_point()
Section titled “.load_point()”Problem.load_point(point_index: int) -> NoneDeprecated: switching the active solution point is no longer supported.
.. deprecated:
Populated properties and :meth:`variable_values` always reflect thefirst solution (``sol_index = 0``, matching Python 0-basedindexing). ``load_point(0)`` is a no-op kept for backwardcompatibility and emits a :class:`DeprecationWarning`. Any otherindex raises :class:`NotImplementedError` — use``Variable.values(sol_idx, val)`` to query a specific solution::
Var = problem.Variable val = Float.ref() # or Integer.ref() for Integer-typed problems model.select(Var.name, val).where(Var.values(2, val)) # 3rd solutionParameters:
(point_indexint) - Must be0. Any other value raisesNotImplementedError.
Raises:
ValueError- If point_index is not a non-negative integer.NotImplementedError- If point_index is not0.
Referenced By:
RelationalAI Documentation └── Release Notes └── Python API Release Notes └── What’s New in Version 1.0.13 └── Upgrade Notes
.solve_info()
Section titled “.solve_info()”Problem.solve_info() -> SolveInfoDataReturn solver result metadata as a cached SolveInfoData.
Fetches all result metadata in a single query. Subsequent calls
return the cached snapshot until the next Problem.solve.
Returns:
SolveInfoData- Frozen dataclass with typed fields. Beyond the always-presenttermination_status/objective_value/solve_time_sec/num_points, it carries the booleansensitivity/conflictflags echoing what the solve requested,conflict_status(aftersolve(conflict=True)),primal_status/dual_status(aftersolve(sensitivity=True)), and a rawancillarymap of any other solver metadata the service reports; seeSolveInfoData. Useprint(si)orsi.display()for a formatted summary. IfProblem.solvehas not been called, all fields are None (erroris()).
Referenced By
Section titled “Referenced By”RelationalAI Documentation ├── Build With RelationalAI │ └── Understand how PyRel works > Use advanced reasoning > Prescriptive reasoning │ ├── Choose a backend │ │ ├── Use HiGHS │ │ │ └── Example │ │ ├── Use Gurobi │ │ │ └── Example │ │ ├── Use Ipopt │ │ │ └── Example │ │ └── Use MiniZinc │ │ └── Example │ └── Solve a decision problem │ ├── Overview │ │ ├── How PyRel represents a decision problem │ │ └── How solving a decision problem works │ ├── Create a Problem object │ │ ├── Choose a default numeric type │ │ ├── Create a Problem object │ │ └── Inspect a Problem withdisplay()│ ├── Add decision variables │ │ ├── Declare decision variables │ │ ├── Choose variable types and bounds │ │ └── InspectProblemvariables │ ├── Add constraints │ │ ├── Add constraints withProblem.satisfy()│ │ ├── InspectProblemconstraints │ │ └── Avoid common pitfalls │ ├── Solve a decision problem │ │ ├── What happens when you solve a problem │ │ ├── Validate before you solve │ │ ├── Solve for feasibility │ │ ├── Solve optimally │ │ ├── Inspect solve metadata │ │ ├── Set a time limit │ │ ├── Accept a near-optimal solution │ │ ├── Tune solver backend behavior │ │ ├── Print the translated solver model │ │ ├── Check error details when termination status is notOPTIMAL│ │ ├── Handle solve failures │ │ └── Avoid common pitfalls │ └── Work with solutions │ ├── Determine how to access results │ └── Read solver-level variable values └── Release Notes └── Python API Release Notes ├── What’s New in Version 1.0.13 │ └── Upgrade Notes ├── What’s New in Version 1.0.16 │ └── Performance and Reliability ├── What’s New in Version 1.2.0 │ └── Bug Fixes ├── What’s New in Version 1.3.0 │ └── New Features and Enhancements └── What’s New in Version 1.4.1 └── Bug Fixes