Line data Source code
1 : /*
2 : * File: TestSeparableSum.cpp
3 : * Author: Pantelis Sopasakis
4 : *
5 : * Created on Nov 4, 2015, 12:10:44 AM
6 : *
7 : * ForBES is free software: you can redistribute it and/or modify
8 : * it under the terms of the GNU Lesser General Public License as published by
9 : * the Free Software Foundation, either version 3 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * ForBES is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU Lesser General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU Lesser General Public License
18 : * along with ForBES. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "TestSeparableSum.h"
22 : #include "ElasticNet.h"
23 : #include "HingeLoss.h"
24 : #include "MatrixFactory.h"
25 : #include "SeparableSum.h"
26 : #include "ForBES.h"
27 : #include "DistanceToBall2.h"
28 :
29 :
30 :
31 1 : CPPUNIT_TEST_SUITE_REGISTRATION(TestSeparableSum);
32 :
33 4 : TestSeparableSum::TestSeparableSum() {
34 4 : }
35 :
36 8 : TestSeparableSum::~TestSeparableSum() {
37 8 : }
38 :
39 4 : void TestSeparableSum::setUp() {
40 4 : }
41 :
42 4 : void TestSeparableSum::tearDown() {
43 4 : }
44 :
45 1 : void TestSeparableSum::testCall() {
46 1 : size_t num_idx_1 = 3; /* Function #1 points to 3 indices */
47 1 : size_t num_idx_2 = 2; /* Function #2 points to 2 indices */
48 :
49 1 : std::vector<size_t> idx1(num_idx_1);
50 2 : std::vector<size_t> idx2(num_idx_2);
51 :
52 1 : idx1[0] = 0;
53 1 : idx1[1] = 3;
54 1 : idx1[2] = 4; /* f1(x_0, x_3, x_4) */
55 :
56 1 : idx2[0] = 1;
57 1 : idx2[1] = 2; /* f2(x_1, x_2) */
58 :
59 1 : Function * f1 = new ElasticNet(2.5, 1.3);
60 :
61 2 : Matrix b(2, 1);
62 1 : b[0] = 0.8;
63 1 : b[1] = 1.5;
64 1 : Function * f2 = new HingeLoss(b, 1.4);
65 :
66 2 : std::map<Function*, std::vector<size_t>* > fun_idx_map;
67 1 : fun_idx_map[f1] = &idx1;
68 1 : fun_idx_map[f2] = &idx2;
69 :
70 1 : Function * sep_sum = new SeparableSum(fun_idx_map);
71 :
72 2 : Matrix x(5, 1);
73 6 : for (size_t i = 0; i < 5; i++) {
74 5 : x[i] = 0.1 * (i + 1);
75 : }
76 :
77 :
78 : double f_val;
79 1 : _ASSERT(sep_sum->category().defines_f());
80 1 : int status = sep_sum->call(x, f_val);
81 1 : _ASSERT_EQ(ForBESUtils::STATUS_OK, status);
82 :
83 2 : Matrix x1(num_idx_1, 1);
84 1 : x1[0] = x[0];
85 1 : x1[1] = x[3];
86 1 : x1[2] = x[4];
87 :
88 2 : Matrix x2(num_idx_2, 1);
89 1 : x2[0] = x[1];
90 1 : x2[1] = x[2];
91 :
92 : double f1_val;
93 1 : status = f1->call(x1, f1_val);
94 1 : _ASSERT_EQ(ForBESUtils::STATUS_OK, status);
95 :
96 : double f2_val;
97 1 : status = f2->call(x2, f2_val);
98 1 : _ASSERT_EQ(ForBESUtils::STATUS_OK, status);
99 :
100 1 : _ASSERT_NUM_EQ(f1_val + f2_val, f_val, 1e-8);
101 :
102 1 : delete f1;
103 1 : delete f2;
104 2 : delete sep_sum;
105 :
106 1 : }
107 :
108 1 : void TestSeparableSum::testCallConj() {
109 1 : size_t num_idx_1 = 3;
110 1 : size_t num_idx_2 = 2;
111 1 : std::vector<size_t> idx1(num_idx_1);
112 2 : std::vector<size_t> idx2(num_idx_2);
113 :
114 1 : idx1[0] = 0;
115 1 : idx1[1] = 2;
116 1 : idx1[2] = 3; /* f1(x_0, x_2, x_3) */
117 1 : idx2[0] = 1;
118 1 : idx2[1] = 4; /* f2(x_1, x_4) */
119 :
120 1 : Function *f1 = new Norm1(5.34);
121 1 : Function *f2 = new Norm2(2.22);
122 :
123 2 : std::map<Function*, std::vector<size_t>* > fun_idx_map;
124 1 : fun_idx_map[f1] = &idx1;
125 1 : fun_idx_map[f2] = &idx2;
126 :
127 1 : Function * sepsum = new SeparableSum(fun_idx_map);
128 :
129 1 : double fstar = -100.00;
130 :
131 :
132 21 : for (size_t rep = 0; rep < 20; rep++) {
133 20 : Matrix x = MatrixFactory::MakeRandomMatrix(num_idx_1 + num_idx_2, 1, -0.5, 2.0);
134 :
135 40 : Matrix x1(num_idx_1, 1);
136 40 : Matrix x2(num_idx_2, 1);
137 :
138 80 : for (size_t i = 0; i < num_idx_1; i++) {
139 60 : size_t ij = idx1[i];
140 60 : x1[i] = x[ij];
141 : }
142 60 : for (size_t i = 0; i < num_idx_2; i++) {
143 40 : size_t ij = idx2[i];
144 40 : x2[i] = x[ij];
145 : }
146 :
147 : double f1star;
148 : double f2star;
149 :
150 : int status;
151 20 : status = f1->callConj(x1, f1star);
152 20 : _ASSERT(ForBESUtils::is_status_ok(status));
153 :
154 20 : f2->callConj(x2, f2star);
155 20 : _ASSERT(ForBESUtils::is_status_ok(status));
156 :
157 20 : double exp_fstar = f1star + f2star;
158 :
159 :
160 20 : sepsum->callConj(x, fstar);
161 :
162 20 : _ASSERT_NUM_EQ(exp_fstar, fstar, 1e-7);
163 20 : }
164 1 : delete f1;
165 1 : delete f2;
166 2 : delete sepsum;
167 :
168 1 : }
169 :
170 1 : void TestSeparableSum::testCallProx() {
171 1 : size_t num_idx_1 = 3; /* Function #1 points to 3 indices */
172 1 : size_t num_idx_2 = 2; /* Function #2 points to 2 indices */
173 :
174 1 : std::vector<size_t> idx1(num_idx_1);
175 2 : std::vector<size_t> idx2(num_idx_2);
176 :
177 1 : idx1[0] = 0;
178 1 : idx1[1] = 3;
179 1 : idx1[2] = 4; /* f1(x_0, x_3, x_4) */
180 :
181 1 : idx2[0] = 1;
182 1 : idx2[1] = 2; /* f2(x_1, x_2) */
183 :
184 1 : Function * f1 = new ElasticNet(2.5, 1.3);
185 :
186 2 : Matrix b(2, 1);
187 1 : b[0] = 0.8;
188 1 : b[1] = 1.5;
189 1 : Function * f2 = new HingeLoss(b, 1.4);
190 :
191 2 : std::map<Function*, std::vector<size_t>* > fun_idx_map;
192 1 : fun_idx_map[f1] = &idx1;
193 1 : fun_idx_map[f2] = &idx2;
194 :
195 1 : Function * sep_sum = new SeparableSum(fun_idx_map);
196 :
197 2 : Matrix x(5, 1);
198 6 : for (size_t i = 0; i < 5; i++) {
199 5 : x[i] = 10.9 * (i + 1);
200 : }
201 :
202 :
203 1 : double gamma = 0.87;
204 2 : Matrix prox(num_idx_1 + num_idx_2, 1);
205 2 : Matrix prox__(num_idx_1 + num_idx_2, 1);
206 1 : _ASSERT(sep_sum->category().defines_prox());
207 1 : int status = sep_sum->callProx(x, gamma, prox);
208 1 : _ASSERT(ForBESUtils::is_status_ok(status));
209 :
210 : double f_at_prox;
211 1 : status = sep_sum->callProx(x, gamma, prox__, f_at_prox);
212 1 : _ASSERT(ForBESUtils::is_status_ok(status));
213 :
214 1 : _ASSERT_EQ(prox, prox__);
215 :
216 2 : Matrix x1(num_idx_1, 1);
217 1 : x1[0] = x[0];
218 1 : x1[1] = x[3];
219 1 : x1[2] = x[4];
220 :
221 2 : Matrix x2(num_idx_2, 1);
222 1 : x2[0] = x[1];
223 1 : x2[1] = x[2];
224 :
225 2 : Matrix prox1(num_idx_1, 1);
226 2 : Matrix prox2(num_idx_2, 1);
227 :
228 1 : status = f1->callProx(x1, gamma, prox1);
229 1 : _ASSERT_EQ(ForBESUtils::STATUS_OK, status);
230 :
231 1 : status = f2->callProx(x2, gamma, prox2);
232 1 : _ASSERT_EQ(ForBESUtils::STATUS_OK, status);
233 :
234 1 : double tol = 1e-8;
235 1 : _ASSERT_NUM_EQ(prox1[0], prox[0], tol);
236 1 : _ASSERT_NUM_EQ(prox2[0], prox[1], tol);
237 1 : _ASSERT_NUM_EQ(prox2[1], prox[2], tol);
238 1 : _ASSERT_NUM_EQ(prox1[1], prox[3], tol);
239 1 : _ASSERT_NUM_EQ(prox1[2], prox[4], tol);
240 :
241 : double f1_at_prox;
242 : double f2_at_prox;
243 :
244 1 : f1->callProx(x1, gamma, prox1, f1_at_prox);
245 1 : f2->callProx(x2, gamma, prox1, f2_at_prox);
246 :
247 1 : _ASSERT_NUM_EQ(f1_at_prox + f2_at_prox, f_at_prox, 1e-7);
248 :
249 1 : delete f1;
250 1 : delete f2;
251 2 : delete sep_sum;
252 1 : }
253 :
254 1 : void TestSeparableSum::testCallGrad() {
255 : // f = quadratic(x_0, x_2) + huber(x_3) + dist2(x_1, x_4)
256 :
257 1 : size_t num_idx_1 = 2; /* Function #1 points to 1 indices */
258 1 : size_t num_idx_2 = 1; /* Function #2 points to 1 indices */
259 1 : size_t num_idx_3 = 2; /* Function #3 points to 2 indices */
260 1 : size_t n = num_idx_1 + num_idx_2 + num_idx_3;
261 :
262 1 : std::vector<size_t> idx1(num_idx_1);
263 2 : std::vector<size_t> idx2(num_idx_2);
264 2 : std::vector<size_t> idx3(num_idx_3);
265 :
266 1 : idx1[0] = 0;
267 1 : idx1[1] = 2; /* f1(x_0, x_2) */
268 :
269 1 : idx2[0] = 3; /* f2(x_3) */
270 :
271 1 : idx3[0] = 1;
272 1 : idx3[1] = 4; /* f3(x_1, x_4) */
273 :
274 :
275 1 : const double delta = 5.40;
276 :
277 1 : Function * f1 = new Quadratic();
278 1 : Function * f2 = new HuberLoss(delta);
279 1 : Function * f3 = new DistanceToBall2(1.221);
280 :
281 :
282 2 : std::map<Function*, std::vector<size_t>* > fun_idx_map;
283 1 : fun_idx_map[f1] = &idx1;
284 1 : fun_idx_map[f2] = &idx2;
285 1 : fun_idx_map[f3] = &idx3;
286 :
287 1 : Function * sepsum = new SeparableSum(fun_idx_map);
288 :
289 351 : for (size_t rep = 0; rep < 350; rep++) {
290 : double fval;
291 350 : Matrix gradf(n, 1);
292 700 : Matrix x = MatrixFactory::MakeRandomMatrix(n, 1, 0.0, 10.55);
293 350 : int status = sepsum->call(x, fval, gradf);
294 350 : _ASSERT(ForBESUtils::is_status_ok(status));
295 :
296 350 : if (std::fabs(x[idx2[0]]) > delta) {
297 173 : _ASSERT_NUM_EQ(1.0, std::fabs(gradf[idx2[0]]), 1e-9);
298 : } else {
299 177 : double xi = x[idx2[0]];
300 177 : _ASSERT_NUM_EQ(xi / delta, gradf[idx2[0]], 1e-9);
301 : }
302 :
303 350 : _ASSERT_NUM_EQ(x[0], gradf[0], 1e-9);
304 350 : _ASSERT_NUM_EQ(x[2], gradf[2], 1e-9);
305 350 : }
306 :
307 1 : delete sepsum;
308 1 : delete f1;
309 1 : delete f2;
310 2 : delete f3;
311 :
312 :
313 4 : }
|