lumix.nonlinear.terms.LXPiecewiseLinearTerm¶
- class lumix.nonlinear.terms.LXPiecewiseLinearTerm(var, func, num_segments=20, x_min=None, x_max=None, adaptive=True, method='sos2')[source]¶
Piecewise-linear approximation for arbitrary nonlinear functions.
Approximates any univariate nonlinear function using a piecewise-linear function. The domain is divided into segments, and the function is approximated by linear interpolation between breakpoints.
- Three formulation methods are supported:
SOS2: Special Ordered Set type 2 (best when solver supports SOS2)
Incremental: Binary selection variables for each segment
Logarithmic: Gray code encoding (best for many segments, uses fewer binaries)
- Parameters:
- var¶
The input variable to the nonlinear function.
- func¶
The nonlinear function to approximate, taking a float and returning a float.
- num_segments¶
Number of linear segments to use (default: 20).
- x_min¶
Minimum value of the input domain (default: use variable lower bound).
- x_max¶
Maximum value of the input domain (default: use variable upper bound).
- adaptive¶
If True, use adaptive segmentation based on function curvature (default: True).
- method¶
Formulation method - “sos2”, “incremental”, or “logarithmic” (default: “sos2”).
Example
Exponential growth function:
import math from lumix.nonlinear import LXPiecewiseLinearTerm from lumix.core import LXVariable # Approximate exp(t) for t in [0, 5] time = LXVariable[Task, float]("time").continuous().bounds(0, 5) exp_term = LXPiecewiseLinearTerm( var=time, func=lambda t: math.exp(t), num_segments=30, x_min=0.0, x_max=5.0, adaptive=True, method="sos2" )
Custom discount curve:
# Tiered discount: 100% up to 100 units, 90% up to 1000, then 80% quantity = LXVariable[Order, float]("qty").continuous().bounds(0, 2000) def discount_func(q): if q < 100: return 1.0 elif q < 1000: return 0.9 else: return 0.8 discount = LXPiecewiseLinearTerm( var=quantity, func=discount_func, num_segments=50, adaptive=False, # Uniform segments for step function method="incremental" )
Logarithmic cost function:
# Cost grows logarithmically with size size = LXVariable[Component, float]("size").continuous().bounds(1, 1000) log_cost = LXPiecewiseLinearTerm( var=size, func=lambda s: 10 * math.log(s), num_segments=25, method="logarithmic" # Efficient for many segments )
Sigmoid activation:
# Approximate sigmoid function def sigmoid(x): return 1.0 / (1.0 + math.exp(-x)) activation = LXVariable[Node, float]("activation").continuous() sigmoid_approx = LXPiecewiseLinearTerm( var=activation, func=sigmoid, num_segments=40, x_min=-6.0, x_max=6.0, adaptive=True )
Note
Adaptive Segmentation: When adaptive=True, the algorithm places more breakpoints in regions where the function has higher curvature, improving approximation accuracy with fewer segments.
- Method Selection:
Use “sos2” if your solver has native SOS2 support (most efficient)
Use “incremental” for better solver performance with few segments
Use “logarithmic” when you need many segments (uses O(log n) binaries)
Domain Bounds: If x_min and x_max are not specified, they default to the variable’s lower and upper bounds. The variable MUST have finite bounds for piecewise-linear approximation.
Approximation Error: More segments generally provide better approximation but increase model complexity. The num_segments parameter allows you to trade off accuracy for solving speed.
- __init__(var, func, num_segments=20, x_min=None, x_max=None, adaptive=True, method='sos2')¶
Methods
__init__(var, func[, num_segments, x_min, ...])Attributes
-
var:
LXVariable¶
- __init__(var, func, num_segments=20, x_min=None, x_max=None, adaptive=True, method='sos2')¶