1
2 """
3 Created on Thu Sep 26 12:06:07 2013
4
5 Copyright (c) 2013-2014, CEA/DSV/I2BM/Neurospin. All rights reserved.
6
7 @author: Tommy Löfstedt, Edouard Duchesnay
8 @email: tommy.loefstedt@cea.fr, edouard.duchesnay@cea.fr
9 @license: BSD 3-clause.
10 """
11 import abc
12
13 import numpy as np
14
15 from utils import TOLERANCE
16 from utils import RandomUniform
17 from utils import norm2
18
19 __all__ = ["grad_l1", "grad_l1mu", "grad_l2", "grad_l2", "grad_l2_squared",
20 "grad_tv", "grad_tvmu", "grad_grouptvmu"]
24
25 __metaclass__ = abc.ABCMeta
26
28
29 self.l = float(l)
30
31 for k in kwargs:
32 setattr(self, k, kwargs[k])
33
34 @abc.abstractmethod
36 raise NotImplementedError("Abstract method 'grad' must be "
37 "specialised!")
38
39
40 -class L1(Function):
41
45
47 """Sub-gradient of the function
48
49 f(x) = |x|_1,
50
51 where |x|_1 is the L1-norm.
52 """
53 grad = np.zeros((x.shape[0], 1))
54 grad[x >= TOLERANCE] = 1.0
55 grad[x <= -TOLERANCE] = -1.0
56 between = (x > -TOLERANCE) & (x < TOLERANCE)
57 grad[between] = self.rng(between.sum())
58
59 return self.l * grad
60
76
79
83
85 """Gradient of the function
86
87 f(x) = L1(mu, x),
88
89 where L1(mu, x) is the Nesterov smoothed L1-norm.
90 """
91 alpha = (1.0 / self.mu) * x
92 asnorm = np.abs(alpha)
93 i = asnorm > 1.0
94 alpha[i] = np.divide(alpha[i], asnorm[i])
95
96 return self.l * alpha
97
100 """Gradient of the function
101
102 f(x) = L1(mu, x),
103
104 where L1(mu, x) is the Nesterov smoothed L1-norm.
105 """
106 alpha = (1.0 / mu) * beta
107 asnorm = np.abs(alpha)
108 i = asnorm > 1.0
109 alpha[i] = np.divide(alpha[i], asnorm[i])
110
111 return alpha
112
113
114 -class L2(Function):
115
119
121 """Sub-gradient of the function
122
123 f(x) = |x|_2,
124
125 where |x|_2 is the L2-norm.
126 """
127 norm_beta = norm2(x)
128 if norm_beta > TOLERANCE:
129 return x * (1.0 / norm_beta)
130 else:
131 D = x.shape[0]
132 u = (self.rng(D, 1) * 2.0) - 1.0
133 norm_u = norm2(u)
134 a = self.rng()
135
136 return (self.l * (a / norm_u)) * u
137
140 """Sub-gradient of the function
141
142 f(x) = |x|_2,
143
144 where |x|_2 is the L2-norm.
145 """
146 norm_beta = norm2(beta)
147 if norm_beta > TOLERANCE:
148 return beta * (1.0 / norm_beta)
149 else:
150 D = beta.shape[0]
151 u = (rng(D, 1) * 2.0) - 1.0
152 norm_u = norm2(u)
153 a = rng()
154
155 return u * (a / norm_u)
156
159
163
165 """Gradient of the function
166
167 f(x) = (1 / 2) * |x|²_2,
168
169 where |x|²_2 is the squared L2-norm.
170 """
171 return self.l * x
172
175 """Gradient of the function
176
177 f(x) = (1 / 2) * |x|²_2,
178
179 where |x|²_2 is the squared L2-norm.
180 """
181 return beta
182
185
186 __metaclass__ = abc.ABCMeta
187
195
197
198 grad_Ab = 0
199 for i in xrange(len(self.A)):
200 Ai = self.A[i]
201 Ab = Ai.dot(x)
202 grad_Ab += Ai.T.dot(self.norm(Ab, self.rng))
203
204 return self.l * grad_Ab
205
215
217 """ Dual variable of the Nesterov function.
218 """
219 alpha = [0] * len(self.A)
220 for i in xrange(len(self.A)):
221 alpha[i] = self.A[i].dot(x) * (1.0 / self.mu)
222
223
224 alpha = self.project(alpha)
225
226 return alpha
227
229
230 for i in xrange(len(alpha)):
231 astar = alpha[i]
232 normas = np.sqrt(np.sum(astar ** 2.0))
233 if normas > 1.0:
234 astar *= 1.0 / normas
235 alpha[i] = astar
236
237 return alpha
238
241
245
247 """Gradient of the function
248
249 f(x) = TV(x),
250
251 where TV(x) is the total variation function.
252 """
253 beta_flat = x.ravel()
254 Ab = np.vstack([Ai.dot(beta_flat) for Ai in self.A]).T
255 Ab_norm2 = np.sqrt(np.sum(Ab ** 2.0, axis=1))
256
257 upper = Ab_norm2 > TOLERANCE
258 grad_Ab_norm2 = Ab
259 grad_Ab_norm2[upper] = (Ab[upper].T / Ab_norm2[upper]).T
260
261 lower = Ab_norm2 <= TOLERANCE
262 n_lower = lower.sum()
263
264 if n_lower:
265 D = len(self.A)
266 vec_rnd = (self.rng(n_lower, D) * 2.0) - 1.0
267 norm_vec = np.sqrt(np.sum(vec_rnd ** 2.0, axis=1))
268 a = self.rng(n_lower)
269 grad_Ab_norm2[lower] = (vec_rnd.T * (a / norm_vec)).T
270
271 grad = np.vstack([self.A[i].T.dot(grad_Ab_norm2[:, i]) \
272 for i in xrange(len(self.A))])
273 grad = grad.sum(axis=0)
274
275 return self.l * grad.reshape(x.shape)
276
279 beta_flat = beta.ravel()
280 Ab = np.vstack([Ai.dot(beta_flat) for Ai in A]).T
281 Ab_norm2 = np.sqrt(np.sum(Ab ** 2.0, axis=1))
282
283 upper = Ab_norm2 > TOLERANCE
284 grad_Ab_norm2 = Ab
285 grad_Ab_norm2[upper] = (Ab[upper].T / Ab_norm2[upper]).T
286
287 lower = Ab_norm2 <= TOLERANCE
288 n_lower = lower.sum()
289
290 if n_lower:
291 D = len(A)
292 vec_rnd = (rng(n_lower, D) * 2.0) - 1.0
293 norm_vec = np.sqrt(np.sum(vec_rnd ** 2.0, axis=1))
294 a = rng(n_lower)
295 grad_Ab_norm2[lower] = (vec_rnd.T * (a / norm_vec)).T
296
297 grad = np.vstack([A[i].T.dot(grad_Ab_norm2[:, i]) for i in xrange(len(A))])
298 grad = grad.sum(axis=0)
299
300 return grad.reshape(beta.shape)
301
308
313
316
320
322 """Gradient of the function
323
324 f(x) = TV(mu, x),
325
326 where TV(mu, x) is the Nesterov smoothed total variation function.
327 """
328 return self.smoothed_grad(x)
329
331 """ Projection onto the compact space of the smoothed TV function.
332 """
333 ax = alpha[0]
334 ay = alpha[1]
335 az = alpha[2]
336 anorm = ax ** 2.0 + ay ** 2.0 + az ** 2.0
337 i = anorm > 1.0
338
339 anorm_i = anorm[i] ** 0.5
340 ax[i] = np.divide(ax[i], anorm_i)
341 ay[i] = np.divide(ay[i], anorm_i)
342 az[i] = np.divide(az[i], anorm_i)
343
344 return [ax, ay, az]
345
352
355
359
361 """Gradient of the function
362
363 f(x) = GL(mu, x),
364
365 where GL(mu, x) is the Nesterov smoothed group lasso function.
366 """
367 return self.smoothed_grad(x)
368
375
378
382
384 """Gradient of the function
385
386 f(x) = GroupTV(mu, x),
387
388 where GroupTV(mu, x) is the Nesterov smoothed group total variation
389 function.
390 """
391 return self.smoothed_grad(x)
392
394 """ Projection onto the compact space of the smoothed Group TV
395 function.
396 """
397 for g in xrange(0, len(a), 3):
398
399 ax = a[g + 0]
400 ay = a[g + 1]
401 az = a[g + 2]
402 anorm = ax ** 2.0 + ay ** 2.0 + az ** 2.0
403 i = anorm > 1.0
404
405 anorm_i = anorm[i] ** 0.5
406 ax[i] = np.divide(ax[i], anorm_i)
407 ay[i] = np.divide(ay[i], anorm_i)
408 az[i] = np.divide(az[i], anorm_i)
409
410 a[g + 0] = ax
411 a[g + 1] = ay
412 a[g + 2] = az
413
414 return a
415
422
425 """ Projection onto the compact space of the smoothed Group TV function.
426 """
427 for g in xrange(0, len(a), 3):
428
429 ax = a[g + 0]
430 ay = a[g + 1]
431 az = a[g + 2]
432 anorm = ax ** 2.0 + ay ** 2.0 + az ** 2.0
433 i = anorm > 1.0
434
435 anorm_i = anorm[i] ** 0.5
436 ax[i] = np.divide(ax[i], anorm_i)
437 ay[i] = np.divide(ay[i], anorm_i)
438 az[i] = np.divide(az[i], anorm_i)
439
440 a[g + 0] = ax
441 a[g + 1] = ay
442 a[g + 2] = az
443
444 return a
445
448
449 grad_Ab = 0
450 for i in xrange(len(A)):
451 Ai = A[i]
452 Ab = Ai.dot(beta)
453 grad_Ab += Ai.T.dot(grad_norm(Ab, rng))
454
455 return grad_Ab
456
465
468 """ Dual variable of the Nesterov function.
469 """
470 alpha = [0] * len(A)
471 for i in xrange(len(A)):
472 alpha[i] = A[i].dot(beta) * (1.0 / mu)
473
474
475 alpha = proj(alpha)
476
477 return alpha
478
481
482 for i in xrange(len(alpha)):
483 astar = alpha[i]
484 normas = np.sqrt(np.sum(astar ** 2.0))
485 if normas > 1.0:
486 astar *= 1.0 / normas
487 alpha[i] = astar
488
489 return alpha
490
493 """ Projection onto the compact space of the smoothed TV function.
494 """
495 ax = alpha[0]
496 ay = alpha[1]
497 az = alpha[2]
498 anorm = ax ** 2.0 + ay ** 2.0 + az ** 2.0
499 i = anorm > 1.0
500
501 anorm_i = anorm[i] ** 0.5
502 ax[i] = np.divide(ax[i], anorm_i)
503 ay[i] = np.divide(ay[i], anorm_i)
504 az[i] = np.divide(az[i], anorm_i)
505
506 return [ax, ay, az]
507
508
509 if __name__ == "__main__":
510 import doctest
511 doctest.testmod()
512