ipax 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. ipax-0.1.1/.gitignore +52 -0
  2. ipax-0.1.1/AGENTS.md +268 -0
  3. ipax-0.1.1/CLAUDE.md +27 -0
  4. ipax-0.1.1/CONTRIBUTING.md +91 -0
  5. ipax-0.1.1/LICENSE +201 -0
  6. ipax-0.1.1/NOTICE +28 -0
  7. ipax-0.1.1/PKG-INFO +324 -0
  8. ipax-0.1.1/README.md +236 -0
  9. ipax-0.1.1/benchmarks/README.md +77 -0
  10. ipax-0.1.1/benchmarks/asv.conf.json +21 -0
  11. ipax-0.1.1/benchmarks/baselines/__init__.py +359 -0
  12. ipax-0.1.1/benchmarks/corpus/__init__.py +156 -0
  13. ipax-0.1.1/benchmarks/corpus/external.py +61 -0
  14. ipax-0.1.1/benchmarks/generators/__init__.py +44 -0
  15. ipax-0.1.1/benchmarks/harness/__init__.py +484 -0
  16. ipax-0.1.1/benchmarks/harness/plots.py +95 -0
  17. ipax-0.1.1/benchmarks/reports/.gitkeep +1 -0
  18. ipax-0.1.1/benchmarks/runners/bench_solve.py +45 -0
  19. ipax-0.1.1/benchmarks/runners/crosscheck.py +95 -0
  20. ipax-0.1.1/benchmarks/runners/micro/bench_kernels.py +18 -0
  21. ipax-0.1.1/benchmarks/runners/qc.py +135 -0
  22. ipax-0.1.1/benchmarks/runners/scaling.py +119 -0
  23. ipax-0.1.1/conftest.py +49 -0
  24. ipax-0.1.1/docs/architecture.md +55 -0
  25. ipax-0.1.1/docs/concepts/algorithm.md +65 -0
  26. ipax-0.1.1/docs/concepts/linalg.md +42 -0
  27. ipax-0.1.1/docs/concepts/problem.md +32 -0
  28. ipax-0.1.1/docs/contributing.md +25 -0
  29. ipax-0.1.1/docs/getting-started.md +58 -0
  30. ipax-0.1.1/docs/guide/backends.md +95 -0
  31. ipax-0.1.1/docs/guide/diagnostics.md +117 -0
  32. ipax-0.1.1/docs/guide/options.md +173 -0
  33. ipax-0.1.1/docs/guide/problems.md +200 -0
  34. ipax-0.1.1/docs/guide/results.md +127 -0
  35. ipax-0.1.1/docs/index.md +25 -0
  36. ipax-0.1.1/docs/reference.md +101 -0
  37. ipax-0.1.1/examples/README.md +40 -0
  38. ipax-0.1.1/examples/autodiff.py +107 -0
  39. ipax-0.1.1/examples/bound_and_inequality.py +57 -0
  40. ipax-0.1.1/examples/equality_constrained.py +55 -0
  41. ipax-0.1.1/examples/lbfgs_finite_diff.py +55 -0
  42. ipax-0.1.1/examples/matrix_free_krylov.py +76 -0
  43. ipax-0.1.1/examples/nonconvex_hs.py +42 -0
  44. ipax-0.1.1/examples/radiotherapy_sparse.py +127 -0
  45. ipax-0.1.1/examples/unconstrained_quadratic.py +32 -0
  46. ipax-0.1.1/ipax/__init__.py +66 -0
  47. ipax-0.1.1/ipax/_logging.py +244 -0
  48. ipax-0.1.1/ipax/backend/__init__.py +21 -0
  49. ipax-0.1.1/ipax/backend/namespace.py +139 -0
  50. ipax-0.1.1/ipax/backend/operators.py +429 -0
  51. ipax-0.1.1/ipax/backend/sparse/__init__.py +83 -0
  52. ipax-0.1.1/ipax/backend/sparse/_routing.py +146 -0
  53. ipax-0.1.1/ipax/backend/sparse/cupy.py +719 -0
  54. ipax-0.1.1/ipax/backend/sparse/jax.py +39 -0
  55. ipax-0.1.1/ipax/backend/sparse/numpy_scipy.py +529 -0
  56. ipax-0.1.1/ipax/backend/sparse/torch.py +39 -0
  57. ipax-0.1.1/ipax/ipm/__init__.py +21 -0
  58. ipax-0.1.1/ipax/ipm/barrier.py +54 -0
  59. ipax-0.1.1/ipax/ipm/breedveld_ls.py +95 -0
  60. ipax-0.1.1/ipax/ipm/corrections.py +338 -0
  61. ipax-0.1.1/ipax/ipm/driver.py +1404 -0
  62. ipax-0.1.1/ipax/ipm/filter_ls.py +158 -0
  63. ipax-0.1.1/ipax/ipm/hessian.py +218 -0
  64. ipax-0.1.1/ipax/ipm/init.py +178 -0
  65. ipax-0.1.1/ipax/ipm/kkt.py +523 -0
  66. ipax-0.1.1/ipax/ipm/restoration.py +146 -0
  67. ipax-0.1.1/ipax/ipm/step.py +111 -0
  68. ipax-0.1.1/ipax/ipm/termination.py +186 -0
  69. ipax-0.1.1/ipax/linalg/__init__.py +21 -0
  70. ipax-0.1.1/ipax/linalg/dense.py +101 -0
  71. ipax-0.1.1/ipax/linalg/krylov.py +481 -0
  72. ipax-0.1.1/ipax/linalg/regularize.py +68 -0
  73. ipax-0.1.1/ipax/linalg/solver.py +91 -0
  74. ipax-0.1.1/ipax/linalg/sparse.py +126 -0
  75. ipax-0.1.1/ipax/options.py +390 -0
  76. ipax-0.1.1/ipax/problem/__init__.py +22 -0
  77. ipax-0.1.1/ipax/problem/autodiff/__init__.py +60 -0
  78. ipax-0.1.1/ipax/problem/autodiff/jax.py +50 -0
  79. ipax-0.1.1/ipax/problem/autodiff/torch.py +62 -0
  80. ipax-0.1.1/ipax/problem/base.py +123 -0
  81. ipax-0.1.1/ipax/problem/derivatives.py +254 -0
  82. ipax-0.1.1/ipax/problem/finitediff.py +95 -0
  83. ipax-0.1.1/ipax/problem/function.py +326 -0
  84. ipax-0.1.1/ipax/problem/scaling.py +275 -0
  85. ipax-0.1.1/ipax/py.typed +0 -0
  86. ipax-0.1.1/ipax/result.py +218 -0
  87. ipax-0.1.1/ipax/solve.py +387 -0
  88. ipax-0.1.1/ipax/testing/__init__.py +33 -0
  89. ipax-0.1.1/ipax/testing/backends.py +60 -0
  90. ipax-0.1.1/ipax/testing/problems.py +675 -0
  91. ipax-0.1.1/ipax/typing.py +37 -0
  92. ipax-0.1.1/pyproject.toml +230 -0
  93. ipax-0.1.1/tests/__init__.py +1 -0
  94. ipax-0.1.1/tests/_helpers.py +84 -0
  95. ipax-0.1.1/tests/backends/test_cross_backend_conformance.py +76 -0
  96. ipax-0.1.1/tests/conftest.py +46 -0
  97. ipax-0.1.1/tests/contracts/__init__.py +6 -0
  98. ipax-0.1.1/tests/contracts/test_builtin_operator_contracts.py +93 -0
  99. ipax-0.1.1/tests/contracts/test_builtin_problem_contracts.py +60 -0
  100. ipax-0.1.1/tests/contracts/test_builtin_solver_contracts.py +39 -0
  101. ipax-0.1.1/tests/contracts/test_builtin_sparse_contracts.py +22 -0
  102. ipax-0.1.1/tests/contracts/test_operator_contract.py +65 -0
  103. ipax-0.1.1/tests/contracts/test_problem_contract.py +64 -0
  104. ipax-0.1.1/tests/contracts/test_solver_contract.py +37 -0
  105. ipax-0.1.1/tests/contracts/test_sparse_contract.py +57 -0
  106. ipax-0.1.1/tests/integration/test_benchmark_crosscheck.py +79 -0
  107. ipax-0.1.1/tests/integration/test_benchmark_harness.py +88 -0
  108. ipax-0.1.1/tests/integration/test_benchmark_phase4.py +92 -0
  109. ipax-0.1.1/tests/integration/test_benchmark_scaling.py +45 -0
  110. ipax-0.1.1/tests/integration/test_callback_and_logging.py +269 -0
  111. ipax-0.1.1/tests/integration/test_corrections_solve.py +107 -0
  112. ipax-0.1.1/tests/integration/test_dense_nonconvex.py +56 -0
  113. ipax-0.1.1/tests/integration/test_hock_schittkowski.py +97 -0
  114. ipax-0.1.1/tests/integration/test_known_optima.py +196 -0
  115. ipax-0.1.1/tests/integration/test_krylov_solve.py +340 -0
  116. ipax-0.1.1/tests/integration/test_scaling_solve.py +154 -0
  117. ipax-0.1.1/tests/integration/test_sparse_solve.py +261 -0
  118. ipax-0.1.1/tests/integration/test_warm_start_solve.py +84 -0
  119. ipax-0.1.1/tests/property/test_derivative_checks.py +94 -0
  120. ipax-0.1.1/tests/regression/__init__.py +1 -0
  121. ipax-0.1.1/tests/regression/test_doctest_collection_adapters.py +105 -0
  122. ipax-0.1.1/tests/test_purity_gate.py +24 -0
  123. ipax-0.1.1/tests/unit/test_autodiff.py +96 -0
  124. ipax-0.1.1/tests/unit/test_backend_namespace.py +75 -0
  125. ipax-0.1.1/tests/unit/test_barrier.py +38 -0
  126. ipax-0.1.1/tests/unit/test_breedveld.py +70 -0
  127. ipax-0.1.1/tests/unit/test_corrections.py +200 -0
  128. ipax-0.1.1/tests/unit/test_cupy_sparse_adapter.py +407 -0
  129. ipax-0.1.1/tests/unit/test_dense_solver.py +63 -0
  130. ipax-0.1.1/tests/unit/test_filter_ls.py +54 -0
  131. ipax-0.1.1/tests/unit/test_hessian_lbfgs.py +136 -0
  132. ipax-0.1.1/tests/unit/test_inertia_correction.py +59 -0
  133. ipax-0.1.1/tests/unit/test_kkt.py +162 -0
  134. ipax-0.1.1/tests/unit/test_krylov.py +442 -0
  135. ipax-0.1.1/tests/unit/test_logging.py +106 -0
  136. ipax-0.1.1/tests/unit/test_numpy_scipy_feral.py +234 -0
  137. ipax-0.1.1/tests/unit/test_operator_coo.py +231 -0
  138. ipax-0.1.1/tests/unit/test_operator_diagonal.py +90 -0
  139. ipax-0.1.1/tests/unit/test_options.py +131 -0
  140. ipax-0.1.1/tests/unit/test_problem_derivatives.py +107 -0
  141. ipax-0.1.1/tests/unit/test_regularize.py +41 -0
  142. ipax-0.1.1/tests/unit/test_restoration.py +151 -0
  143. ipax-0.1.1/tests/unit/test_result.py +97 -0
  144. ipax-0.1.1/tests/unit/test_scaling.py +222 -0
  145. ipax-0.1.1/tests/unit/test_solver_selection.py +84 -0
  146. ipax-0.1.1/tests/unit/test_sparse_device_routing.py +106 -0
  147. ipax-0.1.1/tests/unit/test_sparse_dispatch.py +19 -0
  148. ipax-0.1.1/tests/unit/test_termination.py +159 -0
  149. ipax-0.1.1/tests/unit/test_warm_start.py +80 -0
ipax-0.1.1/.gitignore ADDED
@@ -0,0 +1,52 @@
1
+ # Byte-compiled / optimized
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ wheels/
12
+
13
+ # Environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+ .python-version
18
+
19
+ # Tooling caches
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+ .pytest_cache/
23
+ .hypothesis/
24
+ .coverage
25
+ .coverage.*
26
+ htmlcov/
27
+ coverage.xml
28
+
29
+ # Benchmarks (asv)
30
+ benchmarks/.asv/
31
+ benchmarks/results/
32
+ benchmarks/html/
33
+ *.prof
34
+
35
+ # Generated benchmark reports (regenerated by the `qc` job; kept as CI artifacts).
36
+ # The directory itself is tracked via benchmarks/reports/.gitkeep.
37
+ benchmarks/reports/*
38
+ !benchmarks/reports/.gitkeep
39
+
40
+ # Docs build
41
+ site/
42
+
43
+ # Editors / OS
44
+ .vscode/
45
+ .idea/
46
+ *.swp
47
+ .DS_Store
48
+ Thumbs.db
49
+
50
+ # Datasets (TROTS etc. are downloaded, never committed)
51
+ data/
52
+ *.mat
ipax-0.1.1/AGENTS.md ADDED
@@ -0,0 +1,268 @@
1
+ # AGENTS.md — `ipax`
2
+
3
+ Operational guide for any agent or contributor working in this repository. This is
4
+ the **canonical** instructions file; `CLAUDE.md` imports it.
5
+
6
+ `ipax` is a pure-Python, **Python Array API–conformant** primal–dual interior-point
7
+ solver for large-scale nonlinear constrained optimization, tuned for radiotherapy
8
+ (RT) treatment-planning scale. The math, the KKT reduction, and the design rules
9
+ that govern changes live in this file; cite the source papers (see References) for the
10
+ algorithmic details.
11
+
12
+ ---
13
+
14
+ ## What this project is
15
+
16
+ - General NLP: `min f(x)` s.t. `c(x)=0`, `g(x)≤0`, `x_L ≤ x ≤ x_U`, solved by a
17
+ log-barrier interior-point method with Lagrange multipliers.
18
+ - **Scale target:** `1e3`–`1e5` variables; dense *or* sparse Jacobians/Hessians.
19
+ - **Default Hessian:** L-BFGS (compact, Powell-damped) of the Lagrangian.
20
+ - **Globalization:** filter line-search (IPOPT, default); Breedveld step controller
21
+ as an alternative mode.
22
+ - **Backends:** NumPy + PyTorch in CI; CuPy/JAX (incl. GPU) supported via the same
23
+ code path. Inspirations: IPOPT (Wächter & Biegler 2006), Breedveld et al. (2017).
24
+
25
+ ---
26
+
27
+ ## Non-negotiable invariants (read first)
28
+
29
+ 1. **No concrete array library in the core.** Never `import numpy`/`torch`/`cupy`/
30
+ `jax` inside `ipax/` except in `ipax/backend/sparse/` and other explicitly
31
+ labeled adapters. Get the namespace from the input arrays:
32
+ ```python
33
+ from ipax.backend.namespace import array_namespace
34
+ xp = array_namespace(x) # then use xp.*, xp.linalg.*
35
+ ```
36
+ 2. **Stay inside the standard.** Use only the Array API main namespace + the optional
37
+ `linalg` extension. The extension provides `cholesky, eigh/eigvalsh, qr, svd,
38
+ solve, inv, pinv, slogdet, matrix_norm, vector_norm, …`. It does **NOT** provide
39
+ LDLᵀ-with-inertia, triangular solve, `lstsq`, or any sparse type — do not assume
40
+ them. Gap-fillers go in `backend/` with a comment naming the missing primitive.
41
+ 3. **Linear algebra is injected, never hard-wired.** All Jacobians/Hessians are
42
+ `LinearOperator`s; all KKT solves go through the `LinearSolver` protocol. Adding a
43
+ solve strategy must not touch `ipm/driver.py`.
44
+ 4. **Sparsity is an adapter concern.** The core emits structure as Array-API
45
+ index/value vectors; per-backend wrappers in `backend/sparse/` build the actual
46
+ sparse matrix and factor it. The IPM never sees a backend sparse object.
47
+ 5. **No `localStorage`-style global state.** Solver state lives in explicit objects;
48
+ no module-level mutable singletons.
49
+
50
+ If a change appears to require breaking 1–4, stop and discuss in the PR description
51
+ instead of working around it.
52
+
53
+ ---
54
+
55
+ ## Architecture map
56
+
57
+ ```
58
+ ipax/
59
+ typing.py options.py result.py solve.py
60
+ problem/ base.py (Problem ABC), function.py, derivatives.py,
61
+ finitediff.py, autodiff/{jax,torch}.py
62
+ backend/ namespace.py, operators.py,
63
+ sparse/{numpy_scipy,cupy,torch,jax,_routing}.py
64
+ linalg/ solver.py, dense.py, krylov.py, regularize.py
65
+ ipm/ barrier, kkt, step, filter_ls, restoration, breedveld_ls,
66
+ hessian, init, driver
67
+ testing/ problems.py (analytic oracles), backends.py
68
+ tests/ contracts/, unit/, property/, integration/, backends/, regression/
69
+ examples/ minimal runnable examples for the current implementation surface
70
+ benchmarks/ generators/, corpus/, harness, runners, reports
71
+ ```
72
+
73
+ - **`Problem`** (`problem/base.py`): user-facing ABC. Required: `n_vars`,
74
+ `objective`. Optional (resolved by `derivatives.py`): `gradient`, nonlinear
75
+ `eq/ineq_constraints` + Jacobians, and `lagrangian_hessian`. **Linear constraints
76
+ are declared separately** (`linear_eq`/`linear_ineq`, constant data) from nonlinear
77
+ ones — constant Jacobian, no Hessian term, assembled once. Derivative precedence:
78
+ analytic → autodiff → finite-diff (grads/Jacobians); analytic → autodiff-HVP →
79
+ **L-BFGS** (Hessian). Never form a dense Hessian at scale.
80
+ - **`LinearOperator`** (`backend/operators.py`): `matvec`/`rmatvec`/`matmat`;
81
+ subclasses `Dense, Diagonal, LowRank, LBFGSOperator, MatrixFreeJacobian,
82
+ SparseOperator, Composite`. All `Problem` Jacobians/Hessians normalize to these.
83
+ - **`LinearSolver`** (`linalg/solver.py`): `DenseSolver` (Cholesky/solve),
84
+ `KrylovSolver` (CG/MINRES/GMRES, matrix-free — default at scale), and
85
+ `SparseDirectSolver` (per-backend). Auto-selected by size/density/capabilities.
86
+
87
+ ---
88
+
89
+ ## Math conventions (match the code to these)
90
+
91
+ - Standard form uses slacks: `g(x)+s=0, s≥0`. Multipliers: `y` (equalities),
92
+ `λ` (inequalities/slacks), `z_L,z_U` (bounds). `diag(v)=V`; `e`=all-ones.
93
+ - Two KKT solve routes: the **condensed normal-equations** route (Array-API-native,
94
+ default for dense & matrix-free; PD via Powell-damped L-BFGS + primal–dual
95
+ regularization, so **no inertia oracle required**) and the **indefinite augmented**
96
+ route (sparse-direct backends with LDLᵀ inertia only).
97
+ - Regularization: Friedlander–Orban primal–dual (`δ_w` on (1,1), `−δ_c` on (2,2));
98
+ on Cholesky failure escalate `δ_w` from `1e-6`, doubling, per Breedveld.
99
+ - Convergence: scaled KKT ∞-norm ≤ `ε_tol` (default `1e-8`), IPOPT scaling
100
+ `s_d, s_c`.
101
+
102
+ Keep symbol names in code aligned with the math conventions above (`W, Sigma_x,
103
+ Sigma_s, N, mu, tau, alpha, theta, phi`).
104
+
105
+ ---
106
+
107
+ ## Coding standards
108
+
109
+ - Python ≥ 3.10, full type hints, `from __future__ import annotations`.
110
+ - Public API documented with docstrings; numerical choices cite the paper/eq.
111
+ - Config via frozen dataclasses in `options.py`; no magic numbers in the loop body.
112
+ - Formatting/lint: `ruff` (format + lint) and `mypy` clean before commit.
113
+ - Pure functions where practical; no hidden global state; explicit `xp`/device
114
+ threading.
115
+ - Floats are `float64` by default; never hard-code a dtype — read it from inputs.
116
+
117
+ ---
118
+
119
+ ## Testing & verification
120
+
121
+ - **Test-driven.** For every change write the failing tests first, then implement
122
+ to green. New protocol implementations must pass the shared **contract test
123
+ batteries** (`tests/contracts/`) for `Problem`, `LinearOperator`, `LinearSolver`,
124
+ `SparseDirectSolver` — this is what keeps "pluggable" honest.
125
+ - **Always multi-backend.** Tests parametrize over the namespace fixture
126
+ (`tests/conftest.py`): NumPy + PyTorch minimum; CuPy/JAX/GPU when available.
127
+ `array-api-strict` is the **purity gate** (raises on out-of-standard calls).
128
+ - **Purity check** in CI: fail if banned imports appear outside allowed adapters.
129
+ - **Oracles:** verify KKT conditions at the solution; closed-form QP/LP optima,
130
+ Hock–Schittkowski set, cross-check vs `scipy.optimize` and `cyipopt` when installed.
131
+ - **Derivative harness:** FD vs analytic vs autodiff grad/Jacobian + Hessian-vector
132
+ checks (also a public utility usable on any user `Problem`).
133
+ - **Benchmarks are separate** (`benchmarks/`, not `tests/`): synthetic RT-like
134
+ generators (`1e3`–`1e5` vars, 5–50% density) + standard sets, tracked over commits
135
+ with `asv`; run nightly, not per-PR.
136
+ - A non-trivial change is not done until the relevant tests pass on **both** NumPy
137
+ and PyTorch. Add a regression test with every bug fix.
138
+
139
+ ### Commands
140
+
141
+ `scripts/check.py` is the **single verification entrypoint** — it runs the same
142
+ gates CI runs (`format → lint → types → purity → test`), so "is my change done?"
143
+ is one command instead of reconstructing flags from `ci.yml`, the pre-commit
144
+ config and this file. Prefer it over invoking the tools by hand.
145
+
146
+ ```bash
147
+ python scripts/check.py # all gates (multi-backend tests included)
148
+ python scripts/check.py --fast # skip the slow test gate (lint/types/purity only)
149
+ python scripts/check.py lint types # only the named gates
150
+ python scripts/check.py --list # show the gates and their commands
151
+
152
+ # Equivalent raw invocations (what the gates wrap):
153
+ pytest -q # full test suite
154
+ IPAX_BACKENDS=numpy,torch,array_api_strict pytest -q
155
+ ruff check . && ruff format --check . && mypy ipax
156
+ python scripts/check_purity.py # import-purity gate (invariants #1/#4)
157
+ asv run # tracked perf benchmarks (benchmarks/)
158
+ ```
159
+
160
+ Enforcement layers (same `check_purity.py` logic, different triggers): the
161
+ **pre-commit** hook runs it at commit; a **Claude Code PostToolUse hook**
162
+ (`scripts/hooks/purity_guard.py`, wired in `.claude/settings.json`) runs it the
163
+ moment an edit touches `ipax/`, so a purity violation is caught immediately
164
+ rather than at commit. Other agents (e.g. aider's `lint-cmd`) can point at the
165
+ same script.
166
+
167
+ ### Agent tooling
168
+
169
+ Claude Code config lives in `.claude/` and is checked in:
170
+
171
+ - **`invariant-auditor`** subagent (`.claude/agents/invariant-auditor.md`) — a
172
+ read-only reviewer that audits a diff against the five invariants, the math/citation
173
+ conventions, the testing discipline, and the GPU/device-efficiency rules (the
174
+ *semantic* checks the purity hook and contract tests cannot see). Invoke it
175
+ explicitly before a commit or PR — e.g. *"use the invariant-auditor to review my
176
+ changes"*.
177
+ - **`/verify`** (`.claude/commands/verify.md`) — run `scripts/check.py` and summarize.
178
+ - **`/tdd`** (`.claude/commands/tdd.md`) — drive a change through the mandated
179
+ red→green→verify→regression loop, multi-backend and invariant-aware.
180
+
181
+ The auditor's rubric is the static half of GPU performance work; the measured half is
182
+ a future `benchmarks/runners/device_efficiency.py` (sync/kernel profiling on real
183
+ hardware), deferred until GPU CI exists.
184
+
185
+ ---
186
+
187
+ ## Scope guardrails
188
+
189
+ **In:** equality + inequality + bound constraints; L-BFGS + exact Hessian; dense,
190
+ matrix-free, and sparse-direct solver routes; filter line-search + restoration;
191
+ optional Mehrotra–Gondzio higher-order corrections; multi-backend.
192
+
193
+ **Out (do not add without discussion):** Breedveld's dose-matrix `N=AᵀDA+Q+T`
194
+ condensation, graph permutation/tiling, mixed-precision tiled BLAS, chained
195
+ dose-influence products; hard-coded RT cost-functions (LTCP/gEUD/dose-volume);
196
+ multi-criteria / Pareto / beam-angle layers. Keep dimensionality & sparsity in
197
+ mind, but the RT-specific kernels belong in a separate downstream layer.
198
+
199
+ ---
200
+
201
+ ## Direction
202
+
203
+ `ipax` is **beta**. The solver surface above is complete; near-term work
204
+ is **maintenance and measurement**, not new features. The project is also actively
205
+ **looking for adopters** — early real-world use on the `Problem`/`solve` API is what
206
+ drives the road to `1.0.0`.
207
+
208
+ ### Road to 1.0.0
209
+
210
+ `1.0.0` is the point where the public API carries a semver stability promise. It is
211
+ gated on these exit criteria (the day-to-day work below is *how* we get there, not a
212
+ new feature list):
213
+
214
+ 1. **API stability.** The public surface — `solve`, `Problem`, `Options`, `Result`,
215
+ and the `LinearOperator`/`LinearSolver` protocols — holds stable across the `0.x`
216
+ series, with any deprecations carried for at least one minor release.
217
+ 2. **Published documentation.** The MkDocs site (`docs/`: API reference + guides) is
218
+ built and published.
219
+ 3. **GPU & sparse-direct verification.** The CuPy/cuDSS and Torch/JAX-CUDA sparse
220
+ routes and the MUMPS inertia path are exercised on real hardware in CI, or
221
+ documented as provisional until GPU CI exists.
222
+ 4. **Adopter validation.** At least one external workload runs on `ipax`, with the
223
+ feedback folded back into the API.
224
+ 5. **Tracked baselines.** `asv` performance baselines and the QC accuracy sweep are
225
+ tracked across releases, with cross-checks vs IPOPT/SciPy/OSQP green.
226
+
227
+ Near-term work is **maintenance and measurement**, not new features:
228
+
229
+ - **Bug-fixing.** Tighten correctness on hard nonconvex/ill-conditioned problems;
230
+ every fix ships with a regression test (`tests/regression/`).
231
+ - **Performance.** Drive the `benchmarks/` scaling and micro suites (`asv`), reduce
232
+ per-iteration cost and memory across the dense/matrix-free/sparse routes, and
233
+ profile device efficiency once GPU CI exists.
234
+ - **Accuracy benchmarking.** Grow the QC sweep and reference cross-checks
235
+ (vs IPOPT/SciPy/OSQP), track scaled-KKT accuracy and robustness across the corpus
236
+ and backends over time.
237
+
238
+ New algorithmic features beyond the current scope are out until discussed.
239
+
240
+ ---
241
+
242
+ ## Documentation
243
+
244
+ Documentation lives in the folder docs and uses mkdocs.
245
+
246
+ Minimal runnable examples live in `examples/`. Keep them short, feature-scoped,
247
+ and runnable from the repository root with the local virtual environment. Examples
248
+ may import a concrete backend such as NumPy at the application edge; the `ipax/`
249
+ core must remain backend-agnostic.
250
+
251
+ ---
252
+
253
+ ## References
254
+
255
+ - Wächter, A. & Biegler, L. T. (2006). "On the implementation of an interior-point
256
+ filter line-search algorithm for large-scale nonlinear programming."
257
+ *Mathematical Programming* 106(1), 25–57. <https://doi.org/10.1007/s10107-004-0559-y>
258
+ - Breedveld, S., van den Berg, B. & Heijmen, B. (2017). "An interior-point
259
+ implementation developed and tuned for radiation therapy treatment planning."
260
+ *Computational Optimization and Applications* 68(2), 209–242.
261
+ <https://doi.org/10.1007/s10589-017-9919-4>. TROTS dataset <http://www.trots.eu>.
262
+ - Array API `linalg` extension:
263
+ <https://data-apis.org/array-api/latest/extensions/linear_algebra_functions.html>
264
+ - `array-api-compat` / `array-api-extra`:
265
+ <https://data-apis.org/array-api-compat/> · <https://data-apis.org/array-api-extra/>
266
+ - Friedlander, M. P. & Orban, D. (2012). "A primal–dual regularized interior-point
267
+ method for convex quadratic programs." *Mathematical Programming Computation* 4(1),
268
+ 71–107. <https://doi.org/10.1007/s12532-012-0035-2>
ipax-0.1.1/CLAUDE.md ADDED
@@ -0,0 +1,27 @@
1
+ # CLAUDE.md
2
+
3
+ The canonical instructions for this repository live in **[AGENTS.md](./AGENTS.md)**.
4
+ They are imported below so Claude Code loads them automatically:
5
+
6
+ @AGENTS.md
7
+
8
+ ---
9
+
10
+ ## Claude-specific notes
11
+
12
+ - Treat `AGENTS.md` as the single source of truth. If guidance here ever conflicts
13
+ with it, `AGENTS.md` wins — and update one of the two files so they agree again.
14
+ - Before non-trivial work, read the math conventions and KKT reduction in
15
+ `AGENTS.md`, and the source papers they cite (full citations in the AGENTS.md
16
+ References section).
17
+ - The five **non-negotiable invariants** in `AGENTS.md` (no concrete array library
18
+ in the core, stay inside the Array API standard, injected linear algebra, sparsity
19
+ as an adapter concern, no global mutable state) are hard constraints. If a task
20
+ seems to need breaking one, say so and propose options instead of working around
21
+ it silently.
22
+ - When you touch numerics, add or run the **multi-backend** tests (NumPy **and**
23
+ PyTorch at minimum) and the derivative-check harness before declaring done.
24
+ - Cite the relevant paper/equation in code comments when implementing an algorithmic
25
+ step (e.g. `# Wächter & Biegler 2006, eq. (19)` / `# Breedveld 2017, eq. (18)`).
26
+ - Consult the source papers (full citations with DOIs in the AGENTS.md References
27
+ section) rather than guessing at the algorithm details.
@@ -0,0 +1,91 @@
1
+ # Contributing to `ipax`
2
+
3
+ Thanks for working on `ipax`. The canonical, detailed contributor guide is
4
+ [`AGENTS.md`](./AGENTS.md) — it is the single source of truth for the math
5
+ conventions, architecture, and the rules below. This file is the short version.
6
+
7
+ ## Non-negotiable invariants
8
+
9
+ These are hard constraints. A change that appears to require breaking one should be
10
+ raised for discussion in the PR, not worked around silently.
11
+
12
+ 1. **No concrete array library in the core.** Never import `numpy`/`torch`/`cupy`/
13
+ `jax` inside `ipax/` except in `ipax/backend/sparse/` and `ipax/problem/autodiff/`.
14
+ Get the namespace from the input arrays:
15
+ ```python
16
+ from ipax.backend.namespace import array_namespace
17
+ xp = array_namespace(x)
18
+ ```
19
+ 2. **Stay inside the Array API standard** — main namespace + the optional `linalg`
20
+ extension only. Gap-fillers (triangular solve, `lstsq`, …) go in `backend/` with a
21
+ comment naming the missing primitive.
22
+ 3. **Linear algebra is injected, never hard-wired.** Jacobians/Hessians are
23
+ `LinearOperator`s; all KKT solves go through the `LinearSolver` protocol. Adding a
24
+ solve strategy must not touch `ipm/driver.py`.
25
+ 4. **Sparsity is an adapter concern.** The core emits structure as Array-API
26
+ index/value vectors; per-backend wrappers in `backend/sparse/` build and factor the
27
+ sparse matrix. The IPM never sees a backend sparse object.
28
+ 5. **No global mutable state.** Solver state lives in explicit objects.
29
+
30
+ The purity boundary (invariants #1/#4) is enforced by `scripts/check_purity.py` — at
31
+ commit via a pre-commit hook and, for Claude Code, via a PostToolUse hook the moment
32
+ an edit touches `ipax/`.
33
+
34
+ ## Coding standards
35
+
36
+ - Python ≥ 3.10, full type hints, `from __future__ import annotations`.
37
+ - Public API documented with docstrings; numerical choices cite the paper/equation
38
+ (e.g. `# Wächter & Biegler 2006, eq. (19)`).
39
+ - Config via frozen dataclasses in `options.py`; no magic numbers in the loop body.
40
+ - Floats are `float64` by default — never hard-code a dtype; read it from inputs.
41
+ - `ruff` (format + lint) and `mypy` clean before commit.
42
+
43
+ ## Testing
44
+
45
+ - **Test-driven.** Write the failing test first, then implement to green.
46
+ - **Always multi-backend.** Numerical tests parametrize over the namespace fixture
47
+ (NumPy + PyTorch minimum; CuPy/JAX/GPU when available); `array-api-strict` is the
48
+ purity gate.
49
+ - New protocol implementations must pass the shared **contract batteries**
50
+ (`tests/contracts/`).
51
+ - Every bug fix ships with a regression test in `tests/regression/`.
52
+
53
+ ## Verification
54
+
55
+ `scripts/check.py` is the single verification entrypoint and runs exactly what CI
56
+ runs (`format → lint → types → purity → test`):
57
+
58
+ ```bash
59
+ python scripts/check.py # all gates (multi-backend tests included)
60
+ python scripts/check.py --fast # skip the slow test gate
61
+ python scripts/check.py --list # show the gates and the raw commands they wrap
62
+ ```
63
+
64
+ ## Direction
65
+
66
+ `ipax` is **beta** and **looking for adopters** — trying it on a real
67
+ workload and reporting back is one of the most valuable contributions right now.
68
+ The solver surface is complete; near-term work is **maintenance and measurement**
69
+ rather than new features:
70
+
71
+ - **Bug-fixing** — correctness on hard nonconvex / ill-conditioned problems, each fix
72
+ with a regression test.
73
+ - **Performance** — drive the `benchmarks/` scaling and micro suites (`asv`); reduce
74
+ per-iteration cost and memory across the dense / matrix-free / sparse routes.
75
+ - **Accuracy benchmarking** — grow the QC sweep and reference cross-checks
76
+ (vs IPOPT/SciPy/OSQP); track scaled-KKT accuracy and robustness over time.
77
+
78
+ ### Road to 1.0.0
79
+
80
+ `1.0.0` carries a semver stability promise for the public API. It is gated on:
81
+ **(1)** a stable public surface (`solve`, `Problem`, `Options`, `Result`,
82
+ `LinearOperator`/`LinearSolver`) across the `0.x` series, deprecations carried one
83
+ minor release; **(2)** the published MkDocs site; **(3)** GPU/sparse-direct
84
+ (CuPy/cuDSS, Torch/JAX-CUDA, MUMPS inertia) verified on real hardware in CI or
85
+ documented as provisional; **(4)** at least one external adopter workload with
86
+ feedback folded back in; **(5)** tracked `asv` and QC baselines with reference
87
+ cross-checks green. See [`README.md`](./README.md#road-to-100) and
88
+ [`AGENTS.md`](./AGENTS.md#road-to-100) for the full version.
89
+
90
+ New algorithmic features beyond the current scope (see the scope guardrails in
91
+ `AGENTS.md`) are out until discussed.
ipax-0.1.1/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Niklas Wahl
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
ipax-0.1.1/NOTICE ADDED
@@ -0,0 +1,28 @@
1
+ ipax
2
+ Copyright 2026 Niklas Wahl
3
+
4
+ Licensed under the Apache License, Version 2.0. See the LICENSE file for the
5
+ full terms.
6
+
7
+ ------------------------------------------------------------------------------
8
+ Algorithmic attribution
9
+ ------------------------------------------------------------------------------
10
+
11
+ ipax is an independent, clean-room implementation written from the published
12
+ literature. It contains no source code from the works below; they are cited as
13
+ the academic sources of the algorithms it implements:
14
+
15
+ - Wächter, A. & Biegler, L. T. (2006). "On the implementation of an
16
+ interior-point filter line-search algorithm for large-scale nonlinear
17
+ programming." Mathematical Programming 106(1), 25-57.
18
+ https://doi.org/10.1007/s10107-004-0559-y
19
+
20
+ - Breedveld, S., van den Berg, B. & Heijmen, B. (2017). "An interior-point
21
+ implementation developed and tuned for radiation therapy treatment
22
+ planning." Computational Optimization and Applications 68(2), 209-242.
23
+ https://doi.org/10.1007/s10589-017-9919-4
24
+
25
+ - Friedlander, M. P. & Orban, D. (2012). "A primal-dual regularized
26
+ interior-point method for convex quadratic programs." Mathematical
27
+ Programming Computation 4(1), 71-107.
28
+ https://doi.org/10.1007/s12532-012-0035-2