Skip to content

Conversation

@SarahRo
Copy link
Contributor

@SarahRo SarahRo commented Nov 24, 2025

Description

This PR introduces a clearer distinction between priors and distributions used for sampling initial conditions.
Moreover, the Parameter object is replaced with a ParameterInfo object and two subclasses (ParameterBounds and ParameterDistribution). This ensures that distributions cannot be set with conflicting bounds. If bounds are needed the distribution itself has to be bounded (for example, by using scipy.stats.truncate).

Renaming of classes and their methods and properties:

  • BasePrior -> Distribution
  • JointPrior -> JointDistribution
  • Parameter -> ParameterInfo
  • Parameter.sample_from_prior -> ParameterInfo.sample_from_distribution
  • Parameter._prior-> ParameterInfo._distribution
  • Parameter.prior -> ParameterInfo.distribution
  • Parameters.sample_from_priors -> Parameters.sample_from_distributions
  • Parameters.priors ->Parameters.distributions

Changes to structure:

Parameter is replaced with a base class (ParameterInfo) and two subclasses (ParameterDistribution and ParameterBounds).

The constructor for ParameterInfo takes neither a distribution (formerly prior) nor bounds as input. Bounds can be forced by using the set_bounds method.

A ParameterDistribution object requires a distribution as input (either of type scipy.stats.distributions.rv_frozen or pybop.Distribution), but does not take bounds as input. Bounds are extracted from the distribution using the support method from scipy.stats. Conflicting bounds can still be forced using the set_bounds method.

A ParameterBounds object requires bounds as input. A distribution cannot be set by the user. For finite bounds, a uniform distribution is set automatically.

Changes to functionality:

  • The pybop.Distribution object (formerly pybop.BasePrior) is now constructed using a distribution of type scipy.stats.distributions.rv_frozen rather than scipy.stats.rv_continuous. This means all parameters of the distribution are fixed by the user before passing it to the constructor of the class. The class no longer holds the values for the loc and scale paremeters. This was necessary to allow for distributions that have additional input parameters (e.g. scipy.stats.truncnorm).
  • A constructor for pybop.Distribution is now implemented. This allows a user to initialise a pybop.Distribution with any distribution of type scipy.stats.distributions.rv_frozen and use a finite difference approximation of the derivative of the log distribution.
  • Distribution.mean is now a method rather than a property so that it matches the same method for scipy.stats.distributions.rv_frozen.
  • Distribution.sigma is replaced with the method Distribution.std, to match scipy.stats.distribtuions.rv_frozen
  • Distribution.support replace Distribution.bounds, again to match scipy.stats.distributions.rv_frozen.
  • __repr__ has been modified for pybop.Distribution and its subclasses to match information held by the class.
  • A ParameterDistribution object can be initialised with either a pybop.Distribution or a scipy.stats.distribution.rv_frozen. Previously, a Parameter required a BasePrior (now Distribution).
  • pybop.Gaussian can now also be used for a truncated version of the normal distribution by providing the input truncated_at. The underlying distribution in this case is scipy.stats.truncnorm.

Potential issues with these changes:

  • Having a Distribution and a ParameterDistribution object might cause confusion.
  • Rather than introducing ParameterDistribution and ParameterBounds as subclasses, it would also be possible to simply modify the constructor of ParameterInfo to only allow either bounds or a distribution input. This could reduce complexity. The question here is, does the distinction between ParameterDistribution and ParameterBounds improve clarity or does it just introduce unnecessary complexity?

Fixes # (issue)

Type of change

Please add a line in the relevant section of CHANGELOG.md to document the change (include PR #).

Important checks:

Please confirm the following before marking the PR as ready for review:

  • No style issues: $ pre-commit run or $ nox -s pre-commit (see CONTRIBUTING.md for how to set this up to run automatically when committing locally, in just two lines of code)
  • All tests pass: nox -s tests
  • The documentation builds: nox -s doctest
  • Code is commented for hard-to-understand areas
  • Tests added that prove fix is effective or that feature works

@codecov
Copy link

codecov bot commented Nov 24, 2025

Codecov Report

❌ Patch coverage is 97.65625% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.46%. Comparing base (4628a65) to head (27bd4e9).
⚠️ Report is 3 commits behind head on develop.

Files with missing lines Patch % Lines
pybop/applications/ocp_methods.py 0.00% 1 Missing ⚠️
pybop/parameters/parameter.py 98.07% 1 Missing ⚠️
pybop/problems/problem.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #839      +/-   ##
===========================================
+ Coverage    89.41%   89.46%   +0.04%     
===========================================
  Files           63       63              
  Lines         4857     4880      +23     
===========================================
+ Hits          4343     4366      +23     
  Misses         514      514              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@NicolaCourtier
Copy link
Member

NicolaCourtier commented Nov 28, 2025

Thank you @SarahRo, this is definitely going in the right direction. Thanks for highlighting two potential issues, I would say:

  1. Distribution can be moved out of the main namespace and become pybop.parameters.Distribution or similar. (Only needed if we keep ParameterDistribution...)
  2. You've found a nice way of setting bounds which, I think, would allow us to move back to pybop.Parameter(initial_value, distribution, bounds). As long as these options are passed together on construction, the result is consistent, but we will need to upgrade (or just remove) the post-set-up set_bounds and remove_bounds functions.

Also, please remove the margin attribute and the clip functions (hopefully no longer needed) and point the Parameter.__call__ function at the _initial_value instead of the deprecated _current_value. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants