44from numba import float64
55
66from skglm .datafits .base import BaseDatafit
7+ from skglm .utils import spectral_norm
78
89
910class Quadratic (BaseDatafit ):
@@ -22,6 +23,10 @@ class Quadratic(BaseDatafit):
2223 The coordinatewise gradient Lipschitz constants. Equal to
2324 norm(X, axis=0) ** 2 / n_samples.
2425
26+ global_lipschitz : float
27+ Global Lipschitz constant. Equal to
28+ norm(X, ord=2) ** 2 / n_samples.
29+
2530 Note
2631 ----
2732 The class is jit compiled at fit time using Numba compiler.
@@ -35,6 +40,7 @@ def get_spec(self):
3540 spec = (
3641 ('Xty' , float64 [:]),
3742 ('lipschitz' , float64 [:]),
43+ ('global_lipschitz' , float64 ),
3844 )
3945 return spec
4046
@@ -44,14 +50,18 @@ def params_to_dict(self):
4450 def initialize (self , X , y ):
4551 self .Xty = X .T @ y
4652 n_features = X .shape [1 ]
53+ self .global_lipschitz = norm (X , ord = 2 ) ** 2 / len (y )
4754 self .lipschitz = np .zeros (n_features , dtype = X .dtype )
4855 for j in range (n_features ):
4956 self .lipschitz [j ] = (X [:, j ] ** 2 ).sum () / len (y )
5057
51- def initialize_sparse (
52- self , X_data , X_indptr , X_indices , y ):
58+ def initialize_sparse (self , X_data , X_indptr , X_indices , y ):
5359 n_features = len (X_indptr ) - 1
5460 self .Xty = np .zeros (n_features , dtype = X_data .dtype )
61+
62+ self .global_lipschitz = spectral_norm (X_data , X_indptr , X_indices , len (y )) ** 2
63+ self .global_lipschitz /= len (y )
64+
5565 self .lipschitz = np .zeros (n_features , dtype = X_data .dtype )
5666 for j in range (n_features ):
5767 nrm2 = 0.
@@ -111,6 +121,10 @@ class Logistic(BaseDatafit):
111121 The coordinatewise gradient Lipschitz constants. Equal to
112122 norm(X, axis=0) ** 2 / (4 * n_samples).
113123
124+ global_lipschitz : float
125+ Global Lipschitz constant. Equal to
126+ norm(X, ord=2) ** 2 / (4 * n_samples).
127+
114128 Note
115129 ----
116130 The class is jit compiled at fit time using Numba compiler.
@@ -123,6 +137,7 @@ def __init__(self):
123137 def get_spec (self ):
124138 spec = (
125139 ('lipschitz' , float64 [:]),
140+ ('global_lipschitz' , float64 ),
126141 )
127142 return spec
128143
@@ -140,9 +155,14 @@ def raw_hessian(self, y, Xw):
140155
141156 def initialize (self , X , y ):
142157 self .lipschitz = (X ** 2 ).sum (axis = 0 ) / (len (y ) * 4 )
158+ self .global_lipschitz = norm (X , ord = 2 ) ** 2 / (len (y ) * 4 )
143159
144160 def initialize_sparse (self , X_data , X_indptr , X_indices , y ):
145161 n_features = len (X_indptr ) - 1
162+
163+ self .global_lipschitz = spectral_norm (X_data , X_indptr , X_indices , len (y )) ** 2
164+ self .global_lipschitz /= 4 * len (y )
165+
146166 self .lipschitz = np .zeros (n_features , dtype = X_data .dtype )
147167 for j in range (n_features ):
148168 Xj = X_data [X_indptr [j ]:X_indptr [j + 1 ]]
@@ -187,6 +207,11 @@ class QuadraticSVC(BaseDatafit):
187207 ----------
188208 lipschitz : array, shape (n_features,)
189209 The coordinatewise gradient Lipschitz constants.
210+ Equal to norm(yXT, axis=0) ** 2.
211+
212+ global_lipschitz : float
213+ Global Lipschitz constant. Equal to
214+ norm(yXT, ord=2) ** 2.
190215
191216 Note
192217 ----
@@ -200,6 +225,7 @@ def __init__(self):
200225 def get_spec (self ):
201226 spec = (
202227 ('lipschitz' , float64 [:]),
228+ ('global_lipschitz' , float64 ),
203229 )
204230 return spec
205231
@@ -209,12 +235,16 @@ def params_to_dict(self):
209235 def initialize (self , yXT , y ):
210236 n_features = yXT .shape [1 ]
211237 self .lipschitz = np .zeros (n_features , dtype = yXT .dtype )
238+ self .global_lipschitz = norm (yXT , ord = 2 ) ** 2
212239 for j in range (n_features ):
213240 self .lipschitz [j ] = norm (yXT [:, j ]) ** 2
214241
215- def initialize_sparse (
216- self , yXT_data , yXT_indptr , yXT_indices , y ):
242+ def initialize_sparse (self , yXT_data , yXT_indptr , yXT_indices , y ):
217243 n_features = len (yXT_indptr ) - 1
244+
245+ self .global_lipschitz = spectral_norm (
246+ yXT_data , yXT_indptr , yXT_indices , max (yXT_indices )+ 1 ) ** 2
247+
218248 self .lipschitz = np .zeros (n_features , dtype = yXT_data .dtype )
219249 for j in range (n_features ):
220250 nrm2 = 0.
@@ -264,8 +294,16 @@ class Huber(BaseDatafit):
264294
265295 Attributes
266296 ----------
297+ delta : float
298+ Threshold hyperparameter.
299+
267300 lipschitz : array, shape (n_features,)
268- The coordinatewise gradient Lipschitz constants.
301+ The coordinatewise gradient Lipschitz constants. Equal to
302+ norm(X, axis=0) ** 2 / n_samples.
303+
304+ global_lipschitz : float
305+ Global Lipschitz constant. Equal to
306+ norm(X, ord=2) ** 2 / n_samples.
269307
270308 Note
271309 ----
@@ -279,7 +317,8 @@ def __init__(self, delta):
279317 def get_spec (self ):
280318 spec = (
281319 ('delta' , float64 ),
282- ('lipschitz' , float64 [:])
320+ ('lipschitz' , float64 [:]),
321+ ('global_lipschitz' , float64 ),
283322 )
284323 return spec
285324
@@ -289,12 +328,17 @@ def params_to_dict(self):
289328 def initialize (self , X , y ):
290329 n_features = X .shape [1 ]
291330 self .lipschitz = np .zeros (n_features , dtype = X .dtype )
331+ self .global_lipschitz = 0.
292332 for j in range (n_features ):
293333 self .lipschitz [j ] = (X [:, j ] ** 2 ).sum () / len (y )
334+ self .global_lipschitz += (X [:, j ] ** 2 ).sum () / len (y )
294335
295- def initialize_sparse (
296- self , X_data , X_indptr , X_indices , y ):
336+ def initialize_sparse (self , X_data , X_indptr , X_indices , y ):
297337 n_features = len (X_indptr ) - 1
338+
339+ self .global_lipschitz = spectral_norm (X_data , X_indptr , X_indices , len (y )) ** 2
340+ self .global_lipschitz /= len (y )
341+
298342 self .lipschitz = np .zeros (n_features , dtype = X_data .dtype )
299343 for j in range (n_features ):
300344 nrm2 = 0.
0 commit comments