twindex 0.2.0__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.
- twindex-0.2.0/CHANGELOG.md +54 -0
- twindex-0.2.0/CITATION.cff +19 -0
- twindex-0.2.0/CODE_OF_CONDUCT.md +12 -0
- twindex-0.2.0/CONTRIBUTING.md +25 -0
- twindex-0.2.0/LICENSE +21 -0
- twindex-0.2.0/MANIFEST.in +8 -0
- twindex-0.2.0/PKG-INFO +84 -0
- twindex-0.2.0/README.md +277 -0
- twindex-0.2.0/SECURITY.md +20 -0
- twindex-0.2.0/docs/DESIGN.md +237 -0
- twindex-0.2.0/docs/THEORY.md +299 -0
- twindex-0.2.0/docs/api.md +33 -0
- twindex-0.2.0/docs/assets/icon.svg +7 -0
- twindex-0.2.0/docs/assets/logo-dark.svg +11 -0
- twindex-0.2.0/docs/assets/logo-light.svg +11 -0
- twindex-0.2.0/docs/audit.md +65 -0
- twindex-0.2.0/docs/changelog.md +1 -0
- twindex-0.2.0/docs/examples.md +32 -0
- twindex-0.2.0/docs/getting-started.md +51 -0
- twindex-0.2.0/docs/index.md +44 -0
- twindex-0.2.0/docs/pypi.md +38 -0
- twindex-0.2.0/docs/study.md +164 -0
- twindex-0.2.0/docs/tutorial.md +81 -0
- twindex-0.2.0/examples/README.md +29 -0
- twindex-0.2.0/examples/box_inside.json +63 -0
- twindex-0.2.0/examples/box_outside.json +63 -0
- twindex-0.2.0/examples/generate.py +50 -0
- twindex-0.2.0/examples/inconclusive.json +56 -0
- twindex-0.2.0/examples/invalid_bc.json +72 -0
- twindex-0.2.0/examples/invalid_gap.json +72 -0
- twindex-0.2.0/examples/paving_regression.json +74 -0
- twindex-0.2.0/examples/paving_theta500.json +82 -0
- twindex-0.2.0/examples/regression_pair.json +72 -0
- twindex-0.2.0/examples/study/m17_eps100000_local_paving.json +106 -0
- twindex-0.2.0/examples/study/m17_eps100000_pair.json +144 -0
- twindex-0.2.0/examples/study/m17_eps100000_paving.json +211 -0
- twindex-0.2.0/examples/study/m17_eps1000_affine_local_paving.json +232 -0
- twindex-0.2.0/examples/study/m17_eps100_pair.json +144 -0
- twindex-0.2.0/examples/study/m17_eps100_paving.json +211 -0
- twindex-0.2.0/examples/study/m3_eps100000_pair.json +116 -0
- twindex-0.2.0/examples/study/m3_eps100000_paving.json +183 -0
- twindex-0.2.0/examples/study/m3_eps100_pair.json +116 -0
- twindex-0.2.0/examples/study/m3_eps100_paving.json +183 -0
- twindex-0.2.0/examples/theta500_pair.json +146 -0
- twindex-0.2.0/examples/witness_theta500.json +86 -0
- twindex-0.2.0/mkdocs.yml +64 -0
- twindex-0.2.0/pyproject.toml +62 -0
- twindex-0.2.0/scripts/verify_release.py +53 -0
- twindex-0.2.0/setup.cfg +4 -0
- twindex-0.2.0/tests/test_affine.py +130 -0
- twindex-0.2.0/tests/test_audit_models.py +106 -0
- twindex-0.2.0/tests/test_checker.py +314 -0
- twindex-0.2.0/tests/test_fit.py +64 -0
- twindex-0.2.0/tests/test_models.py +173 -0
- twindex-0.2.0/tests/test_paving.py +189 -0
- twindex-0.2.0/tests/test_poly.py +100 -0
- twindex-0.2.0/tests/test_rational.py +94 -0
- twindex-0.2.0/tests/test_release.py +43 -0
- twindex-0.2.0/tests/test_search_cli_examples.py +85 -0
- twindex-0.2.0/tests/test_sivia.py +181 -0
- twindex-0.2.0/tests/test_solver_candidate.py +100 -0
- twindex-0.2.0/tests/test_tutorial_study.py +92 -0
- twindex-0.2.0/twindex/__init__.py +3 -0
- twindex-0.2.0/twindex/affine.py +135 -0
- twindex-0.2.0/twindex/audit.py +103 -0
- twindex-0.2.0/twindex/candidate.py +66 -0
- twindex-0.2.0/twindex/certificate.py +688 -0
- twindex-0.2.0/twindex/checker.py +502 -0
- twindex-0.2.0/twindex/cli.py +127 -0
- twindex-0.2.0/twindex/data/box_inside.json +63 -0
- twindex-0.2.0/twindex/data/box_outside.json +63 -0
- twindex-0.2.0/twindex/data/inconclusive.json +56 -0
- twindex-0.2.0/twindex/data/invalid_bc.json +72 -0
- twindex-0.2.0/twindex/data/invalid_gap.json +72 -0
- twindex-0.2.0/twindex/data/paving_regression.json +74 -0
- twindex-0.2.0/twindex/data/paving_theta500.json +82 -0
- twindex-0.2.0/twindex/data/regression_pair.json +72 -0
- twindex-0.2.0/twindex/data/theta500_pair.json +146 -0
- twindex-0.2.0/twindex/data/witness_theta500.json +86 -0
- twindex-0.2.0/twindex/fit.py +73 -0
- twindex-0.2.0/twindex/model.py +269 -0
- twindex-0.2.0/twindex/poly.py +255 -0
- twindex-0.2.0/twindex/rational.py +98 -0
- twindex-0.2.0/twindex/search.py +264 -0
- twindex-0.2.0/twindex/solver.py +101 -0
- twindex-0.2.0/twindex.egg-info/PKG-INFO +84 -0
- twindex-0.2.0/twindex.egg-info/SOURCES.txt +89 -0
- twindex-0.2.0/twindex.egg-info/dependency_links.txt +1 -0
- twindex-0.2.0/twindex.egg-info/entry_points.txt +2 -0
- twindex-0.2.0/twindex.egg-info/requires.txt +23 -0
- twindex-0.2.0/twindex.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-11-07
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **General models.** `Model(degree, source, nonlinearity)`: Bernstein
|
|
11
|
+
coefficients of any degree up to 20, a polynomial source term, and
|
|
12
|
+
nonlinearities `Σ a_m u^m` with odd `m` and `a_m ≥ 0`. The original problem is
|
|
13
|
+
`V1_MODEL`. The theory is generalised accordingly (THEORY §1, §3–§5).
|
|
14
|
+
- **Certificate format v2** with an explicit `problem.model` block. Version 1
|
|
15
|
+
certificates are still read and checked unchanged.
|
|
16
|
+
- **Affine candidate families** (THEORY Theorem 3′): a box certificate can carry
|
|
17
|
+
sensitivity polynomials, so that one certificate covers a whole parameter box
|
|
18
|
+
with residual bound `Σ_α h^α ‖R_α‖`. Around `θ̄ = 500` this certifies boxes with
|
|
19
|
+
5× the half-width (625× the volume) of the plain bound.
|
|
20
|
+
- **`twindex audit`**: certify how well a parameter can be recovered from your
|
|
21
|
+
own measurements (CSV), noise level, prior and model.
|
|
22
|
+
- Model-aware solver, twin search and SIVIA.
|
|
23
|
+
- Affine SIVIA: pavings use sensitivity families by default. On the full
|
|
24
|
+
`[0,1000]⁴` prior they now prove most leaves incompatible, but the compatible
|
|
25
|
+
set is too thin for axis-aligned boxes, so certified upper bounds remain the
|
|
26
|
+
prior width (see the study). A local example proves `L = U = 50` with one affine leaf.
|
|
27
|
+
- `twindex.fit`: certify any callable (a PINN, a solver) as an exact candidate.
|
|
28
|
+
- Inhomogeneous boundary values `u(0) = α`, `u(1) = β`.
|
|
29
|
+
- A redesigned landing page with a hero figure, typeset equations and a results gallery.
|
|
30
|
+
- Sensor/noise studies for targets θ₀, θ₁ and θ₂ with certified results, and an
|
|
31
|
+
executable tutorial.
|
|
32
|
+
- `mypy --strict` on the trusted core, a 90% coverage gate, and CI on Linux
|
|
33
|
+
(Python 3.10–3.13), macOS and Windows.
|
|
34
|
+
|
|
35
|
+
## [0.1.0] - 2026-11-07
|
|
36
|
+
|
|
37
|
+
First public release.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- Exact trusted core (standard library only, no floating point): rational
|
|
41
|
+
parsing, exact polynomials, certified Bernstein sup-norm bounds, the model
|
|
42
|
+
problem, a strict certificate format (v1), and the checker with VALID /
|
|
43
|
+
INVALID / INCONCLUSIVE verdicts.
|
|
44
|
+
- Certificate kinds: `witness`, `pair`, `box` and `paving` (SIVIA-style
|
|
45
|
+
two-sided bounds on the target diameter).
|
|
46
|
+
- Untrusted search side: a Chebyshev–Newton solver, exact candidate conversion,
|
|
47
|
+
a local twin search and an adaptive SIVIA driver.
|
|
48
|
+
- `twindex` command line: `check`, `example`, `search`, `sivia`.
|
|
49
|
+
- Ten example certificates covering every kind and every verdict, replayed in
|
|
50
|
+
CI on Python 3.10–3.13.
|
|
51
|
+
- Full proofs in `docs/THEORY.md`; interface contract in `docs/DESIGN.md`.
|
|
52
|
+
|
|
53
|
+
[0.2.0]: https://github.com/Arnav-M10/Twindex/releases/tag/v0.2.0
|
|
54
|
+
[0.1.0]: https://github.com/Arnav-M10/Twindex/releases/tag/v0.1.0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use Twindex in your research, please cite it as below."
|
|
3
|
+
title: "Twindex: exact, replayable certificates of data-compatible ambiguity for a PDE inverse problem"
|
|
4
|
+
type: software
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Mittal
|
|
7
|
+
given-names: Arnav
|
|
8
|
+
version: 0.2.0
|
|
9
|
+
date-released: 2026-11-07
|
|
10
|
+
license: MIT
|
|
11
|
+
repository-code: "https://github.com/Arnav-M10/Twindex"
|
|
12
|
+
url: "https://arnav-m10.github.io/Twindex/"
|
|
13
|
+
keywords:
|
|
14
|
+
- inverse problems
|
|
15
|
+
- identifiability
|
|
16
|
+
- certified computation
|
|
17
|
+
- exact arithmetic
|
|
18
|
+
- partial differential equations
|
|
19
|
+
- set inversion
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
This project follows the
|
|
4
|
+
[Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
|
|
5
|
+
|
|
6
|
+
In short: be respectful, assume good faith, and keep discussion about the work.
|
|
7
|
+
Harassment of any kind is not tolerated.
|
|
8
|
+
|
|
9
|
+
Report unacceptable behaviour privately to the maintainer through
|
|
10
|
+
[GitHub private reporting](https://github.com/Arnav-M10/Twindex/security/advisories/new)
|
|
11
|
+
or by contacting the repository owner on GitHub. Reports are handled
|
|
12
|
+
confidentially.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
* Read [docs/DESIGN.md](docs/DESIGN.md) first; it is the interface contract and says who owns which module.
|
|
4
|
+
* The trusted core (`rational`, `poly`, `model`, `certificate`, `checker`) uses the standard library only and never touches floats.
|
|
5
|
+
* Work on a branch, then open a pull request. Every pull request is reviewed by the owner of each module it touches.
|
|
6
|
+
* Before merging: `ruff check .` and `pytest` must pass.
|
|
7
|
+
|
|
8
|
+
Setup:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python3 -m venv .venv
|
|
12
|
+
.venv/bin/pip install -e ".[dev]"
|
|
13
|
+
.venv/bin/pytest
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Docs (MkDocs Material, deployed to GitHub Pages from `main`):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
.venv/bin/pip install -e ".[docs]"
|
|
20
|
+
.venv/bin/mkdocs serve
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Releasing: bump `version` in `pyproject.toml` and `twindex/__init__.py`, update
|
|
24
|
+
`CHANGELOG.md` and `CITATION.cff`, then publish a GitHub release tagged `vX.Y.Z`.
|
|
25
|
+
The `release` workflow builds, verifies and uploads to PyPI through trusted publishing.
|
twindex-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arnav Mittal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
include LICENSE README.md CONTRIBUTING.md
|
|
2
|
+
include mkdocs.yml CHANGELOG.md CITATION.cff SECURITY.md CODE_OF_CONDUCT.md
|
|
3
|
+
recursive-include docs *.md
|
|
4
|
+
recursive-include docs/assets *.svg
|
|
5
|
+
recursive-include scripts *.py
|
|
6
|
+
recursive-include examples *.json *.md *.py
|
|
7
|
+
recursive-include tests *.py
|
|
8
|
+
recursive-include twindex/data *.json
|
twindex-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: twindex
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Exact, replayable certificates that noisy PDE data cannot tell two parameter sets apart.
|
|
5
|
+
Author: Arnav Mittal
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Arnav-M10/Twindex
|
|
8
|
+
Project-URL: Documentation, https://arnav-m10.github.io/Twindex/
|
|
9
|
+
Project-URL: Source, https://github.com/Arnav-M10/Twindex
|
|
10
|
+
Project-URL: Issues, https://github.com/Arnav-M10/Twindex/issues
|
|
11
|
+
Keywords: inverse problems,identifiability,certified computation,computer-assisted proof,exact arithmetic,PDE,PINN,set inversion,optimal recovery
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: search
|
|
27
|
+
Requires-Dist: numpy>=1.24; extra == "search"
|
|
28
|
+
Provides-Extra: studies
|
|
29
|
+
Requires-Dist: numpy>=1.24; extra == "studies"
|
|
30
|
+
Requires-Dist: matplotlib>=3.7; extra == "studies"
|
|
31
|
+
Provides-Extra: docs
|
|
32
|
+
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
33
|
+
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
|
|
34
|
+
Requires-Dist: numpy>=1.24; extra == "docs"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: numpy>=1.24; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
38
|
+
Requires-Dist: hypothesis>=6; extra == "dev"
|
|
39
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
40
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
41
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
42
|
+
Requires-Dist: setuptools>=77; extra == "dev"
|
|
43
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# Twindex
|
|
48
|
+
|
|
49
|
+
**Your inverse solver returned a parameter. Twindex proves whether the data could have determined it.**
|
|
50
|
+
|
|
51
|
+
Least squares, Bayesian inference and physics-informed neural networks always
|
|
52
|
+
*return* a parameter. None of them can tell you whether a completely different
|
|
53
|
+
parameter fits your measurements just as well. Twindex finds such *twins* for
|
|
54
|
+
the boundary value problem
|
|
55
|
+
|
|
56
|
+
-u''(x) + q(x; theta) f(u(x)) = g(x) on (0, 1), u(0) = alpha, u(1) = beta,
|
|
57
|
+
q(x; theta) = sum_j theta_j B_j(x) (Bernstein basis, theta >= 0),
|
|
58
|
+
f(u) = sum of a_m u^m over odd m, a_m >= 0,
|
|
59
|
+
|
|
60
|
+
and proves it: it writes a JSON certificate that a small exact checker
|
|
61
|
+
(standard-library Python, no floating point, type-checked with mypy --strict)
|
|
62
|
+
replays and verifies. A VALID pair certificate proves that **no method** can
|
|
63
|
+
recover the parameter from those data with worst-case error below half the
|
|
64
|
+
certified gap.
|
|
65
|
+
|
|
66
|
+
```console
|
|
67
|
+
$ pip install twindex # exact checker + CLI, zero dependencies
|
|
68
|
+
$ pip install "twindex[search]" # + numerical search, audits and fitting (numpy)
|
|
69
|
+
|
|
70
|
+
$ twindex example theta500_pair > pair.json
|
|
71
|
+
$ twindex check pair.json
|
|
72
|
+
pair.json: VALID: ... every estimator of theta_0 has worst-case error >= 500 on these data
|
|
73
|
+
|
|
74
|
+
$ twindex audit --data measurements.csv --epsilon 1/1000 --prior 0,1000 --target 1 --output audit/
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
* Certificates: witness, pair (twins), box, and paving (two-sided bounds).
|
|
78
|
+
* Your model: any coefficient degree, polynomial sources, odd-power
|
|
79
|
+
nonlinearities, inhomogeneous boundary values.
|
|
80
|
+
* `twindex.fit`: check any callable (a PINN, scipy, FEM) as an exact candidate.
|
|
81
|
+
* Every step is proved: https://arnav-m10.github.io/Twindex/THEORY/
|
|
82
|
+
|
|
83
|
+
Documentation: https://arnav-m10.github.io/Twindex/ ·
|
|
84
|
+
Source: https://github.com/Arnav-M10/Twindex
|
twindex-0.2.0/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Arnav-M10/Twindex/main/docs/assets/logo-dark.svg">
|
|
4
|
+
<img alt="Twindex" src="https://raw.githubusercontent.com/Arnav-M10/Twindex/main/docs/assets/logo-light.svg" width="560">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://github.com/Arnav-M10/Twindex/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Arnav-M10/Twindex/actions/workflows/ci.yml/badge.svg"></a>
|
|
10
|
+
<a href="https://pypi.org/project/twindex/"><img alt="PyPI" src="https://img.shields.io/pypi/v/twindex.svg"></a>
|
|
11
|
+
<a href="https://pypi.org/project/twindex/"><img alt="Python" src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue.svg"></a>
|
|
12
|
+
<a href="https://arnav-m10.github.io/Twindex/"><img alt="Docs" src="https://img.shields.io/badge/docs-online-informational.svg"></a>
|
|
13
|
+
<img alt="Typed core" src="https://img.shields.io/badge/core-mypy%20--strict-2f81f7.svg">
|
|
14
|
+
<a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green.svg"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<b>Your inverse solver returned a parameter. Twindex proves whether the data could have determined it.</b><br>
|
|
19
|
+
<a href="https://arnav-m10.github.io/Twindex/">Documentation</a> ·
|
|
20
|
+
<a href="https://arnav-m10.github.io/Twindex/tutorial/">Tutorial</a> ·
|
|
21
|
+
<a href="https://arnav-m10.github.io/Twindex/audit/">Audit your data</a> ·
|
|
22
|
+
<a href="docs/THEORY.md">Theory</a> ·
|
|
23
|
+
<a href="examples/">Examples</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<p align="center">
|
|
29
|
+
<img src="docs/assets/hero.png" alt="Two very different coefficients q(x) give solutions that agree at every sensor to within the noise" width="100%">
|
|
30
|
+
</p>
|
|
31
|
+
|
|
32
|
+
Least squares, Bayesian inference and physics-informed neural networks will
|
|
33
|
+
always *return* a parameter. None of them can tell you whether a **completely
|
|
34
|
+
different** parameter fits your measurements just as well. Twindex finds such
|
|
35
|
+
*twins* and proves it: it writes an exact certificate that a small checker,
|
|
36
|
+
standard-library Python with no floating point, replays and verifies.
|
|
37
|
+
|
|
38
|
+
> [!IMPORTANT]
|
|
39
|
+
> The figure above is a real certificate (`examples/theta500_pair.json`). Two
|
|
40
|
+
> coefficient fields that differ by up to 1000 produce solutions that agree at all
|
|
41
|
+
> five sensors to within the noise level $`\varepsilon = 10^{-3}`$. Therefore **no
|
|
42
|
+
> method** can recover $`\theta_0`$ from those data with worst-case error below 500.
|
|
43
|
+
> Anyone can re-check the claim with `twindex check`.
|
|
44
|
+
|
|
45
|
+
**Contents:**
|
|
46
|
+
[Why Twindex](#why-twindex) ·
|
|
47
|
+
[What it certifies](#what-twindex-certifies) ·
|
|
48
|
+
[Why you can trust a VALID](#why-you-can-trust-a-valid) ·
|
|
49
|
+
[Installation](#installation) ·
|
|
50
|
+
[Quick start](#quick-start) ·
|
|
51
|
+
[Features](#features) ·
|
|
52
|
+
[Results](#results) ·
|
|
53
|
+
[How it works](#how-it-works) ·
|
|
54
|
+
[Documentation](#documentation) ·
|
|
55
|
+
[Citing](#citing-twindex)
|
|
56
|
+
|
|
57
|
+
## Why Twindex
|
|
58
|
+
|
|
59
|
+
PINN libraries such as DeepXDE and PhysicsNeMo, Bayesian samplers and classical
|
|
60
|
+
least squares are built to **estimate** parameters. Twindex answers a different
|
|
61
|
+
question, *could any estimate be trusted?*, and it answers with a proof.
|
|
62
|
+
|
|
63
|
+
| | returns | uncertainty | proves that the data cannot pin the parameter down |
|
|
64
|
+
|---|---|---|---|
|
|
65
|
+
| least squares, PINNs | one estimate $`\hat\theta`$ | none | no |
|
|
66
|
+
| Bayesian inference | a posterior | probabilistic, depends on the prior and likelihood | no |
|
|
67
|
+
| **Twindex** | **certified twins** $`\theta, \theta'`$ | **worst case, under bounded noise** | **yes, by an exact certificate** |
|
|
68
|
+
|
|
69
|
+
Twindex complements estimators rather than replacing them: run your solver, then
|
|
70
|
+
ask Twindex whether a very different answer fits the data just as well.
|
|
71
|
+
|
|
72
|
+
## What Twindex certifies
|
|
73
|
+
|
|
74
|
+
Twindex works with the boundary value problem
|
|
75
|
+
|
|
76
|
+
$$
|
|
77
|
+
-u''(x) + q(x;\theta)\, f\big(u(x)\big) = g(x), \qquad x \in (0,1), \qquad u(0)=\alpha,\quad u(1)=\beta,
|
|
78
|
+
$$
|
|
79
|
+
|
|
80
|
+
$$
|
|
81
|
+
q(x;\theta) = \sum_{j=0}^{d} \theta_j \binom{d}{j} x^j (1-x)^{d-j}, \qquad
|
|
82
|
+
f(u) = \sum_{m \text{ odd}} a_m u^m \quad (a_m \ge 0), \qquad \theta \in \Theta \subseteq [0,\infty)^{d+1}.
|
|
83
|
+
$$
|
|
84
|
+
|
|
85
|
+
Here $`g`$ is any polynomial, and the coefficient degree $`d`$, the nonlinearity
|
|
86
|
+
and the boundary values are yours to choose. Given sensors $`x_i`$, measurements
|
|
87
|
+
$`y_i`$ and a noise bound $`\varepsilon`$, the **data-compatible set** is
|
|
88
|
+
|
|
89
|
+
$$
|
|
90
|
+
\mathcal{C}_y = \Big\lbrace\, \theta \in \Theta \;:\; \max_i \big\vert u_\theta(x_i) - y_i \big\vert \le \varepsilon \,\Big\rbrace.
|
|
91
|
+
$$
|
|
92
|
+
|
|
93
|
+
| certificate | proves |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `witness` | one parameter $`\theta \in \mathcal{C}_y`$ |
|
|
96
|
+
| `pair` | twins $`\theta, \theta' \in \mathcal{C}_y`$, so every estimator $`T`$ satisfies $`\sup_{\vartheta \in \mathcal{C}_y} \lvert T(y) - \vartheta_k \rvert \ge \tfrac12 \lvert \theta_k - \theta'_k \rvert`$ |
|
|
97
|
+
| `box` | a whole box of parameters lies inside $`\mathcal{C}_y`$, or misses it entirely |
|
|
98
|
+
| `paving` | a covering of $`\Theta`$ with certified two-sided bounds $`L \le \mathrm{diam}_k \mathcal{C}_y \le U`$ |
|
|
99
|
+
|
|
100
|
+
## Why you can trust a VALID
|
|
101
|
+
|
|
102
|
+
Every verdict follows from one theorem, proved in [docs/THEORY.md](docs/THEORY.md).
|
|
103
|
+
If a polynomial $`p`$ matches the boundary values exactly and its residual is
|
|
104
|
+
bounded,
|
|
105
|
+
|
|
106
|
+
$$
|
|
107
|
+
\rho \;\ge\; \max_{x \in [0,1]} \Big\vert -p''(x) + q(x;\theta)\, f\big(p(x)\big) - g(x) \Big\vert,
|
|
108
|
+
$$
|
|
109
|
+
|
|
110
|
+
then the **true** solution satisfies, for every $`x \in [0,1]`$,
|
|
111
|
+
|
|
112
|
+
$$
|
|
113
|
+
\big\vert u_\theta(x) - p(x) \big\vert \;\le\; \rho \, \frac{x(1-x)}{2}.
|
|
114
|
+
$$
|
|
115
|
+
|
|
116
|
+
<details>
|
|
117
|
+
<summary><b>Proof sketch</b> (complete proofs in <a href="docs/THEORY.md">docs/THEORY.md</a>)</summary>
|
|
118
|
+
|
|
119
|
+
<br>
|
|
120
|
+
|
|
121
|
+
Let $`e = u_\theta - p`$ and $`r = -p'' + q f(p) - g`$. Because $`f`$ is a sum of odd
|
|
122
|
+
powers with nonnegative coefficients, $`f(u) - f(p) = (u - p)\,H(u, p)`$ with
|
|
123
|
+
$`H \ge 0`$. Hence
|
|
124
|
+
|
|
125
|
+
$$
|
|
126
|
+
-e'' + c\,e = -r, \qquad c = q\,H(u_\theta, p) \ge 0, \qquad e(0) = e(1) = 0.
|
|
127
|
+
$$
|
|
128
|
+
|
|
129
|
+
The barrier $`b(x) = \rho\,x(1-x)/2`$ satisfies $`-b'' + c\,b \ge \rho \ge \lvert r \rvert`$, so
|
|
130
|
+
$`w_\pm = b \mp e`$ satisfy $`-w_\pm'' + c\,w_\pm \ge 0`$ with zero boundary values.
|
|
131
|
+
A one-dimensional maximum principle gives $`w_\pm \ge 0`$, that is, $`\lvert e \rvert \le b`$.
|
|
132
|
+
|
|
133
|
+
</details>
|
|
134
|
+
|
|
135
|
+
There is no convergence assumption and no floating point. $`\rho`$ is computed
|
|
136
|
+
from Bernstein coefficients with exact rational subdivision, and every sensor
|
|
137
|
+
inequality is decided in exact arithmetic. The checker, about 2,000 lines of
|
|
138
|
+
standard-library Python and type-checked with `mypy --strict`, gives one of
|
|
139
|
+
three verdicts:
|
|
140
|
+
|
|
141
|
+
* **VALID**: the claim is proved;
|
|
142
|
+
* **INVALID**: the certificate is malformed or its claim is false;
|
|
143
|
+
* **INCONCLUSIVE**: nothing was proved either way.
|
|
144
|
+
|
|
145
|
+
## Installation
|
|
146
|
+
|
|
147
|
+
```console
|
|
148
|
+
$ pip install twindex # exact checker + CLI, zero dependencies
|
|
149
|
+
$ pip install "twindex[search]" # + numerical search, audits and fitting (numpy)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Quick start
|
|
153
|
+
|
|
154
|
+
**Replay a certificate.**
|
|
155
|
+
|
|
156
|
+
```console
|
|
157
|
+
$ twindex example theta500_pair > pair.json
|
|
158
|
+
$ twindex check pair.json
|
|
159
|
+
pair.json: VALID: both witnesses are data-compatible; D_Q(y) >= 1000, so every estimator of theta_0 has worst-case error >= 500 on these data
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Audit your own measurements** (a CSV file with columns `x,y`):
|
|
163
|
+
|
|
164
|
+
```console
|
|
165
|
+
$ twindex audit --data measurements.csv --epsilon 1/1000 --prior 0,1000 --target 1 --output audit/
|
|
166
|
+
theta_1 cannot have a uniform absolute-error guarantee better than +/-500 from these data (certified).
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Check a PINN** (or any solver) as a black box:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from twindex.fit import certify_callable
|
|
173
|
+
|
|
174
|
+
report = certify_callable(lambda x: my_pinn(x), problem, theta_hat)
|
|
175
|
+
print(report.verdict) # VALID: theta_hat really is consistent with the data
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Features
|
|
179
|
+
|
|
180
|
+
* **Exact, replayable certificates.** Plain JSON that anyone can re-check with
|
|
181
|
+
`twindex check`. Floats are rejected everywhere, even inside JSON.
|
|
182
|
+
* **Your model.** Bernstein coefficients of any degree, polynomial sources,
|
|
183
|
+
odd-power nonlinearities $`f(u) = \sum_m a_m u^m`$, and inhomogeneous boundary
|
|
184
|
+
values.
|
|
185
|
+
* **Audits.** `twindex audit` turns measurements, a noise level and a prior into
|
|
186
|
+
a certified statement about how well a parameter can be recovered.
|
|
187
|
+
* **Bring your own solver.** `twindex.fit` turns any callable (a PINN, scipy,
|
|
188
|
+
FEM) into an exact candidate.
|
|
189
|
+
* **Two-sided bounds.** SIVIA-style pavings, with affine candidate families that
|
|
190
|
+
certify whole parameter boxes at once.
|
|
191
|
+
* **Honest by construction.** The search is a heuristic; the checker is the
|
|
192
|
+
judge. INCONCLUSIVE is never reported as a disproof.
|
|
193
|
+
|
|
194
|
+
## Results
|
|
195
|
+
|
|
196
|
+
<p align="center">
|
|
197
|
+
<img src="docs/assets/results.png" alt="Certified ambiguity of theta_0 against sensor count and noise level" width="80%">
|
|
198
|
+
</p>
|
|
199
|
+
|
|
200
|
+
How much do more sensors and less noise buy? Certified lower bounds on the
|
|
201
|
+
ambiguity of $`\theta_0`$, for data generated at $`\bar\theta = (500, 500, 500, 500)`$
|
|
202
|
+
with prior $`[0, 1000]^4`$, rounded down ([full study](https://arnav-m10.github.io/Twindex/study/)):
|
|
203
|
+
|
|
204
|
+
| sensors | $`\varepsilon = 10^{-2}`$ | $`\varepsilon = 10^{-3}`$ | $`\varepsilon = 10^{-4}`$ | $`\varepsilon = 10^{-5}`$ |
|
|
205
|
+
|---:|---:|---:|---:|---:|
|
|
206
|
+
| 3 | 1000 | 1000 | 749 | 718 |
|
|
207
|
+
| 5 | 1000 | 1000 | 763 | 85 |
|
|
208
|
+
| 9 | 1000 | 1000 | 766 | 97 |
|
|
209
|
+
| 17 | 1000 | 1000 | 761 | 85 |
|
|
210
|
+
|
|
211
|
+
Even 17 sensors at noise $`10^{-3}`$ cannot narrow $`\theta_0`$ below its entire
|
|
212
|
+
prior range. These are certified lower bounds from a local search; they are
|
|
213
|
+
not optimal.
|
|
214
|
+
|
|
215
|
+
## How it works
|
|
216
|
+
|
|
217
|
+
1. **Search** (untrusted, floating point). A Chebyshev–Newton solver and a
|
|
218
|
+
local search propose twins. Each solution is rounded to an exact rational
|
|
219
|
+
polynomial that matches the boundary values exactly.
|
|
220
|
+
2. **Bound** (trusted, exact). The residual $`\rho`$ is bounded from Bernstein
|
|
221
|
+
coefficients with exact de Casteljau subdivision.
|
|
222
|
+
3. **Transfer.** The barrier theorem moves the bound from the candidate to the
|
|
223
|
+
true PDE solution.
|
|
224
|
+
4. **Decide.** At each sensor,
|
|
225
|
+
$`\lvert p(x_i) - y_i \rvert + \rho\, x_i(1-x_i)/2 \le \varepsilon`$ proves
|
|
226
|
+
compatibility, and $`\lvert p(x_i) - y_i \rvert - \rho\, x_i(1-x_i)/2 > \varepsilon`$
|
|
227
|
+
proves incompatibility.
|
|
228
|
+
|
|
229
|
+
## Documentation
|
|
230
|
+
|
|
231
|
+
| | |
|
|
232
|
+
|---|---|
|
|
233
|
+
| [Getting started](https://arnav-m10.github.io/Twindex/getting-started/) | install, check, search |
|
|
234
|
+
| [Tutorial](https://arnav-m10.github.io/Twindex/tutorial/) | certify your own twins in five minutes |
|
|
235
|
+
| [Audit](https://arnav-m10.github.io/Twindex/audit/) | your data, your model, a certified answer |
|
|
236
|
+
| [Theory](docs/THEORY.md) | complete proofs of every step |
|
|
237
|
+
| [Certificate format](docs/DESIGN.md) | JSON schema and verdict rules |
|
|
238
|
+
| [API reference](https://arnav-m10.github.io/Twindex/api/) | every public function |
|
|
239
|
+
|
|
240
|
+
## Scope
|
|
241
|
+
|
|
242
|
+
* One space dimension and steady problems with a monotone nonlinearity. The
|
|
243
|
+
barrier argument needs $`q \ge 0`$ and a nondecreasing $`f`$.
|
|
244
|
+
* Bounded (worst-case) noise, not a statistical model. A certificate speaks
|
|
245
|
+
about the data $`y`$ it declares.
|
|
246
|
+
* The search is a heuristic. A VALID result is a proof; failing to find twins
|
|
247
|
+
proves nothing.
|
|
248
|
+
|
|
249
|
+
## Related work
|
|
250
|
+
|
|
251
|
+
Twindex combines known ideas: optimal recovery (Micchelli–Rivlin; Foucart–Liao),
|
|
252
|
+
state and parameter estimation for parametric PDEs (Cohen, Dahmen, Mula,
|
|
253
|
+
Nichols), set inversion by interval analysis (Jaulin–Walter), certified
|
|
254
|
+
reduced-basis possibility regions (Patera and coauthors), and maximum-principle
|
|
255
|
+
a posteriori bounds (Nochetto et al.). What it adds is an exact, independently
|
|
256
|
+
replayable checker. See [THEORY §9](docs/THEORY.md#9-related-work).
|
|
257
|
+
|
|
258
|
+
## Contributing
|
|
259
|
+
|
|
260
|
+
Contributions are welcome; start with [CONTRIBUTING.md](CONTRIBUTING.md) and the
|
|
261
|
+
[design contract](docs/DESIGN.md). A soundness bug (VALID for a false claim) is
|
|
262
|
+
treated as a security issue; please report it privately ([SECURITY.md](SECURITY.md)).
|
|
263
|
+
|
|
264
|
+
## Citing Twindex
|
|
265
|
+
|
|
266
|
+
```bibtex
|
|
267
|
+
@software{twindex,
|
|
268
|
+
author = {Mittal, Arnav},
|
|
269
|
+
title = {Twindex: exact, replayable certificates of data-compatible ambiguity for a PDE inverse problem},
|
|
270
|
+
year = {2026},
|
|
271
|
+
url = {https://github.com/Arnav-M10/Twindex}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
[MIT](LICENSE). Built by Arnav Mittal with Claude Code and Codex.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Twindex is a verifier, so a **soundness bug** (any input that makes the checker
|
|
4
|
+
report VALID for a claim that is false) is treated as a security issue.
|
|
5
|
+
|
|
6
|
+
## Reporting
|
|
7
|
+
|
|
8
|
+
Please report soundness bugs and other vulnerabilities privately through
|
|
9
|
+
[GitHub private vulnerability reporting](https://github.com/Arnav-M10/Twindex/security/advisories/new).
|
|
10
|
+
Include a certificate or a Python snippet that reproduces the problem. Do not
|
|
11
|
+
open a public issue for a soundness bug before a fix is released.
|
|
12
|
+
|
|
13
|
+
Expect an acknowledgement within a week. Fixes ship with a regression test and
|
|
14
|
+
a changelog entry.
|
|
15
|
+
|
|
16
|
+
## Supported versions
|
|
17
|
+
|
|
18
|
+
| version | supported |
|
|
19
|
+
|---------|-----------|
|
|
20
|
+
| 0.1.x | yes |
|