diff --git a/CHANGELOG.md b/CHANGELOG.md index e5e93849d..26a6f942a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Features +- [#319](https://github.com/pybop-team/PyBOP/pull/319/) - Adds `CuckooSearch` optimiser with corresponding tests. - [#379](https://github.com/pybop-team/PyBOP/pull/379) - Adds model.simulateS1 to weekly benchmarks. - [#174](https://github.com/pybop-team/PyBOP/issues/174) - Adds new logo and updates Readme for accessibility. - [#316](https://github.com/pybop-team/PyBOP/pull/316) - Adds Adam with weight decay (AdamW) optimiser, adds depreciation warning for pints.Adam implementation. diff --git a/examples/notebooks/multi_optimiser_identification.ipynb b/examples/notebooks/multi_optimiser_identification.ipynb index 1422985da..c4fc3b929 100644 --- a/examples/notebooks/multi_optimiser_identification.ipynb +++ b/examples/notebooks/multi_optimiser_identification.ipynb @@ -36,27 +36,42 @@ "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", + "Requirement already satisfied: pip in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (24.0)\n", + "Collecting pip\n", + " Using cached pip-24.1.1-py3-none-any.whl.metadata (3.6 kB)\n", + "Collecting ipywidgets\n", + " Using cached ipywidgets-8.1.3-py3-none-any.whl.metadata (2.4 kB)\n", + "Requirement already satisfied: comm>=0.1.3 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipywidgets) (0.2.2)\n", + "Requirement already satisfied: ipython>=6.1.0 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipywidgets) (8.26.0)\n", + "Requirement already satisfied: traitlets>=4.3.1 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipywidgets) (5.14.3)\n", + "Collecting widgetsnbextension~=4.0.11 (from ipywidgets)\n", + " Using cached widgetsnbextension-4.0.11-py3-none-any.whl.metadata (1.6 kB)\n", + "Collecting jupyterlab-widgets~=3.0.11 (from ipywidgets)\n", + " Using cached jupyterlab_widgets-3.0.11-py3-none-any.whl.metadata (4.1 kB)\n", + "Requirement already satisfied: decorator in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (5.1.1)\n", + "Requirement already satisfied: jedi>=0.16 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.19.1)\n", + "Requirement already satisfied: matplotlib-inline in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.1.7)\n", + "Requirement already satisfied: prompt-toolkit<3.1.0,>=3.0.41 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (3.0.47)\n", + "Requirement already satisfied: pygments>=2.4.0 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (2.18.0)\n", + "Requirement already satisfied: stack-data in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from ipython>=6.1.0->ipywidgets) (0.6.3)\n", + "Requirement already satisfied: pexpect>4.3 in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/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 /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/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 /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from pexpect>4.3->ipython>=6.1.0->ipywidgets) (0.7.0)\n", + "Requirement already satisfied: wcwidth in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/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 /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/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 /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from stack-data->ipython>=6.1.0->ipywidgets) (2.4.1)\n", + "Requirement already satisfied: pure-eval in /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/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 /home/engs2510/.pyenv/versions/3.12.2/envs/pybop/lib/python3.12/site-packages (from asttokens>=2.1.0->stack-data->ipython>=6.1.0->ipywidgets) (1.16.0)\n", + "Using cached pip-24.1.1-py3-none-any.whl (1.8 MB)\n", + "Using cached ipywidgets-8.1.3-py3-none-any.whl (139 kB)\n", + "Using cached jupyterlab_widgets-3.0.11-py3-none-any.whl (214 kB)\n", + "Using cached widgetsnbextension-4.0.11-py3-none-any.whl (2.3 MB)\n", + "Installing collected packages: widgetsnbextension, pip, jupyterlab-widgets, ipywidgets\n", + " Attempting uninstall: pip\n", + " Found existing installation: pip 24.0\n", + " Uninstalling pip-24.0:\n", + " Successfully uninstalled pip-24.0\n", + "Successfully installed ipywidgets-8.1.3 jupyterlab-widgets-3.0.11 pip-24.1.1 widgetsnbextension-4.0.11\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" ] @@ -307,6 +322,7 @@ " pybop.PSO,\n", " pybop.XNES,\n", " pybop.NelderMead,\n", + " pybop.CuckooSearch,\n", "]\n", "\n", "scipy_optimisers = [\n", @@ -346,7 +362,147 @@ "Running AdamW\n", "NOTE: Boundaries ignored by AdamW\n", "Running GradientDescent\n", - "NOTE: Boundaries ignored by Gradient Descent\n", + "NOTE: Boundaries ignored by \n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", + "Error: Error in Function::call for 'event_0' [MXFunction] at .../casadi/core/function.cpp:361:\n", + ".../casadi/core/function_internal.hpp:1649: Input 1 (i1) has mismatching shape. Got 100-by-1. Allowed dimensions, in general, are:\n", + " - The input dimension N-by-M (here 300-by-1)\n", + " - A scalar, i.e. 1-by-1\n", + " - M-by-N if N=1 or M=1 (i.e. a transposed vector)\n", + " - N-by-M1 if K*M1=M for some K (argument repeated horizontally)\n", + " - N-by-P*M, indicating evaluation with multiple arguments (P must be a multiple of 1 for consistency with previous inputs)\n", "Running IRPropMin\n" ] } @@ -378,7 +534,8 @@ "Running PSO\n", "Running XNES\n", "Running NelderMead\n", - "NOTE: Boundaries ignored by NelderMead\n" + "NOTE: Boundaries ignored by \n", + "Running CuckooSearch\n" ] } ], @@ -441,16 +598,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "| Optimiser: AdamW | Results: [0.80186169 0.66943058] |\n", - "| Optimiser: Gradient descent | Results: [0.44491146 1.59642543] |\n", - "| Optimiser: iRprop- | Results: [0.8 0.66516386] |\n", - "| Optimiser: Covariance Matrix Adaptation Evolution Strategy (CMA-ES) | Results: [0.7999994 0.66516056] |\n", - "| Optimiser: Seperable Natural Evolution Strategy (SNES) | Results: [0.79672265 0.66566242] |\n", - "| Optimiser: Particle Swarm Optimisation (PSO) | Results: [0.79978922 0.66557426] |\n", - "| Optimiser: Exponential Natural Evolution Strategy (xNES) | Results: [0.79992605 0.66513294] |\n", - "| Optimiser: Nelder-Mead | Results: [0.81389091 0.66318217] |\n", - "| Optimiser: SciPyMinimize | Results: [0.63594266 0.7 ] |\n", - "| Optimiser: SciPyDifferentialEvolution | Results: [0.79999973 0.6651644 ] |\n" + "| Optimiser: AdamW | Results: [0.79283046 0.66146761] |\n", + "| Optimiser: Gradient descent | Results: [0.54971799 0.92691691] |\n", + "| Optimiser: iRprop- | Results: [0.72245096 0.67281911] |\n", + "| Optimiser: Covariance Matrix Adaptation Evolution Strategy (CMA-ES) | Results: [0.72099365 0.67312846] |\n", + "| Optimiser: Seperable Natural Evolution Strategy (SNES) | Results: [0.72092695 0.67313321] |\n", + "| Optimiser: Particle Swarm Optimisation (PSO) | Results: [0.71681934 0.67366943] |\n", + "| Optimiser: Exponential Natural Evolution Strategy (xNES) | Results: [0.71352763 0.67470134] |\n", + "| Optimiser: Nelder-Mead | Results: [0.72127038 0.67308243] |\n", + "| Optimiser: Cuckoo Search | Results: [0.70772893 0.67571981] |\n", + "| Optimiser: SciPyMinimize | Results: [0.62747952 0.7 ] |\n", + "| Optimiser: SciPyDifferentialEvolution | Results: [0.72100138 0.67312735] |\n" ] } ], @@ -509,7 +667,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelAdamWTime / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelAdamWTime / sVoltage / V" ] }, "metadata": {}, @@ -518,7 +676,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelGradient descentTime / sVoltage / V" + "05001000150020003.53.63.73.83.944.1ReferenceModelGradient descentTime / sVoltage / V" ] }, "metadata": {}, @@ -527,7 +685,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModeliRprop-Time / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModeliRprop-Time / sVoltage / V" ] }, "metadata": {}, @@ -536,7 +694,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelCovariance Matrix Adaptation Evolution Strategy (CMA-ES)Time / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelCovariance Matrix Adaptation Evolution Strategy (CMA-ES)Time / sVoltage / V" ] }, "metadata": {}, @@ -545,7 +703,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelSeperable Natural Evolution Strategy (SNES)Time / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelSeperable Natural Evolution Strategy (SNES)Time / sVoltage / V" ] }, "metadata": {}, @@ -554,7 +712,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelParticle Swarm Optimisation (PSO)Time / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelParticle Swarm Optimisation (PSO)Time / sVoltage / V" ] }, "metadata": {}, @@ -563,7 +721,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelExponential Natural Evolution Strategy (xNES)Time / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelExponential Natural Evolution Strategy (xNES)Time / sVoltage / V" ] }, "metadata": {}, @@ -572,7 +730,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelNelder-MeadTime / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelNelder-MeadTime / sVoltage / V" ] }, "metadata": {}, @@ -581,7 +739,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelSciPyMinimizeTime / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelCuckoo SearchTime / sVoltage / V" ] }, "metadata": {}, @@ -590,7 +748,16 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelSciPyDifferentialEvolutionTime / sVoltage / V" + "05001000150020003.53.63.73.83.94ReferenceModelSciPyMinimizeTime / sVoltage / V" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "05001000150020003.53.63.73.83.94ReferenceModelSciPyDifferentialEvolutionTime / sVoltage / V" ] }, "metadata": {}, @@ -627,7 +794,16 @@ { "data": { "image/svg+xml": [ - "51015202530012345AdamWIterationCost" + "5101520253000.511.522.533.5AdamWIterationCost" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "1020300.60.650.70.750.80.851020300.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -636,7 +812,7 @@ { "data": { "image/svg+xml": [ - "05101520250.60.650.70.750.80.8505101520250.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "510152011.522.533.5Gradient descentIterationCost" ] }, "metadata": {}, @@ -645,7 +821,7 @@ { "data": { "image/svg+xml": [ - "510152024681012141618Gradient descentIterationCost" + "510152000.10.20.30.40.50.60.70.851015200.40.50.60.70.80.911.11.21.3Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -654,7 +830,7 @@ { "data": { "image/svg+xml": [ - "0510150510152025300510150.511.522.5Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "10203040506000.511.522.533.5iRprop-IterationCost" ] }, "metadata": {}, @@ -663,7 +839,7 @@ { "data": { "image/svg+xml": [ - "10203040012345iRprop-IterationCost" + "2040600.650.70.750.82040600.50.550.60.65Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -672,7 +848,7 @@ { "data": { "image/svg+xml": [ - "0102030400.60.650.70.750.80102030400.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "1020304000.511.522.53Covariance Matrix Adaptation Evolution Strategy (CMA-ES)IterationCost" ] }, "metadata": {}, @@ -681,7 +857,7 @@ { "data": { "image/svg+xml": [ - "1020304000.511.522.53Covariance Matrix Adaptation Evolution Strategy (CMA-ES)IterationCost" + "501001502002500.50.550.60.650.70.750.8501001502002500.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -690,7 +866,7 @@ { "data": { "image/svg+xml": [ - "0501001502002500.550.60.650.70.750.80501001502002500.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "10203040506000.511.522.5Seperable Natural Evolution Strategy (SNES)IterationCost" ] }, "metadata": {}, @@ -699,7 +875,7 @@ { "data": { "image/svg+xml": [ - "10203040506000.511.522.533.5Seperable Natural Evolution Strategy (SNES)IterationCost" + "1002003000.550.60.650.70.750.81002003000.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -708,7 +884,7 @@ { "data": { "image/svg+xml": [ - "01002003000.550.60.650.70.750.801002003000.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "10203040500.0030.00350.0040.00450.005Particle Swarm Optimisation (PSO)IterationCost" ] }, "metadata": {}, @@ -717,7 +893,7 @@ { "data": { "image/svg+xml": [ - "1020304000.511.522.5Particle Swarm Optimisation (PSO)IterationCost" + "501001502002500.50.550.60.650.70.750.8501001502002500.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -726,7 +902,7 @@ { "data": { "image/svg+xml": [ - "0501001502000.50.550.60.650.70.750.80501001502000.40.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "10203040506000.511.52Exponential Natural Evolution Strategy (xNES)IterationCost" ] }, "metadata": {}, @@ -735,7 +911,7 @@ { "data": { "image/svg+xml": [ - "10203040506000.511.522.5Exponential Natural Evolution Strategy (xNES)IterationCost" + "1002003000.540.560.580.60.620.640.660.680.70.721002003000.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -744,7 +920,7 @@ { "data": { "image/svg+xml": [ - "01002003000.60.650.70.750.801002003000.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "10203040506000.511.522.533.5Nelder-MeadIterationCost" ] }, "metadata": {}, @@ -753,7 +929,7 @@ { "data": { "image/svg+xml": [ - "10203040506000.511.522.533.5Nelder-MeadIterationCost" + "2040600.620.640.660.680.70.722040600.50.550.60.650.70.75Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -762,7 +938,7 @@ { "data": { "image/svg+xml": [ - "02040600.60.650.70.750.80.8502040600.450.50.550.60.650.70.750.80.850.9Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "1020304050600.0030.00350.0040.00450.005Cuckoo SearchIterationCost" ] }, "metadata": {}, @@ -771,7 +947,7 @@ { "data": { "image/svg+xml": [ - "510152025012345SciPyMinimizeIterationCost" + "1002003000.50.550.60.650.70.750.81002003000.40.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -780,7 +956,7 @@ { "data": { "image/svg+xml": [ - "05101520250.60.610.620.630.640.650.660.6705101520250.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "510152000.10.20.30.40.50.6SciPyMinimizeIterationCost" ] }, "metadata": {}, @@ -789,7 +965,7 @@ { "data": { "image/svg+xml": [ - "510152025300.00360.00380.0040.00420.0044SciPyDifferentialEvolutionIterationCost" + "102030400.560.580.60.620.640.660.680.70.72102030400.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -798,7 +974,16 @@ { "data": { "image/svg+xml": [ - "05101520250.7550.760.7650.770.7750.780.7850.790.7950.805101520250.6650.6660.6670.6680.6690.67Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "51015200.00290.0030.00310.00320.00330.00340.0035SciPyDifferentialEvolutionIterationCost" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "2004006000.50.550.60.650.70.750.82004006000.40.450.50.550.60.650.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -835,7 +1020,16 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5AdamWNegative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4AdamWNegative electrode active material volume fractionPositive electrode active material volume fraction" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Gradient descentNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -844,7 +1038,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5Gradient descentNegative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4iRprop-Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -853,7 +1047,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5iRprop-Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Covariance Matrix Adaptation Evolution Strategy (CMA-ES)Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -862,7 +1056,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5Covariance Matrix Adaptation Evolution Strategy (CMA-ES)Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Seperable Natural Evolution Strategy (SNES)Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -871,7 +1065,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5Seperable Natural Evolution Strategy (SNES)Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Particle Swarm Optimisation (PSO)Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -880,7 +1074,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5Particle Swarm Optimisation (PSO)Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Exponential Natural Evolution Strategy (xNES)Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -889,7 +1083,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5Exponential Natural Evolution Strategy (xNES)Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Nelder-MeadNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -898,7 +1092,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5Nelder-MeadNegative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4Cuckoo SearchNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -907,7 +1101,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5SciPyMinimizeNegative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4SciPyMinimizeNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -916,7 +1110,7 @@ { "data": { "image/svg+xml": [ - "0.50.550.60.650.70.750.80.50.550.60.650.70.750.80.511.522.533.5SciPyDifferentialEvolutionNegative electrode active material volume fractionPositive electrode active material volume fraction" + "0.50.550.60.650.70.750.80.550.60.650.70.750.80.40.81.21.622.4SciPyDifferentialEvolutionNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, diff --git a/examples/notebooks/optimiser_calibration.ipynb b/examples/notebooks/optimiser_calibration.ipynb index ec4c1551a..3b09cd377 100644 --- a/examples/notebooks/optimiser_calibration.ipynb +++ b/examples/notebooks/optimiser_calibration.ipynb @@ -126,9 +126,24 @@ "outputs": [], "source": [ "parameter_set = pybop.ParameterSet.pybamm(\"Chen2020\")\n", + "parameter_set.update(\n", + " {\n", + " \"Negative electrode active material volume fraction\": 0.65,\n", + " \"Positive electrode active material volume fraction\": 0.51,\n", + " }\n", + ")\n", "model = pybop.lithium_ion.SPM(parameter_set=parameter_set)\n", - "t_eval = np.arange(0, 900, 3)\n", - "values = model.predict(t_eval=t_eval)" + "init_soc = 0.4\n", + "experiment = pybop.Experiment(\n", + " [\n", + " (\n", + " \"Discharge at 0.5C for 6 minutes (4 second period)\",\n", + " \"Charge at 0.5C for 6 minutes (4 second period)\",\n", + " ),\n", + " ]\n", + " * 2\n", + ")\n", + "values = model.predict(init_soc=init_soc, experiment=experiment)" ] }, { @@ -153,8 +168,10 @@ }, "outputs": [], "source": [ - "sigma = 0.001\n", - "corrupt_values = values[\"Voltage [V]\"].data + np.random.normal(0, sigma, len(t_eval))" + "sigma = 0.002\n", + "corrupt_values = values[\"Voltage [V]\"].data + np.random.normal(\n", + " 0, sigma, len(values[\"Voltage [V]\"].data)\n", + ")" ] }, { @@ -200,7 +217,7 @@ "source": [ "dataset = pybop.Dataset(\n", " {\n", - " \"Time [s]\": t_eval,\n", + " \"Time [s]\": values[\"Time [s]\"].data,\n", " \"Current function [A]\": values[\"Current [A]\"].data,\n", " \"Voltage [V]\": corrupt_values,\n", " }\n", @@ -235,13 +252,15 @@ "parameters = pybop.Parameters(\n", " pybop.Parameter(\n", " \"Negative electrode active material volume fraction\",\n", - " prior=pybop.Gaussian(0.7, 0.025),\n", - " bounds=[0.6, 0.9],\n", + " prior=pybop.Uniform(0.45, 0.7),\n", + " bounds=[0.4, 0.8],\n", + " true_value=0.65,\n", " ),\n", " pybop.Parameter(\n", " \"Positive electrode active material volume fraction\",\n", - " prior=pybop.Gaussian(0.6, 0.025),\n", - " bounds=[0.5, 0.8],\n", + " prior=pybop.Uniform(0.45, 0.7),\n", + " bounds=[0.4, 0.8],\n", + " true_value=0.51,\n", " ),\n", ")" ] @@ -279,7 +298,7 @@ } ], "source": [ - "problem = pybop.FittingProblem(model, parameters, dataset)\n", + "problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc)\n", "cost = pybop.SumSquaredError(problem)\n", "optim = pybop.GradientDescent(cost, sigma0=0.2, max_iterations=100)" ] @@ -307,26 +326,7 @@ }, "id": "-9OVt0EQ04qB" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n", - "Error: Events ['Maximum voltage [V]'] are non-positive at initial conditions\n" - ] - } - ], + "outputs": [], "source": [ "x, final_cost = optim.run()" ] @@ -362,7 +362,7 @@ { "data": { "text/plain": [ - "array([0.70742414, 0.58383355])" + "array([0.64609807, 0.51472958])" ] }, "execution_count": 9, @@ -396,7 +396,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.753.83.853.93.9544.05ReferenceModelOptimised ComparisonTime / sVoltage / V" + "0500100015003.53.553.63.653.7ReferenceModelOptimised ComparisonTime / sVoltage / V" ] }, "metadata": {}, @@ -434,26 +434,30 @@ "text": [ "0.001\n", "NOTE: Boundaries ignored by Gradient Descent\n", - "0.0045\n", + "0.012285714285714285\n", + "NOTE: Boundaries ignored by Gradient Descent\n", + "0.023571428571428573\n", + "NOTE: Boundaries ignored by Gradient Descent\n", + "0.03485714285714286\n", "NOTE: Boundaries ignored by Gradient Descent\n", - "0.008\n", + "0.046142857142857145\n", "NOTE: Boundaries ignored by Gradient Descent\n", - "0.0115\n", + "0.05742857142857143\n", "NOTE: Boundaries ignored by Gradient Descent\n", - "0.015\n", + "0.06871428571428571\n", + "NOTE: Boundaries ignored by Gradient Descent\n", + "0.08\n", "NOTE: Boundaries ignored by Gradient Descent\n" ] } ], "source": [ - "sigmas = np.linspace(\n", - " 0.001, 0.015, 5\n", - ") # Change this to a smaller range for a quicker run\n", + "sigmas = np.linspace(0.001, 0.08, 8) # Change this to a smaller range for a quicker run\n", "xs = []\n", "optims = []\n", "for sigma in sigmas:\n", " print(sigma)\n", - " problem = pybop.FittingProblem(model, parameters, dataset)\n", + " problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc)\n", " cost = pybop.SumSquaredError(problem)\n", " optim = pybop.GradientDescent(cost, sigma0=sigma, max_iterations=100)\n", " x, final_cost = optim.run()\n", @@ -477,11 +481,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "| Sigma: 0.001 | Num Iterations: 100 | Best Cost: 0.0013289907848209911 | Results: [0.69535773 0.67509662] |\n", - "| Sigma: 0.0045 | Num Iterations: 100 | Best Cost: 0.0007218197918308683 | Results: [0.71892626 0.67060898] |\n", - "| Sigma: 0.008 | Num Iterations: 100 | Best Cost: 0.0006371022763628136 | Results: [0.72396797 0.6696914 ] |\n", - "| Sigma: 0.0115 | Num Iterations: 18 | Best Cost: 0.0004608694532019237 | Results: [0.74070995 0.6667419 ] |\n", - "| Sigma: 0.015 | Num Iterations: 100 | Best Cost: 0.0007468897676990436 | Results: [0.71758655 0.67085529] |\n" + "| Sigma: 0.001 | Num Iterations: 100 | Best Cost: 0.008590687346571011 | Results: [0.58273999 0.64430015] |\n", + "| Sigma: 0.012285714285714285 | Num Iterations: 100 | Best Cost: 0.0017482878947612424 | Results: [0.62229759 0.5406604 ] |\n", + "| Sigma: 0.023571428571428573 | Num Iterations: 100 | Best Cost: 0.0013871420979637958 | Results: [0.63941964 0.52140605] |\n", + "| Sigma: 0.03485714285714286 | Num Iterations: 100 | Best Cost: 0.001571369568098984 | Results: [0.62907481 0.53267599] |\n", + "| Sigma: 0.046142857142857145 | Num Iterations: 28 | Best Cost: 0.0013533853388748253 | Results: [0.64673791 0.51409832] |\n", + "| Sigma: 0.05742857142857143 | Num Iterations: 25 | Best Cost: 0.0013584031053821507 | Results: [0.64390064 0.51673076] |\n", + "| Sigma: 0.06871428571428571 | Num Iterations: 74 | Best Cost: 0.0013568172573032275 | Results: [0.64444354 0.51631924] |\n", + "| Sigma: 0.08 | Num Iterations: 73 | Best Cost: 0.0013551215844470215 | Results: [0.64505654 0.51551585] |\n" ] } ], @@ -514,7 +521,34 @@ { "data": { "image/svg+xml": [ - "2040608010000.050.10.150.2Sigma: 0.001IterationCost" + "204060801000.00850.0090.00950.010.01050.0110.01150.012Sigma: 0.001IterationCost" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "0204060800.5840.5860.5880.590.5920.5940204060800.6440.6460.6480.650.6520.6540.6560.658Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "2040608010000.0050.010.0150.020.0250.030.035Sigma: 0.012285714285714285IterationCost" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "0204060800.540.560.580.60.620204060800.520.5250.530.5350.540.5450.550.555Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -523,7 +557,7 @@ { "data": { "image/svg+xml": [ - "0204060800.680.6820.6840.6860.6880.690.6920.6940.6960204060800.610.620.630.640.650.660.67Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "2040608010000.020.040.060.080.1Sigma: 0.023571428571428573IterationCost" ] }, "metadata": {}, @@ -532,7 +566,7 @@ { "data": { "image/svg+xml": [ - "2040608010000.050.10.150.20.25Sigma: 0.0045IterationCost" + "0204060800.50.520.540.560.580.60.620.640204060800.450.460.470.480.490.50.510.520.530.54Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -541,7 +575,7 @@ { "data": { "image/svg+xml": [ - "0204060800.70.7050.710.7150.720204060800.60.610.620.630.640.650.660.67Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "204060801000.0050.010.0150.02Sigma: 0.03485714285714286IterationCost" ] }, "metadata": {}, @@ -550,7 +584,7 @@ { "data": { "image/svg+xml": [ - "2040608010000.050.10.150.20.25Sigma: 0.008IterationCost" + "0204060800.570.580.590.60.610.620.630204060800.540.560.580.60.620.640.660.680.7Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -559,7 +593,7 @@ { "data": { "image/svg+xml": [ - "0204060800.70.7050.710.7150.720.7250204060800.60.610.620.630.640.650.660.67Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "5101520250.0020.0040.0060.0080.010.0120.014Sigma: 0.046142857142857145IterationCost" ] }, "metadata": {}, @@ -568,7 +602,7 @@ { "data": { "image/svg+xml": [ - "5101500.010.020.030.04Sigma: 0.0115IterationCost" + "05101520250.650.660.670.680.6905101520250.520.530.540.550.56Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -577,7 +611,7 @@ { "data": { "image/svg+xml": [ - "0510150.7350.7360.7370.7380.7390.740.7410510150.6350.640.6450.650.6550.660.665Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "5101520250.001350.00140.001450.00150.001550.00160.00165Sigma: 0.05742857142857143IterationCost" ] }, "metadata": {}, @@ -586,7 +620,7 @@ { "data": { "image/svg+xml": [ - "2040608010000.10.20.30.40.5Sigma: 0.015IterationCost" + "051015200.6340.6360.6380.640.6420.644051015200.5150.51550.5160.51650.5170.51750.5180.51850.5190.5195Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -595,7 +629,34 @@ { "data": { "image/svg+xml": [ - "0204060800.660.670.680.690.70.710.720204060800.580.60.620.640.660.680.70.720.740.76Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + "1020304050607000.0050.010.0150.020.0250.030.0350.04Sigma: 0.06871428571428571IterationCost" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "02040600.610.620.630.640.650.660.670.680.6902040600.520.540.560.580.60.620.64Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "102030405060700.0020.0040.0060.0080.01Sigma: 0.08IterationCost" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "02040600.560.570.580.590.60.610.620.630.640.6502040600.520.530.540.550.560.57Negative electrode active material volume fractionPositive electrode active material volume fractionParameter ConvergenceFunction CallFunction CallNegative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -632,7 +693,34 @@ { "data": { "image/svg+xml": [ - "0.60.650.70.750.80.850.90.50.550.60.650.70.750.80.40.81.21.622.4Sigma: 0.001Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.001Negative electrode active material volume fractionPositive electrode active material volume fraction" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.012285714285714285Negative electrode active material volume fractionPositive electrode active material volume fraction" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.023571428571428573Negative electrode active material volume fractionPositive electrode active material volume fraction" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.03485714285714286Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -641,7 +729,7 @@ { "data": { "image/svg+xml": [ - "0.60.650.70.750.80.850.90.50.550.60.650.70.750.80.40.81.21.622.4Sigma: 0.0045Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.046142857142857145Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -650,7 +738,7 @@ { "data": { "image/svg+xml": [ - "0.60.650.70.750.80.850.90.50.550.60.650.70.750.80.40.81.21.622.4Sigma: 0.008Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.05742857142857143Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -659,7 +747,7 @@ { "data": { "image/svg+xml": [ - "0.60.650.70.750.80.850.90.50.550.60.650.70.750.80.40.81.21.622.4Sigma: 0.0115Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.06871428571428571Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -668,7 +756,7 @@ { "data": { "image/svg+xml": [ - "0.60.650.70.750.80.850.90.50.550.60.650.70.750.80.40.81.21.622.4Sigma: 0.015Negative electrode active material volume fractionPositive electrode active material volume fraction" + "0.40.50.60.70.80.40.450.50.550.60.650.70.750.80.10.20.30.4Sigma: 0.08Negative electrode active material volume fractionPositive electrode active material volume fraction" ] }, "metadata": {}, @@ -677,7 +765,7 @@ ], "source": [ "# Plot the cost landscape with optimisation path and updated bounds\n", - "bounds = np.asarray([[0.6, 0.9], [0.5, 0.8]])\n", + "bounds = np.array([[0.4, 0.8], [0.4, 0.8]])\n", "for optim, sigma in zip(optims, sigmas):\n", " pybop.plot2d(optim, bounds=bounds, steps=10, title=f\"Sigma: {sigma}\")" ] @@ -688,12 +776,12 @@ "source": [ "### Updating the Learning Rate\n", "\n", - "Let's take `sigma0 = 0.0115` as the best learning rate for this problem and look at the time-series trajectories." + "Let's take `sigma0 = 0.08` as the best learning rate for this problem and look at the time-series trajectories." ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-04-14T18:59:54.698068Z", @@ -713,7 +801,7 @@ { "data": { "image/svg+xml": [ - "02004006008003.83.853.93.9544.05ReferenceModelOptimised ComparisonTime / sVoltage / V" + "0500100015003.53.553.63.653.7ReferenceModelOptimised ComparisonTime / sVoltage / V" ] }, "metadata": {}, @@ -721,7 +809,7 @@ } ], "source": [ - "optim = pybop.GradientDescent(cost, sigma0=0.0115)\n", + "optim = pybop.Optimisation(cost, optimiser=pybop.GradientDescent, sigma0=0.08)\n", "x, final_cost = optim.run()\n", "pybop.quick_plot(problem, problem_inputs=x, title=\"Optimised Comparison\");" ] diff --git a/pybop/__init__.py b/pybop/__init__.py index 7b3d77bea..92c869f48 100644 --- a/pybop/__init__.py +++ b/pybop/__init__.py @@ -103,6 +103,7 @@ # Optimiser class # +from .optimisers._cuckoo import CuckooSearchImpl from .optimisers._adamw import AdamWImpl from .optimisers.base_optimiser import BaseOptimiser, Result from .optimisers.base_pints_optimiser import BasePintsOptimiser @@ -120,6 +121,7 @@ PSO, SNES, XNES, + CuckooSearch, AdamW, ) from .optimisers.optimisation import Optimisation diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index 6d5edb389..c0f580a2a 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -264,7 +264,7 @@ class MAP(BaseLikelihood): """ - def __init__(self, problem, likelihood, sigma0=None, gradient_step=1e-2): + def __init__(self, problem, likelihood, sigma0=None, gradient_step=1e-3): super(MAP, self).__init__(problem) self.sigma0 = sigma0 self.gradient_step = gradient_step diff --git a/pybop/optimisers/_cuckoo.py b/pybop/optimisers/_cuckoo.py new file mode 100644 index 000000000..0b5907a60 --- /dev/null +++ b/pybop/optimisers/_cuckoo.py @@ -0,0 +1,197 @@ +import numpy as np +from pints import PopulationBasedOptimiser +from scipy.special import gamma + + +class CuckooSearchImpl(PopulationBasedOptimiser): + """ + Cuckoo Search (CS) optimisation algorithm, inspired by the brood parasitism + of some cuckoo species. This algorithm was introduced by Yang and Deb in 2009. + + The algorithm uses a population of host nests (solutions), where each cuckoo + (new solution) tries to replace a worse nest in the population. The quality + or fitness of the nests is determined by the cost function. A fraction + of the worst nests is abandoned at each generation, and new ones are built + randomly. + + The pseudo-code for the Cuckoo Search is as follows: + + 1. Initialise population of n host nests + 2. While (t < max_generations): + a. Get a cuckoo randomly by Lévy flights + b. Evaluate its quality/fitness F + c. Choose a nest among n (say, j) randomly + d. If (F > fitness of j): + i. Replace j with the new solution + e. Abandon a fraction (pa) of the worst nests and build new ones + f. Keep the best solutions/nests + g. Rank the solutions and find the current best + 3. End While + + This implementation also uses a decreasing step size for the Lévy flights, calculated + as sigma = sigma0 / sqrt(iterations), where sigma0 is the initial step size and + iterations is the current iteration number. + + Parameters: + - pa: Probability of discovering alien eggs/solutions (abandoning rate) + + References: + - X. -S. Yang and Suash Deb, "Cuckoo Search via Lévy flights," + 2009 World Congress on Nature & Biologically Inspired Computing (NaBIC), + Coimbatore, India, 2009, pp. 210-214, https://doi.org/10.1109/NABIC.2009.5393690. + + - S. Walton, O. Hassan, K. Morgan, M.R. Brown, + Modified cuckoo search: A new gradient free optimisation algorithm, + Chaos, Solitons & Fractals, Volume 44, Issue 9, 2011, + Pages 710-718, ISSN 0960-0779, + https://doi.org/10.1016/j.chaos.2011.06.004. + """ + + def __init__(self, x0, sigma0=0.01, boundaries=None, pa=0.25): + super().__init__(x0, sigma0, boundaries=boundaries) + + # Problem dimensionality + self._dim = len(x0) + + # Population size and abandon rate + self._n = self._population_size + self._pa = pa + self.step_size = self._sigma0 + self.beta = 1.5 + + # Set states + self._running = False + self._ready_for_tell = False + + # Initialise nests + if self._boundaries: + self._nests = np.random.uniform( + low=self._boundaries.lower(), + high=self._boundaries.upper(), + size=(self._n, self._dim), + ) + else: + self._nests = np.random.normal( + self._x0, self._sigma0, size=(self._n, self._dim) + ) + + self._fitness = np.full(self._n, np.inf) + + # Initialise best solutions + self._x_best = np.copy(x0) + self._f_best = np.inf + + # Set iteration count + self._iterations = 0 + + def ask(self): + """ + Returns a list of next points in the parameter-space + to evaluate from the optimiser. + """ + # Set flag to indicate that the optimiser is ready to receive replies + self._ready_for_tell = True + self._running = True + + # Generate new solutions (cuckoos) by Lévy flights + self.step_size = self._sigma0 / max(1, np.sqrt(self._iterations)) + step = self.levy_flight(self.beta, self._dim) * self.step_size + self.cuckoos = self._nests + step + return self.clip_nests(self.cuckoos) + + def tell(self, replies): + """ + Receives a list of function values from the cost function from points + previously specified by `self.ask()`, and updates the optimiser state + accordingly. + """ + # Update iteration count + self._iterations += 1 + + # Compare cuckoos with current nests + for i in range(self._n): + f_new = replies[i] + if f_new < self._fitness[i]: + self._nests[i] = self.cuckoos[i] + self._fitness[i] = f_new + if f_new < self._f_best: + self._f_best = f_new + self._x_best = self.cuckoos[i] + + # Abandon some worse nests + n_abandon = int(self._pa * self._n) + worst_nests = np.argsort(self._fitness)[-n_abandon:] + for idx in worst_nests: + self.abandon_nests(idx) + self._fitness[idx] = np.inf # reset fitness + + def levy_flight(self, alpha, size): + """ + Generate step sizes via the Mantegna's algorithm for Levy flights + """ + from numpy import pi, power, random, sin + + sigma_u = power( + (gamma(1 + alpha) * sin(pi * alpha / 2)) + / (gamma((1 + alpha) / 2) * alpha * power(2, (alpha - 1) / 2)), + 1 / alpha, + ) + sigma_v = 1 + + u = random.normal(0, sigma_u, size=size) + v = random.normal(0, sigma_v, size=size) + step = u / power(abs(v), 1 / alpha) + + return step + + def abandon_nests(self, idx): + """ + Updates the nests to abandon the worst performers and reinitialise. + """ + if self._boundaries: + self._nests[idx] = np.random.uniform( + low=self._boundaries.lower(), + high=self._boundaries.upper(), + ) + else: + self._nests[idx] = np.random.normal(self._x0, self._sigma0) + + def clip_nests(self, x): + """ + Clip the input array to the boundaries if available. + """ + if self._boundaries: + x = np.clip(x, self._boundaries.lower(), self._boundaries.upper()) + return x + + def _suggested_population_size(self): + """ + Inherited from Pints:PopulationBasedOptimiser. + Returns a suggested population size, based on the + dimension of the parameter space. + """ + return 4 + int(3 * np.log(self._n_parameters)) + + def running(self): + """ + Returns ``True`` if the optimisation is in progress. + """ + return self._running + + def x_best(self): + """ + Returns the best parameter values found so far. + """ + return self._x_best + + def f_best(self): + """ + Returns the best score found so far. + """ + return self._f_best + + def name(self): + """ + Returns the name of the optimiser. + """ + return "Cuckoo Search" diff --git a/pybop/optimisers/pints_optimisers.py b/pybop/optimisers/pints_optimisers.py index 5d616756c..64b77b674 100644 --- a/pybop/optimisers/pints_optimisers.py +++ b/pybop/optimisers/pints_optimisers.py @@ -9,7 +9,7 @@ from pints import IRPropMin as PintsIRPropMin from pints import NelderMead as PintsNelderMead -from pybop import AdamWImpl, BasePintsOptimiser +from pybop import AdamWImpl, BasePintsOptimiser, CuckooSearchImpl class GradientDescent(BasePintsOptimiser): @@ -275,3 +275,31 @@ def __init__(self, cost, **optimiser_kwargs): + "Please choose another optimiser." ) super().__init__(cost, PintsCMAES, **optimiser_kwargs) + + +class CuckooSearch(BasePintsOptimiser): + """ + Adapter for the Cuckoo Search optimiser in PyBOP. + + Cuckoo Search is a population-based optimisation algorithm inspired by the brood parasitism of some cuckoo species. + It is designed to be simple, efficient, and robust, and is suitable for global optimisation problems. + + Parameters + ---------- + **optimiser_kwargs : optional + Valid PyBOP option keys and their values, for example: + x0 : array_like + Initial parameter values. + sigma0 : float + Initial step size. + bounds : dict + A dictionary with 'lower' and 'upper' keys containing arrays for lower and + upper bounds on the parameters. + + See Also + -------- + pybop.CuckooSearch : PyBOP implementation of Cuckoo Search algorithm. + """ + + def __init__(self, cost, **optimiser_kwargs): + super().__init__(cost, CuckooSearchImpl, **optimiser_kwargs) diff --git a/tests/integration/test_spm_parameterisations.py b/tests/integration/test_spm_parameterisations.py index 66a58638f..85ddd6007 100644 --- a/tests/integration/test_spm_parameterisations.py +++ b/tests/integration/test_spm_parameterisations.py @@ -87,6 +87,7 @@ def spm_costs(self, model, parameters, cost_class, init_soc): pybop.SciPyDifferentialEvolution, pybop.AdamW, pybop.CMAES, + pybop.CuckooSearch, pybop.IRPropMin, pybop.NelderMead, pybop.SNES, @@ -108,7 +109,7 @@ def test_spm_optimisers(self, optimiser, spm_costs): ) # Set sigma0 and create optimiser - sigma0 = 0.01 if isinstance(spm_costs, pybop.GaussianLogLikelihood) else 0.05 + sigma0 = 0.006 if isinstance(spm_costs, pybop.MAP) else None optim = optimiser(sigma0=sigma0, **common_args) # Set max unchanged iterations for BasePintsOptimisers diff --git a/tests/unit/test_optimisation.py b/tests/unit/test_optimisation.py index 5770b9910..8444159b9 100644 --- a/tests/unit/test_optimisation.py +++ b/tests/unit/test_optimisation.py @@ -75,6 +75,7 @@ def two_param_cost(self, model, two_parameters, dataset): (pybop.Adam, "Adam"), (pybop.AdamW, "AdamW"), (pybop.CMAES, "Covariance Matrix Adaptation Evolution Strategy (CMA-ES)"), + (pybop.CuckooSearch, "Cuckoo Search"), (pybop.SNES, "Seperable Natural Evolution Strategy (SNES)"), (pybop.XNES, "Exponential Natural Evolution Strategy (xNES)"), (pybop.PSO, "Particle Swarm Optimisation (PSO)"), @@ -126,6 +127,7 @@ def test_no_optimisation_parameters(self, model, dataset): pybop.PSO, pybop.IRPropMin, pybop.NelderMead, + pybop.CuckooSearch, ], ) @pytest.mark.unit @@ -175,6 +177,7 @@ def test_optimiser_kwargs(self, cost, optimiser): ): warnings.simplefilter("always") optim = optimiser(cost=cost, unrecognised=10) + assert not optim.pints_optimiser.running() else: # Check bounds in list format and update tol bounds = [ @@ -249,7 +252,6 @@ def test_optimiser_kwargs(self, cost, optimiser): # Check defaults assert optim.pints_optimiser.n_hyper_parameters() == 5 - assert not optim.pints_optimiser.running() assert optim.pints_optimiser.x_guessed() == optim.pints_optimiser._x0 with pytest.raises(Exception): optim.pints_optimiser.tell([0.1]) @@ -263,6 +265,12 @@ def test_optimiser_kwargs(self, cost, optimiser): assert optim.x0 == x0_new assert optim.x0 != x0 + @pytest.mark.unit + def test_cuckoo_no_bounds(self, dataset, cost, model): + optim = pybop.CuckooSearch(cost=cost, bounds=None, max_iterations=1) + optim.run() + assert optim.pints_optimiser._boundaries is None + @pytest.mark.unit def test_scipy_minimize_with_jac(self, cost): # Check a method that uses gradient information