"""
This file is part of the MasterMSM package.
"""
import copy
import numpy as np
import networkx as nx
import os #, math
import tempfile
from functools import reduce, cmp_to_key
#import operator
from scipy import linalg as spla
#import multiprocessing as mp
import pickle
# thermal energy (kJ/mol)
beta = 1./(8.314e-3*300)
#def difference(k1, k2):
# l = len(k1)
# diff = 0
# for i in range(l):
# if k1[i] != k2[i]:
# diff+=1
# return diff
[docs]def calc_eigsK(rate, evecs=False):
"""
Calculate eigenvalues and eigenvectors of rate matrix K
Parameters
-----------
rate : array
The rate matrix to use.
evecs : bool
Whether we want the eigenvectors of the rate matrix.
Returns:
-------
tauK : numpy array
Relaxation times from K.
peqK : numpy array
Equilibrium probabilities from K.
rvecsK : numpy array, optional
Right eigenvectors of K, sorted.
lvecsK : numpy array, optional
Left eigenvectors of K, sorted.
"""
evalsK, lvecsK, rvecsK = \
spla.eig(rate, left=True)
# sort modes
nkeys = len(rate)
elistK = []
for i in range(nkeys):
elistK.append([i,np.real(evalsK[i])])
elistK.sort(key=cmp_to_key(esort))
# calculate relaxation times from K and T
tauK = []
for i in range(nkeys):
if np.abs(elistK[i][1]) > 1e-10:
iiK, lamK = elistK[i]
tauK.append(-1./lamK)
if len(tauK) == 1:
ieqK = iiK
# equilibrium probabilities
ieqK, _ = elistK[0]
peqK_sum = reduce(lambda x, y: x + y, map(lambda x: rvecsK[x,ieqK],
range(nkeys)))
peqK = rvecsK[:,ieqK]/peqK_sum
if not evecs:
return tauK, peqK
else:
# sort eigenvectors
rvecsK_sorted = np.zeros((nkeys, nkeys), float)
lvecsK_sorted = np.zeros((nkeys, nkeys), float)
for i in range(nkeys):
iiK, lamK = elistK[i]
rvecsK_sorted[:,i] = rvecsK[:,iiK]
lvecsK_sorted[:,i] = lvecsK[:,iiK]
return tauK, peqK, rvecsK_sorted, lvecsK_sorted
[docs]def esort(ei, ej):
""" Sorts eigenvalues.
Parameters
----------
ei : float
Eigenvalue i
ej : float
Eigenvalue j
Returns
-------
bool :
Whether the first value is larger than the second.
"""
_, eval_i = ei
_, eval_j = ej
if eval_j.real > eval_i.real:
return 1
elif eval_j.real < eval_i.real:
return -1
else:
return 0
#def find_keys(state_keys, trans, manually_remove):
# """ eliminate dead ends """
# keep_states = []
# keep_keys = []
# # eliminate dead ends
# nstate = len(state_keys)
# for i in range(nstate):
# key = state_keys[i]
# summ = 0
# sumx = 0
# for j in range(nstate):
# if j!=i:
# summ += trans[j][i] # sources
# sumx += trans[i][j] # sinks
# if summ > 0 and sumx > 0 and trans[i][i] > 0 and key not in manually_remove:
# keep_states.append(i)
# keep_keys.append(state_keys[i])
# return keep_states,keep_keys
#
#def connect_groups(keep_states, trans):
# """ check for connected groups """
# connected_groups = []
# leftover = copy.deepcopy(keep_states)
# while len(leftover) > 0:
# #print leftover
# leftover_new = []
# n_old_new_net = 0
# new_net = [ leftover[0] ]
# n_new_net = len(new_net)
# while n_new_net != n_old_new_net:
# for i in range(len(leftover)):
# l = leftover[i]
# if l in new_net:
# continue
# summ = 0
# for g in new_net:
# summ += trans[l][g]+trans[g][l]
# if summ > 0:
# new_net.append(l)
# n_old_new_net = n_new_net
# n_new_net = len(new_net)
# #print " added %i new members" % (n_new_net-n_old_new_net)
# leftover_new = filter(lambda x: x not in new_net, leftover)
# connected_groups.append(new_net)
# leftover = copy.deepcopy(leftover_new)
# return connected_groups
#
#def isnative(native_string, string):
# s = ""
# for i in range(len(string)):
# if string[i]==native_string[i]:
# s+="1"
# else:
# s+="0"
# return s
[docs]def mat_mul_v(m, v):
""" Multiplies matrix and vector
Parameters
----------
m : np.array
The matrix.
v : np.array
The vector.
Returns
-------
w : np.array
The result
"""
rows = len(m)
w = [0]*rows
irange = range(len(v))
summ = 0
for j in range(rows):
r = m[j]
for i in irange:
summ += r[i]*v[i]
w[j], summ = summ,0
return w
#def dotproduct(v1, v2, sum=sum, imap=itertools.imap, mul=operator.mul):
# return sum(imap(mul,v1,v2))
#
##def rate_analyze(rate):
## # calculates eigenvalues and eigenvectors from rate matrix
## # calculate symmetrized matrix
## kjisym = kji*(kji.transpose())
## kjisym = sqrt(kjisym)
## for j in arange(nstates):
## kjisym[j,j] = -kjisym[j,j]
## # calculate eigenvalues and eigenvectors
## eigvalsym,eigvectsym = linalg.eig(kjisym)
## # index the solutions
## index = argsort(-eigvalsym)
## ieq = index[0]
## # equilibrium population
## peq = eigvectsym[:,ieq]**2
## # order eigenvalues and calculate left and right eigenvectors
## eigval = zeros((nstates),float)
## PsiR = zeros((nstates,nstates),float)
## PsiL = zeros((nstates,nstates),float)
## for i in arange(nstates):
## eigval[i] = eigvalsym[index[i]]
## PsiR[:,i] = eigvectsym[:,index[i]]*eigvectsym[:,ieq]
## PsiL[:,i] = eigvectsym[:,index[i]]/eigvectsym[:,ieq]
## return eigval,PsiR,PsiL,eigvectsym,peq
#
#def propagate(rate, t, pini):
# # propagate dynamics using rate matrix exponential
# expkt = spla.expm2(rate*t)
# return mat_mul_v(expkt,pini)
#
#def propagate_eig(elist, rvecs, lvecs, t, pini):
# # propagate dynamics using rate matrix exponential using eigenvalues and eigenvectors
# nstates = len(pini)
# p = np.zeros((nstates),float)
# for n in range(nstates):
# #print np.exp(-elist[n][1]*t)
# i,e = elist[n]
# p = p + rvecs[:,i]*(np.dot(lvecs[:,i],pini)*\
# np.exp(-abs(e*t)))
# return p
#
#def bootsfiles(traj_list_dt):
# n = len(traj_list_dt)
# traj_list_dt_new = []
# i = 0
# while i < n:
# k = int(np.random.random()*n)
# traj_list_dt_new.append(traj_list_dt[k])
# i += 1
# return traj_list_dt_new
#
#def boots_pick(filename, blocksize):
# raw = open(filename).readlines()
# lraw = len(raw)
# nblocks = int(lraw/blocksize)
# lblock = int(lraw/nblocks)
# try:
# ib = np.random.randint(nblocks-1)
# except ValueError:
# ib = 0
# return raw[ib*lblock:(ib+1)*lblock]
#
#def onrate(states, target, K, peq):
# # steady state rate
# kon = 0.
# for i in states:
# if i != target:
# if K[target,i] > 0:
# kon += K[target,i]*peq[i]
# return kon
#
[docs]def run_commit(states, K, peq, FF, UU):
""" Calculate committors and reactive flux
Parameters
----------
states : list
States in the MSM.
K : np.array
Rate matrix.
peq : np.array
Equilibrium distribution.
FF : list
Definitely folded states.
UU : list
Definitely unfolded states.
Returns
-------
J : np.array
Reactive flux matrix.
pfold : np.array
Values of the committor.
sum_flux : float
Sum of reactive fluxes.
kf : float
Folding rate from flux over population relationship.
"""
nstates = len(states)
# define end-states
UUFF = UU + FF
print (" definitely FF and UU states", UUFF)
I = list(filter(lambda x: x not in UU+FF, states))
NI = len(I)
# calculate committors
b = np.zeros([NI], float)
A = np.zeros([NI,NI], float)
for j_ind in range(NI):
j = I[j_ind]
summ = 0.
for i in FF:
summ += K[i][j]
b[j_ind] = -summ
for i_ind in range(NI):
i = I[i_ind]
A[j_ind][i_ind] = K[i][j]
# solve Ax=b
Ainv = np.linalg.inv(A)
x = np.dot(Ainv,b)
#XX = np.dot(Ainv,A)
pfold = np.zeros(nstates,float)
for i in range(nstates):
if i in UU:
pfold[i] = 0.0
elif i in FF:
pfold[i] = 1.0
else:
ii = I.index(i)
pfold[i] = x[ii]
# stationary distribution
pss = np.zeros(nstates,float)
for i in range(nstates):
pss[i] = (1-pfold[i])*peq[i]
# flux matrix and reactive flux
J = np.zeros([nstates,nstates],float)
for i in range(nstates):
for j in range(nstates):
J[j][i] = K[j][i]*peq[i]*(pfold[j]-pfold[i])
# dividing line is committor = 0.5
sum_flux = 0
left = [x for x in range(nstates) if pfold[x] < 0.5]
right = [x for x in range(nstates) if pfold[x] > 0.5]
for i in left:
for j in right:
sum_flux += J[j][i]
#sum of populations for all reactant states
pU = np.sum([peq[x] for x in range(nstates) if pfold[x] < 0.5])
# pU = np.sum(peq[filter(lambda x: x in UU, range(nstates))])
kf = sum_flux/pU
return J, pfold, sum_flux, kf
[docs]def calc_count_worker(x):
""" mp worker that calculates the count matrix from a trajectory
Parameters
----------
x : list
List containing input for each mp worker. Includes:
distraj :the time series of states
dt : the timestep for that trajectory
keys : the keys used in the assignment
lagt : the lag time for construction
Returns
-------
count : array
"""
# parse input from multiprocessing
distraj = x[0]
dt = x[1]
keys = x[2]
nkeys = len(keys)
lagt = x[3]
sliding = x[4]
ltraj = len(distraj)
lag = int(lagt/dt) # number of frames per lag time
if sliding:
slider = 1 # every state is initial state
else:
slider = lag
count = np.zeros([nkeys,nkeys], np.int32)
for i in range(0, ltraj-lag, slider):
j = i + lag
state_i = distraj[i]
state_j = distraj[j]
if state_i in keys:
idx_i = keys.index(state_i)
if state_j in keys:
idx_j = keys.index(state_j)
try:
count[idx_j][idx_i] += 1
except UnboundLocalError:
pass
return count
[docs]def calc_lifetime(x):
""" mp worker that calculates the count matrix from a trajectory
Parameters
----------
x : list
List containing input for each mp worker. Includes:
distraj :the time series of states
dt : the timestep for that trajectory
keys : the keys used in the assignment
Returns
-------
life : dict
"""
# parse input from multiprocessing
distraj = x[0]
dt = x[1]
keys = x[2]
ltraj = len(distraj)
life = {}
l = 0
for j in range(1, ltraj):
i = j - 1
state_i = distraj[i]
state_j = distraj[j]
if state_i == state_j:
l += 1
elif state_j not in keys:
l += 1
else:
try:
life[state_i].append(l*dt)
except KeyError:
life[state_i] = [l*dt]
l = 1
#try:
# life[state_i].append(l*dt)
#except KeyError:
# life[state_i] = [l*dt]
return life
[docs]def traj_split(data=None, lagt=None, fdboots=None):
""" Splits trajectories into fragments for bootstrapping
Parameters
----------
data : list
Set of trajectories used for building the MSM.
lagt : float
Lag time for building the MSM.
Returns:
-------
filetmp : file object
Open file object with trajectory fragments.
"""
trajs = [[x.distraj, x.dt] for x in data]
ltraj = [len(x[0])*x[1] for x in trajs]
ltraj_median = np.median(ltraj)
timetot = np.sum(ltraj) # total simulation time
while ltraj_median > timetot/20. and ltraj_median > 10.*lagt:
trajs_new = []
#cut trajectories in chunks
for x in trajs:
lx = len(x[0])
trajs_new.append([x[0][:int(lx/2)], x[1]])
trajs_new.append([x[0][int(lx/2):], x[1]])
trajs = trajs_new
ltraj = [len(x[0])*x[1] for x in trajs]
ltraj_median = np.median(ltraj)
# save trajs
fd, filetmp = tempfile.mkstemp()
file = os.fdopen(fd, 'wb')
pickle.dump(trajs, file, protocol=pickle.HIGHEST_PROTOCOL)
file.close()
return filetmp
[docs]def do_boots_worker(x):
""" Worker function for parallel bootstrapping.
Parameters
----------
x : list
A list containing the trajectory filename, the states, the lag time
and the total number of transitions.
"""
#print "# Process %s running on input %s"%(mp.current_process(), x[0])
filetmp, keys, lagt, ncount, slider = x
nkeys = len(keys)
finp = open(filetmp, 'rb')
trans = pickle.load(finp)
finp.close()
ltrans = len(trans)
np.random.seed()
ncount_boots = 0
count = np.zeros([nkeys, nkeys], np.int32)
while ncount_boots < ncount:
itrans = np.random.randint(ltrans)
count_inp = [trans[itrans][0], trans[itrans][1], keys, lagt, slider]
c = calc_count_worker(count_inp)
count += np.matrix(c)
ncount_boots += np.sum(c)
#print ncount_boots, "< %g"%ncount
D = nx.DiGraph(count)
#keep_states = sorted(nx.strongly_connected_components(D)[0])
keep_states = list(sorted(list(nx.strongly_connected_components(D)),
key = len, reverse=True)[0])
keep_keys = list(map(lambda x: keys[x], keep_states))
nkeep = len(keep_keys)
trans = np.zeros([nkeep, nkeep], float)
for i in range(nkeep):
ni = reduce(lambda x, y: x + y, map(lambda x:
count[keep_states[x]][keep_states[i]], range(nkeep)))
for j in range(nkeep):
trans[j][i] = float(count[keep_states[j]][keep_states[i]])/float(ni)
evalsT, rvecsT = spla.eig(trans, left=False)
elistT = []
for i in range(nkeep):
elistT.append([i,np.real(evalsT[i])])
elistT.sort(key=cmp_to_key(esort))
tauT = []
for i in range(1,nkeep):
_, lamT = elistT[i]
tauT.append(-lagt/np.log(lamT))
ieqT, _ = elistT[0]
peqT_sum = reduce(lambda x,y: x + y, map(lambda x: rvecsT[x,ieqT],
range(nkeep)))
peqT = rvecsT[:,ieqT]/peqT_sum
return tauT, peqT, trans, keep_keys
[docs]def calc_trans(nkeep=None, keep_states=None, count=None):
""" Calculates transition matrix.
Uses the maximum likelihood expression by Prinz et al.[1]_
Parameters
----------
lagt : float
Lag time for construction of MSM.
Returns
-------
trans : array
The transition probability matrix.
Notes
-----
..[1] J. H. Prinz, H. Wu, M. Sarich, B. Keller, M. Senne, M. Held,
J. D. Chodera, C. Schutte and F. Noe, "Markov state models:
Generation and validation", J. Chem. Phys. (2011).
"""
trans = np.zeros([nkeep, nkeep], float)
for i in range(nkeep):
ni = reduce(lambda x, y: x + y, map(lambda x:
count[keep_states[x]][keep_states[i]], range(nkeep)))
for j in range(nkeep):
trans[j][i] = float(count[keep_states[j]][keep_states[i]])/float(ni)
return trans
[docs]def calc_rate(nkeep, trans, lagt):
""" Calculate rate matrix from transition matrix.
We use a method based on a Taylor expansion.[1]_
Parameters
----------
nkeep : int
Number of states in transition matrix.
trans: np.array
Transition matrix.
lagt : float
The lag time.
Returns
-------
rate : np.array
The rate matrix.
Notes
-----
..[1] D. De Sancho, J. Mittal and R. B. Best, "Folding kinetics
and unfolded state dynamics of the GB1 hairpin from molecular
simulation", J. Chem. Theory Comput. (2013).
"""
rate = trans/lagt
# enforce mass conservation
for i in range(nkeep):
rate[i][i] = -(np.sum(rate[:i,i]) + np.sum(rate[i+1:,i]))
return rate
[docs]def rand_rate(nkeep, count):
""" Randomly generate initial matrix.
Parameters
----------
nkeep : int
Number of states in transition matrix.
count : np.array
Transition matrix.
Returns
-------
rand_rate : np.array
The random rate matrix.
"""
nkeys = len(count)
rand_rate = np.zeros((nkeys, nkeys), float)
for i in range(nkeys):
for j in range(nkeys):
if i != j:
if (count[i,j] !=0) and (count[j,i] != 0):
rand_rate[j,i] = np.exp(np.random.randn()*-3)
rand_rate[i,i] = -np.sum(rand_rate[:,i] )
return rand_rate
[docs]def calc_mlrate(nkeep, count, lagt, rate_init):
""" Calculate rate matrix using maximum likelihood Bayesian method.
We use a the MLPB method described by Buchete and Hummer.[1]_
Parameters
----------
nkeep : int
Number of states in transition matrix.
count : np.array
Transition matrix.
lagt : float
The lag time.
Returns
-------
rate : np.array
The rate matrix.
Notes
-----
..[1] N.-V. Buchete and G. Hummer, "Coarse master equations for
peptide folding dynamics", J. Phys. Chem. B (2008).
"""
# initialize rate matrix and equilibrium distribution enforcing detailed balance
p_prev = np.sum(count, axis=0)/np.float(np.sum(count))
rate_prev = detailed_balance(nkeep, rate_init, p_prev)
ml_prev = likelihood(nkeep, rate_prev, count, lagt)
# initialize MC sampling
print ("MLPB optimization of rate matrix:\n START")
#print rate_prev,"\n", p_prev, ml_prev
ml_ref = ml_prev
ml_cum = [ml_prev]
temp_cum = [1.]
nstep = 0
nsteps = 1000*nkeep**2
k = -3./nsteps
nfreq = 10
ncycle = 0
accept = 0
rate_best = rate_prev
ml_best = ml_prev
while True:
# random choice of MC move
rate, p = mc_move(nkeep, rate_prev, p_prev)
rate = detailed_balance(nkeep, rate, p)
# calculate likelihood
ml = likelihood(nkeep, rate, count, lagt)
# Boltzmann acceptance / rejection
if ml < ml_prev:
#print " ACCEPT\n"
rate_prev = rate
p_prev = p
ml_prev = ml
accept +=1
if ml < ml_best:
ml_best = ml
rate_best = rate
else:
delta_ml = ml - ml_prev
beta = (1 - np.exp(k*nsteps))/(np.exp(k*nstep) - np.exp(k*nsteps)) if ncycle > 0 else 1
weight = np.exp(-beta*delta_ml)
if np.random.random() < weight:
#print " ACCEPT BOLTZMANN\n"
rate_prev = rate
p_prev = p
ml_prev = ml
accept +=1
nstep +=1
if nstep > nsteps:
ncycle +=1
ml_cum.append(ml_prev)
temp_cum.append(1./beta)
print ("\n END of cycle %g"%ncycle)
print (" acceptance :%g"%(np.float(accept)/nsteps))
accept = 0
print (rate_prev)
print (" L old =", ml_ref,"; L new:", ml_prev)
improvement = (ml_ref - ml_cum[-1])/ml_ref
print (" improvement :%g"%improvement)
if improvement > 0.001 or ncycle < 3:
nstep = 0
ml_ref = np.mean(ml_cum[-nsteps:])
else:
break
elif nstep % nfreq == 0:
ml_cum.append(ml_prev)
temp_cum.append(1./beta)
return rate_best, ml_cum, temp_cum
[docs]def mc_move(nkeep, rate, peq):
""" Make MC move in either rate or equilibrium probability.
Changes in equilibrium probabilities are introduced so that the new value
is drawn from a normal distribution centered at the current value.
Parameters
----------
nkeep : int
The number of states.
rate : array
The rate matrix obeying detailed balance.
peq : array
The equilibrium probability
"""
nparam = nkeep*(nkeep - 1)/2 + nkeep - 1
npeq = nkeep - 1
while True:
i = np.random.randint(0, nparam)
#print i
rate_new = copy.deepcopy(rate)
peq_new = copy.deepcopy(peq)
if i < npeq:
#print " Peq"
scale = np.mean(peq)*0.1
# peq_new[i] = np.random.normal(loc=peq[i], scale=scale)
peq_new[i] = peq[i] + (np.random.random() - 0.5)*scale
peq_new = peq_new/np.sum(peq_new)
if np.all(peq_new > 0):
break
else:
#print " Rate"
i = np.random.randint(0, nkeep - 1)
try:
j = np.random.randint(i + 1, nkeep - 1)
except ValueError:
j = nkeep - 1
try:
scale = np.mean(np.abs(rate>0.))*0.1
#rate_new[j,i] = np.random.normal(loc=rate[j,i], scale=scale)
rate_new[j,i] = rate[j,i] + (np.random.random() - 0.5)*scale
if np.all((rate_new - np.diag(np.diag(rate_new))) >= 0):
break
except ValueError:
pass
#else:
# print rate_new - np.diag(np.diag(rate_new))
return rate_new, peq_new
[docs]def detailed_balance(nkeep, rate, peq):
""" Enforce detailed balance in rate matrix.
Parameters
----------
nkeep : int
The number of states.
rate : array
The rate matrix obeying detailed balance.
peq : array
The equilibrium probability
"""
for i in range(nkeep):
for j in range(i):
rate[j,i] = rate[i,j]*peq[j]/peq[i]
rate[i,i] = 0
rate[i,i] = -np.sum(rate[:,i])
return rate
[docs]def likelihood(nkeep, rate, count, lagt):
""" Likelihood of a rate matrix given a count matrix
We use the procedure described by Buchete and Hummer.[1]_
Parameters
----------
nkeep : int
Number of states in transition matrix.
count : np.array
Transition matrix.
lagt : float
The lag time.
Returns
-------
mlog_like : float
The log likelihood
Notes
-----
..[1] N.-V. Buchete and G. Hummer, "Coarse master equations for
peptide folding dynamics", J. Phys. Chem. B (2008).
"""
# calculate symmetrized rate matrix
ratesym = np.multiply(rate,rate.transpose())
ratesym = np.sqrt(ratesym)
for i in range(nkeep):
ratesym[i,i] = -ratesym[i,i]
# calculate eigenvalues and eigenvectors
evalsym, evectsym = np.linalg.eig(ratesym)
# index the solutions
indx_eig = np.argsort(-evalsym)
# equilibrium population
ieq = indx_eig[0]
# calculate left and right eigenvectors
phiR = np.zeros((nkeep, nkeep))
phiL = np.zeros((nkeep, nkeep))
for i in range(nkeep):
phiR[:,i] = evectsym[:,i]*evectsym[:,ieq]
phiL[:,i] = evectsym[:,i]/evectsym[:,ieq]
# calculate propagators
prop = np.zeros((nkeep, nkeep), float)
for i in range(nkeep):
for j in range(nkeep):
for n in range(nkeep):
prop[j,i] = prop[j,i] + \
phiR[j,n]*phiL[i,n]*np.exp(-abs(evalsym[n])*lagt)
# calculate likelihood using matrix of transitions
log_like = 0.
for i in range(nkeep):
for j in range(nkeep):
if count[j,i] > 0:
log_like = log_like + float(count[j,i])*np.log(prop[j,i])
return -log_like
[docs]def partial_rate(K, elem):
""" Calculates the derivative of the rate matrix
Parameters
----------
K : np.array
The rate matrix.
elem : int
Integer corresponding to which we calculate the
partial derivative.
Returns
-------
d_K : np.array
Partial derivative of rate matrix.
"""
nstates = len(K[0])
d_K = np.zeros((nstates,nstates), float)
for i in range(nstates):
if i != elem:
d_K[i,elem] = beta/2.*K[i,elem];
d_K[elem,i] = -beta/2.*K[elem,i];
for i in range(nstates):
d_K[i,i] = -np.sum(d_K[:,i])
return d_K
[docs]def partial_peq(peq, elem):
""" Calculates derivative of equilibrium distribution
Parameters
----------
peq : np.array
Equilibrium probabilities.
"""
nstates = len(peq)
d_peq = []
for i in range(nstates):
if i != elem:
d_peq.append(beta*peq[i]*peq[elem])
else:
d_peq.append(-beta*peq[i]*(1. - peq[i]))
return d_peq
[docs]def partial_pfold(states, K, d_K, FF, UU, elem):
""" Calculates derivative of pfold """
nstates = len(states)
# define end-states
I = list(filter(lambda x: x not in UU+FF, range(nstates)))
NI = len(I)
# calculate committors
b = np.zeros([NI], float)
A = np.zeros([NI,NI], float)
db = np.zeros([NI], float)
dA = np.zeros([NI,NI], float)
for j_ind in range(NI):
j = I[j_ind]
summ = 0.
sumd = 0.
for i in FF:
summ += K[i][j]
sumd += d_K[i][j]
b[j_ind] = -summ
db[j_ind] = -sumd
for i_ind in range(NI):
i = I[i_ind]
A[j_ind][i_ind] = K[i][j]
dA[j_ind][i_ind] = d_K[i][j]
# solve Ax + Bd(x) = c
Ainv = np.linalg.inv(A)
pfold = np.dot(Ainv,b)
x = np.dot(Ainv,db - np.dot(dA,pfold))
dpfold = np.zeros(nstates,float)
for i in range(nstates):
if i in UU:
dpfold[i] = 0.0
elif i in FF:
dpfold[i] = 0.0
else:
ii = I.index(i)
dpfold[i] = x[ii]
return dpfold
[docs]def partial_flux(states, peq, K, pfold, d_peq, d_K, d_pfold, target):
""" Calculates derivative of reactive flux """
# flux matrix and reactive flux
nstates = len(states)
sum_d_flux = 0
d_J = np.zeros((nstates,nstates),float)
for i in range(nstates):
for j in range(nstates):
d_J[j][i] = d_K[j][i]*peq[i]*(pfold[j]-pfold[i]) + \
K[j][i]*d_peq[i]*(pfold[j]-pfold[i]) + \
K[j][i]*peq[i]*(d_pfold[j]-d_pfold[i])
if j in target and K[j][i]>0: # dividing line corresponds to I to F transitions
sum_d_flux += d_J[j][i]
return sum_d_flux
[docs]def propagate_worker(x):
""" Propagate dynamics using rate matrix exponential
Parameters
----------
x : list
Contains K, the time and the initial population
Returns
-------
popul : np.array
The propagated population
"""
rate, t, pini = x
expkt = spla.expm(rate*t)
popul = mat_mul_v(expkt, pini)
return popul
[docs]def propagateT_worker(x):
""" Propagate dynamics using power of transition matrix
Parameters
----------
x : list
Contains T, the power and initial population
Returns
-------
popul : np.array
The propagated population
"""
trans, power, pini = x
trans_pow = np.linalg.matrix_power(trans,power)
popul = mat_mul_v(trans_pow, pini)
return popul
#def gen_path_lengths(keys, J, pfold, flux, FF, UU):
# """ use BHS prescription for defining path lenghts """
# nkeys = len(keys)
# I = [x for x in range(nkeys) if x not in FF+UU]
# Jnode = []
# # calculate flux going through nodes
# for i in range(nkeys):
# Jnode.append(np.sum([J[i,x] for x in range(nkeys) \
# if pfold[x] < pfold[i]]))
# # define matrix with edge lengths
# Jpath = np.zeros((nkeys, nkeys), float)
# for i in UU:
# for j in I + FF:
# if J[j,i] > 0:
# Jpath[j,i] = np.log(flux/J[j,i]) + 1
# for i in I:
# for j in [x for x in FF+I if pfold[x] > pfold[i]]:
# if J[j,i] > 0:
# Jpath[j,i] = np.log(Jnode[j]/J[j,i]) + 1
# return Jnode, Jpath
#def calc_acf(x):
# """ mp worker that calculates the ACF for a given mode
#
# Parameters
# ----------
# x : list
# List containing input for each mp worker. Includes:
# distraj :the time series of states
# dt : the timestep for that trajectory
# keys : the keys used in the assignment
# lagt : the lag time for construction
#
# Returns
# -------
# acf : array
# The autocorrelation function from that trajectory.
#
# """
# # parse input from multiprocessing
# distraj = x[0]
# dt = x[1]
# keys = x[2]
# nkeys = len(keys)
# lagt = x[3]
## time =
## sliding = x[4]
#
## ltraj = len(distraj)
## lag = int(lagt/dt) # number of frames per lag time
## if sliding:
## slider = 1 # every state is initial state
## else:
## slider = lag
##
## count = np.zeros([nkeys,nkeys], np.int32)
## for i in range(0, ltraj-lag, slider):
## j = i + lag
## state_i = distraj[i]
## state_j = distraj[j]
## if state_i in keys:
## idx_i = keys.index(state_i)
## if state_j in keys:
## idx_j = keys.index(state_j)
## try:
## count[idx_j][idx_i] += 1
## except UnboundLocalError:
## pass
# return acf
#def project_worker(x):
# """ project simulation trajectories on eigenmodes"""
# trans, power, pini = x
# trans_pow = np.linalg.matrix_power(trans,power)
# popul = mat_mul_v(trans_pow, pini)
# return popul
#
[docs]def peq_averages(peq_boots, keep_keys_boots, keys):
""" Return averages from bootstrap results
Parameters
----------
peq_boots : list
List of Peq arrays
keep_keys_boots : list
List of key lists
keys : list
List of keys
Returns:
-------
peq_ave : array
Peq averages
peq_std : array
Peq std
"""
peq_ave = []
peq_std = []
peq_indexes = []
peq_keep = []
for k in keys:
peq_indexes.append([x.index(k) if k in x else None for x in keep_keys_boots])
nboots = len(peq_boots)
for k in keys:
l = keys.index(k)
data = []
for n in range(nboots):
if peq_indexes[l][n] is not None:
data.append(peq_boots[n][peq_indexes[l][n]])
try:
peq_ave.append(np.mean(data))
peq_std.append(np.std(data))
peq_keep.append(data)
except RuntimeWarning:
peq_ave.append(0.)
peq_std.append(0.)
return peq_ave, peq_std
[docs]def tau_averages(tau_boots, keys):
""" Return averages from bootstrap results
Parameters
----------
tau_boots : list
List of Tau arrays
Returns:
-------
tau_ave : array
Tau averages
tau_std : array
Tau std
"""
tau_ave = []
tau_std = []
tau_keep = []
for n in range(len(keys)-1):
try:
data = [x[n] for x in tau_boots if not np.isnan(x[n])]
tau_ave.append(np.mean(data))
tau_std.append(np.std(data))
tau_keep.append(data)
except IndexError:
continue
return tau_ave, tau_std
[docs]def matrix_ave(mat_boots, keep_keys_boots, keys):
""" Return averages from bootstrap results
Parameters
----------
mat_boots : list
List of matrix arrays
keep_keys_boots : list
List of key lists
keys : list
List of keys
Returns:
-------
mat_ave : array
Matrix averages
mat_std : array
Matrix std
"""
mat_ave = []
mat_std = []
nboots = len(keep_keys_boots)
for k in keys:
mat_ave_keep = []
mat_std_keep = []
for kk in keys:
data = []
for n in range(nboots):
try:
l = keep_keys_boots[n].index(k)
ll = keep_keys_boots[n].index(kk)
data.append(mat_boots[n][l,ll])
except IndexError:
data.append(0.)
try:
mat_ave_keep.append(np.mean(data))
mat_std_keep.append(np.std(data))
except RuntimeWarning:
mat_ave_keep.append(0.)
mat_std_keep.append(0.)
mat_ave.append(mat_ave_keep)
mat_std.append(mat_std_keep)
return mat_ave, mat_std