-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
211 lines (164 loc) · 6.55 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import numpy as np
import scipy
import pdb
# Form a companion matrix from the autoregressive matrices
def form_companion(w, u=None):
order = w.shape[0]
size = w.shape[1]
I = np.eye(size * (order - 1))
wcomp = np.block([list(w), [I, np.zeros((size * (order - 1), size))]])
if u is not None:
ucomp = [[u]]
for i in range(order - 1):
ucomp.append([np.zeros((size, size))])
ucomp = np.block(ucomp)
return wcomp, ucomp
else:
return wcomp
# Calculate loadings
# U: matrix of shape (n_neurons * order, dim)
# d: order of model
def calc_loadings(U, d=1):
# Sum over components
U = np.sum(np.power(np.abs(U), 2), axis=-1)
# Reshape and then sum over neurons
U = np.reshape(U, (d, -1))
loadings = np.sum(U, axis=0)
loadings /= np.max(loadings)
return loadings
# Calculate loadings when 2 rectangular matrices are cascaded
# (e.g. dimreduc + decoding)
# U1: matrix of shape (n_neurons * d1, dim)
# U2: matrix of shape (dim * d2, dim2)
def calc_cascaded_loadings(U1, U2, d1=1, d2=1):
# Simply multiply the matrices and then calculate the leverage score.
# Only sensible case if is d2 > dim, in which case we reshape and multiply
# several times
if d2 * U2.shape[0] > U1.shape[1]:
U2 = np.reshape(U2, (d2, U1.shape[1], -1))
V = np.array([U1 @ U2_ for U2_ in U2])
# Sum over final index, and reshaping of d2//d1
U = np.sum(np.sum(np.power(np.abs(V), 2), axis=-1), axis=0)
# Sum over d1
U = np.reshape(U, (d1, -1))
loadings = np.sum(U, axis=0)
loadings /= np.max(loadings)
return loadings
elif d2 * U2.shape[0] == U1.shape[1]:
return calc_loadings(U1 @ U2, d=d1)
else:
raise ValueError("Don't know what to do with this case")
def filter_by_dict(df, root_key, dict_filter):
col = df[root_key].values
filtered_idxs = []
for i, c in enumerate(col):
match = True
for key, val in dict_filter.items():
if c[key] != val:
match = False
if match:
filtered_idxs.append(i)
return df.iloc[filtered_idxs]
# Shortcut to apply multiple filters to pandas dataframe
def apply_df_filters(dtfrm, invert=False, reset_index=True, **kwargs):
filtered_df = dtfrm
for key, value in kwargs.items():
# If the value is the dict
if type(value) == dict:
filtered_df = filter_by_dict(filtered_df, key, value)
else:
if type(value) == list:
matching_idxs = []
for v in value:
df_ = apply_df_filters(filtered_df, reset_index=False, **{key:v})
if invert:
matching_idxs.extend(list(np.setdiff1d(np.arange(filtered_df.shape[0]), list(df_.index))))
else:
matchings_idxs = matching_idxs.extend(list(df_.index))
filtered_df = filtered_df.iloc[matching_idxs]
elif type(value) == str:
filtered_df = filtered_df.loc[[value in s for s in filtered_df[key].values]]
else:
if invert:
filtered_df = filtered_df.loc[filtered_df[key] != value]
else:
filtered_df = filtered_df.loc[filtered_df[key] == value]
if reset_index:
filtered_df.reset_index(inplace=True, drop=True)
# if filtered_df.shape[0] == 0:
# print('Key %s reduced size to 0!' % key)
return filtered_df
def gram_schmidt(v0):
"""
Gives a orthonormal matrix, using modified Gram Schmidt Procedure
:param A: a matrix of column vectors
:return: a matrix of orthonormal column vectors
"""
# assuming A is a square matrix
dim = v0.size
Q = np.zeros((dim, dim), dtype=v0.dtype)
Q[:, 0] = v0/np.linalg.norm(v0, ord=2)
for j in range(1, dim):
# Generate vector with random real and imaginary components
q = np.random.normal(size=dim) + 1j * np.random.normal(size=dim)
for i in range(0, j):
rij = np.vdot(Q[:,i], q)
q = q - rij*Q[:,i]
rjj = np.linalg.norm(q, ord=2)
if np.isclose(rjj,0.0):
raise ValueError("invalid input matrix")
else:
Q[:,j] = q/rjj
return Q
# Assumes eigenvectors are all distinct
# Allows one to calculate the schur decomposition with one's own prescribed eigenvalue ordering
def schur(A, eig_order):
eigvalA, eigvecA = np.linalg.eig(A)
AN = A
# Chain of U matrices
U = []
S = []
for i, eig_idx in enumerate(eig_order):
eigvals, eigvecs = np.linalg.eig(AN)
# Find the eigenvector in the reduced matrix corresponding to the eigenvalue of choice
try:
eig_idxN = np.where(np.isclose(np.real(eigvals), np.real(eigvalA[eig_idx])))[0][0]
except:
pdb.set_trace()
b1 = eigvecs[:, eig_idxN]
# Form an orthonormal basis for the null space
try:
bn = scipy.linalg.orth(scipy.linalg.null_space(b1[np.newaxis, :]))
V = np.append(b1[:, np.newaxis], bn, axis=1)
except:
V = gram_schmidt(b1)
# Needs to be a unitary matrix
try:
assert(np.allclose(np.real(np.conjugate(V).T), np.real(np.linalg.inv(V))))
assert(np.allclose(np.imag(np.conjugate(V).T), np.imag(np.linalg.inv(V))))
except:
V = gram_schmidt(b1)
assert(np.allclose(np.real(np.conjugate(V).T), np.real(np.linalg.inv(V))))
assert(np.allclose(np.imag(np.conjugate(V).T), np.imag(np.linalg.inv(V))))
U.append(V)
# Transform the submatrix and extract its lower diagonal block
B = np.conjugate(V).T @ AN @ V
S.append(B)
AN = B[1:, 1:]
# Chain together the transformation matrices to obtain the final transformation
Q = U[0]
n = A.shape[0]
for i in range(1, len(U)):
# Append the necessary size identity block to the transformations
UU = np.block([[np.eye(i), np.zeros((i, n - i))], [np.zeros((n - i, i)), U[i]]])
Q = Q @ UU
# Extract the upper triangular blocks from the transformed matrices
T = np.zeros((n, n))
for i in range(len(S)):
# Append the necessary number of zeros
if i > 0:
row = np.append(np.zeros(i), S[i][0, :])
else:
row = S[i][0, :]
T[i, :] = row
return T, Q