-
Notifications
You must be signed in to change notification settings - Fork 1
/
mini_algo.py
284 lines (242 loc) · 11.1 KB
/
mini_algo.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
import numpy as np
import sys
from sklearn.base import clone
from sklearn.gaussian_process.kernels import ConstantKernel, RBF
standard_kernel = ConstantKernel(1.) * RBF(length_scale=2.)
from nistats.hemodynamic_models import glover_hrf
from scipy.interpolate import interp1d
hrf = glover_hrf(1., 16., time_length=33)
glover = interp1d(np.linspace(0, 33., len(hrf), endpoint=False), hrf)
def zero_hrf(x):
return np.zeros_like(x)
from gp import _get_hrf_measurements, _get_design_from_hrf_measures
from scipy.sparse import coo_matrix, eye, block_diag
def get_hrf_measurement_covariance(hrf_measurement_points, kernel,
extra_points=None,
eval_gradient=False):
X = np.concatenate(hrf_measurement_points)
if extra_points is not None:
X = np.concatenate([X, extra_points])
return kernel(X[:, np.newaxis], eval_gradient=eval_gradient)
def get_collapser_Zbeta(beta_values, modulation,
beta_indices,
n_extra_points=0):
col_coordinates = np.concatenate(
[i * np.ones(len(beta_ind))
for i, beta_ind in enumerate(beta_indices)])
all_betas = beta_values[np.concatenate(beta_indices).astype('int')]
all_alphas = np.concatenate(modulation)
row_coordinates = np.arange(len(all_betas))
collapser = coo_matrix((all_betas * all_alphas,
(row_coordinates, col_coordinates)),
shape=(len(all_betas),
len(beta_indices)))
collapser = block_diag([collapser,
eye(n_extra_points, n_extra_points)]).tocsc()
return collapser
def get_hrf_measures(y, betas, hrf_kernel_matrix,
mean_vector,
sigma_squared,
alphas, beta_indices,
extra_values=None,
n_extra_points=None,
return_loglikelihood=False,
hrf_kernel_matrix_gradients=None,
return_loglikelihood_gradient=False,
return_loo_error=False):
if n_extra_points is None:
n_extra_points = hrf_kernel_matrix.shape[0] - len(y)
if extra_values is None:
extra_values = np.zeros(n_extra_points)
y = np.concatenate([y, extra_values])
Zcollapser = get_collapser_Zbeta(betas, alphas,
beta_indices,
n_extra_points=n_extra_points)
measurement_covariance = Zcollapser.T.dot(
Zcollapser.T.dot(hrf_kernel_matrix.T).T)
measurement_mean = Zcollapser.T.dot(mean_vector)
cross_covariance = Zcollapser.T.dot(hrf_kernel_matrix.T).T
noisy_measurement_covariance = measurement_covariance.copy()
noisy_measurement_covariance[
:len(y) - n_extra_points,
:len(y) - n_extra_points] += np.eye(
len(y) - n_extra_points) * sigma_squared
G = np.linalg.inv(noisy_measurement_covariance)
dual_coef = G.dot(y - measurement_mean)
hrf_measurements = mean_vector + cross_covariance.dot(dual_coef)
outputs = [hrf_measurements]
if return_loglikelihood:
data_fit = -.5 * np.dot(y - measurement_mean, dual_coef)
ev = np.linalg.eigvalsh(noisy_measurement_covariance)
complexity = -.5 * np.sum(np.log(ev))
loglikelihood = data_fit + complexity - y.shape[0] / 2. * np.log(2 * np.pi)
outputs.append(loglikelihood)
if return_loglikelihood_gradient and hrf_kernel_matrix_gradients is not None:
dual_minus_G = dual_coef[:, np.newaxis] * dual_coef - G
measurement_kernel_gradients = np.concatenate([Zcollapser.T.dot(
Zcollapser.T.dot(grad_mat).T)[..., np.newaxis]
for grad_mat in hrf_kernel_matrix_gradients.T], axis=2)
ll_gradient = .5 * (dual_minus_G[..., np.newaxis] *
measurement_kernel_gradients).sum(0).sum(0)
outputs.append(ll_gradient)
if return_loo_error:
diagG = np.diag(G)
looe = dual_coef / diagG
outputs.append(looe)
return outputs
def alternating_optimization(paradigm, y, hrf_length=32.,
t_r=None, time_offset=None,
frame_times=None,
kernel=standard_kernel,
mean=glover,
sigma_squared=1.,
initial_beta=None,
modulation=None,
n_alternations=20,
rescale_hrf=False,
optimize_kernel=False,
optimize_sigma_squared=False,
n_iter_optimize=None,
clamp_endpoints=True):
if frame_times is None:
if t_r is None or time_offset is None:
raise ValueError(
'If frametimes not specified, then set t_r and time_offset')
kernel = clone(kernel)
# if rescale_hrf:
# scale_kernel = ConstantKernel(1.)
# kernel = scale_kernel * kernel
# scale_kernel = kernel.k1
(hrf_measurement_points,
visible_events,
alphas, beta_indices,
unique_events) = _get_hrf_measurements(paradigm, modulation=modulation,
hrf_length=hrf_length, t_r=t_r,
time_offset=time_offset,
zeros_extremes=False,
frame_times=frame_times)
if initial_beta is None:
betas = np.ones(len(unique_events))
else:
betas = initial_beta.copy()
if clamp_endpoints:
extra_points = np.array([0., hrf_length * .99])
else:
extra_points = None
hrf_kernel_matrix, gradients = get_hrf_measurement_covariance(
hrf_measurement_points, kernel,
extra_points=extra_points, eval_gradient=True)
mean_vector = mean(np.concatenate(hrf_measurement_points))
if extra_points is not None:
mean_vector = np.concatenate([mean_vector, mean(extra_points)])
all_residual_norms = []
all_hrf_measures = []
all_lls = []
all_gradients = []
all_looe = []
all_thetas = []
all_sigmas_squared = []
step_size = .01
y = y.view()
y.shape = -1,
for alternating_iter in range(n_alternations):
hrf_measures_, ll, ll_gradient, looe = get_hrf_measures(y, betas, hrf_kernel_matrix,
mean_vector, sigma_squared,
alphas, beta_indices,
n_extra_points=(
len(extra_points) if extra_points is not None else 0),
return_loglikelihood=True,
hrf_kernel_matrix_gradients=gradients,
return_loglikelihood_gradient=True,
return_loo_error=True)
hrf_measures = (hrf_measures_[:-len(extra_points)]
if extra_points is not None else None)
if rescale_hrf:
hrf_size = np.abs(hrf_measures).max()
hrf_measures /= hrf_size
# scale_kernel.theta /= hrf_size
design = _get_design_from_hrf_measures(
hrf_measures,
beta_indices)
betas = np.linalg.pinv(design).dot(y)
residual_norm_squared = ((y - design.dot(betas)) ** 2).sum()
if optimize_sigma_squared:
sigma_squared = residual_norm_squared / (design.shape[0] - design.shape[1])
all_sigmas_squared.append(sigma_squared)
all_residual_norms.append(residual_norm_squared)
all_hrf_measures.append(hrf_measures)
all_lls.append(ll)
all_gradients.append(ll_gradient)
all_looe.append(looe)
all_thetas.append(kernel.theta.copy())
if optimize_kernel:
if len(all_looe) > 1:
if (all_looe[-1] ** 2).sum() > (all_looe[-2] ** 2).sum():
# revert theta step
kernel.theta = np.log(np.maximum(np.exp(kernel.theta) - step_size * ll_gradient, 1e-4))
step_size *= .5
# print('Gradient step too large, reverting. Step size {}'.format(step_size))
kernel.theta = np.log(np.maximum(np.exp(kernel.theta) + step_size * ll_gradient, 1e-4))
if np.isnan(kernel.theta).any(): stop
hrf_kernel_matrix, gradients = get_hrf_measurement_covariance(
hrf_measurement_points, kernel,
extra_points=extra_points, eval_gradient=True)
if np.isnan(hrf_kernel_matrix).any():
stop
sys.stdout.write('.')
sys.stdout.flush()
return betas, (hrf_measurement_points, hrf_measures), all_residual_norms, all_hrf_measures, all_lls, all_gradients, all_looe, all_thetas, all_sigmas_squared
if __name__ == "__main__":
from data_generator import (
generate_spikes_time_series as generate_experiment)
n_events=100
n_blank_events=25
event_spacing=6
t_r=2
hrf_length=32.
event_types=['ev1', 'ev2']
jitter_min=-1
jitter_max=1
time_offset = 20
modulation=None
seed = 42
from nistats.hemodynamic_models import _gamma_difference_hrf
simulation_hrf = _gamma_difference_hrf(tr=1., oversampling=20,
time_length=hrf_length + 1,
undershoot=16., delay=11.)
xs = np.linspace(0., hrf_length + 1, len(simulation_hrf), endpoint=False)
f_sim_hrf = interp1d(xs, simulation_hrf)
from data_generator import make_design_matrix_hrf
paradigm, design_, modulation, measurement_times = generate_experiment(
n_events=n_events,
n_blank_events=n_blank_events,
event_spacing=event_spacing,
t_r=t_r, hrf_length=hrf_length,
event_types=event_types,
jitter_min=jitter_min,
jitter_max=jitter_max,
time_offset=time_offset,
modulation=modulation,
return_jitter=True,
seed=seed)
design_ = make_design_matrix_hrf(measurement_times, paradigm, f_hrf=f_sim_hrf)
design = design_[event_types].values
rng = np.random.RandomState(seed)
beta = rng.randn(len(event_types))
y_clean = design.dot(beta)
noise = rng.randn(len(y_clean))
noise /= np.linalg.norm(noise)
y_noisy = y_clean + np.linalg.norm(y_clean) * noise * 2
kernel = RBF()
(betas, (hrf_measurement_points, hrf_measures),
residuals, hrfs, lls, grads, looes, thetas, sigmas_squared) = alternating_optimization(
paradigm, y_noisy,
hrf_length,
frame_times=measurement_times,
modulation=modulation,
mean=zero_hrf,
n_alternations=50,
sigma_squared=4,
rescale_hrf=True,
optimize_kernel=True,
optimize_sigma_squared=False)