Variable Types
LunaModel supports four different variable types, each suited for different optimization scenarios.
Overview of Variable Types
| Type | Domain | Use Case |
|---|---|---|
BINARY |
{0, 1} | Yes/no decisions, selection problems |
INTEGER |
Whole numbers | Counting, discrete quantities |
SPIN |
{-1, +1} | Ising models, quantum annealing |
REAL |
Continuous | Measurements, rates, percentages |
Binary Variables
Binary variables represent boolean decisions: yes/no, true/false, selected/not selected.
Creating Binary Variables
from luna_model import Model, Vtype
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
Common Use Cases
Selection problems:
# Select items from a set
items = ['A', 'B', 'C', 'D']
selected = {item: model.add_variable(f"select_{item}", vtype=Vtype.BINARY)
for item in items}
# Select exactly 2 items
model.add_constraint(sum(selected.values()) == 2)
Activation/deactivation:
# Activate at most 3 features
features = [model.add_variable(f"feature_{i}", vtype=Vtype.BINARY)
for i in range(5)]
model.add_constraint(sum(features) <= 3)
Logical conditions:
# If x then y
x = model.add_variable("x", vtype=Vtype.BINARY)
y = model.add_variable("y", vtype=Vtype.BINARY)
model.add_constraint(y <= x) # y can only be 1 if x is 1
# x OR y (at least one must be true)
model.add_constraint(x + y >= 1)
# x XOR y (exactly one must be true)
model.add_constraint(x + y == 1)
Properties
- Domain: {0, 1}
- No need to specify bounds (implicit)
- Most efficient for boolean decisions
- Can be converted to/from SPIN variables
Integer Variables
Integer variables represent discrete quantities that can take whole number values.
Creating Integer Variables
# Basic integer variable
x = model.add_variable("x", vtype=Vtype.INTEGER, lower=0, upper=100)
# Unbounded integer (not recommended)
y = model.add_variable("y", vtype=Vtype.INTEGER)
# Negative integers allowed
z = model.add_variable("z", vtype=Vtype.INTEGER, lower=-10, upper=10)
Common Use Cases
Counting:
# Number of items to produce
num_items = model.add_variable("num_items",
vtype=Vtype.INTEGER,
lower=0,
upper=1000)
# Number of workers to assign
num_workers = model.add_variable("num_workers",
vtype=Vtype.INTEGER,
lower=1, # At least one worker
upper=50)
Quantities:
# Inventory levels
inventory = {}
for product in ['A', 'B', 'C']:
inventory[product] = model.add_variable(
f"inv_{product}",
vtype=Vtype.INTEGER,
lower=0, # Non-negative
upper=10000 # Warehouse capacity
)
Discrete choices:
# Select a priority level (1-5)
priority = model.add_variable("priority", vtype=Vtype.INTEGER, lower=1, upper=5)
# Time periods (0-23 for hours)
hour = model.add_variable("hour", vtype=Vtype.INTEGER, lower=0, upper=23)
Properties
- Domain: Whole numbers (ℤ)
- Always specify bounds (lb and ub) for better performance
- More flexible than BINARY but slower to optimize
- Can be converted to BINARY variables using transformations
Integer vs. Binary
When to use INTEGER vs. multiple BINARY variables:
# Approach 1: Single integer (more compact)
quantity = model.add_variable("qty", vtype=Vtype.INTEGER, lower=0, upper=5)
# Approach 2: Multiple binaries (more constraints)
# Useful when you need to reference individual quantities
binary_vars = [model.add_variable(f"qty_{i}", vtype=Vtype.BINARY)
for i in range(6)]
quantity_value = sum(i * binary_vars[i] for i in range(6))
model.add_constraint(sum(binary_vars) == 1) # Exactly one selected
Use INTEGER when: - The value itself is important - Range is large (>10) - Linear relationships suffice
Use multiple BINARYs when: - You need to reference specific values - Complex logical constraints apply - Targeting binary optimization solvers
Spin Variables
Spin variables are used in Ising models and quantum annealing applications.
Creating Spin Variables
Common Use Cases
Ising models:
# Create spin system
n = 5
spins = [model.add_variable(f"s_{i}", vtype=Vtype.SPIN) for i in range(n)]
# Ising objective: minimize energy
# H = -Σ J_ij * s_i * s_j - Σ h_i * s_i
J = [[0, 1, -1, 0, 0],
[1, 0, 1, -1, 0],
[-1, 1, 0, 1, -1],
[0, -1, 1, 0, 1],
[0, 0, -1, 1, 0]]
energy = 0
for i in range(n):
for j in range(i+1, n):
energy = energy - J[i][j] * spins[i] * spins[j]
model.objective = energy
Quantum annealing:
# Spins for quantum annealer
qubits = [model.add_variable(f"q_{i}", vtype=Vtype.SPIN)
for i in range(10)]
# QUBO-style objective
objective = sum(qubits[i] * qubits[j]
for i in range(10)
for j in range(i+1, 10))
model.objective = objective
Properties
- Domain: {-1, +1}
- No bounds needed (implicit)
- Natural for physics-inspired optimization
- Can be converted to/from BINARY variables
Spin-Binary Conversion
Relationship between SPIN and BINARY:
# Mathematical relationship:
# spin = 2 * binary - 1
# binary = (spin + 1) / 2
# Convert using transformation passes
from luna_model.transformation import PassManager
from luna_model.transformation.passes import BinarySpinPass
# Convert all to SPIN
pm = PassManager([BinarySpinPass(Vtype.SPIN, None)])
result = pm.run(model)
spin_model = result.model
# Convert all to BINARY
pm = PassManager([BinarySpinPass(Vtype.BINARY, None)])
result = pm.run(model)
binary_model = result.model
Real Variables
Real variables represent continuous quantities.
Creating Real Variables
# Bounded real variable
x = model.add_variable("x", vtype=Vtype.REAL, lower=0.0, upper=1.0)
# Percentage
percentage = model.add_variable("pct", vtype=Vtype.REAL, lower=0.0, upper=100.0)
# Rate (can be negative)
growth_rate = model.add_variable("rate", vtype=Vtype.REAL, lower=-0.5, upper=0.5)
Common Use Cases
Continuous measurements:
# Temperature, pressure, etc.
temperature = model.add_variable("temp",
vtype=Vtype.REAL,
lower=-20.0,
upper=120.0)
Fractions and ratios:
# Allocation percentage (0-1)
allocation = model.add_variable("alloc", vtype=Vtype.REAL, lower=0.0, upper=1.0)
# Multiple allocations that sum to 1
n = 5
allocations = [model.add_variable(f"alloc_{i}", vtype=Vtype.REAL, lower=0.0)
for i in range(n)]
model.add_constraint(sum(allocations) == 1.0)
Relaxations:
# LP relaxation of integer/binary problems
# (solve continuous version first, then round)
x_relaxed = model.add_variable("x_relax", vtype=Vtype.REAL, lower=0.0, upper=1.0)
Properties
- Domain: Real numbers (ℝ)
- Requires explicit bounds for most solvers
- Most flexible but may need rounding for discrete problems
- Generally faster to optimize than INTEGER
Mixed Variable Types
You can mix different variable types in the same model:
model = Model()
# Binary decisions
is_active = model.add_variable("active", vtype=Vtype.BINARY)
# Integer quantities
quantity = model.add_variable("qty", vtype=Vtype.INTEGER, lower=0, upper=100)
# Real measurements
efficiency = model.add_variable("eff", vtype=Vtype.REAL, lower=0.0, upper=1.0)
# Constraints mixing types
model.add_constraint(quantity <= 100 * is_active) # Big-M constraint
model.add_constraint(efficiency * quantity >= 50) # Bilinear constraint
Type Conversions
LunaModel provides built-in transformations:
Binary ↔ Spin
from luna_model.transformation import PassManager
from luna_model.transformation.passes import BinarySpinPass
model = Model()
x = model.add_variable("x", vtype=Vtype.BINARY)
s = model.add_variable("s", vtype=Vtype.SPIN)
# Convert everything to SPIN
pm = PassManager([BinarySpinPass(Vtype.SPIN, None)])
result = pm.run(model)
Integer → Binary
Choosing the Right Type
Decision guide:
| Question | Answer | Recommended Type |
|---|---|---|
| Is it a yes/no decision? | Yes | BINARY |
| Is it a discrete count or quantity? | Small range (< 10) | Consider BINARY |
| Is it a discrete count or quantity? | Large range | INTEGER |
| Is it for quantum annealing? | Yes | SPIN |
| Is it continuous or fractional? | Yes | REAL |
Performance Considerations
| Variable Type | Speed | Support | Notes |
|---|---|---|---|
| BINARY | ✅ Fastest | ✅ Universal | Best for boolean decisions, easy to interpret |
| INTEGER | ⚠️ Slower | ✅ Good | More compact than multiple binaries, always set bounds |
| SPIN | ⚠️ Varies | ⚠️ Limited | Natural for quantum annealing, requires conversion for classical solvers |
| REAL | ✅ Fast | ✅ Good | Fastest to optimize, may need rounding for discrete problems |
Examples by Domain
Logistics (BINARY + INTEGER)
# Route selection (binary)
routes = [model.add_variable(f"route_{i}", vtype=Vtype.BINARY)
for i in range(10)]
# Package quantities (integer)
packages = model.add_variable("packages", vtype=Vtype.INTEGER, lower=0, upper=100)
Scheduling (BINARY + REAL)
# Task assignment (binary)
assigned = model.add_variable("assigned", vtype=Vtype.BINARY)
# Task duration (real)
duration = model.add_variable("duration", vtype=Vtype.REAL, lower=0.0, upper=8.0)
Quantum Computing (SPIN)
# All spin for quantum annealer
qubits = [model.add_variable(f"q_{i}", vtype=Vtype.SPIN)
for i in range(100)]
Financial Optimization (REAL)
# Portfolio weights (real, sum to 1)
n_assets = 10
weights = [model.add_variable(f"w_{i}", vtype=Vtype.REAL, lower=0.0)
for i in range(n_assets)]
model.add_constraint(sum(weights) == 1.0)
Next Steps
- Expressions - Build expressions with your variables
- Constraints - Add constraints for different variable types