AI agent that auto-tunes Gurobi MIP solver runs via parameter tuning + formulation rewriting, with a soft-prompt-specialized proposer trained on MIPLIB.
Project description
optimaze
A multi-agent system that auto-tunes Gurobi MIP solver runs. A central orchestrator runs the optimization loop, coordinating specialized agents that propose a solver config, run it, and diff the result — converging on a faster Gurobi configuration for each model.
What this is
optimaze wraps Gurobi in an agentic tuning loop. A central orchestrator
runs the optimization loop and coordinates three specialized agents:
- Proposer (tuning-strategy) — decides the next solver config to try. It runs a Thompson-sampling bandit over a 9-parameter space (MIPFocus, presolve, cuts, symmetry breaking, and more), seeded by k-NN retrieval over structurally similar MIPLIB instances, with an optional LLM that injects creative out-of-distribution configs.
- Solver runner — executes each proposed config against the model in a pool of warm Gurobi worker subprocesses and records the outcome: solve time, optimality gap, and branch-and-bound nodes.
- Performance differ — compares every run to the default-config baseline, updates the proposer's beliefs, and writes an auditable report explaining why the winning config won.
The orchestrator loops until the time budget is spent or progress stalls. An automated eval harness scores runs against the MIPLIB benchmark suite, and every run emits structured logs plus a markdown report from day one — so you can always see what each agent did and why.
Compared to grbtune (Gurobi's built-in exhaustive tuner), optimaze reads the
model: it uses constraint-graph features and priors from structurally similar
instances rather than starting from scratch.
Install
pip install optimaze
Optional extras:
pip install 'optimaze[hosted-llm]' # Anthropic / OpenAI backends
pip install 'optimaze[train]' # PEFT, datasets, accelerate (offline soft-prompt training)
pip install 'optimaze[dev]' # pytest, ruff, mypy
You'll need a working Gurobi license on the machine that runs trials — Gurobi runs locally and stays under your license.
Quickstart
optimaze tune model.mps
Defaults: 600 s wall-clock budget, up to 50 trials, 4 parallel workers, output
written to ./optimaze_out/. Use --no-llm for a pure-bandit run (no model
download, no API key needed):
optimaze tune model.mps --no-llm --budget 60 --max-trials 10 --seed 42
Library use:
from pathlib import Path
from optimaze.config import OptimazeConfig
from optimaze.core.orchestrator import Orchestrator
orchestrator = Orchestrator(config=OptimazeConfig(output_dir=Path("./optimaze_out")))
result = orchestrator.tune(Path("model.mps"), budget_seconds=60, max_trials=10, seed=42)
print(f"best solve_time: {result.best_outcome.solve_time_s:.3f}s "
f"({result.improvement_pct:.2f}% improvement)")
Example output
The following is the real output of a smoke-test run on the MIPLIB instance
markshare_4_0 (dev-tier, 4-trial budget, no LLM, seed 42). Verbatim from
optimaze_out/tuning_report.md:
# optimaze tuning report — markshare_4_0
- trials: 4
- baseline solve_time: 0.308s
- best solve_time: 0.236s
- improvement: 23.32%
## Best config
- `BranchDir` = 0
- `Cuts` = -1
- `Heuristics` = 0.6438651200806645
- `MIPFocus` = 1
- `Method` = 3
- `NodeMethod` = 1
- `Presolve` = 2
- `Symmetry` = 0
- `VarBranch` = 3
## Rationale
Best config was 1.19x faster than runner-up (0.24s vs 0.28s) because
Heuristics=0.6438651200806645 (vs 0.05), MIPFocus=1 (vs 0), Method=3 (vs -1),
Presolve=2 (vs -1), Symmetry=0 (vs -1), VarBranch=3 (vs -1).
## Trial log
1. 0.308s status=2 Baseline: Gurobi default config (always tried first).
2. 0.236s status=2 Thompson sample: arm pulled 0x, posterior Beta(α=1.00, β=1.00).
3. 0.281s status=2 Thompson sample: arm pulled 1x, posterior Beta(α=1.20, β=1.80).
4. 0.421s status=2 Thompson sample: arm pulled 0x, posterior Beta(α=1.00, β=1.00).
Headline: 23.32% wall-clock improvement on markshare_4_0 with 4 trials
(0.308s → 0.236s). This is one instance, not a benchmark sweep; broader
ML-eval numbers will land alongside the trained soft prefix in v0.4.2.
optimaze_out/ also contains:
best_config.json— the winning parameter set as JSONtrials.jsonl— one line per trial with config, hash, status, gap, nodeslogs/— per-trial Gurobi logs
How it works
Online (optimaze tune)
user model (.mps / .mps.gz)
│
▼
InstanceEmbedder ──► 24-d feature vector
│
▼
InstanceIndex (faiss k-NN) ──► priors from similar historical instances
│
▼
BanditProposer (Thompson sampling + optional LLM rationale)
│
▼
TrialPool (warm Gurobi worker subprocesses)
│
▼
PerfDiffer ──► updates bandit posterior, generates explanation
│
└──► loop until budget exhausted or 5 trials w/o improvement
│
▼
Emit: best_config.json, tuning_report.md, trials.jsonl
Optional --repo: GitCommitter opens branch + PR
Per-arm posteriors are Beta over a normalized speedup
r = clip(baseline_time / trial_time, 0, 5) / 5; the proposer Thompson-samples
one θ per arm and picks the max. The default Gurobi config is always tried
first as the baseline arm.
Offline (training pipeline, not run by users)
MIPLIB instances (240 catalogued; tiers: dev=10 / ci=50 / benchmark=180)
│
▼
ActiveLearningHarness (Bayesian acquisition picks informative trials)
│
├─► TrialPool ──► trial_outcomes.parquet
└─► InstanceEmbedder ──► instance_index.faiss
│
▼
DatasetBuilder (outcomes → (features → config + rationale) pairs;
rationales labelled by Claude/GPT at training time)
│
▼
SoftPromptTrainer (PEFT prefix tuning over Qwen2.5-7B, ~20 tokens)
│
▼
optimaze_prefix.pt (lands in v0.4.2)
The offline pipeline ships in the wheel under optimaze.harness and
optimaze.training. Users can re-run it on their own trial data, but the
shipped artifacts come from our training runs.
LLM backends
| Backend | Flag | Soft prefix | Network | Notes |
|---|---|---|---|---|
| Qwen local | --llm qwen-local (default) |
yes (when shipped) | none | Loads Qwen/Qwen2.5-7B-Instruct via transformers. CPU works; GPU recommended once the trained prefix lands. |
| Anthropic | --llm anthropic |
no (hard-prompt) | yes | Requires ANTHROPIC_API_KEY. Install with pip install 'optimaze[hosted-llm]'. |
| OpenAI | --llm openai |
no (hard-prompt) | yes | Requires OPENAI_API_KEY. Install with pip install 'optimaze[hosted-llm]'. |
| Pure bandit | --no-llm |
n/a | none | No model load, no API key. The example above used this mode. |
If an LLM backend fails to initialise (missing API key, no model cache, etc.), the CLI logs a warning and falls back to pure-bandit Thompson sampling. The bandit alone is enough to produce the 23.32% improvement shown above.
Differentiator vs grbtune
- Reads the model. Structural features (sparsity, integrality, constraint
graph) inform config proposals.
grbtuneis black-box exhaustive search. - Cold-start from neighbours. k-NN over the MIPLIB index reuses configs that worked on structurally similar instances rather than starting from the default every time.
- Auditable. Every run emits a markdown report explaining the winning
config relative to the baseline and the runner-up; optional
--repoopens a PR with the diff.
Status / v0.4.0 notes
This is the first release of the v2 rewrite. Honest scope:
- Bandit + retrieval pipeline: complete and shipping. The 23.32% number above used this path with the LLM disabled.
- Hosted LLM backends (Anthropic, OpenAI): complete; use hard-prompt templates. Production-ready as creative proposers.
- Trained soft-prompt artifact (
optimaze_prefix.pt): not yet shipped. The training code (optimaze.training) is included but the artifact lands in v0.4.2. v0.4.0 ships with a hard-prompt fallback inBanditProposerandQwenLocalClientthat works without a GPU and without the prefix file. - Formulation rewriting (
.pyinput):formulation/rewriter.pyis in-tree but the CLI path is param-only in v0.4.0. v0.5 is when the four rewrite types (aggregate constraints, eliminate redundant vars, symmetry-breaking, bound tightening) get plumbed throughoptimaze tune.
If you're evaluating optimaze against grbtune: the differentiator at this
version is cold-start + retrieval + auditable rationales. Soft-prompt
specialisation is the v0.4.2 differentiator.
Roadmap
See the full design spec at
docs/superpowers/specs/2026-05-17-optimaze-v2-design.md,
section 15 (out of scope for v1) and section 18 (build order).
- v0.4.2 — Trained soft-prompt artifact (
optimaze_prefix.pt) shipped in the wheel. ML-eval reports (≥60% of ci-tier instances ≥10% faster vs. default). Soft-prompt ablation numbers. - v0.5 —
formulation_trialplumbed through CLI: four named rewrite types (aggregate constraints, eliminate redundant vars, symmetry-breaking, tighten bounds). Param interaction modelling. - Later — LoRA fine-tune swap-in once the harness has ≥1k labelled trials. HuggingFace Hub mirror of the prefix artifact. GNN constraint-graph embeddings.
Citation
If you use optimaze in academic work, please cite:
@software{puri_optimaze_2026,
author = {Puri, Daniel},
title = {optimaze: multi-agent auto-tuning for Gurobi MIP solvers},
year = {2026},
version = {0.4.1},
url = {https://github.com/danielpuri1901/optimaze-agent}
}
License
MIT. See LICENSE.
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
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 optimaze-0.4.1.tar.gz.
File metadata
- Download URL: optimaze-0.4.1.tar.gz
- Upload date:
- Size: 131.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0fe515baa86ba8067e53cf2bb5c2e21081c6322e129bb89fb76640f527bebe7
|
|
| MD5 |
c23f41fc07c7010559cd9006d7cbab53
|
|
| BLAKE2b-256 |
73948ce058484387247ff34493079048fc5d1117b3e5b0cf420554f77cc622e4
|
File details
Details for the file optimaze-0.4.1-py3-none-any.whl.
File metadata
- Download URL: optimaze-0.4.1-py3-none-any.whl
- Upload date:
- Size: 77.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f3e66eb6ad3d6b62fa4aeec6011a97ac927fbe829f3ce7d593a2d7c451d9efb
|
|
| MD5 |
0276d5c10a94f2c2c1dee5f2c7302a44
|
|
| BLAKE2b-256 |
189378412f1bd0df3497258ed0bd32adfc5579e3ed7bccbf3de9a9c7a50ab730
|