folf
folf computes Edmundson-Madansky (UB) and Jensen-based (LB) piecewise linear approximations suitable for embedding the first order loss function in mixed-integer linear optimization models.
Features
- Empirical first-order and complementary first-order loss functions for sums of independent SciPy distributions, with series, plotting, and CSV export
- Simple random and Latin hypercube sampling, including empirical loss functions for weighted scalar products of random variables
- Exact closed-form loss functions for scalar projections of multivariate normal demand, using either full covariance or independent-demand variance
- Uniform and minimax Jensen partitions for standard normal demand, including partition probabilities, conditional means, and maximum approximation error
- Numerically stable partitioning and linearisation calculations, with tail-safe normal probabilities, log-space breakpoints, and tolerance-aware mass allocation
- Empirical Jensen piecewise linear lower approximations for both loss-function orientations, with pointwise and maximum error calculations
- Cached standard normal linearization data and automatic selection of segment and variance-partition counts for a requested error tolerance
Who This Is For
This library is useful when you need approximate or empirical first-order loss functions for inventory and stochastic optimization workflows, especially with normal or sampled demand models.
References
R. Rossi, S. A. Tarim, B. Hnich, and S. Prestwich, "Piecewise linear lower and upper bounds for the standard normal first order loss function," Applied Mathematics and Computation, Elsevier, vol. 231, pp. 489-502, 2014.
R. Rossi, E.M.T. Hendrix, "Computing linearisation parameters of arbitrarily distributed first order loss functions," in Proceedings of MAGO'14, XII Global Optimization Workshop (GOW).
R. Rossi, S. Prestwich, and S. A. Tarim, "Mixed-Integer Linear Programming Approximations for the Stochastic Knapsack," Computers & Operations Research, Elsevier, Vol. 194: 107571, 2026.
Installation
pip install -e .
Command-line usage:
folf-cli --help
For development:
pip install -e .[dev]
Quick Start
CLI: Plot a loss function and its piecewise linearisation
folf-cli plot-loss \
--distribution poisson:20 \
--distribution norm:8:2 \
--distribution gamma:4:1.5 \
--sampling LHS \
--samples 5000 \
--x-min 10 \
--x-max 60 \
--precision 0.5 \
--piecewise-masses 0.25,0.25,0.25,0.25 \
--loss-type complementary \
--output artifacts/loss_piecewise.png
Supported distributions in CLI:
poisson:<lambda>norm:<mu>:<sigma>gamma:<shape>:<scale>
The command produces a plot with:
- Empirical loss function curve
- Piecewise linearisation curve
1. Empirical first-order loss from sampled distributions
from scipy.stats import gamma, norm, poisson
from folf import FirstOrderLossFunction
from folf.utilities.probability.sampling import SAMPLING
folf = FirstOrderLossFunction(
distributions=[
poisson(20), # discrete demand component
norm(8, 2), # approximately normal component
gamma(a=4, scale=1.5), # right-skewed positive component
],
sampling_strategy=SAMPLING.SRS,
)
x = 70.0
nb_samples = 5_000
complementary = folf.get_complementary_first_order_loss_function_value(x, nb_samples)
regular = folf.get_first_order_loss_function_value(x, nb_samples)
print("CL(x):", complementary)
print("L(x):", regular)
1b. Compare SRS vs LHS sampling strategies
from scipy.stats import gamma, norm, poisson
from folf import FirstOrderLossFunction
from folf.utilities.probability.sampling import SAMPLING
distributions = [
poisson(20),
norm(8, 2),
gamma(a=4, scale=1.5),
]
srs_model = FirstOrderLossFunction(distributions, sampling_strategy=SAMPLING.SRS)
lhs_model = FirstOrderLossFunction(distributions, sampling_strategy=SAMPLING.LHS)
x = 70.0
nb_samples = 2_000
cl_srs = srs_model.get_complementary_first_order_loss_function_value(x, nb_samples)
cl_lhs = lhs_model.get_complementary_first_order_loss_function_value(x, nb_samples)
print("CL(x) using SRS:", cl_srs)
print("CL(x) using LHS:", cl_lhs)
Use SRS for a straightforward baseline and LHS when you want lower Monte Carlo
variance for the same sample count.
2. Scalar-product first-order loss with multivariate normal demand
import numpy as np
from folf import FirstOrderLossFunctionScalarProductMVN
model = FirstOrderLossFunctionScalarProductMVN(
mean=np.array([10.0, 15.0, 20.0]),
covariance=np.array(
[
[4.0, 1.2, 0.8],
[1.2, 9.0, 2.0],
[0.8, 2.0, 16.0],
]
),
independent_demand=False,
)
weights = np.array([0.5, 0.3, 0.2])
y = 14.0
cl = model.get_complementary_first_order_loss_function_value(y, weights)
l = model.get_first_order_loss_function_value(y, weights)
print("CL(y):", cl)
print("L(y):", l)
3. Choose piecewise linearization parameters
If you are embedding first-order loss terms in an optimization model (for
example MILP or MIP), you typically replace nonlinear loss expressions with a
piecewise linear approximation. LinearisationFactory.choose_linearisation_parameters
helps pick:
w_segments: how many loss-function segments to useq: how many variance/sqrt partitions to use
for a requested approximation tolerance epsilon, variance bound vmax, and
cost coefficient c.
from folf import LinearisationFactory
epsilon = 0.5
vmax = 4.0
c = 10.0
w_segments, q = LinearisationFactory.choose_linearisation_parameters(epsilon, vmax, c)
print("segments:", w_segments)
print("q:", q)
Typical Workflow
- Model your demand distribution(s) with SciPy distributions or a normal mean/covariance pair.
- Compute CL(x) or L(x) either empirically (sampling) or from the MVN closed-form helper.
- If building optimization models, use the Jensen partitioners or linearization factory to derive approximation parameters.
Public API At A Glance
- FirstOrderLossFunction
- FirstOrderLossFunctionScalarProduct
- FirstOrderLossFunctionScalarProductMVN
- JensenUniformPartitioner
- JensenMinimaxPartitioner
- PiecewiseStandardNormalFirstOrderLossFunction
- LinearisationFactory
Quality Checks
./venv/bin/python -m pytest -q
./venv/bin/python -m ruff check .
./venv/bin/python -m mypy src/folf
Release Metadata
- Homepage: https://github.com/gwren/folf
- Repository: https://github.com/gwren/folf
- Issue Tracker: https://github.com/gwren/folf/issues
- Changelog: CHANGELOG.md
Package Layout
src/folf: Main library packagesrc/folf/utilities: Utility modulestests: Basic smoke and behavior tests
Release files for folf 1.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| folf-1.0.3.tar.gz | 60.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| folf-1.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.3 kB
Release files / folf-1.0.3.tar.gz
| Download URL | folf-1.0.3.tar.gz |
|---|---|
| Size | 60.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a1c61daa757e70581159af01c361e3e602ae9938caf0f94351e693fe8e7bdab8
|
|
BLAKE2b-256 checksum How to use checksums |
5d86d96d4bd7a948ec6238863739ec315bcebdb1cab7202bfda69ddf7611c20a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.12
|
Release files / folf-1.0.3-py3-none-any.whl
| Download URL | folf-1.0.3-py3-none-any.whl |
|---|---|
| Size | 22.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
83b380cbe9345b9af1e9af87d5db4ed42434cd283d82b29468e6eeecde4d6b82
|
|
BLAKE2b-256 checksum How to use checksums |
15a85ce538f13ee89743c1a9dad09d2c5ad883ab0e579fa1faf8456c9f1c89f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.12
|