pygeoinf 1.0.8__py3-none-any.whl → 1.1.0__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 +113 -85
- 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 -435
- pygeoinf/symmetric_space/symmetric_space.py +330 -142
- {pygeoinf-1.0.8.dist-info → pygeoinf-1.1.0.dist-info}/METADATA +1 -1
- pygeoinf-1.1.0.dist-info/RECORD +20 -0
- pygeoinf/forms.py +0 -168
- pygeoinf/symmetric_space/line.py +0 -384
- pygeoinf-1.0.8.dist-info/RECORD +0 -21
- {pygeoinf-1.0.8.dist-info → pygeoinf-1.1.0.dist-info}/LICENSE +0 -0
- {pygeoinf-1.0.8.dist-info → pygeoinf-1.1.0.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
|
|
@@ -119,12 +174,10 @@ class HilbertSpaceDirectSum(HilbertSpace):
|
|
|
119
174
|
"""
|
|
120
175
|
if len(xps) != self.number_of_subspaces:
|
|
121
176
|
raise ValueError("Incorrect number of dual vectors provided.")
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
),
|
|
127
|
-
)
|
|
177
|
+
|
|
178
|
+
cps = [space.dual.to_components(xp) for space, xp in zip(self._spaces, xps)]
|
|
179
|
+
cp = np.concatenate(cps, 0)
|
|
180
|
+
return LinearForm(self, components=cp)
|
|
128
181
|
|
|
129
182
|
def canonical_dual_inverse_isomorphism(self, xp: LinearForm) -> List[LinearForm]:
|
|
130
183
|
"""
|
|
@@ -136,61 +189,16 @@ class HilbertSpaceDirectSum(HilbertSpace):
|
|
|
136
189
|
Args:
|
|
137
190
|
xp (LinearForm): A dual vector on the direct sum space.
|
|
138
191
|
"""
|
|
139
|
-
return [
|
|
140
|
-
LinearForm(space, mapping=lambda x, j=i: xp(self.subspace_inclusion(j)(x)))
|
|
141
|
-
for i, space in enumerate(self.subspaces)
|
|
142
|
-
]
|
|
143
|
-
|
|
144
|
-
# ... (Private methods remain the same) ...
|
|
145
|
-
def __to_components(self, xs: List[Any]) -> np.ndarray:
|
|
146
|
-
cs = [space.to_components(x) for space, x in zip(self._spaces, xs)]
|
|
147
|
-
return np.concatenate(cs, 0)
|
|
148
192
|
|
|
149
|
-
|
|
150
|
-
|
|
193
|
+
cp = self.dual.to_components(xp)
|
|
194
|
+
xps = []
|
|
151
195
|
i = 0
|
|
152
196
|
for space in self._spaces:
|
|
153
197
|
j = i + space.dim
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
xp = space.dual.from_components(cp[i:j])
|
|
199
|
+
xps.append(xp)
|
|
156
200
|
i = j
|
|
157
|
-
return
|
|
158
|
-
|
|
159
|
-
def __inner_product(self, x1s: List[Any], x2s: List[Any]) -> float:
|
|
160
|
-
return sum(
|
|
161
|
-
space.inner_product(x1, x2) for space, x1, x2 in zip(self._spaces, x1s, x2s)
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
def __to_dual(self, xs: List[Any]) -> LinearForm:
|
|
165
|
-
if len(xs) != self.number_of_subspaces:
|
|
166
|
-
raise ValueError("Input list has incorrect number of vectors.")
|
|
167
|
-
return self.canonical_dual_isomorphism(
|
|
168
|
-
[space.to_dual(x) for space, x in zip(self._spaces, xs)]
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
def __from_dual(self, xp: LinearForm) -> List[Any]:
|
|
172
|
-
xps = self.canonical_dual_inverse_isomorphism(xp)
|
|
173
|
-
return [space.from_dual(xip) for space, xip in zip(self._spaces, xps)]
|
|
174
|
-
|
|
175
|
-
def __add(self, xs: List[Any], ys: List[Any]) -> List[Any]:
|
|
176
|
-
return [space.add(x, y) for space, x, y in zip(self._spaces, xs, ys)]
|
|
177
|
-
|
|
178
|
-
def __subtract(self, xs: List[Any], ys: List[Any]) -> List[Any]:
|
|
179
|
-
return [space.subtract(x, y) for space, x, y in zip(self._spaces, xs, ys)]
|
|
180
|
-
|
|
181
|
-
def __multiply(self, a: float, xs: List[Any]) -> List[Any]:
|
|
182
|
-
return [space.multiply(a, x) for space, x in zip(self._spaces, xs)]
|
|
183
|
-
|
|
184
|
-
def __ax(self, a: float, xs: List[Any]) -> None:
|
|
185
|
-
for space, x in zip(self._spaces, xs):
|
|
186
|
-
space.ax(a, x)
|
|
187
|
-
|
|
188
|
-
def __axpy(self, a: float, xs: List[Any], ys: List[Any]) -> None:
|
|
189
|
-
for space, x, y in zip(self._spaces, xs, ys):
|
|
190
|
-
space.axpy(a, x, y)
|
|
191
|
-
|
|
192
|
-
def __copy(self, xs: List[Any]) -> List[Any]:
|
|
193
|
-
return [space.copy(x) for space, x in zip(self._spaces, xs)]
|
|
201
|
+
return xps
|
|
194
202
|
|
|
195
203
|
def _subspace_projection_mapping(self, i: int, xs: List[Any]) -> Any:
|
|
196
204
|
return xs[i]
|
|
@@ -204,21 +212,29 @@ class BlockStructure(ABC):
|
|
|
204
212
|
An abstract base class for operators with a block structure.
|
|
205
213
|
"""
|
|
206
214
|
|
|
207
|
-
# ... (class content is the same) ...
|
|
208
215
|
def __init__(self, row_dim: int, col_dim: int) -> None:
|
|
209
216
|
self._row_dim: int = row_dim
|
|
210
217
|
self._col_dim: int = col_dim
|
|
211
218
|
|
|
212
219
|
@property
|
|
213
220
|
def row_dim(self) -> int:
|
|
221
|
+
"""
|
|
222
|
+
Returns the number of rows in the block structure.
|
|
223
|
+
"""
|
|
214
224
|
return self._row_dim
|
|
215
225
|
|
|
216
226
|
@property
|
|
217
227
|
def col_dim(self) -> int:
|
|
228
|
+
"""
|
|
229
|
+
Returns the number of columns in the block structure.
|
|
230
|
+
"""
|
|
218
231
|
return self._col_dim
|
|
219
232
|
|
|
220
233
|
@abstractmethod
|
|
221
234
|
def block(self, i: int, j: int) -> "LinearOperator":
|
|
235
|
+
"""
|
|
236
|
+
Returns the operator in the (i, j)-th sub-block.
|
|
237
|
+
"""
|
|
222
238
|
pass
|
|
223
239
|
|
|
224
240
|
def _check_block_indices(self, i: int, j: int) -> None:
|
|
@@ -230,8 +246,12 @@ class BlockStructure(ABC):
|
|
|
230
246
|
|
|
231
247
|
class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
232
248
|
"""
|
|
233
|
-
A linear operator between direct
|
|
234
|
-
|
|
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.
|
|
235
255
|
"""
|
|
236
256
|
|
|
237
257
|
def __init__(self, blocks: List[List[LinearOperator]]) -> None:
|
|
@@ -245,7 +265,7 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
|
245
265
|
"""
|
|
246
266
|
if not blocks or not blocks[0]:
|
|
247
267
|
raise ValueError("Block structure cannot be empty.")
|
|
248
|
-
|
|
268
|
+
|
|
249
269
|
domains = [operator.domain for operator in blocks[0]]
|
|
250
270
|
codomains = []
|
|
251
271
|
for row in blocks:
|
|
@@ -281,7 +301,7 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
|
281
301
|
return self._blocks[i][j]
|
|
282
302
|
|
|
283
303
|
def __mapping(self, xs: List[Any]) -> List[Any]:
|
|
284
|
-
|
|
304
|
+
|
|
285
305
|
ys = []
|
|
286
306
|
for i in range(self.row_dim):
|
|
287
307
|
codomain = self._codomains[i]
|
|
@@ -293,7 +313,7 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
|
293
313
|
return ys
|
|
294
314
|
|
|
295
315
|
def __adjoint_mapping(self, ys: List[Any]) -> List[Any]:
|
|
296
|
-
|
|
316
|
+
|
|
297
317
|
xs = []
|
|
298
318
|
for j in range(self.col_dim):
|
|
299
319
|
domain = self._domains[j]
|
|
@@ -307,8 +327,12 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
|
|
|
307
327
|
|
|
308
328
|
class ColumnLinearOperator(LinearOperator, BlockStructure):
|
|
309
329
|
"""
|
|
310
|
-
An operator that maps a single
|
|
311
|
-
|
|
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.
|
|
312
336
|
"""
|
|
313
337
|
|
|
314
338
|
def __init__(self, operators: List[LinearOperator]) -> None:
|
|
@@ -358,8 +382,12 @@ class ColumnLinearOperator(LinearOperator, BlockStructure):
|
|
|
358
382
|
|
|
359
383
|
class RowLinearOperator(LinearOperator, BlockStructure):
|
|
360
384
|
"""
|
|
361
|
-
An operator that maps a direct sum
|
|
362
|
-
|
|
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`.
|
|
363
391
|
"""
|
|
364
392
|
|
|
365
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.
|