From d6b76cacb92e543c2ca765cf0766c6d06d02ff66 Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:24:18 +0100 Subject: [PATCH] Rename Sobol sensitivity method --- pybop/analysis/sensitivity_analysis.py | 9 +++++++-- pybop/problems/problem.py | 6 +++--- tests/unit/test_problem.py | 20 +++++++++----------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/pybop/analysis/sensitivity_analysis.py b/pybop/analysis/sensitivity_analysis.py index 49370bd20..67f40ee12 100644 --- a/pybop/analysis/sensitivity_analysis.py +++ b/pybop/analysis/sensitivity_analysis.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING +import numpy as np from SALib.analyze import sobol from SALib.sample.sobol import sample @@ -7,7 +8,7 @@ from pybop.problems.problem import Problem -def sensitivity_analysis( +def get_sobol_sensitivities( problem: "Problem", n_samples: int = 256, calc_second_order: bool = False ) -> dict: """ @@ -36,9 +37,13 @@ def sensitivity_analysis( Sensitivities : dict """ + bounds_array = problem.parameters.get_bounds_array() + if not np.isfinite(bounds_array).all(): + raise ValueError("SOBOL analysis requires finite bounds.") + salib_dict = { "names": problem.parameters.names, - "bounds": problem.parameters.get_bounds_array(), + "bounds": bounds_array, "num_vars": len(problem.parameters), } diff --git a/pybop/problems/problem.py b/pybop/problems/problem.py index c8f3e1ba1..4952192a3 100644 --- a/pybop/problems/problem.py +++ b/pybop/problems/problem.py @@ -1,6 +1,6 @@ import numpy as np -from pybop.analysis.sensitivity_analysis import sensitivity_analysis +from pybop.analysis.sensitivity_analysis import get_sobol_sensitivities from pybop.costs.base_cost import BaseCost from pybop.costs.evaluation import Evaluation from pybop.parameters.parameter import Inputs, Parameters @@ -260,7 +260,7 @@ def get_finite_initial_cost(self): raise ValueError("The initial parameter values return an infinite cost.") return cost0 - def sensitivity_analysis( + def get_sobol_sensitivities( self, n_samples: int = 256, calc_second_order: bool = False ) -> dict: """ @@ -275,7 +275,7 @@ def sensitivity_analysis( calc_second_order : bool, optional Whether to calculate second-order sensitivities. """ - return sensitivity_analysis( + return get_sobol_sensitivities( problem=self, n_samples=n_samples, calc_second_order=calc_second_order ) diff --git a/tests/unit/test_problem.py b/tests/unit/test_problem.py index 18f75bb9a..0449361e7 100644 --- a/tests/unit/test_problem.py +++ b/tests/unit/test_problem.py @@ -22,18 +22,10 @@ def model(self): def parameters(self): return { "Negative particle radius [m]": pybop.Parameter( - distribution=pybop.Gaussian( - 2e-05, - 0.1e-5, - truncated_at=[1e-6, 5e-5], - ) + distribution=pybop.Gaussian(2e-05, 0.1e-5, truncated_at=[1e-6, 5e-5]) ), "Positive particle radius [m]": pybop.Parameter( - distribution=pybop.Gaussian( - 0.5e-05, - 0.1e-5, - truncated_at=[1e-6, 5e-5], - ) + distribution=pybop.Gaussian(0.5e-05, 0.1e-5, truncated_at=[1e-6, 5e-5]) ), } @@ -239,7 +231,7 @@ def test_parameter_sensitivities(self, simulator, dataset): cost = pybop.MeanAbsoluteError(dataset) problem = pybop.Problem(simulator, cost) n_params = len(problem.parameters) - result = problem.sensitivity_analysis(4, calc_second_order=True) + result = problem.get_sobol_sensitivities(4, calc_second_order=True) # Assertions assert isinstance(result, dict) @@ -253,3 +245,9 @@ def test_parameter_sensitivities(self, simulator, dataset): assert isinstance(result["S2_conf"], np.ndarray) assert result["S1"].shape == (n_params,) assert result["ST"].shape == (n_params,) + + problem.parameters["Negative particle radius [m]"] = pybop.Parameter( + distribution=pybop.Gaussian(2e-05, 0.1e-5) # unbounded + ) + with pytest.raises(ValueError, match="SOBOL analysis requires finite bounds."): + problem.get_sobol_sensitivities(4, calc_second_order=True)