Skip to content

Commit b3fcd33

Browse files
author
Stephane Gaiffas
committed
TICK-367
1 parent 6af54a2 commit b3fcd33

File tree

9 files changed

+288
-86
lines changed

9 files changed

+288
-86
lines changed

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ def add_dir_name(dir_name, filenames):
535535
"prox_multi.cpp",
536536
"prox_equality.cpp",
537537
"prox_slope.cpp",
538+
"prox_oscar.cpp",
538539
"prox_binarsity.cpp"],
539540
"h_files": ["prox.h",
540541
"prox_separable.h",
@@ -549,6 +550,7 @@ def add_dir_name(dir_name, filenames):
549550
"prox_multi.h",
550551
"prox_equality.h",
551552
"prox_slope.h",
553+
"prox_oscar.h",
552554
"prox_binarsity.h"],
553555
"swig_files": ["prox_module.i"],
554556
"module_dir": "./tick/optim/prox/",

tick/optim/prox/prox_oscar.py

Lines changed: 21 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,36 @@
11
# License: BSD 3 clause
22

3-
# -*- coding: utf8 -*-
4-
53
from tick.optim.prox.base import Prox
64
import numpy as np
75

8-
from tick.optim.prox.build.prox import ProxSortedL1 as _ProxSortedL1
9-
from tick.optim.prox.build.prox import WeightsType_bh, \
10-
WeightsType_oscar
11-
12-
13-
# TODO: put also the OSCAR weights
14-
# TODO: we should be able to put any weights we want...
6+
from tick.optim.prox.build.prox import ProxOscar as _ProxOscar
157

168

17-
class ProxSortedL1(Prox):
18-
"""Proximal operator of sorted L1 penalization
9+
class ProxOscar(Prox):
10+
"""Proximal operator the oscar penalization.
11+
This penalization is particularly relevant for feature selection, in
12+
generalized linear models, when features correlation is not too high.
1913
2014
Parameters
2115
----------
2216
strength : `float`
2317
Level of penalization
2418
25-
fdr : `float`, default=0.6
26-
Desired False Discovery Rate for detection of non-zeros in
27-
the coefficients
28-
29-
weights_type : "bh" | "oscar", default="bh"
30-
31-
* If "bh", we use Benjamini-Hochberg weights, under a Gaussian
32-
error assumption, and expect a FDR control
33-
* If "oscar", there is no FDR control, and we get the OSCAR
34-
penalization, see notes below for references
19+
# TODO: clean this docstring
20+
ratio : `float`, default=0
21+
The Oscar mixing parameter, with 0 <= ratio <= 1.
22+
For ratio = 0 this is ridge (L2) regularization
23+
For ratio = 1 this is lasso (L1) regularization
24+
For 0 < ratio < 1, the regularization is a linear combination
25+
of L1 and L2.
3526
3627
range : `tuple` of two `int`, default=`None`
3728
Range on which the prox is applied. If `None` then the prox is
3829
applied on the whole vector
3930
40-
Attributes
41-
----------
42-
weights : `np.array`, shape=(n_coeffs,)
43-
The weights used in the penalization. They are automatically
44-
setted, depending on the ``weights_type`` and ``fdr``
45-
parameters.
31+
positive : `bool`, default=`False`
32+
If True, apply an extra projection onto the set of vectors with
33+
non-negative entries
4634
4735
Notes
4836
-----
@@ -58,59 +46,27 @@ class ProxSortedL1(Prox):
5846
"writable": True,
5947
"cpp_setter": "set_strength"
6048
},
61-
"fdr": {
49+
"ratio": {
6250
"writable": True,
63-
"cpp_setter": "set_fdr"
64-
},
65-
"_weights_type": {
66-
"writable": False,
67-
"cpp_setter": "set_weights_type"
51+
"cpp_setter": "set_ratio"
6852
},
6953
"positive": {
7054
"writable": True,
7155
"cpp_setter": "set_positive"
72-
},
73-
"weights": {
74-
"writable": False,
7556
}
7657
}
7758

78-
def __init__(self, strength: float, fdr: float=0.6,
79-
weights_type: str="bh", range: tuple=None,
59+
def __init__(self, strength: float, range: tuple=None,
8060
positive: bool=False):
8161
Prox.__init__(self, range)
8262
self.strength = strength
83-
self.fdr = fdr
84-
self.weights_type = weights_type
8563
self.positive = positive
8664
self.weights = None
8765
if range is None:
88-
self._prox = _ProxSortedL1(self.strength, self.fdr,
89-
self._weights_type,
90-
self.positive)
66+
self._prox = _ProxOscar(self.strength, self.positive)
9167
else:
92-
self._prox = _ProxSortedL1(self.strength, self.fdr,
93-
self._weights_type,
94-
self.range[0], self.range[1],
95-
self.positive)
96-
97-
@property
98-
def weights_type(self):
99-
if self._weights_type == WeightsType_bh:
100-
return "bh"
101-
elif self._weights_type == WeightsType_oscar:
102-
return "oscar"
103-
104-
@weights_type.setter
105-
def weights_type(self, val):
106-
if val == "bh":
107-
self._set("_weights_type", WeightsType_bh)
108-
elif val == "oscar":
109-
self._set("_weights_type", WeightsType_oscar)
110-
raise NotImplementedError("``oscar`` weights.")
111-
else:
112-
raise ValueError("``weights_type`` must be either 'bh' "
113-
"or 'oscar'")
68+
self._prox = _ProxOscar(self.strength, self.range[0], self.range[1],
69+
self.positive)
11470

11571
def _call(self, coeffs: np.ndarray, t: float, out: np.ndarray):
11672
self._prox.call(coeffs, t, out)
@@ -130,8 +86,3 @@ def value(self, coeffs: np.ndarray) -> float:
13086
Value of the penalization at ``coeffs``
13187
"""
13288
return self._prox.value(coeffs)
133-
134-
def _as_dict(self):
135-
dd = Prox._as_dict(self)
136-
del dd["weights"]
137-
return dd

tick/optim/prox/prox_slope.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,9 @@ class ProxSlope(Prox):
2525
Range on which the prox is applied. If `None` then the prox is
2626
applied on the whole vector
2727
28-
Attributes
29-
----------
30-
weights : `np.array`, shape=(n_coeffs,)
31-
The weights used in the penalization. They are automatically
32-
setted, depending on the ``weights_type`` and ``fdr``
33-
parameters.
28+
positive : `bool`, default=`False`
29+
If True, apply an extra projection onto the set of vectors with
30+
non-negative entries
3431
3532
Notes
3633
-----
@@ -53,9 +50,6 @@ class ProxSlope(Prox):
5350
"positive": {
5451
"writable": True,
5552
"cpp_setter": "set_positive"
56-
},
57-
"weights": {
58-
"writable": False,
5953
}
6054
}
6155

@@ -65,7 +59,6 @@ def __init__(self, strength: float, fdr: float=0.6, range: tuple=None,
6559
self.strength = strength
6660
self.fdr = fdr
6761
self.positive = positive
68-
self.weights = None
6962
if range is None:
7063
self._prox = _ProxSlope(self.strength, self.fdr, self.positive)
7164
else:
@@ -91,8 +84,3 @@ def value(self, coeffs: np.ndarray) -> float:
9184
Value of the penalization at ``coeffs``
9285
"""
9386
return self._prox.value(coeffs)
94-
95-
def _as_dict(self):
96-
dd = Prox._as_dict(self)
97-
del dd["weights"]
98-
return dd

tick/optim/prox/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_library(tick_prox EXCLUDE_FROM_ALL
88
prox_zero.cpp prox_zero.h
99
prox_sorted_l1.cpp prox_sorted_l1.h
1010
prox_slope.cpp prox_slope.h
11+
prox_oscar.cpp prox_oscar.h
1112
prox_tv.cpp prox_tv.h
1213
prox_l1w.cpp prox_l1w.h
1314
prox_elasticnet.cpp prox_elasticnet.h

tick/optim/prox/src/prox_oscar.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// License: BSD 3 clause
2+
3+
#include "prox_oscar.h"
4+
5+
ProxOscar::ProxOscar(double strength,
6+
double ratio,
7+
bool positive)
8+
: ProxSortedL1(strength, WeightsType::bh, positive) {
9+
this->ratio = ratio;
10+
}
11+
12+
ProxOscar::ProxOscar(double strength,
13+
double ratio,
14+
ulong start,
15+
ulong end,
16+
bool positive)
17+
: ProxSortedL1(strength, WeightsType::bh, start, end, positive) {
18+
this->ratio = ratio;
19+
}
20+
21+
const std::string ProxOscar::get_class_name() const {
22+
return "ProxOscar";
23+
}
24+
25+
void ProxOscar::compute_weights(void) {
26+
if (!weights_ready) {
27+
ulong size = end - start;
28+
weights = ArrayDouble(size);
29+
for (ulong i = 0; i < size; i++) {
30+
// double tmp = false_discovery_rate / (2 * size);
31+
// weights[i] = strength * standard_normal_inv_cdf(1 - tmp * (i + 1));
32+
weights[i] = 0;
33+
}
34+
weights_ready = true;
35+
}
36+
}
37+
38+
double ProxOscar::get_ratio() const {
39+
return ratio;
40+
}
41+
42+
void ProxOscar::set_ratio(double ratio) {
43+
this->ratio = ratio;
44+
}

tick/optim/prox/src/prox_oscar.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// License: BSD 3 clause
2+
3+
#ifndef TICK_OPTIM_PROX_SRC_PROX_OSCAR_H_
4+
#define TICK_OPTIM_PROX_SRC_PROX_OSCAR_H_
5+
6+
#include "prox_sorted_l1.h"
7+
8+
class ProxOscar : public ProxSortedL1 {
9+
protected:
10+
void compute_weights(void) override;
11+
double ratio;
12+
13+
public:
14+
ProxOscar(double strength, double false_discovery_rate, bool positive);
15+
16+
ProxOscar(double strength,
17+
double false_discovery_rate,
18+
ulong start,
19+
ulong end,
20+
bool positive);
21+
22+
const std::string get_class_name() const override;
23+
24+
virtual double get_ratio() const;
25+
26+
virtual void set_ratio(double ratio);
27+
};
28+
29+
#endif // TICK_OPTIM_PROX_SRC_PROX_OSCAR_H_

tick/optim/prox/swig/prox_module.i

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
%shared_ptr(ProxTV);
1616
%shared_ptr(ProxElasticNet);
1717
%shared_ptr(ProxSlope);
18+
%shared_ptr(ProxOscar);
1819
%shared_ptr(ProxMulti);
1920
%shared_ptr(ProxEquality);
2021
%shared_ptr(ProxBinarsity);
@@ -45,8 +46,10 @@
4546

4647
%include prox_slope.i
4748

49+
%include prox_oscar.i
50+
4851
%include prox_multi.i
4952

5053
%include prox_equality.i
5154

52-
%include prox_binarsity.i
55+
%include prox_binarsity.i

tick/optim/prox/swig/prox_oscar.i

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// License: BSD 3 clause
2+
3+
%{
4+
#include "prox_oscar.h"
5+
%}
6+
7+
class ProxOscar : public Prox {
8+
public:
9+
ProxOscar(double strength, double ratio, bool positive);
10+
11+
ProxOscar(double strength, double ratio, unsigned long start, unsigned long end, bool positive);
12+
13+
virtual double get_ratio() const final;
14+
15+
virtual void set_ratio(double ratio) final;
16+
};

0 commit comments

Comments
 (0)