Core Module API

The core module provides the fundamental building blocks for creating optimization models in LumiX.

Overview

The core module implements a type-safe, data-driven approach to optimization modeling through five main components:

        graph TD
    A[LXModel] --> B[LXVariable]
    A --> C[LXConstraint]
    A --> D[LXExpression]
    B --> D
    C --> D
    E[LXEnums] --> B
    E --> C
    E --> A

    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#ffe1e1
    style D fill:#e1ffe1
    style E fill:#f0e1ff
    

Components

Model Builder

lumix.core.model.LXModel

Main model builder with full type safety and IDE support.

The LXModel class is the central component for building optimization models. It uses the Builder pattern with a fluent API.

Variables

lumix.core.variables.LXVariable

Variable Family - represents multiple solver variables indexed by data models.

The LXVariable class represents variable families that automatically expand to multiple solver variables based on data.

Constraints

lumix.core.constraints.LXConstraint

Constraint Family - represents multiple constraints indexed by data models.

The LXConstraint class represents constraint families that automatically expand to multiple solver constraints based on data.

Expressions

lumix.core.expressions.LXLinearExpression

Type-safe linear expression builder with multi-model support.

lumix.core.expressions.LXQuadraticExpression

Quadratic expression: linear_terms + quadratic_terms + constant

lumix.core.expressions.LXNonLinearExpression

Non-linear expression containing arbitrary non-linear terms.

lumix.core.expressions.LXQuadraticTerm

Quadratic term: coeff * var1 * var2

Expression classes for building objective functions and constraint left-hand sides.

Enumerations

lumix.core.enums.LXVarType

Variable type enumeration for optimization variables.

lumix.core.enums.LXConstraintSense

Constraint sense enumeration for inequality and equality constraints.

lumix.core.enums.LXObjectiveSense

Objective sense enumeration for optimization direction.

Type-safe enumerations for variable types, constraint senses, and objective directions.

Detailed API Reference

Model

Model builder class for LumiX optimization models.

This module provides the LXModel class, which is the central component for building optimization models in LumiX. It implements the Builder pattern with fluent API for creating type-safe, data-driven optimization models.

The model serves as a container for:
  • Variable families (decision variables indexed by data)

  • Constraint families (constraints indexed by data)

  • Objective function (linear or quadratic expression)

  • Goal programming metadata (for multi-objective optimization)

Key Features:
  • Fluent API: Method chaining for concise model building

  • Type Safety: Generic type parameter for compile-time type checking

  • Goal Programming: Native support for multi-objective optimization

  • Auto-expansion: Variable and constraint families expand automatically

Architecture:

LXModel uses the Builder pattern where each method returns self to enable method chaining. The model doesn’t create solver variables directly - instead, it stores variable and constraint families that are expanded during solving.

Examples

Simple production planning model:

from lumix import LXModel, LXVariable, LXConstraint, LXLinearExpression

# Create model
model = LXModel("production_plan")

# Add variables
production = LXVariable[Product, float]("production")\
    .continuous()\
    .bounds(lower=0)\
    .from_data(products)

model.add_variable(production)

# Add constraints
capacity = LXConstraint("capacity")\
    .expression(
        LXLinearExpression().add_term(production, lambda p: p.usage)
    )\
    .le()\
    .rhs(max_capacity)

model.add_constraint(capacity)

# Set objective
model.maximize(
    LXLinearExpression().add_term(production, lambda p: p.profit)
)

Fluent API with method chaining:

model = (
    LXModel[Product]("production")
    .add_variable(production)
    .add_constraint(capacity)
    .maximize(profit_expr)
)

Goal programming for multi-objective optimization:

model = LXModel("multi_objective")\
    .set_goal_mode("weighted")

# Mark constraints as goals with priorities
model.add_constraint(
    profit_constraint.as_goal(priority=1, weight=1.0)
)
model.add_constraint(
    quality_constraint.as_goal(priority=2, weight=0.8)
)

# Solve with goal programming solver
solution = optimizer.solve(model)

Note

The model is solver-agnostic. The same model can be solved with different solvers (OR-Tools, Gurobi, CPLEX, GLPK) by simply changing the optimizer configuration.

See also

class lumix.core.model.LXModel(name)[source]

Bases: Generic[TModel]

Main model builder with full type safety and IDE support.

Creates and manages optimization models with:

  • Variables (single or multi-indexed)

  • Constraints (linear, indexed, multi-model)

  • Objective function (linear or quadratic)

Examples:

# Simple model
model = LXModel("production_plan")
model.add_variable(production)
model.add_constraint(capacity_constraint)
model.maximize(
    LXLinearExpression().add_term(production, lambda p: p.selling_price)
)

# Type-safe model
model = LXModel[Product]("production_plan")
    .add_variable(production)
    .add_constraint(capacity_constraint)
    .maximize(
        LXLinearExpression()
        .add_term(production, lambda p: p.selling_price - p.cost)
    )
Parameters:

name (str)

__init__(name)[source]

Initialize model.

Parameters:

name (str) – Model name

__deepcopy__(memo)[source]

Custom deepcopy that enables what-if analysis with ORM data sources.

This method orchestrates deep copying of the entire model including: 1. All variable families (with ORM data materialization) 2. All constraint families (with ORM data materialization) 3. Objective expression 4. Goal programming metadata

This is the central method that makes what-if analysis possible by creating independent copies of models that can be modified without affecting the original.

Parameters:

memo – Dictionary for tracking circular references during deepcopy

Returns:

Deep copy of this model with all ORM dependencies resolved

Note

After copying, all variables and constraints will have their ORM sessions detached and data materialized. The copy is completely independent and safe for serialization/pickling.

Example

>>> original_model = build_model_with_orm(session)
>>> modified_model = deepcopy(original_model)
>>> # modified_model can now be changed without affecting original_model
__getstate__()[source]

Support for pickle protocol - detach ORM sessions before pickling.

Returns:

Dictionary of instance state safe for pickling

__setstate__(state)[source]

Support for pickle protocol - restore from pickled state.

Parameters:

state – Dictionary of instance state from pickling

add_variable(var)[source]

Add variable with full type checking.

Parameters:

var (LXVariable) – Variable to add

Return type:

Self

Returns:

Self for chaining

add_variables(*variables)[source]

Add multiple variable families.

Parameters:

*variables (LXVariable) – Variables to add

Return type:

Self

Returns:

Self for chaining

add_constraint(constraint)[source]

Add constraint with full type checking.

Parameters:

constraint (LXConstraint) – Constraint to add

Return type:

Self

Returns:

Self for chaining

add_constraints(*constraints)[source]

Add multiple constraints.

Parameters:

*constraints (LXConstraint) – Constraints to add

Return type:

Self

Returns:

Self for chaining

minimize(expr)[source]

Set objective to minimize.

Parameters:

expr (LXLinearExpression | LXQuadraticExpression) – Objective expression (linear or quadratic)

Return type:

Self

Returns:

Self for chaining

maximize(expr)[source]

Set objective to maximize.

Parameters:

expr (LXLinearExpression | LXQuadraticExpression) – Objective expression (linear or quadratic)

Return type:

Self

Returns:

Self for chaining

get_variable(name)[source]

Get variable family by name.

Parameters:

name (str) – Variable name

Return type:

Optional[LXVariable]

Returns:

LXVariable if found, None otherwise

get_constraint(name)[source]

Get constraint by name.

Parameters:

name (str) – Constraint name

Return type:

Optional[LXConstraint]

Returns:

LXConstraint if found, None otherwise

set_goal_mode(mode)[source]

Set goal programming mode.

Parameters:

mode (str) – Goal programming mode (“weighted” or “sequential”)

Return type:

Self

Returns:

Self for chaining

Raises:

ValueError – If mode is not “weighted” or “sequential”

Example

>>> model.set_goal_mode("weighted")
>>> # Solve with weighted objectives (single solve)
>>> model.set_goal_mode("sequential")
>>> # Solve lexicographically (multiple solves)
prepare_goal_programming()[source]

Prepare model for goal programming by relaxing goal constraints.

This method: 1. Identifies constraints marked as goals (via .as_goal()) 2. Relaxes them by adding deviation variables 3. Builds the appropriate objective function based on mode 4. Adds deviation variables to the model 5. Replaces goal constraints with relaxed versions

This is automatically called by the solver, but can be called manually for inspection or debugging.

Return type:

Self

Returns:

Self for chaining

Example

>>> model.set_goal_mode("weighted")
>>> model.prepare_goal_programming()
>>> # Model now has deviation variables and goal objective
has_goals()[source]

Check if model has any goal constraints.

Return type:

bool

Returns:

True if at least one constraint is marked as a goal

populate_goal_deviations(solution)[source]

Populate goal deviation values in the solution.

Extracts deviation variable values from the solution and organizes them by goal name for easy access via solution.get_goal_deviations().

This method is automatically called after solving if the model has goals.

Parameters:

solution (LXSolution) – Solution object to populate

Return type:

LXSolution

Returns:

The solution object with goal_deviations populated

summary()[source]

Get model summary.

Return type:

str

Returns:

String summary of model

Variables

Variable class with multi-indexing support for LumiX.

class lumix.core.variables.LXVariable(name, var_type=LXVarType.CONTINUOUS, lower_bound=None, upper_bound=None, model_type=None, index_func=None, cost_func=None, _filter=None, _data=None, _session=None, _cartesian=None, _multi_cost_func=None, _join_config=None)[source]

Bases: Generic[TModel, TValue]

Variable Family - represents multiple solver variables indexed by data models.

IMPORTANT: LXVariable is NOT a single variable, but a FAMILY/TEMPLATE that automatically expands to multiple solver variables based on data.

When you write:

production = LXVariable[Product, float]("production").from_data(products)

This creates ONE LXVariable object that represents MANY solver variables:

production[product1], production[product2], production[product3], ...

The expansion happens automatically during model building - you don’t loop manually.

Supports:

  • Single-model indexing: LXVariable[Product, float]

  • Multi-model indexing: LXVariable[Tuple[Driver, Date], int]

  • Join-based sparse indexing

  • Cartesian product indexing

Examples:

# Single model - data-driven
production = (
    LXVariable[Product, float]("production")
    .continuous()
    .indexed_by(lambda p: p.id)
    .bounds(lower=0)
    .cost(lambda p: p.unit_cost)
    .from_data(products)  # Provide the data directly
)

# Or with ORM
production = (
    LXVariable[Product, float]("production")
    .continuous()
    .from_model(Product, session=session)  # Query from ORM
)

# Multi-model (cartesian product)
duty = (
    LXVariable[Tuple[Driver, Date], int]("duty")
    .binary()
    .indexed_by_product(
        LXIndexDimension(Driver, lambda d: d.id).from_data(drivers),
        LXIndexDimension(Date, lambda dt: dt.date).from_data(dates)
    )
    .where_multi(lambda driver, date: driver.is_active)
)
Parameters:
name: str
var_type: LXVarType = 'continuous'
lower_bound: Optional[TypeVar(TValue, int, float)] = None
upper_bound: Optional[TypeVar(TValue, int, float)] = None
model_type: Optional[Type[TypeVar(TModel)]] = None
index_func: Optional[Callable[[TypeVar(TModel)], TypeVar(TIndex)]] = None
cost_func: Optional[Callable[[TypeVar(TModel)], float]] = None
__deepcopy__(memo)[source]

Custom deepcopy that detaches ORM sessions and handles lambda closures.

This method enables what-if analysis on variables using ORM data sources by: 1. Materializing lazy-loaded ORM data before copying 2. Detaching ORM objects from database sessions 3. Safely copying lambda functions (index_func, cost_func, filters, etc.) 4. Deep copying cartesian products and join configurations

Parameters:

memo – Dictionary for tracking circular references during deepcopy

Returns:

Deep copy of this variable with all ORM dependencies resolved

Note

After copying, the new variable will have _session=None and all data stored in _data as detached objects safe for pickling.

__getstate__()[source]

Support for pickle protocol - detach ORM sessions before pickling.

Returns:

Dictionary of instance state safe for pickling

__setstate__(state)[source]

Support for pickle protocol - restore from pickled state.

Parameters:

state – Dictionary of instance state from pickling

continuous()[source]

Set as continuous variable. Returns self for chaining.

Return type:

Self

integer()[source]

Set as integer variable. Returns self for chaining.

Return type:

Self

binary()[source]

Set as binary variable. Returns self for chaining.

Return type:

Self

bounds(lower=None, upper=None)[source]

Set variable bounds with full type checking.

Parameters:
Return type:

Self

Returns:

Self for chaining

from_data(data)[source]

Provide data instances directly (for non-ORM usage).

Parameters:

data (List[TypeVar(TModel)]) – List of model instances

Return type:

Self

Returns:

Self for chaining

Example

production = Variable[Product, float](“production”).from_data(products)

from_model(model, session=None)[source]

Bind to ORM model type for automatic querying.

Parameters:
Return type:

Self

Returns:

Self for chaining

Example

production = Variable[Product, float](“production”).from_model(Product, session)

get_instances()[source]

Get the data instances for this variable family.

Return type:

List[TypeVar(TModel)]

Returns:

List of model instances

Raises:

ValueError – If no data source configured

indexed_by(func)[source]

Define indexing function with full type inference.

Parameters:

func (Callable[[TypeVar(TModel)], TypeVar(TIndex)]) – Function to extract index from model instance

Return type:

Self

Returns:

Self for chaining

Examples

.indexed_by(lambda product: product.id) .indexed_by(lambda route: (route.origin, route.destination))

indexed_by_product(dim1, dim2, *extra_dims)[source]

Index by cartesian product of multiple models. Creates variables for every valid combination.

Parameters:
Return type:

Self

Returns:

Self for chaining

Example:

duty = LXVariable[Tuple[Driver, Date], int]("duty")
    .indexed_by_product(
        LXIndexDimension(Driver, lambda d: d.id)
            .where(lambda d: d.is_active),
        LXIndexDimension(Date, lambda dt: dt.date)
            .where(lambda dt: dt >= today)
    )
indexed_by_join(primary, related, join_func, key_func=None)[source]

Index by a relationship/join between models. Only creates variables where relationship exists (sparse).

Parameters:
Return type:

Self

Returns:

Self for chaining

Example:

# Only create variables for valid driver-route assignments
assignment = LXVariable[Tuple[Driver, Route], int]("assign")
    .indexed_by_join(
        Driver,
        Route,
        join_func=lambda d: d.qualified_routes,  # ORM relationship
        key_func=lambda d, r: (d.id, r.id)
    )
cost(func)[source]

Define objective coefficient from model.

Parameters:

func (Callable[[TypeVar(TModel)], float]) – Function to calculate cost from model

Return type:

Self

Returns:

Self for chaining

Example

.cost(lambda product: product.unit_cost)

cost_multi(func)[source]

Define cost function for multi-indexed variables. Function receives all index models as arguments.

Parameters:

func (Callable[..., float]) – Function receiving all dimension models

Return type:

Self

Returns:

Self for chaining

Example

.cost_multi(lambda driver, date: driver.daily_rate * date.overtime_multiplier)

where(predicate)[source]

Filter which model instances to include.

Parameters:

predicate (Callable[[TypeVar(TModel)], bool]) – Filter function

Return type:

Self

Returns:

Self for chaining

Example

.where(lambda p: p.is_active and p.stock > 0)

where_multi(predicate)[source]

Filter multi-indexed variable combinations.

Parameters:

predicate (Callable[..., bool]) – Filter function receiving all dimension models

Return type:

Self

Returns:

Self for chaining

Example:

.where_multi(lambda driver, date, shift:
    driver.can_work_shift(shift) and
    date.weekday() not in driver.days_off
)
__init__(name, var_type=LXVarType.CONTINUOUS, lower_bound=None, upper_bound=None, model_type=None, index_func=None, cost_func=None, _filter=None, _data=None, _session=None, _cartesian=None, _multi_cost_func=None, _join_config=None)
Parameters:
Return type:

None

Constraints

Constraint class for LumiX optimization models.

class lumix.core.constraints.LXConstraint(name, lhs=None, sense=LXConstraintSense.LE, rhs_value=None, rhs_func=None, model_type=None, index_func=None, _data=None, _session=None, goal_metadata=None)[source]

Bases: Generic[TModel]

Constraint Family - represents multiple constraints indexed by data models.

Like LXVariable, an LXConstraint represents a FAMILY of constraints that automatically expands to multiple solver constraints based on data.

Represents: LHS {<=, >=, ==} RHS

Examples:

# Simple single constraint
LXConstraint("total_capacity")
    .expression(LXLinearExpression().add_term(production, 1.0))
    .le()
    .rhs(100)

# Constraint family - one per resource
# Note: In multi-model constraints, the coefficient lambda receives instances from:
#   - p: Product (from the production variable's indexing)
#   - r: Resource (from this constraint's indexing)
# This allows expressing relationships between different data models.
LXConstraint[Resource]("capacity")
    .expression(LXLinearExpression().add_term(production, lambda p, r: p.usage[r.id]))
    .le()
    .rhs(lambda r: r.capacity)
    .from_data(resources)
    .indexed_by(lambda r: r.id)
Parameters:
name: str
lhs: Optional[LXLinearExpression[TypeVar(TModel)]] = None
sense: LXConstraintSense = '<='
rhs_value: Optional[float] = None
rhs_func: Optional[Callable[[TypeVar(TModel)], float]] = None
model_type: Optional[Type[TypeVar(TModel)]] = None
index_func: Optional[Callable[[TypeVar(TModel)], TypeVar(TIndex)]] = None
goal_metadata: Optional[LXGoalMetadata] = None
__deepcopy__(memo)[source]

Custom deepcopy that detaches ORM sessions and handles lambda closures.

This method enables what-if analysis on constraints using ORM data sources by: 1. Materializing lazy-loaded ORM data before copying 2. Detaching ORM objects from database sessions 3. Safely copying lambda functions (index_func, rhs_func, etc.) 4. Deep copying constraint expressions and goal metadata

Parameters:

memo – Dictionary for tracking circular references during deepcopy

Returns:

Deep copy of this constraint with all ORM dependencies resolved

Note

After copying, the new constraint will have _session=None and all data stored in _data as detached objects safe for pickling.

__getstate__()[source]

Support for pickle protocol - detach ORM sessions before pickling.

Returns:

Dictionary of instance state safe for pickling

__setstate__(state)[source]

Support for pickle protocol - restore from pickled state.

Parameters:

state – Dictionary of instance state from pickling

expression(expr)[source]

Set LHS expression.

Parameters:

expr (LXLinearExpression[TypeVar(TModel)]) – Linear expression for left-hand side

Return type:

Self

Returns:

Self for chaining

le()[source]

Set as <= constraint.

Return type:

Self

Returns:

Self for chaining

ge()[source]

Set as >= constraint.

Return type:

Self

Returns:

Self for chaining

eq()[source]

Set as == constraint.

Return type:

Self

Returns:

Self for chaining

rhs(value)[source]

Set RHS (constant or function).

Parameters:

value (Union[float, Callable[[TypeVar(TModel)], float]]) – Right-hand side value (constant or function)

Return type:

Self

Returns:

Self for chaining

Examples

.rhs(100) # constant .rhs(lambda resource: resource.capacity) # from model

from_data(data)[source]

Provide data instances directly.

Parameters:

data (List[TypeVar(TModel)]) – List of model instances

Return type:

Self

Returns:

Self for chaining

from_model(model, session=None)[source]

Bind to model for indexed constraints.

Parameters:
Return type:

Self

Returns:

Self for chaining

indexed_by(func)[source]

Create constraint for each model instance.

Parameters:

func (Callable[[TypeVar(TModel)], TypeVar(TIndex)]) – Function to extract index from model

Return type:

Self

Returns:

Self for chaining

Example

.indexed_by(lambda r: r.id)

as_goal(priority, weight=1.0)[source]

Mark this constraint as a goal for goal programming.

Automatically relaxes the constraint by adding deviation variables and includes it in the goal programming objective function.

Constraint types are handled as follows:

  • LE (expr <= rhs): expr + neg_dev - pos_dev == rhs

    • Positive deviation (exceeding target) is undesired

  • GE (expr >= rhs): expr + neg_dev - pos_dev == rhs

    • Negative deviation (falling short) is undesired

  • EQ (expr == rhs): expr + neg_dev - pos_dev == rhs

    • Both deviations are undesired

Parameters:
  • priority (int) – Priority level (1=highest, 2=second, etc.) Priority 0 is reserved for custom objective terms

  • weight (float) – Relative weight within the same priority level (default: 1.0)

Return type:

Self

Returns:

Self for chaining

Example:

# High priority production goal
.as_goal(priority=1, weight=1.0)

# Lower priority overtime limit
.as_goal(priority=2, weight=0.5)

# Custom objective term (maximize profit)
.as_goal(priority=0, weight=1.0)
is_goal()[source]

Check if this constraint is marked as a goal.

Return type:

bool

Returns:

True if this is a goal constraint, False otherwise

get_instances()[source]

Get the data instances for this constraint family.

Return type:

List[TypeVar(TModel)]

Returns:

List of model instances, or empty list if single constraint

Raises:

ValueError – If indexed but no data source configured

__init__(name, lhs=None, sense=LXConstraintSense.LE, rhs_value=None, rhs_func=None, model_type=None, index_func=None, _data=None, _session=None, goal_metadata=None)
Parameters:
Return type:

None

Expressions

Expression classes for LumiX optimization models.

class lumix.core.expressions.LXLinearExpression(terms=<factory>, constant=0.0, _multi_terms=<factory>)[source]

Bases: Generic[TModel]

Type-safe linear expression builder with multi-model support.

Represents: sum(coeff[i] * var[i]) + constant

Examples:

expr = LXLinearExpression()
expr.add_term(production, 1.0)
expr.add_term(inventory, -1.0)
expr.constant(100)

# Multi-model
expr = LXLinearExpression()
expr.sum_over(duty, where=lambda driver, date: date.is_weekend)
terms: Dict[str, Tuple[LXVariable, Callable[[TModel], float]], Tuple[Callable[[TModel], bool]]]
constant: float = 0.0
__deepcopy__(memo)[source]

Custom deepcopy that handles variables and lambda functions.

This method enables what-if analysis on expressions by: 1. Deep copying all variables in the expression 2. Safely copying coefficient and filter lambda functions 3. Preserving the expression structure

Parameters:

memo – Dictionary for tracking circular references during deepcopy

Returns:

Deep copy of this expression with all dependencies resolved

add_term(var, coeff=1.0, where=None)[source]

Add term with coefficient (constant or function).

Parameters:
Return type:

Self

Returns:

Self for chaining

add_multi_term(var, coeff=<function LXLinearExpression.<lambda>>, where=None)[source]

Add multi-indexed variable to expression.

Parameters:
Return type:

Self

Returns:

Self for chaining

Example:

expr.add_multi_term(
    duty,
    coeff=lambda driver, date: driver.cost * date.multiplier,
    where=lambda driver, date: date.is_weekend
)
sum_over(var, where=None)[source]

Syntactic sugar for summing over all dimensions of a variable.

Parameters:
Return type:

Self

Returns:

Self for chaining

Example:

# Sum all driver duties (over all drivers and dates)
expr.sum_over(duty)

# Sum duties for all drivers on a specific date
expr.sum_over(duty, where=lambda d, dt: dt == specific_date)

Note

Currently sums over all dimensions. Future enhancement could add selective dimension summing (e.g., sum only over drivers, not dates).

add_constant(value)[source]

Add constant to expression.

Parameters:

value (float) – Constant value

Return type:

Self

Returns:

Self for chaining

__add__(other)[source]

Enable expr1 + expr2 or expr + constant.

Parameters:

other (Union[float, Self]) – Expression or constant to add

Return type:

Self

Returns:

Self for chaining

__mul__(scalar)[source]

Enable scalar * expression.

Parameters:

scalar (float) – Scalar multiplier

Return type:

Self

Returns:

Self for chaining

copy()[source]

Create a deep copy of this expression.

Return type:

Self

Returns:

New expression with same terms and constant

__init__(terms=<factory>, constant=0.0, _multi_terms=<factory>)
class lumix.core.expressions.LXQuadraticTerm(var1, var2, coefficient=1.0)[source]

Bases: object

Quadratic term: coeff * var1 * var2

Used in portfolio optimization, risk modeling, etc.

Parameters:
var1: LXVariable
var2: LXVariable
coefficient: float = 1.0
__deepcopy__(memo)[source]

Custom deepcopy that handles variables.

Parameters:

memo – Dictionary for tracking circular references during deepcopy

Returns:

Deep copy of this quadratic term

is_squared_term()[source]

Check if this is x^2 (same variable twice).

Return type:

bool

__init__(var1, var2, coefficient=1.0)
Parameters:
Return type:

None

class lumix.core.expressions.LXQuadraticExpression(linear_terms=<factory>, quadratic_terms=<factory>, constant=0.0)[source]

Bases: object

Quadratic expression: linear_terms + quadratic_terms + constant

Represents: 0.5 * x^T Q x + c^T x + constant

Example

# Portfolio variance: sum(w[i] * w[j] * cov[i,j]) # Plus linear returns: sum(return[i] * w[i]) quad_expr = LXQuadraticExpression() quad_expr.add_quadratic(w[0], w[1], cov[0,1]) quad_expr.linear_terms.add_term(w[0], returns[0])

Parameters:
linear_terms: LXLinearExpression
quadratic_terms: List[LXQuadraticTerm]
constant: float = 0.0
__deepcopy__(memo)[source]

Custom deepcopy that handles linear and quadratic terms.

This method enables what-if analysis on quadratic expressions by: 1. Deep copying the linear expression component 2. Deep copying all quadratic terms 3. Preserving the expression structure

Parameters:

memo – Dictionary for tracking circular references during deepcopy

Returns:

Deep copy of this quadratic expression with all dependencies resolved

add_quadratic(var1, var2, coeff=1.0)[source]

Add quadratic term.

Parameters:
Return type:

Self

Returns:

Self for chaining

add_squared(var, coeff=1.0)[source]

Add x^2 term.

Parameters:
Return type:

Self

Returns:

Self for chaining

__add__(other)[source]

Enable: quad_expr + linear_expr or quad_expr + constant.

Parameters:

other (LXLinearExpression | float) – Linear expression or constant

Return type:

Self

Returns:

Self for chaining

__init__(linear_terms=<factory>, quadratic_terms=<factory>, constant=0.0)
Parameters:
Return type:

None

class lumix.core.expressions.LXNonLinearExpression(linear_terms=<factory>, nonlinear_terms=<factory>, constant=0.0)[source]

Bases: object

Non-linear expression containing arbitrary non-linear terms.

Supports: - Bilinear terms (x * y) - Absolute value (x) - Min/max functions - Piecewise-linear approximations - Conditional expressions - Custom non-linear functions

These will be automatically linearized by the Linearizer engine.

Example

# Create nonlinear expression expr = LXNonLinearExpression()

# Add bilinear product expr.add_product(length, width)

# Add absolute value expr.add_abs(deviation)

# Add piecewise function expr.add_piecewise(time, lambda t: math.exp(t), num_segments=30)

Parameters:
linear_terms: LXLinearExpression
nonlinear_terms: List[Any]
constant: float = 0.0
add_linear(expr)[source]

Add linear terms.

Parameters:

expr (LXLinearExpression) – Linear expression to add

Return type:

Self

Returns:

Self for chaining

add_abs(var, coeff=1.0)[source]

Add absolute value term: coeff * var

Parameters:
  • var (LXVariable) – Variable to take absolute value of

  • coeff (float) – Coefficient (default: 1.0)

Return type:

Self

Returns:

Self for chaining

Example

# Minimize absolute deviation expr.add_abs(actual - target)

add_min(*vars, coefficients=None)[source]

Add minimum function: min(vars)

Parameters:
Return type:

Self

Returns:

Self for chaining

Example

# Minimum of three costs expr.add_min(cost_a, cost_b, cost_c)

add_max(*vars, coefficients=None)[source]

Add maximum function: max(vars)

Parameters:
Return type:

Self

Returns:

Self for chaining

Example

# Maximum capacity expr.add_max(capacity_1, capacity_2, capacity_3)

add_product(var1, var2, coeff=1.0)[source]

Add bilinear product: coeff * var1 * var2

Automatically linearized based on variable types: - Binary × Binary: AND logic - Binary × Continuous: Big-M method - Continuous × Continuous: McCormick envelopes

Parameters:
Return type:

Self

Returns:

Self for chaining

Example

# Rectangle area expr.add_product(length, width)

# Facility open × flow amount expr.add_product(is_open, flow_amount)

add_indicator(binary_var, condition, linear_expr)[source]

Add conditional constraint: if binary_var == condition then linear_expr

Parameters:
  • binary_var (LXVariable) – Binary variable

  • condition (bool) – Condition value (True or False)

  • linear_expr (LXLinearExpression) – Expression to apply when condition is met

Return type:

Self

Returns:

Self for chaining

Example:

# If warehouse is open, then demand must be met
expr.add_indicator(
    is_open,
    True,
    LXLinearExpression().add_term(supply, 1.0)
)
add_piecewise(var, func, num_segments=20, x_min=None, x_max=None, adaptive=True, method='sos2')[source]

Add piecewise-linear approximation of arbitrary function.

Parameters:
  • var (LXVariable) – Input variable

  • func (Callable[[float], float]) – Function to approximate (e.g., lambda x: math.exp(x))

  • num_segments (int) – Number of linear segments

  • x_min (Optional[float]) – Minimum domain value (default: var.lower_bound)

  • x_max (Optional[float]) – Maximum domain value (default: var.upper_bound)

  • adaptive (bool) – Use adaptive breakpoint generation

  • method (Literal['sos2', 'incremental', 'logarithmic']) – Linearization method (“sos2”, “incremental”, “logarithmic”)

Return type:

Self

Returns:

Self for chaining

Example:

# Exponential growth
expr.add_piecewise(time, lambda t: math.exp(t), num_segments=30)

# Custom discount curve
expr.add_piecewise(
    quantity,
    lambda q: 1.0 if q < 100 else 0.9 if q < 1000 else 0.8,
    num_segments=50
)
add_nonlinear_term(term)[source]

Add pre-constructed non-linear term.

Parameters:

term (Any) – Non-linear term object

Return type:

Self

Returns:

Self for chaining

add_nonlinear_terms(terms)[source]

Add multiple non-linear terms.

Parameters:

terms (List[Any]) – List of non-linear terms

Return type:

Self

Returns:

Self for chaining

__init__(linear_terms=<factory>, nonlinear_terms=<factory>, constant=0.0)
Parameters:
Return type:

None

Enumerations

Core enumerations for LumiX optimization library.

This module defines type-safe enumerations for optimization modeling:

  • Variable Types: Continuous, integer, and binary variables

  • Constraint Senses: Less-than-or-equal, greater-than-or-equal, and equality

  • Objective Directions: Minimize and maximize

These enumerations provide IDE autocomplete and type checking throughout the library.

Examples

Creating variables with different types:

from lumix import LXVariable, LXVarType

# Continuous variable (default)
production = LXVariable[Product, float]("production").continuous()

# Integer variable
trucks = LXVariable[Route, int]("trucks").integer()

# Binary variable (0 or 1)
is_open = LXVariable[Facility, int]("is_open").binary()

Using constraint senses:

from lumix import LXConstraint, LXConstraintSense

# Less-than-or-equal constraint
constraint1 = LXConstraint("capacity").le().rhs(100)

# Greater-than-or-equal constraint
constraint2 = LXConstraint("minimum").ge().rhs(50)

# Equality constraint
constraint3 = LXConstraint("balance").eq().rhs(0)

Setting objective direction:

from lumix import LXModel, LXObjectiveSense

# Maximize profit
model.maximize(profit_expr)

# Minimize cost
model.minimize(cost_expr)
class lumix.core.enums.LXVarType(value)[source]

Bases: Enum

Variable type enumeration for optimization variables.

Defines the domain of decision variables in the optimization model. Each type has different solver representations and computational characteristics.

CONTINUOUS

Real-valued variables (floating-point) Range: [-inf, inf] or bounded Use for: Production quantities, weights, percentages Example: production[product] = 123.45

INTEGER

Integer-valued variables Range: Whole numbers only Use for: Counts, discrete quantities Example: num_trucks[route] = 5

BINARY

Binary variables (0 or 1) Range: {0, 1} Use for: Yes/no decisions, selection, activation Example: is_facility_open[location] = 1

Note

  • CONTINUOUS variables generally solve faster than integer variables

  • BINARY is a special case of INTEGER with automatic bounds [0, 1]

  • Mixed-integer problems combine CONTINUOUS with INTEGER/BINARY variables

Examples

Variable type affects solver choice and performance:

# Fast: Linear program with continuous variables
production = LXVariable[Product, float]("prod").continuous()

# Slower: Mixed-integer program
trucks = LXVariable[Route, int]("trucks").integer()

# Classic: Binary decision variables
select = LXVariable[Item, int]("select").binary()
CONTINUOUS = 'continuous'
INTEGER = 'integer'
BINARY = 'binary'
class lumix.core.enums.LXConstraintSense(value)[source]

Bases: Enum

Constraint sense enumeration for inequality and equality constraints.

Defines the relationship between left-hand side (LHS) and right-hand side (RHS) of constraints in the optimization model.

LE

Less-than-or-equal constraint (<=) Meaning: LHS <= RHS Use for: Capacity limits, maximum bounds, upper limits Example: total_production <= factory_capacity

GE

Greater-than-or-equal constraint (>=) Meaning: LHS >= RHS Use for: Minimum requirements, lower bounds, demand satisfaction Example: production >= minimum_quota

EQ

Equality constraint (==) Meaning: LHS == RHS Use for: Balance equations, exact requirements, flow conservation Example: inflow - outflow == 0

Note

  • LE and GE constraints define feasible regions (inequalities)

  • EQ constraints are more restrictive and may reduce feasibility

  • All constraints must be satisfied for a solution to be feasible

Examples

Different constraint types:

# Capacity constraint (upper limit)
LXConstraint("capacity")
    .expression(resource_usage_expr)
    .le()
    .rhs(max_capacity)

# Demand constraint (lower limit)
LXConstraint("demand")
    .expression(production_expr)
    .ge()
    .rhs(min_demand)

# Balance constraint (exact equality)
LXConstraint("flow_balance")
    .expression(inflow - outflow)
    .eq()
    .rhs(0)
LE = '<='
GE = '>='
EQ = '=='
class lumix.core.enums.LXObjectiveSense(value)[source]

Bases: Enum

Objective sense enumeration for optimization direction.

Defines whether the objective function should be minimized or maximized.

MINIMIZE

Minimize the objective function Use for: Costs, distances, time, waste, deviations, risk Example: Minimize total transportation cost

MAXIMIZE

Maximize the objective function Use for: Profit, revenue, efficiency, throughput, utility Example: Maximize total profit

Note

  • Every optimization model must have exactly one objective

  • Minimizing f(x) is equivalent to maximizing -f(x)

  • For multi-objective problems, use goal programming module

Examples

Setting objective direction:

# Maximize profit
model = LXModel("production_plan")
model.maximize(
    LXLinearExpression()
    .add_term(production, lambda p: p.profit)
)

# Minimize cost
model = LXModel("logistics")
model.minimize(
    LXLinearExpression()
    .add_term(shipment, lambda s: s.cost)
)

# Multi-objective (using goal programming)
model.set_goal_mode("weighted")
# Objectives defined via goal constraints
MINIMIZE = 'min'
MAXIMIZE = 'max'