anytimemonitor 0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jonathan Chevelu and Nelly Barbot (IRISA, Université de Rennes)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: anytimemonitor
3
+ Version: 0.9.0
4
+ Summary: Adaptive listening-test controller for TTS-MOS evaluation: anytime-valid confidence sequences, per-pair verdicts, two-lever (items/listeners) budget advice
5
+ Author: Jonathan Chevelu, Nelly Barbot
6
+ License: MIT
7
+ Project-URL: Homepage, https://expression.gitlabpages.inria.fr/replication/2026-doing-mos-well/
8
+ Project-URL: Source, https://github.com/jchevelu/AnytimeMonitor
9
+ Project-URL: Repository, https://gitlab.inria.fr/expression/tools/anytimemonitor
10
+ Project-URL: Archive, https://doi.org/10.5281/zenodo.21326813
11
+ Keywords: speech-synthesis,MOS,listening-test,anytime-valid,confidence-sequence,evaluation
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Scientific/Engineering
20
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: numpy>=1.26
25
+ Requires-Dist: pandas>=2.1
26
+ Requires-Dist: scipy>=1.11
27
+ Requires-Dist: matplotlib>=3.8
28
+ Provides-Extra: ui
29
+ Requires-Dist: streamlit>=1.50; extra == "ui"
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest>=7; extra == "test"
32
+ Dynamic: license-file
33
+
34
+ # AnytimeMonitor — adaptive controller for TTS-MOS listening tests
35
+
36
+ AnytimeMonitor watches a MOS listening test *while it runs* and tells
37
+ the test admin, for every pair of systems, whether the data already
38
+ supports a verdict — and if not, which lever (more items, or more
39
+ listeners) closes the pair fastest.
40
+
41
+ The statistical core is an **anytime-valid confidence sequence**
42
+ (Howard, Ramdas, McAuliffe & Sekhon, 2021) on per-item paired score
43
+ differences. Anytime validity means the coverage guarantee holds at
44
+ *any* data-dependent stopping time: the admin can look at the verdicts
45
+ as often as they like, stop whenever a pair resolves, and the reported
46
+ confidence interval is still valid. No peeking penalty bookkeeping, no
47
+ pre-registered sample size required.
48
+
49
+ This is a research prototype (v0.9). It accompanies a position paper
50
+ on budget allocation in TTS evaluation (under submission, *Computer
51
+ Speech & Language* Special Issue on the evaluation of speech and
52
+ speech synthesis).
53
+
54
+ ## What the admin sees
55
+
56
+ For each pair of systems (A, B), one of six verdicts:
57
+
58
+ | Verdict | Meaning |
59
+ | --- | --- |
60
+ | `STOP` | The anytime-valid CI excludes 0 (0 ∉ CI): the pair is separated. |
61
+ | `STOP_EQUIV` | Strict TOST equivalence: CI ⊂ ±δ_equiv *and* the half-width Ψ_eff ≤ δ_equiv/2. The pair is statistically equivalent within tolerance. |
62
+ | `CONTINUE +N` | Neither verdict yet. The tool predicts how many more **items** (+N) or **listeners** (+L) would close the pair, and which terminal (STOP or STOP_EQUIV) it is heading for. The headline is the cheaper option. |
63
+ | `WAIT` | The listener×system variance component cannot be estimated yet (fewer than 3 listeners with ≥ 2 paired items). Structural: keep collecting before trusting any verdict. |
64
+ | `CORPUS_LIMIT` | The item budget is exhausted *and* the listener lever can no longer close the pair. Terminal. |
65
+ | `LISTENER_LIMIT` | The listener budget is exhausted *and* the item lever can no longer close the pair. Terminal. |
66
+
67
+ > **Verdict naming.** `STOP` is the separation verdict the companion paper
68
+ > calls `STOP_SEP`. `STOP` is the current public label and will be **renamed to
69
+ > `STOP_SEP` in a future release** (to pair symmetrically with `STOP_EQUIV`);
70
+ > code matching on `"STOP"` should expect that change.
71
+
72
+ Verdicts are recomputed from scratch at every call — the controller is
73
+ queried on demand (a handful of times over a test lifecycle), not in a
74
+ real-time loop. A `STOP` is **not sticky**: if you keep collecting
75
+ past it, a borderline pair can revert to `CONTINUE`. This is honest
76
+ anytime-CI behaviour (the guarantee is on coverage, not on the
77
+ monotonicity of the observed difference); in practice the admin acts
78
+ on the first `STOP`.
79
+
80
+ ## Install
81
+
82
+ ```bash
83
+ pip install -e . # from the repository root (pyproject.toml)
84
+ pip install -e ".[ui]" # + Streamlit demo UI
85
+ pip install -e ".[test]" # + pytest; then run `pytest`
86
+ ```
87
+
88
+ Requires Python ≥ 3.10. Pinned versions used for development are in
89
+ `requirements.txt`.
90
+
91
+ ## Quick start
92
+
93
+ ### Programmatic
94
+
95
+ ```python
96
+ import pandas as pd
97
+ from anytimemonitor import AnytimeMonitor
98
+
99
+ ctrl = AnytimeMonitor(mde_threshold=0.30) # δ_sep, in MOS units
100
+
101
+ # Feed ratings as they arrive — long format, one row per judgment
102
+ ctrl.add_ratings(pd.DataFrame({
103
+ "listener_id": ..., # who rated
104
+ "item": ..., # which sentence / stimulus
105
+ "system": ..., # which TTS system
106
+ "rating": ..., # the MOS score
107
+ }))
108
+
109
+ print(ctrl.report()) # all pairs
110
+ dec = ctrl.decision("sysA", "sysB") # one pair, structured
111
+ print(dec.verdict, dec.ci_lo, dec.ci_hi, dec.n_more_items)
112
+ ```
113
+
114
+ ### CLI demo
115
+
116
+ Feed a ratings CSV to the controller batch by batch and watch the per-pair
117
+ verdicts tighten. With no arguments it runs on a bundled synthetic sample:
118
+
119
+ ```bash
120
+ python -m anytimemonitor.demo # bundled sample
121
+ python -m anytimemonitor.demo --input my_ratings.csv # your own long-format CSV
122
+ ```
123
+
124
+ ### Streamlit demo UI
125
+
126
+ ```bash
127
+ streamlit run anytimemonitor/ui/app.py
128
+ ```
129
+
130
+ Upload a ratings CSV (or pick a bundled synthetic example from
131
+ `sample_uploads/`), watch the controller replay the stream
132
+ batch-by-batch, and drag the time-machine slider to see the verdict
133
+ and two-lever advice at any past timestep. See `ui/README.md`.
134
+
135
+ ## How it works
136
+
137
+ For a pair (A, B), the controller builds per-item paired differences
138
+ d_j = mean rating of item j under A minus under B, and maintains a
139
+ sub-Gaussian normal-mixture confidence sequence on their mean
140
+ (Howard et al. 2021). Two ingredients adapt this to listening-test
141
+ data, where ratings are clustered by listener as well as by item:
142
+
143
+ - **Bivariate cluster bootstrap SE.** Resampling *items* gives the
144
+ i.i.d.-part SE that the anytime boundary multiplies; resampling
145
+ *listeners* gives the listener-pool floor ("plancher") — the
146
+ precision ceiling that no amount of extra items can break, added in
147
+ quadrature. When the data is too sparse to bootstrap, the
148
+ controller falls back to the confidence sequence's internal running
149
+ variance proxy.
150
+ - **Variance decomposition layer.** Method-of-moments estimates of
151
+ the system×item and residual components (per system, with a
152
+ Half-Cauchy Bayesian regulariser that prevents the
153
+ truncation-at-zero bias of raw MoM), plus a per-pair estimate of
154
+ the listener×system component. These feed the two-lever
155
+ prediction: +N items shrinks the interaction term, +L listeners
156
+ lowers the floor.
157
+
158
+ The price of anytime validity is a modestly wider interval than a
159
+ fixed-n test (a √log log factor), and deliberate conservatism early
160
+ on: below `avci_t_min_ebernstein` items the variance proxy falls back
161
+ to the worst-case Hoeffding bound, so the first batches produce wide
162
+ intervals. The controller is therefore most useful from a few dozen
163
+ rated items per system onward.
164
+
165
+ The internal architecture is documented in
166
+ [`Architecture.md`](Architecture.md) (and
167
+ [`ui/Architecture.md`](ui/Architecture.md) for the demo UI); the deeper design
168
+ notes for the confidence sequence are in [`AVCI_PLAN.md`](AVCI_PLAN.md).
169
+
170
+ ## Key parameters
171
+
172
+ All knobs are constructor arguments of `AnytimeMonitor`; the
173
+ defaults are deliberately conservative.
174
+
175
+ | Parameter | Default | Role |
176
+ | --- | --- | --- |
177
+ | `mde_threshold` (δ_sep) | (required) | The smallest effect worth *resolving* (MOS): seeds `delta_equiv` and the reported MDE. It does **not** gate `STOP` — separation fires on `0 ∉ CI` regardless. |
178
+ | `delta_equiv` | `None` (= `mde_threshold`) | δ_equiv — equivalence margin for `STOP_EQUIV`. Set it below `mde_threshold` to create an indifference zone. |
179
+ | `alpha` | 0.05 | Nominal level of the confidence sequence. |
180
+ | `power` | 0.80 | Only sets the reported-MDE scale factor (`z_{α/2} + z_power`); does not affect stopping. |
181
+ | `n_max_corpus`, `l_max_listeners` | `None` | Item / listener budgets; enable the `*_LIMIT` terminal verdicts. |
182
+ | `avci_n_planned` | `240` (§7 census median budget) | Planned item budget; the controller derives η = 1/√(N_planned) (Howard's a-priori rule). |
183
+ | `avci_eta` | `None` (→ 1/√`avci_n_planned`) | Set the Howard 2021 mixing parameter η by hand instead of via `avci_n_planned` — η is fixed a priori, never tuned to data. **Pass one or the other, not both** (raises otherwise). |
184
+ | `avci_lissys_floor_r0` | `0.36` (measured r_lisSys band frontier) | A-priori guard on the listener floor: bounds it below by √(2·σ²₀/L) with σ²₀ = 1.5·r₀·σ̂²_eval (the ×1.5 is an MC-calibrated validity margin). Protects coverage when a small pool under-estimates σ²_lisSys. `0` disables. |
185
+ | `avci_lissys_floor_sigma2` | `None` | Absolute σ²₀ override for the guard (expert path, no extra margin). **Exclusive with `avci_lissys_floor_r0`** (raises otherwise). The per-pair `Decision.lissys_prior_exceeded` flags when data-driven floor estimates exceed the declared guard (declared margin exhausted; consider raising r₀). |
186
+ | `avci_gamma_alpha_factor` | 1.0 | Multiplier `c` on the listener floor (√c on the plancher); `1.0` assumes σ²_lisSys ≤ σ²_int. Bypassed when the bootstrap plancher is available (the normal path). |
187
+ | `avci_proxy` | `"ebernstein"` | Variance proxy: running empirical variance (`ebernstein`) or worst-case `hoeffding`. |
188
+ | `avci_d_bounds` | `(-4, 4)` | Range of the per-item paired *difference* (not the raw rating scale): ±(rating max − min), so the default spans a rating range of 4. The normal path bootstraps the SE from the data and **ignores this** (it feeds only the sparse-data fallback and a reported diagnostic); widen it for a larger rating range if you drive `AnytimeCI` directly on sparse data. |
189
+ | `avci_t_min_ebernstein` | 10 | Below this item count the `ebernstein` proxy falls back to `hoeffding` (fallback path only). |
190
+ | `avci_ebernstein_floor_frac` | 0.01 | Relative floor on the empirical-variance proxy, as a fraction of the `hoeffding` bound (fallback path only). |
191
+ | `avci_bootstrap_B` | 200 | Bootstrap resamples per decision call. |
192
+ | `avci_bootstrap_seed` | 0 | PRNG seed for the bivariate cluster bootstrap. |
193
+ | `sigma2_ga_aggregator` | `"median"` | Which bootstrap-CI point anchors the listener floor: `median` / `mean` / `q95` (conservative) / `None`. |
194
+ | `cold_start_ga_ratio` | 2.7 | Conservative BTTF-anchored prior ratio (σ²_lisSys/σ²_int) used before the component is identifiable. |
195
+ | `avci_wait_min_listeners_kge2` | 3 | Structural WAIT gate: minimum listeners with ≥ 2 paired items. |
196
+ | `avci_wait_max_ci_ratio` | 5.0 | WAIT gate: max tolerated q95/median on the σ²_lisSys bootstrap CI before abstaining. |
197
+
198
+ > **Legacy naming.** Parameters and `Decision` fields carrying `ga` /
199
+ > `gamma_alpha` (e.g. `cold_start_ga_ratio`, `sigma2_ga_*`) name the
200
+ > listener×system variance the paper writes `σ²_lisSys`; the identifiers will be
201
+ > **renamed to `lissys` in a future release** (an API change, hence deferred).
202
+
203
+ ## Input data requirements
204
+
205
+ - Long format, columns `listener_id`, `item`, `system`, `rating`
206
+ (extra columns are ignored).
207
+ - Ratings on any bounded scale. The controller works on per-item paired
208
+ *differences*, whose extremes are ±(rating max − min); the default
209
+ `avci_d_bounds = (-4, 4)` spans a rating range of 4. On the normal path the
210
+ standard error is bootstrapped from the data, so the scale is handled
211
+ automatically and this default is safe on any scale; `avci_d_bounds` only
212
+ enters the sparse-data fallback (when the cluster bootstrap cannot run).
213
+ - The design must let listeners rate **multiple items per system**
214
+ (K ≥ 2 somewhere): a design where each listener rates a single item
215
+ — or each item is rated by a single listener — cannot identify the
216
+ listener×system component, and the controller will honestly report
217
+ `WAIT` forever rather than emit verdicts blind to that term.
218
+
219
+ ## Repository layout
220
+
221
+ ```text
222
+ anytimemonitor/
223
+ ├── __init__.py ← public API: AnytimeMonitor, Decision,
224
+ │ VarianceEstimate, estimate_components
225
+ ├── controller.py ← the AnytimeMonitor controller
226
+ ├── avci.py ← AnytimeCI — Howard 2021 sub-Gaussian
227
+ │ normal-mixture confidence sequence
228
+ ├── variance.py ← MoM variance components + Half-Cauchy
229
+ │ regulariser
230
+ ├── demo.py ← CLI example (batch-fed on a bundled sample)
231
+ ├── ui/ ← Streamlit demo (app.py; see ui/README.md + ui/Architecture.md)
232
+ ├── sample_uploads/ ← synthetic example CSVs for the UI
233
+ ├── Architecture.md ← internal architecture (controller + statistics)
234
+ ├── AVCI_PLAN.md ← deeper design notes for the confidence sequence
235
+ ├── TODO.md ← development roadmap
236
+ ├── archive/ ← retired code, notes and frozen runs
237
+ ├── LICENSE ← MIT
238
+ └── requirements.txt ← pinned development stack
239
+ ```
240
+
241
+ The validation and simulation harnesses (synthetic-data generator,
242
+ controller-vs-oracle simulator, false-positive-rate check) are not part
243
+ of the installed package; they ship with the companion paper's
244
+ reproduction code.
245
+
246
+ ## Status and caveats
247
+
248
+ - **Research prototype.** The API may still change. A `pytest` smoke-test
249
+ suite (`anytimemonitor/tests/`) checks the package runs end to end; the
250
+ controller has additionally been validated by simulation
251
+ (coverage under H₀, controller-vs-oracle gap decomposition) and
252
+ exercised on Blizzard Challenge data; it has not yet been plugged
253
+ into a live test platform.
254
+ - **Planning aid first.** The anytime-valid CI is a valid inference
255
+ under optional stopping, so it can be reported as such — but the
256
+ two-lever predictions (+N items / +L listeners) are noisy
257
+ projections from running variance estimates, not guarantees.
258
+ - A full-scale validation campaign (multi-seed, multi-corpus
259
+ false-positive and power sweeps) is planned alongside the companion
260
+ paper; the numbers shipped here come from smaller smoke-scale runs.
261
+
262
+ ## License
263
+
264
+ MIT — see [LICENSE](LICENSE).
265
+
266
+ ## Citation
267
+
268
+ A companion paper is under submission. Until it is out, please cite
269
+ this repository directly.
@@ -0,0 +1,236 @@
1
+ # AnytimeMonitor — adaptive controller for TTS-MOS listening tests
2
+
3
+ AnytimeMonitor watches a MOS listening test *while it runs* and tells
4
+ the test admin, for every pair of systems, whether the data already
5
+ supports a verdict — and if not, which lever (more items, or more
6
+ listeners) closes the pair fastest.
7
+
8
+ The statistical core is an **anytime-valid confidence sequence**
9
+ (Howard, Ramdas, McAuliffe & Sekhon, 2021) on per-item paired score
10
+ differences. Anytime validity means the coverage guarantee holds at
11
+ *any* data-dependent stopping time: the admin can look at the verdicts
12
+ as often as they like, stop whenever a pair resolves, and the reported
13
+ confidence interval is still valid. No peeking penalty bookkeeping, no
14
+ pre-registered sample size required.
15
+
16
+ This is a research prototype (v0.9). It accompanies a position paper
17
+ on budget allocation in TTS evaluation (under submission, *Computer
18
+ Speech & Language* Special Issue on the evaluation of speech and
19
+ speech synthesis).
20
+
21
+ ## What the admin sees
22
+
23
+ For each pair of systems (A, B), one of six verdicts:
24
+
25
+ | Verdict | Meaning |
26
+ | --- | --- |
27
+ | `STOP` | The anytime-valid CI excludes 0 (0 ∉ CI): the pair is separated. |
28
+ | `STOP_EQUIV` | Strict TOST equivalence: CI ⊂ ±δ_equiv *and* the half-width Ψ_eff ≤ δ_equiv/2. The pair is statistically equivalent within tolerance. |
29
+ | `CONTINUE +N` | Neither verdict yet. The tool predicts how many more **items** (+N) or **listeners** (+L) would close the pair, and which terminal (STOP or STOP_EQUIV) it is heading for. The headline is the cheaper option. |
30
+ | `WAIT` | The listener×system variance component cannot be estimated yet (fewer than 3 listeners with ≥ 2 paired items). Structural: keep collecting before trusting any verdict. |
31
+ | `CORPUS_LIMIT` | The item budget is exhausted *and* the listener lever can no longer close the pair. Terminal. |
32
+ | `LISTENER_LIMIT` | The listener budget is exhausted *and* the item lever can no longer close the pair. Terminal. |
33
+
34
+ > **Verdict naming.** `STOP` is the separation verdict the companion paper
35
+ > calls `STOP_SEP`. `STOP` is the current public label and will be **renamed to
36
+ > `STOP_SEP` in a future release** (to pair symmetrically with `STOP_EQUIV`);
37
+ > code matching on `"STOP"` should expect that change.
38
+
39
+ Verdicts are recomputed from scratch at every call — the controller is
40
+ queried on demand (a handful of times over a test lifecycle), not in a
41
+ real-time loop. A `STOP` is **not sticky**: if you keep collecting
42
+ past it, a borderline pair can revert to `CONTINUE`. This is honest
43
+ anytime-CI behaviour (the guarantee is on coverage, not on the
44
+ monotonicity of the observed difference); in practice the admin acts
45
+ on the first `STOP`.
46
+
47
+ ## Install
48
+
49
+ ```bash
50
+ pip install -e . # from the repository root (pyproject.toml)
51
+ pip install -e ".[ui]" # + Streamlit demo UI
52
+ pip install -e ".[test]" # + pytest; then run `pytest`
53
+ ```
54
+
55
+ Requires Python ≥ 3.10. Pinned versions used for development are in
56
+ `requirements.txt`.
57
+
58
+ ## Quick start
59
+
60
+ ### Programmatic
61
+
62
+ ```python
63
+ import pandas as pd
64
+ from anytimemonitor import AnytimeMonitor
65
+
66
+ ctrl = AnytimeMonitor(mde_threshold=0.30) # δ_sep, in MOS units
67
+
68
+ # Feed ratings as they arrive — long format, one row per judgment
69
+ ctrl.add_ratings(pd.DataFrame({
70
+ "listener_id": ..., # who rated
71
+ "item": ..., # which sentence / stimulus
72
+ "system": ..., # which TTS system
73
+ "rating": ..., # the MOS score
74
+ }))
75
+
76
+ print(ctrl.report()) # all pairs
77
+ dec = ctrl.decision("sysA", "sysB") # one pair, structured
78
+ print(dec.verdict, dec.ci_lo, dec.ci_hi, dec.n_more_items)
79
+ ```
80
+
81
+ ### CLI demo
82
+
83
+ Feed a ratings CSV to the controller batch by batch and watch the per-pair
84
+ verdicts tighten. With no arguments it runs on a bundled synthetic sample:
85
+
86
+ ```bash
87
+ python -m anytimemonitor.demo # bundled sample
88
+ python -m anytimemonitor.demo --input my_ratings.csv # your own long-format CSV
89
+ ```
90
+
91
+ ### Streamlit demo UI
92
+
93
+ ```bash
94
+ streamlit run anytimemonitor/ui/app.py
95
+ ```
96
+
97
+ Upload a ratings CSV (or pick a bundled synthetic example from
98
+ `sample_uploads/`), watch the controller replay the stream
99
+ batch-by-batch, and drag the time-machine slider to see the verdict
100
+ and two-lever advice at any past timestep. See `ui/README.md`.
101
+
102
+ ## How it works
103
+
104
+ For a pair (A, B), the controller builds per-item paired differences
105
+ d_j = mean rating of item j under A minus under B, and maintains a
106
+ sub-Gaussian normal-mixture confidence sequence on their mean
107
+ (Howard et al. 2021). Two ingredients adapt this to listening-test
108
+ data, where ratings are clustered by listener as well as by item:
109
+
110
+ - **Bivariate cluster bootstrap SE.** Resampling *items* gives the
111
+ i.i.d.-part SE that the anytime boundary multiplies; resampling
112
+ *listeners* gives the listener-pool floor ("plancher") — the
113
+ precision ceiling that no amount of extra items can break, added in
114
+ quadrature. When the data is too sparse to bootstrap, the
115
+ controller falls back to the confidence sequence's internal running
116
+ variance proxy.
117
+ - **Variance decomposition layer.** Method-of-moments estimates of
118
+ the system×item and residual components (per system, with a
119
+ Half-Cauchy Bayesian regulariser that prevents the
120
+ truncation-at-zero bias of raw MoM), plus a per-pair estimate of
121
+ the listener×system component. These feed the two-lever
122
+ prediction: +N items shrinks the interaction term, +L listeners
123
+ lowers the floor.
124
+
125
+ The price of anytime validity is a modestly wider interval than a
126
+ fixed-n test (a √log log factor), and deliberate conservatism early
127
+ on: below `avci_t_min_ebernstein` items the variance proxy falls back
128
+ to the worst-case Hoeffding bound, so the first batches produce wide
129
+ intervals. The controller is therefore most useful from a few dozen
130
+ rated items per system onward.
131
+
132
+ The internal architecture is documented in
133
+ [`Architecture.md`](Architecture.md) (and
134
+ [`ui/Architecture.md`](ui/Architecture.md) for the demo UI); the deeper design
135
+ notes for the confidence sequence are in [`AVCI_PLAN.md`](AVCI_PLAN.md).
136
+
137
+ ## Key parameters
138
+
139
+ All knobs are constructor arguments of `AnytimeMonitor`; the
140
+ defaults are deliberately conservative.
141
+
142
+ | Parameter | Default | Role |
143
+ | --- | --- | --- |
144
+ | `mde_threshold` (δ_sep) | (required) | The smallest effect worth *resolving* (MOS): seeds `delta_equiv` and the reported MDE. It does **not** gate `STOP` — separation fires on `0 ∉ CI` regardless. |
145
+ | `delta_equiv` | `None` (= `mde_threshold`) | δ_equiv — equivalence margin for `STOP_EQUIV`. Set it below `mde_threshold` to create an indifference zone. |
146
+ | `alpha` | 0.05 | Nominal level of the confidence sequence. |
147
+ | `power` | 0.80 | Only sets the reported-MDE scale factor (`z_{α/2} + z_power`); does not affect stopping. |
148
+ | `n_max_corpus`, `l_max_listeners` | `None` | Item / listener budgets; enable the `*_LIMIT` terminal verdicts. |
149
+ | `avci_n_planned` | `240` (§7 census median budget) | Planned item budget; the controller derives η = 1/√(N_planned) (Howard's a-priori rule). |
150
+ | `avci_eta` | `None` (→ 1/√`avci_n_planned`) | Set the Howard 2021 mixing parameter η by hand instead of via `avci_n_planned` — η is fixed a priori, never tuned to data. **Pass one or the other, not both** (raises otherwise). |
151
+ | `avci_lissys_floor_r0` | `0.36` (measured r_lisSys band frontier) | A-priori guard on the listener floor: bounds it below by √(2·σ²₀/L) with σ²₀ = 1.5·r₀·σ̂²_eval (the ×1.5 is an MC-calibrated validity margin). Protects coverage when a small pool under-estimates σ²_lisSys. `0` disables. |
152
+ | `avci_lissys_floor_sigma2` | `None` | Absolute σ²₀ override for the guard (expert path, no extra margin). **Exclusive with `avci_lissys_floor_r0`** (raises otherwise). The per-pair `Decision.lissys_prior_exceeded` flags when data-driven floor estimates exceed the declared guard (declared margin exhausted; consider raising r₀). |
153
+ | `avci_gamma_alpha_factor` | 1.0 | Multiplier `c` on the listener floor (√c on the plancher); `1.0` assumes σ²_lisSys ≤ σ²_int. Bypassed when the bootstrap plancher is available (the normal path). |
154
+ | `avci_proxy` | `"ebernstein"` | Variance proxy: running empirical variance (`ebernstein`) or worst-case `hoeffding`. |
155
+ | `avci_d_bounds` | `(-4, 4)` | Range of the per-item paired *difference* (not the raw rating scale): ±(rating max − min), so the default spans a rating range of 4. The normal path bootstraps the SE from the data and **ignores this** (it feeds only the sparse-data fallback and a reported diagnostic); widen it for a larger rating range if you drive `AnytimeCI` directly on sparse data. |
156
+ | `avci_t_min_ebernstein` | 10 | Below this item count the `ebernstein` proxy falls back to `hoeffding` (fallback path only). |
157
+ | `avci_ebernstein_floor_frac` | 0.01 | Relative floor on the empirical-variance proxy, as a fraction of the `hoeffding` bound (fallback path only). |
158
+ | `avci_bootstrap_B` | 200 | Bootstrap resamples per decision call. |
159
+ | `avci_bootstrap_seed` | 0 | PRNG seed for the bivariate cluster bootstrap. |
160
+ | `sigma2_ga_aggregator` | `"median"` | Which bootstrap-CI point anchors the listener floor: `median` / `mean` / `q95` (conservative) / `None`. |
161
+ | `cold_start_ga_ratio` | 2.7 | Conservative BTTF-anchored prior ratio (σ²_lisSys/σ²_int) used before the component is identifiable. |
162
+ | `avci_wait_min_listeners_kge2` | 3 | Structural WAIT gate: minimum listeners with ≥ 2 paired items. |
163
+ | `avci_wait_max_ci_ratio` | 5.0 | WAIT gate: max tolerated q95/median on the σ²_lisSys bootstrap CI before abstaining. |
164
+
165
+ > **Legacy naming.** Parameters and `Decision` fields carrying `ga` /
166
+ > `gamma_alpha` (e.g. `cold_start_ga_ratio`, `sigma2_ga_*`) name the
167
+ > listener×system variance the paper writes `σ²_lisSys`; the identifiers will be
168
+ > **renamed to `lissys` in a future release** (an API change, hence deferred).
169
+
170
+ ## Input data requirements
171
+
172
+ - Long format, columns `listener_id`, `item`, `system`, `rating`
173
+ (extra columns are ignored).
174
+ - Ratings on any bounded scale. The controller works on per-item paired
175
+ *differences*, whose extremes are ±(rating max − min); the default
176
+ `avci_d_bounds = (-4, 4)` spans a rating range of 4. On the normal path the
177
+ standard error is bootstrapped from the data, so the scale is handled
178
+ automatically and this default is safe on any scale; `avci_d_bounds` only
179
+ enters the sparse-data fallback (when the cluster bootstrap cannot run).
180
+ - The design must let listeners rate **multiple items per system**
181
+ (K ≥ 2 somewhere): a design where each listener rates a single item
182
+ — or each item is rated by a single listener — cannot identify the
183
+ listener×system component, and the controller will honestly report
184
+ `WAIT` forever rather than emit verdicts blind to that term.
185
+
186
+ ## Repository layout
187
+
188
+ ```text
189
+ anytimemonitor/
190
+ ├── __init__.py ← public API: AnytimeMonitor, Decision,
191
+ │ VarianceEstimate, estimate_components
192
+ ├── controller.py ← the AnytimeMonitor controller
193
+ ├── avci.py ← AnytimeCI — Howard 2021 sub-Gaussian
194
+ │ normal-mixture confidence sequence
195
+ ├── variance.py ← MoM variance components + Half-Cauchy
196
+ │ regulariser
197
+ ├── demo.py ← CLI example (batch-fed on a bundled sample)
198
+ ├── ui/ ← Streamlit demo (app.py; see ui/README.md + ui/Architecture.md)
199
+ ├── sample_uploads/ ← synthetic example CSVs for the UI
200
+ ├── Architecture.md ← internal architecture (controller + statistics)
201
+ ├── AVCI_PLAN.md ← deeper design notes for the confidence sequence
202
+ ├── TODO.md ← development roadmap
203
+ ├── archive/ ← retired code, notes and frozen runs
204
+ ├── LICENSE ← MIT
205
+ └── requirements.txt ← pinned development stack
206
+ ```
207
+
208
+ The validation and simulation harnesses (synthetic-data generator,
209
+ controller-vs-oracle simulator, false-positive-rate check) are not part
210
+ of the installed package; they ship with the companion paper's
211
+ reproduction code.
212
+
213
+ ## Status and caveats
214
+
215
+ - **Research prototype.** The API may still change. A `pytest` smoke-test
216
+ suite (`anytimemonitor/tests/`) checks the package runs end to end; the
217
+ controller has additionally been validated by simulation
218
+ (coverage under H₀, controller-vs-oracle gap decomposition) and
219
+ exercised on Blizzard Challenge data; it has not yet been plugged
220
+ into a live test platform.
221
+ - **Planning aid first.** The anytime-valid CI is a valid inference
222
+ under optional stopping, so it can be reported as such — but the
223
+ two-lever predictions (+N items / +L listeners) are noisy
224
+ projections from running variance estimates, not guarantees.
225
+ - A full-scale validation campaign (multi-seed, multi-corpus
226
+ false-positive and power sweeps) is planned alongside the companion
227
+ paper; the numbers shipped here come from smaller smoke-scale runs.
228
+
229
+ ## License
230
+
231
+ MIT — see [LICENSE](LICENSE).
232
+
233
+ ## Citation
234
+
235
+ A companion paper is under submission. Until it is out, please cite
236
+ this repository directly.
@@ -0,0 +1,21 @@
1
+ """anytimemonitor — adaptive controller for TTS-MOS listening tests.
2
+
3
+ Public API:
4
+ AnytimeMonitor — stateful controller built on an anytime-valid
5
+ confidence sequence (per-item paired diffs).
6
+ Add ratings, query for per-pair verdicts and
7
+ two-lever (+items / +listeners) advice at any
8
+ data-dependent time.
9
+ Decision — dataclass returned by AnytimeMonitor.decision().
10
+
11
+ See README.md for use case and architecture.
12
+ """
13
+ from .controller import Decision, AnytimeMonitor
14
+ from .variance import VarianceEstimate, estimate_components
15
+
16
+ __all__ = [
17
+ "AnytimeMonitor",
18
+ "Decision",
19
+ "VarianceEstimate",
20
+ "estimate_components",
21
+ ]