Linearization Module API¶
The linearization module provides automatic conversion of nonlinear optimization terms into linear or mixed-integer linear programming (MILP) formulations.
Overview¶
The linearization module implements a comprehensive framework for handling nonlinear terms in optimization models:
graph TD
A[LXLinearizer] --> B[LXBilinearLinearizer]
A --> C[LXPiecewiseLinearizer]
D[LXLinearizerConfig] --> A
E[LXLinearizationMethod] --> D
F[LXNonLinearFunctions] --> C
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#ffe1e1
style D fill:#e1ffe1
style E fill:#f0e1ff
style F fill:#f0e1ff
Components¶
Main Engine¶
Automatic linearization engine. |
The LXLinearizer class is the main engine that
orchestrates the linearization process by detecting nonlinear terms, checking solver
capabilities, and applying appropriate techniques.
Configuration¶
Configuration for automatic linearization engine. |
|
Available linearization techniques. |
Configuration classes for controlling linearization behavior, method selection, and accuracy requirements.
Pre-built Functions¶
Pre-built approximations for common nonlinear functions. |
Pre-built piecewise-linear approximations for common nonlinear functions (exp, log, sqrt, power, sigmoid, trigonometric functions).
Linearization Techniques¶
Linearization techniques for bilinear products (x * y). |
|
Piecewise-linear approximation for arbitrary nonlinear functions. |
Specialized linearization techniques for bilinear products and piecewise-linear function approximations.
Detailed API Reference¶
Main Engine¶
Core linearization engine for automatic nonlinear term conversion.
This module orchestrates the entire linearization process: 1. Scan model for nonlinear terms 2. Check solver capabilities 3. Apply appropriate linearization techniques 4. Add auxiliary variables and constraints 5. Return linearized model
- class lumix.linearization.engine.LXLinearizer(model, solver_capability, config=None)[source]
Bases:
objectAutomatic linearization engine.
Transforms nonlinear expressions into linear equivalents by: - Detecting nonlinear terms in model - Checking solver capabilities - Applying appropriate linearization techniques - Adding auxiliary variables and constraints
Example
linearizer = LXLinearizer(model, solver_capability, config)
- if linearizer.needs_linearization():
linearized_model = linearizer.linearize_model()
- Parameters:
model (LXModel)
solver_capability (LXSolverCapability)
config (LXLinearizerConfig | None)
- __init__(model, solver_capability, config=None)[source]
Initialize linearization engine.
- Parameters:
model (
LXModel) – Model to linearizesolver_capability (
LXSolverCapability) – Solver capability informationconfig (
Optional[LXLinearizerConfig]) – Linearization configuration (default: LXLinearizerConfig())
- needs_linearization()[source]
Check if model contains nonlinear terms requiring linearization.
- Return type:
- Returns:
True if linearization is needed
- linearize_model()[source]
Linearize the entire model.
Creates a new model with: - All original variables and constraints - Auxiliary variables for linearized terms - Auxiliary constraints for linearization
- Return type:
- Returns:
Linearized model
Example
linearized = linearizer.linearize_model() solution = solver.solve(linearized)
Configuration¶
Configuration for linearization engine.
- class lumix.linearization.config.LXLinearizationMethod(value)[source]
Bases:
EnumAvailable linearization techniques.
- MCCORMICK()
McCormick envelopes for continuous × continuous products
- BIG_M()
Big-M method for conditional constraints
- BINARY_EXPANSION()
Binary expansion for integer × integer products
- SOS2()
Special Ordered Set type 2 for piecewise-linear
- LOGARITHMIC()
Logarithmic (Gray code) encoding for large integer products
- MCCORMICK = 'mccormick'
- BIG_M = 'big_m'
- BINARY_EXPANSION = 'binary_expansion'
- SOS2 = 'sos2'
- LOGARITHMIC = 'logarithmic'
- class lumix.linearization.config.LXLinearizerConfig(default_method=LXLinearizationMethod.MCCORMICK, tolerance=1e-06, verbose_logging=True, big_m_value=1000000.0, pwl_num_segments=20, pwl_method='sos2', prefer_sos2=True, adaptive_breakpoints=True, auto_detect_bounds=True, mccormick_tighten_bounds=True, binary_expansion_bits=10)[source]
Bases:
objectConfiguration for automatic linearization engine.
- Parameters:
default_method (LXLinearizationMethod)
tolerance (float)
verbose_logging (bool)
big_m_value (float)
pwl_num_segments (int)
pwl_method (Literal['sos2', 'incremental', 'logarithmic'])
prefer_sos2 (bool)
adaptive_breakpoints (bool)
auto_detect_bounds (bool)
mccormick_tighten_bounds (bool)
binary_expansion_bits (int)
- default_method
Default linearization technique
- big_m_value
Big-M constant for conditional constraints (default: 1e6)
- pwl_num_segments
Number of segments for piecewise-linear approximations
- pwl_method
Method for piecewise-linear (“sos2”, “incremental”, “logarithmic”)
- prefer_sos2
Use SOS2 formulation when solver supports it
- adaptive_breakpoints
Use adaptive breakpoint generation for PWL
- auto_detect_bounds
Automatically detect variable bounds for McCormick
- mccormick_tighten_bounds
Apply bound tightening for McCormick envelopes
- binary_expansion_bits
Number of bits for binary expansion method
- tolerance
Numerical tolerance for comparisons
- verbose_logging
Enable detailed linearization logging
Example:
config = LXLinearizerConfig( big_m_value=1e5, pwl_num_segments=30, adaptive_breakpoints=True, mccormick_tighten_bounds=True )
-
default_method:
LXLinearizationMethod= 'mccormick'
-
tolerance:
float= 1e-06
-
verbose_logging:
bool= True
-
big_m_value:
float= 1000000.0
-
pwl_num_segments:
int= 20
-
pwl_method:
Literal['sos2','incremental','logarithmic'] = 'sos2'
-
prefer_sos2:
bool= True
-
adaptive_breakpoints:
bool= True
-
auto_detect_bounds:
bool= True
-
mccormick_tighten_bounds:
bool= True
-
binary_expansion_bits:
int= 10
- __init__(default_method=LXLinearizationMethod.MCCORMICK, tolerance=1e-06, verbose_logging=True, big_m_value=1000000.0, pwl_num_segments=20, pwl_method='sos2', prefer_sos2=True, adaptive_breakpoints=True, auto_detect_bounds=True, mccormick_tighten_bounds=True, binary_expansion_bits=10)
- Parameters:
default_method (LXLinearizationMethod)
tolerance (float)
verbose_logging (bool)
big_m_value (float)
pwl_num_segments (int)
pwl_method (Literal['sos2', 'incremental', 'logarithmic'])
prefer_sos2 (bool)
adaptive_breakpoints (bool)
auto_detect_bounds (bool)
mccormick_tighten_bounds (bool)
binary_expansion_bits (int)
- Return type:
None
Pre-built Functions¶
Pre-built piecewise-linear approximations for common nonlinear functions.
- class lumix.linearization.functions.LXNonLinearFunctions[source]
Bases:
objectPre-built approximations for common nonlinear functions.
All functions use piecewise-linear approximation with adaptive breakpoint generation for improved accuracy.
Example
# Create linearizer linearizer = LXPiecewiseLinearizer(config)
# Approximate exponential growth growth = LXNonLinearFunctions.exp(time, linearizer)
# Logarithmic decay decay = LXNonLinearFunctions.log(age, linearizer, base=10)
- static exp(var, linearizer, segments=30)[source]
Exponential function: e^x
- Parameters:
var (
LXVariable) – Input variablelinearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments (default: 30 for good accuracy)
- Return type:
- Returns:
Output variable representing exp(var)
Example
# Population growth model population = LXNonLinearFunctions.exp(time, linearizer)
# Compound interest future_value = principal * LXNonLinearFunctions.exp(rate * time, linearizer)
- static log(var, linearizer, base=2.718281828459045, segments=30)[source]
Logarithm: log_base(x)
- Parameters:
var (
LXVariable) – Input variable (must be positive)linearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancebase (
float) – Logarithm base (default: e for natural log)segments (
int) – Number of segments
- Return type:
- Returns:
Output variable representing log(var)
Example
# Natural logarithm ln_demand = LXNonLinearFunctions.log(demand, linearizer)
# Base-10 logarithm log10_quantity = LXNonLinearFunctions.log(quantity, linearizer, base=10)
# Information entropy entropy = -probability * LXNonLinearFunctions.log(probability, linearizer, base=2)
- static sqrt(var, linearizer, segments=20)[source]
Square root: √x
- Parameters:
var (
LXVariable) – Input variable (must be non-negative)linearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments
- Return type:
- Returns:
Output variable representing sqrt(var)
Example
# Standard deviation std_dev = LXNonLinearFunctions.sqrt(variance, linearizer)
# Euclidean distance (after squaring) distance = LXNonLinearFunctions.sqrt(x_squared + y_squared, linearizer)
# Flow velocity in pipes velocity = LXNonLinearFunctions.sqrt(pressure_drop, linearizer)
- static power(var, exponent, linearizer, segments=25)[source]
Power function: x^n
- Parameters:
var (
LXVariable) – Input variableexponent (
float) – Power to raise variable tolinearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments
- Return type:
- Returns:
Output variable representing var^exponent
Example
# Cubic cost function cubic_cost = LXNonLinearFunctions.power(production, 3, linearizer)
# Quadratic relationship area = LXNonLinearFunctions.power(radius, 2, linearizer)
# Fractional power (e.g., Cobb-Douglas) output = LXNonLinearFunctions.power(capital, 0.3, linearizer)
- static sigmoid(var, linearizer, segments=40)[source]
Sigmoid function: 1 / (1 + e^(-x))
Useful for modeling probabilities, S-curves, saturation effects.
- Parameters:
var (
LXVariable) – Input variablelinearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments (higher for sharper transition)
- Return type:
- Returns:
Output variable representing sigmoid(var) ∈ [0, 1]
Example
# Probability of success probability = LXNonLinearFunctions.sigmoid(score, linearizer)
# Market saturation market_share = capacity * LXNonLinearFunctions.sigmoid(time, linearizer)
# Learning curve efficiency = LXNonLinearFunctions.sigmoid(experience, linearizer)
- static sin(var, linearizer, segments=50)[source]
Sine function: sin(x)
Useful for seasonal patterns, cyclical behavior.
- Parameters:
var (
LXVariable) – Input variable (typically in radians)linearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments
- Return type:
- Returns:
Output variable representing sin(var) ∈ [-1, 1]
Example
# Seasonal demand pattern (annual cycle) import math day_angle = day_of_year * 2 * math.pi / 365 seasonal_factor = LXNonLinearFunctions.sin(day_angle, linearizer)
# Daily temperature variation hour_angle = hour * 2 * math.pi / 24 temp_variation = LXNonLinearFunctions.sin(hour_angle, linearizer)
- static cos(var, linearizer, segments=50)[source]
Cosine function: cos(x)
Similar to sine but phase-shifted by π/2.
- Parameters:
var (
LXVariable) – Input variable (typically in radians)linearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments
- Return type:
- Returns:
Output variable representing cos(var) ∈ [-1, 1]
Example
# Phase-shifted seasonal pattern import math day_angle = day_of_year * 2 * math.pi / 365 seasonal_factor = LXNonLinearFunctions.cos(day_angle, linearizer)
# Circular motion x-coordinate x_pos = radius * LXNonLinearFunctions.cos(angle, linearizer)
- static tan(var, linearizer, segments=40)[source]
Tangent function: tan(x)
Warning: Has discontinuities at x = π/2 + nπ. Ensure domain avoids these.
- Parameters:
var (
LXVariable) – Input variable (in radians, avoid discontinuities)linearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segments
- Return type:
- Returns:
Output variable representing tan(var)
Example
# Slope calculation slope = LXNonLinearFunctions.tan(angle, linearizer)
- static custom(var, func, linearizer, segments=30, adaptive=True)[source]
Custom user-defined function.
Allows approximation of any arbitrary function.
- Parameters:
var (
LXVariable) – Input variablefunc (
Callable[[float],float]) – Custom function to approximatelinearizer (
LXPiecewiseLinearizer) – Piecewise linearizer instancesegments (
int) – Number of segmentsadaptive (
bool) – Use adaptive breakpoints
- Return type:
- Returns:
Output variable representing func(var)
Example:
# Custom discount curve def discount_curve(q): if q < 100: return 1.0 # No discount elif q < 1000: return 0.9 # 10% discount else: return 0.8 # 20% discount discount = LXNonLinearFunctions.custom( quantity, discount_curve, linearizer, segments=50 ) # Piecewise quadratic def piecewise_quad(x): return x**2 if x < 10 else 100 + 20*(x-10) cost = LXNonLinearFunctions.custom( production, piecewise_quad, linearizer )
Bilinear Linearization¶
Bilinear product linearization techniques.
Implements three main methods: 1. Binary × Binary: AND logic 2. Binary × Continuous: Big-M method 3. Continuous × Continuous: McCormick envelopes
- class lumix.linearization.techniques.bilinear.LXBilinearLinearizer(config)[source]
Bases:
objectLinearization techniques for bilinear products (x * y).
Automatically selects the appropriate technique based on variable types.
- Parameters:
config (LXLinearizerConfig)
- __init__(config)[source]
Initialize bilinear linearizer.
- Parameters:
config (
LXLinearizerConfig) – Linearization configuration
- linearize_bilinear(term)[source]
Linearize bilinear product based on variable types.
- Parameters:
term (
LXBilinearTerm) – Bilinear term to linearize- Return type:
- Returns:
Auxiliary variable representing the product
- Raises:
ValueError – If variable types are not supported
Piecewise-Linear Approximation¶
Piecewise-linear approximation for arbitrary nonlinear functions.
Implements three formulation methods: 1. SOS2: Special Ordered Set type 2 (most efficient when supported) 2. Incremental: Binary selection variables 3. Logarithmic: Gray code encoding (best for many segments)
- class lumix.linearization.techniques.piecewise.LXPiecewiseLinearizer(config)[source]
Bases:
objectPiecewise-linear approximation for arbitrary nonlinear functions.
Supports multiple formulation methods and adaptive breakpoint generation for improved approximation accuracy.
- Parameters:
config (LXLinearizerConfig)
- __init__(config)[source]
Initialize piecewise linearizer.
- Parameters:
config (
LXLinearizerConfig) – Linearization configuration
- approximate_function(func, var, num_segments=None, x_min=None, x_max=None, method=None, adaptive=None)[source]
Create piecewise-linear approximation of arbitrary function.
- Parameters:
func (
Callable[[float],float]) – Function to approximate (e.g., lambda x: math.exp(x))var (
LXVariable) – Input variablenum_segments (
Optional[int]) – Number of linear segments (default: from config)x_min (
Optional[float]) – Minimum domain value (default: var.lower_bound)x_max (
Optional[float]) – Maximum domain value (default: var.upper_bound)method (
Optional[Literal['sos2','incremental','logarithmic']]) – Linearization method (default: from config)adaptive (
Optional[bool]) – Use adaptive breakpoints (default: from config)
- Return type:
- Returns:
Output variable representing f(var)
- Raises:
ValueError – If domain bounds cannot be determined
Example:
# Exponential growth exp_y = linearizer.approximate_function( lambda x: math.exp(x), input_var, num_segments=50 )
Usage Examples¶
Basic Linearization¶
from lumix.linearization import LXLinearizer, LXLinearizerConfig
from lumix.solvers.capabilities import LXSolverCapability
# Configure linearization
config = LXLinearizerConfig(
default_method=LXLinearizationMethod.MCCORMICK,
big_m_value=1e5,
pwl_num_segments=30,
adaptive_breakpoints=True
)
# Create linearizer
solver_capability = LXSolverCapability.for_solver("glpk")
linearizer = LXLinearizer(model, solver_capability, config)
# Linearize if needed
if linearizer.needs_linearization():
linearized_model = linearizer.linearize_model()
# Get statistics
stats = linearizer.get_statistics()
print(f"Added {stats['auxiliary_variables']} auxiliary variables")
print(f"Added {stats['auxiliary_constraints']} auxiliary constraints")
Using Pre-built Functions¶
from lumix.linearization import LXNonLinearFunctions
from lumix.linearization.techniques import LXPiecewiseLinearizer
# Create linearizer
config = LXLinearizerConfig(pwl_num_segments=40)
linearizer = LXPiecewiseLinearizer(config)
# Exponential growth
exp_output = LXNonLinearFunctions.exp(time_var, linearizer, segments=50)
# Logarithmic decay
log_output = LXNonLinearFunctions.log(quantity_var, linearizer, base=10)
# Sigmoid function for probabilities
probability = LXNonLinearFunctions.sigmoid(score_var, linearizer)
# Custom function
def custom_cost(x):
return x**3 - 2*x**2 + x + 5
cost_output = LXNonLinearFunctions.custom(
production_var,
custom_cost,
linearizer,
segments=30,
adaptive=True
)
Direct Technique Usage¶
from lumix.linearization.techniques import LXBilinearLinearizer
from lumix.nonlinear.terms import LXBilinearTerm
# Create bilinear linearizer
config = LXLinearizerConfig(big_m_value=1e6)
linearizer = LXBilinearLinearizer(config)
# Linearize bilinear term
bilinear_term = LXBilinearTerm(
var1=binary_var,
var2=continuous_var,
coefficient=1.0
)
aux_var = linearizer.linearize_bilinear(bilinear_term)
# Add auxiliary elements to model
for var in linearizer.auxiliary_vars:
model.add_variable(var)
for constraint in linearizer.auxiliary_constraints:
model.add_constraint(constraint)
See Also¶
Linearization Concepts - Complete user guide
Linearization Architecture - Architecture details
Extending Linearization - Extending linearization