Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
CopulaConfig.py
Go to the documentation of this file.
1"""Configuration objects used to build statistical copulas.
2
3This module contains small Python dataclasses mirroring the C++ copula
4configuration structs exposed through the ``statistic`` binding. They are meant
5for user-facing Python code and convert themselves to the bound C++ objects when
6calling factories such as :class:`~pyhyperiso.core.Statistic.Copula.CopulaFactoryWrapper`.
7
8A copula models dependence between marginal distributions in a joint law. In the
9C++ backend, the Gaussian and Student-t copulas are parameterized by a
10correlation matrix ``R``. The Student-t copula additionally uses a number of
11degrees of freedom ``nu``.
12"""
13
14from __future__ import annotations
15
16from dataclasses import dataclass
17from enum import Enum
18from typing import Any, Sequence, Union
19
20from pyhyperiso.phyperiso.pyhyperiso import statistic as st
21from pyhyperiso.core.Math.RealMatrix import Matrix
22
23NestedList = Sequence[Sequence[float]]
24MatrixLike = Union[Matrix, NestedList]
25
26
27def _to_matrix(obj: MatrixLike) -> Matrix:
28 """Normalize a Python matrix-like object into a ``Matrix`` wrapper.
29
30 Args:
31 obj: Either an existing ``Matrix`` instance or a nested sequence of
32 numbers with shape ``d x d``.
33
34 Returns:
35 A ``Matrix`` instance ready to be converted to C++.
36
37 Raises:
38 Exception: Propagates validation errors raised by ``Matrix`` when the
39 nested sequence cannot be interpreted as a numeric matrix.
40 """
41 if isinstance(obj, Matrix):
42 return obj
43 return Matrix(data=obj)
44
45
46class CopulaKind(Enum):
47 """Available copula families supported by the statistic backend.
48
49 Attributes:
50 GAUSSIAN: Gaussian copula based on a multivariate normal latent vector.
51 STUDENT_T: Student-t copula, useful when stronger tail dependence is
52 desired.
53 """
54
55 GAUSSIAN = "GAUSSIAN"
56 STUDENT_T = "STUDENT_T"
57
58 def to_cpp(self) -> Any:
59 """Convert the Python enum value to the bound C++ ``CopulaType``.
60
61 Returns:
62 The corresponding C++ enum value from ``statistic.CopulaType``.
63 """
64 return getattr(st.CopulaType, self.value)
65
66
67@dataclass(frozen=True)
69 """Configuration for a Gaussian copula.
70
71 Args:
72 R: Correlation matrix of the latent Gaussian vector. The matrix should
73 be square, symmetric and have diagonal entries close to one. The C++
74 implementation regularizes/projects it before decomposition.
75
76 Examples:
77 >>> cfg = GaussianCopulaConfigPy(R=[[1.0, 0.4], [0.4, 1.0]])
78 >>> cpp_cfg = cfg.to_cpp()
79 """
80
81 R: MatrixLike
82
83 def to_cpp(self) -> Any:
84 """Build the bound C++ ``GaussianCopulaConfig`` object.
85
86 Returns:
87 A C++ configuration object containing the correlation matrix.
88 """
89 Rm = _to_matrix(self.R)
90 return st.GaussianCopulaConfig(Rm.to_cpp())
91
92 @classmethod
93 def from_cpp(cls, cpp: Any) -> "GaussianCopulaConfigPy":
94 """Create a Python config from a bound C++ config.
95
96 Args:
97 cpp: Bound C++ ``GaussianCopulaConfig`` instance.
98
99 Returns:
100 The corresponding Python wrapper.
101 """
102 return cls(R=Matrix._from_cpp(cpp.R))
103
104
105@dataclass(frozen=True)
107 """Configuration for a Student-t copula.
108
109 Args:
110 R: Correlation matrix of the latent Student-t vector.
111 nu: Degrees of freedom. Smaller values increase tail dependence. The C++
112 implementation requires a physically meaningful positive value; in
113 practice values such as ``4`` or larger are common starting points.
114
115 Raises:
116 ValueError: If ``nu`` is not strictly positive.
117
118 Examples:
119 >>> cfg = StudentTCopulaConfigPy(R=[[1.0, 0.2], [0.2, 1.0]], nu=5)
120 >>> cfg.nu
121 5
122 """
123
124 R: MatrixLike
125 nu: int = 4
126
127 def __post_init__(self) -> None:
128 """Validate the number of degrees of freedom."""
129 if int(self.nunu) <= 0:
130 raise ValueError("StudentTCopulaConfigPy.nu must be > 0")
131
132 def to_cpp(self) -> Any:
133 """Build the bound C++ ``StudentTCopulaConfig`` object.
134
135 Returns:
136 A C++ configuration object containing ``R`` and ``nu``.
137 """
138 Rm = _to_matrix(self.R)
139 cfg = st.StudentTCopulaConfig()
140 cfg.R = Rm.to_cpp()
141 cfg.nu = int(self.nunu)
142 return cfg
143
144 @classmethod
145 def from_cpp(cls, cpp: Any) -> "StudentTCopulaConfigPy":
146 """Create a Python config from a bound C++ Student-t config.
147
148 Args:
149 cpp: Bound C++ ``StudentTCopulaConfig`` instance.
150
151 Returns:
152 The corresponding Python wrapper.
153 """
154 return cls(R=Matrix._from_cpp(cpp.R), nu=int(cpp.nu))
155
156
157CopulaConfigPy = Union[GaussianCopulaConfigPy, StudentTCopulaConfigPy]
158
159
160def _config_from_cpp(cpp_cfg: Any) -> CopulaConfigPy:
161 """Dispatch a bound C++ copula config to the matching Python wrapper.
162
163 Args:
164 cpp_cfg: Bound C++ copula configuration object.
165
166 Returns:
167 A ``GaussianCopulaConfigPy`` or ``StudentTCopulaConfigPy`` instance.
168
169 Raises:
170 TypeError: If the C++ object type is not recognized.
171 """
172 if isinstance(cpp_cfg, st.GaussianCopulaConfig):
173 return GaussianCopulaConfigPy.from_cpp(cpp_cfg)
174 if isinstance(cpp_cfg, st.StudentTCopulaConfig):
175 return StudentTCopulaConfigPy.from_cpp(cpp_cfg)
176 raise TypeError(f"Copula config C++ inconnue: {type(cpp_cfg)!r}")
Matrix _to_matrix(MatrixLike obj)