Skip to content

quicopt.pyomo

pyomo

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

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 (BinaryBINARY, 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)