CUQIpy 1.1.1.post0.dev36__py3-none-any.whl → 1.4.1.post0.dev124__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.

Potentially problematic release.


This version of CUQIpy might be problematic. Click here for more details.

Files changed (92) hide show
  1. cuqi/__init__.py +2 -0
  2. cuqi/_version.py +3 -3
  3. cuqi/algebra/__init__.py +2 -0
  4. cuqi/algebra/_abstract_syntax_tree.py +358 -0
  5. cuqi/algebra/_ordered_set.py +82 -0
  6. cuqi/algebra/_random_variable.py +457 -0
  7. cuqi/array/_array.py +4 -13
  8. cuqi/config.py +7 -0
  9. cuqi/density/_density.py +9 -1
  10. cuqi/distribution/__init__.py +3 -2
  11. cuqi/distribution/_beta.py +7 -11
  12. cuqi/distribution/_cauchy.py +2 -2
  13. cuqi/distribution/_custom.py +0 -6
  14. cuqi/distribution/_distribution.py +31 -45
  15. cuqi/distribution/_gamma.py +7 -3
  16. cuqi/distribution/_gaussian.py +2 -12
  17. cuqi/distribution/_inverse_gamma.py +4 -10
  18. cuqi/distribution/_joint_distribution.py +112 -15
  19. cuqi/distribution/_lognormal.py +0 -7
  20. cuqi/distribution/{_modifiedhalfnormal.py → _modified_half_normal.py} +23 -23
  21. cuqi/distribution/_normal.py +34 -7
  22. cuqi/distribution/_posterior.py +9 -0
  23. cuqi/distribution/_truncated_normal.py +129 -0
  24. cuqi/distribution/_uniform.py +47 -1
  25. cuqi/experimental/__init__.py +2 -2
  26. cuqi/experimental/_recommender.py +216 -0
  27. cuqi/geometry/__init__.py +2 -0
  28. cuqi/geometry/_geometry.py +15 -1
  29. cuqi/geometry/_product_geometry.py +181 -0
  30. cuqi/implicitprior/__init__.py +5 -3
  31. cuqi/implicitprior/_regularized_gaussian.py +483 -0
  32. cuqi/implicitprior/{_regularizedGMRF.py → _regularized_gmrf.py} +4 -2
  33. cuqi/implicitprior/{_regularizedUnboundedUniform.py → _regularized_unbounded_uniform.py} +3 -2
  34. cuqi/implicitprior/_restorator.py +269 -0
  35. cuqi/legacy/__init__.py +2 -0
  36. cuqi/{experimental/mcmc → legacy/sampler}/__init__.py +7 -11
  37. cuqi/legacy/sampler/_conjugate.py +55 -0
  38. cuqi/legacy/sampler/_conjugate_approx.py +52 -0
  39. cuqi/legacy/sampler/_cwmh.py +196 -0
  40. cuqi/legacy/sampler/_gibbs.py +231 -0
  41. cuqi/legacy/sampler/_hmc.py +335 -0
  42. cuqi/{experimental/mcmc → legacy/sampler}/_langevin_algorithm.py +82 -111
  43. cuqi/legacy/sampler/_laplace_approximation.py +184 -0
  44. cuqi/legacy/sampler/_mh.py +190 -0
  45. cuqi/legacy/sampler/_pcn.py +244 -0
  46. cuqi/{experimental/mcmc → legacy/sampler}/_rto.py +132 -90
  47. cuqi/legacy/sampler/_sampler.py +182 -0
  48. cuqi/likelihood/_likelihood.py +9 -1
  49. cuqi/model/__init__.py +1 -1
  50. cuqi/model/_model.py +1361 -359
  51. cuqi/pde/__init__.py +4 -0
  52. cuqi/pde/_observation_map.py +36 -0
  53. cuqi/pde/_pde.py +134 -33
  54. cuqi/problem/_problem.py +93 -87
  55. cuqi/sampler/__init__.py +120 -8
  56. cuqi/sampler/_conjugate.py +376 -35
  57. cuqi/sampler/_conjugate_approx.py +40 -16
  58. cuqi/sampler/_cwmh.py +132 -138
  59. cuqi/{experimental/mcmc → sampler}/_direct.py +1 -1
  60. cuqi/sampler/_gibbs.py +288 -130
  61. cuqi/sampler/_hmc.py +328 -201
  62. cuqi/sampler/_langevin_algorithm.py +284 -100
  63. cuqi/sampler/_laplace_approximation.py +87 -117
  64. cuqi/sampler/_mh.py +47 -157
  65. cuqi/sampler/_pcn.py +65 -213
  66. cuqi/sampler/_rto.py +211 -142
  67. cuqi/sampler/_sampler.py +553 -136
  68. cuqi/samples/__init__.py +1 -1
  69. cuqi/samples/_samples.py +24 -18
  70. cuqi/solver/__init__.py +6 -4
  71. cuqi/solver/_solver.py +230 -26
  72. cuqi/testproblem/_testproblem.py +2 -3
  73. cuqi/utilities/__init__.py +6 -1
  74. cuqi/utilities/_get_python_variable_name.py +2 -2
  75. cuqi/utilities/_utilities.py +182 -2
  76. {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info}/METADATA +10 -6
  77. cuqipy-1.4.1.post0.dev124.dist-info/RECORD +101 -0
  78. {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info}/WHEEL +1 -1
  79. CUQIpy-1.1.1.post0.dev36.dist-info/RECORD +0 -92
  80. cuqi/experimental/mcmc/_conjugate.py +0 -197
  81. cuqi/experimental/mcmc/_conjugate_approx.py +0 -81
  82. cuqi/experimental/mcmc/_cwmh.py +0 -191
  83. cuqi/experimental/mcmc/_gibbs.py +0 -268
  84. cuqi/experimental/mcmc/_hmc.py +0 -470
  85. cuqi/experimental/mcmc/_laplace_approximation.py +0 -156
  86. cuqi/experimental/mcmc/_mh.py +0 -78
  87. cuqi/experimental/mcmc/_pcn.py +0 -89
  88. cuqi/experimental/mcmc/_sampler.py +0 -561
  89. cuqi/experimental/mcmc/_utilities.py +0 -17
  90. cuqi/implicitprior/_regularizedGaussian.py +0 -323
  91. {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info/licenses}/LICENSE +0 -0
  92. {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,182 @@
1
+ from abc import ABC, abstractmethod
2
+ import sys
3
+ import numpy as np
4
+ import cuqi
5
+ from cuqi.samples import Samples
6
+ import warnings
7
+
8
+ class Sampler(ABC):
9
+
10
+ def __init__(self, target, x0=None, dim=None, callback=None):
11
+
12
+ warnings.warn(f"\nYou are using the legacy sampler '{self.__class__.__name__}'.\n"
13
+ f"This will be removed in a future release of CUQIpy.\n"
14
+ f"Please consider using the new samplers in the 'cuqi.sampler' module.\n", UserWarning, stacklevel=2)
15
+
16
+ self._dim = dim
17
+ if hasattr(target,'dim'):
18
+ if self._dim is None:
19
+ self._dim = target.dim
20
+ elif self._dim != target.dim:
21
+ raise ValueError("'dim' need to be None or equal to 'target.dim'")
22
+ elif x0 is not None:
23
+ self._dim = len(x0)
24
+
25
+ self.target = target
26
+
27
+ if x0 is None:
28
+ x0 = np.ones(self.dim)
29
+ self.x0 = x0
30
+
31
+ self.callback = callback
32
+
33
+ def step(self, x):
34
+ """
35
+ Perform a single MCMC step
36
+ """
37
+ # Currently a hack to get step method for any sampler
38
+ self.x0 = x
39
+ return self.sample(2).samples[:,-1]
40
+
41
+ def step_tune(self, x, *args, **kwargs):
42
+ """
43
+ Perform a single MCMC step and tune the sampler. This is used during burn-in.
44
+ """
45
+ # Currently a hack to get step method for any sampler
46
+ out = self.step(x)
47
+ self.tune(*args, *kwargs)
48
+ return out
49
+
50
+ def tune(self):
51
+ """
52
+ Tune the sampler parameters.
53
+ """
54
+ pass
55
+
56
+
57
+ @property
58
+ def geometry(self):
59
+ if hasattr(self, 'target') and hasattr(self.target, 'geometry'):
60
+ geom = self.target.geometry
61
+ else:
62
+ geom = cuqi.geometry._DefaultGeometry1D(self.dim)
63
+ return geom
64
+
65
+ @property
66
+ def target(self):
67
+ return self._target
68
+
69
+ @target.setter
70
+ def target(self, value):
71
+ if not isinstance(value, cuqi.distribution.Distribution) and callable(value):
72
+ # obtain self.dim
73
+ if self.dim is not None:
74
+ dim = self.dim
75
+ else:
76
+ raise ValueError(f"If 'target' is a lambda function, the parameter 'dim' need to be specified when initializing {self.__class__}.")
77
+
78
+ # set target
79
+ self._target = cuqi.distribution.UserDefinedDistribution(logpdf_func=value, dim = dim)
80
+
81
+ elif isinstance(value, cuqi.distribution.Distribution):
82
+ self._target = value
83
+ else:
84
+ raise ValueError("'target' need to be either a lambda function or of type 'cuqi.distribution.Distribution'")
85
+
86
+
87
+ @property
88
+ def dim(self):
89
+ if hasattr(self,'target') and hasattr(self.target,'dim'):
90
+ self._dim = self.target.dim
91
+ return self._dim
92
+
93
+
94
+ def sample(self,N,Nb=0):
95
+ # Get samples from the samplers sample method
96
+ result = self._sample(N,Nb)
97
+ return self._create_Sample_object(result,N+Nb)
98
+
99
+ def sample_adapt(self,N,Nb=0):
100
+ # Get samples from the samplers sample method
101
+ result = self._sample_adapt(N,Nb)
102
+ return self._create_Sample_object(result,N+Nb)
103
+
104
+ def _create_Sample_object(self,result,N):
105
+ loglike_eval = None
106
+ acc_rate = None
107
+ if isinstance(result,tuple):
108
+ #Unpack samples+loglike+acc_rate
109
+ s = result[0]
110
+ if len(result)>1: loglike_eval = result[1]
111
+ if len(result)>2: acc_rate = result[2]
112
+ if len(result)>3: raise TypeError("Expected tuple of at most 3 elements from sampling method.")
113
+ else:
114
+ s = result
115
+
116
+ #Store samples in cuqi samples object if more than 1 sample
117
+ if N==1:
118
+ if len(s) == 1 and isinstance(s,np.ndarray): #Extract single value from numpy array
119
+ s = s.ravel()[0]
120
+ else:
121
+ s = s.flatten()
122
+ else:
123
+ s = Samples(s, self.geometry)#, geometry = self.geometry)
124
+ s.loglike_eval = loglike_eval
125
+ s.acc_rate = acc_rate
126
+ return s
127
+
128
+ @abstractmethod
129
+ def _sample(self,N,Nb):
130
+ pass
131
+
132
+ @abstractmethod
133
+ def _sample_adapt(self,N,Nb):
134
+ pass
135
+
136
+ def _print_progress(self,s,Ns):
137
+ """Prints sampling progress"""
138
+ if Ns > 2:
139
+ if (s % (max(Ns//100,1))) == 0:
140
+ msg = f'Sample {s} / {Ns}'
141
+ sys.stdout.write('\r'+msg)
142
+ if s==Ns:
143
+ msg = f'Sample {s} / {Ns}'
144
+ sys.stdout.write('\r'+msg+'\n')
145
+
146
+ def _call_callback(self, sample, sample_index):
147
+ """ Calls the callback function. Assumes input is sample and sample index"""
148
+ if self.callback is not None:
149
+ self.callback(sample, sample_index)
150
+
151
+ class ProposalBasedSampler(Sampler,ABC):
152
+ def __init__(self, target, proposal=None, scale=1, x0=None, dim=None, **kwargs):
153
+ #TODO: after fixing None dim
154
+ #if dim is None and hasattr(proposal,'dim'):
155
+ # dim = proposal.dim
156
+ super().__init__(target, x0=x0, dim=dim, **kwargs)
157
+
158
+ self.proposal =proposal
159
+ self.scale = scale
160
+
161
+
162
+ @property
163
+ def proposal(self):
164
+ return self._proposal
165
+
166
+ @proposal.setter
167
+ def proposal(self, value):
168
+ self._proposal = value
169
+
170
+ @property
171
+ def geometry(self):
172
+ geom1, geom2 = None, None
173
+ if hasattr(self, 'proposal') and hasattr(self.proposal, 'geometry') and self.proposal.geometry.par_dim is not None:
174
+ geom1= self.proposal.geometry
175
+ if hasattr(self, 'target') and hasattr(self.target, 'geometry') and self.target.geometry.par_dim is not None:
176
+ geom2 = self.target.geometry
177
+ if not isinstance(geom1,cuqi.geometry._DefaultGeometry) and geom1 is not None:
178
+ return geom1
179
+ elif not isinstance(geom2,cuqi.geometry._DefaultGeometry) and geom2 is not None:
180
+ return geom2
181
+ else:
182
+ return cuqi.geometry._DefaultGeometry1D(self.dim)
@@ -43,6 +43,14 @@ class Likelihood(Density):
43
43
  def name(self, value):
44
44
  self.distribution.name = value
45
45
 
46
+ @property
47
+ def _name(self):
48
+ return self.distribution._name
49
+
50
+ @_name.setter
51
+ def _name(self, value):
52
+ self.distribution._name = value
53
+
46
54
  @property
47
55
  def FD_enabled(self):
48
56
  """ Return FD_enabled of the likelihood from the underlying distribution """
@@ -204,4 +212,4 @@ class UserDefinedLikelihood(object):
204
212
  return get_non_default_args(self.logpdf_func)
205
213
 
206
214
  def __repr__(self) -> str:
207
- return "CUQI {} function. Parameters {}.".format(self.__class__.__name__,self.get_parameter_names())
215
+ return "CUQI {} function. Parameters {}.".format(self.__class__.__name__,self.get_parameter_names())
cuqi/model/__init__.py CHANGED
@@ -1 +1 @@
1
- from ._model import Model, LinearModel, PDEModel
1
+ from ._model import Model, LinearModel, PDEModel, AffineModel