Variables Guide¶
Variables represent the decisions to be made in your optimization model.
Variable Families¶
Key Concept: In LumiX, an LXVariable is not a single variable,
but a family that expands to multiple solver variables based on your data.
# This ONE LXVariable object represents MANY solver variables
production = LXVariable[Product, float]("production").from_data(products)
# Automatically expands to:
# production[product1], production[product2], production[product3], ...
Variable Types¶
Continuous Variables¶
Real-valued variables for quantities that can take any value:
production = (
LXVariable[Product, float]("production")
.continuous() # Default type
.bounds(lower=0, upper=1000)
.from_data(products)
)
Use for: Production quantities, percentages, weights, allocations
Integer Variables¶
Whole number variables:
num_trucks = (
LXVariable[Route, int]("trucks")
.integer()
.bounds(lower=0, upper=10)
.from_data(routes)
)
Use for: Counts, discrete quantities, number of items
Binary Variables¶
Yes/no decision variables (0 or 1):
is_open = (
LXVariable[Facility, int]("is_open")
.binary() # Automatically sets bounds to [0, 1]
.from_data(facilities)
)
Use for: Selection, activation, yes/no decisions
Indexing¶
Single-Model Indexing¶
Index variables by a single data model:
production = (
LXVariable[Product, float]("production")
.continuous()
.indexed_by(lambda p: p.id) # Index function
.from_data(products) # Data source
)
Index Options:
Simple: .indexed_by(lambda p: p.id)
Tuple: .indexed_by(lambda p: (p.category, p.id))
String: .indexed_by(lambda p: f”{p.factory}_{p.id}”)
Multi-Model Indexing¶
Index by multiple models using Cartesian product:
from lumix import LXIndexDimension, LXCartesianProduct
assignment = (
LXVariable[tuple[Driver, Date, Shift], int]("assignment")
.binary()
.indexed_by_product(
LXIndexDimension(Driver, lambda d: d.id).from_data(drivers),
LXIndexDimension(Date, lambda dt: dt.id).from_data(dates),
LXIndexDimension(Shift, lambda s: s.id).from_data(shifts),
)
)
This creates variables for every combination of driver, date, and shift.
Filtering¶
Filter which instances to include:
Single-Model Filter¶
production = (
LXVariable[Product, float]("production")
.continuous()
.where(lambda p: p.is_active and p.stock > 0)
.from_data(products)
)
Multi-Model Filter¶
assignment = (
LXVariable[tuple[Driver, Date], int]("assignment")
.binary()
.indexed_by_product(
LXIndexDimension(Driver, lambda d: d.id)
.where(lambda d: d.is_qualified) # Per-dimension filter
.from_data(drivers),
LXIndexDimension(Date, lambda dt: dt.id)
.from_data(dates),
)
.where_multi(lambda driver, date: # Cross-dimension filter
date not in driver.days_off
)
)
Data Sources¶
Direct Data¶
Provide data directly:
products = [Product(id="A", cost=10), Product(id="B", cost=20)]
production = (
LXVariable[Product, float]("production")
.from_data(products)
)
ORM Integration¶
Query from database:
from sqlalchemy.orm import Session
production = (
LXVariable[Product, float]("production")
.from_model(Product, session=db_session)
)
Cartesian Product¶
For multi-dimensional variables:
from lumix import LXCartesianProduct
assignment = (
LXVariable[tuple[Driver, Date], int]("assignment")
.from_data(LXCartesianProduct(drivers, dates))
)
Cost Coefficients¶
Define objective coefficients:
Single-Model¶
production = (
LXVariable[Product, float]("production")
.cost(lambda p: p.unit_profit) # Coefficient from data
.from_data(products)
)
# Use in objective
model.maximize(
LXLinearExpression().add_term(production, lambda p: p.profit)
)
Multi-Model¶
shipment = (
LXVariable[tuple[Origin, Destination], float]("shipment")
.cost_multi(lambda o, d: calculate_shipping_cost(o, d))
.indexed_by_product(...)
)
Bounds¶
Set variable bounds:
Simple Bounds¶
production = (
LXVariable[Product, float]("production")
.bounds(lower=0, upper=1000)
.from_data(products)
)
Data-Driven Bounds¶
Bounds can vary per instance:
# Use where clause for conditional bounds
production = (
LXVariable[Product, float]("production")
.continuous()
.bounds(lower=0)
.from_data(products)
)
# Add constraints for upper bounds from data
model.add_constraint(
LXConstraint[Product]("max_production")
.expression(LXLinearExpression().add_term(production, 1.0))
.le()
.rhs(lambda p: p.max_capacity)
.from_data(products)
)
Best Practices¶
Use Type Annotations
# Good: Type-safe production = LXVariable[Product, float]("production") # Bad: No type safety production = LXVariable("production")
Name Variables Clearly
# Good: Descriptive daily_production = LXVariable[Product, float]("daily_production") # Bad: Cryptic x = LXVariable[Product, float]("x")
Use Fluent API
# Good: Readable chain production = ( LXVariable[Product, float]("production") .continuous() .bounds(lower=0) .from_data(products) )
Filter Early
# Good: Filter at variable level production = ( LXVariable[Product, float]("production") .where(lambda p: p.is_active) .from_data(products) ) # Less efficient: Filter in constraints # Creates unnecessary variables
Common Patterns¶
Production Planning¶
production = (
LXVariable[Product, float]("production")
.continuous()
.bounds(lower=0)
.cost(lambda p: -p.unit_cost) # Negative for minimization
.from_data(products)
)
Facility Location¶
is_open = (
LXVariable[Facility, int]("is_open")
.binary()
.cost(lambda f: f.fixed_cost)
.from_data(facilities)
)
Assignment¶
assign = (
LXVariable[tuple[Worker, Task], int]("assign")
.binary()
.indexed_by_product(
LXIndexDimension(Worker, lambda w: w.id).from_data(workers),
LXIndexDimension(Task, lambda t: t.id).from_data(tasks),
)
.where_multi(lambda w, t: t.skill_level <= w.skill_level)
)
Next Steps¶
Constraints Guide - Learn about constraint families
Expressions Guide - Build mathematical expressions
Models Guide - Tie everything together
Core Module API - Full API reference