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

lumix.linearization.engine.LXLinearizer

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

lumix.linearization.config.LXLinearizerConfig

Configuration for automatic linearization engine.

lumix.linearization.config.LXLinearizationMethod

Available linearization techniques.

Configuration classes for controlling linearization behavior, method selection, and accuracy requirements.

Pre-built Functions

lumix.linearization.functions.LXNonLinearFunctions

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

lumix.linearization.techniques.LXBilinearLinearizer

Linearization techniques for bilinear products (x * y).

lumix.linearization.techniques.LXPiecewiseLinearizer

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: object

Automatic 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:
__init__(model, solver_capability, config=None)[source]

Initialize linearization engine.

Parameters:
needs_linearization()[source]

Check if model contains nonlinear terms requiring linearization.

Return type:

bool

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:

LXModel

Returns:

Linearized model

Example

linearized = linearizer.linearize_model() solution = solver.solve(linearized)

get_statistics()[source]

Get linearization statistics.

Return type:

dict

Returns:

Dictionary with counts of linearized terms

Configuration

Configuration for linearization engine.

class lumix.linearization.config.LXLinearizationMethod(value)[source]

Bases: Enum

Available 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: object

Configuration for automatic linearization engine.

Parameters:
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:
Return type:

None

Pre-built Functions

Pre-built piecewise-linear approximations for common nonlinear functions.

class lumix.linearization.functions.LXNonLinearFunctions[source]

Bases: object

Pre-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 variable

  • linearizer (LXPiecewiseLinearizer) – Piecewise linearizer instance

  • segments (int) – Number of segments (default: 30 for good accuracy)

Return type:

LXVariable

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 instance

  • base (float) – Logarithm base (default: e for natural log)

  • segments (int) – Number of segments

Return type:

LXVariable

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:
Return type:

LXVariable

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:
Return type:

LXVariable

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 variable

  • linearizer (LXPiecewiseLinearizer) – Piecewise linearizer instance

  • segments (int) – Number of segments (higher for sharper transition)

Return type:

LXVariable

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:
Return type:

LXVariable

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:
Return type:

LXVariable

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 instance

  • segments (int) – Number of segments

Return type:

LXVariable

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:
Return type:

LXVariable

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: object

Linearization 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:

LXVariable

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: object

Piecewise-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 variable

  • num_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:

LXVariable

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