Files
ensmallen/tests/frankwolfe_test.cpp
T
conradsnicta adf2fc88dd avoid deprecation warnings in armadillo 11.2+ (#347)
avoid deprecation warnings in armadillo 11.2+
2022-06-30 04:12:36 +00:00

226 lines
6.4 KiB
C++

/**
* @file frankwolfe_test.cpp
* @author Chenzhe Diao
* @author Marcus Edel
*
* Test file for Frank-Wolfe type optimizer.
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <ensmallen.hpp>
#include "catch.hpp"
#include "test_function_tools.hpp"
using namespace arma;
using namespace ens;
using namespace ens::test;
/**
* Simple test of Orthogonal Matching Pursuit algorithm.
*/
TEST_CASE("FWOMPTest", "[FrankWolfeTest]")
{
const int k = 5;
mat B1 = eye(3, 3);
mat B2 = 0.1 * randn(3, k);
mat A = join_horiz(B1, B2); // The dictionary is input as columns of A.
vec b = {1.0, 1.0, 0.0}; // Vector to be sparsely approximated.
FuncSq f(A, b);
ConstrLpBallSolver linearConstrSolver(1);
UpdateSpan updateRule;
OMP s(linearConstrSolver, updateRule);
mat coordinates = zeros<mat>(k + 3, 1);
double result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-10));
REQUIRE(coordinates(0) - 1 == Approx(0.0).margin(1e-10));
REQUIRE(coordinates(1) - 1 == Approx(0.0).margin(1e-10));
REQUIRE(coordinates(2) == Approx(0.0).margin(1e-10));
for (int ii = 0; ii < k; ++ii)
{
REQUIRE(coordinates[ii + 3] == Approx(0.0).margin(1e-10));
}
}
/**
* Simple test of Orthogonal Matching Pursuit with regularization.
*/
TEST_CASE("FWRegularizedOMP", "[FrankWolfeTest]")
{
const int k = 10;
mat B1 = 0.1 * eye(k, k);
mat B2 = 100 * randn(k, k);
mat A = join_horiz(B1, B2); // The dictionary is input as columns of A.
vec b(k, arma::fill::zeros); // Vector to be sparsely approximated.
b(0) = 1;
b(1) = 1;
vec lambda(A.n_cols);
for (size_t ii = 0; ii < A.n_cols; ii++)
lambda(ii) = norm(A.col(ii), 2);
FuncSq f(A, b);
ConstrLpBallSolver linearConstrSolver(1, lambda);
UpdateSpan updateRule;
OMP s(linearConstrSolver, updateRule);
mat coordinates = zeros<mat>(2 * k, 1);
double result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-10));
}
/**
* Simple test of Orthogonal Matching Pursuit with support prune.
*/
TEST_CASE("FWPruneSupportOMP", "[FrankWolfeTest]")
{
// The dictionary is input as columns of A.
const int k = 3;
mat B1 = { { 1.0, 0.0, 1.0 },
{ 0.0, 1.0, 1.0 },
{ 0.0, 0.0, 1.0 } };
mat B2 = randu(k, k);
mat A = join_horiz(B1, B2); // The dictionary is input as columns of A.
vec b = { 1.0, 1.0, 0.0 }; // Vector to be sparsely approximated.
FuncSq f(A, b);
ConstrLpBallSolver linearConstrSolver(1);
UpdateSpan updateRule(true);
OMP s(linearConstrSolver, updateRule);
mat coordinates = zeros<mat>(k + 3, 1);
double result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-10));
}
/**
* Simple test of sparse soluton in atom domain with atom norm constraint.
*/
TEST_CASE("FWAtomNormConstraint", "[FrankWolfeTest]")
{
const int k = 5;
mat B1 = eye(3, 3);
mat B2 = 0.1 * randn(3, k);
mat A = join_horiz(B1, B2); // The dictionary is input as columns of A.
vec b = { 1.0, 1.0, 0.0 }; // Vector to be sparsely approximated.
FuncSq f(A, b);
ConstrLpBallSolver linearConstrSolver(1);
UpdateFullCorrection updateRule(2, 0.2);
FrankWolfe<ConstrLpBallSolver, UpdateFullCorrection>
s(linearConstrSolver, updateRule);
mat coordinates = zeros<mat>(k + 3, 1);
double result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-10));
}
/**
* A very simple test of classic Frank-Wolfe algorithm.
* The constrained domain used is unit lp ball.
*/
TEST_CASE("ClassicFW", "[FrankWolfeTest]")
{
TestFuncFW<> f;
double p = 2; // Constraint set is unit lp ball.
ConstrLpBallSolver linearConstrSolver(p);
UpdateClassic updateRule;
FrankWolfe<ConstrLpBallSolver, UpdateClassic>
s(linearConstrSolver, updateRule);
mat coordinates = randu<mat>(3, 1);
double result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(0) - 0.1 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(1) - 0.2 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(2) - 0.3 == Approx(0.0).margin(1e-4));
}
/**
* A very simple test of classic Frank-Wolfe algorithm.
* The constrained domain used is unit lp ball.
* Use arma::fmat.
*/
TEST_CASE("ClassicFWFMat", "[FrankWolfeTest]")
{
TestFuncFW<arma::fmat> f;
double p = 2; // Constraint set is unit lp ball.
ConstrLpBallSolver linearConstrSolver(p);
UpdateClassic updateRule;
FrankWolfe<ConstrLpBallSolver, UpdateClassic>
s(linearConstrSolver, updateRule);
fmat coordinates = randu<fmat>(3, 1);
float result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(0) - 0.1 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(1) - 0.2 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(2) - 0.3 == Approx(0.0).margin(1e-4));
}
/**
* Exactly the same problem with ClassicFW.
* The update step performs a line search now.
* It converges much faster.
*/
TEST_CASE("FWLineSearch", "[FrankWolfeTest]")
{
TestFuncFW<> f;
double p = 2; // Constraint set is unit lp ball.
ConstrLpBallSolver linearConstrSolver(p);
UpdateLineSearch updateRule;
FrankWolfe<ConstrLpBallSolver, UpdateLineSearch>
s(linearConstrSolver, updateRule);
mat coordinates = randu<mat>(3);
double result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(0) - 0.1 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(1) - 0.2 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(2) - 0.3 == Approx(0.0).margin(1e-4));
}
/**
* Exactly the same problem with ClassicFW.
* The update step performs a line search now.
* It converges much faster.
* Use arma::fmat.
*/
TEST_CASE("FWLineSearchFMat", "[FrankWolfeTest]")
{
TestFuncFW<arma::fmat> f;
double p = 2; // Constraint set is unit lp ball.
ConstrLpBallSolver linearConstrSolver(p);
UpdateLineSearch updateRule;
FrankWolfe<ConstrLpBallSolver, UpdateLineSearch>
s(linearConstrSolver, updateRule);
fmat coordinates = randu<fmat>(3);
float result = s.Optimize(f, coordinates);
REQUIRE(result == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(0) - 0.1 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(1) - 0.2 == Approx(0.0).margin(1e-4));
REQUIRE(coordinates(2) - 0.3 == Approx(0.0).margin(1e-4));
}