1"""Joint probability distributions built from marginals and a copula.
3The C++ backend represents a joint law as a set of one-dimensional marginal
4distributions plus a copula. This Python module exposes wrappers and factory
5helpers to build such distributions from Python configuration objects.
8from __future__
import annotations
10from typing
import List, Optional, Sequence, Union
12from pyhyperiso.phyperiso.pyhyperiso
import statistic
as st
13from pyhyperiso.core.Statistic.Copula
import CopulaKind
14from pyhyperiso.core.Statistic.CopulaConfig
import (
15 GaussianCopulaConfigPy
as GaussianCopulaConfig,
16 StudentTCopulaConfigPy
as StudentTCopulaConfig,
18from pyhyperiso.core.Statistic.MarginalConfig
import (
20 GaussianMarginalConfig,
21 LikelihoodMarginalConfig,
23 SplitGaussianMarginalConfig,
27MarginalConfig = Union[
29 GaussianMarginalConfig,
30 SplitGaussianMarginalConfig,
31 LikelihoodMarginalConfig,
33CopulaConfig = Union[GaussianCopulaConfig, StudentTCopulaConfig]
37 """Validate the type of a user-facing argument.
40 value: Object to validate.
41 typ: Expected Python type.
42 name: Argument name used in the error message.
48 TypeError: If ``value`` is not an instance of ``typ``.
50 if not isinstance(value, typ):
51 raise TypeError(f
"{name} must be {typ.__name__}, received {type(value)!r}.")
56 """Convert a Python marginal kind to the C++ enum value."""
57 return _require(kind, MarginalKind,
"MarginalKind").to_cpp()
61 """Convert a Python copula kind to the C++ enum value."""
62 return _require(kind, CopulaKind,
"CopulaKind").to_cpp()
66 """Convert a Python marginal configuration to its C++ representation.
69 TypeError: If the configuration family is not supported.
75 GaussianMarginalConfig,
76 SplitGaussianMarginalConfig,
77 LikelihoodMarginalConfig,
80 raise TypeError(f
"unsupported marginal configuration: {type(cfg)!r}.")
85 """Convert a Python copula configuration to its C++ representation.
88 TypeError: If the configuration family is not supported.
90 if not isinstance(cfg, (GaussianCopulaConfig, StudentTCopulaConfig)):
91 raise TypeError(f
"unsupported copula configuration: {type(cfg)!r}.")
96 """Python wrapper around a C++ joint distribution.
98 A joint distribution combines marginal distributions with a dependence
99 structure encoded by a copula. It is used by the statistic layer for nuisance
100 parameters and experimental observables.
103 cpp_obj: Bound C++ ``JointDistribution`` instance.
106 __slots__ = (
"_cpp_obj",)
109 """Store the bound C++ joint distribution object."""
114 """Wrap a bound C++ joint distribution.
117 cpp_obj: Bound C++ ``JointDistribution`` instance.
120 A Python wrapper retaining the C++ object.
125 """Return the underlying C++ object for internal binding calls."""
128 def sample(self, n: Optional[int] =
None) -> Union[List[float], List[List[float]]]:
129 """Draw one or more samples from the joint distribution.
132 n: Optional number of samples. When omitted, a single sample vector is
136 A single sample ``list[float]`` when ``n`` is ``None``; otherwise a
137 list of sample vectors with shape ``n x dim``.
140 ValueError: If ``n`` is negative.
145 raise ValueError(
"n must be >= 0.")
146 return [[float(v)
for v
in row]
for row
in self.
_cpp_obj.
sample(int(n))]
148 def logpdf(self, x: Sequence[float]) -> float:
149 """Evaluate the joint log-density at ``x``.
152 x: Point in physical variable space. Its length must match the joint
153 distribution dimension.
156 The scalar log-density ``log f(x)``.
161 """Return the distribution dimension."""
166 """Alias for :meth:`dim`, following NumPy naming conventions."""
170 """Return a compact representation useful in notebooks and logs."""
171 return f
"JointDistribution(dim={self.dim()})"
175 """Factory helpers for C++ joint distributions.
177 The factory receives Python marginal and copula configurations, converts
178 them to C++ objects and returns a :class:`JointDistribution` wrapper.
183 marginal_types: Sequence[MarginalKind],
184 marginal_configs: Sequence[MarginalConfig],
185 copula_type: CopulaKind,
186 copula_config: CopulaConfig,
188 seed: Optional[int] =
None,
189 ) -> JointDistribution:
190 """Create a joint distribution with a shared optional seed.
193 marginal_types: Marginal distribution family for each dimension.
194 marginal_configs: Configuration object for each marginal.
195 copula_type: Copula family encoding dependence.
196 copula_config: Copula configuration, typically a correlation matrix.
197 seed: Optional seed passed to the C++ factory.
200 A ``JointDistribution`` wrapper.
203 ValueError: If marginal type/config lengths differ or no marginal is
207 >>> jd = JointDistributionFactory.create(
208 ... [MarginalKind.GAUSSIAN, MarginalKind.FLAT],
209 ... [GaussianMarginalConfig(0.0, 1.0), FlatMarginalConfig(-1.0, 1.0)],
210 ... CopulaKind.GAUSSIAN,
211 ... GaussianCopulaConfig(R=[[1.0, 0.2], [0.2, 1.0]]),
217 if len(marginal_types) != len(marginal_configs):
218 raise ValueError(
"marginal_types et marginal_configs must have the same size.")
219 if not marginal_types:
220 raise ValueError(
"Au moins une marginale est requise.")
222 cpp = st.JointDistribution.create(
227 seed
if seed
is not None else None,
229 return JointDistribution.from_cpp(cpp)
233 marginal_types: Sequence[MarginalKind],
234 marginal_configs: Sequence[MarginalConfig],
235 marginal_seeds: Sequence[int],
236 copula_type: CopulaKind,
237 copula_config: CopulaConfig,
240 ) -> JointDistribution:
241 """Create a joint distribution with explicit marginal and copula seeds.
244 marginal_types: Marginal distribution family for each dimension.
245 marginal_configs: Configuration object for each marginal.
246 marginal_seeds: Seed used for each marginal RNG.
247 copula_type: Copula family encoding dependence.
248 copula_config: Copula configuration.
249 copula_seed: Seed used for the copula RNG.
252 A ``JointDistribution`` wrapper.
255 ValueError: If input sequence lengths differ or no marginal is
258 if len(marginal_types) != len(marginal_configs)
or len(marginal_types) != len(
262 "marginal_types, marginal_configs et marginal_seeds must have the same size."
264 if not marginal_types:
265 raise ValueError(
"Au moins une marginale est requise.")
267 cpp = st.JointDistribution.create_with_seeds(
270 [int(s)
for s
in marginal_seeds],
275 return JointDistribution.from_cpp(cpp)
278__all__ = [
"JointDistribution",
"JointDistributionFactory"]
JointDistribution create(Sequence[MarginalKind] marginal_types, Sequence[MarginalConfig] marginal_configs, CopulaKind copula_type, CopulaConfig copula_config, *Optional[int] seed=None)
JointDistribution create_with_seeds(Sequence[MarginalKind] marginal_types, Sequence[MarginalConfig] marginal_configs, Sequence[int] marginal_seeds, CopulaKind copula_type, CopulaConfig copula_config, *int copula_seed)
Union[List[float], List[List[float]]] sample(self, Optional[int] n=None)
None __init__(self, cpp_obj)
"JointDistribution" from_cpp(cls, cpp_obj)
float logpdf(self, Sequence[float] x)
_require(value, typ, str name)
_cpp_copula_kind(CopulaKind kind)
_cpp_marginal_config(MarginalConfig cfg)
_cpp_copula_config(CopulaConfig cfg)
_cpp_marginal_kind(MarginalKind kind)