Variable aggregation (reduced-space presolve) for nonlinear optimization —
a standalone plugin for discopt that
installs into the discopt.aggregation namespace.
It implements Naik, Biegler, Bent & Parker, Variable aggregation for nonlinear optimization problems (arXiv:2502.13869): substitute variables defined by equality constraints into the rest of a model, eliminating both a variable and a constraint to yield a smaller reduced-space formulation, then recover the eliminated variables from the reduced solution.
import discopt.modeling as dm
from discopt.aggregation import aggregate, solve
m = dm.Model("example")
x = m.continuous("x", lb=-5, ub=5)
y = m.continuous("y", lb=-5, ub=5)
m.subject_to(y - (x + 1) == 0.0) # y is defined by x
m.minimize(x**2 + y**2)
res = solve(m, method="d2") # aggregate → solve → recover
print(res.status, res.objective) # optimal 0.5
print(res.x) # {'x': -0.5, 'y': 0.5} (y recovered)
Installation
pip install discopt-aggregation # from PyPI
Or from a checkout for development:
uv pip install -e ".[dev]" # or: pip install -e ".[dev]"
Requires discopt >= 0.6. The plugin is a discopt.aggregation namespace
package: it merges into the installed discopt package at import time. This
needs discopt/__init__.py to extend its search path, i.e. to contain:
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
discopt 0.6+ includes this line. (If you use an older discopt, add it once.)
Claude skill
This package ships a Claude Code skill
that documents how to use aggregation (method choice, the structure-preserving vs
Hessian-cost tradeoff, every option, recovery, diagnostics, the study harnesses).
Claude Code does not auto-discover skills from installed Python packages, so
after pip install run the bundled installer once to copy it into a
.claude/skills/ directory Claude scans:
discopt-aggregation-skill install # -> ./.claude/skills/ (this project)
discopt-aggregation-skill install --user # -> ~/.claude/skills/ (every project)
discopt-aggregation-skill path # where the packaged skill lives
Working inside this repo, the skill is already at .claude/skills/ (no install
needed). The repo is also a Claude Code plugin (.claude-plugin/plugin.json
skills/), so it can be loaded withclaude --plugin-dir <repo>or published to a marketplace. The single source of truth issrc/discopt/aggregation/skill/; the two.claude/skills/skillsentries are symlinks to it.
Methods
method= selects the strategy, most conservative to most aggressive:
| key | name | eliminates | paper |
|---|---|---|---|
ld1 |
fixed-variable (degree-1) | y = a |
Alg 3 |
ecd2 |
equal-coefficient degree-2 | y = x + a |
Alg 6 |
ld2 |
linear degree-2 | y = a*x + b |
Alg 5 |
d2 |
degree-2 (default) | y = f(x) (≤ 2 variables) |
Alg 4 |
gr |
greedy | y = f(w, x, …) (y linear) |
Alg 1 |
lm |
linear-matching | y = f(w, x, …) (y linear) |
Alg 2 |
ld1/ecd2/ld2/d2 are structure-preserving; gr/lm are
approximate-maximum. The paper recommends d2; it is the default.
Integer aggregation (opt-in, beyond the paper)
By default only scalar continuous variables are eliminated. Pass
aggregate(..., integer_aggregation=True) (or solve(...)) to also eliminate a
scalar integer/binary variable — but only when its integrality is implied,
the standard MIP-presolve condition (Achterberg et al. 2020): the defining
equality is fully affine, the pivot coefficient is ±1, every other variable in
it is integer/binary, and every other coefficient and the constant are integral
(e.g. an all-integer stock balance s[t] = s[t-1] + q[t] - d[t]). Recovery
returns exactly integral values; dual recovery reports available=False over
eliminated integers. The flag is default-off and never eliminates a variable it
cannot prove integral (fractional data, non-unit pivot, or a continuous
participant all abstain), so the default behaviour is unchanged.
Features
- Six strategies, each producing a strictly lower-triangular aggregation set (Lemma 1) with explicit, solve-free post-solve recovery.
- Numerical safeguards: a relative pivot tolerance (
pivot_tol) against ill-conditioned substitutions, and an optional fill-in cap (max_fill). - Redundant-bound pruning (
prune_bounds, on by default): box→inequality constraints proven redundant by interval propagation are omitted. - Array support (
scalarize, on by default): fully-indexed array variables are scalarized so aggregation reaches discretized (DAE) models, then arrays are reassembled on recovery. - Structural metrics (
compare_methods) reproducing the paper's Table 4, including the exact AD Hessian nonzero count.
Examples
python -m discopt.aggregation.examples runs commented, executable walkthroughs
of every method, the recursive pipeline, end-to-end solve + recovery, the
structural comparison, the safeguards, array scalarization, and a
performance/scaling demo.
Tests
uv pip install -e ".[dev]"
python -m pytest tests/ -q # the full suite
Layout
src/discopt/aggregation/ # the plugin (PEP 420 namespace subpackage)
core: matching · incidence · strategies · transform · recovery · api
walking: exprwalk (single DAG-walk authority for all analyses)
structure: scalarize · metrics · diagnostics (Dulmage–Mendelsohn) · ordering
numerics: guards · hessian_cost · duals · derivatives · implicit · tearing · schur
study: testproblems · benchmark · sweep · profiling · presolve · examples
tests/ # test_aggregation_*.py
design/ # follow-up roadmap · parity plan · references.bib
References
Primary:
Sakshi Naik, Lorenz Biegler, Russell Bent, Robert Parker. Variable aggregation for nonlinear optimization problems. arXiv:2502.13869 (2025). https://arxiv.org/abs/2502.13869
The full set of related work — reduced-space / implicit elimination
(Parker et al. 2022), block-triangular KKT (Parker, Garcia & Bent 2026), exact
tearing / minimum feedback set (Baharev et al. 2021), Dulmage–Mendelsohn
debugging (Parker et al. 2023), and the MIP-presolve line (Achterberg et al.
2020) — is collected as verified BibTeX in
design/references.bib, and cited from the design docs
under design/.
Contributing
See CONTRIBUTING.md for dev setup, the lint/type/test gate, and the project conventions (keep discopt/pounce/feral untouched, new behavior opt-in/default-off, a worked example per feature, hash-seed-robust tests).
License
Eclipse Public License 2.0 (EPL-2.0), matching discopt.
Metadata
Release files for discopt-aggregation 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| discopt_aggregation-0.4.1.tar.gz | 198.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| discopt_aggregation-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 346.2 kB
Release files / discopt_aggregation-0.4.1.tar.gz
| Download URL | discopt_aggregation-0.4.1.tar.gz |
|---|---|
| Size | 198.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c10ffe4c273692180f70e0466fb25345d9d2c808020ee63630e06166997f5575
|
|
BLAKE2b-256 checksum How to use checksums |
e4fd61ba41b708e69aaf0f53f5f19c6bf92ee569744b41eac58fd9d2ffc14bd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 12, 2026.
Transparency logRelease files / discopt_aggregation-0.4.1-py3-none-any.whl
| Download URL | discopt_aggregation-0.4.1-py3-none-any.whl |
|---|---|
| Size | 147.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ae98f9fbd3c6137a7be5a3113beb4a66d5aba14f16d7341468ab6f9fb6bdf075
|
|
BLAKE2b-256 checksum How to use checksums |
de7fda6de7160fd6c727675840bbd685bd185e5537f3d73c03d56daf3416bc44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 12, 2026.
Transparency logSecurity for discopt-aggregation 0.4.1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for discopt_aggregation-0.4.1.tar.gz
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 Jul 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.