pygeoinf 1.3.5__py3-none-any.whl → 1.3.7__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/subspaces.py CHANGED
@@ -3,29 +3,25 @@ Defines classes for representing affine and linear subspaces.
3
3
 
4
4
  The primary abstraction is the `AffineSubspace`, which represents a subset of
5
5
  a Hilbert space defined by a translation and a closed linear tangent space.
6
- `LinearSubspace` is a specialization where the translation is zero.
7
6
  """
8
7
 
9
8
  from __future__ import annotations
10
9
  from typing import List, Optional, Any, Callable, TYPE_CHECKING
11
10
  import numpy as np
11
+ import warnings
12
12
 
13
13
  from .linear_operators import LinearOperator
14
14
  from .hilbert_space import HilbertSpace, Vector, EuclideanSpace
15
15
  from .linear_solvers import LinearSolver, CholeskySolver, IterativeLinearSolver
16
16
 
17
17
  if TYPE_CHECKING:
18
- # Avoid circular imports for type checking
19
- pass
18
+ from .gaussian_measure import GaussianMeasure
20
19
 
21
20
 
22
21
  class OrthogonalProjector(LinearOperator):
23
22
  """
24
23
  Internal engine for subspace projections.
25
-
26
24
  Represents an orthogonal projection operator P = P* = P^2.
27
- While this class can be used directly, it is generally recommended to use
28
- `AffineSubspace` or `LinearSubspace` for high-level problem definitions.
29
25
  """
30
26
 
31
27
  def __init__(
@@ -52,9 +48,8 @@ class OrthogonalProjector(LinearOperator):
52
48
  basis_vectors: List[Vector],
53
49
  orthonormalize: bool = True,
54
50
  ) -> OrthogonalProjector:
55
- """Constructs P from a basis spanning the range."""
51
+ """Constructs a projector P onto the span of the provided basis vectors."""
56
52
  if not basis_vectors:
57
- # Return zero operator if basis is empty
58
53
  return domain.zero_operator(domain)
59
54
 
60
55
  if orthonormalize:
@@ -78,7 +73,12 @@ class AffineSubspace:
78
73
  translation: Optional[Vector] = None,
79
74
  constraint_operator: Optional[LinearOperator] = None,
80
75
  constraint_value: Optional[Vector] = None,
76
+ solver: Optional[LinearSolver] = None,
77
+ preconditioner: Optional[LinearOperator] = None,
81
78
  ) -> None:
79
+ """
80
+ Initializes the AffineSubspace.
81
+ """
82
82
  self._projector = projector
83
83
 
84
84
  if translation is None:
@@ -91,6 +91,15 @@ class AffineSubspace:
91
91
  self._constraint_operator = constraint_operator
92
92
  self._constraint_value = constraint_value
93
93
 
94
+ # Logic: If explicit equation exists, default to Cholesky.
95
+ # If implicit, leave None (requires robust solver from user).
96
+ if self._constraint_operator is not None and solver is None:
97
+ self._solver = CholeskySolver(galerkin=True)
98
+ else:
99
+ self._solver = solver
100
+
101
+ self._preconditioner = preconditioner
102
+
94
103
  @property
95
104
  def domain(self) -> HilbertSpace:
96
105
  return self._projector.domain
@@ -108,27 +117,39 @@ class AffineSubspace:
108
117
  return LinearSubspace(self._projector)
109
118
 
110
119
  @property
111
- def has_constraint_equation(self) -> bool:
120
+ def has_explicit_equation(self) -> bool:
121
+ """True if defined by B(u)=w, False if defined only by geometry."""
112
122
  return self._constraint_operator is not None
113
123
 
114
124
  @property
115
125
  def constraint_operator(self) -> LinearOperator:
126
+ """
127
+ Returns B for {u | B(u)=w}.
128
+ Falls back to (I - P) if no explicit operator exists.
129
+ """
116
130
  if self._constraint_operator is None:
117
- raise AttributeError("This subspace is not defined by a linear equation.")
131
+ return self._projector.complement
118
132
  return self._constraint_operator
119
133
 
120
134
  @property
121
135
  def constraint_value(self) -> Vector:
136
+ """
137
+ Returns w for {u | B(u)=w}.
138
+ Falls back to (I - P)x0 if no explicit operator exists.
139
+ """
122
140
  if self._constraint_value is None:
123
- raise AttributeError("This subspace is not defined by a linear equation.")
141
+ complement = self._projector.complement
142
+ return complement(self._translation)
124
143
  return self._constraint_value
125
144
 
126
145
  def project(self, x: Vector) -> Vector:
146
+ """Orthogonally projects x onto the affine subspace."""
127
147
  diff = self.domain.subtract(x, self.translation)
128
148
  proj_diff = self.projector(diff)
129
149
  return self.domain.add(self.translation, proj_diff)
130
150
 
131
151
  def is_element(self, x: Vector, rtol: float = 1e-6) -> bool:
152
+ """Returns True if x lies in the subspace."""
132
153
  proj = self.project(x)
133
154
  diff = self.domain.subtract(x, proj)
134
155
  norm_diff = self.domain.norm(diff)
@@ -136,6 +157,46 @@ class AffineSubspace:
136
157
  scale = norm_x if norm_x > 1e-12 else 1.0
137
158
  return norm_diff <= rtol * scale
138
159
 
160
+ def condition_gaussian_measure(
161
+ self, prior: GaussianMeasure, geometric: bool = False
162
+ ) -> GaussianMeasure:
163
+ """
164
+ Conditions a Gaussian measure on this subspace.
165
+ """
166
+ if geometric:
167
+ # Geometric Projection: u -> P(u - x0) + x0
168
+ # Affine Map: u -> P(u) + (I-P)x0
169
+ shift = self.domain.subtract(
170
+ self.translation, self.projector(self.translation)
171
+ )
172
+ return prior.affine_mapping(operator=self.projector, translation=shift)
173
+
174
+ else:
175
+ # Bayesian Conditioning: u | B(u)=w
176
+
177
+ # Check for singular implicit operator usage
178
+ if not self.has_explicit_equation and self._solver is None:
179
+ raise ValueError(
180
+ "This subspace defines the constraint implicitly as (I-P)u = (I-P)x0. "
181
+ "The operator (I-P) is singular. You must provide a solver "
182
+ "capable of handling singular systems (e.g. MinRes) to the "
183
+ "AffineSubspace constructor."
184
+ )
185
+
186
+ # Local imports
187
+ from .forward_problem import LinearForwardProblem
188
+ from .linear_bayesian import LinearBayesianInversion
189
+
190
+ solver = self._solver
191
+ preconditioner = self._preconditioner
192
+
193
+ constraint_problem = LinearForwardProblem(self.constraint_operator)
194
+ constraint_inversion = LinearBayesianInversion(constraint_problem, prior)
195
+
196
+ return constraint_inversion.model_posterior_measure(
197
+ self.constraint_value, solver, preconditioner=preconditioner
198
+ )
199
+
139
200
  @classmethod
140
201
  def from_linear_equation(
141
202
  cls,
@@ -144,7 +205,7 @@ class AffineSubspace:
144
205
  solver: Optional[LinearSolver] = None,
145
206
  preconditioner: Optional[LinearOperator] = None,
146
207
  ) -> AffineSubspace:
147
- """Constructs the subspace {u | B(u) = w}."""
208
+ """Constructs subspace from B(u)=w."""
148
209
  domain = operator.domain
149
210
  G = operator @ operator.adjoint
150
211
 
@@ -166,7 +227,12 @@ class AffineSubspace:
166
227
  projector = OrthogonalProjector(domain, mapping, complement_projector=P_perp_op)
167
228
 
168
229
  return cls(
169
- projector, translation, constraint_operator=operator, constraint_value=value
230
+ projector,
231
+ translation,
232
+ constraint_operator=operator,
233
+ constraint_value=value,
234
+ solver=solver,
235
+ preconditioner=preconditioner,
170
236
  )
171
237
 
172
238
  @classmethod
@@ -176,18 +242,38 @@ class AffineSubspace:
176
242
  basis_vectors: List[Vector],
177
243
  translation: Optional[Vector] = None,
178
244
  orthonormalize: bool = True,
245
+ solver: Optional[LinearSolver] = None,
246
+ preconditioner: Optional[LinearOperator] = None,
179
247
  ) -> AffineSubspace:
180
248
  """
181
- Constructs the subspace passing through 'translation' with the given
182
- tangent basis.
183
-
184
- Note: This does not define a constraint equation B(u)=w, so it cannot
185
- be used directly with ConstrainedLinearBayesianInversion.
249
+ Constructs an affine subspace from a translation and a basis for the tangent space.
250
+
251
+ This method defines the subspace geometrically. The constraint is implicit:
252
+ (I - P)u = (I - P)x0.
253
+
254
+ Args:
255
+ domain: The Hilbert space.
256
+ basis_vectors: Basis vectors for the tangent space V.
257
+ translation: A point x0 in the subspace.
258
+ orthonormalize: If True, orthonormalizes the basis.
259
+ solver: A linear solver capable of handling the singular operator (I-P).
260
+ Required if you intend to use this subspace for Bayesian conditioning.
261
+ preconditioner: Optional preconditioner for the solver.
186
262
  """
263
+ if solver is None:
264
+ warnings.warn(
265
+ "Constructing a subspace from a tangent basis without a solver. "
266
+ "This defines an implicit constraint with a singular operator. "
267
+ "Bayesian conditioning will fail; geometric projection remains available.",
268
+ UserWarning,
269
+ stacklevel=2,
270
+ )
271
+
187
272
  projector = OrthogonalProjector.from_basis(
188
273
  domain, basis_vectors, orthonormalize=orthonormalize
189
274
  )
190
- return cls(projector, translation)
275
+
276
+ return cls(projector, translation, solver=solver, preconditioner=preconditioner)
191
277
 
192
278
  @classmethod
193
279
  def from_complement_basis(
@@ -198,24 +284,18 @@ class AffineSubspace:
198
284
  orthonormalize: bool = True,
199
285
  ) -> AffineSubspace:
200
286
  """
201
- Constructs the subspace orthogonal to the given basis, passing through
202
- 'translation'.
203
-
204
- This automatically constructs the constraint operator B such that
205
- the subspace is {u | B(u) = B(translation)}.
287
+ Constructs subspace from complement basis.
288
+ Constraint is explicit: <u, e_i> = <x0, e_i>.
206
289
  """
207
- # 1. Orthonormalize basis for stability
208
290
  if orthonormalize:
209
291
  e_vectors = domain.gram_schmidt(basis_vectors)
210
292
  else:
211
293
  e_vectors = basis_vectors
212
294
 
213
- # 2. Construct Projector P_perp
214
295
  complement_projector = OrthogonalProjector.from_basis(
215
- domain, e_vectors, orthonormalize=False # Already done
296
+ domain, e_vectors, orthonormalize=False
216
297
  )
217
298
 
218
- # 3. Construct Projector P = I - P_perp
219
299
  def mapping(x: Any) -> Any:
220
300
  return domain.subtract(x, complement_projector(x))
221
301
 
@@ -223,16 +303,12 @@ class AffineSubspace:
223
303
  domain, mapping, complement_projector=complement_projector
224
304
  )
225
305
 
226
- # 4. Construct Constraint Operator B implicitly defined by the basis
227
- # B: E -> R^k, u -> [<e_1, u>, ..., <e_k, u>]
228
- # Since e_i are orthonormal, BB* = I, which is perfect for solvers.
229
306
  codomain = EuclideanSpace(len(e_vectors))
230
307
 
231
308
  def constraint_mapping(u: Vector) -> np.ndarray:
232
309
  return np.array([domain.inner_product(e, u) for e in e_vectors])
233
310
 
234
311
  def constraint_adjoint(c: np.ndarray) -> Vector:
235
- # sum c_i e_i
236
312
  res = domain.zero
237
313
  for i, e in enumerate(e_vectors):
238
314
  domain.axpy(c[i], e, res)
@@ -242,8 +318,6 @@ class AffineSubspace:
242
318
  domain, codomain, constraint_mapping, adjoint_mapping=constraint_adjoint
243
319
  )
244
320
 
245
- # 5. Determine Constraint Value w = B(translation)
246
- # If translation is None (zero), w is zero.
247
321
  if translation is None:
248
322
  _translation = domain.zero
249
323
  w = codomain.zero
@@ -251,12 +325,20 @@ class AffineSubspace:
251
325
  _translation = translation
252
326
  w = B(_translation)
253
327
 
254
- return cls(projector, _translation, constraint_operator=B, constraint_value=w)
328
+ solver = CholeskySolver(galerkin=True)
329
+
330
+ return cls(
331
+ projector,
332
+ _translation,
333
+ constraint_operator=B,
334
+ constraint_value=w,
335
+ solver=solver,
336
+ )
255
337
 
256
338
 
257
339
  class LinearSubspace(AffineSubspace):
258
340
  """
259
- Represents a linear subspace (an affine subspace passing through zero).
341
+ Represents a linear subspace (an affine subspace passing through the origin).
260
342
  """
261
343
 
262
344
  def __init__(self, projector: OrthogonalProjector) -> None:
@@ -272,14 +354,19 @@ class LinearSubspace(AffineSubspace):
272
354
 
273
355
  @classmethod
274
356
  def from_kernel(
275
- cls, operator: LinearOperator, solver: Optional[LinearSolver] = None
357
+ cls,
358
+ operator: LinearOperator,
359
+ solver: Optional[LinearSolver] = None,
360
+ preconditioner: Optional[LinearOperator] = None,
276
361
  ) -> LinearSubspace:
277
362
  affine = AffineSubspace.from_linear_equation(
278
- operator, operator.codomain.zero, solver
363
+ operator, operator.codomain.zero, solver, preconditioner
279
364
  )
280
365
  instance = cls(affine.projector)
281
366
  instance._constraint_operator = operator
282
367
  instance._constraint_value = operator.codomain.zero
368
+ instance._solver = affine._solver
369
+ instance._preconditioner = preconditioner
283
370
  return instance
284
371
 
285
372
  @classmethod
@@ -288,11 +375,16 @@ class LinearSubspace(AffineSubspace):
288
375
  domain: HilbertSpace,
289
376
  basis_vectors: List[Vector],
290
377
  orthonormalize: bool = True,
378
+ solver: Optional[LinearSolver] = None,
379
+ preconditioner: Optional[LinearOperator] = None,
291
380
  ) -> LinearSubspace:
292
381
  projector = OrthogonalProjector.from_basis(
293
382
  domain, basis_vectors, orthonormalize=orthonormalize
294
383
  )
295
- return cls(projector)
384
+ instance = cls(projector)
385
+ instance._solver = solver
386
+ instance._preconditioner = preconditioner
387
+ return instance
296
388
 
297
389
  @classmethod
298
390
  def from_complement_basis(
@@ -304,8 +396,8 @@ class LinearSubspace(AffineSubspace):
304
396
  affine = AffineSubspace.from_complement_basis(
305
397
  domain, basis_vectors, translation=None, orthonormalize=orthonormalize
306
398
  )
307
- # Copy constraint info from the affine instance
308
399
  instance = cls(affine.projector)
309
400
  instance._constraint_operator = affine.constraint_operator
310
401
  instance._constraint_value = affine.constraint_value
402
+ instance._solver = affine._solver
311
403
  return instance
@@ -8,19 +8,31 @@ import numpy as np
8
8
 
9
9
 
10
10
  class SHVectorConverter:
11
- """
11
+ r"""
12
12
  Handles conversion between pyshtools 3D coefficient arrays and 1D vectors.
13
13
 
14
- This class provides a bridge between the pyshtools data structure for
15
- spherical harmonic coefficients, a 3D array of shape (2, lmax+1, lmax+1),
16
- and the 1D vector format often used in linear algebra and inverse problems.
14
+ This class bridges the gap between the `pyshtools` 3D array format
15
+ (shape `[2, lmax+1, lmax+1]`) and the flat 1D vector format used in
16
+ linear algebra.
17
+
18
+ **Vector Layout:**
19
+ The output vector is ordered first by degree $l$ (ascending from `lmin` to `lmax`),
20
+ and then by order $m$ (ascending from $-l$ to $+l$).
21
+
22
+ The sequence of coefficients is:
23
+
24
+ .. math::
25
+ [u_{l_{min}, -l_{min}}, \dots, u_{l_{min}, l_{min}}, \quad
26
+ u_{l_{min}+1, -(l_{min}+1)}, \dots, u_{l_{min}+1, l_{min}+1}, \quad \dots]
27
+
28
+ **Example (lmin=0):**
17
29
 
18
- The vector is ordered by degree l, and within each degree, by order m,
19
- from -l to +l.
30
+ .. math::
31
+ [u_{0,0}, \quad u_{1,-1}, u_{1,0}, u_{1,1}, \quad u_{2,-2}, u_{2,-1}, u_{2,0}, u_{2,1}, u_{2,2}, \dots]
20
32
 
21
33
  Args:
22
34
  lmax (int): The maximum spherical harmonic degree to include.
23
- lmin (int): The minimum spherical harmonic degree to include. Defaults to 2.
35
+ lmin (int): The minimum spherical harmonic degree to include. Defaults to 0.
24
36
  """
25
37
 
26
38
  def __init__(self, lmax: int, lmin: int = 0):
@@ -532,30 +532,26 @@ class Lebesgue(SphereHelper, HilbertModule, AbstractInvariantLebesgueSpace):
532
532
  )
533
533
 
534
534
  def to_coefficient_operator(self, lmax: int, lmin: int = 0):
535
- """
536
- Returns a LinearOperator that maps an element of the space to
537
- a vector of its spherical harmonic coefficients within the
538
- specified range of degrees.
535
+ r"""
536
+ Returns a LinearOperator mapping a function to its spherical harmonic coefficients.
539
537
 
540
- The output coefficients are ordered in the following manner:
538
+ The operator maps an element of the Hilbert space to a vector in $\mathbb{R}^k$.
539
+ The coefficients in the output vector are ordered by degree $l$ (major)
540
+ and order $m$ (minor), from $-l$ to $+l$.
541
541
 
542
- u_{00}, u_{1-1}, u_{10}, u_{11}, u_{2-2}, u_{2-1}, u_{20}, u_{21}, u_{22}, ...
542
+ **Ordering:**
543
543
 
544
- in this case assuming lmin = 0.
544
+ .. math::
545
+ u = [u_{0,0}, \quad u_{1,-1}, u_{1,0}, u_{1,1}, \quad u_{2,-2}, \dots, u_{2,2}, \quad \dots]
545
546
 
546
- If lmax is larger than the field's lmax, the output will be padded by zeros.
547
+ (assuming `lmin=0`).
547
548
 
548
549
  Args:
549
550
  lmax: The maximum spherical harmonic degree to include in the output.
550
- lmin: The minimum spherical harmonic degree to include in the output.
551
- Defaults to 0.
551
+ lmin: The minimum spherical harmonic degree to include. Defaults to 0.
552
552
 
553
553
  Returns:
554
- A LinearOperator that maps an SHGrid to a NumPy vector of coefficients.
555
-
556
- Notes:
557
- This is a left inverse of the from_coefficient_operator so long a the
558
- values for lmin and lmax are equal.
554
+ A LinearOperator mapping `SHGrid` -> `numpy.ndarray`.
559
555
  """
560
556
 
561
557
  converter = SHVectorConverter(lmax, lmin)
@@ -577,27 +573,25 @@ class Lebesgue(SphereHelper, HilbertModule, AbstractInvariantLebesgueSpace):
577
573
  return LinearOperator(self, codomain, mapping, adjoint_mapping=adjoint_mapping)
578
574
 
579
575
  def from_coefficient_operator(self, lmax: int, lmin: int = 0):
580
- """
581
- Returns a LinearOperator that maps a vector of spherical harmonic coefficients
582
- to an element of the space.
576
+ r"""
577
+ Returns a LinearOperator mapping a vector of coefficients to a function.
583
578
 
584
- The input coefficients are ordered in the following manner:
579
+ The operator maps a vector in $\mathbb{R}^k$ to an element of the Hilbert space.
580
+ The input vector must follow the standard $l$-major, $m$-minor ordering.
585
581
 
586
- u_{00}, u_{1-1}, u_{10}, u_{11}, u_{2-2}, u_{2-1}, u_{20}, u_{21}, u_{22}, ...
582
+ **Ordering:**
587
583
 
588
- in this case assuming lmin = 0.
584
+ .. math::
585
+ v = [u_{0,0}, \quad u_{1,-1}, u_{1,0}, u_{1,1}, \quad u_{2,-2}, \dots, u_{2,2}, \quad \dots]
586
+
587
+ (assuming `lmin=0`).
589
588
 
590
589
  Args:
591
- lmax: The maximum spherical harmonic degree to include in the output.
592
- lmin: The minimum spherical harmonic degree to include in the output.
593
- Defaults to 0.
590
+ lmax: The maximum spherical harmonic degree expected in the input.
591
+ lmin: The minimum spherical harmonic degree expected. Defaults to 0.
594
592
 
595
593
  Returns:
596
- A LinearOperator that maps a NumPy vector of coefficients to an SHGrid.
597
-
598
- Notes:
599
- This is a right inverse of the to_coefficient_operator so long a the
600
- values for lmin and lmax are equal.
594
+ A LinearOperator mapping `numpy.ndarray` -> `SHGrid`.
601
595
  """
602
596
 
603
597
  converter = SHVectorConverter(lmax, lmin)
@@ -783,30 +777,26 @@ class Sobolev(SphereHelper, MassWeightedHilbertModule, AbstractInvariantSobolevS
783
777
  )
784
778
 
785
779
  def to_coefficient_operator(self, lmax: int, lmin: int = 0):
786
- """
787
- Returns a LinearOperator that maps an element of the space to
788
- a vector of its spherical harmonic coefficients within the
789
- specified range of degrees.
780
+ r"""
781
+ Returns a LinearOperator mapping a function to its spherical harmonic coefficients.
790
782
 
791
- The output coefficients are ordered in the following manner:
783
+ The operator maps an element of the Hilbert space to a vector in $\mathbb{R}^k$.
784
+ The coefficients in the output vector are ordered by degree $l$ (major)
785
+ and order $m$ (minor), from $-l$ to $+l$.
792
786
 
793
- u_{00}, u_{1-1}, u_{10}, u_{11}, u_{2-2}, u_{2-1}, u_{20}, u_{21}, u_{22}, ...
787
+ **Ordering:**
794
788
 
795
- in this case assuming lmin = 0.
789
+ .. math::
790
+ u = [u_{0,0}, \quad u_{1,-1}, u_{1,0}, u_{1,1}, \quad u_{2,-2}, \dots, u_{2,2}, \quad \dots]
796
791
 
797
- If lmax is larger than the field's lmax, the output will be padded by zeros.
792
+ (assuming `lmin=0`).
798
793
 
799
794
  Args:
800
795
  lmax: The maximum spherical harmonic degree to include in the output.
801
- lmin: The minimum spherical harmonic degree to include in the output.
802
- Defaults to 0.
796
+ lmin: The minimum spherical harmonic degree to include. Defaults to 0.
803
797
 
804
798
  Returns:
805
- A LinearOperator that maps an SHGrid to a NumPy vector of coefficients.
806
-
807
- Notes:
808
- This is a left inverse of the from_coefficient_operator so long a the
809
- values for lmin and lmax are equal.
799
+ A LinearOperator mapping `SHGrid` -> `numpy.ndarray`.
810
800
  """
811
801
 
812
802
  l2_operator = self.underlying_space.to_coefficient_operator(lmax, lmin)
@@ -816,27 +806,25 @@ class Sobolev(SphereHelper, MassWeightedHilbertModule, AbstractInvariantSobolevS
816
806
  )
817
807
 
818
808
  def from_coefficient_operator(self, lmax: int, lmin: int = 0):
819
- """
820
- Returns a LinearOperator that maps a vector of spherical harmonic coefficients
821
- to an element of the space.
809
+ r"""
810
+ Returns a LinearOperator mapping a vector of coefficients to a function.
822
811
 
823
- The input coefficients are ordered in the following manner:
812
+ The operator maps a vector in $\mathbb{R}^k$ to an element of the Hilbert space.
813
+ The input vector must follow the standard $l$-major, $m$-minor ordering.
824
814
 
825
- u_{00}, u_{1-1}, u_{10}, u_{11}, u_{2-2}, u_{2-1}, u_{20}, u_{21}, u_{22}, ...
815
+ **Ordering:**
826
816
 
827
- in this case assuming lmin = 0.
817
+ .. math::
818
+ v = [u_{0,0}, \quad u_{1,-1}, u_{1,0}, u_{1,1}, \quad u_{2,-2}, \dots, u_{2,2}, \quad \dots]
819
+
820
+ (assuming `lmin=0`).
828
821
 
829
822
  Args:
830
- lmax: The maximum spherical harmonic degree to include in the output.
831
- lmin: The minimum spherical harmonic degree to include in the output.
832
- Defaults to 0.
823
+ lmax: The maximum spherical harmonic degree expected in the input.
824
+ lmin: The minimum spherical harmonic degree expected. Defaults to 0.
833
825
 
834
826
  Returns:
835
- A LinearOperator that maps a NumPy vector of coefficients to an SHGrid.
836
-
837
- Notes:
838
- This is a right inverse of the to_coefficient_operator so long a the
839
- values for lmin and lmax are equal.
827
+ A LinearOperator mapping `numpy.ndarray` -> `SHGrid`.
840
828
  """
841
829
 
842
830
  l2_operator = self.underlying_space.from_coefficient_operator(lmax, lmin)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeoinf
3
- Version: 1.3.5
3
+ Version: 1.3.7
4
4
  Summary: A package for solving geophysical inference and inverse problems
5
5
  License: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -1,4 +1,4 @@
1
- pygeoinf/__init__.py,sha256=IrG0WA80NuoXRJvoKNG7lKcht5ICBEQacjtuqBKQ2fU,3622
1
+ pygeoinf/__init__.py,sha256=OdtIgD3aF4LQ4UzponWblw4nQihRntqnni7m1DPdd5I,4076
2
2
  pygeoinf/auxiliary.py,sha256=lfoTt9ZH4y8SAV8dKZi5EWx1oF_JtxtBMSmlFYqJYfE,1610
3
3
  pygeoinf/backus_gilbert.py,sha256=eFi4blSwOCsg_NuH6WD4gcgjvzvu5g5WpWahGobSBdM,3694
4
4
  pygeoinf/checks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10,24 +10,25 @@ pygeoinf/forward_problem.py,sha256=NnqWp7iMfkhHa9d-jBHzYHClaAfhKmO5D058AcJLLYg,1
10
10
  pygeoinf/gaussian_measure.py,sha256=bBh64xHgmLFl27krn9hkf8qDQjop_39x69cyhJgUHN8,26219
11
11
  pygeoinf/hilbert_space.py,sha256=Yc0Jw0A8Jo12Zpgb_9dhcF7CD77S1IDMrSTDFHpTRB8,27340
12
12
  pygeoinf/inversion.py,sha256=RV0hG2bGnciWdja0oOPKPxnFhYzufqdj-mKYNr4JJ_o,6447
13
- pygeoinf/linear_bayesian.py,sha256=o6m0fYESba8rE-rjxfCqXaV6irPthB1aWf8vhM2v8XQ,11324
13
+ pygeoinf/linear_bayesian.py,sha256=qzWEVaNe9AwG5GBmGHgVHswEMFKBWvOOJDlS95ahyxc,8877
14
14
  pygeoinf/linear_forms.py,sha256=mgZeDRegNKo8kviE68KrxkHR4gG9bf1RgsJz1MtDMCk,9181
15
15
  pygeoinf/linear_operators.py,sha256=Bn-uzwUXi2kkWZ7wc9Uhj3vBHtocN17hnzc_r7DAzTk,64530
16
- pygeoinf/linear_optimisation.py,sha256=vF1T3HE9rPOnXy3PU82-46dlvGwdAvsqUNXOx0o-KD8,20431
17
- pygeoinf/linear_solvers.py,sha256=v-7yjKsa67Ts5EcyJzCdpj-aF0qBrA-akq0kLe59DS4,16843
16
+ pygeoinf/linear_optimisation.py,sha256=RhO-1OsEDGnVHBlCtYyqp8jmW4GeGnGWGPRYPSc5GSg,13922
17
+ pygeoinf/linear_solvers.py,sha256=tYBp_ysePnOgqgKhMXhNHxLM8xi3awiwwdnKXHhmlNk,31071
18
18
  pygeoinf/nonlinear_forms.py,sha256=t7lk-Bha7Xdk9eiwXMmS0F47oTR6jW6qQ3HkgRGk54A,7012
19
19
  pygeoinf/nonlinear_operators.py,sha256=AtkDTQfGDzAnfFDIgiKfdk7uPEI-j_ZA3CNvY5A3U8w,7144
20
20
  pygeoinf/nonlinear_optimisation.py,sha256=skK1ikn9GrVYherD64Qt9WrEYHA2NAJ48msOu_J8Oig,7431
21
21
  pygeoinf/parallel.py,sha256=VVFvNHszy4wSa9LuErIsch4NAkLaZezhdN9YpRROBJo,2267
22
22
  pygeoinf/plot.py,sha256=Uw9PCdxymUiAkFF0BS0kUAZBRWL6sh89FJnSIxtp_2s,13664
23
- pygeoinf/random_matrix.py,sha256=71l6eAXQ2pRMleaz1lXud6O1F78ugKyp3vHcRBXhdwM,17661
24
- pygeoinf/subspaces.py,sha256=5Bo1F9if5oEmKJ0YPWMFaajJKec5aGIrYZKvetwWa3c,10454
23
+ pygeoinf/preconditioners.py,sha256=81PnzoQZzsf5mvXBYsHuadf1CdiGFlMbQn_tC2xPQ1k,4503
24
+ pygeoinf/random_matrix.py,sha256=-U_3-yrVos_86EfNy1flULsWY-Y9G9Yy1GKoSS2gn60,17828
25
+ pygeoinf/subspaces.py,sha256=FJobjDRr8JG1zz-TjBsncJ1M5phQYwbttlaGuJz9ycU,13779
25
26
  pygeoinf/symmetric_space/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
27
  pygeoinf/symmetric_space/circle.py,sha256=GuwVmLdHGTMxMrZfyXIPP3pz_y971ntlD5pl42lKJZ0,18796
27
- pygeoinf/symmetric_space/sh_tools.py,sha256=_A_coPElu8DNYwA5udL92X87wiTyBClTbFtf6ch9nXM,3520
28
- pygeoinf/symmetric_space/sphere.py,sha256=wZlAVG3kKMuPmRURL4r2ZP3sQ8kWOAWRk1G_Lo_TZmo,28983
28
+ pygeoinf/symmetric_space/sh_tools.py,sha256=EDZm0YRZefvCfDjAKZatZMM3UqeTi-Npiflnc1E5slk,3884
29
+ pygeoinf/symmetric_space/sphere.py,sha256=wYaZ2wqkQAHw9pn4vP_6LR9HAXSpzCncCh24xmSSC5A,28481
29
30
  pygeoinf/symmetric_space/symmetric_space.py,sha256=pEIZZYWsdegrYCwUs3bo86JTz3d2LsXFWdRYFa0syFs,17963
30
- pygeoinf-1.3.5.dist-info/METADATA,sha256=m0YVVuJ1McMBzUG1QYdurV1hB91UGmIiSn-STSq9a0s,16482
31
- pygeoinf-1.3.5.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
32
- pygeoinf-1.3.5.dist-info/licenses/LICENSE,sha256=GrTQnKJemVi69FSbHprq60KN0OJGsOSR-joQoTq-oD8,1501
33
- pygeoinf-1.3.5.dist-info/RECORD,,
31
+ pygeoinf-1.3.7.dist-info/METADATA,sha256=rJugIyw0YNv6ccIFCnXNmISbwUclP-V8Zt1ZDLbqPpw,16482
32
+ pygeoinf-1.3.7.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
33
+ pygeoinf-1.3.7.dist-info/licenses/LICENSE,sha256=GrTQnKJemVi69FSbHprq60KN0OJGsOSR-joQoTq-oD8,1501
34
+ pygeoinf-1.3.7.dist-info/RECORD,,