|
15 | 15 | ) |
16 | 16 |
|
17 | 17 |
|
| 18 | +@pytest.fixture(scope="session") |
| 19 | +def basic_setup(): |
| 20 | + """Basic setup shared across LC2ST tests.""" |
| 21 | + dim = 2 |
| 22 | + prior = uniform_prior_gaussian_mixture(dim=dim) |
| 23 | + simulator = gaussian_mixture |
| 24 | + return {"dim": dim, "prior": prior, "simulator": simulator} |
| 25 | + |
| 26 | + |
| 27 | +@pytest.fixture(scope="session") |
| 28 | +def npe_factory(basic_setup): |
| 29 | + """Factory for creating NPE models with different training parameters.""" |
| 30 | + |
| 31 | + def _create_npe(num_simulations, max_epochs=None): |
| 32 | + prior = basic_setup["prior"] |
| 33 | + simulator = basic_setup["simulator"] |
| 34 | + |
| 35 | + theta_train = prior.sample((num_simulations,)) |
| 36 | + x_train = simulator(theta_train) |
| 37 | + |
| 38 | + inference = NPE(prior, density_estimator='maf') |
| 39 | + inference = inference.append_simulations(theta=theta_train, x=x_train) |
| 40 | + |
| 41 | + train_kwargs = {"training_batch_size": 100} |
| 42 | + if max_epochs: |
| 43 | + train_kwargs["max_num_epochs"] = max_epochs |
| 44 | + |
| 45 | + return inference.train(**train_kwargs) |
| 46 | + |
| 47 | + return _create_npe |
| 48 | + |
| 49 | + |
| 50 | +@pytest.fixture(scope="session") |
| 51 | +def badly_trained_npe(npe_factory): |
| 52 | + return npe_factory(num_simulations=100, max_epochs=1) |
| 53 | + |
| 54 | + |
| 55 | +@pytest.fixture(scope="session") |
| 56 | +def well_trained_npe(npe_factory): |
| 57 | + return npe_factory(num_simulations=10_000) |
| 58 | + |
| 59 | + |
| 60 | +@pytest.fixture(scope="session") |
| 61 | +def calibration_data(basic_setup, badly_trained_npe): |
| 62 | + """Calibration data for LC2ST tests.""" |
| 63 | + prior = basic_setup["prior"] |
| 64 | + simulator = basic_setup["simulator"] |
| 65 | + npe = badly_trained_npe |
| 66 | + |
| 67 | + num_cal = 100 # Smaller for quick tests |
| 68 | + thetas = prior.sample((num_cal,)) |
| 69 | + xs = simulator(thetas) |
| 70 | + posterior_samples = npe.sample((1,), xs).reshape(-1, thetas.shape[-1]).detach() |
| 71 | + |
| 72 | + return {"thetas": thetas, "xs": xs, "posterior_samples": posterior_samples} |
| 73 | + |
| 74 | + |
18 | 75 | @pytest.mark.parametrize("method", (LC2ST, LC2ST_NF)) |
19 | 76 | @pytest.mark.parametrize("classifier", ('mlp', 'random_forest', MLPClassifier)) |
20 | 77 | @pytest.mark.parametrize("cv_folds", (1, 2)) |
21 | 78 | @pytest.mark.parametrize("num_ensemble", (1, 3)) |
22 | 79 | @pytest.mark.parametrize("z_score", (True, False)) |
23 | | -def test_running_lc2st(method, classifier, cv_folds, num_ensemble, z_score): |
| 80 | +def test_running_lc2st( |
| 81 | + method, |
| 82 | + classifier, |
| 83 | + cv_folds, |
| 84 | + num_ensemble, |
| 85 | + z_score, |
| 86 | + calibration_data, |
| 87 | + badly_trained_npe, |
| 88 | +): |
24 | 89 | """Tests running inference, LC2ST-(NF) and then getting test quantities.""" |
25 | 90 |
|
26 | | - num_train = 100 |
27 | | - num_cal = 100 |
28 | 91 | num_eval = 100 |
29 | 92 | num_trials_null = 2 |
30 | 93 |
|
31 | | - # task |
32 | | - dim = 2 |
33 | | - prior = uniform_prior_gaussian_mixture(dim=dim) |
34 | | - simulator = gaussian_mixture |
35 | | - |
36 | | - # training data for the density estimator |
37 | | - theta_train = prior.sample((num_train,)) |
38 | | - x_train = simulator(theta_train) |
39 | | - |
40 | | - # Train the neural posterior estimators |
41 | | - inference = NPE(prior, density_estimator='maf') |
42 | | - inference = inference.append_simulations(theta=theta_train, x=x_train) |
43 | | - npe = inference.train(training_batch_size=100, max_num_epochs=1) |
44 | | - |
45 | | - # calibration data for the test |
46 | | - thetas = prior.sample((num_cal,)) |
47 | | - xs = simulator(thetas) |
48 | | - posterior_samples = ( |
49 | | - npe.sample((1,), condition=xs).reshape(-1, thetas.shape[-1]).detach() |
50 | | - ) |
51 | | - assert posterior_samples.shape == thetas.shape |
| 94 | + # Get data from fixtures |
| 95 | + thetas = calibration_data["thetas"] |
| 96 | + xs = calibration_data["xs"] |
| 97 | + posterior_samples = calibration_data["posterior_samples"] |
| 98 | + npe = badly_trained_npe |
52 | 99 |
|
53 | 100 | if method == LC2ST: |
54 | 101 | theta_o = ( |
@@ -107,33 +154,19 @@ def test_running_lc2st(method, classifier, cv_folds, num_ensemble, z_score): |
107 | 154 |
|
108 | 155 | @pytest.mark.slow |
109 | 156 | @pytest.mark.parametrize("method", (LC2ST, LC2ST_NF)) |
110 | | -def test_lc2st_true_positiv_rate(method): |
| 157 | +def test_lc2st_true_positiv_rate(method, basic_setup, badly_trained_npe): |
111 | 158 | """Tests the true positiv rate of the LC2ST-(NF) test: |
112 | 159 | for a "bad" estimator, the LC2ST-(NF) should reject the null hypothesis.""" |
113 | 160 | num_runs = 100 |
114 | 161 | confidence_level = 0.95 |
115 | 162 |
|
116 | | - # use small num_train and num_epochs to obtain "bad" estimator |
117 | | - # (no convergence to the true posterior) |
118 | | - num_train = 100 |
119 | | - num_epochs = 2 |
120 | | - |
121 | 163 | num_cal = 1_000 |
122 | 164 | num_eval = 10_000 |
123 | 165 |
|
124 | | - # task |
125 | | - dim = 2 |
126 | | - prior = uniform_prior_gaussian_mixture(dim=dim) |
127 | | - simulator = gaussian_mixture |
128 | | - |
129 | | - # training data for the density estimator |
130 | | - theta_train = prior.sample((num_train,)) |
131 | | - x_train = simulator(theta_train) |
132 | | - |
133 | | - # Train the neural posterior estimators |
134 | | - inference = NPE(prior, density_estimator='maf') |
135 | | - inference = inference.append_simulations(theta=theta_train, x=x_train) |
136 | | - npe = inference.train(training_batch_size=100, max_num_epochs=num_epochs) |
| 166 | + # Get data from fixtures |
| 167 | + prior = basic_setup["prior"] |
| 168 | + simulator = basic_setup["simulator"] |
| 169 | + npe = badly_trained_npe |
137 | 170 |
|
138 | 171 | thetas = prior.sample((num_cal,)) |
139 | 172 | xs = simulator(thetas) |
@@ -186,32 +219,19 @@ def test_lc2st_true_positiv_rate(method): |
186 | 219 |
|
187 | 220 | @pytest.mark.slow |
188 | 221 | @pytest.mark.parametrize("method", (LC2ST, LC2ST_NF)) |
189 | | -def test_lc2st_false_positiv_rate(method, set_seed): |
| 222 | +def test_lc2st_false_positiv_rate(method, basic_setup, well_trained_npe, set_seed): |
190 | 223 | """Tests the false positiv rate of the LC2ST-(NF) test: |
191 | 224 | for a "good" estimator, the LC2ST-(NF) should not reject the null hypothesis.""" |
192 | 225 | num_runs = 100 |
193 | 226 | confidence_level = 0.95 |
194 | 227 |
|
195 | | - # use big num_train and num_epochs to obtain "good" estimator |
196 | | - # (convergence of the estimator) |
197 | | - num_train = 10_000 |
198 | | - |
199 | 228 | num_cal = 1_000 |
200 | 229 | num_eval = 10_000 |
201 | 230 |
|
202 | | - # task |
203 | | - dim = 2 |
204 | | - prior = uniform_prior_gaussian_mixture(dim=dim) |
205 | | - simulator = gaussian_mixture |
206 | | - |
207 | | - # training data for the density estimator |
208 | | - theta_train = prior.sample((num_train,)) |
209 | | - x_train = simulator(theta_train) |
210 | | - |
211 | | - # Train the neural posterior estimators |
212 | | - inference = NPE(prior, density_estimator='maf') |
213 | | - inference = inference.append_simulations(theta=theta_train, x=x_train) |
214 | | - npe = inference.train(training_batch_size=100) |
| 231 | + # Get data from fixtures |
| 232 | + prior = basic_setup["prior"] |
| 233 | + simulator = basic_setup["simulator"] |
| 234 | + npe = well_trained_npe |
215 | 235 |
|
216 | 236 | thetas = prior.sample((num_cal,)) |
217 | 237 | xs = simulator(thetas) |
|
0 commit comments