Skip to content

Commit e0a3f5c

Browse files
committed
Merge branch 'main' into feature/64-importance_sampling_unit_test
2 parents d1a0c09 + 489afc3 commit e0a3f5c

4 files changed

Lines changed: 59 additions & 20 deletions

File tree

examples/crest_heights_north_sea/doe.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from collections.abc import Callable
2222

2323
import matplotlib.pyplot as plt
24+
import pandas as pd
2425
import torch
2526
from ax import (
2627
Experiment,
@@ -72,14 +73,14 @@
7273
large_dataset_var = QOI_ESTIMATOR.var(ests)
7374

7475

75-
# %%
7676
def run_trials(
7777
experiment: Experiment,
7878
warm_up_generator: Callable[[Experiment], GeneratorRun],
7979
doe_generator: Callable[[Experiment], GeneratorRun],
8080
warm_up_runs: int = 3,
8181
doe_runs: int = 15,
82-
) -> None:
82+
stopping_criteria: Callable[[Experiment], bool] | None = None,
83+
) -> int:
8384
"""Helper function for running trials for an experiment and returning the QOI results using QoI metric.
8485
8586
Args:
@@ -88,8 +89,12 @@ def run_trials(
8889
doe_generator: The generator being used to perform the DoE.
8990
warm_up_runs: Number of warm-up runs to perform before starting the DoE.
9091
doe_runs: Number of DoE runs to perform.
92+
stopping_criteria: Optional function that takes an experiment and returns True if a given
93+
stopping criteria is met. If this stopping criteria is not met after `doe_runs` iterations, the function will
94+
return the number of iterations run.
9195
9296
"""
97+
# Warm-up phase
9398
for i in range(doe_runs + 1):
9499
if i == 0:
95100
for _ in range(warm_up_runs):
@@ -105,12 +110,48 @@ def run_trials(
105110
_ = trial.mark_completed()
106111
print(f"iter {i} done")
107112

113+
# Check stopping criteria after each DoE iteration
114+
if stopping_criteria is not None and stopping_criteria(experiment):
115+
print(f"Stopping criteria met after {i} DoE iterations")
116+
return i
117+
118+
return doe_runs + warm_up_runs
119+
120+
121+
def sem_stopping_criteria(experiment: Experiment, sem_threshold: float = 0.145, metric_name: str = "QoIMetric") -> bool:
122+
"""Stopping criteria based on standard error of the mean (SEM) of QoI metric of the GP.
123+
124+
Args:
125+
experiment: The experiment to check
126+
sem_threshold: SEM threshold for stopping criteria
127+
metric_name: Name of the metric to check for stopping criteria
128+
129+
Returns:
130+
True if stopping criteria is met (SEM below threshold), False otherwise
131+
"""
132+
metrics = experiment.fetch_data()
133+
qoi_metrics = metrics.df[metrics.df["metric_name"] == metric_name]
134+
135+
if len(qoi_metrics) == 0:
136+
print(f"No {metric_name} data found in the experiment.")
137+
return False
138+
139+
# Get the latest QoI metric result
140+
latest_qoi = qoi_metrics.iloc[-1]
141+
142+
if pd.notna(latest_qoi["sem"]) and latest_qoi["sem"] <= sem_threshold:
143+
print(f"SEM threshold met: {latest_qoi['sem']:.4f} <= {sem_threshold}")
144+
return True
145+
146+
return False
147+
108148

109149
# %% [markdown]
110150
# How many iterations to run in the following DOEs
111151

112152
# %%
113-
n_iter = 30
153+
n_iter_sobol = 100
154+
n_iter_doe = 30
114155
warm_up_runs = 8
115156

116157
# %% [markdown]
@@ -150,12 +191,14 @@ def sobol_generator_run(_: Experiment) -> GeneratorRun:
150191

151192
sobol_generator_run = create_sobol_generator(sobol)
152193

153-
run_trials(
194+
195+
last_itr_sobol = run_trials(
154196
experiment=exp_sobol,
155197
warm_up_generator=sobol_generator_run,
156198
doe_generator=sobol_generator_run,
157199
warm_up_runs=warm_up_runs,
158-
doe_runs=n_iter,
200+
doe_runs=n_iter_sobol,
201+
stopping_criteria=sem_stopping_criteria, # Optional: use a stopping criteria based on confidence bound
159202
)
160203

161204

@@ -172,7 +215,9 @@ def sobol_generator_run(_: Experiment) -> GeneratorRun:
172215
exp_sobol, warm_up_runs, metrics=true_loc_scale_function_estimates
173216
)
174217
fig_trial_warm_up.show()
175-
fig_last_trial = plot_gp_fits_2d_surface_from_experiment(exp_sobol, n_iter, metrics=true_loc_scale_function_estimates)
218+
fig_last_trial = plot_gp_fits_2d_surface_from_experiment(
219+
exp_sobol, last_itr_sobol, metrics=true_loc_scale_function_estimates
220+
)
176221
fig_last_trial.show()
177222

178223
# %%
@@ -340,12 +385,13 @@ def look_ahead_generator_run(experiment: Experiment) -> GeneratorRun:
340385
# This needs to be instantiated outside of the loop so the internal state of the generator persists.
341386
sobol = Models.SOBOL(search_space=exp_look_ahead.search_space, seed=5)
342387

343-
run_trials(
388+
last_itr_look_ahead = run_trials(
344389
experiment=exp_look_ahead,
345390
warm_up_generator=create_sobol_generator(sobol),
346391
doe_generator=look_ahead_generator_run,
347392
warm_up_runs=warm_up_runs,
348-
doe_runs=n_iter,
393+
doe_runs=n_iter_doe,
394+
stopping_criteria=sem_stopping_criteria, # Optional: use a stopping criteria based on confidence bound
349395
)
350396

351397
# %%
@@ -355,7 +401,7 @@ def look_ahead_generator_run(experiment: Experiment) -> GeneratorRun:
355401
)
356402
fig_trial_warm_up.show()
357403
fig_last_trial = plot_gp_fits_2d_surface_from_experiment(
358-
exp_look_ahead, n_iter, metrics=true_loc_scale_function_estimates
404+
exp_look_ahead, last_itr_look_ahead, metrics=true_loc_scale_function_estimates
359405
)
360406
fig_last_trial.show()
361407

@@ -371,13 +417,6 @@ def look_ahead_generator_run(experiment: Experiment) -> GeneratorRun:
371417
# %%
372418
# For a more sophisticated stopping criteria experiment, see doe_dev.py as of 2025-06-10.
373419
_, ax = plt.subplots()
374-
_ = ax.axhline(
375-
large_dataset_mean + 1.96 * large_dataset_var**0.5,
376-
c="sandybrown",
377-
label=f"Stopping criteria\n({large_dataset_points} Sobol points)",
378-
)
379-
_ = ax.axhline(large_dataset_mean - 1.96 * large_dataset_var**0.5, c="sandybrown")
380-
_ = ax.axhline(large_dataset_mean, c="sandybrown", linestyle="--", label="Sobol mean")
381420
ax = plot_qoi_estimates_from_experiment(exp_sobol, ax=ax, name="Sobol")
382421
ax = plot_qoi_estimates_from_experiment(exp_look_ahead, ax=ax, color="green", name="look ahead")
383422
_ = ax.axhline(brute_force_qoi, c="black", label="brute_force_value")

examples/crest_heights_north_sea/doe_dev.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def run_multiple_seeded_experiments(
8585
sobol = Models.SOBOL(search_space=exp_sobol.search_space, seed=seed)
8686
sobol_generator_run = create_sobol_generator(sobol)
8787

88-
run_trials(
88+
_ = run_trials(
8989
experiment=exp_sobol,
9090
warm_up_generator=sobol_generator_run,
9191
doe_generator=sobol_generator_run,
@@ -100,7 +100,7 @@ def run_multiple_seeded_experiments(
100100
_ = exp_look_ahead.add_tracking_metric(QOI_METRIC)
101101
sobol = Models.SOBOL(search_space=exp_look_ahead.search_space, seed=seed)
102102

103-
run_trials(
103+
_ = run_trials(
104104
experiment=exp_look_ahead,
105105
warm_up_generator=create_sobol_generator(sobol),
106106
doe_generator=look_ahead_generator_run,

examples/crest_heights_north_sea/results/doe/plots/doe_gp_vs_true_functions.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

examples/crest_heights_north_sea/results/doe/plots/sobol_gp_vs_true_functions.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)