views-frames 1.8.1__tar.gz → 1.9.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.
Files changed (44) hide show
  1. {views_frames-1.8.1 → views_frames-1.9.0}/.gitignore +3 -0
  2. {views_frames-1.8.1 → views_frames-1.9.0}/PKG-INFO +6 -3
  3. {views_frames-1.8.1 → views_frames-1.9.0}/README.md +5 -2
  4. {views_frames-1.8.1 → views_frames-1.9.0}/pyproject.toml +1 -1
  5. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/config.py +9 -3
  6. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/conformance.py +26 -0
  7. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/tower_point.py +9 -3
  8. {views_frames-1.8.1 → views_frames-1.9.0}/LICENSE +0 -0
  9. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/__init__.py +0 -0
  10. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/_typing.py +0 -0
  11. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/_validation.py +0 -0
  12. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/conformance/__init__.py +0 -0
  13. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/feature_frame.py +0 -0
  14. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/index.py +0 -0
  15. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/io/__init__.py +0 -0
  16. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/io/arrow.py +0 -0
  17. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/io/npz.py +0 -0
  18. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/metadata.py +0 -0
  19. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/prediction_frame.py +0 -0
  20. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/protocols.py +0 -0
  21. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/py.typed +0 -0
  22. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/spatial_level.py +0 -0
  23. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames/target_frame.py +0 -0
  24. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/__init__.py +0 -0
  25. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/conformance.py +0 -0
  26. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/frames.py +0 -0
  27. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/grouping.py +0 -0
  28. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/module.py +0 -0
  29. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/proportional.py +0 -0
  30. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/py.typed +0 -0
  31. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/result.py +0 -0
  32. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_reconcile/validation.py +0 -0
  33. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/__init__.py +0 -0
  34. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/_common.py +0 -0
  35. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/aggregate.py +0 -0
  36. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/bimodality.py +0 -0
  37. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/collapse.py +0 -0
  38. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/exceedance.py +0 -0
  39. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/expected_shortfall.py +0 -0
  40. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/interval.py +0 -0
  41. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/point.py +0 -0
  42. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/py.typed +0 -0
  43. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/summarize_tower.py +0 -0
  44. {views_frames-1.8.1 → views_frames-1.9.0}/src/views_frames_summarize/tower.py +0 -0
@@ -44,3 +44,6 @@ research/**/.cache/
44
44
  research/**/results.tsv
45
45
  research/**/run.log
46
46
  research/**/audit_plots/
47
+
48
+ # generated figures (research/figures/ generators; deliverable copies travel with the deliverable)
49
+ reports/plots/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: views-frames
3
- Version: 1.8.1
3
+ Version: 1.9.0
4
4
  Summary: The VIEWS platform data-contract layer: immutable array+identifier frames (numpy only, root of the dependency DAG).
5
5
  Project-URL: Homepage, https://github.com/views-platform/views-frames
6
6
  Project-URL: Repository, https://github.com/views-platform/views-frames
@@ -36,7 +36,7 @@ Description-Content-Type: text/markdown
36
36
  > containers (`FeatureFrame`, `PredictionFrame`, and their anticipated siblings)
37
37
  > that every other repo depends on and that depends on nothing internal.
38
38
  >
39
- > **Status:** **v1.8.0 — frozen API, published to PyPI** (frozen since v1.0.0, ADR-018; the
39
+ > **Status:** **v1.9.0 — frozen API, published to PyPI** (frozen since v1.0.0, ADR-018; the
40
40
  > v1.1 surface is
41
41
  > purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilt the tower
42
42
  > `outside-in`, C-44; v1.3.0 makes the tower summary distribution-agnostic — no magnitude
@@ -47,7 +47,10 @@ Description-Content-Type: text/markdown
47
47
  > sibling package **`views_frames_reconcile`** — forecast reconciliation, ADR-023; v1.8.0
48
48
  > adds the native point-country broadcast + the self-describing reconciliation mode,
49
49
  > the three showcase notebooks, and the frames-family hardening pass — Reconcile.md CIC,
50
- > ADR-025 immutability-by-convention, the adversarial red-test batch). This
50
+ > ADR-025 immutability-by-convention, the adversarial red-test batch; v1.9.0 moves the
51
+ > tower-tip MAP to the **top floor** of the published tower — `tip_mass` 0.25 with the
52
+ > **MAP-containment law** in the conformance suite, ADR-019 Amendment 3 — and adds the
53
+ > `research/figures/` tower-figure toolkit). This
51
54
  > README is the design
52
55
  > bible; the contract it specifies is realised in `src/views_frames/` (index, frames,
53
56
  > io, conformance suite) plus the `src/views_frames_summarize/` sibling package
@@ -4,7 +4,7 @@
4
4
  > containers (`FeatureFrame`, `PredictionFrame`, and their anticipated siblings)
5
5
  > that every other repo depends on and that depends on nothing internal.
6
6
  >
7
- > **Status:** **v1.8.0 — frozen API, published to PyPI** (frozen since v1.0.0, ADR-018; the
7
+ > **Status:** **v1.9.0 — frozen API, published to PyPI** (frozen since v1.0.0, ADR-018; the
8
8
  > v1.1 surface is
9
9
  > purely additive — the coherent posterior summary, ADR-019; v1.2.0 rebuilt the tower
10
10
  > `outside-in`, C-44; v1.3.0 makes the tower summary distribution-agnostic — no magnitude
@@ -15,7 +15,10 @@
15
15
  > sibling package **`views_frames_reconcile`** — forecast reconciliation, ADR-023; v1.8.0
16
16
  > adds the native point-country broadcast + the self-describing reconciliation mode,
17
17
  > the three showcase notebooks, and the frames-family hardening pass — Reconcile.md CIC,
18
- > ADR-025 immutability-by-convention, the adversarial red-test batch). This
18
+ > ADR-025 immutability-by-convention, the adversarial red-test batch; v1.9.0 moves the
19
+ > tower-tip MAP to the **top floor** of the published tower — `tip_mass` 0.25 with the
20
+ > **MAP-containment law** in the conformance suite, ADR-019 Amendment 3 — and adds the
21
+ > `research/figures/` tower-figure toolkit). This
19
22
  > README is the design
20
23
  > bible; the contract it specifies is realised in `src/views_frames/` (index, frames,
21
24
  > io, conformance suite) plus the `src/views_frames_summarize/` sibling package
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "views-frames"
7
- version = "1.8.1"
7
+ version = "1.9.0"
8
8
  description = "The VIEWS platform data-contract layer: immutable array+identifier frames (numpy only, root of the dependency DAG)."
9
9
  authors = [
10
10
  { name = "Simon Polichinel von der Maase", email = "simmaa@prio.org" },
@@ -28,9 +28,15 @@ TOWER_CONFIG: Final[dict[str, Any]] = {
28
28
  [round(0.05 * i, 2) for i in range(1, 19)] # 0.05 … 0.90
29
29
  + [0.92, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99] # fine high-mass tail
30
30
  ),
31
- # The tower-tip reads the median of the floor at this mass — the "shorth". 0.5 is
32
- # the maximally-robust choice: a duplicate would need ~half the draws to hijack it.
33
- "tip_mass": 0.5,
31
+ # The tower-tip reads the median of the floor at this mass — the top floor of the
32
+ # published tower. 0.25 (the top-quartile floor; ADR-019 amendment 2026-07-24,
33
+ # evidence in research/map_hdi/tip_mass_study.py): lower bias than the 0.5 shorth
34
+ # everywhere, RMSE-superior at pooled S, exact on zero-inflated cells, and it
35
+ # passes the real-cell C-44 gate with margin — the tip floor's median is safe as
36
+ # long as a duplicate stack is under half the floor (k > 2·duplicates; at S=32,
37
+ # k=8 tolerates the observed 2-3-zero stacks). Containment law: every HDI of mass
38
+ # > tip_mass/2 provably contains the tip (asserted in conformance.py).
39
+ "tip_mass": 0.25,
34
40
  # Optional magnitude zero rule, OFF by default (``None``). When set to a float, a
35
41
  # row whose every draw is <= it collapses to 0 — a *count* opt-in (sub-1 ⇒ 0).
36
42
  # Left ``None`` for any non-count target: zero-inflation is otherwise handled by the
@@ -126,6 +126,32 @@ def _assert_tower_contract(frame: AnyFrame, n: int) -> None:
126
126
  assert (tip.values[..., 0] >= tlo - 1e-6).all(), "tip below the tip_mass floor"
127
127
  assert (tip.values[..., 0] <= thi + 1e-6).all(), "tip above the tip_mass floor"
128
128
 
129
+ # MAP-containment law (ADR-019 amendment 2026-07-24). Wider-than-tip_mass floors
130
+ # contain the tip by nesting. Below tip_mass, a nested floor still contains it
131
+ # whenever it holds MORE THAN HALF the tip floor's draws: a contiguous sub-window
132
+ # longer than half the parent cannot trim away the parent's middle draw(s), and
133
+ # the tip is their median/average. A floor of mass m spans floor(m·S)+1 draws
134
+ # (the `_ks` value counts inter-draw steps), so the exact condition is
135
+ # 2·(floor(m·S)+1) > floor(tip_mass·S)+1 — asymptotically mass > tip_mass/2.
136
+ # Floors below it carry NO guarantee and are below platform sample resolution
137
+ # (see tower_point.py / research/map_hdi/tip_mass_study.py).
138
+ s_count = int(frame.values.shape[-1])
139
+ n_tip = int(np.floor(tip_mass * s_count)) + 1
140
+ guaranteed = tuple(
141
+ float(m)
142
+ for m in config.canonical_floors()
143
+ if 2 * (int(np.floor(float(m) * s_count)) + 1) > n_tip
144
+ )
145
+ law_tower = hdi_tower(frame, masses=guaranteed)
146
+ for j, m in enumerate(guaranteed):
147
+ glo, ghi = law_tower[..., j, 0], law_tower[..., j, 1]
148
+ assert (tip.values[..., 0] >= glo - 1e-6).all(), (
149
+ f"MAP-containment violated: tip below the {m:.2f} floor"
150
+ )
151
+ assert (tip.values[..., 0] <= ghi + 1e-6).all(), (
152
+ f"MAP-containment violated: tip above the {m:.2f} floor"
153
+ )
154
+
129
155
  # Reproducibility law: the 50% HDI is independent of the other requested masses.
130
156
  just_50 = hdi_tower(frame, masses=(0.5,))
131
157
  assert np.array_equal(just_50[..., 0, :], tower[..., 0, :]), (
@@ -2,15 +2,21 @@
2
2
 
3
3
  `tower_point` is the "most likely single value" we report to a consumer: the median of
4
4
  the draws inside the **`tip_mass` floor** of the nested tower (config-driven, default
5
- 0.5 — the "shorth"). Zero-inflation is handled by that floor's density (a zero-majority
6
- row reads 0); the optional, off-by-default magnitude cutoff applies too if set (C-45).
5
+ 0.25 — the top-quartile floor, i.e. the top floor of the published tower; ADR-019
6
+ amendment 2026-07-24, evidence in `research/map_hdi/tip_mass_study.py`). Zero-inflation
7
+ is handled by that floor's density (a zero-majority row reads 0); the optional,
8
+ off-by-default magnitude cutoff applies too if set (C-45). Containment law: every HDI
9
+ whose floor holds more than half the tip floor's draws provably contains the tip
10
+ (asymptotically, mass > `tip_mass`/2 — a nested contiguous window longer than half
11
+ the parent cannot trim away the parent's median); narrower floors carry no guarantee
12
+ and are below platform sample resolution. Asserted in ``conformance.py``.
7
13
 
8
14
  It is a *new* estimator, deliberately distinct from the frozen `map_estimate` (ADR-018):
9
15
  `map_estimate` is a binned histogram mode with a zero-*mass*-fraction rule and a
10
16
  lowest-index tie-break that is directionally biased on right-skewed, zero-inflated,
11
17
  low-sample posteriors (register C-32). The tower tip is unbinned — it reads the median
12
18
  of a mass-aware floor — so it carries no histogram tie-break **and** is robust to
13
- minority duplicated draws (register C-44; a lonely outlier cannot define a 50%-mass
19
+ minority duplicated draws (register C-44; a lonely outlier cannot define a 25%-mass
14
20
  floor). On a genuinely multi-peaked row the tip is, like any point, ambiguous; pair it
15
21
  with `bimodality` to detect that case rather than collapse it silently.
16
22
  """
File without changes