Source code for cmdtools.estimation.sqra
import numpy as np
import scipy.sparse as sp
[docs]def sqra(u, A, beta, phi):
""" Square-root approximation of the generator
(of the Overdamped Langevin model)
u: vector of pointwise evaluation of the potential
A: adjacency matrix of the discretization
beta: inverse temperature
phi: the flux constant, determined by the temperature and the discr.
"""
pi = np.sqrt(np.exp(- beta * u)) # Boltzmann distribution
pi /= np.sum(pi)
D = sp.diags(pi)
D1 = sp.diags(1 / pi)
Q = phi * D1 @ A @ D
Q = Q - sp.diags(np.array(Q.sum(axis=1)).flatten())
if sp.issparse(Q):
Q = Q.tocsc()
return Q
[docs]def adjacency_nd(dims, torus=False):
nd = len(dims)
N = np.prod(dims)
neighbours = np.vstack([np.diag(np.ones(nd, dtype=int)), -np.diag(np.ones(nd, dtype=int))])
singletondim = np.array(dims) == 1
neighbours[:, singletondim] = 0
row = np.zeros(N*2*nd, dtype=int)
col = np.zeros(N*2*nd, dtype=int)
data = np.ones(N*2*nd, dtype=bool)
cut = np.zeros(N*2*nd, dtype=bool)
for i in range(N):
multi = np.unravel_index(i, dims) # find multiindex of current cell
mn = multi + neighbours # add neighbour offset
if not torus:
cut[i*2*nd:(i+1)*2*nd] = np.sum((mn < 0) + (mn >= dims), axis=1) # check if boundary is hit
mn = np.mod(multi + neighbours, dims) # glue together boundary
neighbour_flat = np.ravel_multi_index(mn.T, dims) # back to flat inds
# print(neighbour_flat)
row[i*2*nd:(i+1)*2*nd] = i
col[i*2*nd:(i+1)*2*nd] = neighbour_flat
if not torus: # cut out the points which were glued at boundaries
sel = ~cut
data = data[sel]
row = row[sel]
col = col[sel]
A = sp.coo_matrix((data, (row, col)))
A.setdiag(0)
return A.tocsr()
[docs]class SQRA:
""" approximate the generator/rate-matrix Q of the Overdamped-Langevin dynamics
Args:
u (ndarray): The potential function evaluated at the grid points.
beta (float): The inverse temperatur of the system (scales the rates nonlinear).
phi (float): Linear scaling factor of the rates (depending on the grid volume).
A (matrix, optional): Adjacency matrix of the grid. If left empty, automatically compute it based on the shape of `u`.
torus(list of bool): Whether to glue the corresponding dimensions together at their resp. boundaries. Only used in the automatic generation of `A`.
Attributes:
A: Adjacency matrix
Q: The computed generator
"""
def __init__(self, u, beta=1, phi=1, A=None, torus=False):
self.u = u
self.beta = beta
self.phi = phi
self.dims = np.shape(u)
self.A = adjacency_nd(self.dims, torus) if A is None else A
self.Q = self.sqra()
self.N = self.Q.shape[0]
[docs] def sqra(self):
return sqra(self.u.flatten(), self.A, self.beta, self.phi)
# conversion between coldness (beta) and epsilon in sde formulation
# necessary for the computation of phi
[docs]def beta_to_epsilon(b):
return 2 / b