bayesmith 0.1.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.
- bayesmith-0.1.0/.gitignore +14 -0
- bayesmith-0.1.0/CHANGELOG.md +32 -0
- bayesmith-0.1.0/LICENSE +21 -0
- bayesmith-0.1.0/PKG-INFO +87 -0
- bayesmith-0.1.0/README.md +66 -0
- bayesmith-0.1.0/docs/migration/README.md +100 -0
- bayesmith-0.1.0/docs/migration/conditioning.md +74 -0
- bayesmith-0.1.0/docs/migration/gls.md +81 -0
- bayesmith-0.1.0/docs/migration/identifiability.md +86 -0
- bayesmith-0.1.0/docs/migration/linear.md +236 -0
- bayesmith-0.1.0/docs/migration/noise.md +191 -0
- bayesmith-0.1.0/docs/migration/numpyro_bridge.md +138 -0
- bayesmith-0.1.0/docs/migration/parameters.md +140 -0
- bayesmith-0.1.0/docs/migration/plan.md +164 -0
- bayesmith-0.1.0/docs/migration/priors.md +89 -0
- bayesmith-0.1.0/docs/migration/sensitivity.md +100 -0
- bayesmith-0.1.0/docs/migration/sqrtinfo.md +67 -0
- bayesmith-0.1.0/docs/migration/uncertainty.md +85 -0
- bayesmith-0.1.0/pyproject.toml +136 -0
- bayesmith-0.1.0/src/bayesmith/__init__.py +173 -0
- bayesmith-0.1.0/src/bayesmith/bridge/__init__.py +1 -0
- bayesmith-0.1.0/src/bayesmith/bridge/numpyro_bridge.py +205 -0
- bayesmith-0.1.0/src/bayesmith/diagnose/__init__.py +50 -0
- bayesmith-0.1.0/src/bayesmith/diagnose/identifiability.py +398 -0
- bayesmith-0.1.0/src/bayesmith/diagnose/local.py +332 -0
- bayesmith-0.1.0/src/bayesmith/diagnose/priors.py +422 -0
- bayesmith-0.1.0/src/bayesmith/diagnose/sensitivity.py +816 -0
- bayesmith-0.1.0/src/bayesmith/dispatch/__init__.py +24 -0
- bayesmith-0.1.0/src/bayesmith/dispatch/classify.py +623 -0
- bayesmith-0.1.0/src/bayesmith/dispatch/execute.py +834 -0
- bayesmith-0.1.0/src/bayesmith/dispatch/plan.py +843 -0
- bayesmith-0.1.0/src/bayesmith/dispatch/streaming.py +237 -0
- bayesmith-0.1.0/src/bayesmith/errors.py +85 -0
- bayesmith-0.1.0/src/bayesmith/evidence/__init__.py +56 -0
- bayesmith-0.1.0/src/bayesmith/evidence/campaign.py +408 -0
- bayesmith-0.1.0/src/bayesmith/evidence/compress.py +384 -0
- bayesmith-0.1.0/src/bayesmith/evidence/diagnostics.py +177 -0
- bayesmith-0.1.0/src/bayesmith/evidence/factorize.py +281 -0
- bayesmith-0.1.0/src/bayesmith/evidence/sqrtinfo.py +403 -0
- bayesmith-0.1.0/src/bayesmith/exact/__init__.py +53 -0
- bayesmith-0.1.0/src/bayesmith/exact/block.py +456 -0
- bayesmith-0.1.0/src/bayesmith/exact/conditioning.py +118 -0
- bayesmith-0.1.0/src/bayesmith/exact/correct.py +344 -0
- bayesmith-0.1.0/src/bayesmith/exact/discrete.py +246 -0
- bayesmith-0.1.0/src/bayesmith/exact/fisher.py +500 -0
- bayesmith-0.1.0/src/bayesmith/exact/gaussian.py +548 -0
- bayesmith-0.1.0/src/bayesmith/exact/gibbs.py +416 -0
- bayesmith-0.1.0/src/bayesmith/exact/gls.py +646 -0
- bayesmith-0.1.0/src/bayesmith/exact/linearity.py +809 -0
- bayesmith-0.1.0/src/bayesmith/exact/precision.py +451 -0
- bayesmith-0.1.0/src/bayesmith/exact/solve.py +537 -0
- bayesmith-0.1.0/src/bayesmith/graph/__init__.py +1 -0
- bayesmith-0.1.0/src/bayesmith/graph/evaluate.py +177 -0
- bayesmith-0.1.0/src/bayesmith/graph/graph.py +151 -0
- bayesmith-0.1.0/src/bayesmith/graph/nodes.py +180 -0
- bayesmith-0.1.0/src/bayesmith/graph/trace.py +271 -0
- bayesmith-0.1.0/tests/__init__.py +0 -0
- bayesmith-0.1.0/tests/crosscheck/__init__.py +0 -0
- bayesmith-0.1.0/tests/crosscheck/conftest.py +53 -0
- bayesmith-0.1.0/tests/crosscheck/test_bridge.py +345 -0
- bayesmith-0.1.0/tests/crosscheck/test_conditioning.py +145 -0
- bayesmith-0.1.0/tests/crosscheck/test_diagnose_identifiability.py +217 -0
- bayesmith-0.1.0/tests/crosscheck/test_diagnose_jeffreys.py +214 -0
- bayesmith-0.1.0/tests/crosscheck/test_diagnose_sensitivity.py +270 -0
- bayesmith-0.1.0/tests/crosscheck/test_dispatch.py +546 -0
- bayesmith-0.1.0/tests/crosscheck/test_gaussian.py +584 -0
- bayesmith-0.1.0/tests/crosscheck/test_linear.py +816 -0
- bayesmith-0.1.0/tests/crosscheck/test_noise_logdet.py +333 -0
- bayesmith-0.1.0/tests/crosscheck/test_parameters.py +475 -0
- bayesmith-0.1.0/tests/crosscheck/test_sqrtinfo_agrees.py +177 -0
- bayesmith-0.1.0/tests/diagnose/__init__.py +0 -0
- bayesmith-0.1.0/tests/diagnose/models.py +370 -0
- bayesmith-0.1.0/tests/diagnose/test_identifiability.py +865 -0
- bayesmith-0.1.0/tests/diagnose/test_jeffreys.py +496 -0
- bayesmith-0.1.0/tests/diagnose/test_prior_sensitivity.py +605 -0
- bayesmith-0.1.0/tests/dispatch/__init__.py +0 -0
- bayesmith-0.1.0/tests/dispatch/test_acceptance.py +1240 -0
- bayesmith-0.1.0/tests/dispatch/test_chain_diagnostics.py +254 -0
- bayesmith-0.1.0/tests/dispatch/test_classify.py +656 -0
- bayesmith-0.1.0/tests/dispatch/test_dispatch_entry.py +1026 -0
- bayesmith-0.1.0/tests/dispatch/test_plan.py +734 -0
- bayesmith-0.1.0/tests/dispatch/test_streaming.py +390 -0
- bayesmith-0.1.0/tests/evidence/__init__.py +0 -0
- bayesmith-0.1.0/tests/evidence/test_campaign.py +416 -0
- bayesmith-0.1.0/tests/evidence/test_compress.py +239 -0
- bayesmith-0.1.0/tests/evidence/test_diagnostics.py +266 -0
- bayesmith-0.1.0/tests/evidence/test_epoch_fold.py +311 -0
- bayesmith-0.1.0/tests/evidence/test_factorize.py +469 -0
- bayesmith-0.1.0/tests/evidence/test_sqrtinfo.py +448 -0
- bayesmith-0.1.0/tests/evidence/test_streaming_equals_batch.py +198 -0
- bayesmith-0.1.0/tests/exact/__init__.py +0 -0
- bayesmith-0.1.0/tests/exact/models.py +1773 -0
- bayesmith-0.1.0/tests/exact/oracle.py +145 -0
- bayesmith-0.1.0/tests/exact/test_block.py +357 -0
- bayesmith-0.1.0/tests/exact/test_boundaries.py +394 -0
- bayesmith-0.1.0/tests/exact/test_conditioning.py +123 -0
- bayesmith-0.1.0/tests/exact/test_correct.py +790 -0
- bayesmith-0.1.0/tests/exact/test_discrete.py +267 -0
- bayesmith-0.1.0/tests/exact/test_extremes.py +309 -0
- bayesmith-0.1.0/tests/exact/test_fisher.py +1024 -0
- bayesmith-0.1.0/tests/exact/test_gaussian.py +564 -0
- bayesmith-0.1.0/tests/exact/test_gibbs.py +476 -0
- bayesmith-0.1.0/tests/exact/test_gls.py +1119 -0
- bayesmith-0.1.0/tests/exact/test_linearity.py +818 -0
- bayesmith-0.1.0/tests/exact/test_precision.py +1057 -0
- bayesmith-0.1.0/tests/exact/test_solve.py +1115 -0
- bayesmith-0.1.0/tests/test_bridge.py +141 -0
- bayesmith-0.1.0/tests/test_conjugate_oracle.py +82 -0
- bayesmith-0.1.0/tests/test_degenerate_graphs.py +121 -0
- bayesmith-0.1.0/tests/test_errors.py +80 -0
- bayesmith-0.1.0/tests/test_evaluate.py +95 -0
- bayesmith-0.1.0/tests/test_graph.py +138 -0
- bayesmith-0.1.0/tests/test_log_joint.py +82 -0
- bayesmith-0.1.0/tests/test_nodes.py +275 -0
- bayesmith-0.1.0/tests/test_plates.py +145 -0
- bayesmith-0.1.0/tests/test_public_api.py +347 -0
- bayesmith-0.1.0/tests/test_trace.py +264 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-08-26
|
|
4
|
+
|
|
5
|
+
First release. Published so that downstream packages can declare a dependency
|
|
6
|
+
on it by name rather than by path; until now the version was `0.0.0` and the
|
|
7
|
+
package was not on any index.
|
|
8
|
+
|
|
9
|
+
### What it does
|
|
10
|
+
|
|
11
|
+
- **Graph core.** Deterministic and probabilistic nodes, plates, and the joint
|
|
12
|
+
log-density assembled from them.
|
|
13
|
+
- **NumPyro bridge.** Any graph is runnable through NUTS, which is also the
|
|
14
|
+
oracle every exact path is verified against.
|
|
15
|
+
- **Structural dispatch** with the linear-Gaussian exact solves: conjugate,
|
|
16
|
+
Wiener, GCR and GLS, selected per subgraph, with declarations such as
|
|
17
|
+
`linear_in` checked at three scales rather than trusted.
|
|
18
|
+
- **Exact enumeration of discrete latents** (`bayesmith.exact.discrete`),
|
|
19
|
+
reading the `Discrete(n)` support declaration. Not yet dispatcher-selected —
|
|
20
|
+
see the README's Status section.
|
|
21
|
+
- **Streaming evidence** as square-root information factors, combined exactly
|
|
22
|
+
across epochs.
|
|
23
|
+
- **Graph diagnostics**: identifiability, prior sensitivity, linearity.
|
|
24
|
+
- **Per-parameter convergence diagnostics** on chain paths: split r-hat and ESS
|
|
25
|
+
per coordinate, gated ESS-first because a fixed r-hat threshold is not a
|
|
26
|
+
well-posed test — see `r_hat_ceiling`'s docstring for the measurements.
|
|
27
|
+
|
|
28
|
+
### Known limits
|
|
29
|
+
|
|
30
|
+
- Forward-backward for chain-structured discrete latents is not implemented.
|
|
31
|
+
- Discrete enumeration is not yet chosen by the dispatcher.
|
|
32
|
+
- The API may move; this is an alpha release.
|
bayesmith-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zheng Zhang
|
|
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.
|
bayesmith-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bayesmith
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A graph of operators is a Bayesian model; its structure chooses the inference.
|
|
5
|
+
Project-URL: Repository, https://github.com/zzhang0123/bayesmith
|
|
6
|
+
Author: Zheng Zhang
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: equinox>=0.13
|
|
17
|
+
Requires-Dist: jax>=0.5
|
|
18
|
+
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: numpyro>=0.15
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# bayesmith
|
|
23
|
+
|
|
24
|
+
A Bayesian model is a graph of operators. Deterministic operators propagate
|
|
25
|
+
dependence; probabilistic operators contribute a conditional density. Together
|
|
26
|
+
they *are* the joint distribution.
|
|
27
|
+
|
|
28
|
+
bayesmith makes that graph **explicit and inspectable**, and then uses its
|
|
29
|
+
structure to choose how the model is fitted — an exact solve where the structure
|
|
30
|
+
permits one, NUTS where it does not.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
block 0 {x} Wiener exact (linear_in checked, 3 scales)
|
|
34
|
+
block 1 {z} enumerate 4 states
|
|
35
|
+
block 2 {sigma, nu} NUTS (numpyro) no exact structure found
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The model tells you how it will be fitted, before it is fitted.
|
|
39
|
+
|
|
40
|
+
## What bayesmith is not
|
|
41
|
+
|
|
42
|
+
It is **not another probabilistic programming language**. Distributions, MCMC
|
|
43
|
+
kernels, variational inference and transforms all come from
|
|
44
|
+
[NumPyro](https://github.com/pyro-ppl/numpyro). bayesmith is the dispatch layer
|
|
45
|
+
above them, and every line in it must answer *"why can NumPyro not do this?"*
|
|
46
|
+
|
|
47
|
+
What it owns, because a trace-based PPL structurally cannot:
|
|
48
|
+
|
|
49
|
+
- **Structural exact inference** — conjugate / Wiener / GCR / GLS solves, and
|
|
50
|
+
exact enumeration of discrete latents, selected per subgraph.
|
|
51
|
+
- **Streaming evidence** — square-root information factors combined exactly
|
|
52
|
+
across datasets and observing epochs.
|
|
53
|
+
- **Diagnostics on the graph** — identifiability, prior sensitivity, and
|
|
54
|
+
linearity checking of the declarations the dispatcher relies on.
|
|
55
|
+
|
|
56
|
+
Declarations such as `linear_in` are *claims about the model*, not hints, so
|
|
57
|
+
they are **checked rather than trusted**: a node declared linear is probed at
|
|
58
|
+
three scales before any exact solve is allowed to use it.
|
|
59
|
+
|
|
60
|
+
## Status
|
|
61
|
+
|
|
62
|
+
**0.1.0, the first release.** Published so other packages can depend on it by
|
|
63
|
+
name. Alpha in the classifier's sense: the API may still move.
|
|
64
|
+
|
|
65
|
+
Implemented and tested, 1163 tests: the graph core with plates and joint
|
|
66
|
+
log-density; the NumPyro bridge, so any graph is runnable through NUTS;
|
|
67
|
+
structural dispatch with the linear-Gaussian exact solves; exact enumeration of
|
|
68
|
+
discrete latents; streaming evidence as square-root information factors; and
|
|
69
|
+
graph diagnostics for identifiability, prior sensitivity and linearity.
|
|
70
|
+
|
|
71
|
+
**Two things the page above describes that 0.1.0 does not do yet.** Stated here
|
|
72
|
+
because a front page is a claim, and finding out afterwards is worse than
|
|
73
|
+
reading it now:
|
|
74
|
+
|
|
75
|
+
- **Enumeration is not dispatcher-selected.** `bayesmith.exact.discrete`
|
|
76
|
+
computes the exact marginal and the posterior marginals over declared
|
|
77
|
+
discrete latents, and reads the `Discrete(n)` support declaration to do it —
|
|
78
|
+
but `classify` does not yet route a discrete subgraph to it. The
|
|
79
|
+
`block 1 {z} enumerate 4 states` line above is therefore a design sketch
|
|
80
|
+
rather than a transcript; call the module directly.
|
|
81
|
+
- **Forward-backward is not implemented**, so a chain of `T` discrete latents
|
|
82
|
+
costs `n ** T` by enumeration rather than `T * n**2`. Enumeration refuses
|
|
83
|
+
past a budget rather than hanging, and names the count it would have visited.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# bayesmith
|
|
2
|
+
|
|
3
|
+
A Bayesian model is a graph of operators. Deterministic operators propagate
|
|
4
|
+
dependence; probabilistic operators contribute a conditional density. Together
|
|
5
|
+
they *are* the joint distribution.
|
|
6
|
+
|
|
7
|
+
bayesmith makes that graph **explicit and inspectable**, and then uses its
|
|
8
|
+
structure to choose how the model is fitted — an exact solve where the structure
|
|
9
|
+
permits one, NUTS where it does not.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
block 0 {x} Wiener exact (linear_in checked, 3 scales)
|
|
13
|
+
block 1 {z} enumerate 4 states
|
|
14
|
+
block 2 {sigma, nu} NUTS (numpyro) no exact structure found
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The model tells you how it will be fitted, before it is fitted.
|
|
18
|
+
|
|
19
|
+
## What bayesmith is not
|
|
20
|
+
|
|
21
|
+
It is **not another probabilistic programming language**. Distributions, MCMC
|
|
22
|
+
kernels, variational inference and transforms all come from
|
|
23
|
+
[NumPyro](https://github.com/pyro-ppl/numpyro). bayesmith is the dispatch layer
|
|
24
|
+
above them, and every line in it must answer *"why can NumPyro not do this?"*
|
|
25
|
+
|
|
26
|
+
What it owns, because a trace-based PPL structurally cannot:
|
|
27
|
+
|
|
28
|
+
- **Structural exact inference** — conjugate / Wiener / GCR / GLS solves, and
|
|
29
|
+
exact enumeration of discrete latents, selected per subgraph.
|
|
30
|
+
- **Streaming evidence** — square-root information factors combined exactly
|
|
31
|
+
across datasets and observing epochs.
|
|
32
|
+
- **Diagnostics on the graph** — identifiability, prior sensitivity, and
|
|
33
|
+
linearity checking of the declarations the dispatcher relies on.
|
|
34
|
+
|
|
35
|
+
Declarations such as `linear_in` are *claims about the model*, not hints, so
|
|
36
|
+
they are **checked rather than trusted**: a node declared linear is probed at
|
|
37
|
+
three scales before any exact solve is allowed to use it.
|
|
38
|
+
|
|
39
|
+
## Status
|
|
40
|
+
|
|
41
|
+
**0.1.0, the first release.** Published so other packages can depend on it by
|
|
42
|
+
name. Alpha in the classifier's sense: the API may still move.
|
|
43
|
+
|
|
44
|
+
Implemented and tested, 1163 tests: the graph core with plates and joint
|
|
45
|
+
log-density; the NumPyro bridge, so any graph is runnable through NUTS;
|
|
46
|
+
structural dispatch with the linear-Gaussian exact solves; exact enumeration of
|
|
47
|
+
discrete latents; streaming evidence as square-root information factors; and
|
|
48
|
+
graph diagnostics for identifiability, prior sensitivity and linearity.
|
|
49
|
+
|
|
50
|
+
**Two things the page above describes that 0.1.0 does not do yet.** Stated here
|
|
51
|
+
because a front page is a claim, and finding out afterwards is worse than
|
|
52
|
+
reading it now:
|
|
53
|
+
|
|
54
|
+
- **Enumeration is not dispatcher-selected.** `bayesmith.exact.discrete`
|
|
55
|
+
computes the exact marginal and the posterior marginals over declared
|
|
56
|
+
discrete latents, and reads the `Discrete(n)` support declaration to do it —
|
|
57
|
+
but `classify` does not yet route a discrete subgraph to it. The
|
|
58
|
+
`block 1 {z} enumerate 4 states` line above is therefore a design sketch
|
|
59
|
+
rather than a transcript; call the module directly.
|
|
60
|
+
- **Forward-backward is not implemented**, so a chain of `T` discrete latents
|
|
61
|
+
costs `n ** T` by enumeration rather than `T * n**2`. Enumeration refuses
|
|
62
|
+
past a budget rather than hanging, and names the count it would have visited.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Cross-check records — and what §六 is still waiting for
|
|
2
|
+
|
|
3
|
+
Migration spec §二 requires one page per module and gates **all** of §六
|
|
4
|
+
(rheplicant's wind-down) on step 6: *"全部通过后,才动 rheplicant 侧对应模
|
|
5
|
+
块"*. This directory is where those pages live. It did not exist until
|
|
6
|
+
2026-08-25, which is why §六 had never started.
|
|
7
|
+
|
|
8
|
+
**Do not read the table below as authority.** It is prose, and prose has no
|
|
9
|
+
test. `tests/test_migration_records.py` is the authority: it
|
|
10
|
+
derives the module list from the spec's own §四 tables, the page list from
|
|
11
|
+
this directory, and the test list from `tests/crosscheck/`, and fails when
|
|
12
|
+
they disagree. If the table and that test ever disagree, the test is right.
|
|
13
|
+
|
|
14
|
+
| §四 row | module | cross-check test | page |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| 4.1 | `linear.py` → `exact/block,linearity,solve` | `test_linear.py` | ✅ |
|
|
17
|
+
| 4.1* | `conditioning.py` → `exact/conditioning` | `test_conditioning.py` | ✅ |
|
|
18
|
+
| 4.1 | `gls.py` → `exact/gls` | `test_noise_logdet.py` (B1 half) | ✅ |
|
|
19
|
+
| 4.1 | `uncertainty.py` (Fisher) → `exact/fisher` | `test_noise_logdet.py` | ✅ |
|
|
20
|
+
| 4.1 | `likelihood.py`/`noise.py` → `exact/gaussian` | `test_gaussian.py` | ✅ `noise.md` |
|
|
21
|
+
| 4.2 | `parameters.py` → node declarations | `test_parameters.py` | ✅ |
|
|
22
|
+
| 4.2 | `noise.py` → probabilistic nodes | `test_gaussian.py` | ✅ `noise.md` |
|
|
23
|
+
| 4.2 | `plan.py`+`engines.py` → dispatch | `test_dispatch.py` | ✅ `plan.md` |
|
|
24
|
+
| 4.2 | `identifiability.py` → `diagnose/` | `test_diagnose_identifiability.py` | ✅ |
|
|
25
|
+
| 4.2 | `sensitivity.py` → `diagnose/` | `test_diagnose_sensitivity.py` | ✅ |
|
|
26
|
+
| 4.2 | `priors.py` → `diagnose/` | `test_diagnose_jeffreys.py` | ✅ |
|
|
27
|
+
| 4.2 | `numpyro_bridge.py` → `bridge/` | — | — |
|
|
28
|
+
| 4.3* | `sqrtinfo` (rewritten; kernel preserved per B11) | `test_sqrtinfo_agrees.py` | ✅ |
|
|
29
|
+
|
|
30
|
+
`*` — has a page but **no source row of its own** in §四, and the test
|
|
31
|
+
records why. `conditioning.py` appears only in the `linear.py` row's
|
|
32
|
+
DESTINATION cell (upstream moved it to `rheplicant.core` so `radio` could
|
|
33
|
+
use it without importing `inference`); `sqrtinfo` belongs to the evidence
|
|
34
|
+
layer, which §四 4.3 marks 不迁移 — rewritten under iron law 2, with its
|
|
35
|
+
numerical kernel required to be preserved exactly and a bitwise
|
|
36
|
+
cross-check enforcing it.
|
|
37
|
+
|
|
38
|
+
## Where the gate actually stands
|
|
39
|
+
|
|
40
|
+
**Open, since 2026-08-25.** Twelve pages exist and **every §四 module has
|
|
41
|
+
one**. `tests/test_migration_records.py::test_the_gate_on_section_six_is_open_and_every_module_has_a_page`
|
|
42
|
+
now asserts that in the other direction: while the list was non-empty it
|
|
43
|
+
blocked §六, and now that §六's steps are being taken against these pages,
|
|
44
|
+
a page may not go missing.
|
|
45
|
+
|
|
46
|
+
| §四 row | closed |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `linear.py` → `exact/{block,linearity,solve}` | `linear.md` |
|
|
49
|
+
| `likelihood.py`/`noise.py` → `exact/gaussian` | `noise.md` |
|
|
50
|
+
| `gls.py`, `uncertainty.py` (Fisher) | `gls.md`, `uncertainty.md` |
|
|
51
|
+
| `parameters.py` → node declarations | `parameters.md` |
|
|
52
|
+
| `noise.py` → probabilistic nodes | `noise.md` |
|
|
53
|
+
| `plan.py`+`engines.py` → dispatch | `plan.md` |
|
|
54
|
+
| `identifiability.py`, `sensitivity.py`, `priors.py` → `diagnose/` | three pages |
|
|
55
|
+
| `numpyro_bridge.py` → `bridge/` | `numpyro_bridge.md` |
|
|
56
|
+
| *(out of ledger)* `conditioning.py`, `sqrtinfo` | two pages, with their reasons |
|
|
57
|
+
|
|
58
|
+
**What that does and does not authorise.** §六 step 1 still governs:
|
|
59
|
+
nothing in `src/rheplicant/inference/` moves except the two exceptions
|
|
60
|
+
already in e-RHINO's Track A Batch 1 (B1's `plan.py` docstring and B4's
|
|
61
|
+
one-line fix), plus docstring pointers to bayesmith. Read §六's five steps
|
|
62
|
+
before starting, and note step 3's possible dividend — the two pytest
|
|
63
|
+
sessions may be able to merge once the evidence layer is out.
|
|
64
|
+
|
|
65
|
+
## What a page must contain
|
|
66
|
+
|
|
67
|
+
The five headings §二 names, in its order. The P5 pages are the worked
|
|
68
|
+
examples.
|
|
69
|
+
|
|
70
|
+
**One thing NOT to write: a case count, unless the module is finished.**
|
|
71
|
+
Some pages here carry one and some do not, and the asymmetry is a
|
|
72
|
+
measurement rather than an oversight. The P5 pages' counts (17, 17, 10)
|
|
73
|
+
were written in session 3 and are still exact, because nothing touched
|
|
74
|
+
those modules afterwards. `plan.md`'s said 6 and was 7 within hours,
|
|
75
|
+
because its author kept working on the module after writing the page. So
|
|
76
|
+
the rule is about timing, not taste: a count is a safe thing to record only
|
|
77
|
+
once the thing counted has stopped moving, and the four pages written
|
|
78
|
+
during active work say `pytest --collect-only -q` instead. Nothing in this
|
|
79
|
+
repository reads a count out of a page, so none of them can go red.
|
|
80
|
+
|
|
81
|
+
The five headings:
|
|
82
|
+
|
|
83
|
+
1. **Fixtures** — reused from rheplicant where possible (they are pinned
|
|
84
|
+
measurements, not fresh guesses), at least one healthy and one that must
|
|
85
|
+
be refused.
|
|
86
|
+
2. **Numerical agreement** — deterministic exits to float64 roundoff,
|
|
87
|
+
sampled exits within MC error. Say which tolerance and why; where
|
|
88
|
+
exactness is not mathematically available (a null direction is a ray, a
|
|
89
|
+
null space is basis-dependent), say what is compared instead.
|
|
90
|
+
3. **Refusal agreement** — every loud rheplicant refusal has a same-shape
|
|
91
|
+
refusal here, with the exception-class mapping recorded.
|
|
92
|
+
4. **Independent oracle** (iron law 4) — analytic truth, a hand-written
|
|
93
|
+
NumPyro model, scipy, or mutation. *Two implementations agreeing is not
|
|
94
|
+
evidence.*
|
|
95
|
+
5. **Intended differences** — with the reason and the equivalence argument.
|
|
96
|
+
A difference that is deliberate and unrecorded is indistinguishable from
|
|
97
|
+
a bug six months later.
|
|
98
|
+
|
|
99
|
+
Iron law 5 adds one hard item: any §三 defect belonging to the module is
|
|
100
|
+
fixed **before** step 2, or written as a signed, sized expected difference.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Cross-check: `conditioning`
|
|
2
|
+
|
|
3
|
+
`rheplicant.core.conditioning` (moved there from `inference/` upstream when
|
|
4
|
+
`radio` needed it and could not import `inference`) →
|
|
5
|
+
`bayesmith.exact.conditioning`. Test: `tests/crosscheck/test_conditioning.py`
|
|
6
|
+
(9 cases). Page written 2026-08-25 from that test's assertions, re-run on
|
|
7
|
+
that date; the cross-check itself predates this page.
|
|
8
|
+
|
|
9
|
+
## 1. Fixtures
|
|
10
|
+
|
|
11
|
+
A symmetric operator with an EXACTLY known spectrum, built as
|
|
12
|
+
`Q diag(λ) Qᵀ` from a QR of a fixed-seed normal matrix and handed to both
|
|
13
|
+
packages as the same callable — so a difference is the algorithm and never
|
|
14
|
+
the setup. Spectra: `[1,2,3]`, `[1e-6,1,5,5.5]`, six-fold degenerate
|
|
15
|
+
`[1]*6`, and a 20-point geometric spectrum with κ = 1e4.
|
|
16
|
+
|
|
17
|
+
For `tree_norm`, three pytrees including the **overflow** case both module
|
|
18
|
+
docstrings argue about: `{a: [1e20, 1e20], b: [0]}`. Squaring first turns
|
|
19
|
+
1e20 into `inf`, and the scaling that avoids it is the entire content of
|
|
20
|
+
the function — a port that dropped it agrees on the ordinary row and
|
|
21
|
+
disagrees here.
|
|
22
|
+
|
|
23
|
+
## 2. Numerical agreement
|
|
24
|
+
|
|
25
|
+
| quantity | agreement |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `tree_norm`, all three trees | **exact float equality** |
|
|
28
|
+
| `largest_eigenvalue` (same operator, template, key, 12 iterations) | **exact float equality** |
|
|
29
|
+
|
|
30
|
+
Equality rather than a tolerance, because it is the same arithmetic in the
|
|
31
|
+
same order on the same library; a tolerance would let a genuinely different
|
|
32
|
+
iteration pass, which is what this file exists to notice.
|
|
33
|
+
|
|
34
|
+
## 3. Refusal agreement
|
|
35
|
+
|
|
36
|
+
Neither module refuses anything — these are numerical estimators, not
|
|
37
|
+
gates. The refusal that *consumes* them (`condition_bound`'s κ ceiling)
|
|
38
|
+
belongs to `solve.py` and is checked in `tests/exact/`.
|
|
39
|
+
|
|
40
|
+
## 4. Independent oracle
|
|
41
|
+
|
|
42
|
+
`jnp.linalg.eigvalsh` of the materialised matrix. Both packages'
|
|
43
|
+
`largest_eigenvalue` must approach the true λ_max **from below** (within
|
|
44
|
+
1e-5 above, and within 1e-3 relative) — "agreeing on a wrong number is
|
|
45
|
+
still agreement", so the shared claim is checked against truth as well as
|
|
46
|
+
against each other.
|
|
47
|
+
|
|
48
|
+
## 5. Intended differences — one, and it is the reason the harness exists
|
|
49
|
+
|
|
50
|
+
**`extreme_eigenvalues` was deliberately NOT ported.** rheplicant estimates
|
|
51
|
+
λ_min by running a second power iteration on `λ_max·I − M`, which on a
|
|
52
|
+
graded spectrum cannot separate the eigenvalues crowded against λ_max — and
|
|
53
|
+
the error is **one-sided and toward danger**: λ_min comes back too LARGE,
|
|
54
|
+
so κ too SMALL, so a convergence guard built on it is silent exactly when
|
|
55
|
+
it should fire. Measured upstream at κ = 1e4: λ_min over-large by 33.9×,
|
|
56
|
+
reported κ = 2.947e+02 against a true 1.000e+04.
|
|
57
|
+
|
|
58
|
+
bayesmith bounds λ_min below by the prior's own curvature instead
|
|
59
|
+
(`exact/solve.py::condition_bound`), giving an UPPER bound on κ — the
|
|
60
|
+
direction a safety guard needs.
|
|
61
|
+
|
|
62
|
+
Both halves are live tests, not prose:
|
|
63
|
+
|
|
64
|
+
- `test_bayesmith_does_not_carry_extreme_eigenvalues` goes red if someone
|
|
65
|
+
ports it after all, and points at the docstring that rejected it.
|
|
66
|
+
- `test_rheplicant_still_carries_it_and_still_leans_the_unsafe_way`
|
|
67
|
+
asserts the bias as an **inequality against the truth** (λ_min > 10×
|
|
68
|
+
true), so it survives an iteration-count retune. When rheplicant fixes
|
|
69
|
+
this, that test goes red and should be deleted together with the
|
|
70
|
+
paragraph it guards.
|
|
71
|
+
|
|
72
|
+
This is a **Track A item for e-RHINO, not this migration** — recorded here
|
|
73
|
+
because it is the first time the harness's reason for existing paid out: a
|
|
74
|
+
manual comparison holds only on the day it is written.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Cross-check: `gls` — and the B1 log-determinant ledger
|
|
2
|
+
|
|
3
|
+
`rheplicant.inference.gls` (`iterative_gls`) → `bayesmith.exact.gls`
|
|
4
|
+
(§四 4.1). Test: `tests/crosscheck/test_noise_logdet.py` (17 cases, shared
|
|
5
|
+
with the Fisher row — see [uncertainty.md](uncertainty.md)) plus
|
|
6
|
+
`tests/exact/test_gls.py::test_the_fixed_point_is_the_unbiased_estimator_
|
|
7
|
+
not_the_gls_biased_one` on this side. Page written 2026-08-25 from those
|
|
8
|
+
tests' assertions, re-run on that date; the cross-checks predate this page.
|
|
9
|
+
|
|
10
|
+
**This page is unusual: its subject is a difference, not an agreement.**
|
|
11
|
+
§三 B1 is a defect in rheplicant, and iron law 5 forbids aligning a new
|
|
12
|
+
implementation to a defective one. So the acceptance is a *signed, sized*
|
|
13
|
+
expected difference on one side and a proof of correctness on the other.
|
|
14
|
+
|
|
15
|
+
## 1. Fixtures
|
|
16
|
+
|
|
17
|
+
A constant-mean model under `RadiometerNoise`, so both estimators have a
|
|
18
|
+
closed form and nothing samples: `f ∈ {0.05, 0.2, 0.5}`, `n = 2000` for the
|
|
19
|
+
closed-form rows and `n = 200 000` for the asymptotic one (measured: at
|
|
20
|
+
f=0.5 the ratio lands 0.024 % from `1+f²` at n=200 000, against 2.5 % at
|
|
21
|
+
n=2000). A `HomoscedasticNoise` row is the anti-vacuity control.
|
|
22
|
+
|
|
23
|
+
## 2. Numerical agreement — of each estimator with its own closed form
|
|
24
|
+
|
|
25
|
+
| claim | agreement |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `include_logdet=False` maximiser = `Σd²/Σd` | rel **1e-8** |
|
|
28
|
+
| `include_logdet=True` maximiser = positive root of `n f² μ² + μ Σd − Σd² = 0` | rel **1e-8** |
|
|
29
|
+
| ratio of the two = `1 + f²` | rel **2e-3**, at three f |
|
|
30
|
+
| the full density's estimate = the truth | rel **5e-3** |
|
|
31
|
+
|
|
32
|
+
The quadratic's root is derived in the test rather than taken from
|
|
33
|
+
rheplicant's docstring, because the asymptotic claim rests on it: it is
|
|
34
|
+
what makes the full density **exactly unbiased** rather than differently
|
|
35
|
+
biased, and a test asserting only "closer to the truth" would pass on an
|
|
36
|
+
estimator that was merely less wrong.
|
|
37
|
+
|
|
38
|
+
The maximiser is found by **scipy on a Python closure** over rheplicant's
|
|
39
|
+
likelihood — no JAX gradient, no algebra of ours. A closed form checked
|
|
40
|
+
against its own rearrangement would be checking arithmetic, not the
|
|
41
|
+
estimator.
|
|
42
|
+
|
|
43
|
+
## 3. Refusal agreement
|
|
44
|
+
|
|
45
|
+
None to compare: neither package refuses the mixture. That is the defect —
|
|
46
|
+
B1's point is that rheplicant's `nuts` route (whose `dist.Normal` carries
|
|
47
|
+
its own `−log σ`) and its `plan.sample` gradient block (which does not)
|
|
48
|
+
target different estimators on one model, **with no guard between them**,
|
|
49
|
+
while the same package's evidence layer raises full-versus-GLS to a
|
|
50
|
+
refusal.
|
|
51
|
+
|
|
52
|
+
## 4. Independent oracles
|
|
53
|
+
|
|
54
|
+
- The two closed forms above (algebra, not either implementation).
|
|
55
|
+
- scipy's optimiser, sharing nothing with either package's gradients.
|
|
56
|
+
- **Anti-vacuity**: under `HomoscedasticNoise` the log-determinant is an
|
|
57
|
+
additive constant and the two estimators must **coincide** — so the gap
|
|
58
|
+
is attributed to the prediction-dependence and not to some other
|
|
59
|
+
difference between the two likelihood spellings.
|
|
60
|
+
- The ratio is checked across an f that varies the answer by a factor of
|
|
61
|
+
25 between its ends, so a constant fudge cannot satisfy it; and the SIGN
|
|
62
|
+
is asserted separately, because it is the half that says which engine is
|
|
63
|
+
the optimistic one (`gls > full > 0`).
|
|
64
|
+
|
|
65
|
+
## 5. Intended differences — the whole point of the row
|
|
66
|
+
|
|
67
|
+
**bayesmith's `iterative_gls` does NOT carry this bias, and must not be
|
|
68
|
+
made to.** It is frozen-sigma IRLS: each inner solve holds σ fixed and
|
|
69
|
+
recomputes it afterwards, so its fixed point satisfies `w = mean(u)`,
|
|
70
|
+
`u = d/x` — the same side as the full density. Measured: the fixed point is
|
|
71
|
+
44–128× closer to `mean(u)` than to `Σu²/Σu`, at `κ ∈ {0.05, 0.2, 0.5, 1.0}`.
|
|
72
|
+
|
|
73
|
+
The spec's first draft asked for the opposite — that bayesmith's frozen-σ
|
|
74
|
+
path differ from a live-σ path by `(1+f²)` — and building that test would
|
|
75
|
+
have pulled a correct estimator toward a bias it does not have. **The
|
|
76
|
+
initial statement of the acceptance was wrong, and measurement is what
|
|
77
|
+
found it**; the correction is recorded in §三 B1 itself, marked
|
|
78
|
+
`[实测确认]`.
|
|
79
|
+
|
|
80
|
+
Consequence for the pending `plan`/`engines` row (§四 4.2): B1 must land
|
|
81
|
+
first, or that comparison will fix the GLS-type target as the reference.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Cross-check: `identifiability`
|
|
2
|
+
|
|
3
|
+
`rheplicant.inference.identifiability` → `bayesmith.diagnose.identifiability`
|
|
4
|
+
(migration spec §八 step 5, acceptance §四 4.2). Executed 2026-08-25; every
|
|
5
|
+
number below was measured in this repo's venv on that date.
|
|
6
|
+
|
|
7
|
+
## 1. Fixtures
|
|
8
|
+
|
|
9
|
+
Per §二 step 1, rheplicant's own pinned fixtures, re-expressed on the graph
|
|
10
|
+
(`tests/diagnose/models.py`):
|
|
11
|
+
|
|
12
|
+
- **Healthy + degenerate pair**: `data[t,f] = gain[t] * (T_ant[t,f] +
|
|
13
|
+
tone[f])`, 8×8, antenna temperature free-per-cell (72 parameters, the
|
|
14
|
+
under-determined case) or through a (3,3) polynomial basis (17). Same
|
|
15
|
+
coefficient matrix, deliberately asymmetric.
|
|
16
|
+
- **Must-refuse fixtures**: ambient float32; a graph pinning its own
|
|
17
|
+
arithmetic to float32; complex/integer selected latents; empty/unknown/
|
|
18
|
+
repeated names; a graph with no observed node; a Poisson observation
|
|
19
|
+
(no `loc`).
|
|
20
|
+
- Auxiliary: mixed-scale (1e10 unit gap), zero-column (a dead latent),
|
|
21
|
+
sort-trap (declaration order ≠ sorted order).
|
|
22
|
+
|
|
23
|
+
## 2. Numerical agreement
|
|
24
|
+
|
|
25
|
+
`tests/crosscheck/test_diagnose_identifiability.py`, same arrays handed to
|
|
26
|
+
both packages (never the same construction recipe — §0.1's PRNG trap):
|
|
27
|
+
|
|
28
|
+
| quantity | agreement |
|
|
29
|
+
|---|---|
|
|
30
|
+
| (n_par, rank, nullity), all four rows | equal, cell for cell: (72,64,8)×2, (17,17,0), (17,16,1) |
|
|
31
|
+
| singular values (basis/off) | rel 1e-10 |
|
|
32
|
+
| `weakest_identified` | rel 1e-9, both 4.822138e-05 |
|
|
33
|
+
| null direction, nullity 1 | elementwise abs 1e-9, sign-fixed on the largest entry |
|
|
34
|
+
| null space, nullity 8 | as projectors `VᵀV`, abs 1e-9 — rows are basis-dependent, the subspace is not |
|
|
35
|
+
|
|
36
|
+
## 3. Refusal agreement
|
|
37
|
+
|
|
38
|
+
Every loud rheplicant refusal has a same-shape refusal here (empty/unknown/
|
|
39
|
+
repeated names, complex → the R-linear explanation, integer, out-of-range
|
|
40
|
+
direction index, inconsistent report). Exception classes map
|
|
41
|
+
`ParameterSpaceError → GraphError`, `StateValidationError → StructureError`
|
|
42
|
+
— bayesmith's own taxonomy; the shared-identity concern of the design doc's
|
|
43
|
+
appendix applies to rheplicant's importers, not to new code here.
|
|
44
|
+
|
|
45
|
+
**One refusal changed mechanism, deliberately.** rheplicant forces
|
|
46
|
+
process-global x64 *inside* the diagnostic and refuses only a model that
|
|
47
|
+
pins its output to float32. This package's rule is that `src/` never touches
|
|
48
|
+
`jax.config`, so there are TWO refusals: ambient float32 (before any
|
|
49
|
+
tracing — a graph built in x64 and called outside it otherwise dies inside
|
|
50
|
+
`jax.linearize` with a bare dtype inconsistency) and result float32 (the
|
|
51
|
+
graph itself casts). Both are pinned, and the float32 counterfactual is
|
|
52
|
+
computed by hand in the suite to show what they protect.
|
|
53
|
+
|
|
54
|
+
## 4. Independent oracles (iron law 4)
|
|
55
|
+
|
|
56
|
+
- "Moving along the reported direction does not move the model": the
|
|
57
|
+
end-to-end statement, evaluated through the graph itself, against a
|
|
58
|
+
random direction of the same size (< 1e-3 of the random movement), on
|
|
59
|
+
both the over- and under-determined fixtures.
|
|
60
|
+
- The free model's `weakest_identified` = 1/√2 exactly — fixed by the
|
|
61
|
+
fixture's geometry, not by either implementation.
|
|
62
|
+
- The no-normalisation counterfactual: the mixed-scale fixture's raw
|
|
63
|
+
spectrum ratio is measured (1.000000e-10) and shown to sit below the
|
|
64
|
+
default tolerance.
|
|
65
|
+
- 5 of the 17 hand-applied mutations in the port's mutation pass targeted
|
|
66
|
+
this module; all killed.
|
|
67
|
+
|
|
68
|
+
## 5. Intended differences
|
|
69
|
+
|
|
70
|
+
1. **The x64 mechanism** (above). Consequence: `DEFAULT_RANK_RTOL = 1e-8`
|
|
71
|
+
was **re-measured, not ported**, per the spec's recorded trap. Under
|
|
72
|
+
this regime: null direction 7.479266e-17 (upstream arithmetic 6.6e-17 —
|
|
73
|
+
the spectrum moved with evaluation order, the verdict did not), weakest
|
|
74
|
+
identified 4.822138e-05 (identical to every printed digit), float32
|
|
75
|
+
null direction 3.116759e-08 (upstream pinned 3.1168e-8). The window and
|
|
76
|
+
the constant survive; the justification now cites this side's numbers.
|
|
77
|
+
2. **Signature**: `(space, pipeline, state_template)` → `graph`. `at`
|
|
78
|
+
defaults to prior centres (`prior_environment`, the dispatch layer's own
|
|
79
|
+
anchoring rule) rather than declared inits — the same point, one
|
|
80
|
+
spelling.
|
|
81
|
+
3. **The Jacobian's row layout** is `dense_operator`'s (observed nodes in
|
|
82
|
+
sorted name order, flattened); rheplicant's is a single prediction
|
|
83
|
+
array. Irrelevant to the rank and the null space; recorded because the
|
|
84
|
+
`jacobian` field is public.
|
|
85
|
+
4. rheplicant's raw-`Bind`-space and x64-subprocess tests do not port
|
|
86
|
+
(no such concepts here); the refusal-mechanism family replaces them.
|