quicopt.pyomo¶
pyomo ¶
quicopt.pyomo — a Pyomo model → a Quicopt Program.
Reads the model at Pyomo's expression-tree level: the single active objective and
each constraint are walked into Quicopt's expression graph, variable bounds and
domain into VarDecl, and each constraint into a Zero/Nonneg row. The
result is a flat Program (one scalar VarDecl per Pyomo VarData; Pyomo
has already expanded indexed components).
An operator the service does not support raises, rather than being approximated or dropped — so a model that imports is a model that means what it says.
Example
Maximize 3x + 5y subject to x + 2y ≤ 5, with 0 ≤ x ≤ 4 and y
binary:
import pyomo.environ as pyo
from quicopt import Client
m = pyo.ConcreteModel()
m.x = pyo.Var(bounds=(0, 4))
m.y = pyo.Var(domain=pyo.Binary)
m.cap = pyo.Constraint(expr=m.x + 2 * m.y <= 5)
m.obj = pyo.Objective(expr=3 * m.x + 5 * m.y, sense=pyo.maximize)
result = Client().solve(m) # the import below happens inside
print(result.status, result.objective) # optimal 14.0
print(result.solution) # {'x1': 3.0, 'x2': 1.0}
Client.solve takes the Pyomo model as it
stands and calls this module on the way out, so the import is not a step to
perform — reach for import_model only to hold the Program itself.
Note what the solution is keyed by: x1/x2, not m.x/m.y.
Variables are named positionally, because Pyomo has already expanded indexed
components and declaration order is what is left to identify them by.
import_model ¶
Convert a Pyomo ConcreteModel into a Quicopt Program.
Each VarData becomes a scalar VarDecl (x{i} in declaration order)
carrying its bounds and domain (Binary→BINARY, integer domains→
INTEGER, else CONTINUOUS); the active objective and every constraint
become Quicopt expressions. Requires exactly one active objective; an absent
Pyomo variable bound means unbounded in that direction (±Inf). A variable that
is fixed but carries no value raises: its pin has no value to pin to, and the ±Inf
reading of an absent bound would turn it into a free variable instead.
A variable given a distribution (see quicopt.stochastic) is not a decision
at all: it leaves the variable list, becomes a named declaration in
Program.sources, and its every use becomes a reference to that name. The
scenario count and seed ride along as model data. Names are assigned across
all variables before the random ones are set aside, so which variables are
random never renumbers the rest. A model that declares no uncertainty imports
to exactly the Program it always did.