Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates to #236 to avoid breaking change to pybop.Optimisation #309

Merged
merged 13 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Features

- [#236](https://github.com/pybop-team/PyBOP/issues/236) - Restructures the optimiser classes to allow the passing of keyword arguments and fixes the setting of max_iterations and minimising.
- [#236](https://github.com/pybop-team/PyBOP/issues/236) - Restructures the optimiser classes, adds a new optimisation API through direct construction and keyword arguments, and fixes the setting of `max_iterations`, and `_minimising`. Introduces `pybop.BaseOptimiser`, `pybop.BasePintsOptimiser`, and `pybop.BaseSciPyOptimiser` classes.
- [#321](https://github.com/pybop-team/PyBOP/pull/321) - Updates Prior classes with BaseClass, adds a `problem.sample_initial_conditions` method to improve stability of SciPy.Minimize optimiser.
- [#249](https://github.com/pybop-team/PyBOP/pull/249) - Add WeppnerHuggins model and GITT example.
- [#304](https://github.com/pybop-team/PyBOP/pull/304) - Decreases the testing suite completion time.
Expand Down
285 changes: 285 additions & 0 deletions examples/notebooks/optimiser_interface.ipynb
BradyPlanden marked this conversation as resolved.
Show resolved Hide resolved
BradyPlanden marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "00940c64-4748-4b08-9a35-ea98ce311e71",
"metadata": {},
"source": [
"# Interacting with PyBOP optimisers\n",
"\n",
"This notebook introduces two interfaces to interact with PyBOP's optimiser classes.\n",
"\n",
"### Set the Environment"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "dd0e1a20-1ba3-4ff5-8f6a-f9c6f25c2a4a",
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:35.622147Z",
"iopub.status.busy": "2024-04-14T18:57:35.621660Z",
"iopub.status.idle": "2024-04-14T18:57:40.849137Z",
"shell.execute_reply": "2024-04-14T18:57:40.848620Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: pip in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (24.0)\n",
"Requirement already satisfied: ipywidgets in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (8.1.2)\n",
"Requirement already satisfied: comm>=0.1.3 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (0.2.2)\n",
"Requirement already satisfied: ipython>=6.1.0 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (8.23.0)\n",
"Requirement already satisfied: traitlets>=4.3.1 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (5.14.2)\n",
"Requirement already satisfied: widgetsnbextension~=4.0.10 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (4.0.10)\n",
"Requirement already satisfied: jupyterlab-widgets~=3.0.10 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (3.0.10)\n",
"Requirement already satisfied: decorator in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (5.1.1)\n",
"Requirement already satisfied: jedi>=0.16 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.19.1)\n",
"Requirement already satisfied: matplotlib-inline in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.1.6)\n",
"Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (3.0.43)\n",
"Requirement already satisfied: pygments>=2.4.0 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (2.17.2)\n",
"Requirement already satisfied: stack-data in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.6.3)\n",
"Requirement already satisfied: pexpect>4.3 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (4.9.0)\n",
"Requirement already satisfied: parso<0.9.0,>=0.8.3 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from jedi>=0.16->ipython>=6.1.0->ipywidgets) (0.8.4)\n",
"Requirement already satisfied: ptyprocess>=0.5 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets) (0.7.0)\n",
"Requirement already satisfied: wcwidth in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from prompt-toolkit<3.1.0,>=3.0.41->ipython>=6.1.0->ipywidgets) (0.2.13)\n",
"Requirement already satisfied: executing>=1.2.0 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.0.1)\n",
"Requirement already satisfied: asttokens>=2.1.0 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.4.1)\n",
"Requirement already satisfied: pure-eval in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (0.2.2)\n",
"Requirement already satisfied: six>=1.12.0 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from asttokens>=2.1.0->stack-data->ipython>=6.1.0->ipywidgets) (1.16.0)\n",
"Note: you may need to restart the kernel to use updated packages.\n",
"Note: you may need to restart the kernel to use updated packages.\n"
]
}
],
"source": [
"%pip install --upgrade pip ipywidgets\n",
"%pip install pybop -q\n",
"\n",
"# Import the necessary libraries\n",
"import numpy as np\n",
"\n",
"import pybop"
]
},
{
"cell_type": "markdown",
"id": "017695fd-ee78-4113-af18-2fea04cf6126",
"metadata": {},
"source": [
"## Setup the model, problem, and cost\n",
"\n",
"The code block below sets up the model, problem, and cost objects. For more information on this process, take a look at other notebooks in the examples directory."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c346b106-99a9-46bc-8b5d-d330ed911660",
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.438835Z",
"iopub.status.busy": "2024-04-14T18:57:46.438684Z",
"iopub.status.idle": "2024-04-14T18:57:46.478613Z",
"shell.execute_reply": "2024-04-14T18:57:46.478339Z"
}
},
"outputs": [],
"source": [
"# Load the parameters\n",
"parameter_set = pybop.ParameterSet(\n",
" json_path=\"../scripts/parameters/initial_ecm_parameters.json\"\n",
")\n",
"parameter_set.import_parameters()\n",
"# Define the model\n",
"model = pybop.empirical.Thevenin(\n",
" parameter_set=parameter_set, options={\"number of rc elements\": 1}\n",
")\n",
"\n",
"# Define the parameters\n",
"parameters = [\n",
" pybop.Parameter(\n",
" \"R0 [Ohm]\",\n",
" prior=pybop.Gaussian(0.0002, 0.0001),\n",
" bounds=[1e-4, 1e-2],\n",
" )\n",
"]\n",
"\n",
"# Generate synthetic data\n",
"t_eval = np.arange(0, 900, 2)\n",
"values = model.predict(t_eval=t_eval)\n",
"\n",
"# Form dataset\n",
"dataset = pybop.Dataset(\n",
" {\n",
" \"Time [s]\": t_eval,\n",
" \"Current function [A]\": values[\"Current [A]\"].data,\n",
" \"Voltage [V]\": values[\"Voltage [V]\"].data,\n",
" }\n",
")\n",
"\n",
"# Construct problem and cost\n",
"problem = pybop.FittingProblem(model, parameters, dataset)\n",
"cost = pybop.SumSquaredError(problem)"
]
},
{
"cell_type": "markdown",
"id": "3ef5b0da-f755-43c6-8904-79d7ee0f218c",
"metadata": {},
"source": [
"## Interacting with the Optimisers\n",
"\n",
"Now that we have setup the required objects, we can introduce the two interfaces fo interacting with PyBOP optimisers. These are:\n",
" \n",
"1. The direct optimiser (i.e. `pybop.XNES`)\n",
"2. The optimisation class (i.e. `pybop.Optimisation`)\n",
" \n",
"These two methods provide two equivalent ways of interacting with PyBOP's optimisers. The first method provides a direct way to select the Optimiser, with the second method being more general method with a default optimiser (`pybop.XNES`) set if you don't provide an optimiser. \n",
"\n",
"First, the direct interface is presented. With this interface the user can select from the [list of optimisers](https://github.com/pybop-team/PyBOP?tab=readme-ov-file#supported-methods) supported in PyBOP and construct them directly. Options can be passed as kwargs, or through get() / set() methods in the case of Pints' based optimisers."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "6244882e-11ad-4bfe-a512-f1c687a06a08",
"metadata": {
"execution": {
"iopub.execute_input": "2024-04-14T18:57:46.512725Z",
"iopub.status.busy": "2024-04-14T18:57:46.512597Z",
"iopub.status.idle": "2024-04-14T18:57:49.259154Z",
"shell.execute_reply": "2024-04-14T18:57:49.257712Z"
}
},
"outputs": [],
"source": [
"optim_one = pybop.XNES(\n",
" cost, max_iterations=50\n",
") # Direct optimiser class with options as kwargs\n",
"optim_one.set_max_iterations(\n",
" 50\n",
") # Alternatively, set() / get() methods for Pints' optimisers\n",
"x1, final_cost = optim_one.run()"
]
},
{
"cell_type": "markdown",
"id": "c62e23f7",
"metadata": {},
"source": [
"Next, the `Optimisation` interface is less direct than the previous one, but provides a single class to work with across PyBOP workflows. The options are passed the same way as the above method, through kwargs or get() / set() methods."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "479fc846",
"metadata": {},
"outputs": [],
"source": [
"optim_two = pybop.Optimisation(\n",
" cost, optimiser=pybop.XNES, max_iterations=50\n",
") # Optimisation class with options as kwargs\n",
"optim_two.set_max_iterations(\n",
" 50\n",
") # Alternatively, set() / get() methods for Pints' optimisers\n",
"x2, final_cost = optim_two.run()"
]
},
{
"cell_type": "markdown",
"id": "5c6ea9fd",
"metadata": {},
"source": [
"We can show the equivalence of these two methods by comparing the optimiser objects:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "de56587e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(optim_one, type(optim_two.optimiser))"
]
},
{
"cell_type": "markdown",
"id": "9f6634c0",
"metadata": {},
"source": [
"For completeness, we can show the optimiser solutions:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "66b74f3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Estimated parameters x1: [0.00099965]\n",
"Estimated parameters x2: [0.00099985]\n"
]
}
],
"source": [
"print(\"Estimated parameters x1:\", x1)\n",
"print(\"Estimated parameters x2:\", x2)"
]
},
{
"cell_type": "markdown",
"id": "94653584",
"metadata": {},
"source": [
"## Closing Comments\n",
"\n",
"As both of these API's provide access to the same optimisers, please use either as you prefer. A couple things to note:\n",
"\n",
"- If you are using a SciPy-based optimiser (`pybop.SciPyMinimize`, `pybop.SciPyDifferentialEvolution`), the `set()` / `get()` methods for the optimiser options are not currently supported. These optimisers required options to be passed as kwargs.\n",
"- The optimiser passed to `pybop.Optimisation` must not be a constructed object."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 1 addition & 1 deletion examples/scripts/spm_pso.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
# Generate problem, cost function, and optimisation class
problem = pybop.FittingProblem(model, parameters, dataset)
cost = pybop.SumSquaredError(problem)
optim = pybop.PSO(cost, max_iterations=100)
optim = pybop.Optimisation(cost, optimiser=pybop.PSO, max_iterations=100)

x, final_cost = optim.run()
print("Estimated parameters:", x)
Expand Down
4 changes: 2 additions & 2 deletions examples/standalone/optimiser.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import numpy as np
from scipy.optimize import minimize

from pybop import Optimisation
from pybop import BaseOptimiser


class StandaloneOptimiser(Optimisation):
class StandaloneOptimiser(BaseOptimiser):
"""
Defines an example standalone optimiser without a Cost.
"""
Expand Down
4 changes: 2 additions & 2 deletions pybop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,14 @@
#
# Optimiser class
#
from ._optimisation import Optimisation
from .optimisers._optimisation import BaseOptimiser
from .optimisers.base_optimiser import BasePintsOptimiser
from .optimisers.scipy_optimisers import (
BaseSciPyOptimiser,
SciPyMinimize,
SciPyDifferentialEvolution
)
from .optimisers.pints_optimisers import (
DefaultOptimiser,
GradientDescent,
Adam,
CMAES,
Expand All @@ -113,6 +112,7 @@
SNES,
XNES,
)
from .optimisers.optimisation import Optimisation

#
# Parameter classes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pybop import BaseCost, BaseLikelihood, DesignCost


class Optimisation:
class BaseOptimiser:
"""
A base class for defining optimisation methods.

Expand Down
4 changes: 2 additions & 2 deletions pybop/optimisers/base_optimiser.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import numpy as np
import pints

from pybop import Optimisation
from pybop import BaseOptimiser


class BasePintsOptimiser(Optimisation):
class BasePintsOptimiser(BaseOptimiser):
"""
A base class for defining optimisation methods from the PINTS library.

Expand Down
Loading
Loading