NNLS.cpp File Reference
#include "main.h"
#include <unsupported/Eigen/NNLS>

Macros

#define EIGEN_RUNTIME_NO_MALLOC
 

Functions

template<typename MatrixType , typename VectorB , typename VectorX , typename Scalar >
void verify_nnls_optimality (const MatrixType &A, const VectorB &b, const VectorX &x, const Scalar tolerance)
 
template<typename MatrixType , typename VectorB , typename VectorX >
void test_nnls_known_solution (const MatrixType &A, const VectorB &b, const VectorX &x_expected)
 
template<typename MatrixType >
void test_nnls_random_problem (const MatrixType &)
 
void test_nnls_handles_zero_rhs ()
 
void test_nnls_handles_Mx0_matrix ()
 
void test_nnls_handles_0x0_matrix ()
 
void test_nnls_handles_dependent_columns ()
 
void test_nnls_handles_wide_matrix ()
 
void test_nnls_known_1 ()
 
void test_nnls_known_2 ()
 
void test_nnls_known_3 ()
 
void test_nnls_known_4 ()
 
void test_nnls_known_5 ()
 
void test_nnls_small_reference_problems ()
 
void test_nnls_with_half_precision ()
 
void test_nnls_special_case_solves_in_zero_iterations ()
 
void test_nnls_special_case_solves_in_n_iterations ()
 
void test_nnls_returns_NoConvergence_when_maxIterations_is_too_low ()
 
void test_nnls_default_maxIterations_is_twice_column_count ()
 
void test_nnls_does_not_allocate_during_solve ()
 
void test_nnls_repeated_calls_to_compute_and_solve ()
 
 EIGEN_DECLARE_TEST (NNLS)
 

Macro Definition Documentation

◆ EIGEN_RUNTIME_NO_MALLOC

#define EIGEN_RUNTIME_NO_MALLOC

Function Documentation

◆ EIGEN_DECLARE_TEST()

EIGEN_DECLARE_TEST ( NNLS  )
442  {
443  // Small matrices with known solutions:
447 
448  for (int i = 0; i < g_repeat; i++) {
449  // Essential NNLS properties, across different types.
454 
455  // Robustness tests:
459 
460  // Properties specific to the implementation,
461  // not NNLS in general.
467 
468  // This test fails. It hits allocations in HouseholderSequence.h
469  // test_nnls_does_not_allocate_during_solve();
470  }
471 }
int i
Definition: BiCGSTAB_step_by_step.cpp:9
void test_nnls_handles_dependent_columns()
Definition: NNLS.cpp:170
void test_nnls_with_half_precision()
Definition: NNLS.cpp:305
void test_nnls_returns_NoConvergence_when_maxIterations_is_too_low()
Definition: NNLS.cpp:373
void test_nnls_handles_0x0_matrix()
Definition: NNLS.cpp:149
void test_nnls_handles_Mx0_matrix()
Definition: NNLS.cpp:127
void test_nnls_handles_wide_matrix()
Definition: NNLS.cpp:200
void test_nnls_handles_zero_rhs()
Definition: NNLS.cpp:104
void test_nnls_special_case_solves_in_n_iterations()
Definition: NNLS.cpp:349
void test_nnls_random_problem(const MatrixType &)
Definition: NNLS.cpp:56
void test_nnls_special_case_solves_in_zero_iterations()
Definition: NNLS.cpp:322
void test_nnls_default_maxIterations_is_twice_column_count()
Definition: NNLS.cpp:394
void test_nnls_small_reference_problems()
Definition: NNLS.cpp:297
void test_nnls_repeated_calls_to_compute_and_solve()
Definition: NNLS.cpp:417
The matrix class, also used for vectors and row-vectors.
Definition: Eigen/Eigen/src/Core/Matrix.h:186
static int g_repeat
Definition: main.h:191
#define CALL_SUBTEST_6(FUNC)
Definition: split_test_helper.h:34
#define CALL_SUBTEST_3(FUNC)
Definition: split_test_helper.h:16
#define CALL_SUBTEST_1(FUNC)
Definition: split_test_helper.h:4
#define CALL_SUBTEST_8(FUNC)
Definition: split_test_helper.h:46
#define CALL_SUBTEST_5(FUNC)
Definition: split_test_helper.h:28
#define CALL_SUBTEST_2(FUNC)
Definition: split_test_helper.h:10
#define CALL_SUBTEST_7(FUNC)
Definition: split_test_helper.h:40
#define CALL_SUBTEST_4(FUNC)
Definition: split_test_helper.h:22

References CALL_SUBTEST_1, CALL_SUBTEST_2, CALL_SUBTEST_3, CALL_SUBTEST_4, CALL_SUBTEST_5, CALL_SUBTEST_6, CALL_SUBTEST_7, CALL_SUBTEST_8, Eigen::g_repeat, i, test_nnls_default_maxIterations_is_twice_column_count(), test_nnls_handles_0x0_matrix(), test_nnls_handles_dependent_columns(), test_nnls_handles_Mx0_matrix(), test_nnls_handles_wide_matrix(), test_nnls_handles_zero_rhs(), test_nnls_random_problem(), test_nnls_repeated_calls_to_compute_and_solve(), test_nnls_returns_NoConvergence_when_maxIterations_is_too_low(), test_nnls_small_reference_problems(), test_nnls_special_case_solves_in_n_iterations(), test_nnls_special_case_solves_in_zero_iterations(), and test_nnls_with_half_precision().

◆ test_nnls_default_maxIterations_is_twice_column_count()

void test_nnls_default_maxIterations_is_twice_column_count ( )
394  {
395  const Index cols = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
396  const Index rows = internal::random<Index>(cols, EIGEN_TEST_MAX_SIZE);
397  const MatrixXd A = MatrixXd::Random(rows, cols);
398 
399  NNLS<MatrixXd> nnls(A);
400 
401  VERIFY_IS_EQUAL(nnls.maxIterations(), 2 * cols);
402 }
int rows
Definition: Tutorial_commainit_02.cpp:1
int cols
Definition: Tutorial_commainit_02.cpp:1
#define EIGEN_TEST_MAX_SIZE
Definition: boostmultiprec.cpp:16
#define VERIFY_IS_EQUAL(a, b)
Definition: main.h:367
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:83

References cols, EIGEN_TEST_MAX_SIZE, rows, and VERIFY_IS_EQUAL.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_does_not_allocate_during_solve()

void test_nnls_does_not_allocate_during_solve ( )
404  {
405  const Index cols = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
406  const Index rows = internal::random<Index>(cols, EIGEN_TEST_MAX_SIZE);
407  const MatrixXd A = MatrixXd::Random(rows, cols);
408  const VectorXd b = VectorXd::Random(rows);
409 
410  NNLS<MatrixXd> nnls(A);
411 
412  internal::set_is_malloc_allowed(false);
413  nnls.solve(b);
414  internal::set_is_malloc_allowed(true);
415 }
Scalar * b
Definition: benchVecAdd.cpp:17

References b, cols, EIGEN_TEST_MAX_SIZE, and rows.

◆ test_nnls_handles_0x0_matrix()

void test_nnls_handles_0x0_matrix ( )
149  {
150  //
151  // SETUP
152  //
153  const MatrixXd A(0, 0);
154  const VectorXd b(0);
155 
156  //
157  // ACT
158  //
159  NNLS<MatrixXd> nnls(A);
160  const VectorXd x = nnls.solve(b);
161 
162  //
163  // VERIFY
164  //
166  VERIFY_LE(nnls.iterations(), 0);
167  VERIFY_IS_EQUAL(x.size(), 0);
168 }
Matrix< SCALARA, Dynamic, Dynamic, opt_A > A
Definition: bench_gemm.cpp:47
@ Success
Definition: Constants.h:440
#define VERIFY_LE(a, b)
Definition: main.h:365
list x
Definition: plotDoE.py:28

References b, Eigen::Success, VERIFY_IS_EQUAL, VERIFY_LE, and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_handles_dependent_columns()

void test_nnls_handles_dependent_columns ( )
170  {
171  //
172  // SETUP
173  //
174  const Index rank = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE / 2);
175  const Index cols = 2 * rank;
176  const Index rows = internal::random<Index>(cols, EIGEN_TEST_MAX_SIZE);
177  const MatrixXd A = MatrixXd::Random(rows, rank) * MatrixXd::Random(rank, cols);
178  const VectorXd b = VectorXd::Random(rows);
179 
180  //
181  // ACT
182  //
183  const double tolerance = 1e-8;
184  NNLS<MatrixXd> nnls(A);
185  const VectorXd &x = nnls.solve(b);
186 
187  //
188  // VERIFY
189  //
190  // What should happen when the input 'A' has dependent columns?
191  // We might still succeed. Or we might not converge.
192  // Either outcome is fine. If Success is indicated,
193  // then 'x' must actually be a solution vector.
194 
195  if (nnls.info() == ComputationInfo::Success) {
196  verify_nnls_optimality(A, b, x, tolerance);
197  }
198 }
Array< double, 1, 3 > e(1./3., 0.5, 2.)
void verify_nnls_optimality(const MatrixType &A, const VectorB &b, const VectorX &x, const Scalar tolerance)
Definition: NNLS.cpp:18

References b, cols, e(), EIGEN_TEST_MAX_SIZE, rows, Eigen::Success, verify_nnls_optimality(), and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_handles_Mx0_matrix()

void test_nnls_handles_Mx0_matrix ( )
127  {
128  //
129  // SETUP
130  //
131  const Index rows = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
132  const MatrixXd A(rows, 0);
133  const VectorXd b = VectorXd::Random(rows);
134 
135  //
136  // ACT
137  //
138  NNLS<MatrixXd> nnls(A);
139  const VectorXd x = nnls.solve(b);
140 
141  //
142  // VERIFY
143  //
145  VERIFY_LE(nnls.iterations(), 0);
146  VERIFY_IS_EQUAL(x.size(), 0);
147 }

References b, EIGEN_TEST_MAX_SIZE, rows, Eigen::Success, VERIFY_IS_EQUAL, VERIFY_LE, and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_handles_wide_matrix()

void test_nnls_handles_wide_matrix ( )
200  {
201  //
202  // SETUP
203  //
204  const Index cols = internal::random<Index>(2, EIGEN_TEST_MAX_SIZE);
205  const Index rows = internal::random<Index>(2, cols - 1);
206  const MatrixXd A = MatrixXd::Random(rows, cols);
207  const VectorXd b = VectorXd::Random(rows);
208 
209  //
210  // ACT
211  //
212  const double tolerance = 1e-8;
213  NNLS<MatrixXd> nnls(A);
214  const VectorXd &x = nnls.solve(b);
215 
216  //
217  // VERIFY
218  //
219  // What should happen when the input 'A' is wide?
220  // The unconstrained least-squares problem has infinitely many solutions.
221  // Subject the the non-negativity constraints,
222  // the solution might actually be unique (e.g. it is [0,0,..,0]).
223  // So, NNLS might succeed or it might fail.
224  // Either outcome is fine. If Success is indicated,
225  // then 'x' must actually be a solution vector.
226 
227  if (nnls.info() == ComputationInfo::Success) {
228  verify_nnls_optimality(A, b, x, tolerance);
229  }
230 }

References b, cols, e(), EIGEN_TEST_MAX_SIZE, rows, Eigen::Success, verify_nnls_optimality(), and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_handles_zero_rhs()

void test_nnls_handles_zero_rhs ( )
104  {
105  //
106  // SETUP
107  //
108  const Index cols = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
109  const Index rows = internal::random<Index>(cols, EIGEN_TEST_MAX_SIZE);
110  const MatrixXd A = MatrixXd::Random(rows, cols);
111  const VectorXd b = VectorXd::Zero(rows);
112 
113  //
114  // ACT
115  //
116  NNLS<MatrixXd> nnls(A);
117  const VectorXd x = nnls.solve(b);
118 
119  //
120  // VERIFY
121  //
123  VERIFY_LE(nnls.iterations(), 1); // 0 or 1 would be be fine for an edge case like this.
125 }
double Zero
Definition: pseudosolid_node_update_elements.cc:35

References b, cols, EIGEN_TEST_MAX_SIZE, rows, Eigen::Success, VERIFY_IS_EQUAL, VERIFY_LE, plotDoE::x, and oomph::PseudoSolidHelper::Zero.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_known_1()

void test_nnls_known_1 ( )
233  {
234  Matrix<double, 4, 2> A(4, 2);
237  A << 1, 1, 2, 4, 3, 9, 4, 16;
238  b << 0.6, 2.2, 4.8, 8.4;
239  x << 0.1, 0.5;
240 
241  return test_nnls_known_solution(A, b, x);
242 }
void test_nnls_known_solution(const MatrixType &A, const VectorB &b, const VectorX &x_expected)
Definition: NNLS.cpp:41

References b, test_nnls_known_solution(), and plotDoE::x.

Referenced by test_nnls_small_reference_problems().

◆ test_nnls_known_2()

void test_nnls_known_2 ( )
245  {
246  Matrix<double, 4, 3> A(4, 3);
249 
250  A << 1, 1, 1, 2, 4, 8, 3, 9, 27, 4, 16, 64;
251  b << 0.73, 3.24, 8.31, 16.72;
252  x << 0.1, 0.5, 0.13;
253 
255 }

References b, test_nnls_known_solution(), and plotDoE::x.

Referenced by test_nnls_small_reference_problems().

◆ test_nnls_known_3()

void test_nnls_known_3 ( )
258  {
259  Matrix<double, 4, 4> A(4, 4);
262 
263  A << 1, 1, 1, 1, 2, 4, 8, 16, 3, 9, 27, 81, 4, 16, 64, 256;
264  b << 0.73, 3.24, 8.31, 16.72;
265  x << 0.1, 0.5, 0.13, 0;
266 
268 }

References b, test_nnls_known_solution(), and plotDoE::x.

Referenced by test_nnls_small_reference_problems().

◆ test_nnls_known_4()

void test_nnls_known_4 ( )
271  {
272  Matrix<double, 4, 3> A(4, 3);
275 
276  A << 1, 1, 1, 2, 4, 8, 3, 9, 27, 4, 16, 64;
277  b << 0.23, 1.24, 3.81, 8.72;
278  x << 0.1, 0, 0.13;
279 
281 }

References b, test_nnls_known_solution(), and plotDoE::x.

Referenced by test_nnls_small_reference_problems().

◆ test_nnls_known_5()

void test_nnls_known_5 ( )
284  {
285  Matrix<double, 4, 3> A(4, 3);
288 
289  A << 1, 1, 1, 2, 4, 8, 3, 9, 27, 4, 16, 64;
290  b << 0.13, 0.84, 2.91, 7.12;
291  // Solution obtained by original nnls() implementation in Fortran
292  x << 0.0, 0.0, 0.1106544;
293 
295 }

References b, test_nnls_known_solution(), and plotDoE::x.

Referenced by test_nnls_small_reference_problems().

◆ test_nnls_known_solution()

template<typename MatrixType , typename VectorB , typename VectorX >
void test_nnls_known_solution ( const MatrixType A,
const VectorB &  b,
const VectorX x_expected 
)
41  {
42  using Scalar = typename MatrixType::Scalar;
43 
44  using std::sqrt;
46  Index max_iter = 5 * A.cols(); // A heuristic guess.
47  NNLS<MatrixType> nnls(A, max_iter, tolerance);
48  const VectorX x = nnls.solve(b);
49 
51  VERIFY_IS_APPROX(x, x_expected);
52  verify_nnls_optimality(A, b, x, tolerance);
53 }
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
SCALAR Scalar
Definition: bench_gemm.cpp:45
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE EIGEN_CONSTEXPR Index cols() const EIGEN_NOEXCEPT
Definition: PlainObjectBase.h:192
#define VERIFY_IS_APPROX(a, b)
Definition: integer_types.cpp:13
Definition: NumTraits.h:172

References b, Eigen::PlainObjectBase< Derived >::cols(), sqrt(), Eigen::Success, VERIFY_IS_APPROX, VERIFY_IS_EQUAL, verify_nnls_optimality(), and plotDoE::x.

Referenced by test_nnls_known_1(), test_nnls_known_2(), test_nnls_known_3(), test_nnls_known_4(), and test_nnls_known_5().

◆ test_nnls_random_problem()

template<typename MatrixType >
void test_nnls_random_problem ( const MatrixType )
56  {
57  //
58  // SETUP
59  //
60 
61  Index cols = MatrixType::ColsAtCompileTime;
62  if (cols == Dynamic) cols = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
63  Index rows = MatrixType::RowsAtCompileTime;
64  if (rows == Dynamic) rows = internal::random<Index>(cols, EIGEN_TEST_MAX_SIZE);
65  VERIFY_LE(cols, rows); // To have a unique LS solution: cols <= rows.
66 
67  // Make some sort of random test problem from a wide range of scales and condition numbers.
68  using std::pow;
69  using Scalar = typename MatrixType::Scalar;
70  const Scalar sqrtConditionNumber = pow(Scalar(10), internal::random<Scalar>(Scalar(0), Scalar(2)));
71  const Scalar scaleA = pow(Scalar(10), internal::random<Scalar>(Scalar(-3), Scalar(3)));
72  const Scalar minSingularValue = scaleA / sqrtConditionNumber;
73  const Scalar maxSingularValue = scaleA * sqrtConditionNumber;
75  generateRandomMatrixSvs(setupRangeSvs<Matrix<Scalar, Dynamic, 1>>(cols, minSingularValue, maxSingularValue), rows,
76  cols, A);
77 
78  // Make a random RHS also with a random scaling.
79  using VectorB = decltype(A.col(0).eval());
80  const Scalar scaleB = pow(Scalar(10), internal::random<Scalar>(Scalar(-3), Scalar(3)));
81  const VectorB b = scaleB * VectorB::Random(A.rows());
82 
83  //
84  // ACT
85  //
86 
87  using Scalar = typename MatrixType::Scalar;
88  using std::sqrt;
89  const Scalar tolerance =
90  sqrt(Eigen::GenericNumTraits<Scalar>::epsilon()) * b.cwiseAbs().maxCoeff() * A.cwiseAbs().maxCoeff();
91  Index max_iter = 5 * A.cols(); // A heuristic guess.
92  NNLS<MatrixType> nnls(A, max_iter, tolerance);
93  const typename NNLS<MatrixType>::SolutionVectorType &x = nnls.solve(b);
94 
95  //
96  // VERIFY
97  //
98 
99  // In fact, NNLS can fail on some problems, but they are rare in practice.
101  verify_nnls_optimality(A, b, x, tolerance);
102 }
MatrixXf MatrixType
Definition: benchmark-blocking-sizes.cpp:52
EIGEN_DEVICE_FUNC const GlobalUnaryPowReturnType< Derived, ScalarExponent > pow(const Eigen::ArrayBase< Derived > &x, const ScalarExponent &exponent)
Definition: GlobalFunctions.h:137
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE EIGEN_CONSTEXPR Index rows() const EIGEN_NOEXCEPT
Definition: PlainObjectBase.h:191
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bfloat16 pow(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:625
const int Dynamic
Definition: Constants.h:25
VectorType setupRangeSvs(const Index dim, const RealScalar min, const RealScalar max)
Definition: random_matrix_helper.h:225
void generateRandomMatrixSvs(const RealScalarVectorType &svs, const Index rows, const Index cols, MatrixType &M)
Definition: random_matrix_helper.h:169

References b, cols, Eigen::PlainObjectBase< Derived >::cols(), Eigen::Dynamic, EIGEN_TEST_MAX_SIZE, Eigen::generateRandomMatrixSvs(), Eigen::bfloat16_impl::pow(), Eigen::ArrayBase< Derived >::pow(), rows, Eigen::PlainObjectBase< Derived >::rows(), Eigen::setupRangeSvs(), sqrt(), Eigen::Success, VERIFY_IS_EQUAL, VERIFY_LE, verify_nnls_optimality(), and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_repeated_calls_to_compute_and_solve()

void test_nnls_repeated_calls_to_compute_and_solve ( )
417  {
418  const Index cols2 = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
419  const Index rows2 = internal::random<Index>(cols2, EIGEN_TEST_MAX_SIZE);
420  const MatrixXd A2 = MatrixXd::Random(rows2, cols2);
421  const VectorXd b2 = VectorXd::Random(rows2);
422 
423  NNLS<MatrixXd> nnls;
424 
425  for (int i = 0; i < 4; ++i) {
426  const Index cols = internal::random<Index>(1, EIGEN_TEST_MAX_SIZE);
427  const Index rows = internal::random<Index>(cols, EIGEN_TEST_MAX_SIZE);
428  const MatrixXd A = MatrixXd::Random(rows, cols);
429 
430  nnls.compute(A);
432 
433  for (int j = 0; j < 3; ++j) {
434  const VectorXd b = VectorXd::Random(rows);
435  const VectorXd x = nnls.solve(b);
437  verify_nnls_optimality(A, b, x, 1e-4);
438  }
439  }
440 }
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References b, cols, e(), EIGEN_TEST_MAX_SIZE, i, j, rows, Eigen::Success, VERIFY_IS_EQUAL, verify_nnls_optimality(), and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_returns_NoConvergence_when_maxIterations_is_too_low()

void test_nnls_returns_NoConvergence_when_maxIterations_is_too_low ( )
373  {
374  // Using the special case that takes `n` iterations,
375  // from `test_nnls_special_case_solves_in_n_iterations`,
376  // we can set max iterations too low and that should cause the solve to fail.
377 
378  const Index n = 10;
379  const Index m = 3 * n;
380  // With high probability, this is full column rank, which we need for uniqueness.
381  const MatrixXd A = MatrixXd::Random(m, n);
382  const VectorXd x = VectorXd::Random(n).cwiseAbs().array() + 1; // all positive.
383  const VectorXd b = A * x;
384 
385  NNLS<MatrixXd> nnls(A);
386  const Index max_iters = n - 1;
387  nnls.setMaxIterations(max_iters);
388  nnls.solve(b);
389 
391  VERIFY(nnls.iterations() == max_iters);
392 }
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
@ NoConvergence
Definition: Constants.h:444
int * m
Definition: level2_cplx_impl.h:294
#define VERIFY(a)
Definition: main.h:362

References b, m, n, Eigen::NoConvergence, VERIFY, VERIFY_IS_EQUAL, and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_small_reference_problems()

void test_nnls_small_reference_problems ( )
297  {
303 }
void test_nnls_known_4()
Definition: NNLS.cpp:271
void test_nnls_known_5()
Definition: NNLS.cpp:284
void test_nnls_known_2()
Definition: NNLS.cpp:245
void test_nnls_known_3()
Definition: NNLS.cpp:258
void test_nnls_known_1()
Definition: NNLS.cpp:233

References test_nnls_known_1(), test_nnls_known_2(), test_nnls_known_3(), test_nnls_known_4(), and test_nnls_known_5().

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_special_case_solves_in_n_iterations()

void test_nnls_special_case_solves_in_n_iterations ( )
349  {
350  // The particular NNLS algorithm that is implemented starts with all variables
351  // in the active set and then adds one variable to the inactive set each iteration.
352  // This test builds a system where all variables are inactive at the solution,
353  // so it should take 'n' iterations to get there.
354  //
355  // If the implementation changes to another algorithm that does not have this property,
356  // then this test will need to change (e.g. starting from all constraints inactive,
357  // or using ADMM, or an interior point solver).
358 
359  const Index n = 10;
360  const Index m = 3 * n;
361  // With high probability, this is full column rank, which we need for uniqueness.
362  const MatrixXd A = MatrixXd::Random(m, n);
363  const VectorXd x = VectorXd::Random(n).cwiseAbs().array() + 1; // all positive.
364  const VectorXd b = A * x;
365 
366  NNLS<MatrixXd> nnls(A);
367  nnls.solve(b);
368 
370  VERIFY(nnls.iterations() == n);
371 }

References b, m, n, Eigen::Success, VERIFY, VERIFY_IS_EQUAL, and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_special_case_solves_in_zero_iterations()

void test_nnls_special_case_solves_in_zero_iterations ( )
322  {
323  // The particular NNLS algorithm that is implemented starts with all variables
324  // in the active set.
325  // This test builds a system where all constraints are active at the solution,
326  // so that initial guess is already correct.
327  //
328  // If the implementation changes to another algorithm that does not have this property,
329  // then this test will need to change (e.g. starting from all constraints inactive,
330  // or using ADMM, or an interior point solver).
331 
332  const Index n = 10;
333  const Index m = 3 * n;
334  const VectorXd b = VectorXd::Random(m);
335  // With high probability, this is full column rank, which we need for uniqueness.
336  MatrixXd A = MatrixXd::Random(m, n);
337  // Make every column of `A` such that adding it to the active set only /increases/ the objective,
338  // this ensuring the NNLS solution is all zeros.
339  const VectorXd alignment = -(A.transpose() * b).cwiseSign();
340  A = A * alignment.asDiagonal();
341 
342  NNLS<MatrixXd> nnls(A);
343  nnls.solve(b);
344 
346  VERIFY(nnls.iterations() == 0);
347 }

References b, m, n, Eigen::Success, VERIFY, and VERIFY_IS_EQUAL.

Referenced by EIGEN_DECLARE_TEST().

◆ test_nnls_with_half_precision()

void test_nnls_with_half_precision ( )
305  {
306  // The random matrix generation tools don't work with `half`,
307  // so here's a simpler setup mostly just to check that NNLS compiles & runs with custom scalar types.
308 
309  using Mat = Matrix<half, 8, 2>;
310  using VecB = Matrix<half, 8, 1>;
311  using VecX = Matrix<half, 2, 1>;
312  Mat A = Mat::Random(); // full-column rank with high probability.
313  VecB b = VecB::Random();
314 
315  NNLS<Mat> nnls(A, 20, half(1e-2f));
316  const VecX x = nnls.solve(b);
317 
319  verify_nnls_optimality(A, b, x, half(1e-1));
320 }
Definition: Half.h:139

References b, e(), Eigen::Success, VERIFY_IS_EQUAL, verify_nnls_optimality(), and plotDoE::x.

Referenced by EIGEN_DECLARE_TEST().

◆ verify_nnls_optimality()

template<typename MatrixType , typename VectorB , typename VectorX , typename Scalar >
void verify_nnls_optimality ( const MatrixType A,
const VectorB &  b,
const VectorX x,
const Scalar  tolerance 
)

Check that 'x' solves the NNLS optimization problem min ||A*x-b|| s.t. 0 <= x. The tolerance parameter is the absolute tolerance on the gradient, A'*(A*x-b).

18  {
19  // The NNLS optimality conditions are:
20  //
21  // * 0 = A'*A*x - A'*b - lambda
22  // * 0 <= x[i] \forall i
23  // * 0 <= lambda[i] \forall i
24  // * 0 = x[i]*lambda[i] \forall i
25  //
26  // we don't know lambda, but by assuming the first optimality condition is true,
27  // we can derive it and then check the others conditions.
28  const VectorX lambda = A.transpose() * (A * x - b);
29 
30  // NNLS solutions are EXACTLY not negative.
31  VERIFY_LE(0, x.minCoeff());
32 
33  // Exact lambda would be non-negative, but computed lambda might leak a little
34  VERIFY_LE(-tolerance, lambda.minCoeff());
35 
36  // x[i]*lambda[i] == 0 <~~> (x[i]==0) || (lambda[i] is small)
37  VERIFY(((x.array() == Scalar(0)) || (lambda.array() <= tolerance)).all());
38 }
cout<< "The eigenvalues of A are:"<< endl<< ces.eigenvalues()<< endl;cout<< "The matrix of eigenvectors, V, is:"<< endl<< ces.eigenvectors()<< endl<< endl;complex< float > lambda
Definition: ComplexEigenSolver_compute.cpp:9

References b, lambda, VERIFY, VERIFY_LE, and plotDoE::x.

Referenced by test_nnls_handles_dependent_columns(), test_nnls_handles_wide_matrix(), test_nnls_known_solution(), test_nnls_random_problem(), test_nnls_repeated_calls_to_compute_and_solve(), and test_nnls_with_half_precision().