quicopt.pyomo — a Pyomo model → Quicopt Program IR.
A front-end of the client. Imports at Pyomo's expression-tree level: the
single active objective and each constraint are walked into the IR expression graph,
variable bounds + domain into VarDecl, and function-in-set constraints into
Zero/Nonneg rows. The result is a flat Program (one scalar VarDecl
per Pyomo VarData; Pyomo has already expanded indexed components).
An operator absent from the service's catalog is a coverage gap to resolve in the
service, never papered over here.
import_model
Convert a Pyomo ConcreteModel into Quicopt's Program IR.
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 IR expressions. Requires exactly one active objective; an absent Pyomo
variable bound is taken as ±Inf (a free direction).
Source code in src/quicopt/pyomo.py
| def import_model(m):
"""Convert a Pyomo ``ConcreteModel`` into Quicopt's ``Program`` IR.
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 IR expressions. Requires exactly one active objective; an absent Pyomo
variable bound is taken as ±Inf (a free direction).
"""
vis = list(m.component_data_objects(pyo.Var))
name = {id(v): f"x{i + 1}" for i, v in enumerate(vis)}
var = lambda v: Var(name[id(v)], ())
vars = []
for v in vis:
domain = BINARY if v.is_binary() else INTEGER if v.is_integer() else CONTINUOUS
lb, ub = (v.value, v.value) if v.fixed else (v.lb, v.ub) # a fixed var ⇒ a [val, val] pin
lb = -inf if lb is None else lb # an absent Pyomo bound ⇒ ±Inf (free)
ub = inf if ub is None else ub
start = v.value if v.value is not None else min(max(0.0, lb), ub) # clamp 0 into [lb, ub]
vars.append(VarDecl(name[id(v)], [], domain, float(lb), float(ub), float(start)))
objs = list(m.component_data_objects(pyo.Objective, active=True))
if len(objs) != 1:
raise ValueError(f"expected exactly one active objective, found {len(objs)}")
sense = "min" if objs[0].sense == pyo.minimize else "max"
objective = _expr(objs[0].expr, var)
cons = []
for c in m.component_data_objects(pyo.Constraint, active=True):
_emit(cons, _expr(c.body, var), c)
return Program(vars=vars, objective=objective, sense=sense, constraints=cons)
|