diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf35b087..4c1a571e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ ## Breaking Changes +- [#938](https://github.com/pybop-team/PyBOP/pull/938) - Make SALib an optional dependency and remove `sensitivity_analysis` in favour of using SALib directly. - [#942](https://github.com/pybop-team/PyBOP/pull/942) - Adds `evaluate_batch` to the costs and ensures that an `Evaluation` is returned. # [v26.3](https://github.com/pybop-team/PyBOP/tree/v26.3) - 2026-03-05 diff --git a/docs/installation.rst b/docs/installation.rst index 50138d81e..990db5047 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -43,25 +43,43 @@ For those who prefer to install PyBOP from a local clone of the repository or wi In editable mode, changes you make to the source code will immediately affect the PyBOP installation without the need for reinstallation. Optional Dependencies ------------------ -``plotly`` - For plotting, PyBOP uses plotly. It can be installed with: +--------------------- +``plotly`` - For plotting, PyBOP uses `plotly `_. It can be installed with: .. code-block:: console pip install pybop[plot] -``scikit-fem`` - This is a dependency for the multi-dimensional pybamm models, and can be installed using: +``salib`` - To compute sensitivities, PyBOP can be paired with the `Sensitivity Analysis Library (SALib) `_: + +.. code-block:: console + + pip install pybop[salib] + +``scikit-fem`` - This is a dependency for the multi-dimensional PyBaMM models, and can be installed using: .. code-block:: console pip install pybop[scifem] -``bpx`` - To use the Faraday Institution's Battery Parameter eXchange (BPX) package install the optional requirement: +``bpx`` - To use the Faraday Institution's `Battery Parameter eXchange (BPX) package `_: .. code-block:: console pip install pybop[bpx] +``ep-bolfi`` - To use Expectation Propagation with Bayesian Optimization for Likelihood-Free Inference (`EP-BOLFI `_): + +.. code-block:: console + + pip install pybop[ep-bolfi] + +``pyprobe`` - To import data from battery cyclers, use `Python Processing for Battery Experiments (PyProBE) `_: + +.. code-block:: console + + pip install pybop[pyprobe] + To install all the optional dependencies, the command ``pip install pybop[all]`` is available. For more information on the optional packages, users are directed towards the `pyproject.toml `_. Verifying Installation diff --git a/pybop/analysis/sensitivity_analysis.py b/pybop/analysis/sensitivity_analysis.py deleted file mode 100644 index 49370bd20..000000000 --- a/pybop/analysis/sensitivity_analysis.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import TYPE_CHECKING - -from SALib.analyze import sobol -from SALib.sample.sobol import sample - -if TYPE_CHECKING: - from pybop.problems.problem import Problem - - -def sensitivity_analysis( - problem: "Problem", n_samples: int = 256, calc_second_order: bool = False -) -> dict: - """ - Computes the parameter sensitivities on the combined cost function using - SOBOL analysis from the SALib module [1]. - - Parameters - ---------- - problem : pybop.Problem - The optimisation problem. - n_samples : int, optional - Number of samples for SOBOL sensitivity analysis, - performs best as order of 2, i.e. 128, 256, etc. - calc_second_order : bool, optional - Whether to calculate second-order sensitivities. - - References - ---------- - .. [1] Iwanaga, T., Usher, W., & Herman, J. (2022). Toward SALib 2.0: - Advancing the accessibility and interpretability of global sensitivity - analyses. Socio-Environmental Systems Modelling, 4, 18155. - doi:10.18174/sesmo.18155 - - Returns - ------- - Sensitivities : dict - """ - - salib_dict = { - "names": problem.parameters.names, - "bounds": problem.parameters.get_bounds_array(), - "num_vars": len(problem.parameters), - } - - # Create samples, compute cost - param_values = sample(salib_dict, n_samples) - costs = problem.evaluate(param_values).values - - return sobol.analyze(salib_dict, costs, calc_second_order=calc_second_order) diff --git a/pybop/problems/problem.py b/pybop/problems/problem.py index 2165fd79d..8cb7f6274 100644 --- a/pybop/problems/problem.py +++ b/pybop/problems/problem.py @@ -1,6 +1,5 @@ import numpy as np -from pybop.analysis.sensitivity_analysis import sensitivity_analysis from pybop.costs.base_cost import BaseCost from pybop.costs.evaluation import Evaluation from pybop.parameters.parameter import Inputs, Parameters @@ -227,25 +226,6 @@ def get_finite_initial_cost(self): raise ValueError("The initial parameter values return an infinite cost.") return cost0 - def sensitivity_analysis( - self, n_samples: int = 256, calc_second_order: bool = False - ) -> dict: - """ - Computes the parameter sensitivities on the combined cost function using - SOBOL analysis. See pybop.analysis.sensitivity_analysis for more details. - - Parameters - ---------- - n_samples : int, optional - Number of samples for SOBOL sensitivity analysis, performs best as a - power of 2, i.e. 128, 256, etc. - calc_second_order : bool, optional - Whether to calculate second-order sensitivities. - """ - return sensitivity_analysis( - problem=self, n_samples=n_samples, calc_second_order=calc_second_order - ) - @property def cost(self): return self._cost diff --git a/pyproject.toml b/pyproject.toml index 7651442b2..acd729723 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,11 +33,11 @@ dependencies = [ "numpy>=1.26", "scipy>=1.12", "pints>=0.6.0", - "SALib>=1.5", ] [project.optional-dependencies] plot = ["plotly>=6"] +salib = ["SALib>=1.5"] scifem = [ "scikit-fem>=8.1.0" # scikit-fem is a dependency for the multi-dimensional pybamm models ] @@ -51,7 +51,7 @@ ep-bolfi = [ pyprobe = [ "PyProBE-Data>=2.5.0;python_version >= '3.11' and python_version < '3.13'" ] -all = ["pybop[plot,scifem,bpx,pyprobe,ep-bolfi]"] +all = ["pybop[plot,salib,scifem,bpx,pyprobe,ep-bolfi]"] [dependency-groups] docs = [ diff --git a/tests/unit/test_problem.py b/tests/unit/test_problem.py index 2e15d7a1d..7a9eb5987 100644 --- a/tests/unit/test_problem.py +++ b/tests/unit/test_problem.py @@ -226,22 +226,3 @@ def test_problem_construct_with_model_predict(self, parameters, model, dataset): problem_output["Voltage [V]"].data, atol=1e-6, ) - - 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) - - # Assertions - assert isinstance(result, dict) - assert "S1" in result - assert "ST" in result - assert isinstance(result["S1"], np.ndarray) - assert isinstance(result["S2"], np.ndarray) - assert isinstance(result["ST"], np.ndarray) - assert isinstance(result["S1_conf"], np.ndarray) - assert isinstance(result["ST_conf"], np.ndarray) - assert isinstance(result["S2_conf"], np.ndarray) - assert result["S1"].shape == (n_params,) - assert result["ST"].shape == (n_params,)