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.1.tar.gz (22.5 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.1-py3-none-any.whl (11.3 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for xplor-0.2.1.tar.gz
Algorithm Hash digest
SHA256 6a7e98391e5d1b941df6bd080ab4906b6244dd22e712d548ab3afb73a47d150e
MD5 e17b07f21e0d68cdcb42068476577df1
BLAKE2b-256 cddffdca913572daad7dddfb01de8cff896fed4dc85c957f28b4f85bc692338a

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for xplor-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 60ff705f779c3290c8e42d6811dc1ef47ce3bac14b1ffe89cc673f19a0188549
MD5 3a6c6e1a9deb3478a3d0d68cb22b3257
BLAKE2b-256 7e02b0eb00d04fb56da6da6dd7fab843afa5cea89c39fcdf003f496be63da4b2

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