pygeoinf 1.0.8__py3-none-any.whl → 1.0.9__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/direct_sum.py CHANGED
@@ -119,12 +119,10 @@ class HilbertSpaceDirectSum(HilbertSpace):
119
119
  """
120
120
  if len(xps) != self.number_of_subspaces:
121
121
  raise ValueError("Incorrect number of dual vectors provided.")
122
- return LinearForm(
123
- self,
124
- mapping=lambda x: sum(
125
- xp(self.subspace_projection(i)(x)) for i, xp in enumerate(xps)
126
- ),
127
- )
122
+
123
+ cps = [space.dual.to_components(xp) for space, xp in zip(self._spaces, xps)]
124
+ cp = np.concatenate(cps, 0)
125
+ return LinearForm(self, components=cp)
128
126
 
129
127
  def canonical_dual_inverse_isomorphism(self, xp: LinearForm) -> List[LinearForm]:
130
128
  """
@@ -136,12 +134,17 @@ class HilbertSpaceDirectSum(HilbertSpace):
136
134
  Args:
137
135
  xp (LinearForm): A dual vector on the direct sum space.
138
136
  """
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
137
 
144
- # ... (Private methods remain the same) ...
138
+ cp = self.dual.to_components(xp)
139
+ xps = []
140
+ i = 0
141
+ for space in self._spaces:
142
+ j = i + space.dim
143
+ xp = space.dual.from_components(cp[i:j])
144
+ xps.append(xp)
145
+ i = j
146
+ return xps
147
+
145
148
  def __to_components(self, xs: List[Any]) -> np.ndarray:
146
149
  cs = [space.to_components(x) for space, x in zip(self._spaces, xs)]
147
150
  return np.concatenate(cs, 0)
@@ -204,7 +207,6 @@ class BlockStructure(ABC):
204
207
  An abstract base class for operators with a block structure.
205
208
  """
206
209
 
207
- # ... (class content is the same) ...
208
210
  def __init__(self, row_dim: int, col_dim: int) -> None:
209
211
  self._row_dim: int = row_dim
210
212
  self._col_dim: int = col_dim
@@ -245,7 +247,7 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
245
247
  """
246
248
  if not blocks or not blocks[0]:
247
249
  raise ValueError("Block structure cannot be empty.")
248
- # ... (rest of the method is the same) ...
250
+
249
251
  domains = [operator.domain for operator in blocks[0]]
250
252
  codomains = []
251
253
  for row in blocks:
@@ -281,7 +283,7 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
281
283
  return self._blocks[i][j]
282
284
 
283
285
  def __mapping(self, xs: List[Any]) -> List[Any]:
284
- # ... (method content is the same) ...
286
+
285
287
  ys = []
286
288
  for i in range(self.row_dim):
287
289
  codomain = self._codomains[i]
@@ -293,7 +295,7 @@ class BlockLinearOperator(LinearOperator, BlockStructure):
293
295
  return ys
294
296
 
295
297
  def __adjoint_mapping(self, ys: List[Any]) -> List[Any]:
296
- # ... (method content is the same) ...
298
+
297
299
  xs = []
298
300
  for j in range(self.col_dim):
299
301
  domain = self._domains[j]
pygeoinf/forms.py CHANGED
@@ -17,6 +17,9 @@ class LinearForm:
17
17
  """
18
18
  Represents a linear form, which is a linear functional that maps
19
19
  vectors from a Hilbert space to a scalar value (a real number).
20
+
21
+ Internally, the form is represented by its components relative to the basis for
22
+ its domain, with these components stored as in a numpy vector.
20
23
  """
21
24
 
22
25
  def __init__(
@@ -40,19 +43,19 @@ class LinearForm:
40
43
  """
41
44
 
42
45
  self._domain: "HilbertSpace" = domain
43
- self._components: Optional[np.ndarray] = components
44
- self._mapping: Callable[[Any], float]
45
46
 
46
47
  if components is None:
47
48
  if mapping is None:
48
49
  raise AssertionError("Neither mapping nor components specified.")
49
- else:
50
- self._mapping = mapping
50
+ self._components = np.zeros(self.domain.dim)
51
+ cx = np.zeros(self.domain.dim)
52
+ for i in range(self.domain.dim):
53
+ cx[i] = 1
54
+ x = self.domain.from_components(cx)
55
+ self._components[i] = mapping(x)
56
+ cx[i] = 0
51
57
  else:
52
- if mapping is None:
53
- self._mapping = self._mapping_from_components
54
- else:
55
- self._mapping = mapping
58
+ self._components: np.ndarray = components
56
59
 
57
60
  @staticmethod
58
61
  def from_linear_operator(operator: "LinearOperator") -> "LinearForm":
@@ -69,35 +72,12 @@ class LinearForm:
69
72
  """The Hilbert space on which the form is defined."""
70
73
  return self._domain
71
74
 
72
- @property
73
- def components_stored(self) -> bool:
74
- """True if the form's component vector is cached."""
75
- return self._components is not None
76
-
77
75
  @property
78
76
  def components(self) -> np.ndarray:
79
77
  """
80
78
  The component vector of the form.
81
-
82
- The components are computed and cached on first access if not
83
- provided during initialization.
84
79
  """
85
- if self.components_stored:
86
- return self._components
87
- else:
88
- self.store_components()
89
- return self.components
90
-
91
- def store_components(self) -> None:
92
- """Computes and caches the component vector of the form."""
93
- if not self.components_stored:
94
- self._components = np.zeros(self.domain.dim)
95
- cx = np.zeros(self.domain.dim)
96
- for i in range(self.domain.dim):
97
- cx[i] = 1
98
- x = self.domain.from_components(cx)
99
- self._components[i] = self(x)
100
- cx[i] = 0
80
+ return self._components
101
81
 
102
82
  @property
103
83
  def as_linear_operator(self) -> "LinearOperator":
@@ -117,21 +97,15 @@ class LinearForm:
117
97
 
118
98
  def __call__(self, x: Any) -> float:
119
99
  """Applies the linear form to a vector."""
120
- return self._mapping(x)
100
+ return np.dot(self._components, self.domain.to_components(x))
121
101
 
122
102
  def __neg__(self) -> "LinearForm":
123
103
  """Returns the additive inverse of the form."""
124
- if self.components_stored:
125
- return LinearForm(self.domain, components=-self._components)
126
- else:
127
- return LinearForm(self.domain, mapping=lambda x: -self(x))
104
+ return LinearForm(self.domain, components=-self._components)
128
105
 
129
106
  def __mul__(self, a: float) -> "LinearForm":
130
107
  """Returns the product of the form and a scalar."""
131
- if self.components_stored:
132
- return LinearForm(self.domain, components=a * self._components)
133
- else:
134
- return LinearForm(self.domain, mapping=lambda x: a * self(x))
108
+ return LinearForm(self.domain, components=a * self._components)
135
109
 
136
110
  def __rmul__(self, a: float) -> "LinearForm":
137
111
  """Returns the product of the form and a scalar."""
@@ -143,26 +117,12 @@ class LinearForm:
143
117
 
144
118
  def __add__(self, other: "LinearForm") -> "LinearForm":
145
119
  """Returns the sum of this form and another."""
146
- if self.components_stored and other.components_stored:
147
- return LinearForm(
148
- self.domain, components=self.components + other.components
149
- )
150
- else:
151
- return LinearForm(self.domain, mapping=lambda x: self(x) + other(x))
120
+ return LinearForm(self.domain, components=self.components + other.components)
152
121
 
153
122
  def __sub__(self, other: "LinearForm") -> "LinearForm":
154
123
  """Returns the difference between this form and another."""
155
- if self.components_stored and other.components_stored:
156
- return LinearForm(
157
- self.domain, components=self.components - other.components
158
- )
159
- else:
160
- return LinearForm(self.domain, mapping=lambda x: self(x) - other(x))
124
+ return LinearForm(self.domain, components=self.components - other.components)
161
125
 
162
126
  def __str__(self) -> str:
163
127
  """Returns the string representation of the form's components."""
164
128
  return self.components.__str__()
165
-
166
- def _mapping_from_components(self, x: Any) -> float:
167
- """Implements the action of the form using its cached components."""
168
- return np.dot(self._components, self.domain.to_components(x))
@@ -221,6 +221,19 @@ class Sobolev(SymmetricSpaceSobolev):
221
221
  # Public methods #
222
222
  # ==============================================#
223
223
 
224
+ def project_function(self, f: Callable[[(float, float)], float]) -> np.ndarray:
225
+ """
226
+ Returns an element of the space by projecting a given function.
227
+
228
+ Args:
229
+ f: A function that takes a point `(lat, lon)` and returns a value.
230
+ """
231
+ u = self.zero
232
+ for j, lon in enumerate(u.lons()):
233
+ for i, lat in enumerate(u.lats()):
234
+ u.data[i, j] = f((lat, lon))
235
+ return u
236
+
224
237
  def random_point(self) -> List[float]:
225
238
  """Returns a random point as `[latitude, longitude]`."""
226
239
  latitude = np.random.uniform(-90.0, 90.0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pygeoinf
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Summary: A package for solving geophysical inference and inverse problems
5
5
  License: BSD-3-Clause
6
6
  Author: David Al-Attar and Dan Heathcote
@@ -16,6 +16,7 @@ Requires-Dist: numpy (>=1.26.0)
16
16
  Requires-Dist: pyqt6 (>=6.0.0)
17
17
  Requires-Dist: pyshtools (>=4.0.0)
18
18
  Requires-Dist: scipy (>=1.0.0)
19
+ Requires-Dist: snakeviz (>=2.2.2,<3.0.0)
19
20
  Description-Content-Type: text/markdown
20
21
 
21
22
  # pygeoinf: A Python Library for Geophysical Inference
@@ -1,6 +1,6 @@
1
1
  pygeoinf/__init__.py,sha256=PsUfD2hfc-eoIz6fHSC-Mn3MHflaU07W2mHT6fLYKr0,1076
2
- pygeoinf/direct_sum.py,sha256=_ZMtK8EQ02nWpU0yYSqeEieR3ZHZgIyN8fgo9jqkm5k,15867
3
- pygeoinf/forms.py,sha256=X3vftbjkUKb4m21BDg-Z5umlc1ibAl47ml40i1Fv33E,5815
2
+ pygeoinf/direct_sum.py,sha256=jgG1BTDfoU7rg_YYBKWZU08Ugj9Vfo7PaDuF3XXIcOo,15706
3
+ pygeoinf/forms.py,sha256=ddgXrRmM3CCBWjSE2RQUFbIlEiBCAlnVVIRwnQ50TaA,4362
4
4
  pygeoinf/forward_problem.py,sha256=4V40qvHN4qAu8CXsxGAuJ5F9oukqXbXbGCARFpBYaD0,9546
5
5
  pygeoinf/gaussian_measure.py,sha256=UEFViRtLYU730U4upV33KB_ksg78tpW0dwGVFgt1hOw,21595
6
6
  pygeoinf/hilbert_space.py,sha256=m0wbzm2XIITs8KenwrNrgvRKilsf-KrX6SR0ylKyhLg,14342
@@ -13,9 +13,9 @@ pygeoinf/random_matrix.py,sha256=d6apC9Sd5ud-QgLnvvXaURdar3LiYJPwz-CYbDojH54,754
13
13
  pygeoinf/symmetric_space/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  pygeoinf/symmetric_space/circle.py,sha256=TWjQwX42EvZ4-zOIJKATo8lVg0YbXRaQgxVzj6xXYlM,12467
15
15
  pygeoinf/symmetric_space/line.py,sha256=FoZL5qVEVWzOkWwhbmwsg3D_0ceX9Nj8L8OJXQevtr4,13203
16
- pygeoinf/symmetric_space/sphere.py,sha256=YezUyB3TfsY6Bb0M6hVVXSA00HDkcyO8I-FMy_I0lOA,24215
16
+ pygeoinf/symmetric_space/sphere.py,sha256=re-TjugmBABcBpR5LEMhSCxJn0T_h3TRm4tjyGGwTyE,24664
17
17
  pygeoinf/symmetric_space/symmetric_space.py,sha256=E-8lXZmW5WWaHgX-YTu--NwV6dXP3M7hIP3J3H6uv3U,9067
18
- pygeoinf-1.0.8.dist-info/LICENSE,sha256=GrTQnKJemVi69FSbHprq60KN0OJGsOSR-joQoTq-oD8,1501
19
- pygeoinf-1.0.8.dist-info/METADATA,sha256=9kra1ONFxBefYI7LkLzHM2NYkax8RwhHtrGbJT3lZY4,14375
20
- pygeoinf-1.0.8.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
21
- pygeoinf-1.0.8.dist-info/RECORD,,
18
+ pygeoinf-1.0.9.dist-info/LICENSE,sha256=GrTQnKJemVi69FSbHprq60KN0OJGsOSR-joQoTq-oD8,1501
19
+ pygeoinf-1.0.9.dist-info/METADATA,sha256=HkPhf8yGiBK2x6bjYgATq-6rr8Y7oVxib31fBqcz_Po,14416
20
+ pygeoinf-1.0.9.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
21
+ pygeoinf-1.0.9.dist-info/RECORD,,