pygeoinf 1.0.9__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pygeoinf/__init__.py +5 -1
- pygeoinf/direct_sum.py +101 -75
- pygeoinf/forward_problem.py +33 -32
- pygeoinf/gaussian_measure.py +97 -71
- pygeoinf/hilbert_space.py +517 -241
- pygeoinf/inversion.py +16 -4
- pygeoinf/linear_bayesian.py +57 -36
- pygeoinf/linear_forms.py +169 -0
- pygeoinf/linear_optimisation.py +34 -23
- pygeoinf/linear_solvers.py +74 -247
- pygeoinf/operators.py +175 -36
- pygeoinf/random_matrix.py +36 -32
- pygeoinf/symmetric_space/circle.py +347 -202
- pygeoinf/symmetric_space/sphere.py +335 -448
- pygeoinf/symmetric_space/symmetric_space.py +330 -142
- {pygeoinf-1.0.9.dist-info → pygeoinf-1.1.1.dist-info}/METADATA +11 -9
- pygeoinf-1.1.1.dist-info/RECORD +20 -0
- pygeoinf/forms.py +0 -128
- pygeoinf/symmetric_space/line.py +0 -384
- pygeoinf-1.0.9.dist-info/RECORD +0 -21
- {pygeoinf-1.0.9.dist-info → pygeoinf-1.1.1.dist-info}/LICENSE +0 -0
- {pygeoinf-1.0.9.dist-info → pygeoinf-1.1.1.dist-info}/WHEEL +0 -0
pygeoinf/__init__.py
CHANGED
|
@@ -8,7 +8,11 @@ from .random_matrix import (
|
|
|
8
8
|
|
|
9
9
|
from .hilbert_space import (
|
|
10
10
|
HilbertSpace,
|
|
11
|
+
DualHilbertSpace,
|
|
11
12
|
EuclideanSpace,
|
|
13
|
+
HilbertModule,
|
|
14
|
+
MassWeightedHilbertSpace,
|
|
15
|
+
MassWeightedHilbertModule,
|
|
12
16
|
)
|
|
13
17
|
|
|
14
18
|
|
|
@@ -18,7 +22,7 @@ from .operators import (
|
|
|
18
22
|
DiagonalLinearOperator,
|
|
19
23
|
)
|
|
20
24
|
|
|
21
|
-
from .
|
|
25
|
+
from .linear_forms import (
|
|
22
26
|
LinearForm,
|
|
23
27
|
)
|
|
24
28
|
|
pygeoinf/direct_sum.py
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Implements direct sums of Hilbert spaces and corresponding block operators.
|
|
3
|
+
|
|
4
|
+
This module provides tools for constructing larger, composite Hilbert spaces and
|
|
5
|
+
operators from smaller ones. This is essential for problems involving multiple
|
|
6
|
+
coupled fields or joint inversions where a single model is constrained by
|
|
7
|
+
data from different experiments.
|
|
8
|
+
|
|
9
|
+
Key Classes
|
|
10
|
+
-----------
|
|
11
|
+
- `HilbertSpaceDirectSum`: A `HilbertSpace` formed by the direct sum of a
|
|
12
|
+
list of other spaces. Vectors in this space are lists of vectors from the
|
|
13
|
+
component subspaces.
|
|
14
|
+
- `BlockLinearOperator`: A `LinearOperator` acting between direct sum spaces,
|
|
15
|
+
represented as a 2D grid (matrix) of sub-operators.
|
|
16
|
+
- `ColumnLinearOperator`: A specialized block operator mapping from a single
|
|
17
|
+
space to a direct sum space.
|
|
18
|
+
- `RowLinearOperator`: A specialized block operator mapping from a direct sum
|
|
19
|
+
space to a single space.
|
|
20
|
+
- `BlockDiagonalLinearOperator`: An efficient representation for block
|
|
21
|
+
operators with zero off-diagonal blocks.
|
|
3
22
|
"""
|
|
4
23
|
|
|
5
24
|
from __future__ import annotations
|
|
@@ -9,15 +28,16 @@ import numpy as np
|
|
|
9
28
|
|
|
10
29
|
from .hilbert_space import HilbertSpace
|
|
11
30
|
from .operators import LinearOperator
|
|
12
|
-
from .
|
|
31
|
+
from .linear_forms import LinearForm
|
|
13
32
|
|
|
14
33
|
|
|
15
34
|
class HilbertSpaceDirectSum(HilbertSpace):
|
|
16
35
|
"""
|
|
17
36
|
A Hilbert space formed from the direct sum of a list of other spaces.
|
|
18
37
|
|
|
19
|
-
|
|
20
|
-
element of the list is a vector from the
|
|
38
|
+
A vector in this space is represented as a list of vectors, where the i-th
|
|
39
|
+
element of the list is a vector from the i-th component subspace. The
|
|
40
|
+
inner product is the sum of the inner products of the components.
|
|
21
41
|
"""
|
|
22
42
|
|
|
23
43
|
def __init__(self, spaces: List[HilbertSpace]) -> None:
|
|
@@ -29,22 +49,58 @@ class HilbertSpaceDirectSum(HilbertSpace):
|
|
|
29
49
|
in the direct sum.
|
|
30
50
|
"""
|
|
31
51
|
self._spaces: List[HilbertSpace] = spaces
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
self._dim = sum([space.dim for space in spaces])
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def dim(self) -> int:
|
|
56
|
+
"""Returns the dimension of the direct sum space."""
|
|
57
|
+
return self._dim
|
|
58
|
+
|
|
59
|
+
def to_components(self, xs: List[Any]) -> np.ndarray:
|
|
60
|
+
cs = [space.to_components(x) for space, x in zip(self._spaces, xs)]
|
|
61
|
+
return np.concatenate(cs, 0)
|
|
62
|
+
|
|
63
|
+
def from_components(self, c: np.ndarray) -> List[Any]:
|
|
64
|
+
xs = []
|
|
65
|
+
i = 0
|
|
66
|
+
for space in self._spaces:
|
|
67
|
+
j = i + space.dim
|
|
68
|
+
x = space.from_components(c[i:j])
|
|
69
|
+
xs.append(x)
|
|
70
|
+
i = j
|
|
71
|
+
return xs
|
|
72
|
+
|
|
73
|
+
def to_dual(self, xs: List[Any]) -> LinearForm:
|
|
74
|
+
if len(xs) != self.number_of_subspaces:
|
|
75
|
+
raise ValueError("Input list has incorrect number of vectors.")
|
|
76
|
+
return self.canonical_dual_isomorphism(
|
|
77
|
+
[space.to_dual(x) for space, x in zip(self._spaces, xs)]
|
|
46
78
|
)
|
|
47
79
|
|
|
80
|
+
def from_dual(self, xp: LinearForm) -> List[Any]:
|
|
81
|
+
xps = self.canonical_dual_inverse_isomorphism(xp)
|
|
82
|
+
return [space.from_dual(xip) for space, xip in zip(self._spaces, xps)]
|
|
83
|
+
|
|
84
|
+
def add(self, xs: List[Any], ys: List[Any]) -> List[Any]:
|
|
85
|
+
return [space.add(x, y) for space, x, y in zip(self._spaces, xs, ys)]
|
|
86
|
+
|
|
87
|
+
def subtract(self, xs: List[Any], ys: List[Any]) -> List[Any]:
|
|
88
|
+
return [space.subtract(x, y) for space, x, y in zip(self._spaces, xs, ys)]
|
|
89
|
+
|
|
90
|
+
def multiply(self, a: float, xs: List[Any]) -> List[Any]:
|
|
91
|
+
return [space.multiply(a, x) for space, x in zip(self._spaces, xs)]
|
|
92
|
+
|
|
93
|
+
def ax(self, a: float, xs: List[Any]) -> None:
|
|
94
|
+
for space, x in zip(self._spaces, xs):
|
|
95
|
+
space.ax(a, x)
|
|
96
|
+
|
|
97
|
+
def axpy(self, a: float, xs: List[Any], ys: List[Any]) -> None:
|
|
98
|
+
for space, x, y in zip(self._spaces, xs, ys):
|
|
99
|
+
space.axpy(a, x, y)
|
|
100
|
+
|
|
101
|
+
def copy(self, xs: List[Any]) -> List[Any]:
|
|
102
|
+
return [space.copy(x) for space, x in zip(self._spaces, xs)]
|
|
103
|
+
|
|
48
104
|
def __eq__(self, other: object) -> bool:
|
|
49
105
|
"""
|
|
50
106
|
Checks for mathematical equality with another direct sum space.
|
|
@@ -55,7 +111,6 @@ class HilbertSpaceDirectSum(HilbertSpace):
|
|
|
55
111
|
if not isinstance(other, HilbertSpaceDirectSum):
|
|
56
112
|
return NotImplemented
|
|
57
113
|
|
|
58
|
-
# This relies on the subspaces having their own __eq__ methods.
|
|
59
114
|
return self.subspaces == other.subspaces
|
|
60
115
|
|
|
61
116
|
@property
|
|
@@ -145,56 +200,6 @@ class HilbertSpaceDirectSum(HilbertSpace):
|
|
|
145
200
|
i = j
|
|
146
201
|
return xps
|
|
147
202
|
|
|
148
|
-
def __to_components(self, xs: List[Any]) -> np.ndarray:
|
|
149
|
-
cs = [space.to_components(x) for space, x in zip(self._spaces, xs)]
|
|
150
|
-
return np.concatenate(cs, 0)
|
|
151
|
-
|
|
152
|
-
def __from_components(self, c: np.ndarray) -> List[Any]:
|
|
153
|
-
xs = []
|
|
154
|
-
i = 0
|
|
155
|
-
for space in self._spaces:
|
|
156
|
-
j = i + space.dim
|
|
157
|
-
x = space.from_components(c[i:j])
|
|
158
|
-
xs.append(x)
|
|
159
|
-
i = j
|
|
160
|
-
return xs
|
|
161
|
-
|
|
162
|
-
def __inner_product(self, x1s: List[Any], x2s: List[Any]) -> float:
|
|
163
|
-
return sum(
|
|
164
|
-
space.inner_product(x1, x2) for space, x1, x2 in zip(self._spaces, x1s, x2s)
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
def __to_dual(self, xs: List[Any]) -> LinearForm:
|
|
168
|
-
if len(xs) != self.number_of_subspaces:
|
|
169
|
-
raise ValueError("Input list has incorrect number of vectors.")
|
|
170
|
-
return self.canonical_dual_isomorphism(
|
|
171
|
-
[space.to_dual(x) for space, x in zip(self._spaces, xs)]
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
def __from_dual(self, xp: LinearForm) -> List[Any]:
|
|
175
|
-
xps = self.canonical_dual_inverse_isomorphism(xp)
|
|
176
|
-
return [space.from_dual(xip) for space, xip in zip(self._spaces, xps)]
|
|
177
|
-
|
|
178
|
-
def __add(self, xs: List[Any], ys: List[Any]) -> List[Any]:
|
|
179
|
-
return [space.add(x, y) for space, x, y in zip(self._spaces, xs, ys)]
|
|
180
|
-
|
|
181
|
-
def __subtract(self, xs: List[Any], ys: List[Any]) -> List[Any]:
|
|
182
|
-
return [space.subtract(x, y) for space, x, y in zip(self._spaces, xs, ys)]
|
|
183
|
-
|
|
184
|
-
def __multiply(self, a: float, xs: List[Any]) -> List[Any]:
|
|
185
|
-
return [space.multiply(a, x) for space, x in zip(self._spaces, xs)]
|
|
186
|
-
|
|
187
|
-
def __ax(self, a: float, xs: List[Any]) -> None:
|
|
188
|
-
for space, x in zip(self._spaces, xs):
|
|
189
|
-
space.ax(a, x)
|
|
190
|
-
|
|
191
|
-
def __axpy(self, a: float, xs: List[Any], ys: List[Any]) -> None:
|
|
192
|
-
for space, x, y in zip(self._spaces, xs, ys):
|
|
193
|
-
space.axpy(a, x, y)
|
|
194
|
-
|
|
195
|
-
def __copy(self, xs: List[Any]) -> List[Any]:
|
|
196
|
-
return [space.copy(x) for space, x in zip(self._spaces, xs)]
|
|
197
|
-
|
|
198
203
|
def _subspace_projection_mapping(self, i: int, xs: List[Any]) -> Any:
|
|
199
204
|
return xs[i]
|
|
200
205
|
|
|
@@ -213,14 +218,23 @@ class BlockStructure(ABC):
|
|
|
213
218
|
|
|
214
219
|
@property
|
|
215
220
|
def row_dim(self) -> int:
|
|
221
|
+
"""
|
|
222
|
+
Returns the number of rows in the block structure.
|
|
223
|
+
"""
|
|
216
224
|
return self._row_dim
|
|
217
225
|
|
|
218
226
|
@property
|
|
219
227
|
def col_dim(self) -> int:
|
|
228
|
+
"""
|
|
229
|
+
Returns the number of columns in the block structure.
|
|
230
|
+
"""
|
|
220
231
|
return self._col_dim
|
|
221
232
|
|
|
222
233
|
@abstractmethod
|
|
223
234
|
def block(self, i: int, j: int) -> "LinearOperator":
|
|
235
|
+
"""
|
|
236
|
+
Returns the operator in the (i, j)-th sub-block.
|
|
237
|
+
"""
|
|
224
238
|
pass
|
|
225
239
|
|
|
226
240
|
def _check_block_indices(self, i: int, j: int) -> None:
|
|
@@ -232,8 +246,12 @@ class BlockStructure(ABC):
|
|
|
232
246
|
|
|
233
247
|
class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
234
248
|
"""
|
|
235
|
-
A linear operator between direct
|
|
236
|
-
|
|
249
|
+
A linear operator between direct sum spaces, defined by a matrix of sub-operators.
|
|
250
|
+
|
|
251
|
+
This operator acts like a matrix where each entry is itself a `LinearOperator`.
|
|
252
|
+
It maps a list of input vectors `[x_1, x_2, ...]` to a list of output
|
|
253
|
+
vectors `[y_1, y_2, ...]`. The constructor checks for dimensional
|
|
254
|
+
consistency between the blocks.
|
|
237
255
|
"""
|
|
238
256
|
|
|
239
257
|
def __init__(self, blocks: List[List[LinearOperator]]) -> None:
|
|
@@ -309,8 +327,12 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
|
309
327
|
|
|
310
328
|
class ColumnLinearOperator(LinearOperator, BlockStructure):
|
|
311
329
|
"""
|
|
312
|
-
An operator that maps a single
|
|
313
|
-
|
|
330
|
+
An operator that maps from a single space to a direct sum space.
|
|
331
|
+
|
|
332
|
+
It can be visualized as a column vector of operators, `[A_1, A_2, ...]^T`.
|
|
333
|
+
It takes a single input vector `x` and produces a list of output vectors
|
|
334
|
+
`[A_1(x), A_2(x), ...]`. This is often used to represent a joint forward
|
|
335
|
+
operator in an inverse problem.
|
|
314
336
|
"""
|
|
315
337
|
|
|
316
338
|
def __init__(self, operators: List[LinearOperator]) -> None:
|
|
@@ -360,8 +382,12 @@ class ColumnLinearOperator(LinearOperator, BlockStructure):
|
|
|
360
382
|
|
|
361
383
|
class RowLinearOperator(LinearOperator, BlockStructure):
|
|
362
384
|
"""
|
|
363
|
-
An operator that maps a direct sum
|
|
364
|
-
|
|
385
|
+
An operator that maps from a direct sum space to a single space.
|
|
386
|
+
|
|
387
|
+
It can be visualized as a row vector of operators, `[A_1, A_2, ...]`.
|
|
388
|
+
It takes a list of input vectors `[x_1, x_2, ...]` and produces a single
|
|
389
|
+
output vector `y = A_1(x_1) + A_2(x_2) + ...`. The adjoint of a
|
|
390
|
+
`ColumnLinearOperator` is a `RowLinearOperator`.
|
|
365
391
|
"""
|
|
366
392
|
|
|
367
393
|
def __init__(self, operators: List[LinearOperator]) -> None:
|
pygeoinf/forward_problem.py
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
This module provides classes
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
Defines the mathematical structure of a forward problem.
|
|
3
|
+
|
|
4
|
+
This module provides classes that encapsulate the core components of an
|
|
5
|
+
inverse problem. A forward problem describes the physical or mathematical
|
|
6
|
+
process that maps a set of unknown model parameters `u` to a set of observable
|
|
7
|
+
data `d`.
|
|
8
|
+
|
|
9
|
+
The module handles both the deterministic relationship `d = A(u)` and the more
|
|
10
|
+
realistic statistical model `d = A(u) + e`, where `e` represents random noise.
|
|
11
|
+
|
|
12
|
+
Key Classes
|
|
13
|
+
-----------
|
|
14
|
+
- `ForwardProblem`: A general class representing the link between a model
|
|
15
|
+
space and a data space via a forward operator, with an optional data error.
|
|
16
|
+
- `LinearForwardProblem`: A specialization for linear problems where the
|
|
17
|
+
forward operator is a `LinearOperator`.
|
|
8
18
|
"""
|
|
9
19
|
|
|
10
20
|
from __future__ import annotations
|
|
@@ -18,7 +28,7 @@ from .direct_sum import ColumnLinearOperator
|
|
|
18
28
|
# This block only runs for type checkers, not at runtime, to prevent
|
|
19
29
|
# circular import errors while still allowing type hints.
|
|
20
30
|
if TYPE_CHECKING:
|
|
21
|
-
from .hilbert_space import HilbertSpace,
|
|
31
|
+
from .hilbert_space import HilbertSpace, Vector
|
|
22
32
|
from .operators import LinearOperator
|
|
23
33
|
|
|
24
34
|
|
|
@@ -33,7 +43,7 @@ class ForwardProblem:
|
|
|
33
43
|
|
|
34
44
|
def __init__(
|
|
35
45
|
self,
|
|
36
|
-
forward_operator:
|
|
46
|
+
forward_operator: LinearOperator,
|
|
37
47
|
/,
|
|
38
48
|
*,
|
|
39
49
|
data_error_measure: Optional["GaussianMeasure"] = None,
|
|
@@ -47,7 +57,7 @@ class ForwardProblem:
|
|
|
47
57
|
from which data errors are assumed to be drawn. If None, the
|
|
48
58
|
data is considered to be error-free.
|
|
49
59
|
"""
|
|
50
|
-
self._forward_operator:
|
|
60
|
+
self._forward_operator: LinearOperator = forward_operator
|
|
51
61
|
self._data_error_measure: Optional["GaussianMeasure"] = data_error_measure
|
|
52
62
|
if self.data_error_measure_set:
|
|
53
63
|
if self.data_space != data_error_measure.domain:
|
|
@@ -56,7 +66,7 @@ class ForwardProblem:
|
|
|
56
66
|
)
|
|
57
67
|
|
|
58
68
|
@property
|
|
59
|
-
def forward_operator(self) ->
|
|
69
|
+
def forward_operator(self) -> LinearOperator:
|
|
60
70
|
"""The forward operator, mapping from model to data space."""
|
|
61
71
|
return self._forward_operator
|
|
62
72
|
|
|
@@ -88,9 +98,7 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
88
98
|
Represents a linear forward problem of the form `d = A(u) + e`.
|
|
89
99
|
|
|
90
100
|
Here, `d` is the data, `A` is the linear forward operator, `u` is the model,
|
|
91
|
-
and `e` is a random error drawn from a Gaussian distribution.
|
|
92
|
-
provides methods for statistical analysis, such as generating synthetic data
|
|
93
|
-
and performing chi-squared tests.
|
|
101
|
+
and `e` is a random error drawn from a Gaussian distribution.
|
|
94
102
|
"""
|
|
95
103
|
|
|
96
104
|
@staticmethod
|
|
@@ -100,8 +108,9 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
100
108
|
"""
|
|
101
109
|
Forms a joint forward problem from a list of separate problems.
|
|
102
110
|
|
|
103
|
-
This is
|
|
104
|
-
multiple, independent measurement systems
|
|
111
|
+
This is a powerful tool for joint inversions, where a single underlying
|
|
112
|
+
model is observed through multiple, independent measurement systems
|
|
113
|
+
(e.g., different types of geophysical surveys).
|
|
105
114
|
|
|
106
115
|
Args:
|
|
107
116
|
forward_problems: A list of `LinearForwardProblem` instances that
|
|
@@ -110,10 +119,6 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
110
119
|
Returns:
|
|
111
120
|
A single `LinearForwardProblem` where the data space is the direct
|
|
112
121
|
sum of the individual data spaces.
|
|
113
|
-
|
|
114
|
-
Raises:
|
|
115
|
-
ValueError: If the list of problems is empty or if they do not all
|
|
116
|
-
share the same model space.
|
|
117
122
|
"""
|
|
118
123
|
if not forward_problems:
|
|
119
124
|
raise ValueError("Cannot form a direct sum from an empty list.")
|
|
@@ -139,7 +144,7 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
139
144
|
joint_forward_operator, data_error_measure=data_error_measure
|
|
140
145
|
)
|
|
141
146
|
|
|
142
|
-
def data_measure(self, model: "
|
|
147
|
+
def data_measure(self, model: "Vector") -> "GaussianMeasure":
|
|
143
148
|
"""
|
|
144
149
|
Returns the Gaussian measure for the data, given a specific model.
|
|
145
150
|
|
|
@@ -160,7 +165,7 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
160
165
|
translation=self.forward_operator(model)
|
|
161
166
|
)
|
|
162
167
|
|
|
163
|
-
def synthetic_data(self, model: "
|
|
168
|
+
def synthetic_data(self, model: "Vector") -> "Vector":
|
|
164
169
|
"""
|
|
165
170
|
Generates a synthetic data vector for a given model.
|
|
166
171
|
|
|
@@ -177,7 +182,7 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
177
182
|
|
|
178
183
|
def synthetic_model_and_data(
|
|
179
184
|
self, prior: "GaussianMeasure"
|
|
180
|
-
) -> Tuple["
|
|
185
|
+
) -> Tuple["Vector", "Vector"]:
|
|
181
186
|
"""
|
|
182
187
|
Generates a random model and corresponding synthetic data.
|
|
183
188
|
|
|
@@ -211,24 +216,20 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
211
216
|
"""
|
|
212
217
|
return chi2.ppf(significance_level, self.data_space.dim)
|
|
213
218
|
|
|
214
|
-
def chi_squared(self, model: "
|
|
219
|
+
def chi_squared(self, model: "Vector", data: "Vector") -> float:
|
|
215
220
|
"""
|
|
216
221
|
Calculates the chi-squared statistic for a given model and data.
|
|
217
222
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
This measures the misfit between the predicted and observed data.
|
|
224
|
+
- If a data error measure with an inverse covariance `C_e^-1` is defined,
|
|
225
|
+
this is the weighted misfit: `(d - A(u))^T * C_e^-1 * (d - A(u))`.
|
|
226
|
+
- Otherwise, it is the squared L2 norm of the data residual: `||d - A(u)||^2`.
|
|
222
227
|
Args:
|
|
223
228
|
model: A vector from the model space.
|
|
224
229
|
data: An observed data vector from the data space.
|
|
225
230
|
|
|
226
231
|
Returns:
|
|
227
232
|
The chi-squared statistic.
|
|
228
|
-
|
|
229
|
-
Raises:
|
|
230
|
-
AttributeError: If a data error measure is set but its inverse
|
|
231
|
-
covariance (precision operator) is not available.
|
|
232
233
|
"""
|
|
233
234
|
residual = self.data_space.subtract(data, self.forward_operator(model))
|
|
234
235
|
|
|
@@ -246,7 +247,7 @@ class LinearForwardProblem(ForwardProblem):
|
|
|
246
247
|
return self.data_space.squared_norm(residual)
|
|
247
248
|
|
|
248
249
|
def chi_squared_test(
|
|
249
|
-
self, significance_level: float, model: "
|
|
250
|
+
self, significance_level: float, model: "Vector", data: "Vector"
|
|
250
251
|
) -> bool:
|
|
251
252
|
"""
|
|
252
253
|
Performs a chi-squared test for goodness of fit.
|