LCOV - code coverage report
Current view: top level - source/tests - TestSeparableSum.cpp (source / functions) Hit Total Coverage
Test: LibForBES Unit Tests Lines: 185 185 100.0 %
Date: 2016-04-18 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.10