Skip to content

Commit 6c1bd0c

Browse files
committed
committors!
1 parent e2b82bc commit 6c1bd0c

File tree

2 files changed

+197
-4
lines changed

2 files changed

+197
-4
lines changed

csnanalysis/csn.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import scipy
22
import networkx as nx
33
import numpy as np
4+
from csnanalysis.matrix import eig_weights, mult_weights
45

56
def count_to_trans(countmat):
67
"""
78
Converts a count matrix (in scipy sparse format) to a transition
89
matrix.
910
"""
1011
tmp = np.array(countmat.toarray(),dtype=float)
11-
colsums = tmp.sum(axis=1)
12+
colsums = tmp.sum(axis=0)
1213
for i,c in enumerate(colsums):
1314
if c > 0:
14-
tmp[i] /= c
15+
tmp[:,i] /= c
1516

1617
return(scipy.sparse.coo_matrix(tmp))
1718

@@ -26,7 +27,7 @@ class CSN(object):
2627
def __init__(self, counts, symmetrize=False):
2728
"""
2829
Initializes a CSN object using a counts matrix. This can either be a numpy array,
29-
a scipy sparse matrix, or a list of lists.
30+
a scipy sparse matrix, or a list of lists. Indices: [to][from], (or, [row][column]).
3031
"""
3132
if type(counts) is list:
3233
self.countmat = scipy.sparse.coo_matrix(counts)
@@ -49,12 +50,14 @@ def __init__(self, counts, symmetrize=False):
4950

5051
self.nnodes = self.countmat.shape[0]
5152
self.transmat = count_to_trans(self.countmat)
53+
54+
self.trim_transmat = None
5255

5356
# initialize networkX directed graph
5457
self.graph = nx.DiGraph()
5558
labels = [{'ID' : i} for i in range(self.nnodes)]
5659
self.graph.add_nodes_from(zip(range(self.nnodes),labels))
57-
self.graph.add_weighted_edges_from(zip(self.transmat.row,self.transmat.col,self.transmat.data))
60+
self.graph.add_weighted_edges_from(zip(self.transmat.col,self.transmat.row,self.transmat.data))
5861

5962
def to_gephi(self, cols='all', node_name='node.csv', edge_name='edge.csv'):
6063
"""
@@ -118,3 +121,52 @@ def trim_graph(self, by_inflow=True, by_outflow=True, min_count=0):
118121
self.trim_nnodes = self.trim_countmat.shape[0]
119122
self.trim_transmat = count_to_trans(self.trim_countmat)
120123

124+
125+
def calc_eig_weights(self,label='eig_weights'):
126+
"""
127+
Calculates weights of states using the highest Eigenvalue of the
128+
transition matrix. By default it uses self.trim_transmat, but will
129+
use self.transmat if no trimming has been done.
130+
131+
The weights are stored as node attributes in self.graph with the label
132+
'label', and are also returned from the function.
133+
"""
134+
135+
if self.trim_transmat is None:
136+
# use full transition matrix
137+
full_wts = eig_weights(self.transmat)
138+
self.add_attr(label, full_wts)
139+
else:
140+
# use trimmed transition matrix
141+
wts = eig_weights(self.trim_transmat)
142+
full_wts = np.zeros(self.nnodes,dtype=float64)
143+
for i,ind in enumerate(self.trim_indices):
144+
full_wts[ind] = wts[i]
145+
self.add_attr(label, full_wts)
146+
147+
return full_wts
148+
149+
def calc_mult_weights(self,label='mult_weights',tol=1e-6):
150+
"""
151+
Calculates weights of states using iterative multiplication of the
152+
transition matrix. By default it uses self.trim_transmat, but will
153+
use self.transmat if no trimming has been done.
154+
155+
The weights are stored as node attributes in self.graph with the label
156+
'label', and are also returned from the function.
157+
"""
158+
159+
if self.trim_transmat is None:
160+
# use full transition matrix
161+
full_wts = mult_weights(self.transmat,tol)
162+
self.add_attr(label, full_wts)
163+
else:
164+
# use trimmed transition matrix
165+
wts = mult_weights(self.trim_transmat,tol)
166+
full_wts = np.zeros(self.nnodes,dtype=float64)
167+
for i,ind in enumerate(self.trim_indices):
168+
full_wts[ind] = wts[i]
169+
self.add_attr(label, full_wts)
170+
171+
return full_wts
172+

csnanalysis/matrix.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
2+
import scipy
3+
import numpy as np
4+
5+
def make_sink(transmat,sink_states):
6+
"""
7+
Constructs a transition matrix with "sink states", where the columns are
8+
replaced with identity vectors (diagonal element = 1, off-diagonals = 0).
9+
10+
Input:
11+
12+
transmat -- An N x N transition matrix in scipy sparse coo format.
13+
Columns should sum to 1. Indices: [to][from].
14+
15+
sink_states: A list of integers denoting sinks.
16+
17+
Output: A transition matrix in scipy sparse coo format.
18+
"""
19+
sink_mat = transmat.copy()
20+
21+
set_to_one = np.zeros(len(sink_states),dtype=bool)
22+
for i in range(len(sink_mat.data)):
23+
if sink_mat.col[i] in sink_states:
24+
if sink_mat.col[i] != sink_mat.row[i]:
25+
sink_mat.data[i] = 0.
26+
else:
27+
sink_mat.data[i] = 1.
28+
set_to_one[sink_states.index(sink_mat.col[i])] = True
29+
30+
# set diagonal elements to 1
31+
for i in range(len(sink_states)):
32+
if not set_to_one[i]:
33+
# add element sink_mat[sink_states[i]][sink_states[i]] = 1
34+
np.append(sink_mat.row,sink_states[i])
35+
np.append(sink_mat.col,sink_states[i])
36+
np.append(sink_mat.data,1.)
37+
38+
# remove zeros
39+
sink_mat.eliminate_zeros()
40+
41+
return sink_mat
42+
43+
def eig_weights(transmat):
44+
"""
45+
Calculates the weights as the top eigenvector of the transition matrix.
46+
47+
Input:
48+
49+
transmat -- An N x N transition matrix as a numpy array or in
50+
scipy sparse coo format. Columns should sum to 1.
51+
Indices: [to][from]
52+
53+
Output: An array of weights of size N.
54+
"""
55+
56+
vals, vecs = scipy.sparse.linalg.eigs(transmat,k=1)
57+
return np.real(vecs[:,0])/np.real(vecs[:,0].sum())
58+
59+
def mult_weights(transmat,tol=1e-6):
60+
"""
61+
Calculates the steady state weights as the columns of transmat^infinity.
62+
transmat^infinity is approximated by successively squaring transmat until
63+
the maximum variation in the rows is less than tol.
64+
65+
Input:
66+
67+
transmat -- An N x N transition matrix as a numpy array or in
68+
scipy sparse coo format. Columns should sum to 1.
69+
Indices: [to][from]
70+
71+
tol -- Threshold for stopping the iterative multiplication.
72+
73+
Output: An array of weights of size N.
74+
"""
75+
76+
banded_mat = trans_mult_iter(transmat,tol)
77+
return banded_mat[:,0]
78+
79+
def trans_mult_iter(transmat,tol,maxstep=20):
80+
"""
81+
Performs iterative multiplication of transmat until the maximum variation in
82+
the rows is less than tol.
83+
"""
84+
if type(transmat) is np.ndarray:
85+
t = transmat.copy()
86+
else:
87+
t = transmat.toarray()
88+
89+
var = 1
90+
step = 0
91+
while var > tol or step > maxstep:
92+
newmat = np.matmul(t,t)
93+
var = np.abs(newmat-t).max()
94+
t = newmat.copy()
95+
step += 1
96+
97+
if step > maxstep:
98+
print("Warning: iterative multiplication not converged after",maxstep,"steps: (var = ",var)
99+
100+
return t
101+
102+
def committor(transmat,basins,tol=1e-6,maxstep=20):
103+
"""
104+
This function computes committor probabilities, given a transition matrix
105+
and a list of states that comprise the basins.
106+
107+
Input:
108+
109+
transmat -- An N x N transition matrix in scipy sparse coo format.
110+
Columns should sum to 1. Indices: [to][from]
111+
112+
basins -- A list of lists, describing which states make up the
113+
basins of attraction. There can be any number of basins.
114+
e.g. [[basin1_a,basin1_b,...],[basin2_a,basin2_b,...]]
115+
116+
Output: An array of committor probabilities of size N x B, where B
117+
is the number of basins. Committors will sum to 1 for each state.
118+
"""
119+
120+
# make sink_matrix
121+
sink_mat = make_sink(transmat,list(np.array(basins).flatten()))
122+
123+
sink_results = trans_mult_iter(sink_mat,tol,maxstep)
124+
125+
committor = np.zeros((transmat.shape[0],len(basins)),dtype=float)
126+
127+
for i in range(transmat.shape[0]):
128+
comm_done = False
129+
for j,b in enumerate(basins):
130+
if i in b:
131+
committor[i][j] = 1
132+
comm_done = True
133+
break
134+
if not comm_done:
135+
for j,b in enumerate(basins):
136+
committor[i][j] = 0.
137+
for bstate in b:
138+
committor[i][j] += sink_results[bstate][i]
139+
140+
return committor
141+

0 commit comments

Comments
 (0)