Skip to main content

Machine-learning enhanced optimization library with pluggable solvers

Project description

mlopt

Machine-learning enhanced optimization with a clean modeling API, pluggable solvers (CBC/HiGHS/GA/Bayesian), and learning-augmented features. Model variables, constraints, and objectives in Python, then solve with mathematical programming or black-box global optimizers. Add surrogate models, pass learning hints, and scale from notebooks to CI/CD and PyPI.

  • Friendly modeling API inspired by LP/MIP tools
  • Multiple backends: CBC via PuLP, HiGHS via PuLP, Genetic Algorithm, Bayesian Optimization
  • ML-first utilities: Gaussian-process surrogates, learning-augmented solver hints
  • Robust validation and logging for safe, observable runs
  • Extensible plugin registry for custom solvers

Install

Prerequisites:

  • Python 3.9+
  • For LP/MIP: install PuLP. For Bayesian Opt: scikit-optimize. For surrogates: scikit-learn.

Examples:

  • Core + CBC via PuLP:
    • pip install pulp
  • Bayesian optimization:
    • pip install scikit-optimize
  • Surrogates:
    • pip install scikit-learn

Local editable install:

  • pip install -e .

Optional developer tools:

  • pip install pytest ruff mypy

Quick start

Linear program with CBC:

from mlopt.core.model import Model
from mlopt.core.objective import Objective
from mlopt.core.constraints import Constraint
from mlopt.plugins.registry import SolverRegistry

m = Model("diet_like")

x1 = m.add_var("x1", low=0)
x2 = m.add_var("x2", low=0)

obj = m.linexpr({x1: 3.0, x2: 2.0})
m.set_objective(Objective(obj, "min"))

m.add_constraint(Constraint(m.linexpr({x1: 2.0, x2: 1.0}), "<=", 100, "c1"))
m.add_constraint(Constraint(m.linexpr({x1: 1.0, x2: 1.0}), ">=", 50, "c2"))

solver = SolverRegistry.create("pulp_cbc")
m.set_solver(solver)
res = m.solve()

print(res.status, res.obj, res.x, res.solver_name)

Bayesian optimization for a black-box:

from typing import Dict
from mlopt.core.model import Model
from mlopt.core.objective import Objective
from mlopt.core.constraints import Constraint
from mlopt.plugins.registry import SolverRegistry

def black_box(a: Dict[str, float]) -> float:
    # (x1 - 2)^2 + (x2 - 3)^2
    return (a["x1"] - 2.0) ** 2 + (a["x2"] - 3.0) ** 2

m = Model("black_box")
m.add_var("x1", low=0.0, up=5.0)
m.add_var("x2", low=0.0, up=5.0)
m.set_objective(Objective(m.linexpr({}, 0.0), "min"))
m.add_constraint(Constraint(m.linexpr({}, 0.0), ">=", 0.0, "noop"))

solver = SolverRegistry.create("bayesopt", n_calls=25, random_state=42)
# If callable API available:
try:
    res = solver.solve_with_callable(m, black_box)  # type: ignore[attr-defined]
except Exception:
    setattr(m, "_black_box", black_box)
    res = solver.solve(m)

print(res.status, res.obj, res.x)

Genetic algorithm (heuristic):

from mlopt.core.model import Model
from mlopt.core.objective import Objective
from mlopt.core.constraints import Constraint
from mlopt.plugins.registry import SolverRegistry

m = Model("ga_example")

x = m.add_var("x", low=0, up=100)
y = m.add_var("y", low=0, up=100)

m.set_objective(Objective(m.linexpr({x: 1.0, y: 2.0}), "min"))
m.add_constraint(Constraint(m.linexpr({x: 1.0, y: 1.0}), ">=", 10, "c1"))

solver = SolverRegistry.create("genetic", pop_size=60, generations=120, seed=123)
m.set_solver(solver)
res = m.solve()
print(res.status, res.obj, res.x)

Concepts

  • Variables: name, bounds, category (Continuous, Integer, Binary)
  • Linear expressions: sum of coefficients times variables plus constant
  • Constraints: LinExpr with sense in {<=, >=, ==} and RHS
  • Objective: LinExpr with direction in {min, max}
  • Model: container for vars, constraints, objective, and a solver
  • SolveResult: status, objective value, solution map, solver name

Public API

Convenience imports:

from mlopt import (
  Model, Var, LinExpr, Constraint, Objective,
  SolveResult, SolverRegistry,
  PuLPCBC, GeneticSolver, BayesianOpt,
  SurrogateModel, LearningHints,
)

Subpackages:

  • core: modeling primitives
  • solvers: backends (CBC, HiGHS, GA, Bayesian)
  • ml: surrogates and hints
  • plugins: registry
  • utils: logging and validation

Solvers

  • CBC via PuLP (pulp_cbc)
    • Requires pulp
    • Deterministic LP/MIP solver, good defaults
  • HiGHS via PuLP (highs)
    • Requires PuLP with HiGHS support
    • Modern LP/MIP solver; adapter selects HiGHS_CMD/Highs_CMD automatically
  • GeneticSolver (meta_ga)
    • Population-based heuristic for nonconvex/black-box; supports seed and early stopping
  • BayesianOpt (bayesopt)
    • Global optimization with Gaussian Process surrogate
    • Accepts black-box via model._black_box or direct callable API (if available)
    • Requires scikit-optimize; respects variable bounds and adds constraint penalties

To create solvers:

solver = SolverRegistry.create("pulp_cbc")
# or
solver = SolverRegistry.create("highs", msg=False)
# or
solver = SolverRegistry.create("genetic", pop_size=50, generations=100, seed=42)
# or
solver = SolverRegistry.create("bayesopt", n_calls=30, random_state=42)

ML features

  • SurrogateModel (Gaussian Process)
    • fit(X, y), predict(X, return_std=False), reproducible with random_state
    • Useful for custom Bayesian/global loops outside the built-in solver
  • LearningHints
    • var_priorities: branching priorities by name
    • var_fixings: proposed fixings (e.g., binary 0/1)
    • cut_hints: numeric cut thresholds
    • to_solver_kwargs() returns normalized dict usable by adapters

Example usage:

from mlopt.ml.surrogate import SurrogateModel
from mlopt.ml.learning_hints import LearningHints

sur = SurrogateModel(random_state=42)
# sur.fit(X, y), sur.predict(X, return_std=True)

hints = LearningHints(var_priorities={"x": 10}, var_fixings={"y": 0})
kwargs = hints.to_solver_kwargs()
# Pass kwargs to adapters that support hint ingestion

Validation and logging

Validation:

  • validate_readiness_for_solver(model) checks:
    • Objective set, variables present, senses valid
    • Bounds consistency, duplicate names
    • Expressions reference declared variables

Logging:

  • get_logger(name) creates a namespaced logger
  • Environment variable MLOPT_LOG_LEVEL controls default level (e.g., DEBUG, INFO)
from mlopt.utils.validation import validate_readiness_for_solver, summary
from mlopt.utils.logging import get_logger, enable_debug

logger = get_logger(__name__)
enable_debug()

validate_readiness_for_solver(m)
logger.info(summary(m))
res = m.solve()
logger.info("Solved: %s obj=%.6f", res.status, res.obj)

Examples

  • examples/lp_example.py: basic LP with CBC
  • examples/bayes_example.py: black-box optimization with BayesianOpt

Run:

  • python examples/lp_example.py
  • python examples/bayes_example.py

Extending

Add a new solver:

  • Implement a class conforming to SolverBase with a solve(model) method returning SolveResult
  • Register it in plugins/registry.py:
    • SolverRegistry.register("my_solver", MySolver)

Support nonlinear or convex modeling:

  • Add new expression/objective types and adapters that translate to the target solver’s API
  • Keep the core API backward compatible

Learning-augmented strategies:

  • Log solve traces, train models to predict branching priorities or variable fixings
  • Use LearningHints to pass them into adapters that support hints

Project structure

  • mlopt/
    • core/: model, variables, expressions, constraints, objective
    • solvers/: base, pulp_cbc, highs, meta_ga, bayesopt
    • ml/: surrogate, learning_hints
    • plugins/: registry
    • utils/: logging, validation
  • examples/: lp_example.py, bayes_example.py
  • tests/: add unit tests for APIs and solvers
  • README.md, pyproject.toml, LICENSE

Development

Setup:

  • python -m venv .venv && source .venv/bin/activate # on Windows: .venv\Scripts\activate
  • pip install -e .
  • pip install pytest ruff mypy

Quality:

  • ruff check .
  • mypy mlopt
  • pytest

Conventional commits and PR guidance:

  • feat:, fix:, docs:, refactor:, test:, chore:
  • Include tests and update README/docs for user-facing changes

Release

  • Update version in pyproject.toml
  • Build:
    • python -m pip install --upgrade build twine
    • python -m build
    • twine check dist/*
  • Publish:
    • twine upload dist/* # or use your CI workflow on tag

Troubleshooting

  • Type checker error “Variable not allowed in type expression”:
    • Avoid annotating with runtime classes from external libs; prefer typing.Any or checker-only aliases.
  • CBC/HiGHS not available:
    • Ensure PuLP is installed and supports the chosen command (PULP_CBC_CMD, HiGHS_CMD/Highs_CMD).
  • Bayesian optimization import error:
    • Install scikit-optimize. If using surrogates, also install scikit-learn.
  • Black-box objective not used:
    • Use solver.solve_with_callable(model, fn) if available, or set model._black_box = fn before solve.

License

Apache-2.0

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

mlopter-0.1.4.tar.gz (13.4 kB view details)

Uploaded Source

Built Distribution

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

mlopter-0.1.4-py3-none-any.whl (4.4 kB view details)

Uploaded Python 3

File details

Details for the file mlopter-0.1.4.tar.gz.

File metadata

  • Download URL: mlopter-0.1.4.tar.gz
  • Upload date:
  • Size: 13.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for mlopter-0.1.4.tar.gz
Algorithm Hash digest
SHA256 a1eff76c867a4d99e40cd19bdf33dfd0b30d0004f3d83d48474abfcd0d90668c
MD5 c8ffa4b69f7268220b3c8e6ee2e1eba8
BLAKE2b-256 eae960d18114664285b88decc52eed08e4ee6e6d878d46d3d1e3d641cf9b1788

See more details on using hashes here.

File details

Details for the file mlopter-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: mlopter-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 4.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for mlopter-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 10c7ea528a95b1161b617e538b9018fbf3caf95d19471914936a182c9730e9b6
MD5 34f6184fc5fdc03d31cbf8533ea5d232
BLAKE2b-256 0f136830bc0a6978e7a6f6f72a1e90a9379483784b9380993e8eb49f71635735

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