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 anAbstractInputDataimplementation.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: concreteAbstractMipModelimplementation.
This style keeps optimization systems modular and easier to evolve as requirements change.
Suggested implementation path
For a new project (ground up)
- Start with a minimal base model made of the set of variables, set of constraints, and set of objective functions that are always present.
- Make sure this base model solves the minimum problem you care about. The base can be feasible and useful even without optional objective functions.
- Test the base model thoroughly.
- Define a settings object early, but leave it intentionally empty to make it explicit that no settings are implemented yet.
- Keep the factory settings-aware from day one, even if the first version only builds the base model.
For an existing project (incremental extension)
- 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
- Map each setting to the collection of constraint builders, variable builders, and objective function builders associated with that setting.
- Implement only the collection of constraint builders, variable builders, and objective function builders needed to implement the new setting / feature.
- 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:
AbstractMipModel- Represents a MIP model.AbstractOptimizationSolver- Represents a MIP solver regardless of implementation.OrToolsSolver- Represents a MIP solver implemented with Google OR Tools.GurobiSolver- Represents a MIP solver implemented with Gurobi.CplexSolver- Represents a MIP solver implemented with CPLEX.ScipSolver- Represents a MIP solver implemented with SCIP.HighsSolver- Represents a MIP solver implemented with HiGHS.LocalSolver- Represents a MIP solver implemented with LocalSolver.
AbstractDataPreparator- A class used to prepare data for the model - this is used by theAbstractMipModel.AbstractDecisionVariableBuilder- A class used to construct decision variables - this is used by theAbstractMipModel.AbstractConstraintBuilder- A class used to construct constraints - this is used by theAbstractMipModel.AbstractObjectiveBuilder- A class used to construct objective function terms - this is used by theAbstractMipModel.AbstractMipModelFactory- A class used to construct a model and injects the necessary builders based on given context or settings.SolverFactory- A class used to construct a solver based on given context or settings.VariableWithObjectiveCoefficient- A class containing a decision variable and its objective coefficient.
Abstract data classes:
AbstractInputData- A class containing input data.AbstractInternalData- A class containing internal data.AbstractOutputData- A class containing output data.
Enums:
VariableDataType- An enum used to represent the data type of a decision variable.SolverType- An enum of solver types.BoundType- An enum of bound types (lower/upper)ConstraintType- An enum of constraint types.DataFrameArgumentType- An enum of dataframe argument types.BoundArgumentType- An enum of bound argument types.FilterColumnArgumentType- An enum of filter column argument types.IndexColumnsArgumentType- An enum of index column argument types.
Above classes is visualised ín the below UML class diagram. For an explanation of UML diagrams, please go to https://www.uml-diagrams.org.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file umip-1.0.0.tar.gz.
File metadata
- Download URL: umip-1.0.0.tar.gz
- Upload date:
- Size: 32.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
946a4ea1e58ff624f42901531b7236e7ad9ebd9737419389f8a92f544a9d9dc9
|
|
| MD5 |
e8e978b694b8b9b418924900176ebe26
|
|
| BLAKE2b-256 |
8f4a1965fd6399dec88595a3d19de647ac2b22b4ab5a3b1fab0968ef5411d03a
|
Provenance
The following attestation bundles were made for umip-1.0.0.tar.gz:
Publisher:
publish-pypi.yaml on SneaksAndData/umip
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
umip-1.0.0.tar.gz -
Subject digest:
946a4ea1e58ff624f42901531b7236e7ad9ebd9737419389f8a92f544a9d9dc9 - Sigstore transparency entry: 2674230197
- Sigstore integration time:
-
Permalink:
SneaksAndData/umip@68f6cb675791340a2b789c3e0510f3cc70953141 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/SneaksAndData
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yaml@68f6cb675791340a2b789c3e0510f3cc70953141 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file umip-1.0.0-py3-none-any.whl.
File metadata
- Download URL: umip-1.0.0-py3-none-any.whl
- Upload date:
- Size: 52.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3736f082a19b548b7548bf94fb1df3370a8469f3e4e008ee3cf5cfe0d725ef4d
|
|
| MD5 |
3c95d093072ab688f162af157573a83a
|
|
| BLAKE2b-256 |
7762cb4960f52a5a27e9681f6b2866ec2d524a21e32e2fc172d16b18d83051b1
|
Provenance
The following attestation bundles were made for umip-1.0.0-py3-none-any.whl:
Publisher:
publish-pypi.yaml on SneaksAndData/umip
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
umip-1.0.0-py3-none-any.whl -
Subject digest:
3736f082a19b548b7548bf94fb1df3370a8469f3e4e008ee3cf5cfe0d725ef4d - Sigstore transparency entry: 2674230145
- Sigstore integration time:
-
Permalink:
SneaksAndData/umip@68f6cb675791340a2b789c3e0510f3cc70953141 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/SneaksAndData
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yaml@68f6cb675791340a2b789c3e0510f3cc70953141 -
Trigger Event:
workflow_dispatch
-
Statement type: