CUQIpy 0.4.0__py3-none-any.whl → 0.4.0.post0.dev19__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: CUQIpy
3
- Version: 0.4.0
3
+ Version: 0.4.0.post0.dev19
4
4
  Summary: Computational Uncertainty Quantification for Inverse problems in Python
5
5
  Maintainer-email: "Nicolai A. B. Riis" <nabr@dtu.dk>, "Jakob S. Jørgensen" <jakj@dtu.dk>, "Amal M. Alghamdi" <amaal@dtu.dk>
6
6
  License: Apache License
@@ -1,6 +1,6 @@
1
1
  cuqi/__init__.py,sha256=K0ss2HNqoLUX7wGpSZdaPKxIaKdRS452fcJm4D0pcEs,433
2
2
  cuqi/_messages.py,sha256=fzEBrZT2kbmfecBBPm7spVu7yHdxGARQB4QzXhJbCJ0,415
3
- cuqi/_version.py,sha256=1d4RdYAEpT5aJ2M_OVtwSGn5gXF3JRmJHKn-h0_9RJU,497
3
+ cuqi/_version.py,sha256=JZ7qGpXX_2jvjPCycacUOXXKJPcOS4cR_KiL1scwDxg,509
4
4
  cuqi/config.py,sha256=wcYvz19wkeKW2EKCGIKJiTpWt5kdaxyt4imyRkvtTRA,526
5
5
  cuqi/diagnostics.py,sha256=5OrbJeqpynqRXOe5MtOKKhe7EAVdOEpHIqHnlMW9G_c,3029
6
6
  cuqi/array/__init__.py,sha256=-EeiaiWGNsE3twRS4dD814BIlfxEsNkTCZUc5gjOXb0,30
@@ -39,7 +39,7 @@ cuqi/model/_model.py,sha256=Q_Mg48adoiv9WZKzzSA_kuyy8mEzXKjR9QM1QnpRTPA,24201
39
39
  cuqi/operator/__init__.py,sha256=0pc9p-KPyl7KtPV0noB0ddI0CP2iYEHw5rbw49D8Njk,136
40
40
  cuqi/operator/_operator.py,sha256=yNwPTh7jR07AiKMbMQQ5_54EgirlKFsbq9JN1EODaQI,8856
41
41
  cuqi/pde/__init__.py,sha256=NyS_ZYruCvy-Yg24qKlwm3ZIX058kLNQX9bqs-xg4ZM,99
42
- cuqi/pde/_pde.py,sha256=wWKwLCLQfJMcpelW0VC5S265dR8RAJEHkKKRs7Ss5rI,10337
42
+ cuqi/pde/_pde.py,sha256=WRkOYyIdT_T3aZepRh0aS9C5nBbUZUcHaA80iSRvgoo,12572
43
43
  cuqi/problem/__init__.py,sha256=JxJty4JqHTOqSG6NeTGiXRQ7OLxiRK9jvVq3lXLeIRw,38
44
44
  cuqi/problem/_problem.py,sha256=jQ1SXFhS9vbjPOu2EgfXfi3MnioM9r49rSPLlDuMpBY,28944
45
45
  cuqi/sampler/__init__.py,sha256=2Rt-ISO5-bNECKOG_JbnLqtwnu9vKnYssmZV1QF-rvQ,361
@@ -63,8 +63,8 @@ cuqi/testproblem/_testproblem.py,sha256=1a52jc92NqsLRGyM5DLySNW0lVVOC6tkyKM_QV4K
63
63
  cuqi/utilities/__init__.py,sha256=EfxHLdsyDNugbmbzs43nV_AeKcycM9sVBjG9WZydagA,351
64
64
  cuqi/utilities/_get_python_variable_name.py,sha256=QwlBVj2koJRA8s8pWd554p7-ElcI7HUwY32HknaR92E,1827
65
65
  cuqi/utilities/_utilities.py,sha256=rjycaxDWExdskIfYXV1z5ZlB0JTlqv3tCmKf08i6U5c,7973
66
- CUQIpy-0.4.0.dist-info/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
67
- CUQIpy-0.4.0.dist-info/METADATA,sha256=AtbU3rbpBpQllnM9HiCWfDES2dQuJcJyus4XBltJCaM,16443
68
- CUQIpy-0.4.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
69
- CUQIpy-0.4.0.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
70
- CUQIpy-0.4.0.dist-info/RECORD,,
66
+ CUQIpy-0.4.0.post0.dev19.dist-info/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
67
+ CUQIpy-0.4.0.post0.dev19.dist-info/METADATA,sha256=tIno8lEYjygD8oI8fQCKj3R69T1IwbERJA6fGMVVA44,16455
68
+ CUQIpy-0.4.0.post0.dev19.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
69
+ CUQIpy-0.4.0.post0.dev19.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
70
+ CUQIpy-0.4.0.post0.dev19.dist-info/RECORD,,
cuqi/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2023-05-26T15:28:04+0200",
11
+ "date": "2023-06-09T10:16:49+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "c13be03edb3eb06da1d67ca35ebbcdc1ccf84f5b",
15
- "version": "0.4.0"
14
+ "full-revisionid": "d7e4a312b9c749d530f7cf3c06068857ab6b2fe2",
15
+ "version": "0.4.0.post0.dev19"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
cuqi/pde/_pde.py CHANGED
@@ -84,6 +84,8 @@ class PDE(ABC):
84
84
 
85
85
  @grid_obs.setter
86
86
  def grid_obs(self,value):
87
+ if value is None:
88
+ value = self.grid_sol
87
89
  self._grids_equal = self._compare_grid(value,self.grid_sol)
88
90
  self._grid_obs = value
89
91
 
@@ -186,7 +188,10 @@ class TimeDependentLinearPDE(LinearPDE):
186
188
  Callable function with signature `PDE_form(parameter, t)` where `parameter` is the Bayesian parameter and `t` is the time at which the PDE form is evaluated. The function returns a tuple of (`differential_operator`, `source_term`, `initial_condition`) where `differential_operator` is the linear operator at time `t`, `source_term` is the source term at time `t`, and `initial_condition` is the initial condition. The types of `differential_operator` and `source_term` are determined by what the method :meth:`linalg_solve` accepts as linear operator and right-hand side, respectively. The type of `initial_condition` should be the same type as the solution returned by :meth:`linalg_solve`.
187
189
 
188
190
  time_steps : ndarray
189
- An array of the discretized times corresponding to the time steps that starts with the initial time and ends with the final time.
191
+ An array of the discretized times corresponding to the time steps that starts with the initial time and ends with the final time
192
+
193
+ time_obs : array_like or str
194
+ If passed as an array_like, it is an array of the times at which the solution is observed. If passed as a string it can be set to `final` to observe at the final time step, or `all` to observe at all time steps. Default is `final`.
190
195
 
191
196
  method: str
192
197
  Time stepping method. Currently two options are available `forward_euler` and `backward_euler`.
@@ -199,12 +204,25 @@ class TimeDependentLinearPDE(LinearPDE):
199
204
  See demos/demo34_TimeDependentLinearPDE.py for 1D heat and 1D wave equations.
200
205
  """
201
206
 
202
- def __init__(self, PDE_form, time_steps, method='forward_euler', **kwargs):
207
+ def __init__(self, PDE_form, time_steps, time_obs='final', method='forward_euler', **kwargs):
203
208
  super().__init__(PDE_form, **kwargs)
204
209
 
205
210
  self.time_steps = time_steps
206
211
  self.method = method
207
212
 
213
+ # Set time_obs
214
+ if time_obs is None:
215
+ raise ValueError("time_obs cannot be None")
216
+ elif isinstance(time_obs, str):
217
+ if time_obs.lower() == 'final':
218
+ time_obs = time_steps[-1:]
219
+ elif time_obs.lower() == 'all':
220
+ time_obs = time_steps
221
+ else:
222
+ raise ValueError("if time_obs is a string, it can only be set "
223
+ +"to `final` or `all`")
224
+ self._time_obs = time_obs
225
+
208
226
  @property
209
227
  def method(self):
210
228
  return self._method
@@ -226,37 +244,62 @@ class TimeDependentLinearPDE(LinearPDE):
226
244
 
227
245
  def solve(self):
228
246
  """Solve PDE by time-stepping"""
247
+ # initialize time-dependent solution
248
+ self.assemble_step(self.time_steps[0])
249
+ u = np.empty((len(self.initial_condition), len(self.time_steps)))
250
+ u[:, 0] = self.initial_condition
229
251
 
230
252
  if self.method == 'forward_euler':
231
253
  for idx, t in enumerate(self.time_steps[:-1]):
232
254
  dt = self.time_steps[idx+1] - t
233
255
  self.assemble_step(t)
234
- if idx == 0:
235
- u = self.initial_condition
236
- u = (dt*self.diff_op + np.eye(len(u)))@u + dt*self.rhs # from u at time t, gives u at t+dt
256
+ u_pre = u[:, idx]
257
+ u[:, idx+1] = (dt*self.diff_op + np.eye(len(u_pre)))@u_pre + dt*self.rhs # from u at time t, gives u at t+dt
237
258
  info = None
238
259
 
239
260
  if self.method == 'backward_euler':
240
261
  for idx, t in enumerate(self.time_steps[1:]):
241
262
  dt = t - self.time_steps[idx]
242
263
  self.assemble_step(t)
243
- if idx == 0:
244
- u = self.initial_condition
245
- A = np.eye(len(u)) - dt*self.diff_op
264
+ u_pre = u[:, idx]
265
+ A = np.eye(len(u_pre)) - dt*self.diff_op
246
266
  # from u at time t-dt, gives u at t
247
- u, info = self._solve_linear_system(
248
- A, u + dt*self.rhs, self._linalg_solve, self._linalg_solve_kwargs)
267
+ u[:, idx+1], info = self._solve_linear_system(
268
+ A, u_pre + dt*self.rhs, self._linalg_solve, self._linalg_solve_kwargs)
249
269
 
250
270
  return u, info
251
271
 
252
272
  def observe(self, solution):
253
-
254
- if self.grids_equal:
255
- solution_obs = solution
273
+
274
+ # If observation grid is the same as solution grid and observation time
275
+ # is the final time step then no need to interpolate
276
+ if self.grids_equal and np.all(self.time_steps[-1:] == self._time_obs):
277
+ solution_obs = solution[..., -1]
278
+
279
+ # Interpolate solution in time and space to the observation
280
+ # time and space
256
281
  else:
257
- solution_obs = interp1d(self.grid_sol, solution, kind='quadratic')(self.grid_obs)
282
+ # Raise error if solution is 2D or 3D in space
283
+ if len(solution.shape) > 2:
284
+ raise ValueError("Interpolation of solutions of 2D and 3D "+
285
+ "space dimensions based on the provided "+
286
+ "grid_obs and time_obs are not supported. "+
287
+ "You can, instead, pass a custom "+
288
+ "observation_map and pass grid_obs and "+
289
+ "time_obs as None.")
290
+
291
+ # Interpolate solution in space and time to the observation
292
+ # time and space
293
+ solution_obs = scipy.interpolate.RectBivariateSpline(
294
+ self.grid_sol, self.time_steps, solution)(self.grid_obs,
295
+ self._time_obs)
258
296
 
297
+ # Apply observation map
259
298
  if self.observation_map is not None:
260
299
  solution_obs = self.observation_map(solution_obs)
261
-
262
- return solution_obs
300
+
301
+ # squeeze if only one time observation
302
+ if len(self._time_obs) == 1:
303
+ solution_obs = solution_obs.squeeze()
304
+
305
+ return solution_obs