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,)