Skip to main content

Universal MIP

umip is both:

  • a MIP modeling package with a unified solver API
  • a framework for structuring optimization systems into explicit, reusable building blocks

The framework side is the core value: it enforces clean boundaries between data prep, variable creation, constraints, objectives, and model assembly. That structure makes large MIP codebases easier to grow, test, reason about, and explain.

Purpose

The aim of this repository is to:

  • Build complex MIP models through explicit, reusable building blocks.
  • Make models modular, highly customizable, and easier to extend over time.
  • Enforce separation of responsibilities by structuring implementations into dedicated builders for variables, constraints, and objective functions.
  • Keep settings and composition logic in model factories, so model classes focus on optimization behavior.
  • Use an object-oriented framework and factory interface to keep model assembly explicit and consistent.
  • Treat objective analytics as a native framework capability, where each objective function builder can expose named analytics and/or granularity analytics.
  • Abstract the solver layer to enable fast switching between solver engines such as OR-Tools, Gurobi, and SCIP.

Installation

This project uses uv for Python version and dependency management.

Prepare environment with default version (always minimum, currently 3.11):

uv sync

For a different Python version, use uv sync --python {version}.

Install optional solver extras as needed:

uv sync --extra ortools
uv sync --extra highs
uv sync --extra gurobi
uv sync --extra docplex
uv sync --extra pyscipopt
uv sync --extra localsolver

or /yolo:

uv sync --all-extras

Solver support

The framework supports multiple backends behind one API.

  • OR-Tools engines (SCIP, CBC, CPLEX, XPRESS, GLPK, Gurobi)
  • Native Gurobi
  • Native CPLEX
  • Native SCIP
  • Native HiGHS

Select a backend via SolverType and create it through SolverFactory.

Minimal usage flow

At a high level, production usage should look like this:

model = MyModelFactory(logger=logger, solver_type=SolverType.ORTOOLS_SCIP).construct(settings=settings)

model.build(input_data=input_data)
model.solve(time_limit=60.0)

output_data = model.get_output_data()
objective_value = model.get_objective_value()
product_analytics = model.get_analytics(granularity="product")

Notes:

  • build(...) expects an AbstractInputData implementation.
  • solve(...) runs the optimization and unpacks variable values through variable builders.
  • get_output_data() delegates conversion via your model's _convert_internal_to_output_data(...).

Modelling variables and constraints in practice

The recommended approach for production models is to back decision variables with DataFrames (pandas or polars), where each row represents one or more decision variable and associated parameters. This keeps variables, their parameters, and their solved values co-located and makes vectorised operations natural.

Creating variables

Use build_column_variables on AbstractDecisionVariableBuilder to add a column of solver variables to a DataFrame in one call. Bounds can be passed as a scalar or as a column name, in which case per-row values are read directly from the DataFrame:

data.items = self.build_column_variables(
    solver=solver,
    data=data.items,
    destination_column=VAR,
    variable_domain=VariableDomain.INTEGER,
    index_name_columns=[ITEM_NAME],
    lower_bound=0.0,
    upper_bound=UPPER_BOUND,  # reads per-row values from the UPPER_BOUND column
)

Unpacking solved values

Use unpack_column_variables after solving to replace the solver variable objects with their solved values in a new column:

data.items = self.unpack_column_variables(
    data=data.items,
    decision_variable_column=VAR,
    decision_variable_value_column=VALUE,
    solver=solver,
    variable_domain=VariableDomain.INTEGER,
)

Building constraints

Pass variable and coefficient columns directly as numpy arrays for vectorised constraint construction — one solver call per constraint regardless of the number of variables involved:

solver.add_constraint(
    coefficients=np.ones(len(data.items)),
    variables=data.items[VAR].to_numpy(),
    upper_bound=100,
    name="flow",
)

See examples/settings_factory_example.py for a complete working example of this pattern.

Multi-granularity analytics and white-boxing

One of the most useful framework features is native support for analytics at different granularities.

Typical pattern:

  • implement objective analytics on highest granularity (for example, product_store)
  • add a higher level (store)
  • add an aggregate level (total)
  • and optionally make these calculations nested/reused across levels with native caching at each level to avoid recalculation

This gives you a practical path to white-boxing complex models, as you can solve an optimization problem, while automatically get individual objective contributions on different granularities. This makes the model traceable and explainable at multiple business levels directly.

Relevant model APIs:

  • model.get_objective_analytics_granularities()
  • model.get_analytics(granularity=...)
  • model.get_named_objectives()

Framework-first project structure

When implementing a model package on top of umip, a common structure is:

  • data_prep/: input normalization and derivations.
  • variables/: one builder per set of variables.
  • constraints/: one builder per set of constraints.
  • objectives/: one builder per objective function + analytics definitions.
  • factories/: factories that build the model based on the input settings enabled for the run.
  • model.py: concrete AbstractMipModel implementation.

This style keeps optimization systems modular and easier to evolve as requirements change.

Suggested implementation path

For a new project (ground up)

  1. Start with a minimal base model made of the set of variables, set of constraints, and set of objective functions that are always present.
  2. Make sure this base model solves the minimum problem you care about. The base can be feasible and useful even without optional objective functions.
  3. Test the base model thoroughly.
  4. Define a settings object early, but leave it intentionally empty to make it explicit that no settings are implemented yet.
  5. Keep the factory settings-aware from day one, even if the first version only builds the base model.

For an existing project (incremental extension)

  1. Add or extend a settings object and wire it in the model factory so composition is decided from input settings.
from dataclasses import dataclass


@dataclass
class FlowProblemMip:
    edge_cost: bool = False
  1. Map each setting to the collection of constraint builders, variable builders, and objective function builders associated with that setting.
  2. Implement only the collection of constraint builders, variable builders, and objective function builders needed to implement the new setting / feature.
  3. Test each new setting / feature in isolation.

Examples

You can inspect the repository examples in examples/, but treat them primarily as development references.

If you specifically want a minimal settings-driven factory composition example, see examples/settings_factory_example.py

Class overview

The UMIP framework is a collection of classes that can be used to construct a MIP model. The classes are:

Abstract data classes:

Enums:

Above classes is visualised ín the below UML class diagram. For an explanation of UML diagrams, please go to https://www.uml-diagrams.org.

UML diagram

Metadata

Release files for umip 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for umip 1.0.2
File Size Uploaded
umip-1.0.2.tar.gz 37.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for umip 1.0.2
File Interpreter ABI Platform
umip-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 112.9 kB

Release files / umip-1.0.2.tar.gz

Download URL umip-1.0.2.tar.gz
Size 37.6 kB
Tags Source
SHA-256 checksum
How to use checksums
717bb6f8ea68de3e120168bc2881b25149ebea50a34cd1d997209f483a9152b9
BLAKE2b-256 checksum
How to use checksums
a1925ee859c0c3acb5fc7425c7c9ad769c9b0f59a36a0f832d3db4eb586d004b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / umip-1.0.2-py3-none-any.whl

Download URL umip-1.0.2-py3-none-any.whl
Size 75.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c299c3bcca350be9b95cc1ab50cde0a8a9c04bb89d0f29a181bc22cbe3128a7e
BLAKE2b-256 checksum
How to use checksums
3527a809e986ec14826287d3778754c69c5c70a7b1e1f474ea0ac23a83b17381
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page