Skip to main content

A unified framework for Operation Research modeling with polars

Project description

xplor: A Modern DataFrame-Centric Optimization Framework

PyPI version License

xplor provides a unified framework for building Operation Research models using polars DataFrames. By leveraging polars' performance and ergonomic API, xplor makes mathematical optimization more intuitive and maintainable.

Features

  • ๐Ÿš€ Polars Integration: Built on top of polars for high-performance data operations
  • ๐Ÿงฉ Solver Agnostic: Designed to support multiple solvers (currently Gurobi, more coming soon)
  • ๐Ÿ“ Intuitive API: Natural expression syntax for constraints and objectives
  • โšก Vectorized Operations: Efficient model building with DataFrame operations
  • ๐Ÿ” Type Hints: Full typing support for better IDE integration

Installation

pip install xplor

For Gurobi support, make sure you have Gurobi installed and licensed:

pip install gurobipy

Quick Start

Here's a simple example showing how to build and solve an optimization model using xplor:

>>> import xplor.gurobi as xpg
>>> import polars as pl
>>> import gurobipy as gp

# Wrap your model with XplorGurobi
>>> model = gp.Model()
>>> xmodel = xpg.XplorGurobi(model, deterministic=True, auto_update=True)

# Create sample data
>>> df = pl.DataFrame({
...     "i": [0, 0, 1, 2, 2],
...     "j": [1, 2, 0, 0, 1],
...     "u": [0.3, 1.2, 0.7, 0.9, 1.2],
...     "c": [1.3, 1.7, 1.4, 1.1, 0.9],
...     "obj": [2.5, 2.7, 1.2, 1.7, 3.9],
... })

# Add variables
>>> df = (
...     df
...     .pipe(xmodel.add_vars, name="x", ub="u", obj="obj", indices=["i", "j"])
...     .pipe(xpg.apply_eval, "y = 2 * x - c")
... )
>>> df
shape: (5, 7)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ i   โ”† j   โ”† u   โ”† c   โ”† obj โ”† x                   โ”† y                 โ”‚
โ”‚ --- โ”† --- โ”† --- โ”† --- โ”† --- โ”† ---                 โ”† ---               โ”‚
โ”‚ i64 โ”† i64 โ”† f64 โ”† f64 โ”† f64 โ”† object              โ”† object            โ”‚
โ•žโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 0   โ”† 1   โ”† 0.3 โ”† 1.3 โ”† 2.5 โ”† <gurobi.Var x[0,1]> โ”† -1.3 + 2.0 x[0,1] โ”‚
โ”‚ 0   โ”† 2   โ”† 1.2 โ”† 1.7 โ”† 2.7 โ”† <gurobi.Var x[0,2]> โ”† -1.7 + 2.0 x[0,2] โ”‚
โ”‚ 1   โ”† 0   โ”† 0.7 โ”† 1.4 โ”† 1.2 โ”† <gurobi.Var x[1,0]> โ”† -1.4 + 2.0 x[1,0] โ”‚
โ”‚ 2   โ”† 0   โ”† 0.9 โ”† 1.1 โ”† 1.7 โ”† <gurobi.Var x[2,0]> โ”† -1.1 + 2.0 x[2,0] โ”‚
โ”‚ 2   โ”† 1   โ”† 1.2 โ”† 0.9 โ”† 3.9 โ”† <gurobi.Var x[2,1]> โ”† -0.9 + 2.0 x[2,1] โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

# Add constraints using grouped operations
>>> (
...     df
...     .group_by("i")
...     .agg(xpg.quicksum("y"), pl.col("c").min())
...     .pipe(xmodel.add_constrs, "y <= c", name="sum_on_j", indices=["i"])
... )
shape: (3, 4)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ i   โ”† y                              โ”† c   โ”† sum_on_j                    โ”‚
โ”‚ --- โ”† ---                            โ”† --- โ”† ---                         โ”‚
โ”‚ i64 โ”† object                         โ”† f64 โ”† object                      โ”‚
โ•žโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 1   โ”† -1.4 + 2.0 x[1,0]              โ”† 1.4 โ”† <gurobi.Constr sum_on_j[1]> โ”‚
โ”‚ 0   โ”† -3.0 + 2.0 x[0,1] + 2.0 x[0,2] โ”† 1.3 โ”† <gurobi.Constr sum_on_j[0]> โ”‚
โ”‚ 2   โ”† -2.0 + 2.0 x[2,0] + 2.0 x[2,1] โ”† 0.9 โ”† <gurobi.Constr sum_on_j[2]> โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

# Solve the model
>>> model.optimize()

# Extract solution
>>> df.with_columns(xpg.read_value("x"))
shape: (5, 7)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ i   โ”† j   โ”† u   โ”† c   โ”† obj โ”† x   โ”† y                 โ”‚
โ”‚ --- โ”† --- โ”† --- โ”† --- โ”† --- โ”† --- โ”† ---               โ”‚
โ”‚ i64 โ”† i64 โ”† f64 โ”† f64 โ”† f64 โ”† f64 โ”† object            โ”‚
โ•žโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 0   โ”† 1   โ”† 0.3 โ”† 1.3 โ”† 2.5 โ”† 0.0 โ”† -1.3 + 2.0 x[0,1] โ”‚
โ”‚ 0   โ”† 2   โ”† 1.2 โ”† 1.7 โ”† 2.7 โ”† 0.0 โ”† -1.7 + 2.0 x[0,2] โ”‚
โ”‚ 1   โ”† 0   โ”† 0.7 โ”† 1.4 โ”† 1.2 โ”† 0.0 โ”† -1.4 + 2.0 x[1,0] โ”‚
โ”‚ 2   โ”† 0   โ”† 0.9 โ”† 1.1 โ”† 1.7 โ”† 0.0 โ”† -1.1 + 2.0 x[2,0] โ”‚
โ”‚ 2   โ”† 1   โ”† 1.2 โ”† 0.9 โ”† 3.9 โ”† 0.0 โ”† -0.9 + 2.0 x[2,1] โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Current Status

xplor is in active development. Currently supported:

  • โœ… Gurobi backend
  • โœ… Basic model building operations
  • โœ… Variable and constraint creation
  • โœ… Expression evaluation
  • โœ… Solution reading

Planned features:

  • ๐Ÿšง Support for additional solvers (CPLEX, CBC, SCIP)
  • ๐Ÿšง Extended modeling capabilities
  • ๐Ÿšง Performance optimizations
  • ๐Ÿšง More utility functions

Why xplor?

xplor aims to modernize the Operation Research workflow by:

  1. Using polars instead of pandas for better performance and memory usage
  2. Providing a consistent API across different solvers
  3. Making model building more intuitive with DataFrame operations
  4. Enabling better code organization and maintenance

Comparison with Other Tools

xplor is heavily inspired by gurobipy-pandas but differs in these key aspects:

  • Uses polars instead of pandas for better performance
  • Designed to be solver-agnostic from the ground up

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

# Clone the repository
git clone https://github.com/gab23r/xplor.git
cd xplor

# Install development dependencies
uv sync --all-extras

# Run tests
pytest

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xplor-0.2.3.tar.gz (23.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xplor-0.2.3-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file xplor-0.2.3.tar.gz.

File metadata

  • Download URL: xplor-0.2.3.tar.gz
  • Upload date:
  • Size: 23.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.8

File hashes

Hashes for xplor-0.2.3.tar.gz
Algorithm Hash digest
SHA256 b2a1157345e65c31ec0cb217256484a3276213a055ee2c3220ed16ebcccb0d46
MD5 dd3ea3e41ed7219c5b3e48cd61148d9a
BLAKE2b-256 66b50e9f5bff36ca401e1e0aff5430d738c554f9a782b2cd248452d06a543b6d

See more details on using hashes here.

File details

Details for the file xplor-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: xplor-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.8

File hashes

Hashes for xplor-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 1773d60469acbd16afd5b4cb42f7a3cc6bf2bc53d7a0e31d1990c81d1b8957c2
MD5 a5fb2cda75689cf4229b414c0f8a8794
BLAKE2b-256 42d7b7da6594750b8fc453a224c79e9de72a6d02f98ee36bc98229e4516ca872

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page