Working with Expressions
Expressions are the mathematical building blocks of optimization models in LunaModel. They represent combinations of variables, constants, and mathematical operations that form objectives and constraints.
Overview
LunaModel supports expressions of arbitrary degree:
- Linear: Sum of variables with coefficients (e.g.,
3x + 2y + 5) - Quadratic: Products of two variables (e.g.,
x*y + 2x² + y²) - Higher-Order: Products of three or more variables (e.g.,
x*y*z)
All expression types can be mixed in a single expression, giving you complete flexibility in modeling.
Creating Expressions
From Variables
The most common way to create expressions is through mathematical operations on variables:
from luna_model import Model, Vtype
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
z = model.add_variable("z", vtype=Vtype.BINARY)
# Linear expression
linear = 3 * x + 2 * y + 5
# Quadratic expression
quadratic = x * y + 2 * x * x
# Higher-order expression
higher = x * y * z
Using quicksum
For creating sums with many terms, use quicksum() for better performance:
from luna_model.utils import quicksum
# Inefficient (for large n)
expr = sum(coeffs[i] * vars[i] for i in range(n))
# Efficient
expr = quicksum(coeffs[i] * vars[i] for i in range(n))
# With starting value
expr = quicksum(terms, start=100)
Performance Tip: quicksum() is significantly faster than Python's built-in sum() or repeated + operations, especially for large models.
Expression Types
Linear Expressions
Linear expressions contain only first-degree terms:
# Simple linear
expr = 3 * x + 2 * y
# With constant
expr = 3 * x + 2 * y + 10
# Many terms
n = 100
variables = [model.add_variable(f"x_{i}") for i in range(n)]
expr = quicksum(i * variables[i] for i in range(n))
Properties:
- Degree: 1
- Form: c₀ + c₁x₁ + c₂x₂ + ... + cₙxₙ
- Supported by all solvers
Quadratic Expressions
Quadratic expressions contain products of two variables:
# Product terms
expr = x * y + 2 * x * z
# Quadratic form (x² + 2xy + y²)
expr = x * x + 2 * x * y + y * y
# Mixed linear and quadratic
expr = 3 * x + x * y + 2 * y
Properties:
- Degree: 2
- Form includes terms like: xᵢxⱼ, xᵢ²
- Supported by QUBO, BQM, and many classical solvers
Higher-Order Expressions
LunaModel supports polynomial expressions of arbitrary degree:
# Cubic expression
expr = x * y * z
# Mixed degrees
expr = x * y * z + x * y + x + 1
# Even higher order
expr = w * x * y * z + w * x * y
Properties: - Degree: 3 or higher - Requires transformation to be solved by most solvers - Use transformations to reduce degree when needed
Expression Operations
Arithmetic Operations
Expressions support standard arithmetic operations:
# Addition
expr1 = 2 * x + 3 * y
expr2 = x + y
combined = expr1 + expr2 # 3x + 4y
# Subtraction
diff = expr1 - expr2 # x + 2y
# Multiplication by scalar
scaled = 2 * expr1 # 4x + 6y
scaled = expr1 * 2 # Same result
# Negation
negated = -expr1 # -2x - 3y
# Division by scalar (multiplication by reciprocal)
halved = expr1 / 2 # x + 1.5y
Powers and Products
# Squaring a variable
x_squared = x * x
x_squared = x**2 # Alternative syntax
# Variable products
product = x * y * z
# Expression multiplication creates higher-order terms
expr1 = x + y
expr2 = y + z
product = expr1 * expr2 # xy + xz + y² + yz
Comparison Operations
Expressions can be compared to create constraints:
# Less than or equal
constraint = 3 * x + 2 * y <= 10
# Greater than or equal
constraint = x + y >= 5
# Equality
constraint = 2 * x + y == 7
Expression Properties
Degree
Get the degree of an expression:
linear = 3 * x + 2 * y
print(linear.degree()) # 1
quadratic = x * y + 2 * x * x
print(quadratic.degree()) # 2
higher = x * y * z
print(higher.degree()) # 3
Variables
Extract variables used in an expression:
expr = 3 * x + 2 * y + x * z
# Get all variables
variables = expr.variables()
print(variables) # [x, y, z]
# Check if variable is present
if x in expr.variables():
print("x is in the expression")
Evaluation
Evaluate expressions with specific variable values:
from luna_model import Expression
expr = 3 * x + 2 * y + x * y
# Evaluate with dictionary
value = expr.evaluate({x.name: 1, y.name: 2})
print(value) # 3(1) + 2(2) + 1(2) = 9
Building Complex Expressions
Incrementally
For large models, build expressions using quicksum:
from luna_model.utils import quicksum
# Efficient: use quicksum
terms = [coeffs[i] * variables[i] for i in range(n)]
expr = quicksum(terms)
With Conditionals
Build expressions based on conditions:
expr = quicksum(
coeff * var
for coeff, var in zip(coeffs, variables)
if coeff > 0 # Only include positive coefficients
)
Nested Structures
Create complex expressions from simpler ones:
# Create sub-expressions
shipping_cost = quicksum(costs[i, j] * flow[i, j]
for i in origins
for j in destinations)
inventory_cost = quicksum(holding[i] * inventory[i]
for i in warehouses)
# Combine
total_cost = shipping_cost + inventory_cost
Expression Types Reference
LunaModel creates appropriate expression types automatically based on operations performed on variables. The degree of an expression indicates its type:
- Degree 0: Constant value
- Degree 1: Linear expression
- Degree 2: Quadratic expression
- Degree 3+: Higher-order polynomial expression
You typically don't need to worry about specific expression classes - they are created automatically when you perform operations on variables.
Common Patterns
Objective Functions
# Minimize distance
model.objective = quicksum(
distances[i, j] * routes[i, j]
for i in cities
for j in cities
)
# Maximize profit
model.objective = quicksum(
prices[i] * quantities[i] - costs[i] * quantities[i]
for i in products
)
Constraint Expressions
# Capacity constraint
model.constraints += quicksum(
sizes[i] * items[i] for i in range(n)
) <= capacity
# Balance constraint
model.constraints += quicksum(
incoming[i] for i in inputs
) == quicksum(
outgoing[j] for j in outputs
)
Quadratic Objectives
# Portfolio variance minimization
model.objective = quicksum(
covariance[i, j] * allocations[i] * allocations[j]
for i in assets
for j in assets
)
Best Practices
Performance
-
Use quicksum() for multiple terms:
-
Build expressions once, reuse when possible:
-
Avoid deep nesting in loops:
Clarity
-
Name sub-expressions for readability:
-
Break complex expressions into parts:
Python# Good fixed_costs = quicksum(setup[i] * is_open[i] for i in facilities) variable_costs = quicksum(unit_cost[i] * quantity[i] for i in facilities) total_costs = fixed_costs + variable_costs # Less clear total_costs = quicksum(setup[i] * is_open[i] + unit_cost[i] * quantity[i] for i in facilities)
Type Safety
- Use consistent types in expressions:
Next Steps
- Constraints - Learn how to create constraints from expressions
- Solutions - Evaluate expressions with solution values
- API Reference: Expression - Detailed API documentation