pyomo-pounce
Pyomo solver plugin for POUNCE, a pure-Rust interior-point NLP solver (a Rust port of IPOPT).
POUNCE speaks the AMPL NL/SOL protocol, so Pyomo drives it through the AMPL Solver Library interface — exactly how Pyomo integrates with IPOPT.
Installation
pip install pyomo-pounce
That single command pulls in the pounce-solver dependency, which
ships a per-platform wheel bundling the pounce executable. After
install, pounce is on your PATH and Pyomo finds it automatically.
Usage
import pyomo_pounce # registers the solver — REQUIRED before SolverFactory('pounce')
from pyomo.environ import *
model = ConcreteModel()
model.x = Var(initialize=0.5)
model.obj = Objective(expr=(model.x - 2)**2)
solver = SolverFactory('pounce')
result = solver.solve(model, tee=True)
print(f"x* = {value(model.x)}") # 2.0
import pyomo_pounceis required. Without it,SolverFactory('pounce')raises a clearUnknownSolver/ "plugin not registered" error — it does not silently run some otherpounce. With it imported, the plugin runs thepouncebinary bundled in thepounce-solverwheel, independent ofPATH. Only a source/dev install without that wheel falls back to apounceonPATH, in which case the plugin warns.To see exactly which binary will run (and whether a stale or unrelated
pounceearlier onPATHwould shadow it), call:import pyomo_pounce pyomo_pounce.check_binary()The check compares the git commit embedded in
pounce --about, not the version string — two builds can share the sameX.Y.Zwhile differing in behavior (as a binary from before/after a fix does).
Pyomo's modern (pyomo.contrib.solver) interface
The same solver is registered against Pyomo's newer solver interface —
the one ipopt_v2 uses — carrying every extra on this page:
import pyomo_pounce
from pyomo.contrib.solver.common.factory import SolverFactory as SolverFactoryV2
solver = SolverFactoryV2('pounce') # returns a v2 Results object
results = solver.solve(model)
print(results.solution_status, results.incumbent_objective)
SolverFactory('pounce_v2') gives the same engine behind the legacy API,
mirroring Pyomo's own ipopt / ipopt_v2 split. SolverFactory('pounce')
is unchanged and remains fully supported.
Both routes return the same numbers — a test solves one model through
each and compares primals, objective, duals and reduced costs. They
differ in API (v2 returns Results and hands the solution back through a
loader, so load_solutions=False gives you values without touching the
model; options are solver_options={...} rather than options={...})
and in per-solve overhead outside the solve, which on IDAES-shaped
collocation models is roughly 0.25 s/solve lower on v2. See
the Pyomo docs page for
the measurements.
Pointing ipopt_v2 at the pounce binary by hand also works, but
silently drops all of the above — the integer-variable guard, the
scaling_factor handling, the sensitivity path and the bundled-binary
resolution. Prefer one of the two registrations above.
Requirements for the v2 route —
pip install pyomo-pounce[pyomo-v2]asks for both:
- Pyomo ≥ 6.10.1, which is where the
SolutionLoader/get_varsAPI this builds on landed. (pyomo.contrib.solver.commonexists from 6.9.2, but 6.9.2–6.10.0 ship the olderSolutionLoaderBase/get_primals.)- pounce-solver > 0.9.0. The v2 route reads the
.solthrough Pyomo'sasl_sol_reader, which is strict where the legacy reader is lenient, so it needs the per-modelOptionsecho added after 0.9.0.Neither applies to
SolverFactory('pounce'). On an older Pyomo,import pyomo_pouncestill works and the legacy plugin behaves exactly as before;pyomo_pounce.HAVE_V2_INTERFACEreports whether the v2 names are available.
Solver Options
Pass options the same way as IPOPT:
solver = SolverFactory('pounce')
solver.options['max_iter'] = 1000
solver.options['tol'] = 1e-10
solver.options['print_level'] = 5
Options are forwarded to POUNCE's OptionsList (ipopt.opt-compatible
keys).
User scaling
The standard scaling_factor Suffix works as it does with IPOPT:
model.scaling_factor = Suffix(direction=Suffix.EXPORT)
model.scaling_factor[model.obj] = 1e-3
model.scaling_factor[model.mass_balance] = 1e2
solver.solve(model, options={'nlp_scaling_method': 'user-scaling'})
Both halves are needed — the Suffix alone is inert (it also drives
Pyomo's own core.scale_model), and the option alone has nothing to
apply, which pyomo-pounce warns about. Untagged components are
unscaled; inactive constraints and fixed variables are skipped.
POUNCE models objective and constraint scaling only, so a
scaling_factor on a Var raises rather than being silently
dropped (#483) —
rescale those variables in the model instead.
Local development / unsupported platforms
If pounce-solver does not ship a wheel for your platform, the pip
install fails on the dependency. Two workarounds:
-
Build POUNCE from source and put it on
PATH— the plugin resolvespounceviashutil.which, so any binary onPATHworks:# in the pounce repo cargo build --release --bin pounce export PATH="$PWD/target/release:$PATH" pip install --no-deps pyomo-pounce pyomo
-
Install
pounce-solverfrom source via maturin:make -C pounce dev # maturin develop --release + the bundled CLI pip install pyomo-pounce
make devrather than a barematurin develop --release: maturin builds the extension module and not the CLI, and this plugin shells out to the CLI. Without it the plugin falls through to the checkout'starget/release/pounce(warning that it did) or, failing that, to whateverpounceis onPATH— see gh #816.
Running the tests locally
Which binary the tests exercise depends on whether a bundled one is
present: the plugin prefers pounce/bin/pounce inside the installed
pounce-solver package, then the surrounding checkout's own
target/release/pounce or target/debug/pounce, and reaches PATH only
when there is neither (gh #816). Setup 1 above bundles nothing, so a plain
source checkout takes one of the fallback rungs while CI takes the bundled
one. Tests that describe the bundled arrangement then skip, and the suite you
run locally is not the suite CI runs.
To match CI, stage the freshly built CLI into the package before building
the wheel — this is what .github/workflows/ci.yml does:
cargo build --release --bin pounce
mkdir -p python/pounce/bin
cp target/release/pounce python/pounce/bin/pounce # the step that bundles it
(cd python && maturin build --release --out dist)
pip install python/dist/*.whl # pounce module + bundled CLI
pip install --no-deps -e pyomo-pounce
pytest pyomo-pounce/tests -q
networkx and scipy are needed for test_block_init.py and
test_repair.py; without them those two files skip.
If a test involving duals, multipliers, or bound reduced costs fails, check the binary before the code:
import pyomo_pounce; pyomo_pounce.check_binary()
It reports which executable will actually run, its build commit, and
whether anything on PATH shadows it. Two builds can share a version
string while differing in commit, so a stale binary reports a plausible
X.Y.Z while returning pre-fix results — that is the failure mode gh #315
added check_binary() for, and the one gh #366 turned out to be about.
License
EPL-2.0, same as POUNCE.
Metadata
Release files for pyomo-pounce 0.12.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyomo_pounce-0.12.0.tar.gz | 228.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyomo_pounce-0.12.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 328.0 kB
Release files / pyomo_pounce-0.12.0.tar.gz
| Download URL | pyomo_pounce-0.12.0.tar.gz |
|---|---|
| Size | 228.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2b23cbc508885c23df122d6d6b6bde5d8463d5f03b35e8f22d32041245e99e0e
|
|
BLAKE2b-256 checksum How to use checksums |
172da7f9a50bc47fb0f64f9a33af108f0e2b7857c5e78a642239065b0de16eb2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 19, 2026.
Transparency logRelease files / pyomo_pounce-0.12.0-py3-none-any.whl
| Download URL | pyomo_pounce-0.12.0-py3-none-any.whl |
|---|---|
| Size | 99.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
159f87d2ae2c0c087dc7921fc20b1dbd8e8e736bd6d4c356ba37274222378694
|
|
BLAKE2b-256 checksum How to use checksums |
eee6171caf3b13a472b8cfbf8a3899571abb7aa3c9e1e740054f589865406372
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 19, 2026.
Transparency log