Built-in Transformations
LunaModel provides built-in pipelines and passes for common model analysis and transformations.
Available Pipelines
ToUnconstrainedBinaryPipeline
Convert a model to an unconstrained binary model.
This pipeline transforms any model with constraints to an unconstrained binary model.
It allows the input model to contain binary, spin or integer variables. Spin and integer
variables are automatically converted to a binary representation. If the input model has
linear constraints they are added to the model's objective as quadratic penalties scaled by
the the maximum bias of the input model's objective times the penalty_scaling paramter.
Warning
If the model's constraints are not linear, an error is raised.
Available Transformation Passes
BinarySpinPass
Convert between Binary and Spin variable types.
Purpose: Transform variables between {0, 1} (binary) and {-1, +1} (spin) domains.
Usage:
from luna_model.transformation.passes import BinarySpinPass
from luna_model import Vtype
# Binary → Spin
pass_to_spin = BinarySpinPass(vtype=Vtype.SPIN, prefix=None)
# Spin → Binary
pass_to_binary = BinarySpinPass(vtype=Vtype.BINARY, prefix=None)
Example:
from luna_model import Model, Vtype
from luna_model.transformation.passes import BinarySpinPass
from luna_model.transformation import PassManager
# Model with binary variables
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
# Convert to spin
pm = PassManager([BinarySpinPass(Vtype.SPIN, prefix=None)])
ir = pm.run(model)
spin_model = ir.model
ChangeSensePass
Change optimization sense between minimization and maximization.
Purpose: Convert MIN ↔ MAX by negating the objective function.
Usage:
from luna_model.transformation.passes import ChangeSensePass
from luna_model import Sense
# To minimization
pass_min = ChangeSensePass(sense=Sense.MIN)
# To maximization
pass_max = ChangeSensePass(sense=Sense.MAX)
Example:
from luna_model import Model, Sense
from luna_model.transformation import PassManager
# Maximization model
model = Model(sense=Sense.MAX)
x = model.add_variable("x")
y = model.add_variable("y")
model.objective = 3 * x + 2 * y
# Convert to minimization
pm = PassManager([ChangeSensePass(Sense.MIN)])
ir = pm.run(model)
min_model = ir.model
assert min_model.sense == Sense.MIN
EqualityConstraintsToQuadraticPenaltyPass
Move all equality constraints to the model's objective as a quadratic penalties.
Purpose: Make the model unconstrained by moving all equality constraints to the objective as quadratic penalties.
Note
This pass requires the MaxBiasAnalysis pass to be executed before this pass is executed.
Caution
Less-equal (<=) or greater-equal (>=) constraints are not respected by this transformation
and have to be handled before this pass using, e.g., the GeToLeConstraintsPass and the LeToEqConstraintsPass.
Example:
from luna_model import Model, Vtype, Sense
from luna_model.transformation import PassManager
from luna_model.transformation.passes import MaxBiasAnalysis
from luna_model.transformation.passes import EqualityConstraintsToQuadraticPenaltyPass
model = Model(sense=Sense.MAX)
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
model.constraints += x + y == 0
pm = PassManager(
[
MaxBiasAnalysis(),
EqualityConstraintsToQuadraticPenaltyPass(),
]
)
ir = pm.run(model)
print(ir.model.objective)
# 41 x y + 21 x + 18 y
GeToLeConstraintsPass
Convert the model's constraints by chaning all greater-equal (>=) constraints to less-equal (<=) constraints.
Purpose: Simplify the kind of constraints used in the model.
Example:
from luna_model import Model, Vtype, Sense
from luna_model.transformation import PassManager
from luna_model.transformation.passes import GeToLeConstraintsPass
model = Model(sense=Sense.MAX)
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
model.constraints += x + y >= 0, "ge_constr"
pm = PassManager([GeToLeConstraintsPass()])
ir = pm.run(model)
ir.model.constraints["ge_constr"]
# -x - y <= 0
LeToEqConstraintsPass
Convert the model's constraints by chaning all less-equal (<=) constraints to equality (==) constraints.
Purpose: Simplify the kind of constraints used in the model.
Example:
from luna_model import Model, Vtype, Sense
from luna_model.transformation import PassManager
from luna_model.transformation.passes import MinValueForConstraintAnalysis
from luna_model.transformation.passes import LeToEqConstraintsPass
model = Model(sense=Sense.MAX)
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
model.constraints += x + y <= 3, "le_constr"
pm = PassManager([MinValueForConstraintAnalysis(), LeToEqConstraintsPass()])
ir = pm.run(model)
ir.model.constraints["le_constr"]
# x + y + slack_le_constr == 0
print(ir.model.get_variable("slack_le_constr"))
# slack_le_constr: Integer(lower=0, upper=3)
IntegerToBinaryPass
Convert integer variables to a binary representation.
Purpose: Transform the variables of type integer to be represented by binary typed variables.
Example:
from luna_model import Model, Vtype
from luna_model.transformation import PassManager
from luna_model.transformation.passes import BinarySpinPass
model = Model()
x = model.add_variable("x", vtype=Vtype.INTEGER, lower=0, upper=3)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
pm = PassManager([IntegerToBinaryPass()])
ir = pm.run(model)
print(ir.model.objective)
# y x_b0 + 2 y x_b1 - 2 y + x_b0 + 2 x_b1
Available Analysis Passes
MaxBiasAnalysis
Analysis pass that computes the maximum absolute coefficient value in the objective.
Purpose: Analyze objective coefficients to determine scaling needs.
Usage:
from luna_model.transformation.passes import MaxBiasAnalysis
# Create analysis pass
analysis = MaxBiasAnalysis()
Example:
from luna_model import Model
from luna_model.transformation import PassManager
from luna_model.transformation.passes import MaxBiasAnalysis
model = Model()
x = model.add_variable("x")
y = model.add_variable("y")
model.objective = 1000 * x + 500 * y
# Run analysis
pm = PassManager([MaxBiasAnalysis()])
ir = pm.run(model)
CheckModelSpecsAnalysis
Analysis pass that checks the model's specs for correctness.
Purpose: Ensures the input model satisfies the specifications expected by any following passes.
Example
from luna_model import Model, Vtype
from luna_model.transformation import PassManager
from luna_model.transformation.passes import CheckModelSpecsAnalysis
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
specs = ModelSpecs(max_degree=2)
pm = PassManager([CheckModelSpecsAnalysis(specs)])
ir = pm.run(model)
MinValueForConstraintAnalysis
Analysis pass that computes the min value possible for all constraints.
Purpose: Specific information on the model's constraints that can be used by other passes in the model's transformation.
Example
from luna_model import Model, Vtype
from luna_model.transformation import PassManager
from luna_model.transformation.passes import CheckModelSpecsAnalysis
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
model.constraints += -5 * x + y <= 2, "my-constraint"
pm = PassManager([MinValueForConstraintAnalysis()])
ir = pm.run(model)
print(ir.cache["min-value-for-constraint"].vals)
# {'my-constraint': -5.0}
SpecsAnalysis
Analysis pass that computes the model's specs and stores them in the cache.
Purpose: This analysis pass can, for example, be used in combination with the IfElsePass to guide the transformation depending on the specifications (ModelSpecs) of a model.
Note
This pass is not to be confused with the CheckModelSpecsAnalysis, which checks
if an input model's specs satisfy a given set of ModelSpecs.
Example
from luna_model import Model, Vtype
from luna_model.transformation import PassManager
from luna_model.transformation.passes import SpecsAnalysis
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.objective = x * y + x - 2 * y
pm = PassManager([SpecsAnalysis()])
ir = pm.run(model)
print(ir.cache["specs"])
# ModelSpecs(sense=Minimize, vtype=Binary, constraints=, max_degree=2, max_constraint_degree=0, max_num_variables=2)
Combining Passes
from luna_model.transformation import PassManager
from luna_model.transformation.passes import (
ChangeSensePass,
BinarySpinPass,
)
from luna_model import Sense, Vtype
# Example: prepare for quantum annealer
pm = PassManager([
ChangeSensePass(Sense.MIN),
BinarySpinPass(Vtype.SPIN, prefix=None),
])
ir = pm.run(model)
transformed_model = ir.model
See Also
- PassManager - Using passes with PassManager
- Custom Passes - Creating custom passes