Spaces:
Paused
Paused
Download tests/test_seqeval/test_units_sequential.py from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 5.92 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/tests/test_seqeval/test_units_sequential.py
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/tests/test_seqeval/test_units_sequential.py
-
curl -L -o test_units_sequential.py https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/tests/test_seqeval/test_units_sequential.py
5.92 kB
| """Statistical machinery: clustering handling, confidence sequences, | |
| three-outcome logic, e-process validity behaviors.""" | |
| from __future__ import annotations | |
| import numpy as np | |
| from sensorflow.seqeval import units | |
| from sensorflow.seqeval.sequential import (DECISION_INSUFFICIENT, | |
| DECISION_PASS, | |
| DECISION_REGRESSION, | |
| EmpiricalBernsteinCS, | |
| PairedSequentialTest) | |
| def _clustered_values(rng, n_clusters, cluster_size, effect_sd=0.45, noise_sd=0.1): | |
| z = rng.normal(0.0, effect_sd, size=n_clusters) | |
| d = np.repeat(z, cluster_size) + rng.normal(0.0, noise_sd, size=n_clusters * cluster_size) | |
| d = np.tanh(d) | |
| cid = np.repeat(np.arange(n_clusters), cluster_size) | |
| return d, cid | |
| class TestClustering: | |
| def test_effective_n_reduced_by_correlation(self): | |
| rng = np.random.default_rng(3) | |
| d, cid = _clustered_values(rng, n_clusters=60, cluster_size=20) | |
| summary = units.cluster_summary(d, cid) | |
| assert summary["n"] == 1200 | |
| assert summary["icc"] > 0.4 | |
| assert summary["design_effect"] > 5 | |
| assert summary["n_effective"] < 0.5 * summary["n"] | |
| def test_iid_data_has_no_design_effect(self): | |
| rng = np.random.default_rng(4) | |
| d = rng.normal(0, 0.3, size=1200) | |
| cid = np.repeat(np.arange(60), 20) # arbitrary grouping of iid data | |
| summary = units.cluster_summary(d, cid) | |
| assert summary["design_effect"] < 1.5 | |
| def test_cluster_aware_sequence_wider_than_naive(self): | |
| """Feeding correlated objects as if iid produces an (invalidly) tighter | |
| interval than the correct cluster-mean treatment.""" | |
| rng = np.random.default_rng(5) | |
| d, cid = _clustered_values(rng, n_clusters=50, cluster_size=24) | |
| naive = PairedSequentialTest(delta=0.01, alpha=0.05) | |
| naive.update_clusters(d) # WRONG: objects as units | |
| means, _ = units.cluster_units(d, cid) | |
| correct = PairedSequentialTest(delta=0.01, alpha=0.05) | |
| correct.update_clusters(means) # RIGHT: clusters as units | |
| naive_width = naive.delta_interval()[1] - naive.delta_interval()[0] | |
| correct_width = correct.delta_interval()[1] - correct.delta_interval()[0] | |
| assert correct_width > naive_width * 1.5 | |
| def test_cluster_means_stay_bounded(self): | |
| rng = np.random.default_rng(6) | |
| d = rng.choice([-1.0, 0.0, 1.0], size=500) | |
| cid = rng.integers(0, 40, size=500) | |
| w = rng.uniform(0.5, 5.0, size=500) | |
| means, sizes = units.cluster_units(d, cid, weights=w) | |
| assert np.all(means <= 1.0) and np.all(means >= -1.0) | |
| assert sizes.sum() == 500 | |
| class TestConfidenceSequence: | |
| def test_cs_covers_true_mean_and_shrinks(self): | |
| rng = np.random.default_rng(7) | |
| cs = EmpiricalBernsteinCS(alpha=0.05) | |
| widths = [] | |
| for chunk in range(20): | |
| for x in rng.binomial(1, 0.7, size=100): | |
| cs.update(float(x)) | |
| widths.append(cs.upper - cs.lower) | |
| assert cs.lower <= 0.7 <= cs.upper | |
| assert widths[-1] < widths[0] | |
| assert widths[-1] < 0.12 | |
| def test_running_intersection_never_widens(self): | |
| rng = np.random.default_rng(8) | |
| cs = EmpiricalBernsteinCS(alpha=0.05) | |
| prev = (0.0, 1.0) | |
| for x in rng.random(2000): | |
| cs.update(x) | |
| assert cs.lower >= prev[0] - 1e-12 | |
| assert cs.upper <= prev[1] + 1e-12 | |
| prev = (cs.lower, cs.upper) | |
| class TestThreeOutcome: | |
| def test_regression_detected(self): | |
| rng = np.random.default_rng(9) | |
| t = PairedSequentialTest(delta=0.005, alpha=0.01) | |
| while t.n_clusters < 5000 and t.decision == DECISION_INSUFFICIENT: | |
| d = rng.choice([-1.0, 0.0, 1.0], p=[0.05, 0.93, 0.02], size=200) # Delta=-3pp | |
| t.update_clusters(d) | |
| t.evaluate() | |
| assert t.decision == DECISION_REGRESSION | |
| def test_pass_on_true_equivalence(self): | |
| rng = np.random.default_rng(10) | |
| t = PairedSequentialTest(delta=0.02, alpha=0.05) | |
| while t.n_clusters < 8000 and t.decision == DECISION_INSUFFICIENT: | |
| d = rng.choice([-1.0, 0.0, 1.0], p=[0.01, 0.98, 0.01], size=200) # Delta=0 | |
| t.update_clusters(d) | |
| t.evaluate() | |
| assert t.decision == DECISION_PASS | |
| def test_tiny_sample_is_insufficient_never_pass(self): | |
| rng = np.random.default_rng(11) | |
| t = PairedSequentialTest(delta=0.005, alpha=0.05) | |
| t.update_clusters(rng.choice([-1.0, 0.0, 1.0], p=[0.02, 0.96, 0.02], size=30)) | |
| t.evaluate() | |
| assert t.decision == DECISION_INSUFFICIENT | |
| def test_bayes_posterior_tracks_regression(self): | |
| t = PairedSequentialTest(delta=0.005) | |
| b = np.ones(2000, dtype=bool) | |
| c = np.ones(2000, dtype=bool) | |
| c[:80] = False # 4pp regression pairs, no improvements | |
| t.record_objects(b, c) | |
| assert t.bayes_p_regression() > 0.99 | |
| def test_no_false_regression_under_null_monte_carlo(self): | |
| """Anytime-valid type-I control at the single-test level: run many null | |
| streams with continuous monitoring; false REGRESSION rate <= alpha.""" | |
| alpha = 0.05 | |
| false_rejections = 0 | |
| reps = 60 | |
| for rep in range(reps): | |
| rng = np.random.default_rng(1000 + rep) | |
| t = PairedSequentialTest(delta=0.005, alpha=alpha) | |
| for _ in range(40): # 40 sequential looks | |
| d = rng.choice([-1.0, 0.0, 1.0], p=[0.015, 0.97, 0.015], size=50) | |
| t.update_clusters(d) | |
| if t.evaluate() == DECISION_REGRESSION: | |
| false_rejections += 1 | |
| break | |
| assert false_rejections / reps <= alpha + 0.02 | |