From 1f1e91de5a253b35b4cf69a75b052c39f29e9ec1 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Sun, 5 May 2024 04:30:04 +0100 Subject: [PATCH 01/41] fixes to pybop.experiment, pybop.base_model for pybamm@develop ahead of pybamm v24.5 --- examples/scripts/parameters/example_BPX.json | 7 ++----- pybop/_experiment.py | 2 -- pybop/models/base_model.py | 16 ++++++++++++---- pyproject.toml | 4 +++- tests/unit/test_experiment.py | 6 +++--- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/examples/scripts/parameters/example_BPX.json b/examples/scripts/parameters/example_BPX.json index 43bbcf90..1e1efaad 100644 --- a/examples/scripts/parameters/example_BPX.json +++ b/examples/scripts/parameters/example_BPX.json @@ -1,7 +1,7 @@ { "Header": { - "BPX": 0.1, - "Title": "Parameterisation example of an LFP|graphite 2 Ah cylindrical 18650 cell.", + "BPX": 0.4, + "Title": "Parameterisation example of an LFP|graphite 2 Ah cylindrical 18650 cell. File downloaded on 19/3/24 from https://github.com/FaradayInstitution/BPX/blob/main/examples/lfp_18650_cell_BPX.json", "Description": "LFP|graphite 2 Ah cylindrical 18650 cell. Parameterisation by About:Energy Limited (aboutenergy.io), December 2022, based on cell cycling data, and electrode data gathered after cell teardown. Electrolyte properties from Nyman et al. 2008 (doi:10.1016/j.electacta.2008.04.023). Negative electrode entropic coefficient data are from O'Regan et al. 2022 (doi:10.1016/j.electacta.2022.140700). Positive electrode entropic coefficient data are from Gerver and Meyers 2011 (doi:10.1149/1.3591799). Other thermal properties are estimated.", "Model": "DFN" }, @@ -70,9 +70,6 @@ "Thickness [m]": 2e-05, "Porosity": 0.47, "Transport efficiency": 0.3222 - }, - "User-defined": { - "Source:": "An example BPX json file downloaded on 19/3/24 from https://github.com/FaradayInstitution/BPX/blob/main/examples/lfp_18650_cell_BPX.json" } } } diff --git a/pybop/_experiment.py b/pybop/_experiment.py index 1c495384..a651dffc 100644 --- a/pybop/_experiment.py +++ b/pybop/_experiment.py @@ -49,6 +49,4 @@ def __init__( period, temperature, termination, - drive_cycles, - cccv_handling, ) diff --git a/pybop/models/base_model.py b/pybop/models/base_model.py index a26a5f31..4134bfd8 100644 --- a/pybop/models/base_model.py +++ b/pybop/models/base_model.py @@ -121,9 +121,13 @@ def build( self.set_params() self._mesh = pybamm.Mesh(self.geometry, self.submesh_types, self.var_pts) - self._disc = pybamm.Discretisation(self.mesh, self.spatial_methods) + self._disc = pybamm.Discretisation( + mesh=self.mesh, + spatial_methods=self.spatial_methods, + check_model=check_model, + ) self._built_model = self._disc.process_model( - self._model_with_set_params, inplace=False, check_model=check_model + self._model_with_set_params, inplace=False ) # Clear solver and setup model @@ -229,9 +233,13 @@ def rebuild( self.set_params(rebuild=True) self._mesh = pybamm.Mesh(self.geometry, self.submesh_types, self.var_pts) - self._disc = pybamm.Discretisation(self.mesh, self.spatial_methods) + self._disc = pybamm.Discretisation( + mesh=self.mesh, + spatial_methods=self.spatial_methods, + check_model=check_model, + ) self._built_model = self._disc.process_model( - self._model_with_set_params, inplace=False, check_model=check_model + self._model_with_set_params, inplace=False ) # Clear solver and setup model diff --git a/pyproject.toml b/pyproject.toml index 14ec41be..7e4f89ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,9 @@ classifiers = [ ] requires-python = ">=3.9, <3.13" dependencies = [ - "pybamm>=23.9", + # "pybamm>=23.9", + # "pybamm @ git+https://github.com/pybamm-team/PyBaMM@develop", + "pybamm @ git+https://github.com/BradyPlanden/PyBaMM@fix-electrode-diffusion-rename", "numpy>=1.16", "scipy>=1.3", "pints>=0.5", diff --git a/tests/unit/test_experiment.py b/tests/unit/test_experiment.py index 6d18ef50..71355ff8 100644 --- a/tests/unit/test_experiment.py +++ b/tests/unit/test_experiment.py @@ -18,9 +18,9 @@ def test_experiment(self): pybop_experiment = pybop.Experiment(protocol) pybamm_experiment = pybamm.Experiment(protocol) - assert [ - step.to_dict() for step in pybop_experiment.operating_conditions_steps - ] == [step.to_dict() for step in pybamm_experiment.operating_conditions_steps] + assert [step.to_dict() for step in pybop_experiment.steps] == [ + step.to_dict() for step in pybamm_experiment.steps + ] assert pybop_experiment.cycle_lengths == pybamm_experiment.cycle_lengths From 1770dc818ebd714b088a4715eaebf72c03dda7a6 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 6 May 2024 00:15:05 +0100 Subject: [PATCH 02/41] increment number of states in spm_UKF example --- examples/scripts/spm_UKF.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/scripts/spm_UKF.py b/examples/scripts/spm_UKF.py index 5299c581..49380aba 100644 --- a/examples/scripts/spm_UKF.py +++ b/examples/scripts/spm_UKF.py @@ -22,7 +22,7 @@ # Make a prediction with measurement noise sigma = 0.001 -t_eval = np.arange(0, 300, 2) +t_eval = np.arange(0, 900, 0.5) values = model.predict(t_eval=t_eval) corrupt_values = values["Voltage [V]"].data + np.random.normal(0, sigma, len(t_eval)) @@ -42,8 +42,8 @@ signal = ["Voltage [V]"] n_states = model.n_states n_signals = len(signal) -covariance = np.diag([0] * 19 + [sigma**2] + [0] * 19 + [sigma**2]) -process_noise = np.diag([0] * 19 + [1e-6] + [0] * 19 + [1e-6]) +covariance = np.diag([0] * 20 + [sigma**2] + [0] * 20 + [sigma**2]) +process_noise = np.diag([0] * 20 + [1e-6] + [0] * 20 + [1e-6]) measurement_noise = np.diag([sigma**2]) observer = pybop.UnscentedKalmanFilterObserver( parameters, From 786d9ca637740a4c49eb84ffabca0ea0c6d6c948 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 22 May 2024 16:55:41 +0100 Subject: [PATCH 03/41] fix: update non-converged DFN test --- pyproject.toml | 3 +-- tests/unit/test_models.py | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9d2b02f9..0de3b16e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,8 +27,7 @@ classifiers = [ requires-python = ">=3.9, <3.13" dependencies = [ # "pybamm>=23.9", - # "pybamm @ git+https://github.com/pybamm-team/PyBaMM@develop", - "pybamm @ git+https://github.com/BradyPlanden/PyBaMM@fix-electrode-diffusion-rename", + "pybamm @ git+https://github.com/pybamm-team/PyBaMM@develop", "numpy>=1.16", "scipy>=1.3", "pints>=0.5", diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index 3137f6f2..671097b7 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -282,10 +282,13 @@ def test_non_converged_solution(self): "Voltage [V]": np.zeros(100), } ) - problem = pybop.FittingProblem(model, parameters=parameters, dataset=dataset) - res = problem.evaluate([-0.2, -0.2]) - _, res_grad = problem.evaluateS1([-0.2, -0.2]) + + # Simulate the DFN with active material values of 0 + # This should not converge, and as such, the + # solution from model.simulate should be inf + res = problem.evaluate([0, 0]) + _, res_grad = problem.evaluateS1([0, 0]) for key in problem.signal: assert np.isinf(res.get(key, [])).any() From 7711a4e80658c26feec3cd6a4e45f35981382502 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Thu, 27 Jun 2024 18:47:28 +0100 Subject: [PATCH 04/41] update target pybamm source to forked develop w/ diffusivity fix --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0de3b16e..1b024709 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ classifiers = [ requires-python = ">=3.9, <3.13" dependencies = [ # "pybamm>=23.9", - "pybamm @ git+https://github.com/pybamm-team/PyBaMM@develop", + "pybamm @ git+https://github.com/bradyplanden/PyBaMM@v24.5rc0-diffusivity-test", "numpy>=1.16", "scipy>=1.3", "pints>=0.5", From a76959f41de09d8a6f39449fa7468d2a00b9afcb Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Fri, 28 Jun 2024 09:58:45 +0100 Subject: [PATCH 05/41] tests: updts to test_non_converged_solution. Changes to pybamm v24.5 have resulted in solver.solve() to return values for incorrect inputs values when not requesting sensitivities. This removes the test that assert output == np.inf in this case. --- tests/unit/test_models.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index 2bb57733..a458a4d2 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -342,12 +342,11 @@ def test_non_converged_solution(self): ) problem = pybop.FittingProblem(model, parameters=parameters, dataset=dataset) - # Simulate the DFN with active material values of 0 - # This should not converge, and as such, the - # solution from model.simulate should be inf - res = problem.evaluate([0, 0]) - _, res_grad = problem.evaluateS1([0, 0]) + # Simulate the DFN with active material values of 0. + # The solution from model.simulateS1 should be inf + # and the gradient should be inf. + output_S1, res_grad = problem.evaluateS1([0, 0]) for key in problem.signal: - assert np.isinf(res.get(key, [])).any() + assert np.isinf(output_S1.get(key, [])).any() assert np.isinf(res_grad).any() From c8deb1501eb9b3dfae3bdf9fee0c304316d22fa8 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Fri, 28 Jun 2024 10:50:06 +0100 Subject: [PATCH 06/41] test: updt asserts to capture differing outputs --- tests/unit/test_models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index a458a4d2..4a05b55a 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -343,10 +343,11 @@ def test_non_converged_solution(self): problem = pybop.FittingProblem(model, parameters=parameters, dataset=dataset) # Simulate the DFN with active material values of 0. - # The solution from model.simulateS1 should be inf - # and the gradient should be inf. + # The solutions will not change as the solver will not converge. + output = problem.evaluate([0, 0]) output_S1, res_grad = problem.evaluateS1([0, 0]) for key in problem.signal: + assert np.allclose(output.get(key, [])[0], output.get(key, [])) assert np.isinf(output_S1.get(key, [])).any() assert np.isinf(res_grad).any() From 06dd97980ee411b2fac8b51d15185121f2e42598 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Fri, 28 Jun 2024 11:08:25 +0100 Subject: [PATCH 07/41] test: revert to allclose as output vector varies for each OS --- tests/unit/test_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index 4a05b55a..6fc431d5 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -349,5 +349,5 @@ def test_non_converged_solution(self): for key in problem.signal: assert np.allclose(output.get(key, [])[0], output.get(key, [])) - assert np.isinf(output_S1.get(key, [])).any() + assert np.allclose(output_S1.get(key, [])[0], output_S1.get(key, [])) assert np.isinf(res_grad).any() From c2613700ca18c51d95b57cd0c5337fe655c3e0c4 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Fri, 28 Jun 2024 18:26:54 +0100 Subject: [PATCH 08/41] tests: remove gradient == np.inf assert --- tests/unit/test_models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index 6fc431d5..07147a74 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -345,9 +345,8 @@ def test_non_converged_solution(self): # Simulate the DFN with active material values of 0. # The solutions will not change as the solver will not converge. output = problem.evaluate([0, 0]) - output_S1, res_grad = problem.evaluateS1([0, 0]) + output_S1, _ = problem.evaluateS1([0, 0]) for key in problem.signal: assert np.allclose(output.get(key, [])[0], output.get(key, [])) assert np.allclose(output_S1.get(key, [])[0], output_S1.get(key, [])) - assert np.isinf(res_grad).any() From 3e4012850c4de4d340fd326bd8797bf95402e787 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Sun, 30 Jun 2024 13:34:09 +0100 Subject: [PATCH 09/41] tests: Add missing asserts --- tests/unit/test_cost.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 3c0d8151..49a1b8b0 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -149,7 +149,8 @@ def test_costs(self, cost): cost([1.1]) # Test option setting - cost.set_fail_gradient(1) + cost.set_fail_gradient(10) + assert cost._de == 10 if isinstance(cost, pybop.SumSquaredError): e, de = cost.evaluateS1([0.5]) @@ -229,4 +230,4 @@ def test_design_costs( # Compute after updating nominal capacity cost = cost_class(problem, update_capacity=True) - cost([0.4]) + assert np.isfinite(cost([0.4])) From 7423dd8718164ba5a365e7485292874e3dec68aa Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Sun, 30 Jun 2024 18:46:30 +0100 Subject: [PATCH 10/41] feat: add Minkowski cost, remove redundant code in fitting_costs.py, _likelihoods.py --- examples/scripts/spm_AdamW.py | 3 +- examples/scripts/spm_CMAES.py | 3 +- pybop/__init__.py | 1 + pybop/costs/_likelihoods.py | 37 +++----- pybop/costs/base_cost.py | 38 ++++++++ pybop/costs/fitting_costs.py | 157 ++++++++++++++++++++++------------ tests/unit/test_cost.py | 5 +- 7 files changed, 159 insertions(+), 85 deletions(-) diff --git a/examples/scripts/spm_AdamW.py b/examples/scripts/spm_AdamW.py index 44bbf8b1..a4a34479 100644 --- a/examples/scripts/spm_AdamW.py +++ b/examples/scripts/spm_AdamW.py @@ -53,7 +53,8 @@ def noise(sigma): problem = pybop.FittingProblem( model, parameters, dataset, signal=signal, init_soc=init_soc ) -cost = pybop.RootMeanSquaredError(problem) +# cost = pybop.RootMeanSquaredError(problem) +cost = pybop.Minkowski(problem, p=2) optim = pybop.AdamW( cost, verbose=True, diff --git a/examples/scripts/spm_CMAES.py b/examples/scripts/spm_CMAES.py index 1fc051cc..2a89b5d6 100644 --- a/examples/scripts/spm_CMAES.py +++ b/examples/scripts/spm_CMAES.py @@ -41,7 +41,8 @@ signal = ["Voltage [V]", "Bulk open-circuit voltage [V]"] # Generate problem, cost function, and optimisation class problem = pybop.FittingProblem(model, parameters, dataset, signal=signal) -cost = pybop.SumSquaredError(problem) +# cost = pybop.SumSquaredError(problem) +cost = pybop.Minkowski(problem, p=2) optim = pybop.CMAES(cost, max_iterations=100) # Run the optimisation diff --git a/pybop/__init__.py b/pybop/__init__.py index 61971e95..e130d3cc 100644 --- a/pybop/__init__.py +++ b/pybop/__init__.py @@ -85,6 +85,7 @@ from .costs.fitting_costs import ( RootMeanSquaredError, SumSquaredError, + Minkowski, ObserverCost, MAP, ) diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index cce09f9b..088ea9b5 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -34,7 +34,6 @@ def __init__(self, problem, sigma): self._offset = -0.5 * self.n_time_data * np.log(2 * np.pi / self.sigma) self._multip = -1 / (2.0 * self.sigma**2) self.sigma2 = self.sigma**-2 - self._dl = np.ones(self.n_parameters) def set_sigma(self, sigma): """ @@ -70,9 +69,8 @@ def _evaluate(self, x, grad=None): """ y = self.problem.evaluate(x) - for key in self.signal: - if len(y.get(key, [])) != len(self._target.get(key, [])): - return -np.float64(np.inf) # prediction doesn't match target + if not self.verify_prediction(y): + return -np.inf e = np.asarray( [ @@ -84,10 +82,7 @@ def _evaluate(self, x, grad=None): ] ) - if self.n_outputs == 1: - return e.item() - else: - return np.sum(e) + return float(e) if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, x, grad=None): """ @@ -96,11 +91,9 @@ def _evaluateS1(self, x, grad=None): """ y, dy = self.problem.evaluateS1(x) - for key in self.signal: - if len(y.get(key, [])) != len(self._target.get(key, [])): - likelihood = np.float64(np.inf) - dl = self._dl * np.ones(self.n_parameters) - return -likelihood, -dl + if not self.verify_prediction(y): + dl = self._de * np.ones(self.n_parameters) + return -np.inf, dl r = np.asarray([self._target[signal] - y[signal] for signal in self.signal]) likelihood = self._evaluate(x) @@ -144,9 +137,8 @@ def _evaluate(self, x, grad=None): y = self.problem.evaluate(x[: -self.n_outputs]) - for key in self.signal: - if len(y.get(key, [])) != len(self._target.get(key, [])): - return -np.float64(np.inf) # prediction doesn't match target + if not self.verify_prediction(y): + return -np.inf e = np.asarray( [ @@ -159,10 +151,7 @@ def _evaluate(self, x, grad=None): ] ) - if self.n_outputs == 1: - return e.item() - else: - return np.sum(e) + return float(e) if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, x, grad=None): """ @@ -175,11 +164,9 @@ def _evaluateS1(self, x, grad=None): return -np.float64(np.inf), -self._dl * np.ones(self.n_parameters) y, dy = self.problem.evaluateS1(x[: -self.n_outputs]) - for key in self.signal: - if len(y.get(key, [])) != len(self._target.get(key, [])): - likelihood = np.float64(np.inf) - dl = self._dl * np.ones(self.n_parameters) - return -likelihood, -dl + if not self.verify_prediction(y): + dl = self._dl * np.ones(self.n_parameters) + return -np.inf, dl r = np.asarray([self._target[signal] - y[signal] for signal in self.signal]) likelihood = self._evaluate(x) diff --git a/pybop/costs/base_cost.py b/pybop/costs/base_cost.py index 04d0a393..5e5fc413 100644 --- a/pybop/costs/base_cost.py +++ b/pybop/costs/base_cost.py @@ -27,6 +27,7 @@ def __init__(self, problem=None): self.parameters = None self.problem = problem self.x0 = None + self.set_fail_gradient() if isinstance(self.problem, BaseProblem): self._target = self.problem._target self.parameters = self.problem.parameters @@ -151,3 +152,40 @@ def _evaluateS1(self, x): If the method has not been implemented by the subclass. """ raise NotImplementedError + + def set_fail_gradient(self, de: float = 1.0): + """ + Set the fail gradient to a specified value. + + The fail gradient is used if an error occurs during the calculation + of the gradient. This method allows updating the default gradient value. + + Parameters + ---------- + de : float + The new fail gradient value to be used. + """ + if not isinstance(de, float): + de = float(de) + self._de = de + + def verify_prediction(self, y): + """ + Verify that the prediction matches the target data. + + Parameters + ---------- + y : dict + The model predictions. + + Returns + ------- + bool + True if the prediction matches the target data, otherwise False. + """ + if any( + len(y.get(key, [])) != len(self._target.get(key, [])) for key in self.signal + ): + return False + + return True diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index eff56059..22319998 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -20,9 +20,6 @@ class RootMeanSquaredError(BaseCost): def __init__(self, problem): super(RootMeanSquaredError, self).__init__(problem) - # Default fail gradient - self._de = 1.0 - def _evaluate(self, x, grad=None): """ Calculate the root mean square error for a given set of parameters. @@ -43,9 +40,8 @@ def _evaluate(self, x, grad=None): """ prediction = self.problem.evaluate(x) - for key in self.signal: - if len(prediction.get(key, [])) != len(self._target.get(key, [])): - return np.float64(np.inf) # prediction doesn't match target + if not self.verify_prediction(prediction): + return np.inf e = np.asarray( [ @@ -54,10 +50,7 @@ def _evaluate(self, x, grad=None): ] ) - if self.n_outputs == 1: - return e.item() - else: - return np.sum(e) + return float(e) if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, x): """ @@ -80,12 +73,8 @@ def _evaluateS1(self, x): If an error occurs during the calculation of the cost or gradient. """ y, dy = self.problem.evaluateS1(x) - - for key in self.signal: - if len(y.get(key, [])) != len(self._target.get(key, [])): - e = np.float64(np.inf) - de = self._de * np.ones(self.n_parameters) - return e, de + if not self.verify_prediction(y): + return np.inf, self._de * np.ones(self.n_parameters) r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) e = np.sqrt(np.mean(r**2, axis=1)) @@ -96,21 +85,6 @@ def _evaluateS1(self, x): else: return np.sum(e), np.sum(de, axis=1) - def set_fail_gradient(self, de): - """ - Set the fail gradient to a specified value. - - The fail gradient is used if an error occurs during the calculation - of the gradient. This method allows updating the default gradient value. - - Parameters - ---------- - de : float - The new fail gradient value to be used. - """ - de = float(de) - self._de = de - class SumSquaredError(BaseCost): """ @@ -133,9 +107,6 @@ class SumSquaredError(BaseCost): def __init__(self, problem): super(SumSquaredError, self).__init__(problem) - # Default fail gradient - self._de = 1.0 - def _evaluate(self, x, grad=None): """ Calculate the sum of squared errors for a given set of parameters. @@ -155,9 +126,8 @@ def _evaluate(self, x, grad=None): """ prediction = self.problem.evaluate(x) - for key in self.signal: - if len(prediction.get(key, [])) != len(self._target.get(key, [])): - return np.float64(np.inf) # prediction doesn't match target + if not self.verify_prediction(prediction): + return np.inf e = np.asarray( [ @@ -165,10 +135,8 @@ def _evaluate(self, x, grad=None): for signal in self.signal ] ) - if self.n_outputs == 1: - return e.item() - else: - return np.sum(e) + + return float(e) if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, x): """ @@ -191,11 +159,8 @@ def _evaluateS1(self, x): If an error occurs during the calculation of the cost or gradient. """ y, dy = self.problem.evaluateS1(x) - for key in self.signal: - if len(y.get(key, [])) != len(self._target.get(key, [])): - e = np.float64(np.inf) - de = self._de * np.ones(self.n_parameters) - return e, de + if not self.verify_prediction(y): + return np.inf, self._de * np.ones(self.n_parameters) r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) e = np.sum(np.sum(r**2, axis=0), axis=0) @@ -203,20 +168,93 @@ def _evaluateS1(self, x): return e, de - def set_fail_gradient(self, de): + +class Minkowski(BaseCost): + """ + The Minkowski distance is a generalisation of several distance metrics, + including Euclidean and Manhattan distances. It is defined as: + + .. math:: + L_p(x, y) = (\sum_i |x_i - y_i|^p)^{1/p} + + where p ≥ 1 is the order of the Minkowski metric. + + Special cases: + - p = 1: Manhattan distance + - p = 2: Euclidean distance + - p → ∞: Chebyshev distance + + This class implements the Minkowski distance as a cost function for + optimisation problems, allowing for flexible distance-based optimisation + across various problem domains. + + Attributes: + p (float): The order of the Minkowski metric. + + Note: + The cost calculation does not include the p-th root as it doesn't + affect the optimisation process and saves computational effort. + """ + + def __init__(self, problem, p: float = 2.0): + super(Minkowski, self).__init__(problem) + self.p = p + + def _evaluate(self, x, grad=None): + """ + Calculate the Minkowski cost for a given set of parameters. + + Parameters + ---------- + x : array-like + The parameters for which to evaluate the cost. + Returns + ------- + float + The Minkowski cost. """ - Set the fail gradient to a specified value. + prediction = self.problem.evaluate(x) + if not self.verify_prediction(prediction): + return np.inf + + e = np.asarray( + [ + np.sum(np.abs(prediction[signal] - self._target[signal]) ** self.p) + for signal in self.signal + ] + ) - The fail gradient is used if an error occurs during the calculation - of the gradient. This method allows updating the default gradient value. + return float(e) if self.n_outputs == 1 else np.sum(e) + + def _evaluateS1(self, x): + """ + Compute the cost and its gradient with respect to the parameters. Parameters ---------- - de : float - The new fail gradient value to be used. + x : array-like + The parameters for which to compute the cost and gradient. + + Returns + ------- + tuple + A tuple containing the cost and the gradient. The cost is a float, + and the gradient is an array-like of the same length as `x`. + + Raises + ------ + ValueError + If an error occurs during the calculation of the cost or gradient. """ - de = float(de) - self._de = de + y, dy = self.problem.evaluateS1(x) + if not self.verify_prediction(y): + return np.inf, self._de * np.ones(self.n_parameters) + + r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) + e = np.sum(np.sum(np.abs(r) ** self.p)) + de = self.p * np.sum(np.sum(r ** (self.p - 1) * dy.T, axis=2), axis=1) + + return e, de class ObserverCost(BaseCost): @@ -329,7 +367,10 @@ def _evaluate(self, x, grad=None): float The maximum a posteriori cost. """ - log_likelihood = self.likelihood.evaluate(x) + log_likelihood = self.likelihood._evaluate(x) + if not np.isfinite(log_likelihood): + return -np.inf + log_prior = sum( param.prior.logpdf(x_i) for x_i, param in zip(x, self.problem.parameters) ) @@ -358,10 +399,12 @@ def _evaluateS1(self, x): ValueError If an error occurs during the calculation of the cost or gradient. """ - log_likelihood, dl = self.likelihood.evaluateS1(x) + log_likelihood, dl = self.likelihood._evaluateS1(x) + if not np.isfinite(log_likelihood): + return -np.inf, dl + log_prior = sum( param.prior.logpdf(x_i) for x_i, param in zip(x, self.problem.parameters) ) - posterior = log_likelihood + log_prior return posterior, dl diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 3c0d8151..51b1d5bd 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -69,6 +69,7 @@ def problem(self, model, parameters, dataset, signal, request): params=[ pybop.RootMeanSquaredError, pybop.SumSquaredError, + pybop.Minkowski, pybop.ObserverCost, pybop.MAP, ] @@ -79,6 +80,8 @@ def cost(self, problem, request): return cls(problem) elif cls in [pybop.MAP]: return cls(problem, pybop.GaussianLogLikelihoodKnownSigma) + elif cls in [pybop.Minkowski]: + return cls(problem, p=2) elif cls in [pybop.ObserverCost]: inputs = problem.parameters.initial_value() state = problem._model.reinit(inputs) @@ -151,7 +154,7 @@ def test_costs(self, cost): # Test option setting cost.set_fail_gradient(1) - if isinstance(cost, pybop.SumSquaredError): + if not isinstance(cost, (pybop.ObserverCost, pybop.MAP)): e, de = cost.evaluateS1([0.5]) assert np.isscalar(e) From 4c074b243af0dc7e8bd47db1d9ba9f29678facad Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 1 Jul 2024 08:40:57 +0100 Subject: [PATCH 11/41] tests: add catch for incorrect order, tests --- pybop/costs/fitting_costs.py | 4 ++++ tests/unit/test_cost.py | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index 22319998..55f1d629 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -198,6 +198,10 @@ class Minkowski(BaseCost): def __init__(self, problem, p: float = 2.0): super(Minkowski, self).__init__(problem) + if p < 0: + raise ValueError( + "The order of the Minkowski metric must be greater than 0." + ) self.p = p def _evaluate(self, x, grad=None): diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 51b1d5bd..914eba4a 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -181,8 +181,11 @@ def test_costs(self, cost): with pytest.raises(ValueError): cost(["StringInputShouldNotWork"]) - # Test treatment of simulations that terminated early - # by variation of the cut-off voltage. + @pytest.mark.unit + def test_minkowski(self, problem): + # Incorrect order + with pytest.raises(ValueError, match="The order of the Minkowski metric"): + pybop.Minkowski(problem, p=-1) @pytest.mark.parametrize( "cost_class", From b3ef680381395658d26a2964cc2583088f58e59f Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 1 Jul 2024 16:47:03 +0100 Subject: [PATCH 12/41] feat: add cost function comparision example, updt parameters.rvs to work directly with cost() --- .../notebooks/comparing_cost_functions.ipynb | 4505 +++++++++++++++++ pybop/parameters/parameter.py | 4 +- 2 files changed, 4507 insertions(+), 2 deletions(-) create mode 100644 examples/notebooks/comparing_cost_functions.ipynb diff --git a/examples/notebooks/comparing_cost_functions.ipynb b/examples/notebooks/comparing_cost_functions.ipynb new file mode 100644 index 00000000..52666d98 --- /dev/null +++ b/examples/notebooks/comparing_cost_functions.ipynb @@ -0,0 +1,4505 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Investigating different cost functions\n", + "\n", + "In this notebook, we take a look at the different cost function offered in PyBOP. Cost functions conventionally construct a distance metric between two mathematics sets (vectors), which is then used within PyBOP's optimisation algorthims. \n", + "\n", + "First, we install and import the required packages below." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "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.1.1)\n", + "Requirement already satisfied: ipywidgets in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (8.1.3)\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.11 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (4.0.11)\n", + "Requirement already satisfied: jupyterlab-widgets~=3.0.11 in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (from ipywidgets) (3.0.11)\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 numpy as np\n", + "import plotly.graph_objects as go\n", + "\n", + "import pybop" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this notebook, we need to construct parameters, a model and a problem class before we can compare differing cost functions. We start with two parameters, but this is an arbituary selection and can be expanded given the model and data in question." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [], + "source": [ + "parameters = pybop.Parameters(\n", + " pybop.Parameter(\n", + " \"Positive electrode thickness [m]\",\n", + " prior=pybop.Gaussian(7.56e-05, 0.5e-05),\n", + " bounds=[65e-06, 10e-05],\n", + " ),\n", + " pybop.Parameter(\n", + " \"Positive particle radius [m]\",\n", + " prior=pybop.Gaussian(5.22e-06, 0.5e-06),\n", + " bounds=[2e-06, 9e-06],\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will construct the Single Particle Model (SPM) with the Chen2020 parameter set, but like the above, this is an arbitruary selection and can be replaced with any PyBOP model." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": {}, + "outputs": [], + "source": [ + "parameter_set = pybop.ParameterSet.pybamm(\"Chen2020\")\n", + "model = pybop.lithium_ion.SPM(parameter_set=parameter_set)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, as we will need reference data to compare our model predictions to (via the cost function), we will create synthetic data from the model constructed above. " + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "t_eval = np.arange(0, 900, 2)\n", + "values = model.predict(t_eval=t_eval)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can then construct the PyBOP dataset class with the synthetic data as," + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [], + "source": [ + "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", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can put this all together and construct the problem class. In this situation, we are going to compare differing fitting cost functions, so we construct the `FittingProblem`." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "problem = pybop.FittingProblem(model, parameters, dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sum of Square Errors and Root Mean Squared Error\n", + "\n", + "First, let's start with the easiest cost functions, the sum of squared errors (SSE) and the root mean squared error (RMSE). Constructing these classes is very concise in PyBOP, and only requires the problem class." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [], + "source": [ + "cost_SSE = pybop.SumSquaredError(problem)\n", + "cost_RMSE = pybop.RootMeanSquaredError(problem)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can investigate how these functions differ when fitting the parameters. To acquire the distance metric for each of these, we can simply use the constructed class in a call method, such as:" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.2690291451182834e-09" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cost_SSE([7.56e-05, 5.22e-06])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, we can use the `Parameters` class for this," + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[7.56e-05, 5.22e-06]\n" + ] + }, + { + "data": { + "text/plain": [ + "1.2690291451182834e-09" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(parameters.current_value())\n", + "cost_SSE(parameters.current_value())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we want to generate a random sample of candidate solutions from the parameter class prior, we can also do that as:" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[7.60957550e-05 5.48691392e-06]\n" + ] + }, + { + "data": { + "text/plain": [ + "0.014466013735507651" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sample = parameters.rvs()\n", + "print(sample)\n", + "cost_SSE(sample)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Comparing RMSE and SSE\n", + "\n", + "Now, let's vary one of the parameters with a fixed point for the other and create a scatter plot comparing the cost values for the RMSE and SSE functions." + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "mode": "lines", + "name": "SSE", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.053835830058639615, + 0.05111067206222717, + 0.04845554165878352, + 0.04587051804433099, + 0.043355678096208675, + 0.04091109644413583, + 0.038536845477359936, + 0.03623299543973158, + 0.03399961440367254, + 0.031836768282340513, + 0.02974452094548145, + 0.02772293424615578, + 0.02577274436881736, + 0.02389263231281454, + 0.022083354370052568, + 0.020344964563815224, + 0.018677515064371243, + 0.01708105629952934, + 0.015555636789546832, + 0.014101303317362665, + 0.012718100980349315, + 0.011406073186761897, + 0.010165261722848786, + 0.00899570675469101, + 0.007897446886200334, + 0.00687051919031046, + 0.005914959195754908, + 0.005030800981722024, + 0.004218077198061601, + 0.003476819103455494, + 0.002807056595014089, + 0.002210166875264619, + 0.001683316235636507, + 0.0012280406319022843, + 0.0008443648780650153, + 0.0005323126127996372, + 0.00029190631272314684, + 0.00012316735767345152, + 0.00002611605104018711, + 7.716524040349468e-7, + 0.000047152387593465094, + 0.00016527552304902867, + 0.00035515740004700826, + 0.000616813450391432, + 0.0009502582309038722, + 0.0013555054549569752, + 0.0018325680271045303, + 0.0023814580649710623, + 0.0030021869333247487, + 0.003694765325842557, + 0.004459203215392723, + 0.005295511891398787, + 0.006203696238833047, + 0.007183766159368478, + 0.008235729239251834, + 0.009359592477131223, + 0.01055536229286567, + 0.011823044842784848, + 0.01316264573471436, + 0.014574170163856032, + 0.016057622954143685, + 0.017613008498385842, + 0.01924033104259407, + 0.020939594432015526, + 0.022710802251721743, + 0.024553957861849968, + 0.026469064398176645, + 0.028456124854723906, + 0.030515142072100664, + 0.03264611873917076, + 0.03484905751204218, + 0.03712396095868825, + 0.039470831615045304, + 0.041889672012050114, + 0.04438742821038762 + ] + }, + { + "mode": "lines", + "name": "RMSE", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.01093778670061215, + 0.010657357611030684, + 0.010376848339643564, + 0.0100962609189223, + 0.009815597349367391, + 0.009534859603247191, + 0.009254049621335149, + 0.008973169319914116, + 0.008692220583649948, + 0.008411205262074912, + 0.008130125179503895, + 0.007848982134456216, + 0.007567877196680624, + 0.007286613649214647, + 0.007005292343816855, + 0.006723914958120293, + 0.006442483141765766, + 0.00616099853009583, + 0.005879462709682622, + 0.005597877240002611, + 0.005316243657226264, + 0.005034563467065971, + 0.0047528381515910606, + 0.00447106916238934, + 0.00418925792585442, + 0.003907405843128166, + 0.003625514276395038, + 0.003343584564080899, + 0.003061618017418459, + 0.002779615922091673, + 0.0024975795371670853, + 0.0022161863516032415, + 0.0019340896426636757, + 0.0016519622217245394, + 0.001369805240061533, + 0.0010876198404463425, + 0.0008054071609691128, + 0.0005231684616587058, + 0.000240905933879167, + 0.00004140993987049075, + 0.000323702152512203, + 0.0006060354280971968, + 0.0008883910550940557, + 0.0011707675074178496, + 0.0014531637752036625, + 0.0017355789651723812, + 0.002018012238210072, + 0.0023004627867603132, + 0.0025829298322833784, + 0.0028654126427067115, + 0.003147910494065944, + 0.0034304233270995017, + 0.003712949183849843, + 0.003995488055118751, + 0.004278039332646527, + 0.004560602415649159, + 0.004843176710673749, + 0.005125761697930008, + 0.0054083568535111285, + 0.005690961676954868, + 0.005973575693398699, + 0.006256198437174278, + 0.006538829498141437, + 0.006821468468822942, + 0.00710411496586806, + 0.007386768630741797, + 0.007669429125238671, + 0.007952096139414912, + 0.008234769385167406, + 0.008517448592829345, + 0.00880013352323477, + 0.009082823956199262, + 0.009365519694311956, + 0.00964822056271404, + 0.009931703245486995 + ] + } + ], + "layout": { + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x_range = np.linspace(4.8e-06, 5.6e-06, 75)\n", + "y_SSE = []\n", + "y_RMSE = []\n", + "for i in x_range:\n", + " y_SSE.append(cost_SSE([7.56e-05, i]))\n", + " y_RMSE.append(cost_RMSE([7.56e-05, i]))\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(x=x_range, y=y_SSE, mode=\"lines\", name=\"SSE\"))\n", + "fig.add_trace(go.Scatter(x=x_range, y=y_RMSE, mode=\"lines\", name=\"RMSE\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this situation, it's clear that gradient of the SSE cost is much higher than the RMSE. This can be helpful for certain optimisation algorithms, specifically towards improving convergence performance within a predefine number of iterations. However, with incorrect hyperparameter values this can also result in the algorithm not converging due to sampling locations outside of the \"cost valley\"." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Minkowski Cost\n", + "\n", + "Next, we will investigate using the Minkowski cost function. This cost function provides a general formation of the above two cost functions, allowing for hyper parameter calibration on the cost function itself. The Minkowski cost takes the form,\n", + "\n", + "$\\mathcal{L} = \\displaystyle\\sum_{1}^N (\\hat{y}-y)^p$\n", + "\n", + "For p = 1, this becomes L1Norm \n", + "For p = 2, this becomes L2Norm (SSE)\n", + "\n", + "PyBOP offers a Minkowski class, which we will construct below. This class has an optional argument of `p` which designates the order in the above equation. This value can be a float, with the only requirement that it is not negative. First, let's reconstruct the SSE function with a `p` value of 2." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [], + "source": [ + "cost_minkowski = pybop.Minkowski(problem, p=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "mode": "lines", + "name": "SSE", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.053835830058639615, + 0.05111067206222717, + 0.04845554165878352, + 0.04587051804433099, + 0.043355678096208675, + 0.04091109644413583, + 0.038536845477359936, + 0.03623299543973158, + 0.03399961440367254, + 0.031836768282340513, + 0.02974452094548145, + 0.02772293424615578, + 0.02577274436881736, + 0.02389263231281454, + 0.022083354370052568, + 0.020344964563815224, + 0.018677515064371243, + 0.01708105629952934, + 0.015555636789546832, + 0.014101303317362665, + 0.012718100980349315, + 0.011406073186761897, + 0.010165261722848786, + 0.00899570675469101, + 0.007897446886200334, + 0.00687051919031046, + 0.005914959195754908, + 0.005030800981722024, + 0.004218077198061601, + 0.003476819103455494, + 0.002807056595014089, + 0.002210166875264619, + 0.001683316235636507, + 0.0012280406319022843, + 0.0008443648780650153, + 0.0005323126127996372, + 0.00029190631272314684, + 0.00012316735767345152, + 0.00002611605104018711, + 7.716524040349468e-7, + 0.000047152387593465094, + 0.00016527552304902867, + 0.00035515740004700826, + 0.000616813450391432, + 0.0009502582309038722, + 0.0013555054549569752, + 0.0018325680271045303, + 0.0023814580649710623, + 0.0030021869333247487, + 0.003694765325842557, + 0.004459203215392723, + 0.005295511891398787, + 0.006203696238833047, + 0.007183766159368478, + 0.008235729239251834, + 0.009359592477131223, + 0.01055536229286567, + 0.011823044842784848, + 0.01316264573471436, + 0.014574170163856032, + 0.016057622954143685, + 0.017613008498385842, + 0.01924033104259407, + 0.020939594432015526, + 0.022710802251721743, + 0.024553957861849968, + 0.026469064398176645, + 0.028456124854723906, + 0.030515142072100664, + 0.03264611873917076, + 0.03484905751204218, + 0.03712396095868825, + 0.039470831615045304, + 0.041889672012050114, + 0.04438742821038762 + ] + }, + { + "mode": "lines", + "name": "Minkowski", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.053835830058639615, + 0.05111067206222717, + 0.04845554165878352, + 0.04587051804433099, + 0.043355678096208675, + 0.04091109644413583, + 0.038536845477359936, + 0.03623299543973158, + 0.03399961440367254, + 0.031836768282340513, + 0.02974452094548145, + 0.02772293424615578, + 0.02577274436881736, + 0.02389263231281454, + 0.022083354370052568, + 0.020344964563815224, + 0.018677515064371243, + 0.01708105629952934, + 0.015555636789546832, + 0.014101303317362665, + 0.012718100980349315, + 0.011406073186761897, + 0.010165261722848786, + 0.00899570675469101, + 0.007897446886200334, + 0.00687051919031046, + 0.005914959195754908, + 0.005030800981722024, + 0.004218077198061601, + 0.003476819103455494, + 0.002807056595014089, + 0.002210166875264619, + 0.001683316235636507, + 0.0012280406319022843, + 0.0008443648780650153, + 0.0005323126127996372, + 0.00029190631272314684, + 0.00012316735767345152, + 0.00002611605104018711, + 7.716524040349468e-7, + 0.000047152387593465094, + 0.00016527552304902867, + 0.00035515740004700826, + 0.000616813450391432, + 0.0009502582309038722, + 0.0013555054549569752, + 0.0018325680271045303, + 0.0023814580649710623, + 0.0030021869333247487, + 0.003694765325842557, + 0.004459203215392723, + 0.005295511891398787, + 0.006203696238833047, + 0.007183766159368478, + 0.008235729239251834, + 0.009359592477131223, + 0.01055536229286567, + 0.011823044842784848, + 0.01316264573471436, + 0.014574170163856032, + 0.016057622954143685, + 0.017613008498385842, + 0.01924033104259407, + 0.020939594432015526, + 0.022710802251721743, + 0.024553957861849968, + 0.026469064398176645, + 0.028456124854723906, + 0.030515142072100664, + 0.03264611873917076, + 0.03484905751204218, + 0.03712396095868825, + 0.039470831615045304, + 0.041889672012050114, + 0.04438742821038762 + ] + } + ], + "layout": { + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "y_minkowski = []\n", + "for i in x_range:\n", + " y_minkowski.append(cost_minkowski([7.56e-05, i]))\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(x=x_range, y=y_SSE, mode=\"lines\", name=\"SSE\"))\n", + "fig.add_trace(go.Scatter(x=x_range, y=y_minkowski, mode=\"lines\", name=\"Minkowski\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As expected, these cost functions are equivalent. Now, let take a look at how the Minkowski cost changes for different orders of `p`." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [], + "source": [ + "y_minkowski = ()\n", + "p_orders = np.append(np.linspace(1, 3, 5), 0.75)\n", + "for j in p_orders:\n", + " minkowski_order = []\n", + " cost = pybop.Minkowski(problem, p=j)\n", + " for i in x_range:\n", + " minkowski_order.append(cost([7.56e-05, i]))\n", + " y_minkowski += (minkowski_order,)" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "mode": "lines", + "name": "Minkowski 1.0", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 4.552656766704644, + 4.436202925117051, + 4.3197021313635515, + 4.203155156607345, + 4.086562761333203, + 3.969925696234225, + 3.8532447005548, + 3.736520507607969, + 3.619753838775067, + 3.502945403357782, + 3.3860959024243926, + 3.2692060285657063, + 3.152315105913478, + 3.03534657334722, + 2.9183396802589217, + 2.801295078350584, + 2.684213409805027, + 2.567095312655548, + 2.4499414075372727, + 2.3327523058899207, + 2.215528611512171, + 2.098270915375601, + 1.9809798054224617, + 1.8636558566894776, + 1.746299635055733, + 1.628911698808471, + 1.511492592197115, + 1.3940428516001764, + 1.276563003640566, + 1.1590535660401926, + 1.0415150470108951, + 0.9241984469792855, + 0.8066051058958523, + 0.6889841418336626, + 0.5713360225482647, + 0.4536612084843443, + 0.3359601445828524, + 0.21823327001441095, + 0.10048101551366571, + 0.017296186570773475, + 0.135097921585269, + 0.2529237996046896, + 0.3707734312884394, + 0.4886464319121595, + 0.6065424260637728, + 0.7244610470396249, + 0.8424019374031357, + 0.9603647467458134, + 1.0783491329691324, + 1.19635477070232, + 1.3143813351847973, + 1.4324287524120316, + 1.550496227549572, + 1.6685837041158282, + 1.7866908973881013, + 1.9048175251070956, + 2.0229633061020045, + 2.1411279864650505, + 2.2593113100697884, + 2.3775130288520545, + 2.4957329036803886, + 2.613970698878771, + 2.7322261979812748, + 2.8504991844034917, + 2.9687894504882872, + 3.087096797214695, + 3.2054210324010484, + 3.3237619739633995, + 3.442119447297814, + 3.56049328402628, + 3.678883326505933, + 3.7972894235888703, + 3.9157114320354554, + 4.0341492162964325, + 4.152895980262809 + ] + }, + { + "mode": "lines", + "name": "Minkowski 1.5", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.4898109531501431, + 0.4711163397275865, + 0.45265948491854513, + 0.43444404722086893, + 0.4164738187122092, + 0.3987527352296719, + 0.3812848871293262, + 0.36407453205727613, + 0.34712610776420716, + 0.33044424698020214, + 0.3140337947271509, + 0.29789982699749845, + 0.28205309391276184, + 0.2664882597848408, + 0.25121674427648943, + 0.2362447863684241, + 0.22157899356130947, + 0.20722638305226135, + 0.19319442692445904, + 0.17949110797000248, + 0.1661249841016531, + 0.15310526393728813, + 0.1404418981327189, + 0.1281456881391741, + 0.1162284196891101, + 0.1047030269938625, + 0.09358379719055644, + 0.08288663103488439, + 0.07262937839742101, + 0.06283228046628397, + 0.05351856620452157, + 0.04473477504117415, + 0.03647281441377362, + 0.028792057135636567, + 0.021740939035673496, + 0.015382332300756214, + 0.009802659840611192, + 0.005132054317706342, + 0.0016035400319770717, + 0.0001143556885559469, + 0.0024985227036004886, + 0.006400451926923809, + 0.01136008053208959, + 0.017186853071309068, + 0.023767253976017148, + 0.03102353718811392, + 0.03889810774601821, + 0.04734606726107551, + 0.05633113355395888, + 0.06582319171032898, + 0.0757967203622376, + 0.08622975311116587, + 0.09710303931895804, + 0.10839963798281269, + 0.12010442290147164, + 0.13220379410484237, + 0.14468543663157718, + 0.15753813249704368, + 0.17075160406530038, + 0.18431639028061755, + 0.19822374395088735, + 0.21246554546396193, + 0.22703423311474744, + 0.2419227398218076, + 0.2571244423309797, + 0.2726331166884073, + 0.28844289950343405, + 0.3045482549241785, + 0.3209439450398661, + 0.337625003990658, + 0.3545867159714857, + 0.37182459450599725, + 0.38933436477854594, + 0.4071119476497462, + 0.42520131159528496 + ] + }, + { + "mode": "lines", + "name": "Minkowski 2.0", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.053835830058639615, + 0.05111067206222717, + 0.04845554165878352, + 0.04587051804433099, + 0.043355678096208675, + 0.04091109644413583, + 0.038536845477359936, + 0.03623299543973158, + 0.03399961440367254, + 0.031836768282340513, + 0.02974452094548145, + 0.02772293424615578, + 0.02577274436881736, + 0.02389263231281454, + 0.022083354370052568, + 0.020344964563815224, + 0.018677515064371243, + 0.01708105629952934, + 0.015555636789546832, + 0.014101303317362665, + 0.012718100980349315, + 0.011406073186761897, + 0.010165261722848786, + 0.00899570675469101, + 0.007897446886200334, + 0.00687051919031046, + 0.005914959195754908, + 0.005030800981722024, + 0.004218077198061601, + 0.003476819103455494, + 0.002807056595014089, + 0.002210166875264619, + 0.001683316235636507, + 0.0012280406319022843, + 0.0008443648780650153, + 0.0005323126127996372, + 0.00029190631272314684, + 0.00012316735767345152, + 0.00002611605104018711, + 7.716524040349468e-7, + 0.000047152387593465094, + 0.00016527552304902867, + 0.00035515740004700826, + 0.000616813450391432, + 0.0009502582309038722, + 0.0013555054549569752, + 0.0018325680271045303, + 0.0023814580649710623, + 0.0030021869333247487, + 0.003694765325842557, + 0.004459203215392723, + 0.005295511891398787, + 0.006203696238833047, + 0.007183766159368478, + 0.008235729239251834, + 0.009359592477131223, + 0.01055536229286567, + 0.011823044842784848, + 0.01316264573471436, + 0.014574170163856032, + 0.016057622954143685, + 0.017613008498385842, + 0.01924033104259407, + 0.020939594432015526, + 0.022710802251721743, + 0.024553957861849968, + 0.026469064398176645, + 0.028456124854723906, + 0.030515142072100664, + 0.03264611873917076, + 0.03484905751204218, + 0.03712396095868825, + 0.039470831615045304, + 0.041889672012050114, + 0.04438742821038762 + ] + }, + { + "mode": "lines", + "name": "Minkowski 2.5", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.00599191711322915, + 0.005614837670928145, + 0.005252284249011823, + 0.004904075622407746, + 0.004570027202705069, + 0.004249950947933269, + 0.00394365525549855, + 0.003650944854239165, + 0.0033716206762091096, + 0.0031054797184844386, + 0.0028523149064425403, + 0.0026119149314448435, + 0.0023841431604546513, + 0.00216861683016084, + 0.0019651941880159075, + 0.0017736450223797967, + 0.0015937338008630531, + 0.001425219385464411, + 0.0012678546837323456, + 0.00112138629701775, + 0.000985554108220534, + 0.0008600908095216627, + 0.000744721370082053, + 0.000639162421840049, + 0.0005431215539675338, + 0.000456296488362328, + 0.0003783741080510552, + 0.0003090293129011716, + 0.0002479236363165249, + 0.0001947035604222295, + 0.00014899842654114015, + 0.00011050526532029358, + 0.00007861978330882363, + 0.00005300466356442118, + 0.00003318447420002948, + 0.000018640426418369816, + 0.000008795876385119417, + 0.000002991076752446444, + 4.3038274298390353e-7, + 5.270445915798675e-9, + 9.003979808586074e-7, + 0.00000431822910461567, + 0.00001123441928794573, + 0.00002239717290011221, + 0.00003843952236986854, + 0.000059920699355292375, + 0.00008734728377438764, + 0.00012118579674321327, + 0.00016187093273051624, + 0.00020981129900874071, + 0.00026539359878060637, + 0.0003289859601570397, + 0.0004009397926044921, + 0.00048159264211112494, + 0.0005712692214568628, + 0.0006702828578571387, + 0.0007789365945448055, + 0.0008975241532613164, + 0.0010263307066110709, + 0.0011656335801405735, + 0.0013157028571397229, + 0.0014768018987335674, + 0.0016491878406227278, + 0.0018331119797307927, + 0.002028820158004973, + 0.0022365530986257283, + 0.002456546705593221, + 0.002689032344498569, + 0.002934237088317751, + 0.0031923839427503028, + 0.003463692067521774, + 0.003748376959314471, + 0.004046650633773163, + 0.004358721790863581, + 0.004685740314348392 + ] + }, + { + "mode": "lines", + "name": "Minkowski 3.0", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 0.000672010540180176, + 0.0006215445818068404, + 0.0005736619460083426, + 0.0005282960093288246, + 0.0004853799966339682, + 0.0004448469880838419, + 0.0004066299249077272, + 0.00037066161579741045, + 0.0003368747424623972, + 0.00030520186488625393, + 0.00027557542825203585, + 0.00024792776830816926, + 0.00022219999509044707, + 0.0001983058492398132, + 0.00017618690342889782, + 0.00015577510759168608, + 0.0001370023281469653, + 0.0001198003536566866, + 0.00010410089731507588, + 0.00008983560323679237, + 0.00007693605108916783, + 0.00006533376015998112, + 0.00005496019375471181, + 0.00004574676328347495, + 0.00003762483247739311, + 0.00003052572124348461, + 0.000024380709246332556, + 0.000019121040091082827, + 0.00001467792482333856, + 0.00001098254545219628, + 0.000007966058318153334, + 0.000005565044250157669, + 0.0000036984511879415113, + 0.000002304260043483638, + 0.0000013135598309854595, + 6.57429233952412e-7, + 2.6693928478466636e-7, + 7.3156042185628e-8, + 7.143050228922932e-9, + 3.626976645007786e-11, + 1.7316319699762432e-8, + 1.1362712966036485e-7, + 3.57892330925595e-7, + 8.190271976765434e-7, + 0.0000015659367898134636, + 0.000002667514195906144, + 0.000004192638887524692, + 0.000006210175132169095, + 0.000008788970532540042, + 0.000011997854894291971, + 0.000015905638669212935, + 0.000020581123957144487, + 0.000026093057148931477, + 0.00003251019443402278, + 0.00003990125899286269, + 0.000048334949573913744, + 0.00005787993951271706, + 0.00006860487857774382, + 0.00008057838994092174, + 0.00009386907060682614, + 0.00010854549123096708, + 0.00012467619486164957, + 0.0001423296997960524, + 0.00016157449649387292, + 0.00018247904870118223, + 0.00020511179375659537, + 0.0002295411425083705, + 0.0002558354803237996, + 0.0002840631670606586, + 0.0003142925371663506, + 0.00034659190165649327, + 0.0003810295475825476, + 0.00041767373923645864, + 0.0004565927190358625, + 0.0004979780149430431 + ] + }, + { + "mode": "lines", + "name": "Minkowski 0.75", + "type": "scatter", + "x": [ + 0.0000048, + 0.00000481081081081081, + 0.0000048216216216216215, + 0.000004832432432432432, + 0.000004843243243243243, + 0.000004854054054054054, + 0.000004864864864864865, + 0.000004875675675675675, + 0.000004886486486486487, + 0.000004897297297297297, + 0.0000049081081081081075, + 0.000004918918918918919, + 0.000004929729729729729, + 0.0000049405405405405405, + 0.000004951351351351351, + 0.000004962162162162162, + 0.000004972972972972973, + 0.000004983783783783784, + 0.000004994594594594594, + 0.000005005405405405405, + 0.000005016216216216216, + 0.0000050270270270270265, + 0.000005037837837837838, + 0.000005048648648648648, + 0.0000050594594594594595, + 0.00000507027027027027, + 0.000005081081081081081, + 0.000005091891891891892, + 0.000005102702702702702, + 0.000005113513513513513, + 0.000005124324324324324, + 0.000005135135135135135, + 0.0000051459459459459455, + 0.000005156756756756757, + 0.000005167567567567567, + 0.0000051783783783783785, + 0.000005189189189189189, + 0.000005199999999999999, + 0.000005210810810810811, + 0.000005221621621621621, + 0.000005232432432432432, + 0.000005243243243243243, + 0.000005254054054054054, + 0.0000052648648648648645, + 0.000005275675675675676, + 0.000005286486486486486, + 0.000005297297297297297, + 0.000005308108108108108, + 0.000005318918918918918, + 0.00000532972972972973, + 0.00000534054054054054, + 0.000005351351351351351, + 0.000005362162162162162, + 0.000005372972972972973, + 0.0000053837837837837835, + 0.000005394594594594594, + 0.000005405405405405405, + 0.000005416216216216216, + 0.000005427027027027027, + 0.000005437837837837837, + 0.000005448648648648649, + 0.000005459459459459459, + 0.00000547027027027027, + 0.000005481081081081081, + 0.000005491891891891891, + 0.0000055027027027027025, + 0.000005513513513513513, + 0.000005524324324324324, + 0.000005535135135135135, + 0.000005545945945945946, + 0.000005556756756756756, + 0.000005567567567567568, + 0.000005578378378378378, + 0.0000055891891891891885, + 0.0000056 + ], + "y": [ + 14.053907139889564, + 13.783560230681127, + 13.511317404822716, + 13.23711783683918, + 12.960896795149775, + 12.6825852790147, + 12.402109604164844, + 12.119390954019869, + 11.834344837355497, + 11.546880490199324, + 11.256900200611677, + 10.964298522695218, + 10.669056120745427, + 10.370860722857191, + 10.069671396423686, + 9.765341090227512, + 9.457708710972156, + 9.146597129110498, + 8.831810743741293, + 8.513132608461637, + 8.190320916472238, + 7.863104663987423, + 7.531178330432244, + 7.194195120393091, + 6.851758434188353, + 6.503410859604571, + 6.148619715895783, + 5.7867577946097875, + 5.417077056994704, + 5.038671864026152, + 4.650426025432307, + 4.251758702202616, + 3.83923718720681, + 3.4112257453288364, + 2.9643277220935245, + 2.493501134595607, + 1.9905754396139557, + 1.440296696668529, + 0.805019078553833, + 0.2152541425047069, + 1.0053403361036288, + 1.6090158746426986, + 2.1436294001093845, + 2.636737894339122, + 3.1007767806321063, + 3.5427469893844843, + 3.96709806415327, + 4.376887045484544, + 4.774332146317502, + 5.161109525341621, + 5.53852601379768, + 5.907626989303442, + 6.269263264771393, + 6.62414278374316, + 6.972860865942185, + 7.315924806338328, + 7.653771503402037, + 7.986780813406781, + 8.315285650897213, + 8.639579948895134, + 8.959924925914986, + 9.276554082835698, + 9.58967729429178, + 9.89948407028009, + 10.20614631068782, + 10.5098205742672, + 10.81064997659674, + 11.108765803754077, + 11.40428886956552, + 11.69733067827326, + 11.987994436946138, + 12.27637591081336, + 12.562564177930856, + 12.846642283706704, + 13.129354840765275 + ] + } + ], + "layout": { + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for k, _ in enumerate(p_orders):\n", + " fig.add_trace(\n", + " go.Scatter(x=x_range, y=y_minkowski[k], mode=\"lines\", name=f\"Minkowski {_}\")\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As seen above, the Minkowski cost function allows for a variety of different distance metrics to be created. This provides users with another hyper parameter for to calibrate for optimisation algorithm convergence. This addition does expand the global search space, and should be carefully considered before deciding upon.\n", + "\n", + "In this notebook, we've shown the different distance metrics (cost functions) offered in PyBOP. Selection between these functions can effect the identified parameters in the case that the optimiser hyperparameter values are not properly calibrated. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pybop-3.12", + "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": 2 +} diff --git a/pybop/parameters/parameter.py b/pybop/parameters/parameter.py index 76754847..ea814c90 100644 --- a/pybop/parameters/parameter.py +++ b/pybop/parameters/parameter.py @@ -267,7 +267,7 @@ def update(self, values): for i, param in enumerate(self.param.values()): param.update(value=values[i]) - def rvs(self, n_samples: int) -> List: + def rvs(self, n_samples: int = 1) -> List: """ Draw random samples from each parameter's prior distribution. @@ -298,7 +298,7 @@ def rvs(self, n_samples: int) -> List: all_samples.append(samples) - return all_samples + return np.concatenate(all_samples) def get_sigma0(self) -> List: """ From 294347a0e688600c1513c308de27dc368232e549 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 1 Jul 2024 16:59:39 +0100 Subject: [PATCH 13/41] fix: default renderer for github display, updt ploting range --- .../notebooks/comparing_cost_functions.ipynb | 4116 +---------------- 1 file changed, 31 insertions(+), 4085 deletions(-) diff --git a/examples/notebooks/comparing_cost_functions.ipynb b/examples/notebooks/comparing_cost_functions.ipynb index 52666d98..05ff5c16 100644 --- a/examples/notebooks/comparing_cost_functions.ipynb +++ b/examples/notebooks/comparing_cost_functions.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 149, "metadata": {}, "outputs": [ { @@ -52,7 +52,9 @@ "\n", "import numpy as np\n", "import plotly.graph_objects as go\n", + "import plotly.io as pio\n", "\n", + "pio.renderers.default = \"svg\"\n", "import pybop" ] }, @@ -65,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 150, "metadata": {}, "outputs": [], "source": [ @@ -92,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 151, "metadata": {}, "outputs": [], "source": [ @@ -109,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 152, "metadata": {}, "outputs": [], "source": [ @@ -126,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 153, "metadata": {}, "outputs": [], "source": [ @@ -148,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 154, "metadata": {}, "outputs": [], "source": [ @@ -166,7 +168,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 155, "metadata": {}, "outputs": [], "source": [ @@ -183,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 156, "metadata": {}, "outputs": [ { @@ -192,7 +194,7 @@ "1.2690291451182834e-09" ] }, - "execution_count": 87, + "execution_count": 156, "metadata": {}, "output_type": "execute_result" } @@ -210,7 +212,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 157, "metadata": {}, "outputs": [ { @@ -226,7 +228,7 @@ "1.2690291451182834e-09" ] }, - "execution_count": 88, + "execution_count": 157, "metadata": {}, "output_type": "execute_result" } @@ -245,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 158, "metadata": {}, "outputs": [ { @@ -261,7 +263,7 @@ "0.014466013735507651" ] }, - "execution_count": 89, + "execution_count": 158, "metadata": {}, "output_type": "execute_result" } @@ -283,1161 +285,21 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 159, "metadata": {}, "outputs": [ { "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "mode": "lines", - "name": "SSE", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.053835830058639615, - 0.05111067206222717, - 0.04845554165878352, - 0.04587051804433099, - 0.043355678096208675, - 0.04091109644413583, - 0.038536845477359936, - 0.03623299543973158, - 0.03399961440367254, - 0.031836768282340513, - 0.02974452094548145, - 0.02772293424615578, - 0.02577274436881736, - 0.02389263231281454, - 0.022083354370052568, - 0.020344964563815224, - 0.018677515064371243, - 0.01708105629952934, - 0.015555636789546832, - 0.014101303317362665, - 0.012718100980349315, - 0.011406073186761897, - 0.010165261722848786, - 0.00899570675469101, - 0.007897446886200334, - 0.00687051919031046, - 0.005914959195754908, - 0.005030800981722024, - 0.004218077198061601, - 0.003476819103455494, - 0.002807056595014089, - 0.002210166875264619, - 0.001683316235636507, - 0.0012280406319022843, - 0.0008443648780650153, - 0.0005323126127996372, - 0.00029190631272314684, - 0.00012316735767345152, - 0.00002611605104018711, - 7.716524040349468e-7, - 0.000047152387593465094, - 0.00016527552304902867, - 0.00035515740004700826, - 0.000616813450391432, - 0.0009502582309038722, - 0.0013555054549569752, - 0.0018325680271045303, - 0.0023814580649710623, - 0.0030021869333247487, - 0.003694765325842557, - 0.004459203215392723, - 0.005295511891398787, - 0.006203696238833047, - 0.007183766159368478, - 0.008235729239251834, - 0.009359592477131223, - 0.01055536229286567, - 0.011823044842784848, - 0.01316264573471436, - 0.014574170163856032, - 0.016057622954143685, - 0.017613008498385842, - 0.01924033104259407, - 0.020939594432015526, - 0.022710802251721743, - 0.024553957861849968, - 0.026469064398176645, - 0.028456124854723906, - 0.030515142072100664, - 0.03264611873917076, - 0.03484905751204218, - 0.03712396095868825, - 0.039470831615045304, - 0.041889672012050114, - 0.04438742821038762 - ] - }, - { - "mode": "lines", - "name": "RMSE", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.01093778670061215, - 0.010657357611030684, - 0.010376848339643564, - 0.0100962609189223, - 0.009815597349367391, - 0.009534859603247191, - 0.009254049621335149, - 0.008973169319914116, - 0.008692220583649948, - 0.008411205262074912, - 0.008130125179503895, - 0.007848982134456216, - 0.007567877196680624, - 0.007286613649214647, - 0.007005292343816855, - 0.006723914958120293, - 0.006442483141765766, - 0.00616099853009583, - 0.005879462709682622, - 0.005597877240002611, - 0.005316243657226264, - 0.005034563467065971, - 0.0047528381515910606, - 0.00447106916238934, - 0.00418925792585442, - 0.003907405843128166, - 0.003625514276395038, - 0.003343584564080899, - 0.003061618017418459, - 0.002779615922091673, - 0.0024975795371670853, - 0.0022161863516032415, - 0.0019340896426636757, - 0.0016519622217245394, - 0.001369805240061533, - 0.0010876198404463425, - 0.0008054071609691128, - 0.0005231684616587058, - 0.000240905933879167, - 0.00004140993987049075, - 0.000323702152512203, - 0.0006060354280971968, - 0.0008883910550940557, - 0.0011707675074178496, - 0.0014531637752036625, - 0.0017355789651723812, - 0.002018012238210072, - 0.0023004627867603132, - 0.0025829298322833784, - 0.0028654126427067115, - 0.003147910494065944, - 0.0034304233270995017, - 0.003712949183849843, - 0.003995488055118751, - 0.004278039332646527, - 0.004560602415649159, - 0.004843176710673749, - 0.005125761697930008, - 0.0054083568535111285, - 0.005690961676954868, - 0.005973575693398699, - 0.006256198437174278, - 0.006538829498141437, - 0.006821468468822942, - 0.00710411496586806, - 0.007386768630741797, - 0.007669429125238671, - 0.007952096139414912, - 0.008234769385167406, - 0.008517448592829345, - 0.00880013352323477, - 0.009082823956199262, - 0.009365519694311956, - 0.00964822056271404, - 0.009931703245486995 - ] - } - ], - "layout": { - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - } - } - } + "image/svg+xml": [ + "4.8μ5.2μ5.4μ5.6μ00.010.020.030.040.050.060.070.08SSERMSE" + ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ - "x_range = np.linspace(4.8e-06, 5.6e-06, 75)\n", + "x_range = np.linspace(4.72e-06, 5.72e-06, 75)\n", "y_SSE = []\n", "y_RMSE = []\n", "for i in x_range:\n", @@ -1475,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 160, "metadata": {}, "outputs": [], "source": [ @@ -1484,1154 +346,14 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 161, "metadata": {}, "outputs": [ { "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "mode": "lines", - "name": "SSE", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.053835830058639615, - 0.05111067206222717, - 0.04845554165878352, - 0.04587051804433099, - 0.043355678096208675, - 0.04091109644413583, - 0.038536845477359936, - 0.03623299543973158, - 0.03399961440367254, - 0.031836768282340513, - 0.02974452094548145, - 0.02772293424615578, - 0.02577274436881736, - 0.02389263231281454, - 0.022083354370052568, - 0.020344964563815224, - 0.018677515064371243, - 0.01708105629952934, - 0.015555636789546832, - 0.014101303317362665, - 0.012718100980349315, - 0.011406073186761897, - 0.010165261722848786, - 0.00899570675469101, - 0.007897446886200334, - 0.00687051919031046, - 0.005914959195754908, - 0.005030800981722024, - 0.004218077198061601, - 0.003476819103455494, - 0.002807056595014089, - 0.002210166875264619, - 0.001683316235636507, - 0.0012280406319022843, - 0.0008443648780650153, - 0.0005323126127996372, - 0.00029190631272314684, - 0.00012316735767345152, - 0.00002611605104018711, - 7.716524040349468e-7, - 0.000047152387593465094, - 0.00016527552304902867, - 0.00035515740004700826, - 0.000616813450391432, - 0.0009502582309038722, - 0.0013555054549569752, - 0.0018325680271045303, - 0.0023814580649710623, - 0.0030021869333247487, - 0.003694765325842557, - 0.004459203215392723, - 0.005295511891398787, - 0.006203696238833047, - 0.007183766159368478, - 0.008235729239251834, - 0.009359592477131223, - 0.01055536229286567, - 0.011823044842784848, - 0.01316264573471436, - 0.014574170163856032, - 0.016057622954143685, - 0.017613008498385842, - 0.01924033104259407, - 0.020939594432015526, - 0.022710802251721743, - 0.024553957861849968, - 0.026469064398176645, - 0.028456124854723906, - 0.030515142072100664, - 0.03264611873917076, - 0.03484905751204218, - 0.03712396095868825, - 0.039470831615045304, - 0.041889672012050114, - 0.04438742821038762 - ] - }, - { - "mode": "lines", - "name": "Minkowski", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.053835830058639615, - 0.05111067206222717, - 0.04845554165878352, - 0.04587051804433099, - 0.043355678096208675, - 0.04091109644413583, - 0.038536845477359936, - 0.03623299543973158, - 0.03399961440367254, - 0.031836768282340513, - 0.02974452094548145, - 0.02772293424615578, - 0.02577274436881736, - 0.02389263231281454, - 0.022083354370052568, - 0.020344964563815224, - 0.018677515064371243, - 0.01708105629952934, - 0.015555636789546832, - 0.014101303317362665, - 0.012718100980349315, - 0.011406073186761897, - 0.010165261722848786, - 0.00899570675469101, - 0.007897446886200334, - 0.00687051919031046, - 0.005914959195754908, - 0.005030800981722024, - 0.004218077198061601, - 0.003476819103455494, - 0.002807056595014089, - 0.002210166875264619, - 0.001683316235636507, - 0.0012280406319022843, - 0.0008443648780650153, - 0.0005323126127996372, - 0.00029190631272314684, - 0.00012316735767345152, - 0.00002611605104018711, - 7.716524040349468e-7, - 0.000047152387593465094, - 0.00016527552304902867, - 0.00035515740004700826, - 0.000616813450391432, - 0.0009502582309038722, - 0.0013555054549569752, - 0.0018325680271045303, - 0.0023814580649710623, - 0.0030021869333247487, - 0.003694765325842557, - 0.004459203215392723, - 0.005295511891398787, - 0.006203696238833047, - 0.007183766159368478, - 0.008235729239251834, - 0.009359592477131223, - 0.01055536229286567, - 0.011823044842784848, - 0.01316264573471436, - 0.014574170163856032, - 0.016057622954143685, - 0.017613008498385842, - 0.01924033104259407, - 0.020939594432015526, - 0.022710802251721743, - 0.024553957861849968, - 0.026469064398176645, - 0.028456124854723906, - 0.030515142072100664, - 0.03264611873917076, - 0.03484905751204218, - 0.03712396095868825, - 0.039470831615045304, - 0.041889672012050114, - 0.04438742821038762 - ] - } - ], - "layout": { - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - } - } - } + "image/svg+xml": [ + "4.8μ5.2μ5.4μ5.6μ00.010.020.030.040.050.060.070.08SSEMinkowski" + ] }, "metadata": {}, "output_type": "display_data" @@ -2657,12 +379,12 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 162, "metadata": {}, "outputs": [], "source": [ "y_minkowski = ()\n", - "p_orders = np.append(np.linspace(1, 3, 5), 0.75)\n", + "p_orders = np.append(0.75, np.linspace(1, 3, 5))\n", "for j in p_orders:\n", " minkowski_order = []\n", " cost = pybop.Minkowski(problem, p=j)\n", @@ -2673,1790 +395,14 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": null, "metadata": {}, "outputs": [ { "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "mode": "lines", - "name": "Minkowski 1.0", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 4.552656766704644, - 4.436202925117051, - 4.3197021313635515, - 4.203155156607345, - 4.086562761333203, - 3.969925696234225, - 3.8532447005548, - 3.736520507607969, - 3.619753838775067, - 3.502945403357782, - 3.3860959024243926, - 3.2692060285657063, - 3.152315105913478, - 3.03534657334722, - 2.9183396802589217, - 2.801295078350584, - 2.684213409805027, - 2.567095312655548, - 2.4499414075372727, - 2.3327523058899207, - 2.215528611512171, - 2.098270915375601, - 1.9809798054224617, - 1.8636558566894776, - 1.746299635055733, - 1.628911698808471, - 1.511492592197115, - 1.3940428516001764, - 1.276563003640566, - 1.1590535660401926, - 1.0415150470108951, - 0.9241984469792855, - 0.8066051058958523, - 0.6889841418336626, - 0.5713360225482647, - 0.4536612084843443, - 0.3359601445828524, - 0.21823327001441095, - 0.10048101551366571, - 0.017296186570773475, - 0.135097921585269, - 0.2529237996046896, - 0.3707734312884394, - 0.4886464319121595, - 0.6065424260637728, - 0.7244610470396249, - 0.8424019374031357, - 0.9603647467458134, - 1.0783491329691324, - 1.19635477070232, - 1.3143813351847973, - 1.4324287524120316, - 1.550496227549572, - 1.6685837041158282, - 1.7866908973881013, - 1.9048175251070956, - 2.0229633061020045, - 2.1411279864650505, - 2.2593113100697884, - 2.3775130288520545, - 2.4957329036803886, - 2.613970698878771, - 2.7322261979812748, - 2.8504991844034917, - 2.9687894504882872, - 3.087096797214695, - 3.2054210324010484, - 3.3237619739633995, - 3.442119447297814, - 3.56049328402628, - 3.678883326505933, - 3.7972894235888703, - 3.9157114320354554, - 4.0341492162964325, - 4.152895980262809 - ] - }, - { - "mode": "lines", - "name": "Minkowski 1.5", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.4898109531501431, - 0.4711163397275865, - 0.45265948491854513, - 0.43444404722086893, - 0.4164738187122092, - 0.3987527352296719, - 0.3812848871293262, - 0.36407453205727613, - 0.34712610776420716, - 0.33044424698020214, - 0.3140337947271509, - 0.29789982699749845, - 0.28205309391276184, - 0.2664882597848408, - 0.25121674427648943, - 0.2362447863684241, - 0.22157899356130947, - 0.20722638305226135, - 0.19319442692445904, - 0.17949110797000248, - 0.1661249841016531, - 0.15310526393728813, - 0.1404418981327189, - 0.1281456881391741, - 0.1162284196891101, - 0.1047030269938625, - 0.09358379719055644, - 0.08288663103488439, - 0.07262937839742101, - 0.06283228046628397, - 0.05351856620452157, - 0.04473477504117415, - 0.03647281441377362, - 0.028792057135636567, - 0.021740939035673496, - 0.015382332300756214, - 0.009802659840611192, - 0.005132054317706342, - 0.0016035400319770717, - 0.0001143556885559469, - 0.0024985227036004886, - 0.006400451926923809, - 0.01136008053208959, - 0.017186853071309068, - 0.023767253976017148, - 0.03102353718811392, - 0.03889810774601821, - 0.04734606726107551, - 0.05633113355395888, - 0.06582319171032898, - 0.0757967203622376, - 0.08622975311116587, - 0.09710303931895804, - 0.10839963798281269, - 0.12010442290147164, - 0.13220379410484237, - 0.14468543663157718, - 0.15753813249704368, - 0.17075160406530038, - 0.18431639028061755, - 0.19822374395088735, - 0.21246554546396193, - 0.22703423311474744, - 0.2419227398218076, - 0.2571244423309797, - 0.2726331166884073, - 0.28844289950343405, - 0.3045482549241785, - 0.3209439450398661, - 0.337625003990658, - 0.3545867159714857, - 0.37182459450599725, - 0.38933436477854594, - 0.4071119476497462, - 0.42520131159528496 - ] - }, - { - "mode": "lines", - "name": "Minkowski 2.0", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.053835830058639615, - 0.05111067206222717, - 0.04845554165878352, - 0.04587051804433099, - 0.043355678096208675, - 0.04091109644413583, - 0.038536845477359936, - 0.03623299543973158, - 0.03399961440367254, - 0.031836768282340513, - 0.02974452094548145, - 0.02772293424615578, - 0.02577274436881736, - 0.02389263231281454, - 0.022083354370052568, - 0.020344964563815224, - 0.018677515064371243, - 0.01708105629952934, - 0.015555636789546832, - 0.014101303317362665, - 0.012718100980349315, - 0.011406073186761897, - 0.010165261722848786, - 0.00899570675469101, - 0.007897446886200334, - 0.00687051919031046, - 0.005914959195754908, - 0.005030800981722024, - 0.004218077198061601, - 0.003476819103455494, - 0.002807056595014089, - 0.002210166875264619, - 0.001683316235636507, - 0.0012280406319022843, - 0.0008443648780650153, - 0.0005323126127996372, - 0.00029190631272314684, - 0.00012316735767345152, - 0.00002611605104018711, - 7.716524040349468e-7, - 0.000047152387593465094, - 0.00016527552304902867, - 0.00035515740004700826, - 0.000616813450391432, - 0.0009502582309038722, - 0.0013555054549569752, - 0.0018325680271045303, - 0.0023814580649710623, - 0.0030021869333247487, - 0.003694765325842557, - 0.004459203215392723, - 0.005295511891398787, - 0.006203696238833047, - 0.007183766159368478, - 0.008235729239251834, - 0.009359592477131223, - 0.01055536229286567, - 0.011823044842784848, - 0.01316264573471436, - 0.014574170163856032, - 0.016057622954143685, - 0.017613008498385842, - 0.01924033104259407, - 0.020939594432015526, - 0.022710802251721743, - 0.024553957861849968, - 0.026469064398176645, - 0.028456124854723906, - 0.030515142072100664, - 0.03264611873917076, - 0.03484905751204218, - 0.03712396095868825, - 0.039470831615045304, - 0.041889672012050114, - 0.04438742821038762 - ] - }, - { - "mode": "lines", - "name": "Minkowski 2.5", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.00599191711322915, - 0.005614837670928145, - 0.005252284249011823, - 0.004904075622407746, - 0.004570027202705069, - 0.004249950947933269, - 0.00394365525549855, - 0.003650944854239165, - 0.0033716206762091096, - 0.0031054797184844386, - 0.0028523149064425403, - 0.0026119149314448435, - 0.0023841431604546513, - 0.00216861683016084, - 0.0019651941880159075, - 0.0017736450223797967, - 0.0015937338008630531, - 0.001425219385464411, - 0.0012678546837323456, - 0.00112138629701775, - 0.000985554108220534, - 0.0008600908095216627, - 0.000744721370082053, - 0.000639162421840049, - 0.0005431215539675338, - 0.000456296488362328, - 0.0003783741080510552, - 0.0003090293129011716, - 0.0002479236363165249, - 0.0001947035604222295, - 0.00014899842654114015, - 0.00011050526532029358, - 0.00007861978330882363, - 0.00005300466356442118, - 0.00003318447420002948, - 0.000018640426418369816, - 0.000008795876385119417, - 0.000002991076752446444, - 4.3038274298390353e-7, - 5.270445915798675e-9, - 9.003979808586074e-7, - 0.00000431822910461567, - 0.00001123441928794573, - 0.00002239717290011221, - 0.00003843952236986854, - 0.000059920699355292375, - 0.00008734728377438764, - 0.00012118579674321327, - 0.00016187093273051624, - 0.00020981129900874071, - 0.00026539359878060637, - 0.0003289859601570397, - 0.0004009397926044921, - 0.00048159264211112494, - 0.0005712692214568628, - 0.0006702828578571387, - 0.0007789365945448055, - 0.0008975241532613164, - 0.0010263307066110709, - 0.0011656335801405735, - 0.0013157028571397229, - 0.0014768018987335674, - 0.0016491878406227278, - 0.0018331119797307927, - 0.002028820158004973, - 0.0022365530986257283, - 0.002456546705593221, - 0.002689032344498569, - 0.002934237088317751, - 0.0031923839427503028, - 0.003463692067521774, - 0.003748376959314471, - 0.004046650633773163, - 0.004358721790863581, - 0.004685740314348392 - ] - }, - { - "mode": "lines", - "name": "Minkowski 3.0", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 0.000672010540180176, - 0.0006215445818068404, - 0.0005736619460083426, - 0.0005282960093288246, - 0.0004853799966339682, - 0.0004448469880838419, - 0.0004066299249077272, - 0.00037066161579741045, - 0.0003368747424623972, - 0.00030520186488625393, - 0.00027557542825203585, - 0.00024792776830816926, - 0.00022219999509044707, - 0.0001983058492398132, - 0.00017618690342889782, - 0.00015577510759168608, - 0.0001370023281469653, - 0.0001198003536566866, - 0.00010410089731507588, - 0.00008983560323679237, - 0.00007693605108916783, - 0.00006533376015998112, - 0.00005496019375471181, - 0.00004574676328347495, - 0.00003762483247739311, - 0.00003052572124348461, - 0.000024380709246332556, - 0.000019121040091082827, - 0.00001467792482333856, - 0.00001098254545219628, - 0.000007966058318153334, - 0.000005565044250157669, - 0.0000036984511879415113, - 0.000002304260043483638, - 0.0000013135598309854595, - 6.57429233952412e-7, - 2.6693928478466636e-7, - 7.3156042185628e-8, - 7.143050228922932e-9, - 3.626976645007786e-11, - 1.7316319699762432e-8, - 1.1362712966036485e-7, - 3.57892330925595e-7, - 8.190271976765434e-7, - 0.0000015659367898134636, - 0.000002667514195906144, - 0.000004192638887524692, - 0.000006210175132169095, - 0.000008788970532540042, - 0.000011997854894291971, - 0.000015905638669212935, - 0.000020581123957144487, - 0.000026093057148931477, - 0.00003251019443402278, - 0.00003990125899286269, - 0.000048334949573913744, - 0.00005787993951271706, - 0.00006860487857774382, - 0.00008057838994092174, - 0.00009386907060682614, - 0.00010854549123096708, - 0.00012467619486164957, - 0.0001423296997960524, - 0.00016157449649387292, - 0.00018247904870118223, - 0.00020511179375659537, - 0.0002295411425083705, - 0.0002558354803237996, - 0.0002840631670606586, - 0.0003142925371663506, - 0.00034659190165649327, - 0.0003810295475825476, - 0.00041767373923645864, - 0.0004565927190358625, - 0.0004979780149430431 - ] - }, - { - "mode": "lines", - "name": "Minkowski 0.75", - "type": "scatter", - "x": [ - 0.0000048, - 0.00000481081081081081, - 0.0000048216216216216215, - 0.000004832432432432432, - 0.000004843243243243243, - 0.000004854054054054054, - 0.000004864864864864865, - 0.000004875675675675675, - 0.000004886486486486487, - 0.000004897297297297297, - 0.0000049081081081081075, - 0.000004918918918918919, - 0.000004929729729729729, - 0.0000049405405405405405, - 0.000004951351351351351, - 0.000004962162162162162, - 0.000004972972972972973, - 0.000004983783783783784, - 0.000004994594594594594, - 0.000005005405405405405, - 0.000005016216216216216, - 0.0000050270270270270265, - 0.000005037837837837838, - 0.000005048648648648648, - 0.0000050594594594594595, - 0.00000507027027027027, - 0.000005081081081081081, - 0.000005091891891891892, - 0.000005102702702702702, - 0.000005113513513513513, - 0.000005124324324324324, - 0.000005135135135135135, - 0.0000051459459459459455, - 0.000005156756756756757, - 0.000005167567567567567, - 0.0000051783783783783785, - 0.000005189189189189189, - 0.000005199999999999999, - 0.000005210810810810811, - 0.000005221621621621621, - 0.000005232432432432432, - 0.000005243243243243243, - 0.000005254054054054054, - 0.0000052648648648648645, - 0.000005275675675675676, - 0.000005286486486486486, - 0.000005297297297297297, - 0.000005308108108108108, - 0.000005318918918918918, - 0.00000532972972972973, - 0.00000534054054054054, - 0.000005351351351351351, - 0.000005362162162162162, - 0.000005372972972972973, - 0.0000053837837837837835, - 0.000005394594594594594, - 0.000005405405405405405, - 0.000005416216216216216, - 0.000005427027027027027, - 0.000005437837837837837, - 0.000005448648648648649, - 0.000005459459459459459, - 0.00000547027027027027, - 0.000005481081081081081, - 0.000005491891891891891, - 0.0000055027027027027025, - 0.000005513513513513513, - 0.000005524324324324324, - 0.000005535135135135135, - 0.000005545945945945946, - 0.000005556756756756756, - 0.000005567567567567568, - 0.000005578378378378378, - 0.0000055891891891891885, - 0.0000056 - ], - "y": [ - 14.053907139889564, - 13.783560230681127, - 13.511317404822716, - 13.23711783683918, - 12.960896795149775, - 12.6825852790147, - 12.402109604164844, - 12.119390954019869, - 11.834344837355497, - 11.546880490199324, - 11.256900200611677, - 10.964298522695218, - 10.669056120745427, - 10.370860722857191, - 10.069671396423686, - 9.765341090227512, - 9.457708710972156, - 9.146597129110498, - 8.831810743741293, - 8.513132608461637, - 8.190320916472238, - 7.863104663987423, - 7.531178330432244, - 7.194195120393091, - 6.851758434188353, - 6.503410859604571, - 6.148619715895783, - 5.7867577946097875, - 5.417077056994704, - 5.038671864026152, - 4.650426025432307, - 4.251758702202616, - 3.83923718720681, - 3.4112257453288364, - 2.9643277220935245, - 2.493501134595607, - 1.9905754396139557, - 1.440296696668529, - 0.805019078553833, - 0.2152541425047069, - 1.0053403361036288, - 1.6090158746426986, - 2.1436294001093845, - 2.636737894339122, - 3.1007767806321063, - 3.5427469893844843, - 3.96709806415327, - 4.376887045484544, - 4.774332146317502, - 5.161109525341621, - 5.53852601379768, - 5.907626989303442, - 6.269263264771393, - 6.62414278374316, - 6.972860865942185, - 7.315924806338328, - 7.653771503402037, - 7.986780813406781, - 8.315285650897213, - 8.639579948895134, - 8.959924925914986, - 9.276554082835698, - 9.58967729429178, - 9.89948407028009, - 10.20614631068782, - 10.5098205742672, - 10.81064997659674, - 11.108765803754077, - 11.40428886956552, - 11.69733067827326, - 11.987994436946138, - 12.27637591081336, - 12.562564177930856, - 12.846642283706704, - 13.129354840765275 - ] - } - ], - "layout": { - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - } - } - } + "image/svg+xml": [ + "5.2μ5.4μ5.6μ02468101214Minkowski 0.75Minkowski 1.0Minkowski 1.5Minkowski 2.0Minkowski 2.5Minkowski 3.0" + ] }, "metadata": {}, "output_type": "display_data" From 5b6f42cdb9dd955c516e3cb8a03e7f17d4e0a5d1 Mon Sep 17 00:00:00 2001 From: Brady Planden <55357039+BradyPlanden@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:13:17 +0100 Subject: [PATCH 14/41] Apply suggestions from code review --- examples/scripts/spm_AdamW.py | 1 - examples/scripts/spm_CMAES.py | 3 +-- pybop/costs/fitting_costs.py | 5 +---- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/scripts/spm_AdamW.py b/examples/scripts/spm_AdamW.py index a4a34479..4eff3dc1 100644 --- a/examples/scripts/spm_AdamW.py +++ b/examples/scripts/spm_AdamW.py @@ -53,7 +53,6 @@ def noise(sigma): problem = pybop.FittingProblem( model, parameters, dataset, signal=signal, init_soc=init_soc ) -# cost = pybop.RootMeanSquaredError(problem) cost = pybop.Minkowski(problem, p=2) optim = pybop.AdamW( cost, diff --git a/examples/scripts/spm_CMAES.py b/examples/scripts/spm_CMAES.py index 2a89b5d6..1fc051cc 100644 --- a/examples/scripts/spm_CMAES.py +++ b/examples/scripts/spm_CMAES.py @@ -41,8 +41,7 @@ signal = ["Voltage [V]", "Bulk open-circuit voltage [V]"] # Generate problem, cost function, and optimisation class problem = pybop.FittingProblem(model, parameters, dataset, signal=signal) -# cost = pybop.SumSquaredError(problem) -cost = pybop.Minkowski(problem, p=2) +cost = pybop.SumSquaredError(problem) optim = pybop.CMAES(cost, max_iterations=100) # Run the optimisation diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index 55f1d629..272a497a 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -175,7 +175,7 @@ class Minkowski(BaseCost): including Euclidean and Manhattan distances. It is defined as: .. math:: - L_p(x, y) = (\sum_i |x_i - y_i|^p)^{1/p} + L_p(x, y) = (\sum_i |x_i - y_i|^p) where p ≥ 1 is the order of the Minkowski metric. @@ -191,9 +191,6 @@ class Minkowski(BaseCost): Attributes: p (float): The order of the Minkowski metric. - Note: - The cost calculation does not include the p-th root as it doesn't - affect the optimisation process and saves computational effort. """ def __init__(self, problem, p: float = 2.0): From 1b9310113456d0cbd2b548f2ba6683b7ec831ce4 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 1 Jul 2024 17:14:47 +0100 Subject: [PATCH 15/41] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93752e1d..04d34207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Features +- [#393](https://github.com/pybop-team/PyBOP/pull/383) - Adds Minkowski cost class, with example and 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. From 43cfcb595a829229bb55a00104c5a28b9323e683 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Tue, 2 Jul 2024 14:40:21 +0100 Subject: [PATCH 16/41] tests: Fix incorrect sigma0 values for likelihood based spm fitting --- tests/integration/test_spm_parameterisations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_spm_parameterisations.py b/tests/integration/test_spm_parameterisations.py index 7eeb0b7c..98509769 100644 --- a/tests/integration/test_spm_parameterisations.py +++ b/tests/integration/test_spm_parameterisations.py @@ -69,10 +69,10 @@ def spm_costs(self, model, parameters, cost_class, init_soc): # Define the cost to optimise problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc) if cost_class in [pybop.GaussianLogLikelihoodKnownSigma]: - return cost_class(problem, sigma=[0.03, 0.03]) + return cost_class(problem, sigma=0.002) elif cost_class in [pybop.MAP]: return cost_class( - problem, pybop.GaussianLogLikelihoodKnownSigma, sigma=[0.03, 0.03] + problem, pybop.GaussianLogLikelihoodKnownSigma, sigma=0.002 ) else: return cost_class(problem) From f146c991e72bd35193eae75017aa00f5bccfcd66 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Wed, 3 Jul 2024 11:26:56 +0100 Subject: [PATCH 17/41] fix: add explicit tests on scipy_minimise cost wrapper for cost==np.inf --- pybop/optimisers/scipy_optimisers.py | 31 +++++++++++++--------------- tests/unit/test_optimisation.py | 8 +++++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pybop/optimisers/scipy_optimisers.py b/pybop/optimisers/scipy_optimisers.py index 84342304..dc2c2248 100644 --- a/pybop/optimisers/scipy_optimisers.py +++ b/pybop/optimisers/scipy_optimisers.py @@ -141,6 +141,19 @@ def _set_up_optimiser(self): # Nest this option within an options dictionary for SciPy minimize self._options["options"]["maxiter"] = self.unset_options.pop(key) + def cost_wrapper(self, x): + self.log["x"].append([x]) + + if not self._options["jac"]: + cost = self.cost(x) / self._cost0 + if np.isinf(cost): + self.inf_count += 1 + cost = 1 + 0.9**self.inf_count # for fake finite gradient + return cost if self.minimising else -cost + + L, dl = self.cost.evaluateS1(x) + return (L, dl) if self.minimising else (-L, -dl) + def _run_optimiser(self): """ Executes the optimisation process using SciPy's minimize function. @@ -174,24 +187,8 @@ def callback(intermediate_result: OptimizeResult): # Scale the cost function, preserving the sign convention, and eliminate nan values self.inf_count = 0 - if not self._options["jac"]: - - def cost_wrapper(x): - self.log["x"].append([x]) - cost = self.cost(x) / self._cost0 - if np.isinf(cost): - self.inf_count += 1 - cost = 1 + 0.9**self.inf_count # for fake finite gradient - return cost if self.minimising else -cost - elif self._options["jac"] is True: - - def cost_wrapper(x): - self.log["x"].append([x]) - L, dl = self.cost.evaluateS1(x) - return L, dl if self.minimising else -L, -dl - return minimize( - cost_wrapper, + self.cost_wrapper, self.x0, bounds=self._scipy_bounds, callback=callback, diff --git a/tests/unit/test_optimisation.py b/tests/unit/test_optimisation.py index 97fe12fc..0a70adbd 100644 --- a/tests/unit/test_optimisation.py +++ b/tests/unit/test_optimisation.py @@ -335,6 +335,7 @@ def test_prior_sampling(self, cost): [ (0.85, 0.2, False), (0.85, 0.001, True), + (1.0, 0.5, False), ], ) def test_scipy_prior_resampling( @@ -368,6 +369,13 @@ def test_scipy_prior_resampling( else: opt.run() + # Test cost_wrapper inf return + cost = opt.cost_wrapper(np.array([0.9])) + assert cost in ( + pytest.approx(1.729, abs=0.001), + pytest.approx(1.9, abs=0.001), + ) + @pytest.mark.unit def test_halting(self, cost): # Test max evalutions From ff03bd235f81656dd692d826414b1b3e9dc0f11c Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Fri, 5 Jul 2024 12:21:27 +0100 Subject: [PATCH 18/41] fix: update scipy.cost_wrapper==np.inf assert values for scipy_minimize, refactor docstring / inf_count --- pybop/optimisers/scipy_optimisers.py | 7 ++++--- tests/unit/test_optimisation.py | 5 +---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pybop/optimisers/scipy_optimisers.py b/pybop/optimisers/scipy_optimisers.py index 2adc00ae..393a233b 100644 --- a/pybop/optimisers/scipy_optimisers.py +++ b/pybop/optimisers/scipy_optimisers.py @@ -142,6 +142,9 @@ def _set_up_optimiser(self): self._options["options"]["maxiter"] = self.unset_options.pop(key) def cost_wrapper(self, x): + """ + Scale the cost function, preserving the sign convention, and eliminate nan values + """ self.log["x"].append([x]) if not self._options["jac"]: @@ -163,6 +166,7 @@ def _run_optimiser(self): result : scipy.optimize.OptimizeResult The result of the optimisation including the optimised parameter values and cost. """ + self.inf_count = 0 # Add callback storing history of parameter values def callback(intermediate_result: OptimizeResult): @@ -184,9 +188,6 @@ def callback(intermediate_result: OptimizeResult): "The initial parameter values return an infinite cost." ) - # Scale the cost function, preserving the sign convention, and eliminate nan values - self.inf_count = 0 - return minimize( self.cost_wrapper, self.x0, diff --git a/tests/unit/test_optimisation.py b/tests/unit/test_optimisation.py index 4b534065..8243fd54 100644 --- a/tests/unit/test_optimisation.py +++ b/tests/unit/test_optimisation.py @@ -374,10 +374,7 @@ def test_scipy_prior_resampling( # Test cost_wrapper inf return cost = opt.cost_wrapper(np.array([0.9])) - assert cost in ( - pytest.approx(1.729, abs=0.001), - pytest.approx(1.9, abs=0.001), - ) + assert cost in [1.729, 1.81, 1.9] @pytest.mark.unit def test_halting(self, cost): From cea297fe31bda39a47f4f1665cf4a10219876293 Mon Sep 17 00:00:00 2001 From: Brady Planden <55357039+BradyPlanden@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:59:47 +0100 Subject: [PATCH 19/41] Apply suggestions from code review --- pybop/costs/_likelihoods.py | 2 +- pybop/costs/fitting_costs.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index de4a2441..d37fa2a5 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -94,7 +94,7 @@ def _evaluateS1(self, inputs: Inputs, grad=None): if not self.verify_prediction(y): dl = self._de * np.ones(self.n_parameters) - return -np.inf, dl + return -np.inf, -dl r = np.asarray([self._target[signal] - y[signal] for signal in self.signal]) likelihood = self._evaluate(inputs) diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index 58dfbe61..8614c496 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -371,7 +371,7 @@ def _evaluate(self, inputs: Inputs, grad=None): """ log_likelihood = self.likelihood._evaluate(inputs) if not np.isfinite(log_likelihood): - return -np.inf + return log_likelihood log_prior = sum( self.parameters[key].prior.logpdf(value) for key, value in inputs.items() @@ -403,7 +403,7 @@ def _evaluateS1(self, inputs: Inputs): """ log_likelihood, dl = self.likelihood._evaluateS1(inputs) if not np.isfinite(log_likelihood): - return -np.inf, -dl + return log_likelihood, dl log_prior = sum( self.parameters[key].prior.logpdf(inputs[key]) for key in inputs.keys() From 60acebc095af3c1cc6938937f8d104af5638ccbf Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Tue, 9 Jul 2024 09:41:56 +0100 Subject: [PATCH 20/41] fix: non-finite gradient values --- pybop/costs/_likelihoods.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index ca364c58..5cbbaac8 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -71,8 +71,7 @@ def _evaluateS1(self, inputs: Inputs) -> Tuple[float, np.ndarray]: y, dy = self.problem.evaluateS1(inputs) if not self.verify_prediction(y): - dl = self._de * np.ones(self.n_parameters) - return -np.inf, -dl + return -np.inf, -self._de * np.ones(self.n_parameters) likelihood = self._evaluate(inputs) @@ -126,7 +125,7 @@ def __init__( self.sigma = Parameters() self._add_sigma_parameters(sigma0) self.parameters.join(self.sigma) - self._dl = np.ones(self.n_parameters) + self._dl = self._de * np.ones(self.n_parameters) def _add_sigma_parameters(self, sigma0): sigma0 = [sigma0] if not isinstance(sigma0, List) else sigma0 @@ -240,8 +239,7 @@ def _evaluateS1(self, inputs: Inputs) -> Tuple[float, np.ndarray]: y, dy = self.problem.evaluateS1(self.problem.parameters.as_dict()) if not self.verify_prediction(y): - dl = self._dl * np.ones(self.n_parameters) - return -np.inf, -dl + return -np.inf, -self._dl likelihood = self._evaluate(inputs) @@ -341,7 +339,7 @@ def _evaluateS1(self, inputs: Inputs) -> Tuple[float, np.ndarray]: self.parameters[key].prior.logpdf(value) for key, value in inputs.items() ) if not np.isfinite(log_prior).any(): - return log_prior, self._de + return log_prior, -self._de * np.ones(self.n_parameters) log_likelihood, dl = self.likelihood._evaluateS1(inputs) From acd135e8b79fb5b37182faaf67890c8945202642 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Tue, 9 Jul 2024 10:10:33 +0100 Subject: [PATCH 21/41] fix: revert to e.item() as float(e) is depreciated, refactor spm_parameterisation's max_unchanged_iterations setting --- pybop/costs/_likelihoods.py | 4 ++-- pybop/costs/fitting_costs.py | 6 +++--- tests/integration/test_spm_parameterisations.py | 8 +++----- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index 5cbbaac8..dba1f1c5 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -62,7 +62,7 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo ] ) - return float(e) if self.n_outputs == 1 else np.sum(e) + return e.item() if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, inputs: Inputs) -> Tuple[float, np.ndarray]: """ @@ -215,7 +215,7 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo ] ) - return float(e) if self.n_outputs == 1 else np.sum(e) + return e.item() if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, inputs: Inputs) -> Tuple[float, np.ndarray]: """ diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index 91c2ed2b..869bff01 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -50,7 +50,7 @@ def _evaluate(self, inputs: Inputs, grad=None): ] ) - return float(e) if self.n_outputs == 1 else np.sum(e) + return e.item() if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, inputs: Inputs): """ @@ -136,7 +136,7 @@ def _evaluate(self, inputs: Inputs, grad=None): ] ) - return float(e) if self.n_outputs == 1 else np.sum(e) + return e.item() if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, inputs: Inputs): """ @@ -226,7 +226,7 @@ def _evaluate(self, inputs: Inputs, grad=None): ] ) - return float(e) if self.n_outputs == 1 else np.sum(e) + return e.item() if self.n_outputs == 1 else np.sum(e) def _evaluateS1(self, inputs): """ diff --git a/tests/integration/test_spm_parameterisations.py b/tests/integration/test_spm_parameterisations.py index d7acaf7d..8429f233 100644 --- a/tests/integration/test_spm_parameterisations.py +++ b/tests/integration/test_spm_parameterisations.py @@ -101,6 +101,7 @@ def test_spm_optimisers(self, optimiser, spm_costs): "cost": spm_costs, "max_iterations": 250, "absolute_tolerance": 1e-6, + "max_unchanged_iterations": 55, } # Add sigma0 to ground truth for GaussianLogLikelihood @@ -117,15 +118,12 @@ def test_spm_optimisers(self, optimiser, spm_costs): sigma0 = 0.05 if isinstance(spm_costs, pybop.MAP) else None optim = optimiser(sigma0=sigma0, **common_args) - # Set max unchanged iterations for BasePintsOptimisers - if issubclass(optimiser, pybop.BasePintsOptimiser): - optim.set_max_unchanged_iterations(iterations=55) - # AdamW will use lowest sigma0 for learning rate, so allow more iterations if issubclass(optimiser, (pybop.AdamW, pybop.IRPropMin)) and isinstance( spm_costs, pybop.GaussianLogLikelihood ): - optim = optimiser(max_unchanged_iterations=75, **common_args) + common_args["max_unchanged_iterations"] = 75 + optim = optimiser(**common_args) initial_cost = optim.cost(x0) x, final_cost = optim.run() From 5f5737a4222a686e0b6184e967a7144894f556e9 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Tue, 9 Jul 2024 12:22:45 +0100 Subject: [PATCH 22/41] fix: remove missed merge items, change isfinite check to return -np.inf --- pybop/costs/_likelihoods.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index dba1f1c5..196a30d3 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -44,10 +44,6 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo Evaluates the Gaussian log-likelihood for the given parameters with known sigma. """ y = self.problem.evaluate(inputs) - if any( - len(y.get(key, [])) != len(self._target.get(key, [])) for key in self.signal - ): - return -np.inf # prediction length doesn't match target if not self.verify_prediction(y): return -np.inf @@ -195,11 +191,6 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo return -np.inf y = self.problem.evaluate(self.problem.parameters.as_dict()) - if any( - len(y.get(key, [])) != len(self._target.get(key, [])) for key in self.signal - ): - return -np.inf # prediction length doesn't match target - if not self.verify_prediction(y): return -np.inf @@ -308,7 +299,7 @@ def _evaluate(self, inputs: Inputs, grad=None) -> float: ) if not np.isfinite(log_prior).any(): - return log_prior + return -np.inf log_likelihood = self.likelihood._evaluate(inputs) posterior = log_likelihood + log_prior @@ -339,7 +330,7 @@ def _evaluateS1(self, inputs: Inputs) -> Tuple[float, np.ndarray]: self.parameters[key].prior.logpdf(value) for key, value in inputs.items() ) if not np.isfinite(log_prior).any(): - return log_prior, -self._de * np.ones(self.n_parameters) + return -np.inf, -self._de * np.ones(self.n_parameters) log_likelihood, dl = self.likelihood._evaluateS1(inputs) From 12a9818bd94c5da760a4fd2d85538d39d312fa73 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Tue, 9 Jul 2024 13:06:27 +0100 Subject: [PATCH 23/41] tests: updt MAP prior lower bound, improve MAP example --- examples/scripts/spm_MAP.py | 45 +++++++++++++------ .../integration/test_spm_parameterisations.py | 2 +- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/examples/scripts/spm_MAP.py b/examples/scripts/spm_MAP.py index f613eccb..40f7a279 100644 --- a/examples/scripts/spm_MAP.py +++ b/examples/scripts/spm_MAP.py @@ -2,12 +2,16 @@ import pybop +# Set variables +sigma = 0.002 +init_soc = 0.7 + # Construct and update initial parameter values parameter_set = pybop.ParameterSet.pybamm("Chen2020") parameter_set.update( { - "Negative electrode active material volume fraction": 0.63, - "Positive electrode active material volume fraction": 0.51, + "Negative electrode active material volume fraction": 0.43, + "Positive electrode active material volume fraction": 0.56, } ) @@ -18,38 +22,51 @@ parameters = pybop.Parameters( pybop.Parameter( "Negative electrode active material volume fraction", - prior=pybop.Gaussian(0.6, 0.05), - bounds=[0.5, 0.8], + prior=pybop.Uniform(0.3, 0.8), + bounds=[0.3, 0.8], + initial_value=0.653, true_value=parameter_set["Negative electrode active material volume fraction"], ), pybop.Parameter( "Positive electrode active material volume fraction", - prior=pybop.Gaussian(0.48, 0.05), + prior=pybop.Uniform(0.3, 0.8), bounds=[0.4, 0.7], + initial_value=0.657, true_value=parameter_set["Positive electrode active material volume fraction"], ), ) -# Generate data -sigma = 0.005 -t_eval = np.arange(0, 900, 3) -values = model.predict(t_eval=t_eval) -corrupt_values = values["Voltage [V]"].data + np.random.normal(0, sigma, len(t_eval)) +# Generate data and corrupt it with noise +experiment = pybop.Experiment( + [ + ( + "Discharge at 0.5C for 3 minutes (4 second period)", + "Charge at 0.5C for 3 minutes (4 second period)", + ), + ] +) +values = model.predict( + init_soc=init_soc, experiment=experiment, inputs=parameters.true_value() +) +corrupt_values = values["Voltage [V]"].data + np.random.normal( + 0, sigma, len(values["Voltage [V]"].data) +) # Form dataset dataset = pybop.Dataset( { - "Time [s]": t_eval, + "Time [s]": values["Time [s]"].data, "Current function [A]": values["Current [A]"].data, "Voltage [V]": corrupt_values, } ) # Generate problem, cost function, and optimisation class -problem = pybop.FittingProblem(model, parameters, dataset) +problem = pybop.FittingProblem(model, parameters, dataset, init_soc=init_soc) cost = pybop.MAP(problem, pybop.GaussianLogLikelihoodKnownSigma, sigma0=sigma) optim = pybop.AdamW( cost, + sigma0=0.05, max_unchanged_iterations=20, min_iterations=20, max_iterations=100, @@ -61,7 +78,7 @@ print("Estimated parameters:", x) # Plot the timeseries output -pybop.quick_plot(problem, problem_inputs=x[0:2], title="Optimised Comparison") +pybop.quick_plot(problem, problem_inputs=x, title="Optimised Comparison") # Plot convergence pybop.plot_convergence(optim) @@ -73,5 +90,5 @@ pybop.plot2d(cost, steps=15) # Plot the cost landscape with optimisation path -bounds = np.asarray([[0.55, 0.77], [0.48, 0.68]]) +bounds = np.asarray([[0.35, 0.7], [0.45, 0.625]]) pybop.plot2d(optim, bounds=bounds, steps=15) diff --git a/tests/integration/test_spm_parameterisations.py b/tests/integration/test_spm_parameterisations.py index 8429f233..d2d8e982 100644 --- a/tests/integration/test_spm_parameterisations.py +++ b/tests/integration/test_spm_parameterisations.py @@ -112,7 +112,7 @@ def test_spm_optimisers(self, optimiser, spm_costs): if isinstance(spm_costs, pybop.MAP): for i in spm_costs.parameters.keys(): spm_costs.parameters[i].prior = pybop.Uniform( - 0.4, 2.0 + 0.2, 2.0 ) # Increase range to avoid prior == np.inf # Set sigma0 and create optimiser sigma0 = 0.05 if isinstance(spm_costs, pybop.MAP) else None From 09be74cbd194ad5d0626588ec7e2cdcdecf8e07a Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Thu, 11 Jul 2024 11:11:02 +0100 Subject: [PATCH 24/41] updt. docstring for sphinx rendering --- pybop/costs/fitting_costs.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index 2e66e677..eaf101c1 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -180,9 +180,10 @@ class Minkowski(BaseCost): where p ≥ 1 is the order of the Minkowski metric. Special cases: - - p = 1: Manhattan distance - - p = 2: Euclidean distance - - p → ∞: Chebyshev distance + + * p = 1: Manhattan distance + * p = 2: Euclidean distance + * p → ∞: Chebyshev distance This class implements the Minkowski distance as a cost function for optimisation problems, allowing for flexible distance-based optimisation From cfe0f31384e6409775cd89abcb5a131c30bc5b74 Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:16:54 +0100 Subject: [PATCH 25/41] Add default n_samples --- pybop/parameters/parameter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pybop/parameters/parameter.py b/pybop/parameters/parameter.py index 4042eb02..7aa3e610 100644 --- a/pybop/parameters/parameter.py +++ b/pybop/parameters/parameter.py @@ -51,7 +51,7 @@ def __init__( self.set_bounds(bounds) self.margin = 1e-4 - def rvs(self, n_samples, random_state=None): + def rvs(self, n_samples: int = 1, random_state=None): """ Draw random samples from the parameter's prior distribution. @@ -61,7 +61,7 @@ def rvs(self, n_samples, random_state=None): Parameters ---------- n_samples : int - The number of samples to draw. + The number of samples to draw (default: 1). Returns ------- @@ -332,7 +332,7 @@ def rvs(self, n_samples: int = 1) -> list: Parameters ---------- n_samples : int - The number of samples to draw. + The number of samples to draw (default: 1). Returns ------- From f5664e169d535ab51740b73f1e6a23b483a78ca1 Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:49:01 +0100 Subject: [PATCH 26/41] Update Minkowski definition --- pybop/costs/fitting_costs.py | 42 ++++++++++++++++++++++++++---------- tests/unit/test_cost.py | 10 ++++----- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index eaf101c1..c4a9b4ff 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -172,34 +172,43 @@ def _evaluateS1(self, inputs: Inputs): class Minkowski(BaseCost): """ The Minkowski distance is a generalisation of several distance metrics, - including Euclidean and Manhattan distances. It is defined as: + including the Euclidean and Manhattan distances. It is defined as: .. math:: - L_p(x, y) = (\\sum_i |x_i - y_i|^p) + L_p(x, y) = ( \\sum_i |x_i - y_i|^p )^(1/p) - where p ≥ 1 is the order of the Minkowski metric. + where p > 0 is the order of the Minkowski distance. For p ≥ 1, the + Minkowski distance is a metric. For 0 < p < 1, it is not a metric, as it + does not satisfy the triangle inequality, although a metric can be + obtained by removing the (1/p) exponent. Special cases: * p = 1: Manhattan distance * p = 2: Euclidean distance - * p → ∞: Chebyshev distance + * p → ∞: Chebyshev distance (not implemented as yet) This class implements the Minkowski distance as a cost function for optimisation problems, allowing for flexible distance-based optimisation across various problem domains. - Attributes: - p (float): The order of the Minkowski metric. + Attributes + ---------- + p : float, optional + The order of the Minkowski distance. """ def __init__(self, problem, p: float = 2.0): super().__init__(problem) if p < 0: raise ValueError( - "The order of the Minkowski metric must be greater than 0." + "The order of the Minkowski distance must be greater than 0." ) - self.p = p + elif not np.isfinite(p): + raise ValueError( + "For p = infinity, an implementation of the Chebyshev distance is required." + ) + self.p = float(p) def _evaluate(self, inputs: Inputs, grad=None): """ @@ -222,6 +231,7 @@ def _evaluate(self, inputs: Inputs, grad=None): e = np.asarray( [ np.sum(np.abs(prediction[signal] - self._target[signal]) ** self.p) + ** (1 / self.p) for signal in self.signal ] ) @@ -253,10 +263,20 @@ def _evaluateS1(self, inputs): return np.inf, self._de * np.ones(self.n_parameters) r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) - e = np.sum(np.sum(np.abs(r) ** self.p)) - de = self.p * np.sum(np.sum(r ** (self.p - 1) * dy.T, axis=2), axis=1) + e = np.asarray( + [ + np.sum(np.abs(y[signal] - self._target[signal]) ** self.p) + ** (1 / self.p) + for signal in self.signal + ] + ) + de = np.sum( + np.sum(r ** (self.p - 1) * dy.T, axis=2) + / (e ** (self.p - 1) + np.finfo(float).eps), + axis=1, + ) - return e, de + return np.sum(e), de class ObserverCost(BaseCost): diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index b4cb86bf..ae9e4f4b 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -72,11 +72,11 @@ def problem(self, model, parameters, dataset, signal, request): @pytest.fixture( params=[ - pybop.RootMeanSquaredError, - pybop.SumSquaredError, + # pybop.RootMeanSquaredError, + # pybop.SumSquaredError, pybop.Minkowski, - pybop.ObserverCost, - pybop.MAP, + # pybop.ObserverCost, + # pybop.MAP, ] ) def cost(self, problem, request): @@ -230,7 +230,7 @@ def test_costs(self, cost): @pytest.mark.unit def test_minkowski(self, problem): # Incorrect order - with pytest.raises(ValueError, match="The order of the Minkowski metric"): + with pytest.raises(ValueError, match="The order of the Minkowski distance"): pybop.Minkowski(problem, p=-1) @pytest.mark.parametrize( From 9e39409601a428a48e5f2bcad59e48a182cbeedb Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:53:31 +0100 Subject: [PATCH 27/41] Update comparing_cost_functions.ipynb --- .../notebooks/comparing_cost_functions.ipynb | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/examples/notebooks/comparing_cost_functions.ipynb b/examples/notebooks/comparing_cost_functions.ipynb index 05ff5c16..3d1d750f 100644 --- a/examples/notebooks/comparing_cost_functions.ipynb +++ b/examples/notebooks/comparing_cost_functions.ipynb @@ -6,7 +6,7 @@ "source": [ "## Investigating different cost functions\n", "\n", - "In this notebook, we take a look at the different cost function offered in PyBOP. Cost functions conventionally construct a distance metric between two mathematics sets (vectors), which is then used within PyBOP's optimisation algorthims. \n", + "In this notebook, we take a look at the different fitting cost functions offered in PyBOP. Cost functions for fitting problems conventionally describe the distance between two points (the target and the prediction) which is to be minimised via PyBOP's optimisation algorithms. \n", "\n", "First, we install and import the required packages below." ] @@ -62,7 +62,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For this notebook, we need to construct parameters, a model and a problem class before we can compare differing cost functions. We start with two parameters, but this is an arbituary selection and can be expanded given the model and data in question." + "For this notebook, we need to construct parameters, a model and a problem class before we can compare differing cost functions. We start with two parameters, but this is an arbitrary selection and can be expanded given the model and data in question." ] }, { @@ -89,7 +89,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Next, we will construct the Single Particle Model (SPM) with the Chen2020 parameter set, but like the above, this is an arbitruary selection and can be replaced with any PyBOP model." + "Next, we will construct the Single Particle Model (SPM) with the Chen2020 parameter set, but like the above, this is an arbitrary selection and can be replaced with any PyBOP model." ] }, { @@ -163,7 +163,7 @@ "source": [ "### Sum of Square Errors and Root Mean Squared Error\n", "\n", - "First, let's start with the easiest cost functions, the sum of squared errors (SSE) and the root mean squared error (RMSE). Constructing these classes is very concise in PyBOP, and only requires the problem class." + "First, let's start with two commonly-used cost functions: the sum of squared errors (SSE) and the root mean squared error (RMSE). Constructing these classes is very concise in PyBOP, and only requires the problem class." ] }, { @@ -180,7 +180,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now, we can investigate how these functions differ when fitting the parameters. To acquire the distance metric for each of these, we can simply use the constructed class in a call method, such as:" + "Now, we can investigate how these functions differ when fitting the parameters. To acquire the cost value for each of these, we can simply use the call method of the constructed class, such as:" ] }, { @@ -316,7 +316,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In this situation, it's clear that gradient of the SSE cost is much higher than the RMSE. This can be helpful for certain optimisation algorithms, specifically towards improving convergence performance within a predefine number of iterations. However, with incorrect hyperparameter values this can also result in the algorithm not converging due to sampling locations outside of the \"cost valley\"." + "In this situation, it's clear that the curvature of the SSE cost is greater than that of the RMSE. This can improve the rate of convergence for certain optimisation algorithms. However, with incorrect hyperparameter values, larger gradients can also result in the algorithm not converging due to sampling locations outside of the \"cost valley\", e.g. infeasible parameter values." ] }, { @@ -325,14 +325,19 @@ "source": [ "### Minkowski Cost\n", "\n", - "Next, we will investigate using the Minkowski cost function. This cost function provides a general formation of the above two cost functions, allowing for hyper parameter calibration on the cost function itself. The Minkowski cost takes the form,\n", + "Next, we will investigate using the Minkowski distance. The Minkowski cost takes a general form, which allows for hyperparameter calibration on the cost function itself, given by\n", "\n", - "$\\mathcal{L} = \\displaystyle\\sum_{1}^N (\\hat{y}-y)^p$\n", + "$\\mathcal{L_p} = \\displaystyle \\Big(\\sum_i |\\hat{y_i}-y_i|^p\\Big)^{1/p}$\n", "\n", - "For p = 1, this becomes L1Norm \n", - "For p = 2, this becomes L2Norm (SSE)\n", + "where p ≥ 0 is the order of the Minkowski distance.\n", "\n", - "PyBOP offers a Minkowski class, which we will construct below. This class has an optional argument of `p` which designates the order in the above equation. This value can be a float, with the only requirement that it is not negative. First, let's reconstruct the SSE function with a `p` value of 2." + "For $p = 1$, it is the Manhattan distance.\n", + "For $p = 2$, it is the Euclidean distance.\n", + "For $p ≥ 1$, the Minkowski distance is a metric, but for $0 Date: Fri, 12 Jul 2024 13:06:25 +0100 Subject: [PATCH 28/41] Add Minkowski p=inf test --- tests/unit/test_cost.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index ae9e4f4b..61011d8a 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -232,6 +232,8 @@ def test_minkowski(self, problem): # Incorrect order with pytest.raises(ValueError, match="The order of the Minkowski distance"): pybop.Minkowski(problem, p=-1) + with pytest.raises(ValueError, match="For p = infinity, an implementation of the Chebyshev distance is required."): + pybop.Minkowski(problem, p=np.inf) @pytest.mark.parametrize( "cost_class", From 973b51cb5030a5b5231e22854823065494bbf024 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 12:12:10 +0000 Subject: [PATCH 29/41] style: pre-commit fixes --- tests/unit/test_cost.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 61011d8a..297a974c 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -232,7 +232,10 @@ def test_minkowski(self, problem): # Incorrect order with pytest.raises(ValueError, match="The order of the Minkowski distance"): pybop.Minkowski(problem, p=-1) - with pytest.raises(ValueError, match="For p = infinity, an implementation of the Chebyshev distance is required."): + with pytest.raises( + ValueError, + match="For p = infinity, an implementation of the Chebyshev distance is required.", + ): pybop.Minkowski(problem, p=np.inf) @pytest.mark.parametrize( From 73427f93bdc0fd84ee71ff01b2b99bfe88bc1514 Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:41:06 +0100 Subject: [PATCH 30/41] Fix for plot2d bounds error --- pybop/parameters/parameter.py | 4 ++-- pybop/plotting/plot2d.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pybop/parameters/parameter.py b/pybop/parameters/parameter.py index 7aa3e610..c10bee33 100644 --- a/pybop/parameters/parameter.py +++ b/pybop/parameters/parameter.py @@ -417,7 +417,7 @@ def get_bounds_for_plotly(self): bounds : numpy.ndarray An array of shape (n_parameters, 2) containing the bounds for each parameter. """ - bounds = np.empty((len(self), 2)) + bounds = np.zeros((len(self), 2)) for i, param in enumerate(self.param.values()): if param.applied_prior_bounds: @@ -427,7 +427,7 @@ def get_bounds_for_plotly(self): UserWarning, stacklevel=2, ) - elif param.bounds is not None: + if param.bounds is not None: bounds[i] = param.bounds else: raise ValueError("All parameters require bounds for plotting.") diff --git a/pybop/plotting/plot2d.py b/pybop/plotting/plot2d.py index 781b697b..d5c85574 100644 --- a/pybop/plotting/plot2d.py +++ b/pybop/plotting/plot2d.py @@ -1,5 +1,6 @@ import sys import warnings +from typing import Union import numpy as np from scipy.interpolate import griddata @@ -10,7 +11,7 @@ def plot2d( cost_or_optim, gradient: bool = False, - bounds: np.ndarray = None, + bounds: Union[np.ndarray, None] = None, steps: int = 10, show: bool = True, use_optim_log: bool = False, From 73f54774caa0d3d5c93ec2437c66f36717794286 Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:57:21 +0100 Subject: [PATCH 31/41] Reset test_cost.py --- tests/unit/test_cost.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 297a974c..051d0c5e 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -72,11 +72,11 @@ def problem(self, model, parameters, dataset, signal, request): @pytest.fixture( params=[ - # pybop.RootMeanSquaredError, - # pybop.SumSquaredError, + pybop.RootMeanSquaredError, + pybop.SumSquaredError, pybop.Minkowski, - # pybop.ObserverCost, - # pybop.MAP, + pybop.ObserverCost, + pybop.MAP, ] ) def cost(self, problem, request): From 64312ae6f6db7f1158d6104245e5a46e3ba43c11 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Fri, 12 Jul 2024 17:34:27 +0100 Subject: [PATCH 32/41] feat: Add SumofPower cost function --- pybop/__init__.py | 1 + pybop/costs/fitting_costs.py | 96 ++++++++++++++++++- .../integration/test_spm_parameterisations.py | 4 + tests/unit/test_cost.py | 11 ++- 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/pybop/__init__.py b/pybop/__init__.py index e337adf3..c9aabb8d 100644 --- a/pybop/__init__.py +++ b/pybop/__init__.py @@ -86,6 +86,7 @@ RootMeanSquaredError, SumSquaredError, Minkowski, + SumofPower, ObserverCost, ) from .costs.design_costs import ( diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index eaf101c1..24a81228 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -122,7 +122,7 @@ def _evaluate(self, inputs: Inputs, grad=None): Returns ------- float - The sum of squared errors. + The Sum of Squared Error. """ prediction = self.problem.evaluate(inputs) @@ -259,6 +259,100 @@ def _evaluateS1(self, inputs): return e, de +class SumofPower(BaseCost): + """ + The Sum of Power [1] is a generalised cost function based on the p-th power + of absolute differences between two vectors. It is defined as: + + .. math:: + C_p(x, y) = \\sum_i |x_i - y_i|^p + + where p ≥ 1 is the power order. + + This class implements the Sum of Power as a cost function for + optimisation problems, allowing for flexible power-based optimisation + across various problem domains. + + Special cases: + + * p = 1: Sum of Absolute Differences + * p = 2: Sum of Squared Differences + * p → ∞: Maximum Absolute Difference + + Note that this is not normalised, unlike distance metrics. To get a + distance metric, you would need to take the p-th root of the result. + + [1]: https://mathworld.wolfram.com/PowerSum.html + + Attributes: + p : float, optional + The power order for Sum of Power. + """ + + def __init__(self, problem, p: float = 2.0): + super().__init__(problem) + if p < 0: + raise ValueError("The order of 'p' must be greater than 0.") + self.p = p + + def _evaluate(self, inputs: Inputs, grad=None): + """ + Calculate the Sum of Power cost for a given set of parameters. + + Parameters + ---------- + inputs : Inputs + The parameters for which to compute the cost and gradient. + + Returns + ------- + float + The Sum of Power cost. + """ + prediction = self.problem.evaluate(inputs) + if not self.verify_prediction(prediction): + return np.inf + + e = np.asarray( + [ + np.sum(np.abs(prediction[signal] - self._target[signal]) ** self.p) + for signal in self.signal + ] + ) + + return e.item() if self.n_outputs == 1 else np.sum(e) + + def _evaluateS1(self, inputs): + """ + Compute the cost and its gradient with respect to the parameters. + + Parameters + ---------- + inputs : Inputs + The parameters for which to compute the cost and gradient. + + Returns + ------- + tuple + A tuple containing the cost and the gradient. The cost is a float, + and the gradient is an array-like of the same length as `inputs`. + + Raises + ------ + ValueError + If an error occurs during the calculation of the cost or gradient. + """ + y, dy = self.problem.evaluateS1(inputs) + if not self.verify_prediction(y): + return np.inf, self._de * np.ones(self.n_parameters) + + r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) + e = np.sum(np.sum(np.abs(r) ** self.p)) + de = self.p * np.sum(np.sum(r ** (self.p - 1) * dy.T, axis=2), axis=1) + + return e, de + + class ObserverCost(BaseCost): """ Observer cost function. diff --git a/tests/integration/test_spm_parameterisations.py b/tests/integration/test_spm_parameterisations.py index d2d8e982..5e9d6b00 100644 --- a/tests/integration/test_spm_parameterisations.py +++ b/tests/integration/test_spm_parameterisations.py @@ -46,6 +46,8 @@ def init_soc(self, request): pybop.GaussianLogLikelihood, pybop.RootMeanSquaredError, pybop.SumSquaredError, + pybop.SumofPower, + pybop.Minkowski, pybop.MAP, ] ) @@ -78,6 +80,8 @@ def spm_costs(self, model, parameters, cost_class, init_soc): return cost_class( problem, pybop.GaussianLogLikelihoodKnownSigma, sigma0=self.sigma0 ) + elif cost_class in [pybop.SumofPower, pybop.Minkowski]: + return cost_class(problem, p=2) else: return cost_class(problem) diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index b4cb86bf..c2391718 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -75,6 +75,7 @@ def problem(self, model, parameters, dataset, signal, request): pybop.RootMeanSquaredError, pybop.SumSquaredError, pybop.Minkowski, + pybop.SumofPower, pybop.ObserverCost, pybop.MAP, ] @@ -85,7 +86,7 @@ def cost(self, problem, request): return cls(problem) elif cls in [pybop.MAP]: return cls(problem, pybop.GaussianLogLikelihoodKnownSigma) - elif cls in [pybop.Minkowski]: + elif cls in [pybop.Minkowski, pybop.SumofPower]: return cls(problem, p=2) elif cls in [pybop.ObserverCost]: inputs = problem.parameters.initial_value() @@ -233,6 +234,14 @@ def test_minkowski(self, problem): with pytest.raises(ValueError, match="The order of the Minkowski metric"): pybop.Minkowski(problem, p=-1) + @pytest.mark.unit + def test_sumofpower(self, problem): + # Incorrect order + with pytest.raises( + ValueError, match="The order of 'p' must be greater than 0." + ): + pybop.SumofPower(problem, p=-1) + @pytest.mark.parametrize( "cost_class", [ From 82d4d9e0f1e4ebc8a00061cb5587cd718c278cf1 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 15 Jul 2024 10:15:53 +0100 Subject: [PATCH 33/41] feat: add catch for p=np.inf for SumofPower, updt examples --- .../notebooks/comparing_cost_functions.ipynb | 198 +++++++++++++++--- examples/scripts/spm_SNES.py | 2 +- pybop/costs/fitting_costs.py | 4 +- tests/unit/test_cost.py | 5 +- 4 files changed, 173 insertions(+), 36 deletions(-) diff --git a/examples/notebooks/comparing_cost_functions.ipynb b/examples/notebooks/comparing_cost_functions.ipynb index 3d1d750f..8152bee3 100644 --- a/examples/notebooks/comparing_cost_functions.ipynb +++ b/examples/notebooks/comparing_cost_functions.ipynb @@ -13,14 +13,14 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 1, "metadata": {}, "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.1.1)\n", + "Requirement already satisfied: pip in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (24.1.2)\n", "Requirement already satisfied: ipywidgets in /Users/engs2510/.pyenv/versions/3.12.2/envs/pybop-3.12/lib/python3.12/site-packages (8.1.3)\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", @@ -67,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -94,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -111,7 +111,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -128,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -150,7 +150,7 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -168,7 +168,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -185,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -194,7 +194,7 @@ "1.2690291451182834e-09" ] }, - "execution_count": 156, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -212,14 +212,14 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[7.56e-05, 5.22e-06]\n" + "[7.56e-05 5.22e-06]\n" ] }, { @@ -228,7 +228,7 @@ "1.2690291451182834e-09" ] }, - "execution_count": 157, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -247,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -263,7 +263,7 @@ "0.014466013735507651" ] }, - "execution_count": 158, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -285,13 +285,13 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ - "4.8μ5.2μ5.4μ5.6μ00.010.020.030.040.050.060.070.08SSERMSE" + "4.8μ5.2μ5.4μ5.6μ00.010.020.030.040.050.060.070.08SSERMSE" ] }, "metadata": {}, @@ -323,7 +323,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Minkowski Cost\n", + "### Minkowski\n", "\n", "Next, we will investigate using the Minkowski distance. The Minkowski cost takes a general form, which allows for hyperparameter calibration on the cost function itself, given by\n", "\n", @@ -331,8 +331,8 @@ "\n", "where p ≥ 0 is the order of the Minkowski distance.\n", "\n", - "For $p = 1$, it is the Manhattan distance.\n", - "For $p = 2$, it is the Euclidean distance.\n", + "For $p = 1$, it is the Manhattan distance. \n", + "For $p = 2$, it is the Euclidean distance. \n", "For $p ≥ 1$, the Minkowski distance is a metric, but for $04.8μ5.2μ5.4μ5.6μ00.010.020.030.040.050.060.070.08SSEMinkowski" + "4.8μ5.2μ5.4μ5.6μ00.050.10.150.20.25RMSE*Nsqrt(SSE)Minkowski" ] }, "metadata": {}, @@ -404,29 +404,25 @@ }, { "cell_type": "code", - "execution_count": 162, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "y_minkowski = ()\n", "p_orders = np.append(0.75, np.linspace(1, 3, 5))\n", - "for j in p_orders:\n", - " minkowski_order = []\n", - " cost = pybop.Minkowski(problem, p=j)\n", - " for i in x_range:\n", - " minkowski_order.append(cost([7.56e-05, i]))\n", - " y_minkowski += (minkowski_order,)" + "y_minkowski = tuple(\n", + " [pybop.Minkowski(problem, p=j)([7.56e-05, i]) for i in x_range] for j in p_orders\n", + ")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ - "5.2μ5.4μ5.6μ02468101214Minkowski 0.75Minkowski 1.0Minkowski 1.5Minkowski 2.0Minkowski 2.5Minkowski 3.0" + "4.8μ5.2μ5.4μ5.6μ00.10.20.30.40.50.60.7Minkowski 0.75Minkowski 1.0Minkowski 1.5Minkowski 2.0Minkowski 2.5Minkowski 3.0" ] }, "metadata": {}, @@ -449,6 +445,142 @@ "source": [ "As seen above, the Minkowski cost allows for a range of different cost functions to be created. This provides users with another hyperparameter to calibrate for optimisation algorithm convergence. This addition does expand the global search space, and should be carefully considered before deciding upon.\n", "\n", + "### Sum of Power\n", + "Next, we can introduce a similar cost function, the `SumofPower` implementation. This cost function can be view as the Minkowski without the distance normalisation. It provides a generalised formulation for the Sum of Squared Error (SSE) cost function, and is given by,\n", + "\n", + "$\\mathcal{L_p} = \\displaystyle \\sum_i |\\hat{y_i}-y_i|^p$\n", + "\n", + "where p ≥ 0 is the order power order. A few special cases include,\n", + "\n", + "p = 1: Sum of Absolute Differences \n", + "p = 2: Sum of Squared Differences\n", + "\n", + "Next we repeat the above examples with the addition of the `SumofPower` class." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "cost_sumofpower = pybop.SumofPower(problem, p=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "4.8μ5.2μ5.4μ5.6μ00.050.10.150.20.25RMSE*NSSESum of Power" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "y_sumofpower = []\n", + "for i in x_range:\n", + " y_sumofpower.append(cost_sumofpower([7.56e-05, i]))\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=x_range,\n", + " y=np.asarray(y_RMSE) * np.sqrt(len(t_eval)),\n", + " mode=\"lines\",\n", + " name=\"RMSE*N\",\n", + " )\n", + ")\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=x_range,\n", + " y=y_SSE,\n", + " mode=\"lines\",\n", + " line=dict(dash=\"dash\"),\n", + " name=\"SSE\",\n", + " )\n", + ")\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=x_range,\n", + " y=y_sumofpower,\n", + " mode=\"lines\",\n", + " line=dict(dash=\"dot\"),\n", + " name=\"Sum of Power\",\n", + " )\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As expected, the `SumofPower` with order `p=2` equates to the `SSE` implementation. Next, we compare the `Minkowski` to the `SumofPower`," + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "p_orders = np.append(0.75, np.linspace(1, 2, 2))\n", + "\n", + "y_minkowski = tuple(\n", + " [pybop.Minkowski(problem, p=j)([7.56e-05, i]) for i in x_range] for j in p_orders\n", + ")\n", + "\n", + "y_sumofpower = tuple(\n", + " [pybop.SumofPower(problem, p=j)([7.56e-05, i]) for i in x_range] for j in p_orders\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "4.8μ5.2μ5.4μ5.6μ00.511.522.5Minkowski 0.75Sum of Power 0.75Minkowski 1.0Sum of Power 1.0Minkowski 2.0Sum of Power 2.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for k, _ in enumerate(p_orders):\n", + " fig.add_trace(\n", + " go.Scatter(x=x_range, y=y_minkowski[k], mode=\"lines\", name=f\"Minkowski {_}\")\n", + " )\n", + " fig.add_trace(\n", + " go.Scatter(\n", + " x=x_range,\n", + " y=y_sumofpower[k],\n", + " mode=\"lines\",\n", + " line=dict(dash=\"dash\"),\n", + " name=f\"Sum of Power {_}\",\n", + " )\n", + " )\n", + "fig.update_yaxes(range=[0, 2.5])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The figure provides valuable insights into the distinct behaviors of the `Minkowski` distance and the `SumofPower` function. One notable difference is the effect of the `1/p` exponent in the `Minkowski` distance, which has a linearising impact on the response. This linearisation can enhance the robustness of certain optimisation algorithms, potentially making them less sensitive to outliers or extreme values. However, this increased robustness may come at the cost of a slower convergence rate, as the linearised response might require more iterations to reach the optimal solution. In contrast, the `SumofPower` function does not exhibit this linearising effect, which can lead to faster convergence in some cases but may be more susceptible to the influence of outliers or extreme values.\n", + "\n", "In this notebook, we've shown the different fitting cost functions offered in PyBOP. Selection between these functions can affect the optimisation result in the case that the optimiser hyperparameter values are not properly calibrated. " ] } diff --git a/examples/scripts/spm_SNES.py b/examples/scripts/spm_SNES.py index 93046d63..7cddfad9 100644 --- a/examples/scripts/spm_SNES.py +++ b/examples/scripts/spm_SNES.py @@ -35,7 +35,7 @@ # Generate problem, cost function, and optimisation class problem = pybop.FittingProblem(model, parameters, dataset) -cost = pybop.SumSquaredError(problem) +cost = pybop.SumofPower(problem, p=2) optim = pybop.SNES(cost, max_iterations=100) x, final_cost = optim.run() diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index d481ad05..7750d851 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -313,7 +313,9 @@ def __init__(self, problem, p: float = 2.0): super().__init__(problem) if p < 0: raise ValueError("The order of 'p' must be greater than 0.") - self.p = p + elif not np.isfinite(p): + raise ValueError("p = np.inf is not yet supported.") + self.p = float(p) def _evaluate(self, inputs: Inputs, grad=None): """ diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 5d3b8369..09372376 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -240,13 +240,16 @@ def test_minkowski(self, problem): pybop.Minkowski(problem, p=np.inf) @pytest.mark.unit - def test_sumofpower(self, problem): + def test_SumofPower(self, problem): # Incorrect order with pytest.raises( ValueError, match="The order of 'p' must be greater than 0." ): pybop.SumofPower(problem, p=-1) + with pytest.raises(ValueError, match="p = np.inf is not yet supported."): + pybop.SumofPower(problem, p=np.inf) + @pytest.mark.parametrize( "cost_class", [ From 58eded61abe3b82f25b9d284ac83c45073bc2f1c Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:26:22 +0100 Subject: [PATCH 34/41] Minor notebook edits --- .../notebooks/comparing_cost_functions.ipynb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/notebooks/comparing_cost_functions.ipynb b/examples/notebooks/comparing_cost_functions.ipynb index 8152bee3..54faa801 100644 --- a/examples/notebooks/comparing_cost_functions.ipynb +++ b/examples/notebooks/comparing_cost_functions.ipynb @@ -161,7 +161,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Sum of Square Errors and Root Mean Squared Error\n", + "### Sum of Squared Errors and Root Mean Squared Error\n", "\n", "First, let's start with two commonly-used cost functions: the sum of squared errors (SSE) and the root mean squared error (RMSE). Constructing these classes is very concise in PyBOP, and only requires the problem class." ] @@ -280,7 +280,7 @@ "source": [ "#### Comparing RMSE and SSE\n", "\n", - "Now, let's vary one of the parameters with a fixed point for the other and create a scatter plot comparing the cost values for the RMSE and SSE functions." + "Now, let's vary one of the parameters, and keep a fixed value for the other, to create a scatter plot comparing the cost values for the RMSE and SSE functions." ] }, { @@ -323,19 +323,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Minkowski\n", + "### Minkowski distance\n", "\n", - "Next, we will investigate using the Minkowski distance. The Minkowski cost takes a general form, which allows for hyperparameter calibration on the cost function itself, given by\n", + "Next, let's investigate the Minkowski distance. The Minkowski cost takes a general form, which allows for hyperparameter calibration on the cost function itself, given by\n", "\n", "$\\mathcal{L_p} = \\displaystyle \\Big(\\sum_i |\\hat{y_i}-y_i|^p\\Big)^{1/p}$\n", "\n", - "where p ≥ 0 is the order of the Minkowski distance.\n", + "where $p ≥ 0$ is the order of the Minkowski distance.\n", "\n", "For $p = 1$, it is the Manhattan distance. \n", "For $p = 2$, it is the Euclidean distance. \n", "For $p ≥ 1$, the Minkowski distance is a metric, but for $0 Date: Mon, 15 Jul 2024 15:11:10 +0100 Subject: [PATCH 35/41] applying suggestion from review --- CHANGELOG.md | 2 +- pybop/costs/_likelihoods.py | 5 ++--- pybop/costs/base_cost.py | 12 +++++++----- pybop/costs/fitting_costs.py | 2 +- pybop/parameters/parameter.py | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c11316..be08914b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Features -- [#393](https://github.com/pybop-team/PyBOP/pull/383) - Adds Minkowski cost class, with example and tests +- [#393](https://github.com/pybop-team/PyBOP/pull/383) - Adds Minkowski and SumofPower cost classes, with an example and corresponding tests. - [#403](https://github.com/pybop-team/PyBOP/pull/403/) - Adds lychee link checking action. ## Bug Fixes diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index 3fe2309e..e65f02fc 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -121,7 +121,6 @@ def __init__( self.sigma = Parameters() self._add_sigma_parameters(sigma0) self.parameters.join(self.sigma) - self._dl = self._de * np.ones(self.n_parameters) def _add_sigma_parameters(self, sigma0): sigma0 = [sigma0] if not isinstance(sigma0, list) else sigma0 @@ -226,11 +225,11 @@ def _evaluateS1(self, inputs: Inputs) -> tuple[float, np.ndarray]: sigma = self.sigma.current_value() if np.any(sigma <= 0): - return -np.inf, -self._dl + return -np.inf, -self._de * np.ones(self.n_parameters) y, dy = self.problem.evaluateS1(self.problem.parameters.as_dict()) if not self.verify_prediction(y): - return -np.inf, -self._dl + return -np.inf, -self._de * np.ones(self.n_parameters) likelihood = self._evaluate(inputs) diff --git a/pybop/costs/base_cost.py b/pybop/costs/base_cost.py index 46c59c6f..d40bbb99 100644 --- a/pybop/costs/base_cost.py +++ b/pybop/costs/base_cost.py @@ -1,3 +1,5 @@ +from typing import Union + from pybop import BaseProblem from pybop.parameters.parameter import Inputs, Parameters @@ -36,19 +38,19 @@ def __init__(self, problem=None): def n_parameters(self): return len(self.parameters) - def __call__(self, inputs: Inputs, grad=None): + def __call__(self, inputs: Union[Inputs, list], grad=None): """ Call the evaluate function for a given set of parameters. """ return self.evaluate(inputs, grad) - def evaluate(self, inputs: Inputs, grad=None): + def evaluate(self, inputs: Union[Inputs, list], grad=None): """ Call the evaluate function for a given set of parameters. Parameters ---------- - inputs : Inputs + inputs : Inputs or array-like The parameters for which to compute the cost and gradient. grad : array-like, optional An array to store the gradient of the cost function with respect @@ -101,13 +103,13 @@ def _evaluate(self, inputs: Inputs, grad=None): """ raise NotImplementedError - def evaluateS1(self, inputs: Inputs): + def evaluateS1(self, inputs: Union[Inputs, list]): """ Call _evaluateS1 for a given set of parameters. Parameters ---------- - inputs : Inputs + inputs : Inputs or array-like The parameters for which to compute the cost and gradient. Returns diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index 7750d851..ac17f3ea 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -287,7 +287,7 @@ class SumofPower(BaseCost): .. math:: C_p(x, y) = \\sum_i |x_i - y_i|^p - where p ≥ 1 is the power order. + where p ≥ 0 is the power order. This class implements the Sum of Power as a cost function for optimisation problems, allowing for flexible power-based optimisation diff --git a/pybop/parameters/parameter.py b/pybop/parameters/parameter.py index c10bee33..2614027c 100644 --- a/pybop/parameters/parameter.py +++ b/pybop/parameters/parameter.py @@ -322,7 +322,7 @@ def update(self, initial_values=None, values=None, bounds=None): else: param.set_bounds(bounds=bounds[i]) - def rvs(self, n_samples: int = 1) -> list: + def rvs(self, n_samples: int = 1) -> np.ndarray: """ Draw random samples from each parameter's prior distribution. From 071577a5c5fe2705a4312184fa4df2b6cedbbf05 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:03:00 +0000 Subject: [PATCH 36/41] chore: update pre-commit hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.1 → v0.5.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.1...v0.5.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 82299a92..28055658 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.5.1" + rev: "v0.5.2" hooks: - id: ruff args: [--fix, --show-fixes] From 4f7eb591d97a62250485c3ccfe5ba56594465b5b Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Mon, 15 Jul 2024 19:58:34 +0100 Subject: [PATCH 37/41] fix E721, '==' assertions --- tests/unit/test_cost.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 09372376..6a7d1a90 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -203,7 +203,7 @@ def test_costs(self, cost): e, de = cost.evaluateS1([0.5]) assert np.isscalar(e) - assert type(de) == np.ndarray + assert isinstance(de, np.ndarray) # Test exception for non-numeric inputs with pytest.raises( From 40133fb4ebd918259dcffc0d33ecb5e5ee58b180 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Sat, 20 Jul 2024 11:24:58 +0100 Subject: [PATCH 38/41] build: increment pybamm to 24.5rc2, ci: increment tests alongside. --- .gitignore | 3 +++ CHANGELOG.md | 1 + examples/scripts/gitt.py | 4 ++-- pybop/models/lithium_ion/weppner_huggins.py | 2 +- pyproject.toml | 3 +-- scripts/ci/build_matrix.sh | 11 +++++++---- tests/integration/test_model_experiment_changes.py | 2 +- tests/unit/test_models.py | 2 +- 8 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 3c3bb708..2bafcca6 100644 --- a/.gitignore +++ b/.gitignore @@ -314,3 +314,6 @@ $RECYCLE.BIN/ # Airspeed Velocity *.asv/ results/ + +# Pycharm +*.idea/ diff --git a/CHANGELOG.md b/CHANGELOG.md index be08914b..9450ad11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#393](https://github.com/pybop-team/PyBOP/pull/383) - Adds Minkowski and SumofPower cost classes, with an example and corresponding tests. - [#403](https://github.com/pybop-team/PyBOP/pull/403/) - Adds lychee link checking action. +- [#313](https://github.com/pybop-team/PyBOP/pull/313/) - Fixes for PyBaMM v24.5, drops support for PyBaMM v23.9, v24.1 ## Bug Fixes diff --git a/examples/scripts/gitt.py b/examples/scripts/gitt.py index 6d3b4a94..4b8c1561 100644 --- a/examples/scripts/gitt.py +++ b/examples/scripts/gitt.py @@ -36,10 +36,10 @@ model = pybop.lithium_ion.WeppnerHuggins(parameter_set=parameter_set) parameters = pybop.Parameter( - "Positive electrode diffusivity [m2.s-1]", + "Positive particle diffusivity [m2.s-1]", prior=pybop.Gaussian(5e-14, 1e-13), bounds=[1e-16, 1e-11], - true_value=parameter_set["Positive electrode diffusivity [m2.s-1]"], + true_value=parameter_set["Positive particle diffusivity [m2.s-1]"], ) problem = pybop.FittingProblem( diff --git a/pybop/models/lithium_ion/weppner_huggins.py b/pybop/models/lithium_ion/weppner_huggins.py index 74c42c70..b8707cca 100644 --- a/pybop/models/lithium_ion/weppner_huggins.py +++ b/pybop/models/lithium_ion/weppner_huggins.py @@ -65,7 +65,7 @@ def __init__(self, name="Weppner & Huggins model", **model_kwargs): # Parameters ###################### - d_s = Parameter("Positive electrode diffusivity [m2.s-1]") + d_s = Parameter("Positive particle diffusivity [m2.s-1]") c_s_max = Parameter("Maximum concentration in positive electrode [mol.m-3]") diff --git a/pyproject.toml b/pyproject.toml index cd99a096..eacb65f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,8 +26,7 @@ classifiers = [ ] requires-python = ">=3.9, <3.13" dependencies = [ - # "pybamm>=23.9", - "pybamm @ git+https://github.com/bradyplanden/PyBaMM@v24.5rc0-diffusivity-test", + "pybamm>=24.5rc2", "numpy>=1.16, <2.0", "scipy>=1.3", "pints>=0.5", diff --git a/scripts/ci/build_matrix.sh b/scripts/ci/build_matrix.sh index 9dfe3224..88d5b9b3 100755 --- a/scripts/ci/build_matrix.sh +++ b/scripts/ci/build_matrix.sh @@ -11,8 +11,11 @@ python_version=("3.9" "3.10" "3.11" "3.12") os=("ubuntu-latest" "windows-latest" "macos-13" "macos-14") -# This command fetches the last two PyBaMM versions excluding release candidates from PyPI -pybamm_version=($(curl -s https://pypi.org/pypi/pybamm/json | jq -r '.releases | keys[]' | grep -v rc | tail -n 2 | paste -sd " " -)) +# This command fetches the last PyBaMM version excluding release candidates from PyPI +#pybamm_version=($(curl -s https://pypi.org/pypi/pybamm/json | jq -r '.releases | keys[]' | grep -v rc | tail -n 1 | paste -sd " " -)) + +# This command fetches the last PyBaMM versions including release candidates from PyPI +pybamm_version=($(curl -s https://pypi.org/pypi/pybamm/json | jq -r '.releases | keys[]' | tail -n 1 | paste -sd " " -)) # open dict json='{ @@ -40,7 +43,7 @@ json+=' ] }' -# Filter out incompatible combinations -json=$(echo "$json" | jq -c 'del(.include[] | select(.pybamm_version == "23.9" and .python_version == "3.12"))') +# Example for filtering out incompatible combinations +#json=$(echo "$json" | jq -c 'del(.include[] | select(.pybamm_version == "23.9" and .python_version == "3.12"))') echo "$json" | jq -c . diff --git a/tests/integration/test_model_experiment_changes.py b/tests/integration/test_model_experiment_changes.py index 64d27132..7bcc33dd 100644 --- a/tests/integration/test_model_experiment_changes.py +++ b/tests/integration/test_model_experiment_changes.py @@ -22,7 +22,7 @@ class TestModelAndExperimentChanges: ), pybop.Parameters( pybop.Parameter( # non-geometric parameter - "Positive electrode diffusivity [m2.s-1]", + "Positive particle diffusivity [m2.s-1]", prior=pybop.Gaussian(3.43e-15, 1e-15), bounds=[1e-15, 5e-15], true_value=4e-15, diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index b2e29842..b12b3639 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -361,7 +361,7 @@ def test_non_converged_solution(self): problem = pybop.FittingProblem(model, parameters=parameters, dataset=dataset) # Simulate the DFN with active material values of 0. - # The solutions will not change as the solver will not converge. + # The solution elements will not change as the solver will not converge. output = problem.evaluate([0, 0]) output_S1, _ = problem.evaluateS1([0, 0]) From 4c100ff618b9ee4327040e77b1fd7656e8ee9ce3 Mon Sep 17 00:00:00 2001 From: Brady Planden Date: Sat, 20 Jul 2024 15:13:38 +0100 Subject: [PATCH 39/41] tests: increase coverage, fix flaky observer tests --- tests/unit/test_observer_unscented_kalman.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/unit/test_observer_unscented_kalman.py b/tests/unit/test_observer_unscented_kalman.py index 0b5d3067..c83fae31 100644 --- a/tests/unit/test_observer_unscented_kalman.py +++ b/tests/unit/test_observer_unscented_kalman.py @@ -93,6 +93,15 @@ def test_cholupdate(self): SquareRootUKF.cholupdate(R1_, u.copy(), 1.0) np.testing.assert_array_almost_equal(R1, R1_) + # Test hypot + f = 10.0 + j = 20.0 + out_1 = f * np.sqrt(1 + 1.0 * f**2 / j**2) + np.testing.assert_allclose(SquareRootUKF.hypot(f, j, 1.0), out_1) + + j = 10.0 + np.testing.assert_allclose(SquareRootUKF.hypot(f, j, 1.0), float(0)) + @pytest.mark.unit def test_unscented_kalman_filter(self, dataset, observer): t_eval = dataset["Time [s]"] @@ -116,6 +125,11 @@ def test_unscented_kalman_filter(self, dataset, observer): decimal=4, ) + # Test get covariance + cov = observer.get_current_measure() + assert cov.shape == (1, 1) + assert float(0) <= cov[0] + @pytest.mark.unit def test_observe_no_measurement(self, observer): with pytest.raises(ValueError): From f3a32bcbd5fda8e2b6fb79c69989f7f41a31de5c Mon Sep 17 00:00:00 2001 From: NicolaCourtier <45851982+NicolaCourtier@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:54:43 +0100 Subject: [PATCH 40/41] Add a WeightedCost (#329) * Add a WeightedCost * Fix setting * Add tests * Update base_cost.py * Update CHANGELOG.md * Update imports * Update x0 to [0.5] * Update spm_weighted_cost.py * Update TypeError with test * Update spm_weighted_cost.py * Update evaluate and _evaluate * Pass current_sensitivities to MAP * Add test_weighted_design_cost * Add evaluate back into GaussianLogLikelihood * Update to super() * Update prediction to self._current_prediction * Update y to self._current_prediction * Update cost_list to args * Add descriptions * refactor: move WeightedCost into separate file --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Brady Planden Co-authored-by: Brady Planden <55357039+BradyPlanden@users.noreply.github.com> --- CHANGELOG.md | 1 + examples/scripts/spm_weighted_cost.py | 61 ++++++++++++ pybop/__init__.py | 1 + pybop/costs/_likelihoods.py | 64 ++++++++---- pybop/costs/_weighted_cost.py | 138 ++++++++++++++++++++++++++ pybop/costs/base_cost.py | 20 +++- pybop/costs/design_costs.py | 2 + pybop/costs/fitting_costs.py | 92 +++++++++++------ tests/unit/test_cost.py | 101 ++++++++++++++++--- 9 files changed, 413 insertions(+), 67 deletions(-) create mode 100644 examples/scripts/spm_weighted_cost.py create mode 100644 pybop/costs/_weighted_cost.py diff --git a/CHANGELOG.md b/CHANGELOG.md index be08914b..5a92d6d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Features +- [#327](https://github.com/pybop-team/PyBOP/issues/327) - Adds the `WeightedCost` subclass, defines when to evaluate a problem and adds the `spm_weighted_cost` example script. - [#393](https://github.com/pybop-team/PyBOP/pull/383) - Adds Minkowski and SumofPower cost classes, with an example and corresponding tests. - [#403](https://github.com/pybop-team/PyBOP/pull/403/) - Adds lychee link checking action. diff --git a/examples/scripts/spm_weighted_cost.py b/examples/scripts/spm_weighted_cost.py new file mode 100644 index 00000000..74c43a33 --- /dev/null +++ b/examples/scripts/spm_weighted_cost.py @@ -0,0 +1,61 @@ +import numpy as np + +import pybop + +# Parameter set and model definition +parameter_set = pybop.ParameterSet.pybamm("Chen2020") +model = pybop.lithium_ion.SPM(parameter_set=parameter_set) + +# Fitting parameters +parameters = pybop.Parameters( + pybop.Parameter( + "Negative electrode active material volume fraction", + prior=pybop.Gaussian(0.68, 0.05), + bounds=[0.5, 0.8], + true_value=parameter_set["Negative electrode active material volume fraction"], + ), + pybop.Parameter( + "Positive electrode active material volume fraction", + prior=pybop.Gaussian(0.58, 0.05), + bounds=[0.4, 0.7], + true_value=parameter_set["Positive electrode active material volume fraction"], + ), +) + +# Generate data +sigma = 0.001 +t_eval = np.arange(0, 900, 3) +values = model.predict(t_eval=t_eval) +corrupt_values = values["Voltage [V]"].data + np.random.normal(0, sigma, len(t_eval)) + +# Form dataset +dataset = pybop.Dataset( + { + "Time [s]": t_eval, + "Current function [A]": values["Current [A]"].data, + "Voltage [V]": corrupt_values, + } +) + +# Generate problem, cost function, and optimisation class +problem = pybop.FittingProblem(model, parameters, dataset) +cost1 = pybop.SumSquaredError(problem) +cost2 = pybop.RootMeanSquaredError(problem) +weighted_cost = pybop.WeightedCost(cost1, cost2, weights=[1, 100]) + +for cost in [weighted_cost, cost1, cost2]: + optim = pybop.IRPropMin(cost, max_iterations=60) + + # Run the optimisation + x, final_cost = optim.run() + print("True parameters:", parameters.true_value()) + print("Estimated parameters:", x) + + # Plot the timeseries output + pybop.quick_plot(problem, problem_inputs=x, title="Optimised Comparison") + + # Plot convergence + pybop.plot_convergence(optim) + + # Plot the cost landscape with optimisation path + pybop.plot2d(optim, steps=15) diff --git a/pybop/__init__.py b/pybop/__init__.py index c9aabb8d..922a6480 100644 --- a/pybop/__init__.py +++ b/pybop/__init__.py @@ -100,6 +100,7 @@ GaussianLogLikelihoodKnownSigma, MAP, ) +from .costs._weighted_cost import WeightedCost # # Optimiser class diff --git a/pybop/costs/_likelihoods.py b/pybop/costs/_likelihoods.py index e65f02fc..1f96e2fb 100644 --- a/pybop/costs/_likelihoods.py +++ b/pybop/costs/_likelihoods.py @@ -43,16 +43,17 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo """ Evaluates the Gaussian log-likelihood for the given parameters with known sigma. """ - y = self.problem.evaluate(inputs) - - if not self.verify_prediction(y): + if not self.verify_prediction(self._current_prediction): return -np.inf e = np.asarray( [ np.sum( self._offset - + self._multip * np.sum((self._target[signal] - y[signal]) ** 2.0) + + self._multip + * np.sum( + (self._target[signal] - self._current_prediction[signal]) ** 2.0 + ) ) for signal in self.signal ] @@ -62,17 +63,22 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo def _evaluateS1(self, inputs: Inputs) -> tuple[float, np.ndarray]: """ - Calls the problem.evaluateS1 method and calculates the log-likelihood and gradient. + Calculates the log-likelihood and gradient. """ - y, dy = self.problem.evaluateS1(inputs) - - if not self.verify_prediction(y): + if not self.verify_prediction(self._current_prediction): return -np.inf, -self._de * np.ones(self.n_parameters) likelihood = self._evaluate(inputs) - r = np.asarray([self._target[signal] - y[signal] for signal in self.signal]) - dl = np.sum((np.sum((r * dy.T), axis=2) / self.sigma2), axis=1) + r = np.asarray( + [ + self._target[signal] - self._current_prediction[signal] + for signal in self.signal + ] + ) + dl = np.sum( + (np.sum((r * self._current_sensitivities.T), axis=2) / self.sigma2), axis=1 + ) return likelihood, dl @@ -117,6 +123,7 @@ def __init__( super().__init__(problem) self._dsigma_scale = dsigma_scale self._logpi = -0.5 * self.n_time_data * np.log(2 * np.pi) + self._fixed_problem = False # keep problem evaluation within _evaluate self.sigma = Parameters() self._add_sigma_parameters(sigma0) @@ -189,8 +196,10 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo if np.any(sigma <= 0): return -np.inf - y = self.problem.evaluate(self.problem.parameters.as_dict()) - if not self.verify_prediction(y): + self._current_prediction = self.problem.evaluate( + self.problem.parameters.as_dict() + ) + if not self.verify_prediction(self._current_prediction): return -np.inf e = np.asarray( @@ -198,7 +207,9 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo np.sum( self._logpi - self.n_time_data * np.log(sigma) - - np.sum((self._target[signal] - y[signal]) ** 2.0) + - np.sum( + (self._target[signal] - self._current_prediction[signal]) ** 2.0 + ) / (2.0 * sigma**2.0) ) for signal in self.signal @@ -209,7 +220,7 @@ def _evaluate(self, inputs: Inputs, grad: Union[None, np.ndarray] = None) -> flo def _evaluateS1(self, inputs: Inputs) -> tuple[float, np.ndarray]: """ - Calls the problem.evaluateS1 method and calculates the log-likelihood. + Calculates the log-likelihood and sensitivities. Parameters ---------- @@ -227,14 +238,23 @@ def _evaluateS1(self, inputs: Inputs) -> tuple[float, np.ndarray]: if np.any(sigma <= 0): return -np.inf, -self._de * np.ones(self.n_parameters) - y, dy = self.problem.evaluateS1(self.problem.parameters.as_dict()) - if not self.verify_prediction(y): + self._current_prediction, self._current_sensitivities = self.problem.evaluateS1( + self.problem.parameters.as_dict() + ) + if not self.verify_prediction(self._current_prediction): return -np.inf, -self._de * np.ones(self.n_parameters) likelihood = self._evaluate(inputs) - r = np.asarray([self._target[signal] - y[signal] for signal in self.signal]) - dl = np.sum((np.sum((r * dy.T), axis=2) / (sigma**2.0)), axis=1) + r = np.asarray( + [ + self._target[signal] - self._current_prediction[signal] + for signal in self.signal + ] + ) + dl = np.sum( + (np.sum((r * self._current_sensitivities.T), axis=2) / (sigma**2.0)), axis=1 + ) dsigma = ( -self.n_time_data / sigma + np.sum(r**2.0, axis=1) / (sigma**3.0) ) / self._dsigma_scale @@ -300,7 +320,10 @@ def _evaluate(self, inputs: Inputs, grad=None) -> float: if not np.isfinite(log_prior).any(): return -np.inf + if self._fixed_problem: + self.likelihood._current_prediction = self._current_prediction log_likelihood = self.likelihood._evaluate(inputs) + posterior = log_likelihood + log_prior return posterior @@ -331,6 +354,11 @@ def _evaluateS1(self, inputs: Inputs) -> tuple[float, np.ndarray]: if not np.isfinite(log_prior).any(): return -np.inf, -self._de * np.ones(self.n_parameters) + if self._fixed_problem: + ( + self.likelihood._current_prediction, + self.likelihood._current_sensitivities, + ) = self._current_prediction, self._current_sensitivities log_likelihood, dl = self.likelihood._evaluateS1(inputs) # Compute a finite difference approximation of the gradient of the log prior diff --git a/pybop/costs/_weighted_cost.py b/pybop/costs/_weighted_cost.py new file mode 100644 index 00000000..effa5a51 --- /dev/null +++ b/pybop/costs/_weighted_cost.py @@ -0,0 +1,138 @@ +from typing import Optional + +import numpy as np + +from pybop import BaseCost +from pybop.parameters.parameter import Inputs + + +class WeightedCost(BaseCost): + """ + A subclass for constructing a linear combination of cost functions as + a single weighted cost function. + + Inherits all parameters and attributes from ``BaseCost``. + + Additional Attributes + --------------------- + costs : list[pybop.BaseCost] + A list of PyBOP cost objects. + weights : list[float] + A list of values with which to weight the cost values. + _different_problems : bool + If True, the problem for each cost is evaluated independently during + each evaluation of the cost (default: False). + """ + + def __init__(self, *args, weights: Optional[list[float]] = None): + self.costs = [] + for cost in args: + if not isinstance(cost, BaseCost): + raise TypeError(f"Received {type(cost)} instead of cost object.") + self.costs.append(cost) + self.weights = weights + self._different_problems = False + + if self.weights is None: + self.weights = np.ones(len(self.costs)) + elif isinstance(self.weights, list): + self.weights = np.array(self.weights) + if not isinstance(self.weights, np.ndarray): + raise TypeError( + "Expected a list or array of weights the same length as costs." + ) + if not len(self.weights) == len(self.costs): + raise ValueError( + "Expected a list or array of weights the same length as costs." + ) + + # Check if all costs depend on the same problem + for cost in self.costs: + if hasattr(cost, "problem") and cost.problem is not self.costs[0].problem: + self._different_problems = True + + if not self._different_problems: + super().__init__(self.costs[0].problem) + self._fixed_problem = self.costs[0]._fixed_problem + else: + super().__init__() + self._fixed_problem = False + for cost in self.costs: + self.parameters.join(cost.parameters) + + def _evaluate(self, inputs: Inputs, grad=None): + """ + Calculate the weighted cost for a given set of parameters. + + Parameters + ---------- + inputs : Inputs + The parameters for which to compute the cost. + grad : array-like, optional + An array to store the gradient of the cost function with respect + to the parameters. + + Returns + ------- + float + The weighted cost value. + """ + e = np.empty_like(self.costs) + + if not self._fixed_problem and self._different_problems: + self.parameters.update(values=list(inputs.values())) + elif not self._fixed_problem: + self._current_prediction = self.problem.evaluate(inputs) + + for i, cost in enumerate(self.costs): + if not self._fixed_problem and self._different_problems: + inputs = cost.parameters.as_dict() + cost._current_prediction = cost.problem.evaluate(inputs) + else: + cost._current_prediction = self._current_prediction + e[i] = cost._evaluate(inputs, grad) + + return np.dot(e, self.weights) + + def _evaluateS1(self, inputs: Inputs): + """ + Compute the weighted cost and its gradient with respect to the parameters. + + Parameters + ---------- + inputs : Inputs + The parameters for which to compute the cost and gradient. + + Returns + ------- + tuple + A tuple containing the cost and the gradient. The cost is a float, + and the gradient is an array-like of the same length as `x`. + """ + e = np.empty_like(self.costs) + de = np.empty((len(self.parameters), len(self.costs))) + + if not self._fixed_problem and self._different_problems: + self.parameters.update(values=list(inputs.values())) + elif not self._fixed_problem: + self._current_prediction, self._current_sensitivities = ( + self.problem.evaluateS1(inputs) + ) + + for i, cost in enumerate(self.costs): + if not self._fixed_problem and self._different_problems: + inputs = cost.parameters.as_dict() + cost._current_prediction, cost._current_sensitivities = ( + cost.problem.evaluateS1(inputs) + ) + else: + cost._current_prediction, cost._current_sensitivities = ( + self._current_prediction, + self._current_sensitivities, + ) + e[i], de[:, i] = cost._evaluateS1(inputs) + + e = np.dot(e, self.weights) + de = np.dot(de, self.weights) + + return e, de diff --git a/pybop/costs/base_cost.py b/pybop/costs/base_cost.py index d40bbb99..eedbbc2c 100644 --- a/pybop/costs/base_cost.py +++ b/pybop/costs/base_cost.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Optional, Union from pybop import BaseProblem from pybop.parameters.parameter import Inputs, Parameters @@ -22,17 +22,25 @@ class BaseCost: An array containing the target data to fit. n_outputs : int The number of outputs in the model. + + Additional Attributes + --------------------- + _fixed_problem : bool + If True, the problem does not need to be rebuilt before the cost is + calculated (default: False). """ - def __init__(self, problem=None): + def __init__(self, problem: Optional[BaseProblem] = None): self.parameters = Parameters() self.problem = problem + self._fixed_problem = False self.set_fail_gradient() if isinstance(self.problem, BaseProblem): self._target = self.problem._target self.parameters.join(self.problem.parameters) self.n_outputs = self.problem.n_outputs self.signal = self.problem.signal + self._fixed_problem = True @property def n_parameters(self): @@ -69,6 +77,9 @@ def evaluate(self, inputs: Union[Inputs, list], grad=None): inputs = self.parameters.verify(inputs) try: + if self._fixed_problem: + self._current_prediction = self.problem.evaluate(inputs) + return self._evaluate(inputs, grad) except NotImplementedError as e: @@ -126,6 +137,11 @@ def evaluateS1(self, inputs: Union[Inputs, list]): inputs = self.parameters.verify(inputs) try: + if self._fixed_problem: + self._current_prediction, self._current_sensitivities = ( + self.problem.evaluateS1(inputs) + ) + return self._evaluateS1(inputs) except NotImplementedError as e: diff --git a/pybop/costs/design_costs.py b/pybop/costs/design_costs.py index ac8ecaca..738dfe61 100644 --- a/pybop/costs/design_costs.py +++ b/pybop/costs/design_costs.py @@ -98,6 +98,7 @@ class GravimetricEnergyDensity(DesignCost): def __init__(self, problem, update_capacity=False): super().__init__(problem, update_capacity) + self._fixed_problem = False # keep problem evaluation within _evaluate def _evaluate(self, inputs: Inputs, grad=None): """ @@ -154,6 +155,7 @@ class VolumetricEnergyDensity(DesignCost): def __init__(self, problem, update_capacity=False): super().__init__(problem, update_capacity) + self._fixed_problem = False # keep problem evaluation within _evaluate def _evaluate(self, inputs: Inputs, grad=None): """ diff --git a/pybop/costs/fitting_costs.py b/pybop/costs/fitting_costs.py index ac17f3ea..18cc752d 100644 --- a/pybop/costs/fitting_costs.py +++ b/pybop/costs/fitting_costs.py @@ -38,14 +38,16 @@ def _evaluate(self, inputs: Inputs, grad=None): The root mean square error. """ - prediction = self.problem.evaluate(inputs) - - if not self.verify_prediction(prediction): + if not self.verify_prediction(self._current_prediction): return np.inf e = np.asarray( [ - np.sqrt(np.mean((prediction[signal] - self._target[signal]) ** 2)) + np.sqrt( + np.mean( + (self._current_prediction[signal] - self._target[signal]) ** 2 + ) + ) for signal in self.signal ] ) @@ -72,13 +74,19 @@ def _evaluateS1(self, inputs: Inputs): ValueError If an error occurs during the calculation of the cost or gradient. """ - y, dy = self.problem.evaluateS1(inputs) - if not self.verify_prediction(y): + if not self.verify_prediction(self._current_prediction): return np.inf, self._de * np.ones(self.n_parameters) - r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) + r = np.asarray( + [ + self._current_prediction[signal] - self._target[signal] + for signal in self.signal + ] + ) e = np.sqrt(np.mean(r**2, axis=1)) - de = np.mean((r * dy.T), axis=2) / (e + np.finfo(float).eps) + de = np.mean((r * self._current_sensitivities.T), axis=2) / ( + e + np.finfo(float).eps + ) if self.n_outputs == 1: return e.item(), de.flatten() @@ -124,14 +132,12 @@ def _evaluate(self, inputs: Inputs, grad=None): float The Sum of Squared Error. """ - prediction = self.problem.evaluate(inputs) - - if not self.verify_prediction(prediction): + if not self.verify_prediction(self._current_prediction): return np.inf e = np.asarray( [ - np.sum((prediction[signal] - self._target[signal]) ** 2) + np.sum((self._current_prediction[signal] - self._target[signal]) ** 2) for signal in self.signal ] ) @@ -158,13 +164,17 @@ def _evaluateS1(self, inputs: Inputs): ValueError If an error occurs during the calculation of the cost or gradient. """ - y, dy = self.problem.evaluateS1(inputs) - if not self.verify_prediction(y): + if not self.verify_prediction(self._current_prediction): return np.inf, self._de * np.ones(self.n_parameters) - r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) + r = np.asarray( + [ + self._current_prediction[signal] - self._target[signal] + for signal in self.signal + ] + ) e = np.sum(np.sum(r**2, axis=0), axis=0) - de = 2 * np.sum(np.sum((r * dy.T), axis=2), axis=1) + de = 2 * np.sum(np.sum((r * self._current_sensitivities.T), axis=2), axis=1) return e, de @@ -224,13 +234,15 @@ def _evaluate(self, inputs: Inputs, grad=None): float The Minkowski cost. """ - prediction = self.problem.evaluate(inputs) - if not self.verify_prediction(prediction): + if not self.verify_prediction(self._current_prediction): return np.inf e = np.asarray( [ - np.sum(np.abs(prediction[signal] - self._target[signal]) ** self.p) + np.sum( + np.abs(self._current_prediction[signal] - self._target[signal]) + ** self.p + ) ** (1 / self.p) for signal in self.signal ] @@ -258,20 +270,27 @@ def _evaluateS1(self, inputs): ValueError If an error occurs during the calculation of the cost or gradient. """ - y, dy = self.problem.evaluateS1(inputs) - if not self.verify_prediction(y): + if not self.verify_prediction(self._current_prediction): return np.inf, self._de * np.ones(self.n_parameters) - r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) + r = np.asarray( + [ + self._current_prediction[signal] - self._target[signal] + for signal in self.signal + ] + ) e = np.asarray( [ - np.sum(np.abs(y[signal] - self._target[signal]) ** self.p) + np.sum( + np.abs(self._current_prediction[signal] - self._target[signal]) + ** self.p + ) ** (1 / self.p) for signal in self.signal ] ) de = np.sum( - np.sum(r ** (self.p - 1) * dy.T, axis=2) + np.sum(r ** (self.p - 1) * self._current_sensitivities.T, axis=2) / (e ** (self.p - 1) + np.finfo(float).eps), axis=1, ) @@ -331,13 +350,15 @@ def _evaluate(self, inputs: Inputs, grad=None): float The Sum of Power cost. """ - prediction = self.problem.evaluate(inputs) - if not self.verify_prediction(prediction): + if not self.verify_prediction(self._current_prediction): return np.inf e = np.asarray( [ - np.sum(np.abs(prediction[signal] - self._target[signal]) ** self.p) + np.sum( + np.abs(self._current_prediction[signal] - self._target[signal]) + ** self.p + ) for signal in self.signal ] ) @@ -364,13 +385,19 @@ def _evaluateS1(self, inputs): ValueError If an error occurs during the calculation of the cost or gradient. """ - y, dy = self.problem.evaluateS1(inputs) - if not self.verify_prediction(y): + if not self.verify_prediction(self._current_prediction): return np.inf, self._de * np.ones(self.n_parameters) - r = np.asarray([y[signal] - self._target[signal] for signal in self.signal]) + r = np.asarray( + [ + self._current_prediction[signal] - self._target[signal] + for signal in self.signal + ] + ) e = np.sum(np.sum(np.abs(r) ** self.p)) - de = self.p * np.sum(np.sum(r ** (self.p - 1) * dy.T, axis=2), axis=1) + de = self.p * np.sum( + np.sum(r ** (self.p - 1) * self._current_sensitivities.T, axis=2), axis=1 + ) return e, de @@ -389,6 +416,7 @@ class ObserverCost(BaseCost): def __init__(self, observer: Observer): super().__init__(problem=observer) self._observer = observer + self._fixed_problem = False # keep problem evaluation within _evaluate def _evaluate(self, inputs: Inputs, grad=None): """ @@ -412,7 +440,7 @@ def _evaluate(self, inputs: Inputs, grad=None): ) return -log_likelihood - def evaluateS1(self, inputs: Inputs): + def _evaluateS1(self, inputs: Inputs): """ Compute the cost and its gradient with respect to the parameters. diff --git a/tests/unit/test_cost.py b/tests/unit/test_cost.py index 6a7d1a90..802accf1 100644 --- a/tests/unit/test_cost.py +++ b/tests/unit/test_cost.py @@ -1,3 +1,5 @@ +from copy import copy + import numpy as np import pytest @@ -250,6 +252,12 @@ def test_SumofPower(self, problem): with pytest.raises(ValueError, match="p = np.inf is not yet supported."): pybop.SumofPower(problem, p=np.inf) + @pytest.fixture + def design_problem(self, model, parameters, experiment, signal): + return pybop.DesignProblem( + model, parameters, experiment, signal=signal, init_soc=0.5 + ) + @pytest.mark.parametrize( "cost_class", [ @@ -259,21 +267,9 @@ def test_SumofPower(self, problem): ], ) @pytest.mark.unit - def test_design_costs( - self, - cost_class, - model, - parameters, - experiment, - signal, - ): - # Construct Problem - problem = pybop.DesignProblem( - model, parameters, experiment, signal=signal, init_soc=0.5 - ) - + def test_design_costs(self, cost_class, design_problem): # Construct Cost - cost = cost_class(problem) + cost = cost_class(design_problem) if cost_class in [pybop.DesignCost]: with pytest.raises(NotImplementedError): @@ -299,5 +295,80 @@ def test_design_costs( cost(["StringInputShouldNotWork"]) # Compute after updating nominal capacity - cost = cost_class(problem, update_capacity=True) + cost = cost_class(design_problem, update_capacity=True) cost([0.4]) + + @pytest.mark.unit + def test_weighted_fitting_cost(self, problem): + cost1 = pybop.SumSquaredError(problem) + cost2 = pybop.RootMeanSquaredError(problem) + + # Test with and without weights + weighted_cost = pybop.WeightedCost(cost1, cost2) + np.testing.assert_array_equal(weighted_cost.weights, np.ones(2)) + weighted_cost = pybop.WeightedCost(cost1, cost2, weights=[1, 1]) + np.testing.assert_array_equal(weighted_cost.weights, np.ones(2)) + weighted_cost = pybop.WeightedCost(cost1, cost2, weights=np.array([1, 1])) + np.testing.assert_array_equal(weighted_cost.weights, np.ones(2)) + with pytest.raises( + TypeError, + match=r"Received instead of cost object.", + ): + weighted_cost = pybop.WeightedCost("Invalid string") + with pytest.raises( + TypeError, + match="Expected a list or array of weights the same length as costs.", + ): + weighted_cost = pybop.WeightedCost(cost1, cost2, weights="Invalid string") + with pytest.raises( + ValueError, + match="Expected a list or array of weights the same length as costs.", + ): + weighted_cost = pybop.WeightedCost(cost1, cost2, weights=[1]) + + # Test with and without different problems + weight = 100 + weighted_cost_2 = pybop.WeightedCost(cost1, cost2, weights=[1, weight]) + assert weighted_cost_2._different_problems is False + assert weighted_cost_2._fixed_problem is True + assert weighted_cost_2.problem is problem + assert weighted_cost_2([0.5]) >= 0 + np.testing.assert_allclose( + weighted_cost_2.evaluate([0.6]), + cost1([0.6]) + weight * cost2([0.6]), + atol=1e-5, + ) + + cost3 = pybop.RootMeanSquaredError(copy(problem)) + weighted_cost_3 = pybop.WeightedCost(cost1, cost3, weights=[1, weight]) + assert weighted_cost_3._different_problems is True + assert weighted_cost_3._fixed_problem is False + assert weighted_cost_3.problem is None + assert weighted_cost_3([0.5]) >= 0 + np.testing.assert_allclose( + weighted_cost_3.evaluate([0.6]), + cost1([0.6]) + weight * cost3([0.6]), + atol=1e-5, + ) + + errors_2, sensitivities_2 = weighted_cost_2.evaluateS1([0.5]) + errors_3, sensitivities_3 = weighted_cost_3.evaluateS1([0.5]) + np.testing.assert_allclose(errors_2, errors_3, atol=1e-5) + np.testing.assert_allclose(sensitivities_2, sensitivities_3, atol=1e-5) + + @pytest.mark.unit + def test_weighted_design_cost(self, design_problem): + cost1 = pybop.GravimetricEnergyDensity(design_problem) + cost2 = pybop.RootMeanSquaredError(design_problem) + + # Test with and without weights + weighted_cost = pybop.WeightedCost(cost1, cost2) + assert weighted_cost._different_problems is False + assert weighted_cost._fixed_problem is False + assert weighted_cost.problem is design_problem + assert weighted_cost([0.5]) >= 0 + np.testing.assert_allclose( + weighted_cost.evaluate([0.6]), + cost1([0.6]) + cost2([0.6]), + atol=1e-5, + ) From d9b718b47fda338f3e4bc50eebc8f75a2dd768e1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:04:18 +0000 Subject: [PATCH 41/41] chore: update pre-commit hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.2 → v0.5.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.2...v0.5.4) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 28055658..2cc81860 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.5.2" + rev: "v0.5.4" hooks: - id: ruff args: [--fix, --show-fixes]