pygeoinf 1.3.8__tar.gz → 1.3.9__tar.gz

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.
Files changed (36) hide show
  1. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/PKG-INFO +2 -1
  2. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/__init__.py +36 -0
  3. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/gaussian_measure.py +42 -12
  4. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/plot.py +7 -1
  5. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/preconditioners.py +1 -1
  6. pygeoinf-1.3.9/pygeoinf/subsets.py +845 -0
  7. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/subspaces.py +173 -23
  8. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/symmetric_space/sphere.py +1 -1
  9. pygeoinf-1.3.9/pygeoinf/utils.py +15 -0
  10. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pyproject.toml +2 -1
  11. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/LICENSE +0 -0
  12. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/README.md +0 -0
  13. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/auxiliary.py +0 -0
  14. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/backus_gilbert.py +0 -0
  15. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/checks/__init__.py +0 -0
  16. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/checks/hilbert_space.py +0 -0
  17. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/checks/linear_operators.py +0 -0
  18. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/checks/nonlinear_operators.py +0 -0
  19. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/direct_sum.py +0 -0
  20. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/forward_problem.py +0 -0
  21. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/hilbert_space.py +0 -0
  22. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/inversion.py +0 -0
  23. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/linear_bayesian.py +0 -0
  24. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/linear_forms.py +0 -0
  25. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/linear_operators.py +0 -0
  26. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/linear_optimisation.py +0 -0
  27. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/linear_solvers.py +0 -0
  28. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/nonlinear_forms.py +0 -0
  29. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/nonlinear_operators.py +0 -0
  30. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/nonlinear_optimisation.py +0 -0
  31. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/parallel.py +0 -0
  32. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/random_matrix.py +0 -0
  33. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/symmetric_space/__init__.py +0 -0
  34. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/symmetric_space/circle.py +0 -0
  35. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/symmetric_space/sh_tools.py +0 -0
  36. {pygeoinf-1.3.8 → pygeoinf-1.3.9}/pygeoinf/symmetric_space/symmetric_space.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygeoinf
3
- Version: 1.3.8
3
+ Version: 1.3.9
4
4
  Summary: A package for solving geophysical inference and inverse problems
5
5
  License: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -20,6 +20,7 @@ Requires-Dist: numpy (>=1.26.0)
20
20
  Requires-Dist: pyqt6 (>=6.0.0)
21
21
  Requires-Dist: pyshtools (>=4.0.0) ; extra == "sphere"
22
22
  Requires-Dist: scipy (>=1.16.1)
23
+ Requires-Dist: threadpoolctl (>=3.6.0,<4.0.0)
23
24
  Description-Content-Type: text/markdown
24
25
 
25
26
  # pygeoinf: A Python Library for Geophysical Inference
@@ -104,8 +104,27 @@ from .nonlinear_optimisation import (
104
104
 
105
105
  from .subspaces import OrthogonalProjector, AffineSubspace, LinearSubspace
106
106
 
107
+ from .subsets import (
108
+ Subset,
109
+ EmptySet,
110
+ UniversalSet,
111
+ Complement,
112
+ Intersection,
113
+ Union,
114
+ SublevelSet,
115
+ LevelSet,
116
+ ConvexSubset,
117
+ Ellipsoid,
118
+ NormalisedEllipsoid,
119
+ EllipsoidSurface,
120
+ Ball,
121
+ Sphere,
122
+ )
123
+
107
124
  from .plot import plot_1d_distributions, plot_corner_distributions
108
125
 
126
+ from .utils import configure_threading
127
+
109
128
  __all__ = [
110
129
  # random_matrix
111
130
  "fixed_rank_random_range",
@@ -184,7 +203,24 @@ __all__ = [
184
203
  "OrthogonalProjector",
185
204
  "AffineSubspace",
186
205
  "LinearSubspace",
206
+ # Subsets
207
+ "Subset",
208
+ "EmptySet",
209
+ "UniversalSet",
210
+ "Complement",
211
+ "Intersection",
212
+ "Union",
213
+ "SublevelSet",
214
+ "LevelSet",
215
+ "ConvexSubset",
216
+ "Ellipsoid",
217
+ "NormalisedEllipsoid",
218
+ "EllipsoidSurface",
219
+ "Ball",
220
+ "Sphere",
187
221
  # plot
188
222
  "plot_1d_distributions",
189
223
  "plot_corner_distributions",
224
+ # utils
225
+ "configure_threading",
190
226
  ]
@@ -27,7 +27,7 @@ import numpy as np
27
27
  from scipy.linalg import eigh
28
28
  from scipy.sparse import diags
29
29
  from scipy.stats import multivariate_normal
30
-
30
+ from joblib import Parallel, delayed
31
31
 
32
32
  from .hilbert_space import EuclideanSpace, HilbertModule, Vector
33
33
 
@@ -44,7 +44,6 @@ from .direct_sum import (
44
44
  # This block is only processed by type checkers, not at runtime.
45
45
  if TYPE_CHECKING:
46
46
  from .hilbert_space import HilbertSpace
47
- from .typing import Vector
48
47
 
49
48
 
50
49
  class GaussianMeasure:
@@ -402,24 +401,52 @@ class GaussianMeasure:
402
401
  raise NotImplementedError("A sample method is not set for this measure.")
403
402
  return self._sample()
404
403
 
405
- def samples(self, n: int) -> List[Vector]:
406
- """Returns a list of n random samples from the measure."""
404
+ def samples(
405
+ self, n: int, /, *, parallel: bool = False, n_jobs: int = -1
406
+ ) -> List[Vector]:
407
+ """
408
+ Returns a list of n random samples from the measure.
409
+
410
+ Args:
411
+ n: Number of samples to draw.
412
+ parallel: If True, draws samples in parallel.
413
+ n_jobs: Number of CPU cores to use. -1 means all available.
414
+ """
407
415
  if n < 1:
408
416
  raise ValueError("Number of samples must be a positive integer.")
409
- return [self.sample() for _ in range(n)]
410
417
 
411
- def sample_expectation(self, n: int) -> Vector:
412
- """Estimates the expectation by drawing n samples."""
418
+ if not parallel:
419
+ return [self.sample() for _ in range(n)]
420
+
421
+ return Parallel(n_jobs=n_jobs)(delayed(self.sample)() for _ in range(n))
422
+
423
+ def sample_expectation(
424
+ self, n: int, /, *, parallel: bool = False, n_jobs: int = -1
425
+ ) -> Vector:
426
+ """
427
+ Estimates the expectation by drawing n samples.
428
+
429
+ Args:
430
+ n: Number of samples to draw.
431
+ parallel: If True, draws samples in parallel.
432
+ n_jobs: Number of CPU cores to use. -1 means all available.
433
+ """
413
434
  if n < 1:
414
435
  raise ValueError("Number of samples must be a positive integer.")
415
- return self.domain.sample_expectation(self.samples(n))
436
+ return self.domain.sample_expectation(
437
+ self.samples(n, parallel=parallel, n_jobs=n_jobs)
438
+ )
416
439
 
417
- def sample_pointwise_variance(self, n: int) -> Vector:
440
+ def sample_pointwise_variance(
441
+ self, n: int, /, *, parallel: bool = False, n_jobs: int = -1
442
+ ) -> Vector:
418
443
  """
419
444
  Estimates the pointwise variance by drawing n samples.
420
445
 
421
- This method is only available if the domain supports vector
422
- multiplication.
446
+ Args:
447
+ n: Number of samples to draw.
448
+ parallel: If True, draws samples in parallel.
449
+ n_jobs: Number of CPU cores to use. -1 means all available.
423
450
  """
424
451
  if not isinstance(self.domain, HilbertModule):
425
452
  raise NotImplementedError(
@@ -428,7 +455,10 @@ class GaussianMeasure:
428
455
  if n < 1:
429
456
  raise ValueError("Number of samples must be a positive integer.")
430
457
 
431
- samples = self.samples(n)
458
+ # Step 1: Draw samples (Parallelized)
459
+ samples = self.samples(n, parallel=parallel, n_jobs=n_jobs)
460
+
461
+ # Step 2: Compute variance using vector arithmetic
432
462
  expectation = self.expectation
433
463
  variance = self.domain.zero
434
464
 
@@ -220,6 +220,8 @@ def plot_corner_distributions(
220
220
  show_plot: bool = True,
221
221
  include_sigma_contours: bool = True,
222
222
  colormap: str = "Blues",
223
+ parallel: bool = False,
224
+ n_jobs: int = -1,
223
225
  ):
224
226
  """
225
227
  Create a corner plot for multi-dimensional posterior distributions.
@@ -233,6 +235,8 @@ def plot_corner_distributions(
233
235
  show_plot: Whether to display the plot
234
236
  include_sigma_contours: Whether to include 1-sigma contour lines
235
237
  colormap: Colormap for 2D plots
238
+ parallel: Compute dense covariance matrix in parallel, default False.
239
+ n_jobs: Number of cores to use in parallel calculations, default -1.
236
240
 
237
241
  Returns:
238
242
  fig, axes: Figure and axes array
@@ -243,7 +247,9 @@ def plot_corner_distributions(
243
247
  posterior_measure, "covariance"
244
248
  ):
245
249
  mean_posterior = posterior_measure.expectation
246
- cov_posterior = posterior_measure.covariance.matrix(dense=True, parallel=True)
250
+ cov_posterior = posterior_measure.covariance.matrix(
251
+ dense=True, parallel=parallel, n_jobs=n_jobs
252
+ )
247
253
  else:
248
254
  raise ValueError(
249
255
  "posterior_measure must have 'expectation' and 'covariance' attributes"
@@ -36,7 +36,7 @@ class JacobiPreconditioningMethod(LinearSolver):
36
36
  method: str = "variable",
37
37
  rtol: float = 1e-2,
38
38
  block_size: int = 10,
39
- parallel: bool = True,
39
+ parallel: bool = False,
40
40
  n_jobs: int = -1,
41
41
  ) -> None:
42
42
  # Damping is removed: the operator passed to __call__ is already damped