LumiX
A modern, type-safe wrapper for optimization solvers with automatic data-driven modeling
LumiX makes mathematical programming accessible, maintainable, and enjoyable by providing a unified, type-safe interface to multiple optimization solvers.
✨ Key Features
- 🎯 Type-Safe & IDE-Friendly — Full type hints and autocomplete support for a superior development experience
- 🔌 Multi-Solver Support — Seamlessly switch between OR-Tools, Gurobi, CPLEX, GLPK, and CP-SAT
- 📊 Data-Driven Modeling — Build models directly from your data with automatic indexing and mapping
- 🔄 Automatic Linearization — Automatically linearize non-linear constraints (bilinear, absolute value, piecewise)
- 📈 Advanced Analysis — Built-in sensitivity analysis, scenario analysis, and what-if analysis tools
- 📊 Interactive Visualization — Plotly-based charts for solutions, sensitivity, scenarios, and Gantt schedules
- 🎯 Goal Programming — Native support for multi-objective optimization with priorities and weights
- ⚡ ORM Integration — Map solutions directly to your ORM models for seamless data flow
- 🚀 Fast Model Building —
keys=looks terms up by index key instead of scanning whole variable families (Performance Considerations)
🚀 Quick Start
Installation
# Install core library
pip install lumix-opt
# Install with a solver (e.g., OR-Tools - free and open-source)
pip install "lumix-opt[ortools]"
# Or install with multiple solvers
pip install "lumix-opt[ortools,gurobi,cplex]"
Simple Example
from dataclasses import dataclass
from lumix import (
LXModel,
LXVariable,
LXConstraint,
LXLinearExpression,
LXOptimizer,
)
# Define your data
@dataclass
class Product:
id: str
name: str
profit: float
resource_usage: float
products = [
Product("A", "Product A", profit=30, resource_usage=2),
Product("B", "Product B", profit=40, resource_usage=3),
]
# Define decision variables
production = (
LXVariable[Product, float]("production")
.continuous()
.bounds(lower=0)
.indexed_by(lambda p: p.id)
.from_data(products)
)
# Build the model
model = (
LXModel("production_plan")
.add_variable(production)
.maximize(
LXLinearExpression()
.add_term(production, lambda p: p.profit)
)
)
# Add constraints
model.add_constraint(
LXConstraint("resource_limit")
.expression(
LXLinearExpression()
.add_term(production, lambda p: p.resource_usage)
)
.le()
.rhs(100) # Resource capacity
)
# Solve
optimizer = LXOptimizer().use_solver("ortools")
solution = optimizer.solve(model)
# Access results
if solution.is_optimal():
print(f"Optimal profit: ${solution.objective_value:,.2f}")
for product in products:
qty = solution.variables["production"][product.id]
print(f"Produce {qty:.2f} units of {product.name}")
🔧 Supported Solvers
LumiX provides a unified interface to multiple solvers:
| Solver | Linear | Integer | Quadratic | Advanced Features | License | Best For |
|---|---|---|---|---|---|---|
| OR-Tools | ✓ | ✓ | ✗ | SOS, Indicator | Apache 2.0 (Free) | General LP/MIP, Learning |
| Gurobi | ✓ | ✓ | ✓ | SOCP, PWL, Callbacks | Commercial/Academic | Large-scale, Production |
| CPLEX | ✓ | ✓ | ✓ | SOCP, PWL, Callbacks | Commercial/Academic | Large-scale, Production |
| GLPK | ✓ | ✓ | ✗ | Basic | GPL (Free) | Small problems, Teaching |
| CP-SAT | ✗ | ✓ | ✗ | Constraint Programming | Apache 2.0 (Free) | Scheduling, Assignment |
Switching Solvers
# Just change one line to switch solvers
optimizer = LXOptimizer().use_solver("ortools") # Free
optimizer = LXOptimizer().use_solver("gurobi") # Requires license
optimizer = LXOptimizer().use_solver("cplex") # Requires license
optimizer = LXOptimizer().use_solver("glpk") # Free
optimizer = LXOptimizer().use_solver("cpsat") # Free
📚 Core Capabilities
Variables with Automatic Indexing
# Single-dimension indexing
production = (
LXVariable[Product, float]("production")
.continuous()
.indexed_by(lambda p: p.id)
.from_data(products)
)
# Multi-dimension indexing
from lumix import LXIndexDimension
assignment = (
LXVariable[Tuple[Driver, Date, Shift], int]("assignment")
.binary()
.indexed_by_product(
LXIndexDimension(Driver, lambda d: d.id).from_data(drivers),
LXIndexDimension(Date, lambda dt: dt.id).from_data(dates),
LXIndexDimension(Shift, lambda s: s.id).from_data(shifts),
)
)
Type-Safe Expressions
# Linear expressions with automatic coefficient extraction
profit_expr = (
LXLinearExpression()
.add_term(production, lambda p: p.profit)
)
# Quadratic expressions
quadratic_expr = (
LXQuadraticExpression()
.add_quadratic_term(x, y, coefficient=0.5)
)
Automatic Linearization
LumiX can automatically linearize non-linear terms:
from lumix import LXNonLinearExpression, LXOptimizer
# Products, absolute values and piecewise-linear functions are linearized at solve time
expr = (
LXNonLinearExpression()
.add_product(x, y) # x * y (bounds come from x and y)
.add_abs(x) # |x|
.add_piecewise(x, lambda v: abs(v) ** 0.5,
breakpoints=[-5, 0, 5, 10]) # sqrt(|x|), exact at the breakpoints
)
model.maximize(expr)
solution = LXOptimizer().use_solver("ortools").enable_linearization().solve(model)
Absolute values are exact, and a piecewise term is the linear interpolation between its
breakpoints. A product of two continuous variables is a relaxation (McCormick envelopes):
LumiX warns and sets solution.relaxation. See the
Correctness Notes.
Advanced Analysis
from lumix import (
LXScenario,
LXSensitivityAnalyzer,
LXScenarioAnalyzer,
LXWhatIfAnalyzer,
)
# Sensitivity analysis
sens = LXSensitivityAnalyzer(model, solution)
report = sens.generate_report()
print(report)
# Scenario analysis (the baseline is included automatically)
scenario_analyzer = LXScenarioAnalyzer(model, optimizer)
scenario_analyzer.add_scenario(
LXScenario("more_resources").modify_constraint_rhs("resource_limit", set_value=150)
)
results = scenario_analyzer.run_all_scenarios()
# What-if analysis
whatif = LXWhatIfAnalyzer(model, optimizer)
result = whatif.increase_constraint_rhs("resource_limit", by=10)
print(f"Impact: ${result.delta_objective:,.2f}")
Goal Programming
from lumix import LXConstraint, LXLinearExpression
# Mark constraints as goals with priorities
model.add_constraint(
LXConstraint("profit_goal")
.expression(profit_expr)
.ge()
.rhs(1000)
.as_goal(priority=1, weight=1.0) # Highest priority
)
model.add_constraint(
LXConstraint("quality_goal")
.expression(quality_expr)
.ge()
.rhs(95)
.as_goal(priority=2, weight=0.8) # Lower priority
)
# Set goal programming mode and prepare
model.set_goal_mode("weighted")
model.prepare_goal_programming()
# Solve with standard optimizer
optimizer = LXOptimizer().use_solver("gurobi")
solution = optimizer.solve(model)
Interactive Visualization
LumiX includes Plotly-based visualization tools for analyzing optimization results:
# Install visualization dependencies
pip install "lumix-opt[viz]"
from lumix.visualization import (
LXSolutionVisualizer, # Variable values, constraint utilization
LXSensitivityPlot, # Tornado charts, shadow prices
LXScenarioCompare, # Multi-scenario comparison
LXGoalProgressChart, # Goal achievement gauges
LXScheduleGantt, # Gantt charts for scheduling
LXAssignmentMatrix, # Assignment problem heatmaps
LXSpatialMap, # Facility location maps
LXModelGraph, # Variable-constraint network
LXDashboard, # Combined overview dashboards
)
# Visualize solution
viz = LXSolutionVisualizer(solution, model)
viz.show() # Opens in browser/Jupyter
# Sensitivity tornado chart
sens_plot = LXSensitivityPlot(sensitivity_analyzer)
sens_plot.plot_tornado(top_n=10).show()
# Export to HTML
viz.to_html("solution.html")
📖 Documentation
- Installation Guide — Install LumiX and solvers
- Quick Start — Build your first model
- Solver Guide — Choose the right solver
- Performance Considerations — Keep large models fast
- Examples — 11 comprehensive examples
- Notebooks — 15 interactive Jupyter tutorials
Examples
The repository includes 11 examples demonstrating various features:
- Production Planning — Single-model indexing, data-driven modeling
- Driver Scheduling — Multi-dimensional indexing, scheduling
- Facility Location — Binary variables, fixed costs
- Basic LP — Simple linear programming
- CP-SAT Assignment — Constraint programming solver
- McCormick Bilinear — Bilinear term linearization
- Piecewise Functions — Piecewise-linear approximations
- Scenario Analysis — Multiple scenario comparison
- Sensitivity Analysis — Parameter sensitivity
- What-If Analysis — Decision support
- Goal Programming — Multi-objective optimization
Jupyter Notebooks
Interactive tutorials are available in the notebooks/ directory:
| Notebook | Topic |
|---|---|
| 01-11 | Tutorial notebooks covering all example topics above |
| 12-15 | High School Timetabling — Comprehensive case study |
The High School Timetabling series (notebooks 12-15) demonstrates:
- 3D multi-model indexing (Lecture × TimeSlot × Classroom)
- SQLAlchemy ORM integration with
from_model(session) - Goal programming for teacher preferences
- Production-ready configuration patterns
🎯 Why LumiX?
Before (Traditional Approach)
# Manual indexing, no type safety
x = {}
for i in range(len(products)):
x[i] = model.addVar(name=f"x_{i}")
# String-based error-prone expressions
model.addConstr(
sum(x[i] * data[i] for i in range(len(products))) <= capacity
)
After (LumiX)
# Type-safe, data-driven, IDE-friendly
production = (
LXVariable[Product, float]("production")
.continuous()
.indexed_by(lambda p: p.id)
.from_data(products)
)
model.add_constraint(
LXConstraint("capacity")
.expression(
LXLinearExpression()
.add_term(production, lambda p: p.usage)
)
.le()
.rhs(capacity)
)
Benefits:
- ✓ Full IDE autocomplete
- ✓ Type checking catches errors early
- ✓ No manual indexing
- ✓ Data-driven coefficients
- ✓ Readable, maintainable code
- ✓ Easy to refactor
🛠️ Development
Setup Development Environment
git clone https://github.com/tdelphi1981/LumiX.git
cd LumiX
pip install -e .[dev]
Run Tests
pytest
Type Checking
mypy src/lumix
Code Formatting
black src/lumix
ruff check src/lumix
📦 Project Structure
lumix/
├── src/lumix/
│ ├── core/ # Core model building (variables, constraints, expressions)
│ ├── solvers/ # Solver interfaces (OR-Tools, Gurobi, CPLEX, GLPK, CP-SAT)
│ ├── analysis/ # Analysis tools (sensitivity, scenario, what-if)
│ ├── linearization/ # Automatic linearization engine
│ ├── goal_programming/ # Goal programming support
│ ├── indexing/ # Multi-dimensional indexing
│ ├── nonlinear/ # Non-linear terms
│ ├── solution/ # Solution handling and mapping
│ ├── visualization/ # Plotly-based interactive charts
│ └── utils/ # Utilities (logger, ORM, rational converter)
├── examples/ # 11 comprehensive examples
├── notebooks/ # 15 Jupyter tutorial notebooks
├── tests/ # Test suite
└── docs/ # Sphinx documentation
🤝 Contributing
Contributions are welcome! Please:
- Open an issue to discuss your idea
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
📄 License
LumiX is licensed under the Academic Free License v3.0.
This is a permissive open-source license that:
- ✓ Allows commercial use
- ✓ Allows modification and distribution
- ✓ Provides patent protection
- ✓ Is OSI-approved
🙏 Acknowledgments
LumiX builds upon the excellent work of:
📞 Support
- Documentation: https://lumix.readthedocs.io
- Issues: https://github.com/tdelphi1981/LumiX/issues
- Discussions: https://github.com/tdelphi1981/LumiX/discussions
🗺️ Roadmap
- Additional solver support (HiGHS, SCIP)
- Jupyter notebook integration
- Interactive visualization tools
- Cloud solver integration
- SQLAlchemy ORM support
- Django ORM support
- Advanced constraint programming features
- Parallel scenario evaluation
- Model versioning and serialization
Authors
This project is maintained by:
- Tolga BERBER - tolga.berber@fen.ktu.edu.tr
- Beyzanur SİYAH - beyzanursiyah@ktu.edu.tr
For a complete list of contributors, see AUTHORS.
Made with ❤️ by the LumiX Contributors
Metadata
Release files for lumix-opt 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| lumix_opt-0.3.2.tar.gz | 248.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lumix_opt-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 471.4 kB
Release files / lumix_opt-0.3.2.tar.gz
| Download URL | lumix_opt-0.3.2.tar.gz |
|---|---|
| Size | 248.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d8b5595f9a510d66e466f43e69e148863a306a5f21b59e2667c530fa6c7577b2
|
|
BLAKE2b-256 checksum How to use checksums |
0c3b0e27463f3eb062462ab06b69d7a08eae4cf9b90b35a5a7faa003cccf2209
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / lumix_opt-0.3.2-py3-none-any.whl
| Download URL | lumix_opt-0.3.2-py3-none-any.whl |
|---|---|
| Size | 223.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e2fdd9de9358921c29baae9e6dc983b8e0064379e3239bc5defa78a84721a382
|
|
BLAKE2b-256 checksum How to use checksums |
b571ea109291974188803d29aca5300871a16381cc4b86147e8aa06775addc23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for lumix-opt 0.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.