nested-recd 0.1.0__tar.gz → 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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nested-recd
3
- Version: 0.1.0
4
- Summary: Nested ordinal RECD: Φ1–Φ3 conjunction levels and λ-weighted Discrete Extramental Clock
3
+ Version: 0.2.0
4
+ Summary: Nested ordinal RECD: Φ1–Φ3, continuous excess³ (primary Level-3), and λ-weighted Discrete Extramental Clock
5
5
  Author-email: Johel Padilla-Villanueva <joel.padilla2@upr.edu>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/johelpadilla/nested-recd
@@ -39,9 +39,9 @@ Dynamic: license-file
39
39
  [![Python](https://img.shields.io/pypi/pyversions/nested-recd.svg)](https://pypi.org/project/nested-recd/)
40
40
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
41
41
 
42
- **Nested ordinal RECD** — pure-NumPy implementation of nested ordinal conjunction levels (Φ₁, Φ₂, Φ₃) and λ-weighted Discrete Extramental Clock (RECD) accumulation.
42
+ **Nested ordinal RECD** — pure-NumPy implementation of nested ordinal conjunction levels (Φ₁, Φ₂, Φ₃), the continuous **excess³** Level-3 proxy, and λ-weighted Discrete Extramental Clock (RECD) accumulation.
43
43
 
44
- This is the standalone library behind the nested-time structure used in the CCTP/SDDB cardiac pilot and the experimental design in *Conversación de la naturaleza del tiempo*.
44
+ This is the **canonical Level-3 software** for the Systemic Tau / RECD stack (cardiac CCTP/SDDB pilot, excess³ methods preprint, Academy Learning Tau).
45
45
 
46
46
  ## Install
47
47
 
@@ -49,56 +49,85 @@ This is the standalone library behind the nested-time structure used in the CCTP
49
49
  pip install nested-recd
50
50
  ```
51
51
 
52
- From source:
52
+ From source (development):
53
53
 
54
54
  ```bash
55
- pip install git+https://github.com/johelpadilla/nested-recd.git
55
+ pip install -e ".[dev]"
56
56
  ```
57
57
 
58
+ ## excess³ contract (methods)
59
+
60
+ Canonical specification: Padilla-Villanueva (2026), *excess³: A Pre-Specified Continuous Proxy…*
61
+ DOI: [10.5281/zenodo.21385937](https://doi.org/10.5281/zenodo.21385937) · repo: [github.com/johelpadilla/excess3](https://github.com/johelpadilla/excess3)
62
+
63
+ | Rule | Software |
64
+ |------|----------|
65
+ | `excess3 = 0.6·Syn + 0.4·Surp` | `ALPHA_SYN`, `ALPHA_SURP` (fixed a priori) |
66
+ | Continuous primary | `out["excess3"]` / `compute_phi3_excess` |
67
+ | Φ₃ secondary | binary ticks of excess3 vs `theta3` |
68
+ | Null on full contrast | `surrogate_pvalue_delta_excess3` (phase-shuffle / permute on `\|Δ\|`) |
69
+ | Proxy ≠ full PID | documented; no complete Williams–Beer atom claimed |
70
+ | Parallel nesting | Φ₁, Φ₂, excess³ computed without hard Boolean ascent |
71
+
72
+ Defaults: `m=3`, delay `1`, window `13`, `theta3=0.10` (software); cardio-like reference `DEFAULT_THETA3_CARDIO=0.08`.
73
+
58
74
  ## Quick start
59
75
 
60
76
  ```python
61
77
  import numpy as np
62
- from nested_recd import compute_recd_from_conjunctions, compute_weighted_contributions
78
+ from nested_recd import compute_recd_from_conjunctions, ALPHA_SYN, ALPHA_SURP
63
79
 
64
- # Multivariate series: shape (T, N)
65
80
  rng = np.random.default_rng(0)
66
81
  X = rng.normal(size=(800, 3)).cumsum(axis=0)
67
82
 
68
83
  out = compute_recd_from_conjunctions(X, m=3, d=4, theta3=0.10)
69
84
 
70
- print("mean Φ1:", float(np.nanmean(out["phi1"])))
71
- print("mean Φ2:", float(np.nanmean(out["phi2"])))
85
+ print("mean excess³ (primary):", float(np.nanmean(out["excess3"])))
72
86
  print("Φ3 active fraction:", float(np.nanmean(out["phi3"] > 0)))
87
+ print("weights:", out["params"]["alpha_syn"], out["params"]["alpha_surp"])
73
88
  print("final T_recd:", float(out["T_recd"][-1]))
89
+ ```
74
90
 
75
- w = compute_weighted_contributions(out)
76
- print("frac level-3 contribution:", w["frac_contrib3"])
91
+ ### Continuous excess³ only
92
+
93
+ ```python
94
+ from nested_recd import generate_multivariate_symbols, compute_phi3_excess
95
+
96
+ S = generate_multivariate_symbols(X, m=3)
97
+ phi3, excess3 = compute_phi3_excess(S, window=13, theta=0.10, stride=1)
77
98
  ```
78
99
 
79
- Optional: supply a Systemic Tau series `tau_s` (aligned in time) so that λ is derived empirically:
100
+ ### Surrogate p-value on |Δ excess3|
80
101
 
81
102
  ```python
82
- out = compute_recd_from_conjunctions(X, tau_s=tau_s)
103
+ from nested_recd import surrogate_pvalue_delta_excess3
104
+
105
+ res = surrogate_pvalue_delta_excess3(X, split=400, n_surr=99, method="phase", seed=0)
106
+ print(res["delta_obs"], res["p_value"])
83
107
  ```
84
108
 
85
- Or fix the regime weight with a scalar / array override:
109
+ ### Optional τ_s for λ regime weights
86
110
 
87
111
  ```python
112
+ out = compute_recd_from_conjunctions(X, tau_s=tau_s)
113
+ # or
88
114
  out = compute_recd_from_conjunctions(X, lam_override=0.5)
89
115
  ```
90
116
 
117
+ **Note:** `delta_recd` / `T_recd` use **binary Φ₃** in the α-weighted clock (legacy Discrete Extramental Clock). For scientific Level-3 claims, report **continuous excess³** and nulls on `|Δ excess3|`.
118
+
91
119
  ## What it computes
92
120
 
93
121
  | Symbol | Meaning |
94
122
  |--------|---------|
95
123
  | **Φ₁** | Co-occurrence of identical ordinal symbols across variable pairs |
96
124
  | **Φ₂** | Persistence of pairwise ordinal relations over lag `d` |
97
- | **Φ₃** | Higher-order synergy proxy (total-correlation excess + joint surprise) |
125
+ | **excess³** | Continuous hybrid: `0.6·Syn + 0.4·Surp` (**primary** Level-3) |
126
+ | **Φ₃** | Binary ticks of excess³ above `theta3` (**secondary**) |
98
127
  | **λ** | Chaos / reorganization intensity (from `τ_s` or `lam_override`) |
99
128
  | **α(λ)** | Level weights: α₁ decays with λ; α₂, α₃ grow with λ |
100
- | **ΔRECD** | `α₁Φ₁ + α₂Φ₂ + α₃Φ₃` |
101
- | **T_recd** | Cumulative sum of ΔRECD (nested-time clock) |
129
+ | **ΔRECD** | `α₁Φ₁ + α₂Φ₂ + α₃Φ₃` (legacy clock; binary Φ₃) |
130
+ | **T_recd** | Cumulative sum of ΔRECD |
102
131
 
103
132
  Ordinal symbols use Bandt–Pompe patterns (`m=3` by default).
104
133
 
@@ -107,7 +136,7 @@ Ordinal symbols use Bandt–Pompe patterns (`m=3` by default).
107
136
  ```python
108
137
  from nested_recd import phase_shuffle_independent, random_permutation_independent
109
138
 
110
- X_null = phase_shuffle_independent(X, seed=42) # IAAFT per column
139
+ X_null = phase_shuffle_independent(X, seed=42)
111
140
  X_perm = random_permutation_independent(X, seed=42)
112
141
  ```
113
142
 
@@ -115,8 +144,12 @@ X_perm = random_permutation_independent(X, seed=42)
115
144
 
116
145
  ```python
117
146
  from nested_recd import (
147
+ ALPHA_SYN, ALPHA_SURP,
148
+ DEFAULT_THETA3, DEFAULT_THETA3_CARDIO,
118
149
  compute_recd_from_conjunctions,
119
150
  compute_phi1, compute_phi2, compute_phi3,
151
+ compute_phi3_excess, compute_excess3_window,
152
+ mean_excess_pre_post, surrogate_pvalue_delta_excess3,
120
153
  compute_lambda, alpha_weights, regime_lambda_proxy,
121
154
  compute_weighted_contributions, high_level3_rate,
122
155
  generate_multivariate_symbols,
@@ -128,25 +161,33 @@ from nested_recd import (
128
161
 
129
162
  | Project | Role |
130
163
  |---------|------|
131
- | [`systemictau`](https://pypi.org/project/systemictau/) | Full Systemic Tau library (τ_s, platform, studio) |
132
- | [`cctp-sddb-systemic-tau`](https://github.com/johelpadilla/cctp-sddb-systemic-tau) | Cardiac VF pilot using this nested RECD pipeline |
133
- | This package | Lightweight, installable nested-time / ordinal RECD core |
164
+ | [`systemictau`](https://pypi.org/project/systemictau/) | Full Systemic Tau library (τ_s, gate RECD, studio) — optional `[nested]` extra |
165
+ | [`systemictau-web`](https://github.com/johelpadilla/systemictau-web) | Streamlit app; depends on this package |
166
+ | [`excess3`](https://github.com/johelpadilla/excess3) | Methods + intro ES + primer (specification) |
167
+ | [`cctp-sddb-systemic-tau`](https://github.com/johelpadilla/cctp-sddb-systemic-tau) | Cardiac VF pilot |
168
+ | This package | Lightweight, installable nested-time / excess³ core |
134
169
 
135
170
  ## Citation
136
171
 
137
- If you use this software, please cite the CCTP/SDDB pilot archive:
172
+ If you use excess³ / Level-3 from this package, cite the **methods** preprint (canonical specification):
138
173
 
139
- > Padilla-Villanueva, J. (2026). *CCTP/SDDB: Systemic Tau and ordinal RECD before spontaneous ventricular fibrillation* (v1.0.1). Zenodo. https://doi.org/10.5281/zenodo.21270699
174
+ > Padilla-Villanueva, J. (2026). *excess³: A Pre-Specified Continuous Proxy for Order-3 Synergistic Surplus* (methods). Zenodo. https://doi.org/10.5281/zenodo.21385937
140
175
 
141
- And the theoretical nested-time / RECD framework as appropriate for your venue.
176
+ Software / related stack:
177
+
178
+ > Padilla-Villanueva, J. (2026). Systemic Tau software archive. Zenodo. https://doi.org/10.5281/zenodo.20576241
179
+
180
+ CCTP/SDDB pilot:
181
+
182
+ > Padilla-Villanueva, J. (2026). *CCTP/SDDB: Systemic Tau and ordinal RECD before spontaneous ventricular fibrillation* (v1.0.1). Zenodo. https://doi.org/10.5281/zenodo.21270699
142
183
 
143
184
  ```bibtex
144
185
  @software{padilla_nested_recd_2026,
145
186
  author = {Padilla-Villanueva, Johel},
146
- title = {nested-recd: Nested ordinal RECD levels},
187
+ title = {nested-recd: Nested ordinal RECD levels and continuous excess³},
147
188
  year = {2026},
148
189
  url = {https://github.com/johelpadilla/nested-recd},
149
- version = {0.1.0}
190
+ version = {0.2.0}
150
191
  }
151
192
  ```
152
193
 
@@ -0,0 +1,162 @@
1
+ # nested-recd
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/nested-recd.svg)](https://pypi.org/project/nested-recd/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/nested-recd.svg)](https://pypi.org/project/nested-recd/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ **Nested ordinal RECD** — pure-NumPy implementation of nested ordinal conjunction levels (Φ₁, Φ₂, Φ₃), the continuous **excess³** Level-3 proxy, and λ-weighted Discrete Extramental Clock (RECD) accumulation.
8
+
9
+ This is the **canonical Level-3 software** for the Systemic Tau / RECD stack (cardiac CCTP/SDDB pilot, excess³ methods preprint, Academy Learning Tau).
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install nested-recd
15
+ ```
16
+
17
+ From source (development):
18
+
19
+ ```bash
20
+ pip install -e ".[dev]"
21
+ ```
22
+
23
+ ## excess³ contract (methods)
24
+
25
+ Canonical specification: Padilla-Villanueva (2026), *excess³: A Pre-Specified Continuous Proxy…*
26
+ DOI: [10.5281/zenodo.21385937](https://doi.org/10.5281/zenodo.21385937) · repo: [github.com/johelpadilla/excess3](https://github.com/johelpadilla/excess3)
27
+
28
+ | Rule | Software |
29
+ |------|----------|
30
+ | `excess3 = 0.6·Syn + 0.4·Surp` | `ALPHA_SYN`, `ALPHA_SURP` (fixed a priori) |
31
+ | Continuous primary | `out["excess3"]` / `compute_phi3_excess` |
32
+ | Φ₃ secondary | binary ticks of excess3 vs `theta3` |
33
+ | Null on full contrast | `surrogate_pvalue_delta_excess3` (phase-shuffle / permute on `\|Δ\|`) |
34
+ | Proxy ≠ full PID | documented; no complete Williams–Beer atom claimed |
35
+ | Parallel nesting | Φ₁, Φ₂, excess³ computed without hard Boolean ascent |
36
+
37
+ Defaults: `m=3`, delay `1`, window `13`, `theta3=0.10` (software); cardio-like reference `DEFAULT_THETA3_CARDIO=0.08`.
38
+
39
+ ## Quick start
40
+
41
+ ```python
42
+ import numpy as np
43
+ from nested_recd import compute_recd_from_conjunctions, ALPHA_SYN, ALPHA_SURP
44
+
45
+ rng = np.random.default_rng(0)
46
+ X = rng.normal(size=(800, 3)).cumsum(axis=0)
47
+
48
+ out = compute_recd_from_conjunctions(X, m=3, d=4, theta3=0.10)
49
+
50
+ print("mean excess³ (primary):", float(np.nanmean(out["excess3"])))
51
+ print("Φ3 active fraction:", float(np.nanmean(out["phi3"] > 0)))
52
+ print("weights:", out["params"]["alpha_syn"], out["params"]["alpha_surp"])
53
+ print("final T_recd:", float(out["T_recd"][-1]))
54
+ ```
55
+
56
+ ### Continuous excess³ only
57
+
58
+ ```python
59
+ from nested_recd import generate_multivariate_symbols, compute_phi3_excess
60
+
61
+ S = generate_multivariate_symbols(X, m=3)
62
+ phi3, excess3 = compute_phi3_excess(S, window=13, theta=0.10, stride=1)
63
+ ```
64
+
65
+ ### Surrogate p-value on |Δ excess3|
66
+
67
+ ```python
68
+ from nested_recd import surrogate_pvalue_delta_excess3
69
+
70
+ res = surrogate_pvalue_delta_excess3(X, split=400, n_surr=99, method="phase", seed=0)
71
+ print(res["delta_obs"], res["p_value"])
72
+ ```
73
+
74
+ ### Optional τ_s for λ regime weights
75
+
76
+ ```python
77
+ out = compute_recd_from_conjunctions(X, tau_s=tau_s)
78
+ # or
79
+ out = compute_recd_from_conjunctions(X, lam_override=0.5)
80
+ ```
81
+
82
+ **Note:** `delta_recd` / `T_recd` use **binary Φ₃** in the α-weighted clock (legacy Discrete Extramental Clock). For scientific Level-3 claims, report **continuous excess³** and nulls on `|Δ excess3|`.
83
+
84
+ ## What it computes
85
+
86
+ | Symbol | Meaning |
87
+ |--------|---------|
88
+ | **Φ₁** | Co-occurrence of identical ordinal symbols across variable pairs |
89
+ | **Φ₂** | Persistence of pairwise ordinal relations over lag `d` |
90
+ | **excess³** | Continuous hybrid: `0.6·Syn + 0.4·Surp` (**primary** Level-3) |
91
+ | **Φ₃** | Binary ticks of excess³ above `theta3` (**secondary**) |
92
+ | **λ** | Chaos / reorganization intensity (from `τ_s` or `lam_override`) |
93
+ | **α(λ)** | Level weights: α₁ decays with λ; α₂, α₃ grow with λ |
94
+ | **ΔRECD** | `α₁Φ₁ + α₂Φ₂ + α₃Φ₃` (legacy clock; binary Φ₃) |
95
+ | **T_recd** | Cumulative sum of ΔRECD |
96
+
97
+ Ordinal symbols use Bandt–Pompe patterns (`m=3` by default).
98
+
99
+ ## Surrogates
100
+
101
+ ```python
102
+ from nested_recd import phase_shuffle_independent, random_permutation_independent
103
+
104
+ X_null = phase_shuffle_independent(X, seed=42)
105
+ X_perm = random_permutation_independent(X, seed=42)
106
+ ```
107
+
108
+ ## API surface
109
+
110
+ ```python
111
+ from nested_recd import (
112
+ ALPHA_SYN, ALPHA_SURP,
113
+ DEFAULT_THETA3, DEFAULT_THETA3_CARDIO,
114
+ compute_recd_from_conjunctions,
115
+ compute_phi1, compute_phi2, compute_phi3,
116
+ compute_phi3_excess, compute_excess3_window,
117
+ mean_excess_pre_post, surrogate_pvalue_delta_excess3,
118
+ compute_lambda, alpha_weights, regime_lambda_proxy,
119
+ compute_weighted_contributions, high_level3_rate,
120
+ generate_multivariate_symbols,
121
+ phase_shuffle_independent, generate_surrogate_ensemble,
122
+ )
123
+ ```
124
+
125
+ ## Relation to other projects
126
+
127
+ | Project | Role |
128
+ |---------|------|
129
+ | [`systemictau`](https://pypi.org/project/systemictau/) | Full Systemic Tau library (τ_s, gate RECD, studio) — optional `[nested]` extra |
130
+ | [`systemictau-web`](https://github.com/johelpadilla/systemictau-web) | Streamlit app; depends on this package |
131
+ | [`excess3`](https://github.com/johelpadilla/excess3) | Methods + intro ES + primer (specification) |
132
+ | [`cctp-sddb-systemic-tau`](https://github.com/johelpadilla/cctp-sddb-systemic-tau) | Cardiac VF pilot |
133
+ | This package | Lightweight, installable nested-time / excess³ core |
134
+
135
+ ## Citation
136
+
137
+ If you use excess³ / Level-3 from this package, cite the **methods** preprint (canonical specification):
138
+
139
+ > Padilla-Villanueva, J. (2026). *excess³: A Pre-Specified Continuous Proxy for Order-3 Synergistic Surplus* (methods). Zenodo. https://doi.org/10.5281/zenodo.21385937
140
+
141
+ Software / related stack:
142
+
143
+ > Padilla-Villanueva, J. (2026). Systemic Tau software archive. Zenodo. https://doi.org/10.5281/zenodo.20576241
144
+
145
+ CCTP/SDDB pilot:
146
+
147
+ > Padilla-Villanueva, J. (2026). *CCTP/SDDB: Systemic Tau and ordinal RECD before spontaneous ventricular fibrillation* (v1.0.1). Zenodo. https://doi.org/10.5281/zenodo.21270699
148
+
149
+ ```bibtex
150
+ @software{padilla_nested_recd_2026,
151
+ author = {Padilla-Villanueva, Johel},
152
+ title = {nested-recd: Nested ordinal RECD levels and continuous excess³},
153
+ year = {2026},
154
+ url = {https://github.com/johelpadilla/nested-recd},
155
+ version = {0.2.0}
156
+ }
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT © 2026 Johel Padilla-Villanueva
162
+ ORCID: [0000-0002-5797-6931](https://orcid.org/0000-0002-5797-6931)
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nested-recd"
7
- version = "0.1.0"
8
- description = "Nested ordinal RECD: Φ1–Φ3 conjunction levels and λ-weighted Discrete Extramental Clock"
7
+ version = "0.2.0"
8
+ description = "Nested ordinal RECD: Φ1–Φ3, continuous excess³ (primary Level-3), and λ-weighted Discrete Extramental Clock"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  license = "MIT"
@@ -2,8 +2,8 @@
2
2
  nested-recd
3
3
  ===========
4
4
 
5
- Nested ordinal RECD: Φ₁ / Φ₂ / Φ₃ conjunction levels and λ-weighted
6
- Discrete Extramental Clock accumulation.
5
+ Nested ordinal RECD: Φ₁ / Φ₂ / Φ₃ conjunction levels, continuous excess³
6
+ (primary Level-3 readout), and λ-weighted Discrete Extramental Clock.
7
7
 
8
8
  Quick start
9
9
  -----------
@@ -11,7 +11,7 @@ Quick start
11
11
  >>> from nested_recd import compute_recd_from_conjunctions
12
12
  >>> X = np.random.randn(500, 3).cumsum(axis=0)
13
13
  >>> out = compute_recd_from_conjunctions(X)
14
- >>> out["T_recd"][-1]
14
+ >>> float(np.nanmean(out["excess3"])) # continuous primary Level-3 score
15
15
  """
16
16
 
17
17
  from nested_recd.ordinal_levels import (
@@ -19,13 +19,21 @@ from nested_recd.ordinal_levels import (
19
19
  DEFAULT_DELAY,
20
20
  DEFAULT_D_PERSIST,
21
21
  DEFAULT_WINDOW_TAU,
22
+ DEFAULT_WINDOW,
22
23
  DEFAULT_THETA_CHAOS,
23
24
  DEFAULT_THETA3,
25
+ DEFAULT_THETA3_CARDIO,
24
26
  DELTA_FEIGENBAUM,
27
+ ALPHA_SYN,
28
+ ALPHA_SURP,
25
29
  generate_multivariate_symbols,
26
30
  compute_phi1,
27
31
  compute_phi2,
28
32
  compute_phi3,
33
+ compute_phi3_excess,
34
+ compute_excess3_window,
35
+ mean_excess_pre_post,
36
+ surrogate_pvalue_delta_excess3,
29
37
  compute_lambda,
30
38
  alpha_weights,
31
39
  regime_lambda_proxy,
@@ -43,7 +51,7 @@ from nested_recd.surrogates import (
43
51
  compute_null_distribution,
44
52
  )
45
53
 
46
- __version__ = "0.1.0"
54
+ __version__ = "0.2.0"
47
55
 
48
56
  __all__ = [
49
57
  "__version__",
@@ -51,13 +59,21 @@ __all__ = [
51
59
  "DEFAULT_DELAY",
52
60
  "DEFAULT_D_PERSIST",
53
61
  "DEFAULT_WINDOW_TAU",
62
+ "DEFAULT_WINDOW",
54
63
  "DEFAULT_THETA_CHAOS",
55
64
  "DEFAULT_THETA3",
65
+ "DEFAULT_THETA3_CARDIO",
56
66
  "DELTA_FEIGENBAUM",
67
+ "ALPHA_SYN",
68
+ "ALPHA_SURP",
57
69
  "generate_multivariate_symbols",
58
70
  "compute_phi1",
59
71
  "compute_phi2",
60
72
  "compute_phi3",
73
+ "compute_phi3_excess",
74
+ "compute_excess3_window",
75
+ "mean_excess_pre_post",
76
+ "surrogate_pvalue_delta_excess3",
61
77
  "compute_lambda",
62
78
  "alpha_weights",
63
79
  "regime_lambda_proxy",
@@ -19,14 +19,20 @@ import numpy as np
19
19
  from typing import Dict, Tuple, Optional
20
20
  import warnings
21
21
 
22
- # Defaults quirúrgicos (ver diseño)
22
+ # Defaults quirúrgicos (ver diseño + excess³ methods contract)
23
23
  DEFAULT_M = 3
24
24
  DEFAULT_DELAY = 1
25
25
  DEFAULT_D_PERSIST = 4
26
26
  DEFAULT_WINDOW_TAU = 13
27
+ DEFAULT_WINDOW = DEFAULT_WINDOW_TAU # alias (methods paper)
27
28
  DEFAULT_THETA_CHAOS = 0.41
28
29
  DELTA_FEIGENBAUM = 4.6692016091
29
- DEFAULT_THETA3 = 0.10 # Ajustado a la baja tras piloto (más sensible sin perder especificidad)
30
+ DEFAULT_THETA3 = 0.10 # software default
31
+ DEFAULT_THETA3_CARDIO = 0.08 # methods cardio-like reference
32
+
33
+ # excess³ = α_Syn · Syn + α_Surp · Surp (fixed a priori; never fit on target data)
34
+ ALPHA_SYN = 0.6
35
+ ALPHA_SURP = 0.4
30
36
 
31
37
  def _gen_ordinal(x: np.ndarray, m: int = DEFAULT_M, delay: int = DEFAULT_DELAY) -> np.ndarray:
32
38
  """Bandt–Pompe ordinal symbols (pure NumPy, no numba required)."""
@@ -234,77 +240,248 @@ def _joint_entropy_and_marginals(S_window: np.ndarray) -> Tuple[float, float, fl
234
240
  return H_joint, H_marg_sum, synergy_proxy
235
241
 
236
242
 
237
- def compute_phi3(
243
+ def compute_excess3_window(
244
+ win: np.ndarray,
245
+ use_surprise: bool = True,
246
+ alpha_syn: float = ALPHA_SYN,
247
+ alpha_surp: float = ALPHA_SURP,
248
+ ) -> float:
249
+ """
250
+ excess³ score on a single window of joint ordinal symbols (shape (w, N)).
251
+
252
+ Canonical hybrid proxy (methods contract):
253
+ excess3 = alpha_syn · Syn + alpha_surp · Surp
254
+ with default weights (0.6, 0.4) fixed a priori — never optimised on the
255
+ scientific dataset under test.
256
+
257
+ Syn: synergistic residual multiinformation proxy
258
+ max(0, TC − (N−1)·MĪ_pair).
259
+ Surp: observed-vs-independence joint surprise (frequency-weighted log-ratio).
260
+
261
+ This is a **proxy**, not a complete PID synergy atom.
262
+ """
263
+ win = np.asarray(win)
264
+ if win.ndim != 2 or win.shape[0] == 0:
265
+ return float("nan")
266
+ if win.shape[1] < 2:
267
+ return 0.0
268
+
269
+ _, _, syn = _joint_entropy_and_marginals(win)
270
+ if not use_surprise:
271
+ return float(syn)
272
+
273
+ from collections import Counter
274
+
275
+ joint_tuples = [tuple(int(v) for v in row) for row in win]
276
+ counter = Counter(joint_tuples)
277
+ T_win = len(joint_tuples)
278
+ surprises = []
279
+ for u, cnt in counter.items():
280
+ p_indep = 1.0
281
+ for k, val in enumerate(u):
282
+ p_indep *= max(float(np.mean(win[:, k] == val)), 1e-9)
283
+ p_obs = cnt / T_win
284
+ ratio = p_obs / max(p_indep, 1e-9)
285
+ excess_log = max(0.0, np.log2(ratio)) if ratio > 1 else 0.0
286
+ surprises.append(excess_log * (cnt / T_win))
287
+ joint_surprise = float(np.sum(surprises)) if surprises else 0.0
288
+ return float(alpha_syn * syn + alpha_surp * joint_surprise)
289
+
290
+
291
+ def compute_phi3_excess(
238
292
  S: np.ndarray,
239
- window: int = 13,
293
+ window: int = DEFAULT_WINDOW_TAU,
240
294
  theta: float = DEFAULT_THETA3,
241
295
  stride: int = 1,
242
- use_surprise: bool = True
296
+ use_surprise: bool = True,
297
+ alpha_syn: float = ALPHA_SYN,
298
+ alpha_surp: float = ALPHA_SURP,
299
+ fill: bool = False,
243
300
  ) -> Tuple[np.ndarray, np.ndarray]:
244
301
  """
245
- Φ₃(t): Score de sinergia / irreductibilidad (proxy mejorado post-piloto).
246
-
247
- Dos componentes:
248
- - excess (total correlation - pairwise) del método anterior.
249
- - joint_surprise: promedio de "sorpresa" de las tuplas observadas
250
- vs modelo de independencia ( -log2(P_indep) ponderado por frecuencia observada ).
251
-
252
- Si use_surprise=True, el score combina ambos. Esto hace el proxy
253
- más sensible a configuraciones conjuntas "improbables bajo independencia"
254
- que no se explican por marginales (más cerca de irreductibilidad).
255
-
256
- Retorna (phi3_binary, excess_raw) -- excess ahora es el score combinado.
302
+ Continuous excess³ (primary) and binary Φ₃ (secondary) on symbol matrix S.
303
+
304
+ Returns
305
+ -------
306
+ phi3 : ndarray
307
+ Binary ticks: 1 if excess3 > theta else 0 (NaN before first full window).
308
+ excess3 : ndarray
309
+ Continuous hybrid score (primary Level-3 readout).
310
+
311
+ Parameters
312
+ ----------
313
+ fill : bool
314
+ If True, forward-fill NaNs after the first finite score (plot convenience).
315
+ Default False preserves sparse stride semantics.
257
316
  """
317
+ S = np.asarray(S)
258
318
  T_eff, N = S.shape
259
319
  phi3 = np.full(T_eff, np.nan)
260
320
  excess = np.full(T_eff, np.nan)
261
321
 
262
- if T_eff < window:
322
+ if T_eff < window or N < 2:
263
323
  return phi3, excess
264
324
 
265
325
  for t in range(window - 1, T_eff, stride):
266
- win = S[t - window + 1 : t + 1]
267
- T_win = len(win)
268
-
269
- # Componente 1: exceso sinérgico previo (total corr - pairwise)
270
- _, _, syn = _joint_entropy_and_marginals(win)
271
-
272
- # Componente 2: Joint surprise (más directo para "irreductible")
273
- if use_surprise:
274
- from collections import Counter
275
- joint_tuples = [tuple(int(v) for v in row) for row in win] # asegurar python ints
276
- counter = Counter(joint_tuples)
277
- uniq = list(counter.keys())
278
- counts = np.array(list(counter.values()))
279
- T_win = len(joint_tuples)
280
-
281
- # P_indep por tupla + "exceso de ocurrencia" (log ratio observado / independencia)
282
- # Esto captura configuraciones que ocurren MÁS de lo esperado por marginales → más "irreducible"
283
- surprises = []
284
- for u, cnt in zip(uniq, counts):
285
- p_indep = 1.0
286
- for k, val in enumerate(u):
287
- pk = np.mean([row[k] == val for row in joint_tuples])
288
- p_indep *= max(pk, 1e-9)
289
- p_obs = cnt / T_win
290
- # log-ratio: >0 cuando ocurre más de lo esperado por independencia
291
- ratio = p_obs / max(p_indep, 1e-9)
292
- excess_log = max(0.0, np.log2(ratio)) if ratio > 1 else 0.0
293
- weight = cnt / T_win
294
- surprises.append(excess_log * weight)
295
-
296
- joint_surprise = float(np.sum(surprises)) if surprises else 0.0
297
- # Combinar (syn ya es en bits-ish, surprise también)
298
- combined = 0.6 * syn + 0.4 * joint_surprise # pesos heurísticos pero transparentes
299
- else:
300
- combined = syn
301
-
302
- excess[t] = combined
303
- phi3[t] = 1.0 if combined > theta else 0.0
326
+ score = compute_excess3_window(
327
+ S[t - window + 1 : t + 1],
328
+ use_surprise=use_surprise,
329
+ alpha_syn=alpha_syn,
330
+ alpha_surp=alpha_surp,
331
+ )
332
+ excess[t] = score
333
+ phi3[t] = 1.0 if score > theta else 0.0
334
+
335
+ if fill:
336
+ last = np.nan
337
+ for t in range(T_eff):
338
+ if np.isfinite(excess[t]):
339
+ last = excess[t]
340
+ elif np.isfinite(last):
341
+ excess[t] = last
342
+ phi3[t] = 1.0 if last > theta else 0.0
304
343
 
305
344
  return phi3, excess
306
345
 
307
346
 
347
+ def compute_phi3(
348
+ S: np.ndarray,
349
+ window: int = 13,
350
+ theta: float = DEFAULT_THETA3,
351
+ stride: int = 1,
352
+ use_surprise: bool = True,
353
+ alpha_syn: float = ALPHA_SYN,
354
+ alpha_surp: float = ALPHA_SURP,
355
+ ) -> Tuple[np.ndarray, np.ndarray]:
356
+ """
357
+ Φ₃(t) binary + continuous excess³ series.
358
+
359
+ Continuous excess³ is the **primary** Level-3 magnitude (methods contract);
360
+ Φ₃ only counts threshold crossings. Weights default to (ALPHA_SYN, ALPHA_SURP).
361
+
362
+ Returns (phi3_binary, excess3_continuous).
363
+ """
364
+ return compute_phi3_excess(
365
+ S,
366
+ window=window,
367
+ theta=theta,
368
+ stride=stride,
369
+ use_surprise=use_surprise,
370
+ alpha_syn=alpha_syn,
371
+ alpha_surp=alpha_surp,
372
+ fill=False,
373
+ )
374
+
375
+
376
+ def mean_excess_pre_post(
377
+ excess3: np.ndarray,
378
+ split: int,
379
+ ) -> Tuple[float, float, float]:
380
+ """
381
+ Mean continuous excess³ before / after a split index, and Δ = post − pre.
382
+
383
+ ``split`` is an index into the excess3 array (same time base as returned by
384
+ compute_recd_from_conjunctions / compute_phi3_excess).
385
+ """
386
+ excess3 = np.asarray(excess3, dtype=float)
387
+ if split <= 0 or split >= len(excess3):
388
+ raise ValueError(f"split={split} out of range for length {len(excess3)}")
389
+ pre = float(np.nanmean(excess3[:split]))
390
+ post = float(np.nanmean(excess3[split:]))
391
+ return pre, post, post - pre
392
+
393
+
394
+ def surrogate_pvalue_delta_excess3(
395
+ X: np.ndarray,
396
+ split: int,
397
+ n_surr: int = 199,
398
+ method: str = "phase",
399
+ seed: int = 0,
400
+ m: int = DEFAULT_M,
401
+ window: int = DEFAULT_WINDOW_TAU,
402
+ theta3: float = DEFAULT_THETA3,
403
+ stride: int = 1,
404
+ delay: int = DEFAULT_DELAY,
405
+ alpha_syn: float = ALPHA_SYN,
406
+ alpha_surp: float = ALPHA_SURP,
407
+ ) -> Dict[str, float]:
408
+ """
409
+ Two-sided surrogate p-value for |Δ excess3| under independent phase-shuffle
410
+ (default) or column permutation — methods-paper null protocol.
411
+
412
+ Inference is on the **full contrast** |Δ|, not mean-vs-mean of means alone.
413
+
414
+ Parameters
415
+ ----------
416
+ X : (T, N) array
417
+ Multivariate series (raw values; symbols computed inside).
418
+ split : int
419
+ Split index on the **raw** time axis; mapped to symbol time via
420
+ embedding length (m, delay).
421
+ method : {"phase", "permute"}
422
+ "phase" uses per-column IAAFT / phase-shuffle; "permute" shuffles time.
423
+ """
424
+ from nested_recd.surrogates import (
425
+ phase_shuffle_independent,
426
+ random_permutation_independent,
427
+ )
428
+
429
+ X = np.asarray(X)
430
+ if X.ndim != 2:
431
+ raise ValueError("X must be shape (T, N)")
432
+
433
+ def _delta_on(arr: np.ndarray) -> float:
434
+ S = generate_multivariate_symbols(arr, m=m, delay=delay)
435
+ if len(S) == 0:
436
+ return float("nan")
437
+ # Map split from raw time to symbol index (conservative)
438
+ emb = (m - 1) * delay
439
+ split_s = int(np.clip(split - emb, 1, len(S) - 1))
440
+ _, excess = compute_phi3_excess(
441
+ S,
442
+ window=window,
443
+ theta=theta3,
444
+ stride=stride,
445
+ alpha_syn=alpha_syn,
446
+ alpha_surp=alpha_surp,
447
+ )
448
+ _, _, d = mean_excess_pre_post(excess, split_s)
449
+ return float(d)
450
+
451
+ obs = _delta_on(X)
452
+ null = []
453
+ rng = np.random.default_rng(seed)
454
+ for _ in range(n_surr):
455
+ s = int(rng.integers(0, 2**31 - 1))
456
+ if method == "permute":
457
+ Xs = random_permutation_independent(X, seed=s)
458
+ else:
459
+ Xs = phase_shuffle_independent(X, seed=s)
460
+ null.append(_delta_on(Xs))
461
+ null = np.asarray(null, dtype=float)
462
+ null = null[np.isfinite(null)]
463
+ if not np.isfinite(obs) or len(null) == 0:
464
+ return {
465
+ "delta_obs": float(obs) if np.isfinite(obs) else float("nan"),
466
+ "abs_delta_obs": float("nan"),
467
+ "p_value": float("nan"),
468
+ "n_surr": float(len(null)),
469
+ "method": method,
470
+ }
471
+ abs_obs = abs(obs)
472
+ # Add-one smoothing: (1 + #{|null| >= |obs|}) / (1 + n)
473
+ p = (1.0 + float(np.sum(np.abs(null) >= abs_obs))) / (1.0 + len(null))
474
+ return {
475
+ "delta_obs": float(obs),
476
+ "abs_delta_obs": float(abs_obs),
477
+ "p_value": float(p),
478
+ "n_surr": float(len(null)),
479
+ "method": method,
480
+ "null_mean_abs": float(np.mean(np.abs(null))),
481
+ "null_std_abs": float(np.std(np.abs(null))),
482
+ }
483
+
484
+
308
485
  # ============================================================
309
486
  # λ(t) y pesos α(λ)
310
487
  # ============================================================
@@ -381,27 +558,40 @@ def compute_recd_from_conjunctions(
381
558
  theta3: float = DEFAULT_THETA3,
382
559
  window_tau: int = DEFAULT_WINDOW_TAU,
383
560
  lam_override: Optional[np.ndarray] = None,
561
+ stride: int = 1,
562
+ alpha_syn: float = ALPHA_SYN,
563
+ alpha_surp: float = ALPHA_SURP,
384
564
  **alpha_kwargs
385
565
  ) -> Dict[str, np.ndarray]:
386
566
  """
387
- Pipeline quirúrgico completo:
388
- - Símbolos ordinales
389
- - Φ1, Φ2, Φ3
390
- - λ y α(λ)
391
- - ΔRECD_new y T_new
392
-
393
- Si tau_s no se provee, se espera que el caller lo calcule con la infraestructura existente.
394
-
395
- lam_override: si se provee (escalar o array), se usa directamente para calcular α(λ)
396
- en lugar de derivar λ de tau_s. Útil para Opción 1 (ground-truth r)
397
- o alphas fijos, para aislar el efecto de régimen sobre Nivel 3.
567
+ Full nested ordinal RECD pipeline:
568
+ - Bandt–Pompe symbols
569
+ - Φ1, Φ2, Φ3 (binary) + continuous excess³ (primary Level-3 readout)
570
+ - λ and α(λ) regime weights
571
+ - ΔRECD and cumulative T_recd
572
+
573
+ Level-3 contract (excess³ methods):
574
+ - Continuous ``excess3`` is the primary magnitude for order-3 claims.
575
+ - ``phi3`` is a secondary threshold discretisation of excess3.
576
+ - ``delta_recd`` keeps the legacy λ-weighted clock using **binary** Φ₃
577
+ (back-compat with CCTP / Discrete Extramental Clock nesting). Do not
578
+ treat ``delta_recd`` alone as the Level-3 scientific readout.
579
+
580
+ If tau_s is omitted, λ=0 (warning). Use ``lam_override`` for regime ground truth.
398
581
  """
399
582
  S = generate_multivariate_symbols(X, m=m)
400
583
  T_eff = S.shape[0]
401
584
 
402
585
  phi1 = compute_phi1(S)
403
586
  phi2 = compute_phi2(S, d=d)
404
- phi3, excess3 = compute_phi3(S, window=window_tau, theta=theta3)
587
+ phi3, excess3 = compute_phi3(
588
+ S,
589
+ window=window_tau,
590
+ theta=theta3,
591
+ stride=stride,
592
+ alpha_syn=alpha_syn,
593
+ alpha_surp=alpha_surp,
594
+ )
405
595
 
406
596
  # Determinar λ: override > tau-derived > zero
407
597
  if lam_override is not None:
@@ -425,6 +615,7 @@ def compute_recd_from_conjunctions(
425
615
  phi3_safe = np.nan_to_num(phi3, nan=0.0)
426
616
  excess3_safe = np.nan_to_num(excess3, nan=0.0)
427
617
 
618
+ # Legacy RECD clock: binary Φ₃ in the α-weighted sum (back-compat)
428
619
  delta_recd = a1 * phi1 + a2 * phi2 + a3 * phi3_safe
429
620
  T_recd = np.nancumsum(delta_recd)
430
621
 
@@ -441,9 +632,17 @@ def compute_recd_from_conjunctions(
441
632
  "delta_recd": delta_recd,
442
633
  "T_recd": T_recd,
443
634
  "params": {
444
- "m": m, "d": d, "theta3": theta3,
445
- "window_tau": window_tau, **alpha_kwargs
446
- }
635
+ "m": m,
636
+ "d": d,
637
+ "theta3": theta3,
638
+ "window_tau": window_tau,
639
+ "stride": stride,
640
+ "alpha_syn": alpha_syn,
641
+ "alpha_surp": alpha_surp,
642
+ "level3_primary": "excess3",
643
+ "level3_secondary": "phi3",
644
+ **alpha_kwargs,
645
+ },
447
646
  }
448
647
 
449
648
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nested-recd
3
- Version: 0.1.0
4
- Summary: Nested ordinal RECD: Φ1–Φ3 conjunction levels and λ-weighted Discrete Extramental Clock
3
+ Version: 0.2.0
4
+ Summary: Nested ordinal RECD: Φ1–Φ3, continuous excess³ (primary Level-3), and λ-weighted Discrete Extramental Clock
5
5
  Author-email: Johel Padilla-Villanueva <joel.padilla2@upr.edu>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/johelpadilla/nested-recd
@@ -39,9 +39,9 @@ Dynamic: license-file
39
39
  [![Python](https://img.shields.io/pypi/pyversions/nested-recd.svg)](https://pypi.org/project/nested-recd/)
40
40
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
41
41
 
42
- **Nested ordinal RECD** — pure-NumPy implementation of nested ordinal conjunction levels (Φ₁, Φ₂, Φ₃) and λ-weighted Discrete Extramental Clock (RECD) accumulation.
42
+ **Nested ordinal RECD** — pure-NumPy implementation of nested ordinal conjunction levels (Φ₁, Φ₂, Φ₃), the continuous **excess³** Level-3 proxy, and λ-weighted Discrete Extramental Clock (RECD) accumulation.
43
43
 
44
- This is the standalone library behind the nested-time structure used in the CCTP/SDDB cardiac pilot and the experimental design in *Conversación de la naturaleza del tiempo*.
44
+ This is the **canonical Level-3 software** for the Systemic Tau / RECD stack (cardiac CCTP/SDDB pilot, excess³ methods preprint, Academy Learning Tau).
45
45
 
46
46
  ## Install
47
47
 
@@ -49,56 +49,85 @@ This is the standalone library behind the nested-time structure used in the CCTP
49
49
  pip install nested-recd
50
50
  ```
51
51
 
52
- From source:
52
+ From source (development):
53
53
 
54
54
  ```bash
55
- pip install git+https://github.com/johelpadilla/nested-recd.git
55
+ pip install -e ".[dev]"
56
56
  ```
57
57
 
58
+ ## excess³ contract (methods)
59
+
60
+ Canonical specification: Padilla-Villanueva (2026), *excess³: A Pre-Specified Continuous Proxy…*
61
+ DOI: [10.5281/zenodo.21385937](https://doi.org/10.5281/zenodo.21385937) · repo: [github.com/johelpadilla/excess3](https://github.com/johelpadilla/excess3)
62
+
63
+ | Rule | Software |
64
+ |------|----------|
65
+ | `excess3 = 0.6·Syn + 0.4·Surp` | `ALPHA_SYN`, `ALPHA_SURP` (fixed a priori) |
66
+ | Continuous primary | `out["excess3"]` / `compute_phi3_excess` |
67
+ | Φ₃ secondary | binary ticks of excess3 vs `theta3` |
68
+ | Null on full contrast | `surrogate_pvalue_delta_excess3` (phase-shuffle / permute on `\|Δ\|`) |
69
+ | Proxy ≠ full PID | documented; no complete Williams–Beer atom claimed |
70
+ | Parallel nesting | Φ₁, Φ₂, excess³ computed without hard Boolean ascent |
71
+
72
+ Defaults: `m=3`, delay `1`, window `13`, `theta3=0.10` (software); cardio-like reference `DEFAULT_THETA3_CARDIO=0.08`.
73
+
58
74
  ## Quick start
59
75
 
60
76
  ```python
61
77
  import numpy as np
62
- from nested_recd import compute_recd_from_conjunctions, compute_weighted_contributions
78
+ from nested_recd import compute_recd_from_conjunctions, ALPHA_SYN, ALPHA_SURP
63
79
 
64
- # Multivariate series: shape (T, N)
65
80
  rng = np.random.default_rng(0)
66
81
  X = rng.normal(size=(800, 3)).cumsum(axis=0)
67
82
 
68
83
  out = compute_recd_from_conjunctions(X, m=3, d=4, theta3=0.10)
69
84
 
70
- print("mean Φ1:", float(np.nanmean(out["phi1"])))
71
- print("mean Φ2:", float(np.nanmean(out["phi2"])))
85
+ print("mean excess³ (primary):", float(np.nanmean(out["excess3"])))
72
86
  print("Φ3 active fraction:", float(np.nanmean(out["phi3"] > 0)))
87
+ print("weights:", out["params"]["alpha_syn"], out["params"]["alpha_surp"])
73
88
  print("final T_recd:", float(out["T_recd"][-1]))
89
+ ```
74
90
 
75
- w = compute_weighted_contributions(out)
76
- print("frac level-3 contribution:", w["frac_contrib3"])
91
+ ### Continuous excess³ only
92
+
93
+ ```python
94
+ from nested_recd import generate_multivariate_symbols, compute_phi3_excess
95
+
96
+ S = generate_multivariate_symbols(X, m=3)
97
+ phi3, excess3 = compute_phi3_excess(S, window=13, theta=0.10, stride=1)
77
98
  ```
78
99
 
79
- Optional: supply a Systemic Tau series `tau_s` (aligned in time) so that λ is derived empirically:
100
+ ### Surrogate p-value on |Δ excess3|
80
101
 
81
102
  ```python
82
- out = compute_recd_from_conjunctions(X, tau_s=tau_s)
103
+ from nested_recd import surrogate_pvalue_delta_excess3
104
+
105
+ res = surrogate_pvalue_delta_excess3(X, split=400, n_surr=99, method="phase", seed=0)
106
+ print(res["delta_obs"], res["p_value"])
83
107
  ```
84
108
 
85
- Or fix the regime weight with a scalar / array override:
109
+ ### Optional τ_s for λ regime weights
86
110
 
87
111
  ```python
112
+ out = compute_recd_from_conjunctions(X, tau_s=tau_s)
113
+ # or
88
114
  out = compute_recd_from_conjunctions(X, lam_override=0.5)
89
115
  ```
90
116
 
117
+ **Note:** `delta_recd` / `T_recd` use **binary Φ₃** in the α-weighted clock (legacy Discrete Extramental Clock). For scientific Level-3 claims, report **continuous excess³** and nulls on `|Δ excess3|`.
118
+
91
119
  ## What it computes
92
120
 
93
121
  | Symbol | Meaning |
94
122
  |--------|---------|
95
123
  | **Φ₁** | Co-occurrence of identical ordinal symbols across variable pairs |
96
124
  | **Φ₂** | Persistence of pairwise ordinal relations over lag `d` |
97
- | **Φ₃** | Higher-order synergy proxy (total-correlation excess + joint surprise) |
125
+ | **excess³** | Continuous hybrid: `0.6·Syn + 0.4·Surp` (**primary** Level-3) |
126
+ | **Φ₃** | Binary ticks of excess³ above `theta3` (**secondary**) |
98
127
  | **λ** | Chaos / reorganization intensity (from `τ_s` or `lam_override`) |
99
128
  | **α(λ)** | Level weights: α₁ decays with λ; α₂, α₃ grow with λ |
100
- | **ΔRECD** | `α₁Φ₁ + α₂Φ₂ + α₃Φ₃` |
101
- | **T_recd** | Cumulative sum of ΔRECD (nested-time clock) |
129
+ | **ΔRECD** | `α₁Φ₁ + α₂Φ₂ + α₃Φ₃` (legacy clock; binary Φ₃) |
130
+ | **T_recd** | Cumulative sum of ΔRECD |
102
131
 
103
132
  Ordinal symbols use Bandt–Pompe patterns (`m=3` by default).
104
133
 
@@ -107,7 +136,7 @@ Ordinal symbols use Bandt–Pompe patterns (`m=3` by default).
107
136
  ```python
108
137
  from nested_recd import phase_shuffle_independent, random_permutation_independent
109
138
 
110
- X_null = phase_shuffle_independent(X, seed=42) # IAAFT per column
139
+ X_null = phase_shuffle_independent(X, seed=42)
111
140
  X_perm = random_permutation_independent(X, seed=42)
112
141
  ```
113
142
 
@@ -115,8 +144,12 @@ X_perm = random_permutation_independent(X, seed=42)
115
144
 
116
145
  ```python
117
146
  from nested_recd import (
147
+ ALPHA_SYN, ALPHA_SURP,
148
+ DEFAULT_THETA3, DEFAULT_THETA3_CARDIO,
118
149
  compute_recd_from_conjunctions,
119
150
  compute_phi1, compute_phi2, compute_phi3,
151
+ compute_phi3_excess, compute_excess3_window,
152
+ mean_excess_pre_post, surrogate_pvalue_delta_excess3,
120
153
  compute_lambda, alpha_weights, regime_lambda_proxy,
121
154
  compute_weighted_contributions, high_level3_rate,
122
155
  generate_multivariate_symbols,
@@ -128,25 +161,33 @@ from nested_recd import (
128
161
 
129
162
  | Project | Role |
130
163
  |---------|------|
131
- | [`systemictau`](https://pypi.org/project/systemictau/) | Full Systemic Tau library (τ_s, platform, studio) |
132
- | [`cctp-sddb-systemic-tau`](https://github.com/johelpadilla/cctp-sddb-systemic-tau) | Cardiac VF pilot using this nested RECD pipeline |
133
- | This package | Lightweight, installable nested-time / ordinal RECD core |
164
+ | [`systemictau`](https://pypi.org/project/systemictau/) | Full Systemic Tau library (τ_s, gate RECD, studio) — optional `[nested]` extra |
165
+ | [`systemictau-web`](https://github.com/johelpadilla/systemictau-web) | Streamlit app; depends on this package |
166
+ | [`excess3`](https://github.com/johelpadilla/excess3) | Methods + intro ES + primer (specification) |
167
+ | [`cctp-sddb-systemic-tau`](https://github.com/johelpadilla/cctp-sddb-systemic-tau) | Cardiac VF pilot |
168
+ | This package | Lightweight, installable nested-time / excess³ core |
134
169
 
135
170
  ## Citation
136
171
 
137
- If you use this software, please cite the CCTP/SDDB pilot archive:
172
+ If you use excess³ / Level-3 from this package, cite the **methods** preprint (canonical specification):
138
173
 
139
- > Padilla-Villanueva, J. (2026). *CCTP/SDDB: Systemic Tau and ordinal RECD before spontaneous ventricular fibrillation* (v1.0.1). Zenodo. https://doi.org/10.5281/zenodo.21270699
174
+ > Padilla-Villanueva, J. (2026). *excess³: A Pre-Specified Continuous Proxy for Order-3 Synergistic Surplus* (methods). Zenodo. https://doi.org/10.5281/zenodo.21385937
140
175
 
141
- And the theoretical nested-time / RECD framework as appropriate for your venue.
176
+ Software / related stack:
177
+
178
+ > Padilla-Villanueva, J. (2026). Systemic Tau software archive. Zenodo. https://doi.org/10.5281/zenodo.20576241
179
+
180
+ CCTP/SDDB pilot:
181
+
182
+ > Padilla-Villanueva, J. (2026). *CCTP/SDDB: Systemic Tau and ordinal RECD before spontaneous ventricular fibrillation* (v1.0.1). Zenodo. https://doi.org/10.5281/zenodo.21270699
142
183
 
143
184
  ```bibtex
144
185
  @software{padilla_nested_recd_2026,
145
186
  author = {Padilla-Villanueva, Johel},
146
- title = {nested-recd: Nested ordinal RECD levels},
187
+ title = {nested-recd: Nested ordinal RECD levels and continuous excess³},
147
188
  year = {2026},
148
189
  url = {https://github.com/johelpadilla/nested-recd},
149
- version = {0.1.0}
190
+ version = {0.2.0}
150
191
  }
151
192
  ```
152
193
 
@@ -1,21 +1,38 @@
1
- """Smoke and invariant tests for nested-recd."""
1
+ """Smoke and invariant tests for nested-recd (excess³ contract)."""
2
2
 
3
3
  import numpy as np
4
4
  import pytest
5
5
 
6
6
  from nested_recd import (
7
7
  __version__,
8
+ ALPHA_SYN,
9
+ ALPHA_SURP,
10
+ DEFAULT_THETA3,
11
+ DEFAULT_THETA3_CARDIO,
8
12
  compute_recd_from_conjunctions,
9
13
  compute_phi1,
14
+ compute_phi3,
15
+ compute_phi3_excess,
16
+ compute_excess3_window,
10
17
  compute_weighted_contributions,
11
18
  generate_multivariate_symbols,
19
+ mean_excess_pre_post,
12
20
  phase_shuffle_independent,
13
21
  random_permutation_independent,
22
+ surrogate_pvalue_delta_excess3,
14
23
  )
15
24
 
16
25
 
17
26
  def test_version():
18
- assert __version__ == "0.1.0"
27
+ assert __version__ == "0.2.0"
28
+
29
+
30
+ def test_weights_a_priori():
31
+ assert ALPHA_SYN == 0.6
32
+ assert ALPHA_SURP == 0.4
33
+ assert abs(ALPHA_SYN + ALPHA_SURP - 1.0) < 1e-12
34
+ assert DEFAULT_THETA3 == 0.10
35
+ assert DEFAULT_THETA3_CARDIO == 0.08
19
36
 
20
37
 
21
38
  def test_pipeline_shapes_and_bounds():
@@ -26,6 +43,7 @@ def test_pipeline_shapes_and_bounds():
26
43
  T = out["phi1"].shape[0]
27
44
  assert out["phi2"].shape == (T,)
28
45
  assert out["phi3"].shape == (T,)
46
+ assert out["excess3"].shape == (T,)
29
47
  assert out["delta_recd"].shape == (T,)
30
48
  assert out["T_recd"].shape == (T,)
31
49
  assert out["S"].shape[1] == 3
@@ -34,7 +52,12 @@ def test_pipeline_shapes_and_bounds():
34
52
  assert np.all((out["phi2"] >= 0) & (out["phi2"] <= 1))
35
53
  assert np.nanmax(out["phi3"]) <= 1.0
36
54
  assert np.nanmin(out["phi3"]) >= 0.0 or np.isnan(np.nanmin(out["phi3"]))
37
- assert np.all(np.diff(out["T_recd"]) >= -1e-12) # cumulative non-decreasing (nan-safe path)
55
+ assert np.all(np.diff(out["T_recd"]) >= -1e-12)
56
+
57
+ # Contract: continuous primary declared in params
58
+ assert out["params"]["level3_primary"] == "excess3"
59
+ assert out["params"]["alpha_syn"] == ALPHA_SYN
60
+ assert out["params"]["alpha_surp"] == ALPHA_SURP
38
61
 
39
62
 
40
63
  def test_identical_series_high_phi1():
@@ -85,3 +108,46 @@ def test_phi1_single_variable_zeros():
85
108
  S = np.zeros((50, 1), dtype=int)
86
109
  phi1 = compute_phi1(S)
87
110
  assert np.all(phi1 == 0)
111
+
112
+
113
+ def test_phi3_delegates_to_phi3_excess():
114
+ rng = np.random.default_rng(6)
115
+ S = generate_multivariate_symbols(rng.normal(size=(120, 3)).cumsum(0), m=3)
116
+ a = compute_phi3(S, window=13, theta=0.10)
117
+ b = compute_phi3_excess(S, window=13, theta=0.10)
118
+ np.testing.assert_allclose(a[0], b[0], equal_nan=True)
119
+ np.testing.assert_allclose(a[1], b[1], equal_nan=True)
120
+
121
+
122
+ def test_excess3_window_weights():
123
+ rng = np.random.default_rng(7)
124
+ # XOR-like synergistic lock: pairs weak, triple determined
125
+ n = 40
126
+ a = rng.integers(0, 2, size=n)
127
+ b = rng.integers(0, 2, size=n)
128
+ c = a ^ b
129
+ win = np.column_stack([a, b, c]).astype(int)
130
+ score = compute_excess3_window(win)
131
+ assert np.isfinite(score)
132
+ assert score >= 0.0
133
+
134
+
135
+ def test_mean_excess_pre_post():
136
+ x = np.array([0.0, 0.0, 1.0, 1.0, 1.0])
137
+ pre, post, delta = mean_excess_pre_post(x, split=2)
138
+ assert pre == 0.0
139
+ assert post == 1.0
140
+ assert delta == 1.0
141
+
142
+
143
+ def test_surrogate_pvalue_smoke():
144
+ rng = np.random.default_rng(8)
145
+ T = 180
146
+ # Independent noise: |Δ| should not be extreme
147
+ X = rng.normal(size=(T, 3))
148
+ res = surrogate_pvalue_delta_excess3(
149
+ X, split=T // 2, n_surr=19, method="permute", seed=1, window=11, stride=2
150
+ )
151
+ assert "p_value" in res
152
+ assert 0.0 <= res["p_value"] <= 1.0
153
+ assert res["n_surr"] >= 1
@@ -1,121 +0,0 @@
1
- # nested-recd
2
-
3
- [![PyPI version](https://img.shields.io/pypi/v/nested-recd.svg)](https://pypi.org/project/nested-recd/)
4
- [![Python](https://img.shields.io/pypi/pyversions/nested-recd.svg)](https://pypi.org/project/nested-recd/)
5
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
-
7
- **Nested ordinal RECD** — pure-NumPy implementation of nested ordinal conjunction levels (Φ₁, Φ₂, Φ₃) and λ-weighted Discrete Extramental Clock (RECD) accumulation.
8
-
9
- This is the standalone library behind the nested-time structure used in the CCTP/SDDB cardiac pilot and the experimental design in *Conversación de la naturaleza del tiempo*.
10
-
11
- ## Install
12
-
13
- ```bash
14
- pip install nested-recd
15
- ```
16
-
17
- From source:
18
-
19
- ```bash
20
- pip install git+https://github.com/johelpadilla/nested-recd.git
21
- ```
22
-
23
- ## Quick start
24
-
25
- ```python
26
- import numpy as np
27
- from nested_recd import compute_recd_from_conjunctions, compute_weighted_contributions
28
-
29
- # Multivariate series: shape (T, N)
30
- rng = np.random.default_rng(0)
31
- X = rng.normal(size=(800, 3)).cumsum(axis=0)
32
-
33
- out = compute_recd_from_conjunctions(X, m=3, d=4, theta3=0.10)
34
-
35
- print("mean Φ1:", float(np.nanmean(out["phi1"])))
36
- print("mean Φ2:", float(np.nanmean(out["phi2"])))
37
- print("Φ3 active fraction:", float(np.nanmean(out["phi3"] > 0)))
38
- print("final T_recd:", float(out["T_recd"][-1]))
39
-
40
- w = compute_weighted_contributions(out)
41
- print("frac level-3 contribution:", w["frac_contrib3"])
42
- ```
43
-
44
- Optional: supply a Systemic Tau series `tau_s` (aligned in time) so that λ is derived empirically:
45
-
46
- ```python
47
- out = compute_recd_from_conjunctions(X, tau_s=tau_s)
48
- ```
49
-
50
- Or fix the regime weight with a scalar / array override:
51
-
52
- ```python
53
- out = compute_recd_from_conjunctions(X, lam_override=0.5)
54
- ```
55
-
56
- ## What it computes
57
-
58
- | Symbol | Meaning |
59
- |--------|---------|
60
- | **Φ₁** | Co-occurrence of identical ordinal symbols across variable pairs |
61
- | **Φ₂** | Persistence of pairwise ordinal relations over lag `d` |
62
- | **Φ₃** | Higher-order synergy proxy (total-correlation excess + joint surprise) |
63
- | **λ** | Chaos / reorganization intensity (from `τ_s` or `lam_override`) |
64
- | **α(λ)** | Level weights: α₁ decays with λ; α₂, α₃ grow with λ |
65
- | **ΔRECD** | `α₁Φ₁ + α₂Φ₂ + α₃Φ₃` |
66
- | **T_recd** | Cumulative sum of ΔRECD (nested-time clock) |
67
-
68
- Ordinal symbols use Bandt–Pompe patterns (`m=3` by default).
69
-
70
- ## Surrogates
71
-
72
- ```python
73
- from nested_recd import phase_shuffle_independent, random_permutation_independent
74
-
75
- X_null = phase_shuffle_independent(X, seed=42) # IAAFT per column
76
- X_perm = random_permutation_independent(X, seed=42)
77
- ```
78
-
79
- ## API surface
80
-
81
- ```python
82
- from nested_recd import (
83
- compute_recd_from_conjunctions,
84
- compute_phi1, compute_phi2, compute_phi3,
85
- compute_lambda, alpha_weights, regime_lambda_proxy,
86
- compute_weighted_contributions, high_level3_rate,
87
- generate_multivariate_symbols,
88
- phase_shuffle_independent, generate_surrogate_ensemble,
89
- )
90
- ```
91
-
92
- ## Relation to other projects
93
-
94
- | Project | Role |
95
- |---------|------|
96
- | [`systemictau`](https://pypi.org/project/systemictau/) | Full Systemic Tau library (τ_s, platform, studio) |
97
- | [`cctp-sddb-systemic-tau`](https://github.com/johelpadilla/cctp-sddb-systemic-tau) | Cardiac VF pilot using this nested RECD pipeline |
98
- | This package | Lightweight, installable nested-time / ordinal RECD core |
99
-
100
- ## Citation
101
-
102
- If you use this software, please cite the CCTP/SDDB pilot archive:
103
-
104
- > Padilla-Villanueva, J. (2026). *CCTP/SDDB: Systemic Tau and ordinal RECD before spontaneous ventricular fibrillation* (v1.0.1). Zenodo. https://doi.org/10.5281/zenodo.21270699
105
-
106
- And the theoretical nested-time / RECD framework as appropriate for your venue.
107
-
108
- ```bibtex
109
- @software{padilla_nested_recd_2026,
110
- author = {Padilla-Villanueva, Johel},
111
- title = {nested-recd: Nested ordinal RECD levels},
112
- year = {2026},
113
- url = {https://github.com/johelpadilla/nested-recd},
114
- version = {0.1.0}
115
- }
116
- ```
117
-
118
- ## License
119
-
120
- MIT © 2026 Johel Padilla-Villanueva
121
- ORCID: [0000-0002-5797-6931](https://orcid.org/0000-0002-5797-6931)
File without changes
File without changes