From 8e951a5e197588680899c4c70a725208f1402bb6 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Mon, 18 Oct 2021 10:51:13 +0100 Subject: [PATCH 01/18] Update _core.py --- pints/_core.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pints/_core.py b/pints/_core.py index adba9b54a..575c4c69b 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -321,6 +321,26 @@ def values(self): return self._values +class ProblemCollection(object): + """ + Represents an inference problem where a model is fit to a multi-valued time + series, such as measured from a system with multiple outputs, where the + different time series are potentially measured at different time intervals. + + This class is also of use for + + Parameters + ---------- + model + A model or model wrapper extending :class:`ForwardModel`. + times + A sequence of points in time. Must be non-negative and non-decreasing. + values + A sequence of multi-valued measurements. Must have shape + ``(n_times, n_outputs)``, where ``n_times`` is the number of points in + ``times`` and ``n_outputs`` is the number of outputs in the model. + """ + class TunableMethod(object): """ From e3169ff07196f4cb145a7a86f54b3ab04f803706 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Mon, 18 Oct 2021 16:58:06 +0100 Subject: [PATCH 02/18] outline of class initialisation for problemcollection --- pints/__init__.py | 2 +- pints/_core.py | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/pints/__init__.py b/pints/__init__.py index df559710b..b075bc683 100644 --- a/pints/__init__.py +++ b/pints/__init__.py @@ -66,7 +66,7 @@ def version(formatted=False): # from ._core import ForwardModel, ForwardModelS1 from ._core import TunableMethod -from ._core import SingleOutputProblem, MultiOutputProblem +from ._core import SingleOutputProblem, MultiOutputProblem, ProblemCollection # # Utility classes and methods diff --git a/pints/_core.py b/pints/_core.py index 575c4c69b..0e06bdc76 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -327,19 +327,44 @@ class ProblemCollection(object): series, such as measured from a system with multiple outputs, where the different time series are potentially measured at different time intervals. - This class is also of use for + This class is also of use when different outputs are modelled with + different likelihoods or score functions. Parameters ---------- model A model or model wrapper extending :class:`ForwardModel`. - times - A sequence of points in time. Must be non-negative and non-decreasing. - values - A sequence of multi-valued measurements. Must have shape - ``(n_times, n_outputs)``, where ``n_times`` is the number of points in - ``times`` and ``n_outputs`` is the number of outputs in the model. + args + Consecutive times, values lists for each output chunk. For example, + times_1, values_1, times_2, values_2: where times_1 = [1.2, 2.5, 3] and + values_1 = [2.3, 4.5, 4.5]; times_2 = [4, 5, 6, 7] and + values_2 = [[3.4, 1.1, 0.5, 0.6], [1.2, 3.3, 4.5, 5.5]]. """ + def __init__(self, model, *args): + if len(args) < 2: + raise ValueError('Must supply at least one time series.') + if len(args) % 2 != 0: + raise ValueError( + 'Must supply times and values for each time series.') + self._timeses = [] + self._valueses = [] + self._output_indices = [] + + k = 0 + for i in range(len(args) // 2): + times = np.array(args[k]) + times_shape = times.shape + if len(times_shape) != 1: + raise ValueError('Times must be one-dimensional.') + values = np.array(args[k + 1]) + values_shape = values.shape + if values_shape[0] != times_shape[0]: + raise ValueError('Outputs must be of same length as times.') + self._timeses.append(times) + self._valueses.append(values) + self._output_indices.extend([i] * values_shape[1]) + self._times_all = np.sort(list(set(np.concatenate(self._timeses)))) + class TunableMethod(object): From d37cfd04c8acff52727809dd90f7109db58fa82b Mon Sep 17 00:00:00 2001 From: ben18785 Date: Tue, 19 Oct 2021 11:28:21 +0100 Subject: [PATCH 03/18] added subproblem method --- pints/_core.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pints/_core.py b/pints/_core.py index 0e06bdc76..e5c8254c9 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -341,6 +341,8 @@ class ProblemCollection(object): values_2 = [[3.4, 1.1, 0.5, 0.6], [1.2, 3.3, 4.5, 5.5]]. """ def __init__(self, model, *args): + + self._model = model if len(args) < 2: raise ValueError('Must supply at least one time series.') if len(args) % 2 != 0: @@ -351,7 +353,8 @@ def __init__(self, model, *args): self._output_indices = [] k = 0 - for i in range(len(args) // 2): + self._num_output_sets = len(args) // 2 + for i in range(self._num_output_sets): times = np.array(args[k]) times_shape = times.shape if len(times_shape) != 1: @@ -365,6 +368,21 @@ def __init__(self, model, *args): self._output_indices.extend([i] * values_shape[1]) self._times_all = np.sort(list(set(np.concatenate(self._timeses)))) + def subproblem(self, index): + """ + Creates a `pints.Problem` corresponding to a particular output index. + """ + if index >= self._num_output_sets: + raise ValueError('Index must be less than number of output sets.') + + times = self._times[index] + values = self._valueses[index] + if len(values.shape) == 1: + problem = pints.SingleOutputProblem(self._model, times, values) + else: + problem = pints.MultiOutputProblem(self._model, times, values) + return problem + class TunableMethod(object): From f3d31f724370da989115cc9a16a9abd8c885c672 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Tue, 19 Oct 2021 13:08:37 +0100 Subject: [PATCH 04/18] basic structure of subproblem added --- pints/__init__.py | 7 ++- pints/_core.py | 147 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 143 insertions(+), 11 deletions(-) diff --git a/pints/__init__.py b/pints/__init__.py index b075bc683..44abfe2e2 100644 --- a/pints/__init__.py +++ b/pints/__init__.py @@ -66,7 +66,12 @@ def version(formatted=False): # from ._core import ForwardModel, ForwardModelS1 from ._core import TunableMethod -from ._core import SingleOutputProblem, MultiOutputProblem, ProblemCollection +from ._core import ( + SingleOutputProblem, + MultiOutputProblem, + ProblemCollection, + SubProblem, +) # # Utility classes and methods diff --git a/pints/_core.py b/pints/_core.py index e5c8254c9..5db8d687e 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -321,6 +321,132 @@ def values(self): return self._values +class SubProblem(object): + """ + Represents an inference problem for a subset of outputs from a multi-output + model. This is likely to be used either when the measurement times across + some outputs are irregular or different outputs require different objective + functions (i.e. log-likelihoods or score functions). + + Parameters + ---------- + model + A model or model wrapper extending :class:`ForwardModel`. + times + A sequence of points in time. Must be non-negative and non-decreasing. + values + Can either be a one-dimensional sequence of scalar output values + measured at the times in ``times``; or a sequence of multi-valued + measurements with shape ``(n_times, n_outputs)``, where ``n_times`` is + the number of points in ``times`` and ``n_outputs`` is the number of + outputs in the model. + """ + def __init__(self, model, times, values): + + # Check model + self._model = model + + # Check times, copy so that they can no longer be changed and set them + # to read-only + self._times = pints.vector(times) + if np.any(self._times < 0): + raise ValueError('Times cannot be negative.') + if np.any(self._times[:-1] > self._times[1:]): + raise ValueError('Times must be non-decreasing.') + + self._n_parameters = int(model.n_parameters()) + self._n_times = len(self._times) + + values = np.array(values) + values_shape = values.shape + if len(values_shape) == 1: + self._n_outputs = 1 + + # Check values, copy so that they can no longer be changed + self._values = pints.vector(values) + + # Check times and values array have right shape + if len(self._values) != self._n_times: + raise ValueError( + 'Times and values arrays must have same length.') + else: + self._n_outputs = values_shape[1] + self._values = pints.matrix2d(values) + + # Check for correct shape + if self._values.shape != (self._n_times, self._n_outputs): + raise ValueError( + 'Values array must have shape `(n_times, n_outputs)`.') + + def evaluate(self, parameters): + """ + Runs a simulation using the given parameters, returning the simulated + values. + + The returned data is a NumPy array with shape ``(n_times, n_outputs)``. + """ + y = np.asarray(self._model.simulate(parameters, self._times)) + return y.reshape(self._n_times, self._n_outputs) + + def evaluateS1(self, parameters): + """ + Runs a simulation using the given parameters, returning the simulated + values. + + The returned data is a tuple of NumPy arrays ``(y, y')``, where ``y`` + has shape ``(n_times, n_outputs)``, while ``y'`` has shape + ``(n_times, n_outputs, n_parameters)``. + + *This method only works for problems whose model implements the + :class:`ForwardModelS1` interface.* + """ + y, dy = self._model.simulateS1(parameters, self._times) + return ( + np.asarray(y).reshape(self._n_times, self._n_outputs), + np.asarray(dy).reshape( + self._n_times, self._n_outputs, self._n_parameters) + ) + + def n_outputs(self): + """ + Returns the number of outputs for this problem. + """ + return self._n_outputs + + def n_parameters(self): + """ + Returns the dimension (the number of parameters) of this problem. + """ + return self._n_parameters + + def n_times(self): + """ + Returns the number of sampling points, i.e. the length of the vectors + returned by :meth:`times()` and :meth:`values()`. + """ + return self._n_times + + def times(self): + """ + Returns this problem's times. + + The returned value is a read-only NumPy array of shape + ``(n_times, n_outputs)``, where ``n_times`` is the number of time + points and ``n_outputs`` is the number of outputs. + """ + return self._times + + def values(self): + """ + Returns this problem's values. + + The returned value is a read-only NumPy array of shape + ``(n_times, n_outputs)``, where ``n_times`` is the number of time + points and ``n_outputs`` is the number of outputs. + """ + return self._values + + class ProblemCollection(object): """ Represents an inference problem where a model is fit to a multi-valued time @@ -353,8 +479,8 @@ def __init__(self, model, *args): self._output_indices = [] k = 0 - self._num_output_sets = len(args) // 2 - for i in range(self._num_output_sets): + self._n_output_sets = len(args) // 2 + for i in range(self._n_output_sets): times = np.array(args[k]) times_shape = times.shape if len(times_shape) != 1: @@ -365,23 +491,24 @@ def __init__(self, model, *args): raise ValueError('Outputs must be of same length as times.') self._timeses.append(times) self._valueses.append(values) - self._output_indices.extend([i] * values_shape[1]) + if len(values_shape) > 1: + n_outputs = values_shape[1] + else: + n_outputs = 1 + self._output_indices.extend([i] * n_outputs) + k += 2 self._times_all = np.sort(list(set(np.concatenate(self._timeses)))) def subproblem(self, index): """ Creates a `pints.Problem` corresponding to a particular output index. """ - if index >= self._num_output_sets: + if index >= self._n_output_sets: raise ValueError('Index must be less than number of output sets.') - times = self._times[index] + times = self._timeses[index] values = self._valueses[index] - if len(values.shape) == 1: - problem = pints.SingleOutputProblem(self._model, times, values) - else: - problem = pints.MultiOutputProblem(self._model, times, values) - return problem + return pints.SubProblem(self._model, times, values) class TunableMethod(object): From 26b04450c9e366aecbccd1f53f367369c7863989 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Tue, 19 Oct 2021 13:31:27 +0100 Subject: [PATCH 05/18] Changed instantiation of subproblem so that it's done via the collection --- pints/__init__.py | 2 +- pints/_core.py | 35 ++++++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/pints/__init__.py b/pints/__init__.py index 44abfe2e2..75ae71467 100644 --- a/pints/__init__.py +++ b/pints/__init__.py @@ -70,7 +70,7 @@ def version(formatted=False): SingleOutputProblem, MultiOutputProblem, ProblemCollection, - SubProblem, + SubProblem ) # diff --git a/pints/_core.py b/pints/_core.py index 5db8d687e..9fdc9aa31 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -341,14 +341,19 @@ class SubProblem(object): the number of points in ``times`` and ``n_outputs`` is the number of outputs in the model. """ - def __init__(self, model, times, values): + def __init__(self, collection, index): - # Check model + # Get items from collection + model = collection.model() self._model = model + timeses = collection.timeses() + self._times = pints.vector(timeses[index]) + values = collection.valueses() + values = values[index] # Check times, copy so that they can no longer be changed and set them # to read-only - self._times = pints.vector(times) + if np.any(self._times < 0): raise ValueError('Times cannot be negative.') if np.any(self._times[:-1] > self._times[1:]): @@ -499,16 +504,32 @@ def __init__(self, model, *args): k += 2 self._times_all = np.sort(list(set(np.concatenate(self._timeses)))) + def _evaluate(self, parameters, index): + pass + + def _evaluateS1(self, parameters, index): + pass + + def model(self): + """ Returns forward model. """ + return self._model + def subproblem(self, index): """ - Creates a `pints.Problem` corresponding to a particular output index. + Creates a `pints.SubProblem` corresponding to a particular output + index. """ if index >= self._n_output_sets: raise ValueError('Index must be less than number of output sets.') + return pints.SubProblem(self, index) + + def timeses(self): + """ Returns list of times sequences: one for each output chunk. """ + return self._timeses - times = self._timeses[index] - values = self._valueses[index] - return pints.SubProblem(self._model, times, values) + def valueses(self): + """ Returns list of value chunks: one for each output chunk. """ + return self._valueses class TunableMethod(object): From e368198d7f2c82fd4ea419d1734fd905d27e5b11 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Tue, 19 Oct 2021 14:40:04 +0100 Subject: [PATCH 06/18] Update _core.py --- pints/_core.py | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/pints/_core.py b/pints/_core.py index 9fdc9aa31..2a214d9ba 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -344,6 +344,8 @@ class SubProblem(object): def __init__(self, collection, index): # Get items from collection + self._collection = collection + self._index = index model = collection.model() self._model = model timeses = collection.timeses() @@ -387,30 +389,15 @@ def evaluate(self, parameters): """ Runs a simulation using the given parameters, returning the simulated values. - - The returned data is a NumPy array with shape ``(n_times, n_outputs)``. """ - y = np.asarray(self._model.simulate(parameters, self._times)) - return y.reshape(self._n_times, self._n_outputs) + return self._collection._evaluate(parameters, self._index) def evaluateS1(self, parameters): """ Runs a simulation using the given parameters, returning the simulated values. - - The returned data is a tuple of NumPy arrays ``(y, y')``, where ``y`` - has shape ``(n_times, n_outputs)``, while ``y'`` has shape - ``(n_times, n_outputs, n_parameters)``. - - *This method only works for problems whose model implements the - :class:`ForwardModelS1` interface.* """ - y, dy = self._model.simulateS1(parameters, self._times) - return ( - np.asarray(y).reshape(self._n_times, self._n_outputs), - np.asarray(dy).reshape( - self._n_times, self._n_outputs, self._n_parameters) - ) + return self._collection._evaluateS1(parameters, self._index) def n_outputs(self): """ From eafe63a1ff0375a3e4b4c6d7fcd91cbd0047a87c Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 10:53:55 +0100 Subject: [PATCH 07/18] Update _core.py --- pints/_core.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/pints/_core.py b/pints/_core.py index 2a214d9ba..2a400f3e6 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -459,7 +459,6 @@ class ProblemCollection(object): values_2 = [[3.4, 1.1, 0.5, 0.6], [1.2, 3.3, 4.5, 5.5]]. """ def __init__(self, model, *args): - self._model = model if len(args) < 2: raise ValueError('Must supply at least one time series.') @@ -490,12 +489,72 @@ def __init__(self, model, *args): self._output_indices.extend([i] * n_outputs) k += 2 self._times_all = np.sort(list(set(np.concatenate(self._timeses)))) + self._output_indices = np.array(self._output_indices) + + # vars to handle caching across multiple output chunks + self._cached_output = None + self._cached_sensitivities = None + self._cached_parameters = None + + def _output_sorter(self, y, index): + """ + Returns output(s) corresponding to a given index at times corresponding + to that output. + """ + # lookup times in times array + times = self._timeses[index] + time_indices = [np.where(self._times_all == x)[0][0] for x in times] + + # find relevant output indices + output_indices = np.where(self._output_indices == index).tolist() + + y_short = y[time_indices, output_indices] + if len(y_short.shape) == 1: + y_short = y_short.reshape((len(self._timeses[index]),)) + return y_short + + def _output_and_sensitivity_sorter(self, y, dy, index): + """ + Returns output(s) corresponding to a given index at times corresponding + to that output. + """ + # lookup times in times array + times = self._timeses[index] + time_indices = [np.where(self._times_all == x)[0][0] for x in times] + + # find relevant output indices + output_indices = np.where(self._output_indices == index).tolist() + + y_short = y[time_indices, output_indices] + if len(y_short.shape) == 1: + y_short = y_short.reshape((len(self._timeses[index]),)) + + # sort sensitivities + dy_short = dy[time_indices, output_indices, :] + + return y_short, dy_short def _evaluate(self, parameters, index): - pass + """ Evaluates model or returns cached result. """ + parameters = pints.vector(parameters) + if not np.array_equal(self._cached_parameters, parameters): + y = np.asarray(self._model.simulate(parameters, self._times_all)) + self._cached_output = y + self._cached_parameters = parameters + + return self._output_sorter(self._cached_output, index) def _evaluateS1(self, parameters, index): - pass + """ Evaluates model with sensitivities or returns cached result. """ + parameters = pints.vector(parameters) + if not np.array_equal(self._cached_parameters, parameters): + y, dy = np.asarray( + self._model.simulateS1(parameters, self._times_all)) + self._cached_output = y + self._cached_sensitivities = dy + self._cached_parameters = parameters + return self._output_and_sensitivity_sorter( + self._cached_output, self._cached_sensitivities, index) def model(self): """ Returns forward model. """ From 974e0f00ea868e5eefd4593193a687a5e88ea69e Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 11:10:39 +0100 Subject: [PATCH 08/18] draft of subproblem and collection classes --- pints/_core.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pints/_core.py b/pints/_core.py index 2a400f3e6..ebb1dad6e 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -506,10 +506,12 @@ def _output_sorter(self, y, index): time_indices = [np.where(self._times_all == x)[0][0] for x in times] # find relevant output indices - output_indices = np.where(self._output_indices == index).tolist() + output_indices = np.where(self._output_indices == index)[0] - y_short = y[time_indices, output_indices] - if len(y_short.shape) == 1: + # pick rows then columns + y_short = y[time_indices, :] + y_short = y_short[:, output_indices] + if y_short.shape[1] == 1: y_short = y_short.reshape((len(self._timeses[index]),)) return y_short @@ -523,15 +525,17 @@ def _output_and_sensitivity_sorter(self, y, dy, index): time_indices = [np.where(self._times_all == x)[0][0] for x in times] # find relevant output indices - output_indices = np.where(self._output_indices == index).tolist() + output_indices = np.where(self._output_indices == index)[0] - y_short = y[time_indices, output_indices] - if len(y_short.shape) == 1: + # pick rows then columns + y_short = y[time_indices, :] + y_short = y_short[:, output_indices] + if y_short.shape[1] == 1: y_short = y_short.reshape((len(self._timeses[index]),)) # sort sensitivities - dy_short = dy[time_indices, output_indices, :] - + dy_short = dy[time_indices, :, :] + dy_short = dy_short[:, output_indices, :] return y_short, dy_short def _evaluate(self, parameters, index): @@ -548,8 +552,7 @@ def _evaluateS1(self, parameters, index): """ Evaluates model with sensitivities or returns cached result. """ parameters = pints.vector(parameters) if not np.array_equal(self._cached_parameters, parameters): - y, dy = np.asarray( - self._model.simulateS1(parameters, self._times_all)) + y, dy = self._model.simulateS1(parameters, self._times_all) self._cached_output = y self._cached_sensitivities = dy self._cached_parameters = parameters From 539407e19861426ade3f1b4758148a626b2fdfcb Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 12:47:44 +0100 Subject: [PATCH 09/18] added example notebook for wragged output example --- examples/stats/multiple_wragged_outputs.ipynb | 424 ++++++++++++++++++ pints/_core.py | 1 - 2 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 examples/stats/multiple_wragged_outputs.ipynb diff --git a/examples/stats/multiple_wragged_outputs.ipynb b/examples/stats/multiple_wragged_outputs.ipynb new file mode 100644 index 000000000..7aa8923bd --- /dev/null +++ b/examples/stats/multiple_wragged_outputs.ipynb @@ -0,0 +1,424 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Multi-output model with wragged time measurements\n", + "\n", + "This notebook describes how to fit a model in PINTS which has multiple outputs with different measurement times for each. Additionally, we illustrate how this same framework allows different measurement models to be used for different outputs within the same forward model when doing inference.\n", + "\n", + "For this, we consider the [Goodwin-Oscillator](../toy/model-goodwin-oscillator.ipynb) model, which has three outputs." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import pints\n", + "import pints.toy as toy\n", + "import pints.plot\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import pints.noise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We first create some simulated data." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "model = pints.toy.GoodwinOscillatorModel()\n", + "real_parameters = model.suggested_parameters()\n", + "times = model.suggested_times()\n", + "values = model.simulate(real_parameters, times)\n", + "\n", + "# add noise\n", + "noise1 = 0.001\n", + "noise2 = 0.01\n", + "noise3 = 0.1\n", + "noisy_values = np.array(values, copy=True)\n", + "noisy_values[:, 0] += np.random.normal(0, noise1, len(times))\n", + "noisy_values[:, 1] += np.random.normal(0, noise2, len(times))\n", + "noisy_values[:, 2] += np.random.normal(0, noise3, len(times))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we assume that only the first half of time measurements are taken for the first two outputs; and only the second half for the third." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "times_12 = times[:100]\n", + "outputs_12 = noisy_values[:100, :2]\n", + "times_3 = times[100:]\n", + "outputs_3 = noisy_values[100:, 2]\n", + "\n", + "plt.figure()\n", + "plt.subplot(3, 1, 1)\n", + "plt.plot(times_12, outputs_12[:, 0], 'b')\n", + "plt.subplot(3, 1, 2)\n", + "plt.plot(times_12, outputs_12[:, 1], 'g')\n", + "plt.subplot(3, 1, 3)\n", + "plt.plot(times_3, outputs_3, 'r')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We now create a `ProblemCollection` that allows us to construct problems for each of the outputs." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "collection = pints.ProblemCollection(model, times_12, outputs_12,\n", + " times_3, outputs_3)\n", + "problem_0 = collection.subproblem(0)\n", + "problem_1 = collection.subproblem(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each problem has a method for solving the ODE system." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# solve system\n", + "sol_12 = problem_0.evaluate(real_parameters)\n", + "\n", + "## this uses cached result from first solve under the hood\n", + "sol_3 = problem_1.evaluate(real_parameters)\n", + "\n", + "# overlay ODE solutions over noisy data\n", + "plt.figure()\n", + "plt.subplot(3, 1, 1)\n", + "plt.plot(times_12, outputs_12[:, 0], 'b')\n", + "plt.plot(times_12, sol_12[:, 0])\n", + "plt.subplot(3, 1, 2)\n", + "plt.plot(times_12, outputs_12[:, 1], 'g')\n", + "plt.plot(times_12, sol_12[:, 1])\n", + "plt.subplot(3, 1, 3)\n", + "plt.plot(times_3, outputs_3, 'r')\n", + "plt.plot(times_3, sol_3)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Inference\n", + "We now describe how we can perform inference for the overall system. Here, we assume different likelihoods for each of the two output chunks." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "log_likelihood_0 = pints.GaussianKnownSigmaLogLikelihood(problem_0, [noise1, noise2])\n", + "log_likelihood_1 = pints.GaussianLogLikelihood(problem_1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This means that each of our log-likelihoods have a different number of parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "log_likelihood_0.n_parameters()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "log_likelihood_1.n_parameters()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then aggregate these into a single callable object that we can use for inference." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class CombinedLogLikelihood(pints.LogPDF):\n", + " def __init__(self, log_likelihood_0, log_likelihood_1):\n", + " self._log_likelihood_0 = log_likelihood_0\n", + " self._log_likelihood_1 = log_likelihood_1\n", + "\n", + " def __call__(self, x):\n", + " return log_likelihood_0(x[:5]) + log_likelihood_1(x)\n", + "\n", + " def n_parameters(self):\n", + " return 6\n", + " \n", + "combined_log_likelihood = CombinedLogLikelihood(log_likelihood_0, log_likelihood_1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using this, we now perform maximum likelihood estimation." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Maximising LogPDF\n", + "Using Covariance Matrix Adaptation Evolution Strategy (CMA-ES)\n", + "Running in sequential mode.\n", + "Population size: 9\n", + "Iter. Eval. Best Time m:s\n", + "0 9 -72.07721 0:00.2\n", + "1 18 561.0911 0:00.2\n", + "2 27 561.0911 0:00.3\n", + "3 36 925.9462 0:00.4\n", + "20 189 962.1331 0:01.4\n", + "40 369 967.3481 0:02.7\n", + "60 549 968.1981 0:03.9\n", + "80 729 968.257 0:05.1\n", + "100 909 968.2647 0:06.4\n", + "120 1089 968.3049 0:07.7\n", + "140 1269 968.3356 0:09.0\n", + "160 1449 968.3361 0:10.2\n", + "180 1629 968.3361 0:11.5\n", + "200 1809 968.3361 0:12.8\n", + "220 1989 968.3361 0:14.0\n", + "240 2169 968.3361 0:15.3\n", + "260 2349 968.3361 0:16.6\n", + "280 2529 968.3361 0:17.8\n", + "300 2709 968.3361 0:19.1\n", + "320 2889 968.3361 0:20.4\n", + "340 3069 968.3361 0:21.6\n", + "360 3249 968.3361 0:22.9\n", + "380 3429 968.3361 0:24.2\n", + "400 3609 968.3361 0:25.4\n", + "420 3789 968.3361 0:26.7\n", + "440 3969 968.3361 0:28.0\n", + "460 4149 968.3361 0:29.2\n", + "480 4329 968.3361 0:30.5\n", + "486 4374 968.3361 0:30.8\n", + "Halting: No significant change for 200 iterations.\n" + ] + } + ], + "source": [ + "# Define some boundaries\n", + "boundaries = pints.RectangularBoundaries([-5, -5], [5, 5])\n", + "\n", + "# Pick an initial point at actual parameter values\n", + "x0 = real_parameters.tolist() + [noise3]\n", + "\n", + "# And run!\n", + "xbest, fbest = pints.optimise(combined_log_likelihood, x0)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.97637205, 4.11780789, 0.11608288, 0.08182563, 0.1016085 ,\n", + " 0.09407352])" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "xbest" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now overlaying the estimated ODE solutions on top of the data." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# solve system\n", + "sol_12_a = problem_0.evaluate(xbest[:5])\n", + "\n", + "## this uses cached result from first solve under the hood\n", + "sol_3_a = problem_1.evaluate(xbest[:5])\n", + "\n", + "# overlay ODE solutions over noisy data\n", + "plt.figure()\n", + "plt.subplot(3, 1, 1)\n", + "plt.plot(times_12, outputs_12[:, 0], 'b')\n", + "plt.plot(times_12, sol_12[:, 0])\n", + "plt.plot(times_12, sol_12_a[:, 0])\n", + "plt.subplot(3, 1, 2)\n", + "plt.plot(times_12, outputs_12[:, 1], 'g')\n", + "plt.plot(times_12, sol_12[:, 1])\n", + "plt.plot(times_12, sol_12_a[:, 1])\n", + "plt.subplot(3, 1, 3)\n", + "plt.plot(times_3, outputs_3, 'r')\n", + "plt.plot(times_3, sol_3)\n", + "plt.plot(times_3, sol_3_a)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The estimated solutions are close to the true solutions." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/pints/_core.py b/pints/_core.py index ebb1dad6e..5383798ae 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -545,7 +545,6 @@ def _evaluate(self, parameters, index): y = np.asarray(self._model.simulate(parameters, self._times_all)) self._cached_output = y self._cached_parameters = parameters - return self._output_sorter(self._cached_output, index) def _evaluateS1(self, parameters, index): From 4f04acaeb4ffc67317918a22e8355a5a9eda7997 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 12:52:48 +0100 Subject: [PATCH 10/18] Updated docstrings --- pints/_core.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/pints/_core.py b/pints/_core.py index 5383798ae..e24085b8d 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -325,21 +325,16 @@ class SubProblem(object): """ Represents an inference problem for a subset of outputs from a multi-output model. This is likely to be used either when the measurement times across - some outputs are irregular or different outputs require different objective + outputs are differ or when different outputs require different objective functions (i.e. log-likelihoods or score functions). Parameters ---------- - model - A model or model wrapper extending :class:`ForwardModel`. - times - A sequence of points in time. Must be non-negative and non-decreasing. - values - Can either be a one-dimensional sequence of scalar output values - measured at the times in ``times``; or a sequence of multi-valued - measurements with shape ``(n_times, n_outputs)``, where ``n_times`` is - the number of points in ``times`` and ``n_outputs`` is the number of - outputs in the model. + collection + An object of :class:`ProblemCollection`. + index + An integer index corresponding to the particular output chunk in the + collection. """ def __init__(self, collection, index): @@ -442,8 +437,9 @@ def values(self): class ProblemCollection(object): """ Represents an inference problem where a model is fit to a multi-valued time - series, such as measured from a system with multiple outputs, where the - different time series are potentially measured at different time intervals. + series, such as when measured from a system with multiple outputs, where + the different time series are potentially measured at different time + intervals. This class is also of use when different outputs are modelled with different likelihoods or score functions. @@ -517,8 +513,8 @@ def _output_sorter(self, y, index): def _output_and_sensitivity_sorter(self, y, dy, index): """ - Returns output(s) corresponding to a given index at times corresponding - to that output. + Returns output(s) and sensitivities corresponding to a given index at + times corresponding to that output. """ # lookup times in times array times = self._timeses[index] From 15233ef6ea7db48b5a6111d0b8a410b81867c7dd Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 13:49:43 +0100 Subject: [PATCH 11/18] starting to add tests --- pints/_core.py | 226 +++++++++--------- .../test_subproblem_and_problemcollection.py | 95 ++++++++ 2 files changed, 208 insertions(+), 113 deletions(-) create mode 100644 pints/tests/test_subproblem_and_problemcollection.py diff --git a/pints/_core.py b/pints/_core.py index e24085b8d..9ebd980ad 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -321,119 +321,6 @@ def values(self): return self._values -class SubProblem(object): - """ - Represents an inference problem for a subset of outputs from a multi-output - model. This is likely to be used either when the measurement times across - outputs are differ or when different outputs require different objective - functions (i.e. log-likelihoods or score functions). - - Parameters - ---------- - collection - An object of :class:`ProblemCollection`. - index - An integer index corresponding to the particular output chunk in the - collection. - """ - def __init__(self, collection, index): - - # Get items from collection - self._collection = collection - self._index = index - model = collection.model() - self._model = model - timeses = collection.timeses() - self._times = pints.vector(timeses[index]) - values = collection.valueses() - values = values[index] - - # Check times, copy so that they can no longer be changed and set them - # to read-only - - if np.any(self._times < 0): - raise ValueError('Times cannot be negative.') - if np.any(self._times[:-1] > self._times[1:]): - raise ValueError('Times must be non-decreasing.') - - self._n_parameters = int(model.n_parameters()) - self._n_times = len(self._times) - - values = np.array(values) - values_shape = values.shape - if len(values_shape) == 1: - self._n_outputs = 1 - - # Check values, copy so that they can no longer be changed - self._values = pints.vector(values) - - # Check times and values array have right shape - if len(self._values) != self._n_times: - raise ValueError( - 'Times and values arrays must have same length.') - else: - self._n_outputs = values_shape[1] - self._values = pints.matrix2d(values) - - # Check for correct shape - if self._values.shape != (self._n_times, self._n_outputs): - raise ValueError( - 'Values array must have shape `(n_times, n_outputs)`.') - - def evaluate(self, parameters): - """ - Runs a simulation using the given parameters, returning the simulated - values. - """ - return self._collection._evaluate(parameters, self._index) - - def evaluateS1(self, parameters): - """ - Runs a simulation using the given parameters, returning the simulated - values. - """ - return self._collection._evaluateS1(parameters, self._index) - - def n_outputs(self): - """ - Returns the number of outputs for this problem. - """ - return self._n_outputs - - def n_parameters(self): - """ - Returns the dimension (the number of parameters) of this problem. - """ - return self._n_parameters - - def n_times(self): - """ - Returns the number of sampling points, i.e. the length of the vectors - returned by :meth:`times()` and :meth:`values()`. - """ - return self._n_times - - def times(self): - """ - Returns this problem's times. - - The returned value is a read-only NumPy array of shape - ``(n_times, n_outputs)``, where ``n_times`` is the number of time - points and ``n_outputs`` is the number of outputs. - """ - return self._times - - def values(self): - """ - Returns this problem's values. - - The returned value is a read-only NumPy array of shape - ``(n_times, n_outputs)``, where ``n_times`` is the number of time - points and ``n_outputs`` is the number of outputs. - """ - return self._values - - class ProblemCollection(object): """ Represents an inference problem where a model is fit to a multi-valued time @@ -576,6 +463,119 @@ def valueses(self): return self._valueses +class SubProblem(object): + """ + Represents an inference problem for a subset of outputs from a multi-output + model. This is likely to be used either when the measurement times across + outputs are differ or when different outputs require different objective + functions (i.e. log-likelihoods or score functions). + + Parameters + ---------- + collection + An object of :class:`ProblemCollection`. + index + An integer index corresponding to the particular output chunk in the + collection. + """ + def __init__(self, collection, index): + + # Get items from collection + self._collection = collection + self._index = index + model = collection.model() + self._model = model + timeses = collection.timeses() + self._times = pints.vector(timeses[index]) + values = collection.valueses() + values = values[index] + + # Check times, copy so that they can no longer be changed and set them + # to read-only + + if np.any(self._times < 0): + raise ValueError('Times cannot be negative.') + if np.any(self._times[:-1] > self._times[1:]): + raise ValueError('Times must be non-decreasing.') + + self._n_parameters = int(model.n_parameters()) + self._n_times = len(self._times) + + values = np.array(values) + values_shape = values.shape + if len(values_shape) == 1: + self._n_outputs = 1 + + # Check values, copy so that they can no longer be changed + self._values = pints.vector(values) + + # Check times and values array have right shape + if len(self._values) != self._n_times: + raise ValueError( + 'Times and values arrays must have same length.') + else: + self._n_outputs = values_shape[1] + self._values = pints.matrix2d(values) + + # Check for correct shape + if self._values.shape != (self._n_times, self._n_outputs): + raise ValueError( + 'Values array must have shape `(n_times, n_outputs)`.') + + def evaluate(self, parameters): + """ + Runs a simulation using the given parameters, returning the simulated + values. + """ + return self._collection._evaluate(parameters, self._index) + + def evaluateS1(self, parameters): + """ + Runs a simulation using the given parameters, returning the simulated + values. + """ + return self._collection._evaluateS1(parameters, self._index) + + def n_outputs(self): + """ + Returns the number of outputs for this problem. + """ + return self._n_outputs + + def n_parameters(self): + """ + Returns the dimension (the number of parameters) of this problem. + """ + return self._n_parameters + + def n_times(self): + """ + Returns the number of sampling points, i.e. the length of the vectors + returned by :meth:`times()` and :meth:`values()`. + """ + return self._n_times + + def times(self): + """ + Returns this problem's times. + + The returned value is a read-only NumPy array of shape + ``(n_times, n_outputs)``, where ``n_times`` is the number of time + points and ``n_outputs`` is the number of outputs. + """ + return self._times + + def values(self): + """ + Returns this problem's values. + + The returned value is a read-only NumPy array of shape + ``(n_times, n_outputs)``, where ``n_times`` is the number of time + points and ``n_outputs`` is the number of outputs. + """ + return self._values + + class TunableMethod(object): """ diff --git a/pints/tests/test_subproblem_and_problemcollection.py b/pints/tests/test_subproblem_and_problemcollection.py new file mode 100644 index 000000000..cfa586a50 --- /dev/null +++ b/pints/tests/test_subproblem_and_problemcollection.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# Tests SubProblem and ProblemCollection classes +# +# This file is part of PINTS (https://github.com/pints-team/pints/) which is +# released under the BSD 3-clause license. See accompanying LICENSE.md for +# copyright notice and full license details. +# +import pints +import pints.toy +import numpy as np +import unittest + + +class TestSubProblem(unittest.TestCase): + """ + Tests SubProblem methods. + """ + @classmethod + def setUpClass(cls): + """ Prepare problem for tests. """ + + model = pints.toy.GoodwinOscillatorModel() + real_parameters = model.suggested_parameters() + times = model.suggested_times() + values = model.simulate(real_parameters, times) + cls.model = model + cls.real_parameters = real_parameters + cls.times = times + + # add noise + noise1 = 0.001 + noise2 = 0.01 + noise3 = 0.1 + noisy_values = np.array(values, copy=True) + noisy_values[:, 0] += np.random.normal(0, noise1, len(times)) + noisy_values[:, 1] += np.random.normal(0, noise2, len(times)) + noisy_values[:, 2] += np.random.normal(0, noise3, len(times)) + + cls.times_12 = times[:100] + cls.outputs_12 = noisy_values[:100, :2] + cls.times_3 = times[100:] + cls.outputs_3 = noisy_values[100:, 2] + + def test_problem_collection_methods(self): + # Tests problem collection + + collection = pints.ProblemCollection( + self.model, self.times_12, self.outputs_12, self.times_3, + self.outputs_3) + + # check overall times + timeses = collection.timeses() + times_stack = [self.times_12, self.times_3] + k = 0 + for times in timeses: + self.assertTrue(np.array_equal(times, times_stack[k])) + k += 1 + + # check overall values + valueses = collection.valueses() + values_stack = [self.outputs_12, self.outputs_3] + k = 0 + for values in valueses: + self.assertTrue(np.array_equal(values, values_stack[k])) + k += 1 + + # test subproblem classes + problem_0 = collection.subproblem(0) + problem_1 = collection.subproblem(1) + + self.assertTrue(isinstance(problem_0, pints.SubProblem)) + self.assertTrue(isinstance(problem_1, pints.SubProblem)) + + # check model returned + model = collection.model() + self.assertTrue(isinstance(model, pints.ForwardModelS1)) + + def test_problem_collection_errors(self): + # Tests problem collection errors + + # supplied no data? + self.assertRaisesRegex( + ValueError, 'Must supply at least one time series.', + pints.ProblemCollection, self.model) + + # supplied only a times vector without data + self.assertRaisesRegex( + ValueError, 'Must supply times and values for each time series.', + pints.ProblemCollection, self.model, self.times_12, + self.outputs_12, self.times_3) + + +if __name__ == '__main__': + unittest.main() From 405c2e11374f7a6f7508280a4d9eb2855b9666bf Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 14:42:31 +0100 Subject: [PATCH 12/18] first draft of tests --- pints/_core.py | 31 ++---- .../test_subproblem_and_problemcollection.py | 105 +++++++++++++++++- 2 files changed, 114 insertions(+), 22 deletions(-) diff --git a/pints/_core.py b/pints/_core.py index 9ebd980ad..0abef0bd9 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -419,6 +419,10 @@ def _output_and_sensitivity_sorter(self, y, dy, index): # sort sensitivities dy_short = dy[time_indices, :, :] dy_short = dy_short[:, output_indices, :] + + if len(output_indices) == 1: + dy_short = dy_short.reshape( + len(time_indices), 1, dy_short.shape[2]) return y_short, dy_short def _evaluate(self, parameters, index): @@ -433,7 +437,10 @@ def _evaluate(self, parameters, index): def _evaluateS1(self, parameters, index): """ Evaluates model with sensitivities or returns cached result. """ parameters = pints.vector(parameters) - if not np.array_equal(self._cached_parameters, parameters): + + # extra or here catches if evaluate has been called before evaluateS1 + if (not np.array_equal(self._cached_parameters, parameters) or + self._cached_sensitivities is None): y, dy = self._model.simulateS1(parameters, self._times_all) self._cached_output = y self._cached_sensitivities = dy @@ -490,38 +497,24 @@ def __init__(self, collection, index): values = collection.valueses() values = values[index] - # Check times, copy so that they can no longer be changed and set them - # to read-only - - if np.any(self._times < 0): - raise ValueError('Times cannot be negative.') - if np.any(self._times[:-1] > self._times[1:]): - raise ValueError('Times must be non-decreasing.') - self._n_parameters = int(model.n_parameters()) self._n_times = len(self._times) values = np.array(values) values_shape = values.shape + + # here don't check array sizes as this will be done in the + # problemcollection.subproblem method if len(values_shape) == 1: self._n_outputs = 1 - # Check values, copy so that they can no longer be changed + # copy so that they can no longer be changed self._values = pints.vector(values) - # Check times and values array have right shape - if len(self._values) != self._n_times: - raise ValueError( - 'Times and values arrays must have same length.') else: self._n_outputs = values_shape[1] self._values = pints.matrix2d(values) - # Check for correct shape - if self._values.shape != (self._n_times, self._n_outputs): - raise ValueError( - 'Values array must have shape `(n_times, n_outputs)`.') - def evaluate(self, parameters): """ Runs a simulation using the given parameters, returning the simulated diff --git a/pints/tests/test_subproblem_and_problemcollection.py b/pints/tests/test_subproblem_and_problemcollection.py index cfa586a50..4263e259b 100644 --- a/pints/tests/test_subproblem_and_problemcollection.py +++ b/pints/tests/test_subproblem_and_problemcollection.py @@ -12,9 +12,9 @@ import unittest -class TestSubProblem(unittest.TestCase): +class TestProblemCollection(unittest.TestCase): """ - Tests SubProblem methods. + Tests ProblemCollection methods. """ @classmethod def setUpClass(cls): @@ -84,12 +84,111 @@ def test_problem_collection_errors(self): ValueError, 'Must supply at least one time series.', pints.ProblemCollection, self.model) - # supplied only a times vector without data + # supplied only a times vector without data? self.assertRaisesRegex( ValueError, 'Must supply times and values for each time series.', pints.ProblemCollection, self.model, self.times_12, self.outputs_12, self.times_3) + # supplied a 2d time vector? + self.assertRaisesRegex( + ValueError, 'Times must be one-dimensional.', + pints.ProblemCollection, self.model, self.outputs_12, + self.outputs_12) + + # supplied times that aren't same length as outputs? + self.assertRaisesRegex( + ValueError, 'Outputs must be of same length as times.', + pints.ProblemCollection, self.model, [1, 2, 3, 4, 5], + self.outputs_12) + + +class TestSubProblem(unittest.TestCase): + """ + Tests SubProblem methods. + """ + @classmethod + def setUpClass(cls): + """ Prepare problem for tests. """ + + model = pints.toy.GoodwinOscillatorModel() + real_parameters = model.suggested_parameters() + times = model.suggested_times() + values = model.simulate(real_parameters, times) + cls.model = model + cls.real_parameters = real_parameters + cls.times = times + cls.values = values + + cls.times_12 = times[:100] + cls.outputs_12 = values[:100, :2] + cls.times_3 = times[100:] + cls.outputs_3 = values[100:, 2] + + collection = pints.ProblemCollection( + cls.model, cls.times_12, cls.outputs_12, cls.times_3, + cls.outputs_3) + cls.problem_0 = collection.subproblem(0) + cls.problem_1 = collection.subproblem(1) + + # also solve using sensitivity methods as ODE solution (due to + # numerics) very slightly different + val_s, dy = model.simulateS1(real_parameters, times) + + cls.outputs_12_s = val_s[:100, :2] + cls.outputs_3_s = val_s[100:, 2] + + cls.dy_0 = dy[:100, :2, :] + + # reshape output here otherwise loses a dimension + dy_1 = dy[100:, 2, :] + cls.dy_1 = dy_1.reshape(100, 1, 5) + + def test_evaluate(self): + # Tests that chunked solution same as splitting overall into bits + + sol_0 = self.problem_0.evaluate(self.real_parameters) + sol_1 = self.problem_1.evaluate(self.real_parameters) + + self.assertTrue(np.array_equal(sol_0, self.outputs_12)) + self.assertTrue(np.array_equal(sol_1, self.outputs_3)) + + def test_evaluateS1(self): + # Tests that chunked solution and sens same as splitting overall + + sol_0, dy_0 = self.problem_0.evaluateS1(self.real_parameters) + sol_1, dy_1 = self.problem_1.evaluateS1(self.real_parameters) + + self.assertTrue(np.array_equal(sol_0, self.outputs_12_s)) + self.assertTrue(np.array_equal(sol_1, self.outputs_3_s)) + self.assertTrue(np.array_equal(self.dy_0, dy_0)) + self.assertTrue(np.array_equal(self.dy_1, dy_1)) + + def test_methods(self): + # Tests methods return appropriate values + + # outputs correct? + self.assertEqual(self.problem_0.n_outputs(), 2) + self.assertEqual(self.problem_1.n_outputs(), 1) + + # parameters correct? + self.assertEqual(self.problem_0.n_parameters(), 5) + self.assertEqual(self.problem_1.n_parameters(), 5) + + # times correct? + self.assertTrue(np.array_equal(self.times_12, self.problem_0.times())) + self.assertTrue(np.array_equal(self.times_3, self.problem_1.times())) + + # n_times correct? + self.assertEqual(len(self.times_12), self.problem_0.n_times()) + self.assertEqual(len(self.times_3), self.problem_1.n_times()) + + # values correct? + self.assertTrue(np.array_equal( + self.outputs_12, self.problem_0.values())) + self.assertTrue(np.array_equal( + self.outputs_3, self.problem_1.values())) + if __name__ == '__main__': unittest.main() From a6cfdeb52547aa57b89f59dc69eac53ad74964c0 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 14:51:44 +0100 Subject: [PATCH 13/18] Added wragged outputs notebook to readme --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index dcd063b8a..412b5d0f1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -99,6 +99,7 @@ relevant code. - [Multiplicative Gaussian noise](./stats/multiplicative-gaussian-errors.ipynb) - [Pooling parameters](./stats/pooling.ipynb) - [Student-t noise model](./stats/student-t-sampling-error.ipynb) +- [Wragged output model](./stats/multiple_wragged_outputs.ipynb) ## Toy problems From 062f167d38d196c076421fd93d0a72966521b69a Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 20 Oct 2021 18:21:59 +0100 Subject: [PATCH 14/18] Changes to docstrings --- docs/source/core_classes_and_methods.rst | 14 ++++++++++---- .../tests/test_subproblem_and_problemcollection.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/source/core_classes_and_methods.rst b/docs/source/core_classes_and_methods.rst index b49a62f25..aa3aae6e3 100644 --- a/docs/source/core_classes_and_methods.rst +++ b/docs/source/core_classes_and_methods.rst @@ -4,8 +4,9 @@ Core classes and methods .. currentmodule:: pints -Pints provides the :class:`SingleOutputProblem` and -:class:`MultiOutputProblem` classes to formulate +Pints provides the :class:`SingleOutputProblem`, +:class:`MultiOutputProblem`, :class:`ProblemCollection` +and :class:`SubProblem` classes to formulate inverse problems based on time series data and :class:`ForwardModel`. @@ -14,7 +15,9 @@ Overview: - :class:`ForwardModel` - :class:`ForwardModelS1` - :class:`MultiOutputProblem` +- :class:`ProblemCollection` - :class:`SingleOutputProblem` +- :class:`SubProblem` - :class:`TunableMethod` - :func:`version` @@ -36,7 +39,10 @@ Forward model with sensitivities Problems ******** -.. autoclass:: SingleOutputProblem - .. autoclass:: MultiOutputProblem +.. autoclass:: ProblemCollection + +.. autoclass:: SingleOutputProblem + +.. autoclass:: SubProblem diff --git a/pints/tests/test_subproblem_and_problemcollection.py b/pints/tests/test_subproblem_and_problemcollection.py index 4263e259b..6005af4f3 100644 --- a/pints/tests/test_subproblem_and_problemcollection.py +++ b/pints/tests/test_subproblem_and_problemcollection.py @@ -102,6 +102,16 @@ def test_problem_collection_errors(self): pints.ProblemCollection, self.model, [1, 2, 3, 4, 5], self.outputs_12) + # selected index exceeding number of output chunks? + collection = pints.ProblemCollection( + self.model, self.times_12, self.outputs_12, self.times_3, + self.outputs_3) + + self.assertRaisesRegex( + ValueError, 'Index must be less than number of output sets.', + collection.subproblem, 2 + ) + class TestSubProblem(unittest.TestCase): """ From 4da42851b20ca881c2ab1e8d56e46ffeddd6ced0 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 15 Dec 2021 14:33:51 +0000 Subject: [PATCH 15/18] Update _core.py --- pints/_core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pints/_core.py b/pints/_core.py index 0abef0bd9..7188c73e9 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -343,6 +343,7 @@ class ProblemCollection(object): """ def __init__(self, model, *args): self._model = model + print("pints length = ", len(args)) if len(args) < 2: raise ValueError('Must supply at least one time series.') if len(args) % 2 != 0: From 622476146c6833879b800c363ab3dc1986992035 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 15 Dec 2021 14:34:08 +0000 Subject: [PATCH 16/18] Update _core.py --- pints/_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pints/_core.py b/pints/_core.py index 7188c73e9..0abef0bd9 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -343,7 +343,6 @@ class ProblemCollection(object): """ def __init__(self, model, *args): self._model = model - print("pints length = ", len(args)) if len(args) < 2: raise ValueError('Must supply at least one time series.') if len(args) % 2 != 0: From a536fd84dad96034e4d40acdf606c224ce111888 Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 15 Dec 2021 15:50:32 +0000 Subject: [PATCH 17/18] works for single output problems --- pints/_core.py | 20 +++++-- .../test_subproblem_and_problemcollection.py | 57 +++++++++++++++++++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/pints/_core.py b/pints/_core.py index 0abef0bd9..51cd53ae2 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -392,6 +392,8 @@ def _output_sorter(self, y, index): output_indices = np.where(self._output_indices == index)[0] # pick rows then columns + if y.ndim == 1: + y = np.expand_dims(y, axis=1) y_short = y[time_indices, :] y_short = y_short[:, output_indices] if y_short.shape[1] == 1: @@ -411,18 +413,24 @@ def _output_and_sensitivity_sorter(self, y, dy, index): output_indices = np.where(self._output_indices == index)[0] # pick rows then columns + if y.ndim == 1: + y = np.expand_dims(y, axis=1) y_short = y[time_indices, :] y_short = y_short[:, output_indices] if y_short.shape[1] == 1: y_short = y_short.reshape((len(self._timeses[index]),)) # sort sensitivities - dy_short = dy[time_indices, :, :] - dy_short = dy_short[:, output_indices, :] - - if len(output_indices) == 1: - dy_short = dy_short.reshape( - len(time_indices), 1, dy_short.shape[2]) + # if only single problem output + if dy.ndim == 2: + dy_short = dy[time_indices, :] + # multi-output problem + else: + dy_short = dy[time_indices, :, :] + dy_short = dy_short[:, output_indices, :] + if len(output_indices) == 1: + dy_short = dy_short.reshape( + len(time_indices), 1, dy_short.shape[2]) return y_short, dy_short def _evaluate(self, parameters, index): diff --git a/pints/tests/test_subproblem_and_problemcollection.py b/pints/tests/test_subproblem_and_problemcollection.py index 6005af4f3..e1bcd92dd 100644 --- a/pints/tests/test_subproblem_and_problemcollection.py +++ b/pints/tests/test_subproblem_and_problemcollection.py @@ -76,6 +76,35 @@ def test_problem_collection_methods(self): model = collection.model() self.assertTrue(isinstance(model, pints.ForwardModelS1)) + def test_univariate_problem(self): + # checks that method works with univariate output problem + + model = pints.toy.LogisticModel() + real_parameters = [0.015, 500] + times = np.linspace(0, 1000, 1000) + values = model.simulate(real_parameters, times) + + noise = 10 + values += np.random.normal(0, noise, values.shape) + real_parameters.append(noise) + real_parameters = np.array(real_parameters) + + # check times and values returned + collection = pints.ProblemCollection( + model, times, values) + timeses = collection.timeses() + self.assertTrue(np.array_equal(timeses[0], times)) + valueses = collection.valueses() + self.assertTrue(np.array_equal(valueses[0], values)) + + # check subproblem can be made + problem_0 = collection.subproblem(0) + self.assertTrue(isinstance(problem_0, pints.SubProblem)) + + # check model returned + model = collection.model() + self.assertTrue(isinstance(model, pints.ForwardModelS1)) + def test_problem_collection_errors(self): # Tests problem collection errors @@ -199,6 +228,34 @@ def test_methods(self): self.assertTrue(np.array_equal( self.outputs_3, self.problem_1.values())) + def test_univariate_problem(self): + # Tests that subproblems work with a univariate output problem + + model = pints.toy.LogisticModel() + real_parameters = [0.015, 500] + times = np.linspace(0, 1000, 1000) + values = model.simulate(real_parameters, times) + + # check that same returned from subproblem as when making a + # single output problem + collection = pints.ProblemCollection( + model, times, values) + problem = pints.SingleOutputProblem( + model, times, values) + problem_0 = collection.subproblem(0) + sol_0 = problem_0.evaluate(real_parameters) + sol_1 = problem.evaluate(real_parameters) + self.assertTrue(np.array_equal(sol_0, sol_1)) + + # check same true for sensitivities + sol_0, dy_0 = problem_0.evaluateS1(real_parameters) + sol_1, dy_1 = problem.evaluateS1(real_parameters) + self.assertTrue(np.array_equal(sol_0, sol_1)) + self.assertTrue(np.array_equal(dy_0, dy_1)) + + + + if __name__ == '__main__': unittest.main() From d68edbb3bec36a7ba13071b424c160809446aa3a Mon Sep 17 00:00:00 2001 From: ben18785 Date: Wed, 15 Dec 2021 16:13:16 +0000 Subject: [PATCH 18/18] Update test_subproblem_and_problemcollection.py --- pints/tests/test_subproblem_and_problemcollection.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pints/tests/test_subproblem_and_problemcollection.py b/pints/tests/test_subproblem_and_problemcollection.py index e1bcd92dd..3dd83b00e 100644 --- a/pints/tests/test_subproblem_and_problemcollection.py +++ b/pints/tests/test_subproblem_and_problemcollection.py @@ -254,8 +254,5 @@ def test_univariate_problem(self): self.assertTrue(np.array_equal(dy_0, dy_1)) - - - if __name__ == '__main__': unittest.main()