Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
180 commits
Select commit Hold shift + click to select a range
2c8a04e
Correct LP/QP classification in routing dev note
claude May 28, 2026
25607b4
Reframe global nonconvex QP as deferred, document re-entry seams
claude May 28, 2026
1f2dc1a
Scope presolve integration for the convex solver with literature review
claude May 28, 2026
d91fe12
Constrain presolve plan to pure Rust; learn from PaPILO, use rayon
claude May 28, 2026
5daa695
Sequence presolve into the implementation phasing
claude May 28, 2026
06a1e7e
Review fixes: drop simplex, add ConvexQcqp/SOCP route, verification p…
claude May 28, 2026
0d8eb4e
Distinguish algorithmic vs wall-clock competitiveness (Phase 3 vs 3.5)
claude May 28, 2026
dc9180d
Clarify active-set is opt-in; auto cannot route to it from the NL path
claude May 28, 2026
9c42f2e
Make cone-generic scaffolding an explicit Phase 2 deliverable
claude May 28, 2026
324d347
Correct mu handling: convex IPM needs a barrier parameter strategy
claude May 28, 2026
3c6c957
Specify the quadratic-objective HSDE variant in Phase 3
claude May 28, 2026
af76c01
Extract constant P/A once for the convex solver
claude May 28, 2026
4b9f256
Make Phase 1 classifier unit tests hermetic
claude May 28, 2026
bba882b
Add active-set SQP relationship section; drop stale year in heading
claude May 28, 2026
9a17e32
Add performance-engineering companion note
claude May 28, 2026
95d5bb8
Firm up tier-2 determinism decision; split 2a/2b
claude May 28, 2026
15fa629
Add actionable "how to hold tier 2" rules
claude May 29, 2026
2abdb97
Final read-through fixes to performance-engineering note
claude May 29, 2026
21e6ea7
feat(cli): LP/QP dispatch scaffolding (Phase 1)
claude May 30, 2026
3437f49
feat(convex): bare primal-dual IPM for convex QP (Phase 2, first incr…
claude May 30, 2026
36d30f3
test(convex): iteration-count comparison example vs NLP builtins
claude May 30, 2026
04f07ed
feat(convex): Mehrotra predictor-corrector + QP-vs-NLP iteration check
claude May 30, 2026
83327ac
perf(convex): constant-pattern factor reuse + scaling harness
claude May 30, 2026
940ae70
feat(cli): wire .nl convex LP/QP dispatch to pounce-convex
claude May 30, 2026
417bdb7
docs: user guide for LP/QP solver routing
claude May 30, 2026
13f6a5c
feat(cli): recover convex-QP constraint duals for .sol output
claude May 30, 2026
e856442
feat(convex): verified infeasibility / unboundedness detection
claude May 30, 2026
f262032
docs(dev-note): split Phase 3 HSDE — certificates now, embedding at SOCP
claude May 30, 2026
6dd8ade
docs(perf-note): align Phase 3 label with the HSDE split
claude May 30, 2026
5031545
feat(convex): QP presolve with transaction-stack postsolve (Phase 3.5)
claude May 30, 2026
37834c6
fix(convex): restore presolve postsolve z-mapping loop body
claude May 30, 2026
038ca33
feat(convex): LP-oriented presolve reductions toward PaPILO parity
claude May 31, 2026
f39d0e1
docs(convex): fix stale test-file reference in presolve module docs
claude May 31, 2026
e8e5347
feat(convex): explicit variable-bound form (lb/ub) on QpProblem
claude May 31, 2026
b43608a
feat(convex): activity-bound presolve reductions (redundant rows, inf…
claude May 31, 2026
944bd99
feat(convex): free column singleton substitution presolve
claude May 31, 2026
a5f1ee4
feat(cli): run presolve before the convex LP/QP solve
claude May 31, 2026
9cc0390
docs(dev-note): record Phase 3.5 presolve implementation status
claude May 31, 2026
77cc1dd
feat(convex,cli): presolve stats + qp_presolve toggle, end to end
claude May 31, 2026
34fcae6
feat(convex): batched / multiple-RHS QP solving
claude May 31, 2026
3b92c8b
feat(convex): QpFactorization build-once / solve-many handle
claude May 31, 2026
176b9a7
feat(py): PyO3 bindings for the convex LP/QP solver (pounce-convex)
claude May 31, 2026
c07ce02
Add Python QP wrapper and differentiable JAX QP layer
claude May 31, 2026
3fa6985
JAX QP: add matrix gradients (P/G/A) and a parallel differentiable batch
claude May 31, 2026
0f5a097
Convex QP: warm starting across the core, bindings, and JAX layer
claude May 31, 2026
60406ee
Convex QP: warm starting for the parallel batch path
claude May 31, 2026
b9ba14a
Convex QP: adaptive warm-start floor (exploit the warm duals)
claude May 31, 2026
43a4b0e
Presolve: forcing constraints (the deferred PaPILO reduction)
claude May 31, 2026
23258c4
Presolve: parallel-row detection (scalar-multiple rows)
claude May 31, 2026
4a0141d
Presolve: dominated columns (sign-definite column → bound)
claude May 31, 2026
73448d3
Presolve: bound tightening — the last deferred reduction (full dual)
claude May 31, 2026
b154806
Presolve: iterate the reduction passes to a fixpoint
claude May 31, 2026
fa242cb
Presolve: lift the disjoint-source restriction via the fixpoint
claude May 31, 2026
a36daf0
Batch QP: per-backend serial toggle, drop the FERAL_PARALLEL env danc…
claude May 31, 2026
06f0cd2
Batch QP: drop the 64 MiB worker-stack pool (no longer needed)
claude May 31, 2026
d1cc225
Convex: SOCP design note + Phase 1 CompositeCone refactor
claude May 31, 2026
1140854
Convex SOCP Phase 2a: cone trait generalization + SecondOrderCone NT …
claude May 31, 2026
305d105
Convex: SOCP Phase 2b — end-to-end second-order cone solving
claude May 31, 2026
82d1290
SOCP: Python bindings (solve_socp) for second-order cone programs
claude May 31, 2026
28544bb
SOCP: warm starting (cone-aware recentering)
claude May 31, 2026
ffe4b65
SOCP: cone-aware presolve (presolve_conic)
claude May 31, 2026
32df774
SOCP: differentiable layer (cone-aware OptNet)
claude May 31, 2026
dd4f051
SOCP: Tier-B sparse low-rank KKT for second-order cones
claude May 31, 2026
791f2ac
docs: document convex LP/QP/SOCP solver + add example notebooks
claude May 31, 2026
6095c00
docs: scope Clarabel cone parity + HSDE foundation design notes
claude May 31, 2026
738463f
feat(convex): HSDE driver for linear conic programs (Phase H2)
claude May 31, 2026
f0b9e58
feat(convex): quadratic-objective HSDE embedding (Phase H3)
claude May 31, 2026
1307cf9
feat(convex): promote HSDE to a selectable driver (Phase H4, revised)
claude May 31, 2026
69b1abe
feat(convex): exponential-cone barrier oracles (Phase H5 groundwork)
claude May 31, 2026
1ee47e6
docs(hsde): derive the non-symmetric cone Newton step (H5 blueprint)
claude May 31, 2026
c47910f
docs(hsde): non-symmetric driver prototype findings; correct scaling
claude May 31, 2026
a2642f5
style: cargo fmt the pounce-convex / cli sources
jkitchin May 31, 2026
de48d04
fix(convex): correct LP/QP dispatch + JAX status; add regressions
jkitchin May 31, 2026
b841f9c
feat(convex): non-symmetric (exp/power) cones + SOC mixing on an HSDE…
jkitchin May 31, 2026
c34eaaa
docs(hsde): record the CBLIB benchmark-tier plan (deferred)
jkitchin May 31, 2026
0c75e98
feat(qp): Python parity quick-wins — top-level exports, residuals, tr…
jkitchin Jun 1, 2026
e8a5f66
feat(qp): post-optimal sensitivity (sIPOPT analog) for the convex QP
jkitchin Jun 1, 2026
ec2cc9c
feat(qp): reduced Hessian + eigendecomposition; hoist eigensolver to …
jkitchin Jun 1, 2026
159e0ca
feat(cli): CBF reader + CBLIB exp-cone benchmark tier (parse → conic …
jkitchin Jun 1, 2026
50e8c10
test(cli): CBLIB conic-vs-NLP cross-check — independent validation
jkitchin Jun 1, 2026
4bb3af3
docs(hsde): record the CBLIB exp-cone GP tier as landed
jkitchin Jun 1, 2026
4a3770b
feat(cli): power cones in the CBF reader (POWCONES) + NLP cross-check
jkitchin Jun 1, 2026
67c2197
feat(benchmarks): CBLIB conic tier — pounce_cblib report binary + suite
jkitchin Jun 1, 2026
73c86d7
docs(hsde): record the CBLIB power-cone tier + harness integration as…
jkitchin Jun 1, 2026
e981349
docs: "Choosing a Solver" overview page + solver-landscape SVG
jkitchin Jun 1, 2026
aeafced
feat(convex): PSD cone oracles (Phase H7 foundation)
jkitchin Jun 1, 2026
1a67c80
feat(convex): PSD cone — Cone trait impl (matrix-Jordan NT machinery)
jkitchin Jun 1, 2026
49384e1
feat(convex): PSD cone driver integration — SDP solves end to end (H7)
jkitchin Jun 1, 2026
5ae69e8
docs: record PSD/H7 as landed (hsde.md + Choosing a Solver)
jkitchin Jun 1, 2026
01e2adf
feat(py): expose the PSD cone to Python (solve_socp cones=[("psd", n)])
jkitchin Jun 1, 2026
b1dff05
feat(convex): PSD sparsity — block-diagonal (connected-components) de…
jkitchin Jun 1, 2026
19a5559
docs(hsde): record PSD Python wiring + block-diagonal sparsity
jkitchin Jun 1, 2026
416db08
feat(convex): chordal range-space decomposition for sparse SDPs (H7)
jkitchin Jun 1, 2026
53b6ff8
feat(cli): CBF PSDCON / HCOORD / DCOORD reading (SDP instances)
jkitchin Jun 1, 2026
91bf7aa
docs(hsde): record chordal decomposition + CBF SDP reading as landed
jkitchin Jun 1, 2026
060319e
feat(convex): SOS global lower bounds for polynomial minimization
jkitchin Jun 1, 2026
ad7b0ba
feat(convex): constrained SOS via Putinar localizing multipliers
jkitchin Jun 1, 2026
33f3c1f
feat(convex): SOS minimizer extraction + exactness certificate
jkitchin Jun 1, 2026
1d1698f
feat(convex): multi-atom SOS minimizer extraction (no nonsymmetric eig)
jkitchin Jun 1, 2026
64b65e2
feat(py): expose the SOS polynomial global optimizer to Python
jkitchin Jun 1, 2026
cffba9d
feat(convex): facial reduction for SOS minimizer extraction
jkitchin Jun 1, 2026
e30d7bd
fix(convex): carry degenerate order-3 SOS solves to optimality
jkitchin Jun 1, 2026
9681cf0
feat(global): spatial branch-and-bound for nonconvex NLPs (pounce-glo…
jkitchin Jun 1, 2026
65f35c2
feat(global): local NLP upper bounds for spatial branch-and-bound
jkitchin Jun 1, 2026
ccc5ae4
feat(global): tight polyhedral envelopes for univariate atoms
jkitchin Jun 1, 2026
ddcadc8
feat(global): cutting-plane (sandwich) bound refinement
jkitchin Jun 1, 2026
5fa7699
feat(global): optimization-based bound tightening (OBBT)
jkitchin Jun 1, 2026
1aa3296
feat(global): αBB convex underestimators for the objective
jkitchin Jun 1, 2026
1bd2774
feat(global): level-1 RLT cuts (linear constraint × bound factors)
jkitchin Jun 1, 2026
20685d5
feat(global): tighter multi-grouping relaxation of trilinear products
jkitchin Jun 1, 2026
2783334
docs(global): document the global-optimization capability
jkitchin Jun 1, 2026
a7b0904
feat(global): most-violation branching
jkitchin Jun 1, 2026
2a27bf0
feat(global): reliability branching (pseudocost + strong branching)
jkitchin Jun 1, 2026
a03b08a
feat(global): parallel OBBT (deterministic, ~2.3x wall-clock)
jkitchin Jun 1, 2026
da46268
feat(global): parallel node pool (~2.6x wall-clock)
jkitchin Jun 1, 2026
6af53e1
docs(global): note why the node pool keeps OBBT serial
jkitchin Jun 1, 2026
c022927
build(global): lock the rayon dependency edge
jkitchin Jun 1, 2026
c899d41
feat(py): expose the spatial branch-and-bound global optimizer to Python
jkitchin Jun 1, 2026
c362370
feat(cli): --solver global — spatial branch-and-bound on .nl models
jkitchin Jun 1, 2026
09d37bb
feat(global): faithful solve-report JSON (genuine constraint-violatio…
jkitchin Jun 1, 2026
893e412
feat(global): exact Lagrangian Hessian for the local upper-bound solve
jkitchin Jun 1, 2026
e5935af
feat(global): valid sloped relaxation for sin/cos over boxes wider th…
jkitchin Jun 1, 2026
54bcf91
docs: solver-landscape SVG — PSD cone shipped, not "planned"
jkitchin Jun 1, 2026
f80271b
test(global): mixed eq+ineq, ratio-term, and SOS↔sBB agreement
jkitchin Jun 1, 2026
596cd90
docs(global): refresh stale sine-test comment for the wide-trig relax…
jkitchin Jun 1, 2026
ae6398d
feat(global): frontier memory estimation, reporting, and a pre-solve …
jkitchin Jun 1, 2026
21fa638
bench(global): graduated verifiable benchmark suite + tier README
jkitchin Jun 1, 2026
46ad3e3
docs(bench): measure node-pool contention — frontier sharding (#7) no…
jkitchin Jun 1, 2026
53ff8e3
docs: reposition POUNCE as a general interior-point solver
claude Jun 2, 2026
468cc2e
refactor(debug): generalize the solver debugger over a DebugState trait
claude Jun 2, 2026
4d43c56
feat(debug): wire the interactive debugger into the convex IPM
claude Jun 2, 2026
6e0c94d
feat(debug): wire the symmetric HSDE driver into the convex debugger
claude Jun 2, 2026
3d31047
feat(debug): wire the non-symmetric exp/power HSDE driver into the de…
claude Jun 2, 2026
df074e6
feat(debug): convex debugger mutation + rewind (set / goto)
claude Jun 2, 2026
2455bfd
feat(debug): conic debugging from the CLI (pounce_cblib --debug)
claude Jun 2, 2026
78754f1
feat(debug): branch-and-bound tree-debugger engine
claude Jun 2, 2026
8ff9cd2
feat(debug): interactive branch-and-bound tree debugger in the CLI
claude Jun 2, 2026
023b12d
feat(debug): step into a B&B node's relaxation (tree ↔ interior-point)
claude Jun 2, 2026
9dd0175
feat(debug): scripted step-into via a shared command queue + debugger…
claude Jun 2, 2026
ad8706a
docs(python): scipy.optimize compatibility table + why minimize is NL…
claude Jun 2, 2026
5ea45c3
test(cli): set var_names/con_names in NlProblem test initializers
claude Jun 5, 2026
6d67d7b
Merge main (0.4.0 release) into PR #70 (convex + global solvers)
jkitchin Jun 6, 2026
adf6496
docs(dev-notes): multi-solver maintenance tech-debt audit
jkitchin Jun 6, 2026
e1b91e2
fix(convex): correct LP/QP reported objective and clear IPM stall
jkitchin Jun 6, 2026
499980a
feat(convex): make HSDE the default LP/QP driver; Ruiz equilibration …
jkitchin Jun 6, 2026
3254272
feat(cli): print a routing banner naming the detected class and selec…
jkitchin Jun 6, 2026
caf6bcb
feat(python): auto-route minimize() to the convex LP/QP solver
jkitchin Jun 6, 2026
82d0a45
docs: document HSDE driver, routing banner, and minimize() auto-routing
jkitchin Jun 6, 2026
0e22541
bench: add NLP-vs-convex(HSDE) solver comparison script
jkitchin Jun 6, 2026
e1b53e2
build: pin feral to git rev bb74821 (MC64/scaling perf, issue #80)
jkitchin Jun 6, 2026
ec957ae
feat(global): make spatial B&B options CLI-tunable and add a wall-clo…
jkitchin Jun 7, 2026
b888fc4
fix(global): drop near-singular envelope tangents (chance false-infea…
jkitchin Jun 7, 2026
cbdd8d3
fix(convex): Ruiz-equilibration fallback for ill-scaled HSDE relaxati…
jkitchin Jun 7, 2026
4a72ecb
bench(global): add GLOBALLib proven-optimum benchmark (59/104 OK)
jkitchin Jun 7, 2026
514a58d
test(pr70): bootstrap hardening loop — tracker + comparison harness
jkitchin Jun 7, 2026
cd08d7d
test(pr70): A1 routing classification — 5 new cases green, no misrout…
jkitchin Jun 7, 2026
1a88733
test(pr70): A2 forced-mismatch must error — 6 cases green, no mis-solve
jkitchin Jun 7, 2026
c82e988
test(pr70): B objective validation — found HIGH-sev capri LP wrong-an…
jkitchin Jun 7, 2026
5c777de
test(pr70): C status/edge-case honesty — limit statuses never report …
jkitchin Jun 7, 2026
d617e0c
test(pr70): D nonsymmetric cones & SDP — end-to-end exp/power/PSD; ne…
jkitchin Jun 7, 2026
78a16ff
fix(convex): add acceptable-iterate tier to symmetric HSDE driver
jkitchin Jun 7, 2026
64f1a9d
test(pr70): E global solver soundness — valid global lower bounds + s…
jkitchin Jun 7, 2026
d225566
test(pr70): F presolve round-trip — heavily-reduced primal+dual recovery
jkitchin Jun 7, 2026
4696453
test(pr70): G FFI/Python surface — cross-path JSON schema uniformity
jkitchin Jun 7, 2026
e457ede
test(pr70): H hygiene — both suites green, PR70-new libs clippy-clean…
jkitchin Jun 7, 2026
d12a27f
fix(convex): correct postsolve primal-recovery order (capri LP wrong …
jkitchin Jun 7, 2026
8eac8ee
fix(convex): solve near-boundary exp-cone GPs and certify PSD infeasi…
jkitchin Jun 7, 2026
ce1ad8e
test(pr70): guard pytest against a stale compiled extension
jkitchin Jun 7, 2026
6dfdfbd
feat(simplex): add pounce-simplex — faer sparse-LU basis + dual warm-…
jkitchin Jun 7, 2026
d58bc20
perf(global): per-node cost overhaul — OBBT scheduling, warm-starts, …
jkitchin Jun 7, 2026
e49b457
fix(simplex): drop collapsed coefficients in equilibration (wrong-ans…
jkitchin Jun 7, 2026
e949f53
bench(globallib): fast dev tiers + IPM-vs-simplex OBBT cross-check
jkitchin Jun 7, 2026
9932653
feat(simplex): Harris two-pass + EXPAND anti-degeneracy ratio test
jkitchin Jun 8, 2026
9a4c908
refactor: strip pounce-global from the LP/QP merge path
jkitchin Jun 8, 2026
032c4f8
refactor: strip pounce-simplex from the LP/QP merge path
jkitchin Jun 8, 2026
f36d263
build: bump feral to HEAD 11fb4b9 (issue #80 MC64/scaling perf)
jkitchin Jun 8, 2026
369a7b9
docs: add CLAUDE.md — release/publishing facts + crucible KB pointer
jkitchin Jun 8, 2026
a4d98d3
docs: add vision/positioning notes and v0.4.0 announcement draft
jkitchin Jun 8, 2026
36f50bf
refactor: strip the global-solver tree debugger from the LP/QP merge …
jkitchin Jun 8, 2026
d259131
style: snake_case the p_calculator test fn name (zero-warning build)
jkitchin Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
!/benchmarks/lp/mps_to_nl.py
!/benchmarks/lpopt/build_subset.py
!/benchmarks/lpopt/mps_to_nl.py
# The cblib suite is the conic (exp/power cone) tier: CBLIB .cbf instances
# solved through the pounce_cblib binary. Track its runner (README via the
# per-suite rule above); the per-run pounce.json stays ignored like every
# other suite.
!/benchmarks/cblib/run_cblib.py
# Vanderbei reference status (derived once from cute_table.pdf): which
# problems have a documented feasible optimum vs. are hard/infeasible/
# unbounded/untabulated. Tracked so we never have to re-derive it.
Expand Down Expand Up @@ -87,6 +92,9 @@ python/dist/
python/examples/*.png
python/*.egg-info/

# Local virtualenvs (e.g. for building the extension + running notebooks)
.venv/

# Claude Code local state
.claude/

Expand Down
85 changes: 85 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,91 @@ changes.

## [0.4.0] — 2026-06-05

### Added — Convex / conic solver (`pounce-convex`; `solve_qp` / `solve_socp`)

POUNCE is no longer NLP-only: a new pure-Rust convex interior-point solver
(`pounce-convex`) handles **LP, convex QP, SOCP, and PSD / exp / power cones**,
solving each to a **global** optimum (a convex problem has no other kind). It
uses a homogeneous self-dual embedding (HSDE) — symmetric for the self-dual
cones and a non-symmetric driver for the exponential/power cones — over a
`Cone` abstraction (`nonneg`, `soc`, `psd`, `exp`, `power`, plus composite and
chordal decompositions for sparse SDPs). Convex solvers extract the constant
`P`, `A`, `c`, `b` data once at setup rather than re-evaluating per iteration,
and share the `pounce-linsol` / `pounce-linalg` factorization substrate with the
NLP path. Python entry points are typed (not SciPy-shaped, by necessity — a cone
program is *data*, not a callable): `solve_qp(P, c, A, b, G, h, lb, ub, …)`,
`solve_socp(…, cones=…)`, plus `solve_qp_batch` / `solve_qp_multi_rhs` for
batched factor reuse, and a reduced-Hessian sensitivity API. The CLI reads conic
instances from CBLIB / `.cbf` (including PSDCON / HCOORD / DCOORD SDP blocks).

### Fixed — Convex LP/QP reported objective dropped tree-folded constant

The convex LP/QP path (`solver_selection=lp-ipm` / `qp-ipm`) reported an
objective off by the objective's constant term whenever AMPL/Pyomo folded that
constant into the **nonlinear objective tree** (the `+9` of `(x-3)²`) rather
than the `.nl` linear-section constant. The quadratic-form extractor
(`analyze_quadratic_full`) discarded the degree-0 term — correct for the
*minimizer*, wrong for the *reported value* — so e.g. `HS21` reported `0.04`
instead of `−99.96` and `HS35` `−8.889` instead of `0.111`. The extractor now
returns that constant and the convex driver adds it to the reported objective
alongside `obj_constant`; the optimal point was always correct. Caught by a
head-to-head NLP-vs-convex run over the Maros-Mészáros QP and NETLIB LP suites
(`benchmarks/nl_compare_nlp_vs_convex.md`).

### Fixed — Convex LP/QP IPM stalled on badly-scaled NETLIB LPs

The static KKT regularization `δ` (added on the reduced KKT diagonal so the
LDLᵀ has a stable inertia) was `1e-8`, large enough to **floor the achievable
primal residual** at `δ·‖dy‖`: with a full Newton step `A·dx = −r_p + δ·dy`, so
on instances with large equality multipliers the primal infeasibility cannot
fall below `δ·‖dy‖`. On NETLIB `adlittle` (`‖dy‖ ≈ 4e8`) this froze `inf_pr`
near 4 and the LP IPM ran to its iteration cap, returning a wrong objective
(`439665` vs the published `225494.96`). Lowering the default `δ` to `1e-10` —
still strictly positive, so the system stays quasi-definite — clears the floor:
`adlittle` now converges in ~57 iterations to the optimum, `stocfor1` speeds up
(139 → 71 iters), and the rest of the LP/QP suites are unchanged (the QP suite
is bit-identical). The whole `1e-9‥1e-11` band converges the benchmark suites;
`1e-10` is centered in it.

Also: the convex IPM's opt-in iteration trace now records a **terminal record at
the converged iterate** (the NLP path's N+1 convention), so the trace always
ends at the optimum instead of at the last pre-step state — previously a solve
that converged in a single step left only the cold-start record in the trace.

### Added — SOS polynomial global optimization (`sos_minimize`)

`sos_minimize(objective, *, inequalities, equalities, …)` computes **certified
global** lower bounds for polynomial optimization via a sum-of-squares /
Lasserre relaxation (Putinar localizing multipliers for constraints), built on
the new PSD cone. When the relaxation is exact it extracts the global
minimizer(s) with an exactness certificate (multi-atom extraction without a
non-symmetric eig, plus facial reduction for degenerate solves).

### Added — Spatial branch-and-bound global optimizer (`pounce-global`;
`minimize_global` / `--solver global`)

A new `pounce-global` crate solves **factorable nonconvex NLPs to a certified
global optimum** by spatial branch-and-bound: αBB convex underestimators,
polyhedral envelopes for univariate atoms, level-1 RLT cuts, multi-grouping
trilinear relaxations, optimization-based bound tightening (OBBT), and
cutting-plane bound refinement, with local NLP upper bounds. Branching is
reliability-based (pseudocost + strong branching); the node pool and OBBT run in
parallel (deterministic, ~2.3–2.6× wall-clock). Exposed as
`minimize_global(objective, *, constraints, lo, hi, …)` in Python (a symbolic
`Expr` + box) and `pounce --solver global` on `.nl` models, with frontier
memory estimation and a pre-solve warning.

### Added — Multi-backend interactive debugger (convex IPM + B&B tree)

The interactive debugger was generalized over a `DebugState` trait so one REPL
drives all three solvers. New backends: a **convex/conic** debugger
(`pounce_cblib --debug`, wired through the symmetric and non-symmetric HSDE
drivers) and an **interactive branch-and-bound tree debugger** that can `step`
through nodes and `into` a node's relaxation — handing off to the interior-point
REPL via a shared command queue (tree ↔ interior-point). This composes with the
0.4.0 debugger features below (quote-aware tokenization, `ask` provider presets,
`--debug-json` protocol, Ctrl-C escape hatch).

### Added — `pounce.curve_fit` (Python)

A `scipy.optimize.curve_fit`-style nonlinear fitter on top of the
Expand Down
43 changes: 43 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# pounce — release / publishing facts

pounce ships to **three** registries on each release. Two are automated by
GitHub Actions (tag-triggered); the **crates.io one is manual** and is the
easiest to forget — it is NOT triggered by pushing a tag or by creating a
GitHub Release.

## Surfaces (all must reach the same X.Y.Z)

1. **PyPI `pounce-solver`** — `.github/workflows/release-pounce.yml`, triggered
by pushing a `python-vX.Y.Z` tag. Builds wheels (incl. Windows) + sdist,
publishes to PyPI.
2. **PyPI `pyomo-pounce`** — `.github/workflows/release-pyomo-pounce.yml`,
triggered by a `pyomo-pounce-vX.Y.Z` tag.
3. **crates.io — 16 workspace crates** — **MANUAL**, via
`scripts/publish-crates.sh` (run locally). NO workflow does `cargo publish`.
Full procedure in `dev-notes/cargo-release.md`. The script publishes in
topological (dependency) order; resume a mid-batch failure with
`--start-from <crate>`. New-crate rate limits apply on first publish only.
Crates with `publish = false` (pounce-py, pounce-studio-*, iter-diff) are
intentionally excluded.

The CLI binary is also bundled inside the PyPI wheels, so an end user
`pip install pounce-solver` does not require the crates.io publish — but the
crates.io publish is still part of a complete release.

## GitHub Release

Created **by hand** (`gh release create vX.Y.Z --notes-file <file>`); no workflow
makes it. Body has historically been the matching `## [X.Y.Z]` section of
CHANGELOG.md. A git tag alone does NOT create a Release, and creating a Release
does NOT trigger any workflow (nothing has an `on: release` trigger).

## Checking what's published (don't get this wrong)

crates.io API needs a User-Agent or it silently looks unpublished:

curl -s -H "User-Agent: pounce-release-check (jkitchin@andrew.cmu.edu)" \
https://crates.io/api/v1/crates/<name> | python3 -c \
"import sys,json; c=json.load(sys.stdin).get('crate'); print(c['max_version'] if c else 'NOT PUBLISHED')"

Sanity-check against `serde` first; if serde reads NOT PUBLISHED your request is
being rejected, not the crate missing.
34 changes: 20 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ members = [
"crates/pounce-presolve",
"crates/pounce-l1penalty",
"crates/pounce-qp",
"crates/pounce-convex",
"crates/pounce-sensitivity",
"crates/pounce-solve-report",
"crates/pounce-observability",
Expand Down Expand Up @@ -41,6 +42,7 @@ default-members = [
"crates/pounce-presolve",
"crates/pounce-l1penalty",
"crates/pounce-qp",
"crates/pounce-convex",
"crates/pounce-sensitivity",
"crates/pounce-solve-report",
"crates/pounce-observability",
Expand Down Expand Up @@ -74,11 +76,17 @@ pounce-restoration = { path = "crates/pounce-restoration", version = "0.4.0" }
pounce-presolve = { path = "crates/pounce-presolve", version = "0.4.0" }
pounce-l1penalty = { path = "crates/pounce-l1penalty", version = "0.4.0" }
pounce-qp = { path = "crates/pounce-qp", version = "0.4.0" }
pounce-convex = { path = "crates/pounce-convex", version = "0.4.0" }
pounce-sensitivity = { path = "crates/pounce-sensitivity", version = "0.4.0" }
pounce-solve-report = { path = "crates/pounce-solve-report", version = "0.4.0" }
pounce-studio-core = { path = "crates/pounce-studio-core", version = "0.4.0" }
pounce-observability = { path = "crates/pounce-observability", version = "0.4.0" }
feral = "0.10.0"
# feral HEAD past the 0.10.0 release checkpoint: issue #80 — MC64/scaling
# perf work (Hungarian-heap reuse across columns, localized dense-column
# cost, ldlt_compress profiling). Not yet on crates.io; pinned by rev for
# reproducibility. NOTE: this git pin blocks the crates.io publish of the
# pounce crates until feral cuts a release carrying these commits.
feral = { git = "https://github.com/jkitchin/feral.git", rev = "11fb4b98d7caac0383c53b6a969e27632efcef77" }
# Dense linear algebra for the debugger's numerical rank diagnosis
# (SVD of the active-constraint Jacobian). Pure-Rust, MIT; we pull only
# the dense `std` core — no rayon/sparse/rand/npy.
Expand Down
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ endif

.PHONY: all build debug test check clippy fmt fmt-check doc book install uninstall clean help \
install-mcp uninstall-mcp install-skill uninstall-skill \
python-ext python-test \
benchmark benchmark-rerun benchmark-report benchmark-gams

all: build
Expand Down Expand Up @@ -118,6 +119,20 @@ clean:
help:
@sed -n 's/^# \{0,1\}//p' Makefile | sed -n '1,45p'

# ---- Python extension + tests -------------------------------------------
# Rebuild the native extension in place, then run the Python test suite.
# This is the safe way to run pytest: a stale in-place `_pounce*.so` (left
# by an earlier `maturin develop`) silently shadows the current binding and
# makes the suite fail with confusing errors. `python-ext` rebuilds it, and
# `python/tests/conftest.py` additionally guards against running pytest
# against a stale artifact. Requires `maturin` and the test extras in the
# active environment (`pip install -e 'python[dev]'`).
python-ext:
cd python && maturin develop

python-test: python-ext
cd python && python -m pytest tests -q

# ---- Benchmarks ----------------------------------------------------------
# Single source of truth: benchmarks/Makefile. These shims forward
# everything so users can drive runs from the repo root.
Expand Down
Loading