1
2 """
3 The :mod:`parsimony.functions.losses` module contains the loss functions used
4 throughout the package. These represent mathematical functions and should thus
5 have properties used by the corresponding algorithms. These properties are
6 defined in :mod:`parsimony.functions.properties`.
7
8 Loss functions should be stateless. Loss functions may be shared and copied
9 and should therefore not hold anything that cannot be recomputed the next time
10 it is called.
11
12 Created on Mon Apr 22 10:54:29 2013
13
14 Copyright (c) 2013-2014, CEA/DSV/I2BM/Neurospin. All rights reserved.
15
16 @author: Tommy Löfstedt, Vincent Guillemot, Edouard Duchesnay and
17 Fouad Hadj-Selem
18 @email: lofstedt.tommy@gmail.com, edouard.duchesnay@cea.fr
19 @license: BSD 3-clause.
20 """
21 import numpy as np
22
23 try:
24 from . import properties
25 except ValueError:
26 import parsimony.functions.properties as properties
27 import parsimony.utils as utils
28 import parsimony.utils.consts as consts
29
30 __all__ = ["LinearRegression", "RidgeRegression",
31 "LogisticRegression", "RidgeLogisticRegression",
32 "LatentVariableVariance", "LinearFunction"]
33
34
35 -class LinearRegression(properties.CompositeFunction,
36 properties.Gradient,
37 properties.LipschitzContinuousGradient,
38 properties.StepSize):
39 """The Linear regression loss function.
40 """
42 """
43 Parameters
44 ----------
45 X : Numpy array (n-by-p). The regressor matrix.
46
47 y : Numpy array (n-by-1). The regressand vector.
48
49 k : Non-negative float. The ridge parameter.
50
51 mean : Boolean. Whether to compute the squared loss or the mean
52 squared loss. Default is True, the mean squared loss.
53 """
54 self.X = X
55 self.y = y
56
57 self.mean = bool(mean)
58
59 self.reset()
60
62 """Free any cached computations from previous use of this Function.
63
64 From the interface "Function".
65 """
66 self._L = None
67
69 """Function value.
70
71 From the interface "Function".
72
73 Parameters
74 ----------
75 beta : Numpy array. Regression coefficient vector. The point at which
76 to evaluate the function.
77 """
78 if self.mean:
79 d = 2.0 * float(self.X.shape[0])
80 else:
81 d = 2.0
82
83 f = (1.0 / d) * np.sum((np.dot(self.X, beta) - self.y) ** 2.0)
84
85 return f
86
87 - def grad(self, beta):
88 """Gradient of the function at beta.
89
90 From the interface "Gradient".
91
92 Parameters
93 ----------
94 beta : The point at which to evaluate the gradient.
95
96 Examples
97 --------
98 >>> import numpy as np
99 >>> from parsimony.functions.losses import LinearRegression
100 >>>
101 >>> np.random.seed(42)
102 >>> X = np.random.rand(100, 150)
103 >>> y = np.random.rand(100, 1)
104 >>> lr = LinearRegression(X=X, y=y)
105 >>> beta = np.random.rand(150, 1)
106 >>> round(np.linalg.norm(lr.grad(beta)
107 ... - lr.approx_grad(beta, eps=1e-4)), 9)
108 1.3e-08
109 """
110 grad = np.dot(self.X.T, np.dot(self.X, beta) - self.y)
111
112 if self.mean:
113 grad *= 1.0 / float(self.X.shape[0])
114
115 return grad
116
118 """Lipschitz constant of the gradient.
119
120 From the interface "LipschitzContinuousGradient".
121
122 Examples
123 --------
124 >>> import numpy as np
125 >>> from parsimony.functions.losses import LinearRegression
126 >>>
127 >>> np.random.seed(42)
128 >>> X = np.random.rand(10, 15)
129 >>> y = np.random.rand(10, 1)
130 >>> lr = LinearRegression(X=X, y=y)
131 >>> L = lr.L()
132 >>> L_ = lr.approx_L((15, 1), 10000)
133 >>> L >= L_
134 True
135 >>> round((L - L_) / L, 14)
136 0.14039091870818
137 """
138 if self._L is None:
139
140 from parsimony.algorithms.nipals import FastSVD
141
142
143 n, p = self.X.shape
144 if (max(n, p) > 500 and max(n, p) <= 1000 \
145 and float(max(n, p)) / min(n, p) <= 1.3) \
146 or (max(n, p) > 1000 and max(n, p) <= 5000 \
147 and float(max(n, p)) / min(n, p) <= 5.0) \
148 or (max(n, p) > 5000 and max(n, p) <= 10000 \
149 and float(max(n, p)) / min(n, p) <= 15.0) \
150 or (max(n, p) > 10000 and max(n, p) <= 20000 \
151 and float(max(n, p)) / min(n, p) <= 200.0) \
152 or max(n, p) > 10000:
153
154 v = FastSVD().run(self.X, max_iter=1000)
155 us = np.dot(self.X, v)
156 self._L = np.sum(us ** 2.0)
157
158 else:
159 s = np.linalg.svd(self.X,
160 full_matrices=False, compute_uv=False)
161 self._L = np.max(s) ** 2.0
162
163 if self.mean:
164 self._L /= float(n)
165
166 return self._L
167
168 - def step(self, beta, index=0):
169 """The step size to use in descent methods.
170
171 Parameters
172 ----------
173 beta : Numpy array. The point at which to determine the step size.
174 """
175 return 1.0 / self.L()
176
177
178 -class RidgeRegression(properties.CompositeFunction,
179 properties.Gradient,
180 properties.LipschitzContinuousGradient,
181 properties.StronglyConvex,
182 properties.StepSize):
183 """The Ridge Regression function, i.e. a representation of
184
185 f(x) = (0.5 / n) * ||Xb - y||²_2 + lambda * 0.5 * ||b||²_2,
186
187 where ||.||²_2 is the L2 norm.
188 """
189
190 - def __init__(self, X, y, k, penalty_start=0, mean=True):
191 """
192 Parameters
193 ----------
194 X : Numpy array (n-by-p). The regressor matrix.
195
196 y : Numpy array (n-by-1). The regressand vector.
197
198 k : Non-negative float. The ridge parameter.
199
200 penalty_start : Non-negative integer. The number of columns, variables
201 etc., to except from penalisation. Equivalently, the first
202 index to be penalised. Default is 0, all columns are included.
203
204 mean : Boolean. Whether to compute the squared loss or the mean
205 squared loss. Default is True, the mean squared loss.
206 """
207 self.X = X
208 self.y = y
209 self.k = float(k)
210
211 self.penalty_start = int(penalty_start)
212 self.mean = bool(mean)
213
214 self.reset()
215
217 """Free any cached computations from previous use of this Function.
218
219 From the interface "Function".
220 """
221 self._lambda_max = None
222 self._lambda_min = None
223
225 """Function value.
226
227 From the interface "Function".
228
229 Parameters
230 ----------
231 beta : Numpy array. Regression coefficient vector. The point at which
232 to evaluate the function.
233 """
234 if self.penalty_start > 0:
235 beta_ = beta[self.penalty_start:, :]
236 else:
237 beta_ = beta
238
239 if self.mean:
240 d = 2.0 * float(self.X.shape[0])
241 else:
242 d = 2.0
243
244 f = (1.0 / d) * np.sum((np.dot(self.X, beta) - self.y) ** 2.0) \
245 + (self.k / 2.0) * np.sum(beta_ ** 2.0)
246
247 return f
248
249 - def grad(self, beta):
250 """Gradient of the function at beta.
251
252 From the interface "Gradient".
253
254 Parameters
255 ----------
256 beta : Numpy array. The point at which to evaluate the gradient.
257
258 Examples
259 --------
260 >>> import numpy as np
261 >>> from parsimony.functions.losses import RidgeRegression
262 >>>
263 >>> np.random.seed(42)
264 >>> X = np.random.rand(100, 150)
265 >>> y = np.random.rand(100, 1)
266 >>> rr = RidgeRegression(X=X, y=y, k=3.14159265)
267 >>> beta = np.random.rand(150, 1)
268 >>> round(np.linalg.norm(rr.grad(beta)
269 ... - rr.approx_grad(beta, eps=1e-4)), 9)
270 1.3e-08
271 """
272 gradOLS = np.dot((np.dot(self.X, beta) - self.y).T, self.X).T
273
274 if self.mean:
275 gradOLS *= 1.0 / float(self.X.shape[0])
276
277 if self.penalty_start > 0:
278 gradL2 = np.vstack((np.zeros((self.penalty_start, 1)),
279 self.k * beta[self.penalty_start:, :]))
280 else:
281 gradL2 = self.k * beta
282
283 grad = gradOLS + gradL2
284
285 return grad
286
288 """Lipschitz constant of the gradient.
289
290 From the interface "LipschitzContinuousGradient".
291 """
292 if self._lambda_max is None:
293 s = np.linalg.svd(self.X, full_matrices=False, compute_uv=False)
294
295 self._lambda_max = np.max(s) ** 2.0
296
297 if len(s) < self.X.shape[1]:
298 self._lambda_min = 0.0
299 else:
300 self._lambda_min = np.min(s) ** 2.0
301
302 if self.mean:
303 self._lambda_max /= float(self.X.shape[0])
304 self._lambda_min /= float(self.X.shape[0])
305
306 return self._lambda_max + self.k
307
308 @utils.deprecated("StronglyConvex.parameter")
310 """Smallest eigenvalue of the corresponding covariance matrix.
311
312 From the interface "Eigenvalues".
313 """
314 return self.parameter()
315
317 """Returns the strongly convex parameter for the function.
318
319 From the interface "StronglyConvex".
320 """
321 if self._lambda_min is None:
322 self._lambda_max = None
323 self.L()
324
325 return self._lambda_min + self.k
326
327 - def step(self, beta, index=0):
328 """The step size to use in descent methods.
329
330 Parameters
331 ----------
332 beta : Numpy array. The point at which to determine the step size.
333 """
334 return 1.0 / self.L()
335
336
337 -class LogisticRegression(properties.AtomicFunction,
338 properties.Gradient,
339 properties.LipschitzContinuousGradient,
340 properties.StepSize):
341 """The Logistic Regression loss function.
342
343 (Re-weighted) Log-likelihood (cross-entropy):
344 * f(beta) = -Sum wi (yi log(pi) + (1 − yi) log(1 − pi))
345 = -Sum wi (yi xi' beta − log(1 + e(x_i'beta))),
346
347 * grad f(beta) = -Sum wi[ xi (yi - pi)] + k beta,
348
349 where pi = p(y=1 | xi, beta) = 1 / (1 + exp(-x_i'beta)) and wi is the
350 weight for sample i.
351
352 See [Hastie 2009, p.: 102, 119 and 161, Bishop 2006 p.: 206] for details.
353
354 Parameters
355 ----------
356 X : Numpy array (n-by-p). The regressor matrix.
357
358 y : Numpy array (n-by-1). The regressand vector.
359
360 weights: Numpy array (n-by-1). The sample's weights.
361
362 mean : Boolean. Whether to compute the squared loss or the mean squared
363 loss. Default is True, the mean squared loss.
364 """
365 - def __init__(self, X, y, weights=None, mean=True):
366 self.X = X
367 self.y = y
368 if weights is None:
369
370
371 weights = np.ones(y.shape).reshape(y.shape)
372
373 self.weights = weights
374 self.mean = bool(mean)
375
376 self.reset()
377
379 """Free any cached computations from previous use of this Function.
380
381 From the interface "Function".
382 """
383 self._L = None
384
386 """Function value at the point beta.
387
388 From the interface "Function".
389
390 Parameters
391 ----------
392 beta : Numpy array. Regression coefficient vector. The point at which
393 to evaluate the function.
394 """
395 Xbeta = np.dot(self.X, beta)
396 negloglike = -np.sum(self.weights *
397 ((self.y * Xbeta) - np.log(1 + np.exp(Xbeta))))
398
399 if self.mean:
400 negloglike /= float(self.X.shape[0])
401
402 return negloglike
403
404 - def grad(self, beta):
405 """Gradient of the function at beta.
406
407 From the interface "Gradient".
408
409 Parameters
410 ----------
411 beta : Numpy array. The point at which to evaluate the gradient.
412
413 Examples
414 --------
415 >>> import numpy as np
416 >>> from parsimony.functions.losses import LogisticRegression
417 >>>
418 >>> np.random.seed(42)
419 >>> X = np.random.rand(100, 150)
420 >>> y = np.random.randint(0, 2, (100, 1))
421 >>> lr = LogisticRegression(X=X, y=y, mean=True)
422 >>> beta = np.random.rand(150, 1)
423 >>> round(np.linalg.norm(lr.grad(beta)
424 ... - lr.approx_grad(beta, eps=1e-4)), 10)
425 4e-10
426 >>>
427 >>> np.random.seed(42)
428 >>> X = np.random.rand(100, 150)
429 >>> y = np.random.randint(0, 2, (100, 1))
430 >>> lr = LogisticRegression(X=X, y=y, mean=False)
431 >>> beta = np.random.rand(150, 1)
432 >>> round(np.linalg.norm(lr.grad(beta)
433 ... - lr.approx_grad(beta, eps=1e-4)), 9)
434 3.9e-08
435 """
436 Xbeta = np.dot(self.X, beta)
437
438 pi = np.reciprocal(1.0 + np.exp(-Xbeta))
439
440 grad = -np.dot(self.X.T, self.weights * (self.y - pi))
441
442 if self.mean:
443 grad *= 1.0 / float(self.X.shape[0])
444
445 return grad
446
448 """Lipschitz constant of the gradient.
449
450 Returns the maximum eigenvalue of (1 / 4) * X'WX.
451
452 From the interface "LipschitzContinuousGradient".
453
454 Examples
455 --------
456 >>> import numpy as np
457 >>> from parsimony.functions.losses import LogisticRegression
458 >>>
459 >>> np.random.seed(42)
460 >>> X = np.random.rand(10, 15)
461 >>> y = np.random.randint(0, 2, (10, 1))
462 >>> lr = LogisticRegression(X=X, y=y, mean=True)
463 >>> L = lr.L()
464 >>> L_ = lr.approx_L((15, 1), 10000)
465 >>> L >= L_
466 True
467 >>> round((L - L_) / L, 15)
468 0.45110910457988
469 >>> lr = LogisticRegression(X=X, y=y, mean=False)
470 >>> L = lr.L()
471 >>> L_ = lr.approx_L((15, 1), 10000)
472 >>> L >= L_
473 True
474 >>> round((L - L_) / L, 13)
475 0.430306683612
476 """
477 if self._L == None:
478
479 PWX = 0.5 * np.sqrt(self.weights) * self.X
480
481 s = np.linalg.svd(PWX, full_matrices=False, compute_uv=False)
482 self._L = np.max(s) ** 2.0
483
484 if self.mean:
485 self._L /= float(self.X.shape[0])
486
487 return self._L
488
489 - def step(self, beta, index=0):
490 """The step size to use in descent methods.
491
492 Parameters
493 ----------
494 beta : Numpy array. The point at which to determine the step size.
495 """
496 return 1.0 / self.L()
497
498
499 -class RidgeLogisticRegression(properties.CompositeFunction,
500 properties.Gradient,
501 properties.LipschitzContinuousGradient,
502 properties.StepSize):
503 """The Logistic Regression loss function with a squared L2 penalty.
504
505 Ridge (re-weighted) log-likelihood (cross-entropy):
506
507 * f(beta) = -loglik + k/2 * ||beta||^2_2
508 = -Sum wi (yi log(pi) + (1 − yi) log(1 − pi)) + k/2*||beta||^2_2
509 = -Sum wi (yi xi' beta − log(1 + e(xi' beta))) + k/2*||beta||^2_2
510
511 * grad f(beta) = -Sum wi[ xi (yi - pi)] + k beta
512
513 pi = p(y=1|xi, beta) = 1 / (1 + exp(-xi' beta))
514 wi: sample i weight
515 [Hastie 2009, p.: 102, 119 and 161, Bishop 2006 p.: 206]
516 """
517 - def __init__(self, X, y, k=0.0, weights=None, penalty_start=0, mean=True):
518 """
519 Parameters
520 ----------
521 X : Numpy array (n-by-p). The regressor matrix. Training vectors, where
522 n is the number of samples and p is the number of features.
523
524 y : Numpy array (n-by-1). The regressand vector. Target values (class
525 labels in classification).
526
527 k : Non-negative float. The ridge parameter.
528
529 weights: Numpy array (n-by-1). The sample's weights.
530
531 penalty_start : Non-negative integer. The number of columns, variables
532 etc., to except from penalisation. Equivalently, the first
533 index to be penalised. Default is 0, all columns are included.
534
535 mean : Boolean. Whether to compute the mean loss or not. Default is
536 True, the mean loss is computed.
537 """
538 self.X = X
539 self.y = y
540 self.k = max(0.0, float(k))
541 if weights is None:
542 weights = np.ones(y.shape)
543 self.weights = weights
544 self.penalty_start = max(0, int(penalty_start))
545 self.mean = bool(mean)
546
547 self.reset()
548
550 """Free any cached computations from previous use of this Function.
551
552 From the interface "Function".
553 """
554 self._L = None
555
557 """Function value of Logistic regression at beta.
558
559 Parameters
560 ----------
561 beta : Numpy array. Regression coefficient vector. The point at which
562 to evaluate the function.
563 """
564
565 Xbeta = np.dot(self.X, beta)
566 negloglike = -np.sum(self.weights *
567 ((self.y * Xbeta) - np.log(1 + np.exp(Xbeta))))
568
569 if self.mean:
570 negloglike *= 1.0 / float(self.X.shape[0])
571
572 if self.penalty_start > 0:
573 beta_ = beta[self.penalty_start:, :]
574 else:
575 beta_ = beta
576
577 return negloglike + (self.k / 2.0) * np.sum(beta_ ** 2.0)
578
579 - def grad(self, beta):
580 """Gradient of the function at beta.
581
582 From the interface "Gradient".
583
584 Parameters
585 ----------
586 beta : Numpy array. The point at which to evaluate the gradient.
587
588 Examples
589 --------
590 >>> import numpy as np
591 >>> from parsimony.functions.losses import RidgeLogisticRegression
592 >>>
593 >>> np.random.seed(42)
594 >>> X = np.random.rand(100, 150)
595 >>> y = np.random.rand(100, 1)
596 >>> y[y < 0.5] = 0.0
597 >>> y[y >= 0.5] = 1.0
598 >>> rr = RidgeLogisticRegression(X=X, y=y, k=2.71828182, mean=True)
599 >>> beta = np.random.rand(150, 1)
600 >>> round(np.linalg.norm(rr.grad(beta)
601 ... - rr.approx_grad(beta, eps=1e-4)), 11) < 1e-9
602 True
603 >>>
604 >>> np.random.seed(42)
605 >>> X = np.random.rand(100, 150)
606 >>> y = np.random.rand(100, 1)
607 >>> y[y < 0.5] = 0.0
608 >>> y[y >= 0.5] = 1.0
609 >>> rr = RidgeLogisticRegression(X=X, y=y, k=2.71828182, mean=False)
610 >>> beta = np.random.rand(150, 1)
611 >>> np.linalg.norm(rr.grad(beta)
612 ... - rr.approx_grad(beta, eps=1e-4)) < 5e-8
613 True
614 """
615 Xbeta = np.dot(self.X, beta)
616
617 pi = np.reciprocal(1.0 + np.exp(-Xbeta))
618
619 grad = -np.dot(self.X.T, self.weights * (self.y - pi))
620 if self.mean:
621 grad *= 1.0 / float(self.X.shape[0])
622
623 if self.penalty_start > 0:
624 gradL2 = np.vstack((np.zeros((self.penalty_start, 1)),
625 self.k * beta[self.penalty_start:, :]))
626 else:
627 gradL2 = self.k * beta
628
629 grad = grad + gradL2
630
631 return grad
632
633
634
635
636
638 """Lipschitz constant of the gradient.
639
640 Returns the maximum eigenvalue of (1 / 4) * X'WX.
641
642 From the interface "LipschitzContinuousGradient".
643 """
644 if self._L == None:
645
646 PWX = 0.5 * np.sqrt(self.weights) * self.X
647
648
649
650
651 s = np.linalg.svd(PWX, full_matrices=False, compute_uv=False)
652 self._L = np.max(s) ** 2.0
653
654 if self.mean:
655 self._L /= float(self.X.shape[0])
656
657 self._L += self.k
658
659 return self._L
660
661 - def step(self, beta, index=0):
662 """The step size to use in descent methods.
663
664 Parameters
665 ----------
666 beta : Numpy array. The point at which to determine the step size.
667 """
668 return 1.0 / self.L()
669
670
671 -class LatentVariableVariance(properties.Function,
672 properties.Gradient,
673 properties.StepSize,
674 properties.LipschitzContinuousGradient):
675
677
678 self.X = X
679 if unbiased:
680 self._n = float(X.shape[0] - 1.0)
681 else:
682 self._n = float(X.shape[0])
683
684 self.reset()
685
687
688 self._lambda_max = None
689
691 """Function value.
692
693 From the interface "Function".
694
695 Examples
696 --------
697 >>> import numpy as np
698 >>> from parsimony.algorithms.nipals import FastSVD
699 >>> from parsimony.functions.losses import LatentVariableVariance
700 >>>
701 >>> np.random.seed(1337)
702 >>> X = np.random.rand(50, 150)
703 >>> w = np.random.rand(150, 1)
704 >>> var = LatentVariableVariance(X)
705 >>> round(var.f(w), 12)
706 -1295.854475188615
707 >>> round(-np.dot(w.T, np.dot(X.T, np.dot(X, w)))[0, 0] / 49.0, 12)
708 -1295.854475188615
709 """
710 Xw = np.dot(self.X, w)
711 wXXw = np.dot(Xw.T, Xw)[0, 0]
712 return -wXXw / self._n
713
715 """Gradient of the function.
716
717 From the interface "Gradient".
718
719 Parameters
720 ----------
721 w : The point at which to evaluate the gradient.
722
723 Examples
724 --------
725 >>> import numpy as np
726 >>> from parsimony.functions.losses import LatentVariableVariance
727 >>>
728 >>> np.random.seed(42)
729 >>> X = np.random.rand(50, 150)
730 >>> var = LatentVariableVariance(X)
731 >>> w = np.random.rand(150, 1)
732 >>> np.linalg.norm(var.grad(w) - var.approx_grad(w, eps=1e-4)) < 5e-8
733 True
734 """
735 grad = -np.dot(self.X.T, np.dot(self.X, w)) * (2.0 / self._n)
736
737
738
739
740 return grad
741
743 """Lipschitz constant of the gradient with given index.
744
745 From the interface "LipschitzContinuousGradient".
746
747 Examples
748 --------
749 >>> import numpy as np
750 >>> from parsimony.algorithms.nipals import FastSVD
751 >>> from parsimony.functions.losses import LatentVariableVariance
752 >>>
753 >>> np.random.seed(1337)
754 >>> X = np.random.rand(50, 150)
755 >>> w = np.random.rand(150, 1)
756 >>> var = LatentVariableVariance(X)
757 >>> round(var.L(), 10)
758 47025.0809786841
759 >>> _, S, _ = np.linalg.svd(np.dot(X.T, X))
760 >>> round(np.max(S) * 49 / 2.0, 10)
761 47025.0809786841
762 """
763 if self._lambda_max is None:
764 from parsimony.algorithms.nipals import FastSVD
765 v = FastSVD().run(self.X, max_iter=1000)
766 us = np.dot(self.X, v)
767
768 self._lambda_max = np.linalg.norm(us) ** 2.0
769
770 return self._n * self._lambda_max / 2.0
771
772 - def step(self, w, index=0):
773 """The step size to use in descent methods.
774
775 Parameters
776 ----------
777 w : Numpy array. The point at which to determine the step size.
778
779 Examples
780 --------
781 >>> import numpy as np
782 >>> from parsimony.algorithms.nipals import FastSVD
783 >>> from parsimony.functions.losses import LatentVariableVariance
784 >>>
785 >>> np.random.seed(42)
786 >>> X = np.random.rand(50, 150)
787 >>> w = np.random.rand(150, 1)
788 >>> var = LatentVariableVariance(X)
789 >>> var.step(w)
790 2.1979627581251385e-05
791 >>> _, S, _ = np.linalg.svd(np.dot(X.T, X))
792 >>> round(1.0 / (np.max(S) * 49 / 2.0), 15)
793 2.1979627581e-05
794 """
795 return 1.0 / self.L()
796
797
798 -class LinearFunction(properties.CompositeFunction,
799 properties.Gradient,
800 properties.LipschitzContinuousGradient,
801 properties.StepSize):
802 """A linear function.
803 """
805 """
806 Parameters
807 ----------
808 a : Numpy array (p-by-1). The slope.
809 """
810 self.a = a
811
812 self.reset()
813
815 """Free any cached computations from previous use of this Function.
816
817 From the interface "Function".
818 """
819 pass
820
822 """Function value.
823
824 From the interface "Function".
825
826 Parameters
827 ----------
828 beta : Numpy array. Regression coefficient vector. The point at which
829 to evaluate the function.
830 """
831 f = np.dot(self.a.T, x)
832
833 return f
834
836 """Gradient of the function at beta.
837
838 From the interface "Gradient".
839
840 Parameters
841 ----------
842 x : The point at which to evaluate the gradient.
843 """
844 grad = self.a
845
846 return grad
847
849 """Lipschitz constant of the gradient.
850
851 From the interface "LipschitzContinuousGradient".
852
853 Examples
854 --------
855 >>> import numpy as np
856 >>> from parsimony.functions.losses import LinearFunction
857 >>>
858 >>> np.random.seed(42)
859 >>> a = np.random.rand(10, 15)
860 >>> f = LinearFunction(a)
861 >>> L = f.L()
862 >>> L_ = f.approx_L((15, 1), 10)
863 >>> L >= L_
864 True
865 >>> L - L_
866 0.0
867 """
868 return 0.0
869
870 - def step(self, beta=None, index=0):
871 """The step size to use in descent methods.
872 """
873 return 1.0
874
875
876 if __name__ == "__main__":
877 import doctest
878 doctest.testmod()
879