diff --git a/pints/_log_likelihoods.py b/pints/_log_likelihoods.py index fa4397bdb..04e528039 100644 --- a/pints/_log_likelihoods.py +++ b/pints/_log_likelihoods.py @@ -236,6 +236,22 @@ def __call__(self, x): - np.sum(np.log(1 + (error / sigma)**2), axis=0) ) + def evaluate_pointwise_loglikelihoods(self, x): + """ + See :meth:`ProblemLogLikelihood.evaluate_pointwise_loglikelihoods()`. + """ + m = self._no + + # problem parameters + problem_parameters = x[:-m] + error = self._values - self._problem.evaluate(problem_parameters) + + # Distribution parameters + sigma = np.asarray(x[-m:]) + + # Calculate + return - np.log(np.pi) - np.log(sigma) - np.log(1 + (error / sigma)**2) + class ConstantAndMultiplicativeGaussianLogLikelihood( pints.ProblemLogLikelihood): @@ -352,6 +368,27 @@ def __call__(self, parameters): return log_likelihood + def evaluate_pointwise_loglikelihoods(self, x): + """ + See :meth:`ProblemLogLikelihood.evaluate_pointwise_loglikelihoods()`. + """ + noise_parameters = np.asarray(x[-self._np:]) + sigma_base = noise_parameters[:self._no] + eta = noise_parameters[self._no:2 * self._no] + sigma_rel = noise_parameters[2 * self._no:] + + # Evaluate function and compute intermediate values + function_values = self._problem.evaluate(x[:-self._np]) + error = self._values - function_values + sigma_tot = sigma_base + sigma_rel * function_values**eta + + # Compute the pointwise log-likelihoods for each observation + pointwise = (- 0.5 * np.log(2 * np.pi) + - np.log(sigma_tot) + - 0.5 * error**2 / sigma_tot**2) + + return pointwise + def evaluateS1(self, parameters): r""" See :meth:`LogPDF.evaluateS1()`. @@ -642,8 +679,8 @@ def __init__(self, problem, sigma): raise ValueError('Standard deviation must be greater than zero.') # Pre-calculate parts - self._offset = -0.5 * self._nt * np.log(2 * np.pi) - self._offset -= self._nt * np.log(sigma) + self._offset_no_sum = -0.5 * np.log(2 * np.pi) - np.log(sigma) + self._offset = self._offset_no_sum * self._nt self._multip = -1 / (2.0 * sigma**2) # Pre-calculate S1 parts @@ -653,6 +690,13 @@ def __call__(self, x): error = self._values - self._problem.evaluate(x) return np.sum(self._offset + self._multip * np.sum(error**2, axis=0)) + def evaluate_pointwise_loglikelihoods(self, x): + """ + See :meth:`ProblemLogLikelihood.evaluate_pointwise_loglikelihoods()`. + """ + error = self._values - self._problem.evaluate(x) + return self._offset_no_sum + self._multip * error**2 + def evaluateS1(self, x): """ See :meth:`LogPDF.evaluateS1()`. """ # Evaluate, and get residuals @@ -739,6 +783,22 @@ def __call__(self, x): return np.sum(- self._logn - self._nt * np.log(sigma) - np.sum(error**2, axis=0) / (2 * sigma**2)) + def evaluate_pointwise_loglikelihoods(self, x): + """ + See :meth:`ProblemLogLikelihood.evaluate_pointwise_loglikelihoods()`. + """ + sigma = np.asarray(x[-self._no:]) + + # Compute intermediate values + error = self._values - self._problem.evaluate(x[:-self._no]) + + # Compute the pointwise log-likelihoods for each observation + pointwise = (-0.5 * np.log(2 * np.pi) + - np.log(sigma) + - error**2 / (2 * sigma**2)) + + return pointwise + def evaluateS1(self, x): """ See :meth:`LogPDF.evaluateS1()`. """ sigma = np.asarray(x[-self._no:]) @@ -874,6 +934,25 @@ def __call__(self, x): return log_likelihood + def evaluate_pointwise_loglikelihoods(self, x): + """ + See :meth:`ProblemLogLikelihood.evaluate_pointwise_loglikelihoods()`. + """ + noise_parameters = np.asarray(x[-self._np:]) + eta = np.asarray(noise_parameters[0::2]) + sigma = np.asarray(noise_parameters[1::2]) + + # Compute intermediate values + function_values = self._problem.evaluate(x[:-self._np]) + error = self._values - function_values + sigma_tot = function_values**eta * sigma + + # Compute the pointwise log-likelihoods for each observation + pointwise = (-0.5 * np.log(2 * np.pi) + - np.log(sigma_tot) + - error**2 / (2 * sigma_tot**2)) + return pointwise + class ScaledLogLikelihood(pints.ProblemLogLikelihood): """ @@ -988,6 +1067,27 @@ def __call__(self, x): - 0.5 * (1 + nu) * np.sum(np.log(nu + (error / sigma)**2), axis=0) ) + def evaluate_pointwise_loglikelihoods(self, x): + """ + See :meth:`ProblemLogLikelihood.evaluate_pointwise_loglikelihoods()`. + """ + m = 2 * self._no + + # problem parameters + problem_parameters = x[:-m] + error = self._values - self._problem.evaluate(problem_parameters) + + # Distribution parameters + parameters = x[-m:] + nu = np.asarray(parameters[0::2]) + sigma = np.asarray(parameters[1::2]) + + # Calculate + return (0.5 * nu * np.log(nu) + - np.log(sigma) + - np.log(scipy.special.beta(0.5 * nu, 0.5)) + - 0.5 * (1 + nu) * np.log(nu + (error / sigma)**2)) + class UnknownNoiseLogLikelihood(GaussianLogLikelihood): """ diff --git a/pints/_log_pdfs.py b/pints/_log_pdfs.py index 98334d149..ce7f18b6a 100644 --- a/pints/_log_pdfs.py +++ b/pints/_log_pdfs.py @@ -336,6 +336,18 @@ def n_parameters(self): """ See :meth:`LogPDF.n_parameters()`. """ return self._n_parameters + def evaluate_pointwise_loglikelihoods(self, x): + """ + Evaluates the Log-likelihood at each observation for the given + parameters, x. Returns a numpy array of length no. timepoints if no. + outputs = 1. Otherwise returns a 2d array of size no. timepoints by no. + outputs. + + *This is an optional method that is not always implemented.* + """ + + raise NotImplementedError + class LogPosterior(LogPDF): """ diff --git a/pints/tests/test_log_likelihoods.py b/pints/tests/test_log_likelihoods.py index 67b66fb52..4432472da 100755 --- a/pints/tests/test_log_likelihoods.py +++ b/pints/tests/test_log_likelihoods.py @@ -277,6 +277,95 @@ def test_call_two_dim_array_multi(self): # Check that likelihood returns expected value self.assertEqual(score, -49.51182454195375) + def test_evaluate_pointwise_loglikelihoods_list(self): + # Convert data to list + values = self.data_single.tolist() + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.CauchyLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 10] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_one_dim_array(self): + # Convert data to array of shape (n_times,) + values = np.reshape(self.data_single, (self.n_times,)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.CauchyLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 10] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_single(self): + # Convert data to array of shape (n_times, 1) + values = np.reshape(self.data_single, (self.n_times, 1)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.CauchyLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 10] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_multi(self): + # Create an object with links to the model and time series + problem = pints.MultiOutputProblem( + self.model_multi, self.times, self.data_multi) + + # Create log_likelihood + likelihood = pints.CauchyLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 0, 0, 0, 13, 8, 13.5, 10.5] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, self.data_multi.shape) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + class TestConstantAndMultiplicativeGaussianLogLikelihood(unittest.TestCase): @@ -448,6 +537,100 @@ def test_call_multiplicative_gaussian_log_likelihood_agrees_multi(self): multi_score = multi_log_likelihood(multi_test_parameters) self.assertAlmostEqual(score, multi_score) + def test_evaluate_pointwise_loglikelihoods_list(self): + # Convert data to list + values = self.data_single.tolist() + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.ConstantAndMultiplicativeGaussianLogLikelihood( + problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 0.5, 1.1, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_one_dim_array(self): + # Convert data to array of shape (n_times,) + values = np.reshape(self.data_single, (self.n_times,)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.ConstantAndMultiplicativeGaussianLogLikelihood( + problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 0.5, 1.1, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_single(self): + # Convert data to array of shape (n_times, 1) + values = np.reshape(self.data_single, (self.n_times, 1)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.ConstantAndMultiplicativeGaussianLogLikelihood( + problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 0.5, 1.1, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_multi(self): + # Create an object with links to the model and time series + problem = pints.MultiOutputProblem( + self.model_multi, self.times, self.data_multi) + + # Create log_likelihood + likelihood = pints.ConstantAndMultiplicativeGaussianLogLikelihood( + problem) + + # Evaluate likelihood for test parameters + parameters = [ + 2.0, 2.0, 2.0, 0.5, 0.5, 0.5, 1.1, 1.1, 1.1, 1.0, 1.0, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, self.data_multi.shape) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + def test_evaluateS1_list(self): # Convert data to list values = self.data_single.tolist() @@ -999,6 +1182,95 @@ def test_call_two_dim_array_multi(self): # Check that likelihood returns expected value self.assertEqual(score, -196.9122623984561) + def test_evaluate_pointwise_loglikelihoods_list(self): + # Convert data to list + values = self.data_single.tolist() + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.GaussianKnownSigmaLogLikelihood(problem, 1.5) + + # Evaluate likelihood for test parameters + parameters = [-1] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_one_dim_array(self): + # Convert data to array of shape (n_times,) + values = np.reshape(self.data_single, (self.n_times,)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.GaussianKnownSigmaLogLikelihood(problem, 1.5) + + # Evaluate likelihood for test parameters + parameters = [-1] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_single(self): + # Convert data to array of shape (n_times, 1) + values = np.reshape(self.data_single, (self.n_times, 1)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.GaussianKnownSigmaLogLikelihood(problem, 1.5) + + # Evaluate likelihood for test parameters + parameters = [-1] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_multi(self): + # Create an object with links to the model and time series + problem = pints.MultiOutputProblem( + self.model_multi, self.times, self.data_multi) + + # Create log_likelihood + likelihood = pints.GaussianKnownSigmaLogLikelihood(problem, 1) + + # Evaluate likelihood for test parameters + parameters = [0, 0, 0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, self.data_multi.shape) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + def test_evaluateS1_list(self): # Convert data to list values = self.data_single.tolist() @@ -1243,6 +1515,95 @@ def test_call_two_dim_array_multi(self): # Check that likelihood returns expected value self.assertEqual(score, -50.75425117450455) + def test_evaluate_pointwise_loglikelihoods_list(self): + # Convert data to list + values = self.data_single.tolist() + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.GaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2, self.sigma] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_one_dim_array(self): + # Convert data to array of shape (n_times,) + values = np.reshape(self.data_single, (self.n_times,)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.GaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2, self.sigma] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_single(self): + # Convert data to array of shape (n_times, 1) + values = np.reshape(self.data_single, (self.n_times, 1)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.GaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2, self.sigma] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_multi(self): + # Create an object with links to the model and time series + problem = pints.MultiOutputProblem( + self.model_multi, self.times, self.data_multi) + + # Create log_likelihood + likelihood = pints.GaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 0, 0, 0.1, 0.1, 0.1] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, self.data_multi.shape) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + def test_evaluateS1_list(self): # Convert data to list values = self.data_single.tolist() @@ -1518,6 +1879,95 @@ def test_call_two_dim_array_multi(self): # Check that likelihood returns expected value self.assertEqual(score, -46.324126706784014) + def test_evaluate_pointwise_loglikelihoods_list(self): + # Convert data to list + values = self.data_single.tolist() + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.MultiplicativeGaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 2.0, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_one_dim_array(self): + # Convert data to array of shape (n_times,) + values = np.reshape(self.data_single, (self.n_times,)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.MultiplicativeGaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 2.0, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_single(self): + # Convert data to array of shape (n_times, 1) + values = np.reshape(self.data_single, (self.n_times, 1)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.MultiplicativeGaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 2.0, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_multi(self): + # Create an object with links to the model and time series + problem = pints.MultiOutputProblem( + self.model_multi, self.times, self.data_multi) + + # Create log_likelihood + likelihood = pints.MultiplicativeGaussianLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [2.0, 2.0, 2.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, self.data_multi.shape) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + class TestScaledLogLikelihood(unittest.TestCase): @@ -1884,6 +2334,95 @@ def test_call_two_dim_array_multi(self): # Check that scaled likelihood returns expected value self.assertEqual(score, -47.83720347766944) + def test_evaluate_pointwise_loglikelihoods_list(self): + # Convert data to list + values = self.data_single.tolist() + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.StudentTLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 3, 10] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_one_dim_array(self): + # Convert data to array of shape (n_times,) + values = np.reshape(self.data_single, (self.n_times,)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.StudentTLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 3, 10] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_single(self): + # Convert data to array of shape (n_times, 1) + values = np.reshape(self.data_single, (self.n_times, 1)) + + # Create an object with links to the model and time series + problem = pints.SingleOutputProblem( + self.model_single, self.times, values) + + # Create log_likelihood + likelihood = pints.StudentTLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 3, 10] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, (self.n_times,)) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + + def test_evaluate_pointwise_loglikelihoods_two_dim_array_multi(self): + # Create an object with links to the model and time series + problem = pints.MultiOutputProblem( + self.model_multi, self.times, self.data_multi) + + # Create log_likelihood + likelihood = pints.StudentTLogLikelihood(problem) + + # Evaluate likelihood for test parameters + parameters = [0, 0, 0, 0, 2, 13, 1, 8, 2.5, 13.5, 3.4, 10.5] + pointwise = likelihood.evaluate_pointwise_loglikelihoods(parameters) + + # Check that the loglikelihoods match the shape of the data + self.assertEqual(pointwise.shape, self.data_multi.shape) + + # Check that the sum of the pointwise loglikelihoods agrees with call + # There are floating point deviations because the summation occurs at + # different points in __call__() + self.assertAlmostEqual(np.sum(pointwise), likelihood(parameters)) + if __name__ == '__main__': unittest.main()