From 524cb646b144eaaa2f4f4a76f81f0edcdb611fe2 Mon Sep 17 00:00:00 2001 From: Michael Clerx Date: Wed, 4 Feb 2026 16:12:03 +0000 Subject: [PATCH 1/3] Added ProblemErrorMeasure.problem(). --- pints/_error_measures.py | 8 +++++++- pints/tests/test_error_measures.py | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/pints/_error_measures.py b/pints/_error_measures.py index 8f3956b4c..07813baf4 100644 --- a/pints/_error_measures.py +++ b/pints/_error_measures.py @@ -49,7 +49,7 @@ class ProblemErrorMeasure(ErrorMeasure): :class:`single` or :class:`multi-output` problems. """ - def __init__(self, problem=None): + def __init__(self, problem): super(ProblemErrorMeasure, self).__init__() self._problem = problem self._times = problem.times() @@ -62,6 +62,12 @@ def n_parameters(self): """ See :meth:`ErrorMeasure.n_parameters()`. """ return self._n_parameters + def problem(self): + """ + Returns the problem this error measure was defined on. + """ + return self._problem + class MeanSquaredError(ProblemErrorMeasure): r""" diff --git a/pints/tests/test_error_measures.py b/pints/tests/test_error_measures.py index 473c86f23..b1d8f1946 100755 --- a/pints/tests/test_error_measures.py +++ b/pints/tests/test_error_measures.py @@ -338,6 +338,14 @@ def test_bad_constructor(self): 'Number of weights must match number of problem outputs.', pints.MeanSquaredError, problem, weights) + def test_problem_access(self): + # Test underlying ProblemErrorMeasure method problem() + + problem = pints.SingleOutputProblem( + self.model_single, self.times, self.data_single) + error = pints.MeanSquaredError(problem) + self.assertIs(error.problem(), problem) + class TestNormalisedRootMeanSquaredError(unittest.TestCase): From 0e27f1e303689f74524f02ceb813c121ece36d98 Mon Sep 17 00:00:00 2001 From: Michael Clerx Date: Wed, 4 Feb 2026 16:53:52 +0000 Subject: [PATCH 2/3] Added ProblemLogLikelihood.problem() --- CHANGELOG.md | 1 + pints/_error_measures.py | 4 +--- pints/_log_pdfs.py | 4 ++++ pints/tests/test_error_measures.py | 20 ++++++++++++-------- pints/tests/test_log_likelihoods.py | 16 ++++++++++++++++ 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0addbf4de..d95b02e13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ## Unreleased ### Added +- [#1715](https://github.com/pints-team/pints/pull/1715) Added `ProblemErrorMeasure.problem()`, `ProblemLogLikelihood.problem()` ### Changed - [#1713](https://github.com/pints-team/pints/pull/1713) PINTS now requires matplotlib 2.2 or newer. ### Deprecated diff --git a/pints/_error_measures.py b/pints/_error_measures.py index 07813baf4..1885ff43d 100644 --- a/pints/_error_measures.py +++ b/pints/_error_measures.py @@ -63,9 +63,7 @@ def n_parameters(self): return self._n_parameters def problem(self): - """ - Returns the problem this error measure was defined on. - """ + """ Returns the problem this error measure was defined on. """ return self._problem diff --git a/pints/_log_pdfs.py b/pints/_log_pdfs.py index 5bd13cc99..c625f76c0 100644 --- a/pints/_log_pdfs.py +++ b/pints/_log_pdfs.py @@ -321,6 +321,7 @@ class ProblemLogLikelihood(LogPDF): ---------- problem The time-series problem this log-likelihood is defined for. + """ def __init__(self, problem): super(ProblemLogLikelihood, self).__init__() @@ -334,6 +335,9 @@ def n_parameters(self): """ See :meth:`LogPDF.n_parameters()`. """ return self._n_parameters + def problem(self): + return self._problem + class LogPosterior(LogPDF): """ diff --git a/pints/tests/test_error_measures.py b/pints/tests/test_error_measures.py index b1d8f1946..83f0a3a0a 100755 --- a/pints/tests/test_error_measures.py +++ b/pints/tests/test_error_measures.py @@ -338,14 +338,6 @@ def test_bad_constructor(self): 'Number of weights must match number of problem outputs.', pints.MeanSquaredError, problem, weights) - def test_problem_access(self): - # Test underlying ProblemErrorMeasure method problem() - - problem = pints.SingleOutputProblem( - self.model_single, self.times, self.data_single) - error = pints.MeanSquaredError(problem) - self.assertIs(error.problem(), problem) - class TestNormalisedRootMeanSquaredError(unittest.TestCase): @@ -504,6 +496,18 @@ def test_bad_constructor(self): pints.ProbabilityBasedError, MiniProblem()) +class TestProblemErrorMeasure(unittest.TestCase): + """ Tests shared methods of the ProblemErrorMeasure abstract class. """ + + def test_shared(self): + # Test underlying ProblemErrorMeasure method problem() + + problem = MiniProblem() + error = pints.MeanSquaredError(problem) + self.assertIs(error.problem(), problem) + self.assertEqual(error.n_parameters(), problem.n_parameters()) + + class TestRootMeanSquaredError(unittest.TestCase): @classmethod diff --git a/pints/tests/test_log_likelihoods.py b/pints/tests/test_log_likelihoods.py index 6105d0866..c817682a7 100755 --- a/pints/tests/test_log_likelihoods.py +++ b/pints/tests/test_log_likelihoods.py @@ -2572,6 +2572,22 @@ def test_negative_sd(self): self.assertEqual(log_likelihood([1, 1, 0]), -np.inf) +class TestProblemLogLikelihood(unittest.TestCase): + """ Test shared ProblemLogLikelihood methods. """ + + @classmethod + def setUpClass(cls): + cls.model = pints.toy.ConstantModel(1) + cls.times = np.array([1, 2, 3, 4]) + cls.data = np.asarray([1.9, 2.1, 1.8, 2.2]) + + def test_shared_methods(self): + problem = pints.SingleOutputProblem(self.model, self.times, self.data) + log_likelihood = pints.GaussianKnownSigmaLogLikelihood(problem, 1.5) + self.assertEqual(log_likelihood.n_parameters(), problem.n_parameters()) + self.assertIs(log_likelihood.problem(), problem) + + class TestScaledLogLikelihood(unittest.TestCase): @classmethod From 96fe8fde27a22c464e9f32d22ad703a417df3b18 Mon Sep 17 00:00:00 2001 From: Michael Clerx Date: Wed, 4 Feb 2026 17:00:32 +0000 Subject: [PATCH 3/3] Added Problem.model() methods --- CHANGELOG.md | 2 +- pints/_core.py | 12 +++++++++--- pints/tests/test_multi_output_problem.py | 1 + pints/tests/test_single_output_problem.py | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d95b02e13..eb7d8fddd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. ## Unreleased ### Added -- [#1715](https://github.com/pints-team/pints/pull/1715) Added `ProblemErrorMeasure.problem()`, `ProblemLogLikelihood.problem()` +- [#1715](https://github.com/pints-team/pints/pull/1715) Added methods `ProblemErrorMeasure.problem()`, `ProblemLogLikelihood.problem()`, `SingleOutputProblem.model()` and `MultiOutputProblem.model()`. ### Changed - [#1713](https://github.com/pints-team/pints/pull/1713) PINTS now requires matplotlib 2.2 or newer. ### Deprecated diff --git a/pints/_core.py b/pints/_core.py index adba9b54a..d474b7f25 100644 --- a/pints/_core.py +++ b/pints/_core.py @@ -170,10 +170,12 @@ def evaluateS1(self, parameters): np.asarray(dy).reshape((self._n_times, self._n_parameters)) ) + def model(self): + """ Returns the :class:`ForwardModel` underlying this problem. """ + return self._model + def n_outputs(self): - """ - Returns the number of outputs for this problem (always 1). - """ + """ Returns the number of outputs for this problem (always 1). """ return 1 def n_parameters(self): @@ -281,6 +283,10 @@ def evaluateS1(self, parameters): self._n_times, self._n_outputs, self._n_parameters) ) + def model(self): + """ Returns the :class:`ForwardModel` underlying this problem. """ + return self._model + def n_outputs(self): """ Returns the number of outputs for this problem. diff --git a/pints/tests/test_multi_output_problem.py b/pints/tests/test_multi_output_problem.py index bc8fb38d8..23d0921a0 100755 --- a/pints/tests/test_multi_output_problem.py +++ b/pints/tests/test_multi_output_problem.py @@ -35,6 +35,7 @@ def test_basics(self): self.assertEqual(problem.n_parameters(), model.n_parameters(), 2) self.assertEqual(problem.n_outputs(), model.n_outputs(), 3) self.assertEqual(problem.n_times(), len(times)) + self.assertIs(problem.model(), model) # Test errors times[0] = -2 diff --git a/pints/tests/test_single_output_problem.py b/pints/tests/test_single_output_problem.py index 099284df2..b6d82c27b 100755 --- a/pints/tests/test_single_output_problem.py +++ b/pints/tests/test_single_output_problem.py @@ -32,6 +32,7 @@ def test_basics(self): self.assertEqual(problem.n_parameters(), model.n_parameters(), 2) self.assertEqual(problem.n_outputs(), model.n_outputs(), 1) self.assertEqual(problem.n_times(), len(times)) + self.assertIs(problem.model(), model) # Test errors times[0] = -2