@@ -1026,13 +1026,17 @@ def _validate(self) -> Self:
10261026 return self
10271027
10281028 @property
1029- def prior_dist (self ) -> Distribution :
1030- """Get the prior distribution of the parameter."""
1031- if self .estimate is False :
1029+ def prior_dist (self ) -> Distribution | None :
1030+ """Get the prior distribution of the parameter.
1031+
1032+ :return: The prior distribution of the parameter, or None if no prior
1033+ distribution is set.
1034+ """
1035+ if not self .estimate :
10321036 raise ValueError (f"Parameter `{ self .id } ' is not estimated." )
10331037
10341038 if self .prior_distribution is None :
1035- return Uniform ( self . lb , self . ub )
1039+ return None
10361040
10371041 if not (cls := _prior_to_cls .get (self .prior_distribution )):
10381042 raise ValueError (
@@ -1820,12 +1824,66 @@ def x_fixed_indices(self) -> list[int]:
18201824 """Parameter table non-estimated parameter indices."""
18211825 return [i for i , p in enumerate (self .parameters ) if not p .estimate ]
18221826
1827+ @property
1828+ def has_map_objective (self ) -> bool :
1829+ """Whether this problem encodes a maximum a posteriori (MAP) objective.
1830+
1831+ A PEtab problem is considered to have a MAP objective if there is a
1832+ prior distribution specified for at least one estimated parameter.
1833+
1834+ :returns: ``True`` if MAP objective, ``False`` otherwise.
1835+ """
1836+ return any (
1837+ p .prior_distribution is not None
1838+ for p in self .parameters
1839+ if p .estimate
1840+ )
1841+
1842+ @property
1843+ def has_ml_objective (self ) -> bool :
1844+ """Whether this problem encodes a maximum likelihood (ML) objective.
1845+
1846+ A PEtab problem is considered to have an ML objective if there are no
1847+ prior distributions specified for any estimated parameters.
1848+
1849+ :returns: ``True`` if ML objective, ``False`` otherwise.
1850+ """
1851+ return not self .has_map_objective
1852+
18231853 def get_priors (self ) -> dict [str , Distribution ]:
18241854 """Get prior distributions.
18251855
1826- :returns: The prior distributions for the estimated parameters.
1856+ Note that this will default to uniform distributions over the
1857+ parameter bounds for parameters without an explicit prior.
1858+
1859+ :returns: The prior distributions for the estimated parameters in case
1860+ the problem has a MAP objective, an empty dictionary otherwise.
1861+ """
1862+ if not self .has_map_objective :
1863+ return {}
1864+
1865+ return {
1866+ p .id : p .prior_dist if p .prior_distribution else Uniform (p .lb , p .ub )
1867+ for p in self .parameters
1868+ if p .estimate
1869+ }
1870+
1871+ def get_startpoint_distributions (self ) -> dict [str , Distribution ]:
1872+ """Get distributions for sampling startpoints.
1873+
1874+ The distributions are the prior distributions for estimated parameters
1875+ that have a prior distribution defined, and uniform distributions
1876+ over the parameter bounds for estimated parameters without an explicit
1877+ prior.
1878+
1879+ :returns: Mapping of parameter IDs to distributions for sampling
1880+ startpoints.
18271881 """
1828- return {p .id : p .prior_dist for p in self .parameters if p .estimate }
1882+ return {
1883+ p .id : p .prior_dist if p .prior_distribution else Uniform (p .lb , p .ub )
1884+ for p in self .parameters
1885+ if p .estimate
1886+ }
18291887
18301888 def sample_parameter_startpoints (self , n_starts : int = 100 , ** kwargs ):
18311889 """Create 2D array with starting points for optimization"""
0 commit comments