10#if !defined(MiniTensor_Solvers_h)
11#define MiniTensor_Solvers_h
26template<
typename T,
int N>
27using FAD = Sacado::Fad::SLFad<T, N>;
35template<
typename FunctionDerived,
typename S, Index M>
54 template<
typename T, Index N>
61 template<
typename T, Index N>
68 template<
typename T, Index N>
75 template<
typename T, Index N>
126template<
typename Constra
intDerived,
typename S, Index NC, Index NV>
137 template<
typename T, Index N>
144 template<
typename T, Index N>
179template<
typename Constra
intDerived,
typename S, Index NC, Index NV>
194template<
typename T, Index N>
218template<
typename T, Index N>
228 template<
typename STEP,
typename FN>
264 template<
typename FN>
478template<
typename T, Index N>
485 template<
typename FN>
505template<
typename T, Index N>
512 template<
typename FN>
558template<
typename T, Index N>
579template<
typename T, Index N>
606template<
typename T, Index N>
633template<
typename T, Index N>
654template<
typename T, Index N>
674template<
typename FN,
typename T, Index N>
680 is_fad = Sacado::IsADType<T>::value ==
true;
682 static_assert(is_fad ==
false,
"AD types not allowed for type T");
741template<
typename FN,
typename T, Index N>
742std::unique_ptr<StepBase<FN, T, N>>
748template<
typename FN,
typename T, Index N>
790template<
typename FN,
typename T, Index N>
798 NAME{
"Newton with Line Search"};
832template<
typename FN,
typename T, Index N>
899template<
typename FN,
typename T, Index N>
907 NAME{
"Preconditioned Conjugate Gradient"};
975template<
typename FN,
typename T, Index N>
983 NAME{
"Line Search Regularized"};
1035template<
typename FunctionDerived,
typename S, Index M>
1036template<
typename T, Index N>
1046 return 0.5 *
dot(r, r);
1052template<
typename FunctionDerived,
typename S, Index M>
1053template<
typename T, Index N>
1063 assert(dimension <= DIMENSION);
1068 for (
Index i{0}; i < dimension; ++i) {
1069 x_ad(i) = AD(dimension, i, x(i));
1073 f_ad = f.value(x_ad);
1076 gradient(dimension);
1078 for (
Index i{0}; i < dimension; ++i) {
1079 gradient(i) = f_ad.dx(i);
1088template<
typename FunctionDerived,
typename S, Index M>
1089template<
typename T, Index N>
1094 return f.gradient(x);
1100template<
typename FunctionDerived,
typename S, Index M>
1101template<
typename T, Index N>
1111 assert(dimension <= DIMENSION);
1116 for (
Index i{0}; i < dimension; ++i) {
1117 x_ad(i) = AD(dimension, i, x(i));
1121 r_ad = f.gradient(x_ad);
1126 for (
Index i{0}; i < dimension; ++i) {
1127 for (
Index j{0}; j < dimension; ++j) {
1128 Hessian(i, j) = r_ad(i).dx(j);
1138template<
typename FunctionDerived,
typename S, Index M>
1144 failure_message = msg;
1151template<
typename FunctionDerived,
typename S, Index M>
1162template<
typename FunctionDerived,
typename S, Index M>
1174template<
typename FunctionDerived,
typename S, Index M>
1179 failure_message = msg;
1186template<
typename FunctionDerived,
typename S, Index M>
1191 return failure_message;
1197template<
typename Constra
intDerived,
typename S, Index NC, Index NV>
1198template<
typename T, Index N>
1210template<
typename Constra
intDerived,
typename S, Index NC, Index NV>
1211template<
typename T, Index N>
1221 assert(num_var <= NUM_VAR);
1226 for (
Index i{0}; i < num_var; ++i) {
1227 x_ad(i) = AD(num_var, i, x(i));
1231 r_ad = c.value(x_ad);
1237 Jacobian(num_constr, num_var);
1239 for (
Index i{0}; i < num_constr; ++i) {
1240 for (
Index j{0}; j < num_var; ++j) {
1241 Jacobian(i, j) = r_ad(i).dx(j);
1251template<
typename T, Index N>
1261template<
typename T, Index N>
1266 is_fad = Sacado::IsADType<T>::value ==
true;
1268 static_assert(is_fad ==
false,
"AD types not allowed for type T");
1276template<
typename T, Index N>
1277template<
typename STEP,
typename FN>
1282 step_method_name = step_method.name();
1283 function_name = FN::NAME;
1284 initial_guess = soln;
1287 resi = fn.gradient(soln);
1289 initial_value = fn.value(soln);
1290 previous_value = initial_value;
1291 failed = failed || fn.get_failed();
1292 if (fn.get_failed() ==
true) failure_message = fn.get_failure_message();
1293 initial_norm =
norm(resi);
1295 updateConvergenceCriterion(initial_norm);
1297 step_method.initialize(fn, soln, resi);
1299 while (continueSolve() ==
true) {
1302 step = step_method.step(fn, soln, resi);
1306 resi = fn.gradient(soln);
1308 failed = failed || fn.get_failed();
1309 if (fn.get_failed() ==
true) failure_message = fn.get_failure_message();
1312 norm_resi =
norm(resi);
1314 updateConvergenceCriterion(norm_resi);
1317 value = fn.value(soln);
1319 failed = failed || fn.get_failed();
1320 if (fn.get_failed() ==
true) failure_message = fn.get_failure_message();
1322 updateDivergenceCriterion(value);
1327 recordFinals(fn, soln);
1334template<
typename T, Index N>
1340 converged_string = converged ==
true ?
"YES" :
"NO";
1347 os <<
"Method : " << step_method_name <<
'\n';
1348 os <<
"Function : " << function_name <<
'\n';
1349 os <<
"Converged : " << converged_string <<
'\n';
1350 os <<
"Max Iters : " << max_num_iter <<
'\n';
1351 os <<
"Iters Taken : " << num_iter <<
'\n';
1353 os << std::scientific << std::setprecision(17);
1355 os <<
"Initial |R| : " << std::setw(24) << initial_norm <<
'\n';
1356 os <<
"Abs Tol : " << std::setw(24) << abs_tol <<
'\n';
1357 os <<
"Abs Error : " << std::setw(24) << abs_error <<
'\n';
1358 os <<
"Rel Tol : " << std::setw(24) << rel_tol <<
'\n';
1359 os <<
"Rel Error : " << std::setw(24) << rel_error <<
'\n';
1360 os <<
"Initial X : " << initial_guess <<
'\n';
1361 os <<
"Initial f(X) : " << std::setw(24) << initial_value <<
'\n';
1362 os <<
"Final X : " << final_soln <<
'\n';
1363 os <<
"Final f(X) : " << std::setw(24) << final_value <<
'\n';
1364 os <<
"Final Df(X) : " << final_gradient <<
'\n';
1365 os <<
"Final DDf(X) : " << final_hessian <<
'\n';
1374template<
typename T, Index N>
1380 rel_error = initial_norm > 0.0 ? abs_error / initial_norm : T(0.0);
1383 converged_absolute = abs_error <= abs_tol;
1386 converged_relative = rel_error <= rel_tol;
1388 converged = converged_absolute || converged_relative;
1391 converged_acceptable = abs_error <= acc_tol && num_iter == max_num_iter - 1;
1393 if (converged ==
false && converged_acceptable ==
true) {
1396 warning_message =
"Reached acceptable tolerance";
1405template<
typename T, Index N>
1410 monotonic = fn_value <= previous_value;
1412 if (enforce_monotonicity ==
true && monotonic ==
false) {
1414 failure_message =
"Non-monotonic";
1417 T reduction_ratio = previous_value > 0.0 ? (fn_value / previous_value) : 0.0;
1419 if (reduction_ratio > stagnation_tol) {
1420 ++num_stagnation_iter;
1423 num_stagnation_iter = 0;
1426 non_stagnant = num_stagnation_iter < max_stagnation_iter;
1429 if (enforce_non_stagnation ==
true && non_stagnant ==
false) {
1431 warning_message =
"Stagnant residual";
1434 previous_value = fn_value;
1436 bounded = fn_value <= growth_limit * initial_value;
1438 if (enforce_boundedness ==
true && bounded ==
false) {
1440 failure_message =
"Growing unbounded";
1449template<
typename T, Index N>
1455 if (failed ==
true)
return false;
1459 zero_resi = ((abs_error > 0.0) ==
false);
1461 if (zero_resi ==
true)
return false;
1466 exceeds_min_iter = num_iter >= min_num_iter;
1468 if (exceeds_min_iter ==
false)
return true;
1473 exceeds_max_iter = num_iter >= max_num_iter;
1475 if (exceeds_max_iter ==
true)
return false;
1479 continue_solve = (converged ==
false);
1481 return continue_solve;
1487template<
typename T, Index N>
1488template<
typename FN>
1494 final_value = fn.value(x);
1495 final_gradient = fn.gradient(x);
1496 final_hessian = fn.hessian(x);
1502template<
typename FN,
typename T, Index N>
1507 return solve(A, b, preconditioner_type);
1513template<
typename T, Index N>
1518 return solve(A, b, preconditioner_type);
1525template<
typename T, Index N>
1534 I = identity<T, N>(dimension);
1541 lambda =
norm(Hessian);
1543 for (
Index i{0}; i < max_num_iter; ++i) {
1546 K = Hessian + lambda * I;
1554 std::tie(L, is_posdef) =
cholesky(K);
1556 if (is_posdef ==
false) {
1557 MT_ERROR_EXIT(
"Trust region subproblem encountered singular Hessian.");
1560 step = - this->lin_solve(K, gradient);
1563 q = this->lin_solve(L, step);
1575 lambda_incr = nps * (np - region_size) / nqs / region_size;
1577 lambda += std::max(lambda_incr, 0.0);
1589template<
typename T, Index N>
1598 I = identity<T, N>(dimension);
1605 lambda =
norm(Hessian);
1607 for (
Index i{0}; i < max_num_iter; ++i) {
1617 K = HTH + lambda * I;
1625 std::tie(L, is_posdef) =
cholesky(K);
1627 if (is_posdef ==
false) {
1628 MT_ERROR_EXIT(
"Trust region subproblem encountered singular Hessian.");
1631 step = - this->lin_solve(K, HTr);
1634 q = this->lin_solve(L, step);
1646 lambda_incr = nps * (np - region_size) / nqs / region_size;
1648 lambda += std::max(lambda_incr, 0.0);
1660template<
typename T, Index N>
1666 normg_H =
dot(gradient,
dot(Hessian, gradient));
1668 if (normg_H < 0.0) {
1669 MT_ERROR_EXIT(
"Trust region subproblem encountered singular Hessian.");
1675 normg_squared =
dot(gradient, gradient);
1678 step_minimizer = - normg_squared / normg_H * gradient;
1681 step_unconstrained = - this->lin_solve(Hessian, gradient);
1684 normg_cubed =
norm(gradient) * normg_squared;
1687 tau = std::min(1.0, normg_cubed / (region_size * normg_H));
1690 step{
tau * step_minimizer};
1693 step = step_minimizer + (
tau - 1.0) * (step_unconstrained - step_minimizer);
1704template<
typename T, Index N>
1713 normHTr_squared =
dot(HTr, HTr);
1719 normHTr_HTH =
dot(HTr,
dot(HTH, HTr));
1721 if (normHTr_HTH < 0.0) {
1722 MT_ERROR_EXIT(
"Trust region subproblem encountered singular Hessian.");
1728 normHTr_cubed =
norm(HTr) * normHTr_squared;
1731 tau = std::min(1.0, normHTr_cubed / (region_size * normHTr_HTH));
1734 step_minimizer = - normHTr_squared / normHTr_HTH * HTr;
1737 step_unconstrained = - this->lin_solve(HTH, HTr);
1740 step{
tau * step_minimizer};
1743 step = step_minimizer + (
tau - 1.0) * (step_unconstrained - step_minimizer);
1752template<
typename T, Index N>
1753template<
typename FN>
1765 projection_direction =
dot(direction, direction);
1767 for (
Index i{0}; i < max_num_iter; ++i) {
1770 soln_next = soln + step;
1773 gradient_next = fn.gradient(soln_next);
1776 Hessian_next = fn.hessian(soln_next);
1779 projection =
dot(gradient_next, direction);
1782 contraction =
dot(direction,
dot(Hessian_next, direction));
1785 step_length = - projection / contraction;
1787 step += step_length * direction;
1790 ls_length2 = step_length * step_length * projection_direction;
1793 line_search_converged = ls_length2 <= tolerance * tolerance;
1795 if (line_search_converged ==
true)
break;
1806template<
typename T, Index N>
1807template<
typename FN>
1822 resid = fn.gradient(soln);
1825 resid_newton = fn.gradient(soln + step);
1831 resid_norm =
dot(resid, resid);
1834 resid_newton_norm =
dot(resid_newton, resid_newton);
1837 resid_line_norm = resid_newton_norm;
1839 for (
Index i{0}; i < max_num_iter; i++) {
1841 if (line_iter == max_line_iter) {
1845 search_parameter += search_increment;
1848 if (search_parameter >= 1.0)
break;
1850 step_line_search = alpha * step;
1853 soln_line_search = soln + step_line_search;
1856 gradient_line_search = fn.gradient(soln_line_search);
1859 resid_line_norm_old = resid_line_norm;
1861 resid_line_norm =
dot(gradient_line_search, gradient_line_search);
1863 if (resid_line_norm <= (search_parameter * resid_norm)) {
1864 step = step_line_search;
1869 num = 0.25 * alpha * alpha * resid_line_norm_old;
1872 den = resid_line_norm + resid_line_norm_old * (0.5 * alpha - 1.0);
1878 is_zero_den = (std::fabs(den) < den_tol);
1880 alpha = is_zero_den ==
true ? 0.5 * alpha :
max(0.5 * alpha, num / den);
1892template<
typename FN,
typename T, Index N>
1903template<
typename FN,
typename T, Index N>
1909 Hessian = fn.hessian(soln);
1912 step = - this->lin_solve(Hessian, resi);
1921template<
typename FN,
typename T, Index N>
1932template<
typename FN,
typename T, Index N>
1938 Hessian = fn.hessian(soln);
1941 step = - this->lin_solve(Hessian, resi);
1945 newton_backtrack_ls;
1948 ls_step = newton_backtrack_ls.
step(fn, step, soln);
1956template<
typename FN,
typename T, Index N>
1961 region_size = initial_region_size;
1969template<
typename FN,
typename T, Index N>
1975 Hessian = fn.hessian(soln);
1979 step = - this->lin_solve(Hessian, resi);
1985 if (norm_step < region_size)
return step;
1993 step = tr_exact.
step(Hessian, resi);
1996 soln_next = soln + step;
1999 resi_next = fn.gradient(soln_next);
2012 reduction = (nr - nrp) / (nr - nrKp);
2017 computed_size =
norm(step);
2019 if (reduction < 0.25) {
2021 region_size = 0.25 * computed_size;
2026 increase_region_size = reduction > 0.75;
2028 if (increase_region_size ==
true) {
2029 region_size = std::min(2.0 * region_size, max_region_size);
2034 if (reduction <= min_reduction) {
2044template<
typename FN,
typename T, Index N>
2050 Hessian = fn.hessian(soln);
2052 precon_resi = - this->lin_solve(Hessian, gradient);
2054 search_direction = precon_resi;
2056 projection_new = -
dot(gradient, search_direction);
2058 restart_directions_counter = 0;
2070template<
typename FN,
typename T, Index N>
2080 step = newton_ls.
step(fn, search_direction, soln);
2083 soln_next = soln + step;
2086 gradient_next = fn.gradient(soln_next);
2089 projection_old = projection_new;
2092 projection_mid = -
dot(gradient_next, precon_resi);
2095 Hessian = fn.hessian(soln_next);
2097 precon_resi = - this->lin_solve(Hessian, gradient_next);
2099 projection_new = -
dot(gradient_next, precon_resi);
2102 gram_schmidt_factor = (projection_new - projection_mid) / projection_old;
2104 ++restart_directions_counter;
2107 rewind = restart_directions_counter == restart_directions_interval;
2110 bad_directions = gram_schmidt_factor <= 0.0;
2113 restart_directions = rewind || bad_directions;
2115 if (restart_directions ==
true) {
2117 search_direction = precon_resi;
2118 restart_directions_counter = 0;
2122 search_direction = precon_resi + gram_schmidt_factor * search_direction;
2132template<
typename FN,
typename T, Index N>
2143template<
typename FN,
typename T, Index N>
2152 Hessian = fn.hessian(soln);
2158 bad_hessian =
inv_cond(Hessian) * hessian_cond_tol < 1.0;
2161 if (bad_hessian ==
true) {
2169 step = tr_exact.
step(Hessian, gradient);
2173 step = - this->lin_solve(Hessian, gradient);
2181 ls_step = newton_ls.
step(fn, step, soln);
2192template<
typename FN,
typename T, Index N>
2193std::unique_ptr<StepBase<FN, T, N>>
2196 using STUP = std::unique_ptr<StepBase<FN, T, N>>;
2198 switch (step_type) {
2225 return STUP(
nullptr);
#define MT_ERROR_EXIT(...)
KOKKOS_INLINE_FUNCTION Index get_dimension() const
KOKKOS_INLINE_FUNCTION void fill(Filler const value)
KOKKOS_INLINE_FUNCTION Vector< typename Promote< S, T >::type, M > dot(Matrix< T, M, N > const &A, Vector< S, N > const &u)
KOKKOS_INLINE_FUNCTION Matrix< T, M, N > transpose(Matrix< T, N, M > const &A)
KOKKOS_INLINE_FUNCTION T norm_square(Vector< T, N > const &u)
T inv_cond(Tensor< T, N > const &A)
std::pair< Tensor< T, N >, bool > cholesky(Tensor< T, N > const &A)
RHS solve(Tensor< T, N > const &A, RHS const &b, PreconditionerType const pt=PreconditionerType::IDENTITY)
KOKKOS_INLINE_FUNCTION T norm(Tensor< T, N > const &A)
Vector< T, N > final_soln
Vector< T, N > gradient(FunctionDerived &f, Vector< T, N > const &x)
char const * step_method_name
PreconditionerType preconditioner_type
Tensor< T, N > hessian(FunctionDerived &f, Vector< T, N > const &x)
virtual Vector< T, N > step(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
virtual ~NewtonWithLineSearchStep()
static constexpr char const *const NAME
Vector< T, N > step(Tensor< T, N > const &Hessian, Vector< T, N > const &gradient)
virtual Vector< T, N > step(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
virtual char const * name()
void set_failure_message(char const *const msg=nullptr)
T value(FunctionDerived &f, Vector< T, N > const &x)
virtual char const * name()=0
Vector< T, N > lin_solve(Tensor< T, N > const &A, Vector< T, N > const &b)
Vector< T, N > step(Tensor< T, N > const &Hessian, Vector< T, N > const &gradient)
virtual void initialize(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
static constexpr char const *const NAME
Vector< T, N > step(FN &fn, Vector< T, N > const &direction, Vector< T, N > const &soln)
Bounds(Vector< T, N > const &l, Vector< T, N > const &u)
PreconditionerType preconditioner_type
Vector< T, NC > value(ConstraintDerived &c, Vector< T, N > const &x)
virtual char const * name()
virtual Vector< T, N > step(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
void printReport(std::ostream &os)
void set_failed(char const *const msg=nullptr)
void recordFinals(FN &fn, Vector< T, N > const &x)
Vector< T, N > precon_resi
Vector< T, N > step(Tensor< T, N > const &Hessian, Vector< T, N > const &gradient)
Vector< T, N > residual(FunctionDerived &f, Vector< T, N > const &x)
char const * failure_message
bool enforce_monotonicity
virtual char const * name()
static constexpr char const *const NAME
Vector< T, N > initial_guess
static constexpr Index NUM_VAR
Index num_stagnation_iter
static constexpr Index NUM_CONSTR
static constexpr char const *const NAME
virtual ~LineSearchRegularizedStep()
Vector< T, N > search_direction
char const * function_name
char const * warning_message
virtual ~TrustRegionStep()
static constexpr bool IS_EQUALITY
void updateDivergenceCriterion(T const fn_value)
std::unique_ptr< StepBase< FN, T, N > > stepFactory(StepType step_type)
Index restart_directions_counter
Index restart_directions_interval
Vector< T, N > final_gradient
virtual Vector< T, N > step(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
static constexpr bool IS_EQUALITY
virtual void initialize(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
virtual char const * name()
Index max_stagnation_iter
Vector< T, N > step(Tensor< T, N > const &Hessian, Vector< T, N > const &gradient)
Vector< T, N > lin_solve(Tensor< T, N > const &A, Vector< T, N > const &b)
Matrix< T, NC, NV > gradient(ConstraintDerived &c, Vector< T, N > const &x)
virtual Vector< T, N > step(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
virtual void initialize(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
void solve(STEP &step_method, FN &fn, Vector< T, N > &x)
virtual void initialize(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
virtual void initialize(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)=0
virtual Vector< T, N > step(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)=0
Vector< T, N > step(FN &fn, Vector< T, N > const &direction, Vector< T, N > const &soln)
static constexpr Index DIMENSION
char const * failure_message
Keep a message to inform what went wrong above.
static constexpr char const *const NAME
virtual void initialize(FN &fn, Vector< T, N > const &x, Vector< T, N > const &r)
Tensor< T, N > final_hessian
bool enforce_non_stagnation
Sacado::Fad::SLFad< T, N > FAD
The Fad type to use.
virtual char const * name()
char const * get_failure_message()
virtual ~ConjugateGradientStep()
void updateConvergenceCriterion(T const abs_error)
bool continueSolve() const
uint32_t Index
Indexing type.
KOKKOS_INLINE_FUNCTION Sacado::ScalarType< T >::type tau()
KOKKOS_INLINE_FUNCTION T max(T const &a, T const &b)