Linearization Concepts¶
This guide covers automatic linearization of nonlinear optimization terms in LumiX.
Introduction¶
The linearization module transforms nonlinear expressions into linear or mixed-integer linear equivalents, enabling you to:
Solve nonlinear models with linear programming (LP) or mixed-integer programming (MIP) solvers
Handle solver limitations by converting unsupported nonlinear terms
Improve solve times by using specialized linear formulations
Maintain model accuracy through configurable approximation methods
Philosophy¶
Traditional Approach (Manual Linearization)¶
Manual linearization is error-prone and requires deep mathematical knowledge:
# Manual McCormick envelope for x * y
# Requires: x ∈ [xL, xU], y ∈ [yL, yU]
z = model.addVar(name="z") # Auxiliary variable for x * y
# Four McCormick constraints
model.addConstr(z >= xL*y + yL*x - xL*yL)
model.addConstr(z >= xU*y + yU*x - xU*yU)
model.addConstr(z <= xL*y + yU*x - xL*yU)
model.addConstr(z <= xU*y + yL*x - xU*yL)
# Use z in objective instead of x * y
model.setObjective(profit * z, GRB.MAXIMIZE)
Problems:
✗ Manual constraint creation
✗ Easy to make mistakes in formulation
✗ Hard to maintain and modify
✗ Different formulations for different variable types
LumiX Approach (Automatic)¶
LumiX handles linearization automatically:
from lumix.linearization import LXLinearizer, LXLinearizerConfig
from lumix.solvers.capabilities import LXSolverCapability
# Build model with nonlinear terms (naturally)
model = LXModel("production")
profit_expr = production * price # Bilinear term
model.maximize(profit_expr)
# Configure linearization
config = LXLinearizerConfig(
default_method=LXLinearizationMethod.MCCORMICK,
adaptive_breakpoints=True
)
# Automatic linearization
linearizer = LXLinearizer(model, solver_capability, config)
if linearizer.needs_linearization():
linearized_model = linearizer.linearize_model()
Benefits:
✓ Automatic detection of nonlinear terms
✓ Optimal technique selection based on variable types
✓ No manual constraint creation
✓ Configurable accuracy vs. complexity trade-offs
✓ Solver-aware (uses native features when available)
Supported Nonlinear Terms¶
Bilinear Products (x × y)¶
Linearization depends on variable types:
Variable Types |
Technique |
Complexity |
|---|---|---|
Binary × Binary |
AND logic |
3 constraints |
Binary × Continuous |
Big-M method |
4 constraints |
Continuous × Continuous |
McCormick envelopes |
4 constraints |
See Bilinear Product Linearization for details.
Absolute Values (x)¶
Linearized using two constraints:
z ≥ x
z ≥ -x
z minimized (if in objective) or bounded appropriately
Piecewise-Linear Functions¶
Arbitrary nonlinear functions approximated using:
SOS2 formulation: Most efficient when solver supports SOS2
Incremental formulation: Binary selection variables
Logarithmic formulation: Gray code encoding for many segments
See Piecewise-Linear Approximation and Pre-built Nonlinear Functions for details.
Min/Max Functions¶
Linearized using auxiliary variables and constraints:
min(x₁, x₂, …, xₙ): z ≤ xᵢ for all i (z minimized)
max(x₁, x₂, …, xₙ): z ≥ xᵢ for all i (z maximized)
Indicator Constraints¶
Conditional constraints linearized using Big-M method:
if b = 1 then expr ≤ rhs → expr ≤ rhs + M(1-b)
if b = 0 then expr ≤ rhs → expr ≤ rhs + M·b
See Linearization Engine for details.
How Linearization Works¶
sequenceDiagram
participant User
participant Linearizer
participant Scanner
participant Techniques
participant Model
User->>Linearizer: linearize_model()
Linearizer->>Scanner: Scan for nonlinear terms
Scanner-->>Linearizer: [bilinear, abs, pwl, ...]
loop For each term
Linearizer->>Techniques: Apply appropriate technique
Techniques-->>Linearizer: Auxiliary vars & constraints
end
Linearizer->>Model: Build linearized model
Model-->>User: Linearized model ready to solve
Steps:
Detection: Scan objective and constraints for nonlinear terms
Analysis: Check variable types and solver capabilities
Selection: Choose appropriate linearization technique
Application: Create auxiliary variables and constraints
Assembly: Build new linearized model
Configuration¶
Fine-tune linearization behavior:
config = LXLinearizerConfig(
# General settings
default_method=LXLinearizationMethod.MCCORMICK,
tolerance=1e-6,
verbose_logging=True,
# Big-M settings
big_m_value=1e5, # Adjust based on problem scale
# Piecewise-linear settings
pwl_num_segments=30, # More segments = better accuracy
pwl_method="sos2", # or "incremental", "logarithmic"
adaptive_breakpoints=True, # Concentrate points where function curves
# McCormick settings
auto_detect_bounds=True,
mccormick_tighten_bounds=True,
# Binary expansion settings
binary_expansion_bits=10 # For integers up to 2^10 = 1024
)
See Configuration for all options.
Quick Examples¶
Basic Usage¶
from lumix import LXModel, LXVariable
from lumix.linearization import LXLinearizer, LXLinearizerConfig
from lumix.solvers import LXOptimizer
# Build model with nonlinear terms
model = LXModel("portfolio")
# Bilinear term: return * allocation
profit = return_var * allocation_var
model.maximize(profit)
# Linearize
config = LXLinearizerConfig()
optimizer = LXOptimizer().use_solver("glpk")
solver_capability = optimizer.get_capability()
linearizer = LXLinearizer(model, solver_capability, config)
linearized_model = linearizer.linearize_model()
# Solve
solution = optimizer.solve(linearized_model)
Using Pre-built Functions¶
from lumix.linearization import LXNonLinearFunctions
from lumix.linearization.techniques import LXPiecewiseLinearizer
config = LXLinearizerConfig(pwl_num_segments=40)
linearizer = LXPiecewiseLinearizer(config)
# Exponential growth: e^(growth_rate * time)
growth_output = LXNonLinearFunctions.exp(
growth_rate_var,
linearizer,
segments=50
)
# Logarithmic utility: log(consumption)
utility = LXNonLinearFunctions.log(
consumption_var,
linearizer,
base=math.e
)
# Sigmoid probability: 1 / (1 + e^(-score))
probability = LXNonLinearFunctions.sigmoid(
score_var,
linearizer,
segments=40
)
See Pre-built Nonlinear Functions for all available functions.
Performance Considerations¶
Accuracy vs. Complexity Trade-offs¶
Piecewise-Linear Approximation:
More segments → better accuracy but more variables/constraints
Adaptive breakpoints → better accuracy with same number of segments
Recommended: 20-50 segments for most functions
Big-M Method:
Smaller M → tighter relaxation → faster solving
M too small → incorrect solutions
Recommended: Use tight bounds when possible
McCormick Envelopes:
Tighter bounds → better relaxation → faster solving
Enable bound tightening for best performance
Memory and Variables¶
Each linearized term adds auxiliary variables and constraints:
Term Type |
Aux. Variables |
Aux. Constraints |
Complexity |
|---|---|---|---|
Bilinear (Binary × Binary) |
1 |
3 |
O(1) |
Bilinear (Binary × Cont.) |
1 |
4 |
O(1) |
Bilinear (Cont. × Cont.) |
1 |
4 |
O(1) |
Absolute value |
1 |
2 |
O(1) |
Min/Max (n terms) |
1 |
n |
O(n) |
PWL (SOS2, k segments) |
k+1 λ + 1 output |
3 |
O(k) |
PWL (Incremental, k seg.) |
k binary + k delta + 1 out |
3k + 1 |
O(k) |
Optimization Tips:
Use filters to reduce variable expansion before linearization
Choose appropriate pwl_num_segments based on required accuracy
Prefer SOS2 formulation when solver supports it
Use adaptive breakpoints to reduce segments needed
Component Details¶
Dive deeper into each component:
Next Steps¶
Continue to:
Configuration - Configuration options
Linearization Engine - Using the linearization engine
Bilinear Product Linearization - Bilinear product linearization
Piecewise-Linear Approximation - Piecewise-linear approximation
Pre-built Nonlinear Functions - Pre-built function approximations
Linearization Module API - Complete API reference
Linearization Architecture - Architecture details