Source code for cuqi.distribution._lmrf
import numpy as np
from cuqi.geometry import _DefaultGeometry1D, Image2D
from cuqi.operator import FirstOrderFiniteDifference
from cuqi.distribution import Distribution
from cuqi.utilities import force_ndarray
[docs]
class LMRF(Distribution):
"""Laplace distribution on the difference between neighboring nodes.
For 1D, the Laplace difference distribution assumes that
.. math::
x_i-x_{i-1} \sim \mathrm{Laplace}(0, b),
where :math:`b` is the scale parameter.
For 2D the differences are defined in both horizontal and vertical directions.
It is possible to define boundary conditions using the `bc_type` parameter.
The location parameter is a shift of the :math:`\mathbf{x}`.
Parameters
----------
location : scalar or ndarray
The location parameter of the distribution.
scale : scalar
The scale parameter of the distribution.
bc_type : string
The boundary conditions of the difference operator.
Example
-------
.. code-block:: python
import cuqi
prior = cuqi.distribution.LMRF(location=0, scale=0.1, geometry=128)
"""
[docs]
def __init__(self, location=None, scale=None, bc_type="zero", **kwargs):
# Init from abstract distribution class
super().__init__(**kwargs)
self.location = location
self.scale = scale
self._bc_type = bc_type
# Ensure geometry has shape
if not self.geometry.fun_shape or self.geometry.par_dim == 1:
raise ValueError(f"Distribution {self.__class__.__name__} must be initialized with supported geometry (geometry of which the fun_shape is not None) and has parameter dimension greater than 1.")
# Default physical_dim to geometry's dimension if not provided
physical_dim = len(self.geometry.fun_shape)
# Ensure provided physical dimension is either 1 or 2
if physical_dim not in [1, 2]:
raise ValueError("Only physical dimension 1 or 2 supported.")
self._physical_dim = physical_dim
if self._physical_dim == 2:
N = int(np.sqrt(self.dim))
num_nodes = (N, N)
else:
num_nodes = self.dim
self._diff_op = FirstOrderFiniteDifference(num_nodes=num_nodes, bc_type=bc_type)
@property
def location(self):
return self._location
@location.setter
def location(self, value):
self._location = force_ndarray(value, flatten=True)
[docs]
def pdf(self, x):
Dx = self._diff_op @ (x-self.location) # np.diff(X)
return (1/(2*self.scale))**(len(Dx)) * np.exp(-np.linalg.norm(Dx, ord=1, axis=0)/self.scale)
[docs]
def logpdf(self, x):
Dx = self._diff_op @ (x-self.location)
return len(Dx)*(-(np.log(2)+np.log(self.scale))) - np.linalg.norm(Dx, ord=1, axis=0)/self.scale
def _sample(self,N=1,rng=None):
raise NotImplementedError("'LMRF.sample' is not implemented. Sampling can be performed with the 'sampler' module.")
# def cdf(self, x): # TODO
# return 1/2 + 1/2*np.sign(x-self.loc)*(1-np.exp(-np.linalg.norm(x, ord=1, axis=0)/self.scale))
# def sample(self): # TODO
# p = np.random.rand(self.dim)
# return self.loc - self.scale*np.sign(p-1/2)*np.log(1-2*abs(p-1/2))