From b7024f2ec1d00aba8ef7086f6eb8680df0e54f14 Mon Sep 17 00:00:00 2001 From: Sebastian Winter Date: Tue, 24 Mar 2026 01:27:23 +0100 Subject: [PATCH] minor plotting clean up, and basic_example/simulator broadcasting --- axtreme_knowledge_base | 2 +- .../problem/brute_force.py | 9 ++++-- .../erd_n_sample_per_period_x1000.png | Bin 0 -> 42341 bytes .../problem/simulator.py | 29 ++++++++++++++--- src/axtreme/plotting/gp_fit.py | 30 +++++++++++++++--- src/axtreme/plotting/histogram3d.py | 2 +- tests/qoi/test_gp_brute_force_system.py | 2 +- .../test_marginal_cdf_extrapolation_system.py | 2 +- tutorials/ax_botorch/optimisation.ipynb | 4 +-- 9 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 examples/basic_example_usecase/problem/results/brute_force/erd_n_sample_per_period_x1000.png diff --git a/axtreme_knowledge_base b/axtreme_knowledge_base index de771a17..993683fc 160000 --- a/axtreme_knowledge_base +++ b/axtreme_knowledge_base @@ -1 +1 @@ -Subproject commit de771a17b7382387db384470ceb8d535567263fe +Subproject commit 993683fcb7a4ff61f6101dc2ebe9988dc930c724 diff --git a/examples/basic_example_usecase/problem/brute_force.py b/examples/basic_example_usecase/problem/brute_force.py index 91b7e001..a10b3781 100644 --- a/examples/basic_example_usecase/problem/brute_force.py +++ b/examples/basic_example_usecase/problem/brute_force.py @@ -185,7 +185,7 @@ def _brute_force_calc( N_ENV_SAMPLES_PER_PERIOD = N_YEARS_IN_PERIOD * N_SECONDS_IN_YEAR // N_SECONDS_IN_TIME_STEP N_ENV_SAMPLES_PER_PERIOD = 1000 - samples, x_max = collect_or_calculate_results(N_ENV_SAMPLES_PER_PERIOD, 300_000) + samples, x_max = collect_or_calculate_results(N_ENV_SAMPLES_PER_PERIOD, 10_000) _ = plt.hist(samples, bins=100, density=True) _ = plt.title( @@ -199,8 +199,11 @@ def _brute_force_calc( f"results/brute_force/erd_n_sample_per_period_{N_ENV_SAMPLES_PER_PERIOD}.png", ) plt.show() - - _ = plt.scatter(x_max[:, 0], x_max[:, 1]) + _ = plt.scatter(x_max[:, 0], x_max[:, 1], alpha=0.5, s=5) + _ = plt.title("Locations of max response in environment space (point is sample erd)") + plt.savefig( + f"results/brute_force/erd_n_sample_per_period_x{N_ENV_SAMPLES_PER_PERIOD}.png", + ) plt.show() # %% diff --git a/examples/basic_example_usecase/problem/results/brute_force/erd_n_sample_per_period_x1000.png b/examples/basic_example_usecase/problem/results/brute_force/erd_n_sample_per_period_x1000.png new file mode 100644 index 0000000000000000000000000000000000000000..c4fb80448b5a709e9e9cd091e99bfd6f2d50c865 GIT binary patch literal 42341 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 NDArray[np.float64]: +def _true_loc_func_torch(x: torch.Tensor) -> torch.Tensor: + """Takes input of shape (*b, d=2) and returns (*b,) locs.""" # For this toy example we use a Mixture distribution of a MultivariateNormal distribution dist1_mean, dist1_cov = torch.tensor([0.8, 0.8]), torch.tensor([[0.03, 0], [0, 0.03]]) dist2_mean, dist2_cov = torch.tensor([0.2, 0.8]), torch.tensor([[0.04, 0.01], [0.01, 0.04]]) @@ -35,12 +36,23 @@ def _true_loc_func(x: NDArray[np.float64]) -> NDArray[np.float64]: ) ) gmm = MixtureSameFamily(mix, component_dist) - return np.exp(gmm.log_prob(torch.tensor(x)).numpy()) + return gmm.log_prob(x).exp() -def _true_scale_func(x: NDArray[np.float64]) -> NDArray[np.float64]: +def _true_loc_func(x: NDArray[np.float64]) -> NDArray[np.float64]: + """Takes input of shape (*b, d=2) and returns (*b,) locs.""" + return _true_loc_func_torch(torch.tensor(x)).numpy() + + +def _true_scale_func_torch(x: torch.Tensor) -> torch.Tensor: + """Takes input of shape (*b, d=2) and returns (*b,) locs.""" # For this toy example we use a constant scale for simplicity - return np.ones(x.shape[0]) * 0.1 + return torch.ones(x.shape[:-1]) * 0.1 + + +def _true_scale_func(x: NDArray[np.float64]) -> NDArray[np.float64]: + """Takes input of shape (*b, d=2) and returns (*b,) locs.""" + return _true_scale_func_torch(torch.tensor(x)).numpy() def dummy_simulator_function(x: NDArray[np.float64]) -> NDArray[np.float64]: @@ -136,3 +148,12 @@ def _hash_function(x1: float, x2: float) -> int: fig = plt.figure(figsize=(10, 8)) ax = fig.add_subplot(111, projection="3d") _ = ax.scatter(x1_mesh, x2_mesh, samples.reshape(len(x1), len(x2)), cmap="viridis") + + # %% Testing the shape of the underlying function is as expected + assert _true_loc_func_torch(torch.rand(2)).shape == torch.Size([]) + assert _true_loc_func_torch(torch.rand(5, 2)).shape == torch.Size([5]) + assert _true_loc_func_torch(torch.rand(7, 5, 2)).shape == torch.Size([7, 5]) + + assert _true_scale_func_torch(torch.rand(2)).shape == torch.Size([]) + assert _true_scale_func_torch(torch.rand(5, 2)).shape == torch.Size([5]) + assert _true_scale_func_torch(torch.rand(7, 5, 2)).shape == torch.Size([7, 5]) diff --git a/src/axtreme/plotting/gp_fit.py b/src/axtreme/plotting/gp_fit.py index 9065acea..05701fc1 100644 --- a/src/axtreme/plotting/gp_fit.py +++ b/src/axtreme/plotting/gp_fit.py @@ -1,6 +1,6 @@ """Plotting module for visualizing how well the GP fits the data.""" -from collections.abc import Callable +from collections.abc import Callable, Sequence from typing import TypeAlias import matplotlib.pyplot as plt @@ -43,16 +43,38 @@ def plot_surface_over_2d_search_space( # Extract the parameter names and ranges from the search space assert len(search_space.parameters) == 2, "Only 2D search spaces are supported for now." # noqa: PLR2004 - (x1_name, x1_param), (x2_name, x2_param) = list(search_space.parameters.items()) + (_, x1_param), (_, x2_param) = list(search_space.parameters.items()) if not (isinstance(x1_param, RangeParameter) and isinstance(x2_param, RangeParameter)): msg = f"""Expect search_space.parameters to all be of type RangeParameter. Instead got {type(x1_param) = }, and {type(x2_param) = }.""" raise NotImplementedError(msg) + bounds = [(x1_param.lower, x1_param.upper), (x2_param.lower, x2_param.upper)] + return plot_surface_over_2d_space(bounds, funcs, colors, num_points) + + +def plot_surface_over_2d_space( + bounds: Sequence[tuple[float, float]], + funcs: list[Callable[[Numpy2dArray], Numpy1dArray]], + colors: list[str] | None = None, + num_points: int = 101, +) -> Figure: + """Creates a figure with the functions `funcs` plotted over the bounds. + + Note: + Currently only support search spaces with 2 parameters. + + Args: + bounds: For evaluation and plotting `[(x1_low, x1_high), (x2_low, x2_high)]` + funcs: A list of callables that take in a numpy array with shape (num_values, num_parameters=2 ) + and return a numpy array with (num_values) elements. + colors: A list of colors to use for each function. If None, will use default Plotly colors. + num_points: The number of points in each dimension to evaluate the functions at. + """ # Generate parameter ranges using NumPy - x1_values = np.linspace(x1_param.lower, x1_param.upper, num_points) - x2_values = np.linspace(x2_param.lower, x2_param.upper, num_points) + x1_values = np.linspace(bounds[0][0], bounds[0][1], num_points) + x2_values = np.linspace(bounds[1][0], bounds[1][1], num_points) # Create a meshgrid for the parameter values diff --git a/src/axtreme/plotting/histogram3d.py b/src/axtreme/plotting/histogram3d.py index 71f1d456..1aba2151 100644 --- a/src/axtreme/plotting/histogram3d.py +++ b/src/axtreme/plotting/histogram3d.py @@ -91,7 +91,7 @@ def histogram3d( # noqa: PLR0913 **{ "intensity": [0, 0, 0, 0, z_value, z_value, z_value, z_value], "flatshading": flatshading, - "coloaxtremeis": "coloaxtremeis", + "coloraxis": "coloraxis", **mesh3d_kwargs, }, ), diff --git a/tests/qoi/test_gp_brute_force_system.py b/tests/qoi/test_gp_brute_force_system.py index e6ae2efe..5b197f1b 100644 --- a/tests/qoi/test_gp_brute_force_system.py +++ b/tests/qoi/test_gp_brute_force_system.py @@ -11,7 +11,7 @@ This script is designed to be run interactively as well as though pytest. """ -# ruff: noqa: T201 +# ruff: noqa: T201 PT028 # pyright: reportUnnecessaryTypeIgnoreComment=false # %% diff --git a/tests/qoi/test_marginal_cdf_extrapolation_system.py b/tests/qoi/test_marginal_cdf_extrapolation_system.py index 7e03829d..c438aafb 100644 --- a/tests/qoi/test_marginal_cdf_extrapolation_system.py +++ b/tests/qoi/test_marginal_cdf_extrapolation_system.py @@ -11,7 +11,7 @@ This script is designed to be run interactively as well as though pytest. """ -# ruff: noqa: T201 +# ruff: noqa: T201 PT028 # pyright: reportUnnecessaryTypeIgnoreComment=false # %% import json diff --git a/tutorials/ax_botorch/optimisation.ipynb b/tutorials/ax_botorch/optimisation.ipynb index e48bec2f..cb20db25 100644 --- a/tutorials/ax_botorch/optimisation.ipynb +++ b/tutorials/ax_botorch/optimisation.ipynb @@ -331,8 +331,8 @@ "- runtime: 3 seconds\n", "- function eval: 100\n", "\n", - "#### **3: Limit the maxiter or max functio neval**\n", - "- By default perfroms 200 maxiter and ~400 feval are used. Can reducing this give a suitably accurate result?\n", + "#### **3: Limit the `maxiter` or max function eval (`maxfev`)**\n", + "- By default performs 200 `maxiter` and ~400 `maxfev` are used. Can reducing this give a suitably accurate result?\n", "\n", "\n", "```python\n",