17#ifndef __math_constrained_least_squares_h__
18#define __math_constrained_least_squares_h__
23#include <Eigen/Cholesky>
73 template <
typename ValueType>
78 using matrix_type = Eigen::Matrix<value_type,Eigen::Dynamic,Eigen::Dynamic>;
79 using vector_type = Eigen::Matrix<value_type,Eigen::Dynamic,1>;
91 Problem (
const matrix_type& problem_matrix,
92 const matrix_type& inequality_constraint_matrix,
93 const vector_type& inequality_constraint_vector = vector_type(),
94 size_t num_equalities = 0,
95 value_type solution_min_norm_regularisation = 0.0,
96 value_type constraint_min_norm_regularisation = 0.0,
97 size_t max_iterations = 0,
99 bool problem_in_standard_form =
false) :
101 chol_HtH (H.cols(), H.cols()),
102 t (inequality_constraint_vector),
103 lambda_min_norm (constraint_min_norm_regularisation),
105 max_niter (max_iterations ? max_iterations : 10*problem_matrix.cols()),
106 num_eq (num_equalities) {
108 if (H.cols() != inequality_constraint_matrix.cols())
109 throw Exception (
"FIXME: dimensions of problem and constraint matrices do not match (ICLS)");
111 if (solution_min_norm_regularisation < 0.0)
112 throw Exception (
"FIXME: solution norm regularisation is negative (ICLS)");
114 if (lambda_min_norm < 0.0)
115 throw Exception (
"FIXME: constraint norm regularisation is negative (ICLS)");
118 throw Exception (
"FIXME: tolerance is negative (ICLS)");
120 if (t.size() && t.size() != inequality_constraint_matrix.rows())
121 throw Exception (
"FIXME: dimensions of constraint matrix and vector do not match (ICLS)");
123 if (problem_in_standard_form) {
129 chol_HtH.template triangularView<Eigen::Lower>() = H.transpose() * H;
132 chol_HtH.diagonal().array() += solution_min_norm_regularisation * chol_HtH.diagonal().maxCoeff();
134 chol_HtH.template triangularView<Eigen::Lower>() = chol_HtH.template selfadjointView<Eigen::Lower>().llt().matrixL();
138 if (problem_in_standard_form)
139 b2d.noalias() = chol_HtH.template triangularView<Eigen::Lower>().transpose().template solve<Eigen::OnTheRight> (Eigen::MatrixXd::Identity (H.rows(),H.cols()));
141 b2d.noalias() = chol_HtH.template triangularView<Eigen::Lower>().transpose().template solve<Eigen::OnTheRight> (H);
144 B.noalias() = chol_HtH.template triangularView<Eigen::Lower>().transpose().template solve<Eigen::OnTheRight> (inequality_constraint_matrix);
145 for (ssize_t n = 0; n < B.rows(); ++n) {
146 double norm = B.row(n).norm();
163 Problem (
const matrix_type& problem_matrix,
164 const matrix_type& inequality_constraint_matrix,
165 const matrix_type& equality_constraint_matrix,
166 const vector_type& inequality_constraint_vector = vector_type(),
167 const vector_type& equality_constraint_vector = vector_type(),
168 value_type solution_min_norm_regularisation = 0.0,
169 value_type constraint_min_norm_regularisation = 0.0,
170 size_t max_iterations = 0,
172 bool problem_in_standard_form =
false) :
174 concat (inequality_constraint_matrix, equality_constraint_matrix),
175 concat (inequality_constraint_vector, equality_constraint_vector),
176 equality_constraint_matrix.rows(),
177 solution_min_norm_regularisation,
178 constraint_min_norm_regularisation,
181 problem_in_standard_form) {
182 if (equality_constraint_vector.size() || inequality_constraint_vector.size()) {
183 if (ssize_t(num_eq) != equality_constraint_vector.size())
184 throw Exception (
"FIXME: dimensions of equality constraint matrix and vector do not match (ICLS)");
189 size_t num_parameters ()
const {
return H.cols(); }
190 size_t num_measurements ()
const {
return H.rows(); }
191 size_t num_constraints ()
const {
return B.rows(); }
192 size_t num_equalities ()
const {
return num_eq; }
194 matrix_type H, chol_HtH, B, b2d;
197 size_t max_niter, num_eq;
199 static inline matrix_type concat (
const matrix_type& a,
const matrix_type& b) {
200 matrix_type c (a.rows()+b.rows(), a.cols());
201 c.topRows (a.rows()) = a;
202 c.bottomRows (b.rows()) = b;
206 static inline vector_type concat (
const vector_type& a,
const vector_type& b) {
207 vector_type c (a.size()+b.size());
208 c.head (a.size()) = a;
209 c.tail (b.size()) = b;
217 template <
typename ValueType>
222 using matrix_type = Eigen::Matrix<value_type,Eigen::Dynamic,Eigen::Dynamic>;
223 using vector_type = Eigen::Matrix<value_type,Eigen::Dynamic,1>;
227 BtB (
P.chol_HtH.rows(),
P.chol_HtH.cols()),
228 B (
P.B.rows(),
P.B.cols()),
237 size_t operator() (vector_type& x,
const vector_type& b)
239#ifdef MRTRIX_ICLS_DEBUG
240 std::ofstream l_stream (
"l.txt");
241 std::ofstream n_stream (
"n.txt");
244 y_u =
P.b2d.transpose() * b;
250 const size_t num_eq =
P.num_equalities();
251 const size_t num_ineq =
P.num_constraints() - num_eq;
259 std::fill (
active.begin() + num_ineq,
active.end(),
true);
270 while (
c.head(num_ineq).minCoeff (&min_c_index) < -
P.tol) {
271 bool active_set_changed = !
active[min_c_index];
272 active[min_c_index] =
true;
276 size_t num_active = 0;
277 for (
size_t n = 0; n <
active.size(); ++n) {
279 B.row (num_active) =
P.B.row (n);
280 l[num_active] = -
c_u[n];
284 auto B_active =
B.topRows (num_active);
285 auto l_active =
l.head (num_active);
287 BtB.resize (num_active, num_active);
289 BtB.template triangularView<Eigen::Lower>() = B_active * B_active.transpose();
290 BtB.diagonal().array() +=
P.lambda_min_norm;
291 BtB.template selfadjointView<Eigen::Lower>().llt().solveInPlace (l_active);
297 value_type s_min = std::numeric_limits<value_type>::infinity();
298 size_t s_min_index = 0;
300 for (
size_t n = 0; n < num_ineq; ++n) {
302 if (l_active[a] < 0.0) {
317 if (!std::isfinite (s_min)) {
319 x =
y_u + B_active.transpose() * l_active;
322#ifdef MRTRIX_ICLS_DEBUG
323 l_stream <<
lambda <<
"\n";
329 active_set_changed =
true;
330 active[s_min_index] =
false;
337#ifdef MRTRIX_ICLS_DEBUG
338 l_stream <<
lambda <<
"\n";
339 for (
const auto& a :
active)
340 n_stream << a <<
" ";
345 if (!active_set_changed || niter >
P.max_niter)
355 P.chol_HtH.template triangularView<Eigen::Lower>().transpose().solveInPlace (x);
const Problem< value_type > & P
MR::default_type value_type