CUQIpy 1.3.0.post0.dev266__tar.gz → 1.3.0.post0.dev292__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.
Potentially problematic release.
This version of CUQIpy might be problematic. Click here for more details.
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/CUQIpy.egg-info/PKG-INFO +1 -1
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/PKG-INFO +1 -1
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/_version.py +3 -3
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_gibbs.py +28 -2
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/model/_model.py +73 -21
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/pde/_pde.py +71 -5
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/utilities/_utilities.py +1 -1
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_implicit_priors.py +3 -3
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_model.py +237 -32
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/CUQIpy.egg-info/SOURCES.txt +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/CUQIpy.egg-info/dependency_links.txt +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/CUQIpy.egg-info/requires.txt +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/CUQIpy.egg-info/top_level.txt +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/LICENSE +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/README.md +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/_messages.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/array/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/array/_array.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/config.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/_data.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/astronaut.npz +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/camera.npz +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/cat.npz +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/cookie.png +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/data/satellite.mat +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/density/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/density/_density.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/diagnostics.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_beta.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_cauchy.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_cmrf.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_custom.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_distribution.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_gamma.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_gaussian.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_gmrf.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_inverse_gamma.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_joint_distribution.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_laplace.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_lmrf.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_lognormal.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_modifiedhalfnormal.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_normal.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_posterior.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_smoothed_laplace.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_truncated_normal.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/distribution/_uniform.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/_recommender.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/algebra/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/algebra/_ast.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/algebra/_orderedset.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/algebra/_randomvariable.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/geometry/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/geometry/_productgeometry.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_conjugate.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_conjugate_approx.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_cwmh.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_direct.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_hmc.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_laplace_approximation.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_mh.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_pcn.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_rto.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/experimental/mcmc/_sampler.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/geometry/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/geometry/_geometry.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/implicitprior/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/implicitprior/_regularizedGaussian.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/implicitprior/_regularizedUnboundedUniform.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/implicitprior/_restorator.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/likelihood/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/likelihood/_likelihood.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/model/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/operator/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/operator/_operator.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/pde/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/problem/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/problem/_problem.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_conjugate.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_conjugate_approx.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_cwmh.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_gibbs.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_hmc.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_langevin_algorithm.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_laplace_approximation.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_mh.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_pcn.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_rto.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/sampler/_sampler.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/samples/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/samples/_samples.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/solver/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/solver/_solver.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/testproblem/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/testproblem/_testproblem.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/utilities/__init__.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/cuqi/utilities/_get_python_variable_name.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/pyproject.toml +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/requirements.txt +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/setup.cfg +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/setup.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_MRFs.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_abstract_distribution_density.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_bayesian_inversion.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_density.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_distribution.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_distributions_shape.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_geometry.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_joint_distribution.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_likelihood.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_pde.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_posterior.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_problem.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_sampler.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_samples.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_solver.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_testproblem.py +0 -0
- {cuqipy-1.3.0.post0.dev266 → cuqipy-1.3.0.post0.dev292}/tests/test_utilities.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.3.0.post0.
|
|
3
|
+
Version: 1.3.0.post0.dev292
|
|
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>, Chao Zhang <chaz@dtu.dk>
|
|
6
6
|
License: Apache License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.3.0.post0.
|
|
3
|
+
Version: 1.3.0.post0.dev292
|
|
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>, Chao Zhang <chaz@dtu.dk>
|
|
6
6
|
License: Apache License
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-07-04T09:41:59+0300",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.3.0.post0.
|
|
14
|
+
"full-revisionid": "7536b0d728d704a7ab46bca7ff4756075ba0e573",
|
|
15
|
+
"version": "1.3.0.post0.dev292"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -42,6 +42,10 @@ class HybridGibbs:
|
|
|
42
42
|
fully stateful at this point. This means samplers like NUTS will lose
|
|
43
43
|
their internal state between Gibbs steps.
|
|
44
44
|
|
|
45
|
+
The order in which the conditionals are sampled is the order of the
|
|
46
|
+
variables in the sampling strategy, unless a different sampling order
|
|
47
|
+
is specified by the parameter `scan_order`
|
|
48
|
+
|
|
45
49
|
Parameters
|
|
46
50
|
----------
|
|
47
51
|
target : cuqi.distribution.JointDistribution
|
|
@@ -58,6 +62,11 @@ class HybridGibbs:
|
|
|
58
62
|
will call its step method in each Gibbs step.
|
|
59
63
|
Default is 1 for all variables.
|
|
60
64
|
|
|
65
|
+
scan_order : list or str, *optional*
|
|
66
|
+
Order in which the conditional distributions are sampled.
|
|
67
|
+
If set to "random", use a random ordering at each step.
|
|
68
|
+
If not specified, it will be the order in the sampling_strategy.
|
|
69
|
+
|
|
61
70
|
callback : callable, optional
|
|
62
71
|
A function that will be called after each sampling step. It can be useful for monitoring the sampler during sampling.
|
|
63
72
|
The function should take three arguments: the sampler object, the index of the current sampling step, the total number of requested samples. The last two arguments are integers. An example of the callback function signature is: `callback(sampler, sample_index, num_of_samples)`.
|
|
@@ -107,7 +116,7 @@ class HybridGibbs:
|
|
|
107
116
|
|
|
108
117
|
"""
|
|
109
118
|
|
|
110
|
-
def __init__(self, target: JointDistribution, sampling_strategy: Dict[str, Sampler], num_sampling_steps: Dict[str, int] = None, callback=None):
|
|
119
|
+
def __init__(self, target: JointDistribution, sampling_strategy: Dict[str, Sampler], num_sampling_steps: Dict[str, int] = None, scan_order = None, callback=None):
|
|
111
120
|
|
|
112
121
|
# Store target and allow conditioning to reduce to a single density
|
|
113
122
|
self.target = target() # Create a copy of target distribution (to avoid modifying the original)
|
|
@@ -121,6 +130,13 @@ class HybridGibbs:
|
|
|
121
130
|
# Store parameter names
|
|
122
131
|
self.par_names = self.target.get_parameter_names()
|
|
123
132
|
|
|
133
|
+
# Store the scan order
|
|
134
|
+
self._scan_order = scan_order
|
|
135
|
+
|
|
136
|
+
# Check that the parameters of the target align with the sampling_strategy and scan_order
|
|
137
|
+
if set(self.par_names) != set(self.scan_order):
|
|
138
|
+
raise ValueError("Parameter names in JointDistribution do not equal the names in the scan order.")
|
|
139
|
+
|
|
124
140
|
# Initialize sampler (after target is set)
|
|
125
141
|
self._initialize()
|
|
126
142
|
|
|
@@ -148,6 +164,16 @@ class HybridGibbs:
|
|
|
148
164
|
# Validate all targets for samplers.
|
|
149
165
|
self.validate_targets()
|
|
150
166
|
|
|
167
|
+
@property
|
|
168
|
+
def scan_order(self):
|
|
169
|
+
if self._scan_order is None:
|
|
170
|
+
return list(self.samplers.keys())
|
|
171
|
+
if self._scan_order == "random":
|
|
172
|
+
arr = list(self.samplers.keys())
|
|
173
|
+
np.random.shuffle(arr) # Shuffle works in-place
|
|
174
|
+
return arr
|
|
175
|
+
return self._scan_order
|
|
176
|
+
|
|
151
177
|
# ------------ Public methods ------------
|
|
152
178
|
def validate_targets(self):
|
|
153
179
|
""" Validate each of the conditional targets used in the Gibbs steps """
|
|
@@ -217,7 +243,7 @@ class HybridGibbs:
|
|
|
217
243
|
""" Sequentially go through all parameters and sample them conditionally on each other """
|
|
218
244
|
|
|
219
245
|
# Sample from each conditional distribution
|
|
220
|
-
for par_name in self.
|
|
246
|
+
for par_name in self.scan_order:
|
|
221
247
|
|
|
222
248
|
# Set target for current parameter
|
|
223
249
|
self._set_target(par_name)
|
|
@@ -179,7 +179,7 @@ class Model(object):
|
|
|
179
179
|
self._stored_non_default_args =\
|
|
180
180
|
cuqi.utilities.get_non_default_args(self._forward_func)
|
|
181
181
|
return self._stored_non_default_args
|
|
182
|
-
|
|
182
|
+
|
|
183
183
|
@property
|
|
184
184
|
def number_of_inputs(self):
|
|
185
185
|
""" The number of inputs of the model. """
|
|
@@ -422,7 +422,7 @@ class Model(object):
|
|
|
422
422
|
# Use CUQIarray funvals if geometry is consistent
|
|
423
423
|
if isinstance(v, CUQIarray) and v.geometry == geometries[i]:
|
|
424
424
|
kwargs[k] = v.funvals
|
|
425
|
-
# Else, if we still need to convert to function value (is_par[i] is True)
|
|
425
|
+
# Else, if we still need to convert to function value (is_par[i] is True)
|
|
426
426
|
# we use the geometry par2fun method
|
|
427
427
|
elif is_par[i] and v is not None:
|
|
428
428
|
kwargs[k] = geometries[i].par2fun(v)
|
|
@@ -496,7 +496,7 @@ class Model(object):
|
|
|
496
496
|
# Use CUQIarray parameters if geometry is consistent
|
|
497
497
|
if isinstance(v, CUQIarray) and v.geometry == geometries[i]:
|
|
498
498
|
v = v.parameters
|
|
499
|
-
# Else, if we still need to convert to parameter value (is_par[i] is False)
|
|
499
|
+
# Else, if we still need to convert to parameter value (is_par[i] is False)
|
|
500
500
|
# we use the geometry fun2par method
|
|
501
501
|
elif not is_par[i] and v is not None:
|
|
502
502
|
v = geometries[i].fun2par(v)
|
|
@@ -665,7 +665,7 @@ class Model(object):
|
|
|
665
665
|
error_msg = (
|
|
666
666
|
"The "
|
|
667
667
|
+ map_name.lower()
|
|
668
|
-
+ f" input is specified by
|
|
668
|
+
+ f" input is specified by keywords arguments {list(kwargs.keys())} that does not match the non_default_args of the "
|
|
669
669
|
+ map_name
|
|
670
670
|
+ f" {non_default_args}."
|
|
671
671
|
)
|
|
@@ -808,7 +808,11 @@ class Model(object):
|
|
|
808
808
|
new_model = copy(self)
|
|
809
809
|
|
|
810
810
|
# Store the original non_default_args of the model
|
|
811
|
-
new_model._original_non_default_args =
|
|
811
|
+
new_model._original_non_default_args = (
|
|
812
|
+
self._original_non_default_args
|
|
813
|
+
if hasattr(self, "_original_non_default_args")
|
|
814
|
+
else self._non_default_args
|
|
815
|
+
)
|
|
812
816
|
|
|
813
817
|
# Update the non_default_args of the model to match the distribution
|
|
814
818
|
# names. Defaults to x in the case of only one distribution that has no
|
|
@@ -1052,7 +1056,7 @@ class Model(object):
|
|
|
1052
1056
|
|
|
1053
1057
|
# turn grad_is_par to a tuple of bools if it is not already
|
|
1054
1058
|
if isinstance(grad_is_par, bool):
|
|
1055
|
-
grad_is_par = tuple([grad_is_par]*
|
|
1059
|
+
grad_is_par = tuple([grad_is_par]*self.number_of_inputs)
|
|
1056
1060
|
|
|
1057
1061
|
# If the domain geometry is a _ProductGeometry and the gradient is
|
|
1058
1062
|
# stacked, split it
|
|
@@ -1451,7 +1455,7 @@ class PDEModel(Model):
|
|
|
1451
1455
|
:ivar range_geometry: The geometry representing the range.
|
|
1452
1456
|
:ivar domain_geometry: The geometry representing the domain.
|
|
1453
1457
|
"""
|
|
1454
|
-
def __init__(self, PDE: cuqi.pde.PDE, range_geometry, domain_geometry):
|
|
1458
|
+
def __init__(self, PDE: cuqi.pde.PDE, range_geometry, domain_geometry, **kwargs):
|
|
1455
1459
|
|
|
1456
1460
|
if not isinstance(PDE, cuqi.pde.PDE):
|
|
1457
1461
|
raise ValueError("PDE needs to be a cuqi PDE.")
|
|
@@ -1460,23 +1464,30 @@ class PDEModel(Model):
|
|
|
1460
1464
|
self.pde = PDE
|
|
1461
1465
|
self._stored_non_default_args = None
|
|
1462
1466
|
|
|
1463
|
-
|
|
1467
|
+
# If gradient or jacobian is not provided, we create it from the PDE
|
|
1468
|
+
if not np.any([k in kwargs.keys() for k in ["gradient", "jacobian"]]):
|
|
1469
|
+
# Create gradient or jacobian function to pass to the Model based on
|
|
1470
|
+
# the PDE object. The dictionary derivative_kwarg contains the
|
|
1471
|
+
# created function along with the function type (either "gradient"
|
|
1472
|
+
# or "jacobian")
|
|
1473
|
+
derivative_kwarg = self._create_derivative_function()
|
|
1474
|
+
# append derivative_kwarg to kwargs
|
|
1475
|
+
kwargs.update(derivative_kwarg)
|
|
1476
|
+
|
|
1477
|
+
super().__init__(forward=self._forward_func_pde,
|
|
1478
|
+
range_geometry=range_geometry,
|
|
1479
|
+
domain_geometry=domain_geometry,
|
|
1480
|
+
**kwargs)
|
|
1464
1481
|
|
|
1465
1482
|
@property
|
|
1466
1483
|
def _non_default_args(self):
|
|
1467
1484
|
if self._stored_non_default_args is None:
|
|
1468
1485
|
# extract the non-default arguments of the PDE
|
|
1469
|
-
self._stored_non_default_args =
|
|
1470
|
-
self.pde.PDE_form
|
|
1471
|
-
)
|
|
1472
|
-
# remove t from the non-default arguments
|
|
1473
|
-
self._stored_non_default_args = self._non_default_args
|
|
1474
|
-
if "t" in self._non_default_args:
|
|
1475
|
-
self._stored_non_default_args.remove("t")
|
|
1486
|
+
self._stored_non_default_args = self.pde._non_default_args
|
|
1476
1487
|
|
|
1477
1488
|
return self._stored_non_default_args
|
|
1478
1489
|
|
|
1479
|
-
def
|
|
1490
|
+
def _forward_func_pde(self, **kwargs):
|
|
1480
1491
|
|
|
1481
1492
|
self.pde.assemble(**kwargs)
|
|
1482
1493
|
|
|
@@ -1486,14 +1497,55 @@ class PDEModel(Model):
|
|
|
1486
1497
|
|
|
1487
1498
|
return obs
|
|
1488
1499
|
|
|
1489
|
-
def
|
|
1490
|
-
"""
|
|
1500
|
+
def _create_derivative_function(self):
|
|
1501
|
+
"""Private function that creates the derivative function (gradient or
|
|
1502
|
+
jacobian) based on the PDE object. The derivative function is created as
|
|
1503
|
+
a lambda function that takes the direction and the parameters as input
|
|
1504
|
+
and returns the gradient or jacobian of the PDE. This private function
|
|
1505
|
+
returns a dictionary with the created function and the function type
|
|
1506
|
+
(either "gradient" or "jacobian")."""
|
|
1507
|
+
|
|
1491
1508
|
if hasattr(self.pde, "gradient_wrt_parameter"):
|
|
1492
|
-
|
|
1509
|
+
# Build the string that will be used to create the lambda function
|
|
1510
|
+
function_str = (
|
|
1511
|
+
"lambda direction, "
|
|
1512
|
+
+ ", ".join(self._non_default_args)
|
|
1513
|
+
+ ", pde_func: pde_func(direction, "
|
|
1514
|
+
+ ", ".join(self._non_default_args)
|
|
1515
|
+
+ ")"
|
|
1516
|
+
)
|
|
1517
|
+
|
|
1518
|
+
# create the lambda function from the string
|
|
1519
|
+
function = eval(function_str)
|
|
1520
|
+
|
|
1521
|
+
# create partial function from the lambda function with gradient_wrt_parameter
|
|
1522
|
+
# as the first argument
|
|
1523
|
+
grad_func = partial(function, pde_func=self.pde.gradient_wrt_parameter)
|
|
1524
|
+
|
|
1525
|
+
# Return the gradient function
|
|
1526
|
+
return {"gradient": grad_func}
|
|
1527
|
+
|
|
1493
1528
|
elif hasattr(self.pde, "jacobian_wrt_parameter"):
|
|
1494
|
-
|
|
1529
|
+
# Build the string that will be used to create the lambda function
|
|
1530
|
+
function_str = (
|
|
1531
|
+
"lambda "
|
|
1532
|
+
+ ", ".join(self._non_default_args)
|
|
1533
|
+
+ ", pde_func: pde_func( "
|
|
1534
|
+
+ ", ".join(self._non_default_args)
|
|
1535
|
+
+ ")"
|
|
1536
|
+
)
|
|
1537
|
+
|
|
1538
|
+
# create the lambda function from the string
|
|
1539
|
+
function = eval(function_str)
|
|
1540
|
+
|
|
1541
|
+
# create partial function from the lambda function with jacobian_wrt_parameter
|
|
1542
|
+
# as the first argument
|
|
1543
|
+
jacobian_func = partial(function, pde_func=self.pde.jacobian_wrt_parameter)
|
|
1544
|
+
|
|
1545
|
+
# Return the jacobian function
|
|
1546
|
+
return {"jacobian": jacobian_func}
|
|
1495
1547
|
else:
|
|
1496
|
-
|
|
1548
|
+
return {} # empty dictionary if no gradient or jacobian is found
|
|
1497
1549
|
|
|
1498
1550
|
# Add the underlying PDE class name to the repr.
|
|
1499
1551
|
def __repr__(self) -> str:
|
|
@@ -3,6 +3,7 @@ import scipy
|
|
|
3
3
|
from inspect import getsource
|
|
4
4
|
from scipy.interpolate import interp1d
|
|
5
5
|
import numpy as np
|
|
6
|
+
from cuqi.utilities import get_non_default_args
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class PDE(ABC):
|
|
@@ -29,6 +30,7 @@ class PDE(ABC):
|
|
|
29
30
|
self.grid_sol = grid_sol
|
|
30
31
|
self.grid_obs = grid_obs
|
|
31
32
|
self.observation_map = observation_map
|
|
33
|
+
self._stored_non_default_args = None
|
|
32
34
|
|
|
33
35
|
@abstractmethod
|
|
34
36
|
def assemble(self, *args, **kwargs):
|
|
@@ -64,6 +66,13 @@ class PDE(ABC):
|
|
|
64
66
|
|
|
65
67
|
return equal_arrays
|
|
66
68
|
|
|
69
|
+
@property
|
|
70
|
+
def _non_default_args(self):
|
|
71
|
+
"""Returns the non-default arguments of the PDE_form function"""
|
|
72
|
+
if self._stored_non_default_args is None:
|
|
73
|
+
self._stored_non_default_args = get_non_default_args(self.PDE_form)
|
|
74
|
+
return self._stored_non_default_args
|
|
75
|
+
|
|
67
76
|
@property
|
|
68
77
|
def grid_sol(self):
|
|
69
78
|
if hasattr(self,"_grid_sol"):
|
|
@@ -94,6 +103,48 @@ class PDE(ABC):
|
|
|
94
103
|
def grids_equal(self):
|
|
95
104
|
return self._grids_equal
|
|
96
105
|
|
|
106
|
+
def _parse_args_add_to_kwargs(
|
|
107
|
+
self, *args, map_name, **kwargs):
|
|
108
|
+
""" Private function that parses the input arguments and adds them as
|
|
109
|
+
keyword arguments matching (the order of) the non default arguments of
|
|
110
|
+
the pde class.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
# If any args are given, add them to kwargs
|
|
114
|
+
if len(args) > 0:
|
|
115
|
+
if len(kwargs) > 0:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
+ map_name.lower()
|
|
118
|
+
+ " input is specified both as positional and keyword arguments. This is not supported."
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Check if the number of args does not match the number of
|
|
122
|
+
# non_default_args of the model
|
|
123
|
+
if len(args) != len(self._non_default_args):
|
|
124
|
+
raise ValueError(
|
|
125
|
+
"The number of positional arguments does not match the number of non-default arguments of "
|
|
126
|
+
+ map_name.lower()
|
|
127
|
+
+ "."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Add args to kwargs following the order of non_default_args
|
|
131
|
+
for idx, arg in enumerate(args):
|
|
132
|
+
kwargs[self._non_default_args[idx]] = arg
|
|
133
|
+
|
|
134
|
+
# Check kwargs matches non_default_args
|
|
135
|
+
if set(list(kwargs.keys())) != set(self._non_default_args):
|
|
136
|
+
error_msg = (
|
|
137
|
+
map_name.lower()
|
|
138
|
+
+ f" input is specified by keywords arguments {list(kwargs.keys())} that does not match the non_default_args of "
|
|
139
|
+
+ map_name
|
|
140
|
+
+ f" {self._non_default_args}."
|
|
141
|
+
)
|
|
142
|
+
raise ValueError(error_msg)
|
|
143
|
+
|
|
144
|
+
# Make sure order of kwargs is the same as non_default_args
|
|
145
|
+
kwargs = {k: kwargs[k] for k in self._non_default_args}
|
|
146
|
+
|
|
147
|
+
return kwargs
|
|
97
148
|
|
|
98
149
|
class LinearPDE(PDE):
|
|
99
150
|
"""
|
|
@@ -143,7 +194,7 @@ class SteadyStateLinearPDE(LinearPDE):
|
|
|
143
194
|
Parameters
|
|
144
195
|
-----------
|
|
145
196
|
PDE_form : callable function
|
|
146
|
-
Callable function with signature `PDE_form(
|
|
197
|
+
Callable function with signature `PDE_form(parameter1, parameter2, ...)` where `parameter1`, `parameter2`, etc. are the Bayesian unknown parameters (the user can choose any names for these parameters, e.g. `a`, `b`, etc.). The function returns a tuple with the discretized differential operator A and right-hand-side b. The types of A and b are determined by what the method :meth:`linalg_solve` accepts as first and second parameters, respectively.
|
|
147
198
|
|
|
148
199
|
kwargs:
|
|
149
200
|
See :class:`~cuqi.pde.LinearPDE` for the remaining keyword arguments.
|
|
@@ -158,7 +209,10 @@ class SteadyStateLinearPDE(LinearPDE):
|
|
|
158
209
|
|
|
159
210
|
def assemble(self, *args, **kwargs):
|
|
160
211
|
"""Assembles differential operator and rhs according to PDE_form"""
|
|
161
|
-
|
|
212
|
+
kwargs = self._parse_args_add_to_kwargs(
|
|
213
|
+
*args, map_name="assemble", **kwargs
|
|
214
|
+
)
|
|
215
|
+
self.diff_op, self.rhs = self.PDE_form(**kwargs)
|
|
162
216
|
|
|
163
217
|
def solve(self):
|
|
164
218
|
"""Solve the PDE and returns the solution and an information variable `info` which is a tuple of all variables returned by the function `linalg_solve` after the solution."""
|
|
@@ -186,7 +240,7 @@ class TimeDependentLinearPDE(LinearPDE):
|
|
|
186
240
|
Parameters
|
|
187
241
|
-----------
|
|
188
242
|
PDE_form : callable function
|
|
189
|
-
Callable function with signature `PDE_form(
|
|
243
|
+
Callable function with signature `PDE_form(parameter1, parameter2, ..., t)` where `parameter1`, `parameter2`, etc. are the Bayesian unknown parameters (the user can choose any names for these parameters, e.g. `a`, `b`, etc.) 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`.
|
|
190
244
|
|
|
191
245
|
time_steps : ndarray
|
|
192
246
|
An array of the discretized times corresponding to the time steps that starts with the initial time and ends with the final time
|
|
@@ -228,6 +282,18 @@ class TimeDependentLinearPDE(LinearPDE):
|
|
|
228
282
|
def method(self):
|
|
229
283
|
return self._method
|
|
230
284
|
|
|
285
|
+
@property
|
|
286
|
+
def _non_default_args(self):
|
|
287
|
+
"""Returns the non-default arguments of the PDE_form function"""
|
|
288
|
+
if self._stored_non_default_args is None:
|
|
289
|
+
self._stored_non_default_args = get_non_default_args(self.PDE_form)
|
|
290
|
+
# Remove the time argument from the non-default arguments
|
|
291
|
+
# since it is provided automatically by `solve` method and is not
|
|
292
|
+
# an argument to be inferred in Bayesian inference setting.
|
|
293
|
+
if 't' in self._stored_non_default_args:
|
|
294
|
+
self._stored_non_default_args.remove('t')
|
|
295
|
+
return self._stored_non_default_args
|
|
296
|
+
|
|
231
297
|
@method.setter
|
|
232
298
|
def method(self, value):
|
|
233
299
|
if value.lower() != 'forward_euler' and value.lower() != 'backward_euler':
|
|
@@ -237,13 +303,13 @@ class TimeDependentLinearPDE(LinearPDE):
|
|
|
237
303
|
|
|
238
304
|
def assemble(self, *args, **kwargs):
|
|
239
305
|
"""Assemble PDE"""
|
|
306
|
+
kwargs = self._parse_args_add_to_kwargs(*args, map_name="assemble", **kwargs)
|
|
240
307
|
self._parameter_kwargs = kwargs
|
|
241
|
-
self._parameter_args = args
|
|
242
308
|
|
|
243
309
|
def assemble_step(self, t):
|
|
244
310
|
"""Assemble time step at time t"""
|
|
245
311
|
self.diff_op, self.rhs, self.initial_condition = self.PDE_form(
|
|
246
|
-
|
|
312
|
+
**self._parameter_kwargs, t=t
|
|
247
313
|
)
|
|
248
314
|
|
|
249
315
|
def solve(self):
|
|
@@ -188,7 +188,7 @@ def approx_derivative(func, wrt, direction=None, epsilon=np.sqrt(np.finfo(float)
|
|
|
188
188
|
# We compute the Jacobian matrix of func using forward differences.
|
|
189
189
|
# If the function is scalar-valued, we compute the gradient instead.
|
|
190
190
|
# If the direction is provided, we compute the direction-Jacobian product.
|
|
191
|
-
wrt =
|
|
191
|
+
wrt = force_ndarray(wrt, flatten=True)
|
|
192
192
|
f0 = func(wrt)
|
|
193
193
|
Matr = np.zeros([infer_len(wrt), infer_len(f0)])
|
|
194
194
|
dx = np.zeros(len(wrt))
|
|
@@ -210,8 +210,8 @@ def test_regression_increasing():
|
|
|
210
210
|
posterior = joint(y=y_obs)
|
|
211
211
|
|
|
212
212
|
sampling_strategy = {
|
|
213
|
+
'd': cuqi.experimental.mcmc.Conjugate(),
|
|
213
214
|
'x': cuqi.experimental.mcmc.RegularizedLinearRTO(maxit=50, penalty_parameter=20, adaptive = False),
|
|
214
|
-
'd': cuqi.experimental.mcmc.Conjugate()
|
|
215
215
|
}
|
|
216
216
|
sampler = cuqi.experimental.mcmc.HybridGibbs(posterior, sampling_strategy)
|
|
217
217
|
|
|
@@ -241,8 +241,8 @@ def test_regression_convex():
|
|
|
241
241
|
posterior = joint(y=y_obs)
|
|
242
242
|
|
|
243
243
|
sampling_strategy = {
|
|
244
|
-
'
|
|
245
|
-
'
|
|
244
|
+
'd': cuqi.experimental.mcmc.Conjugate(),
|
|
245
|
+
'x': cuqi.experimental.mcmc.RegularizedLinearRTO(maxit=50, penalty_parameter=20, adaptive = False)
|
|
246
246
|
}
|
|
247
247
|
sampler = cuqi.experimental.mcmc.HybridGibbs(posterior, sampling_strategy)
|
|
248
248
|
|