alpaqa develop
Nonconvex constrained optimization
Loading...
Searching...
No Matches
box-constr-problem.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <utility>
7
8namespace alpaqa {
9
10/// Implements common problem functions for minimization problems with box
11/// constraints. Meant to be used as a base class for custom problem
12/// implementations.
13/// Supports optional @f$ \ell_1 @f$-regularization.
14/// @ingroup grp_Problems
15template <Config Conf>
17 public:
20
21 /// Number of decision variables, dimension of x
23 /// Number of constraints, dimension of g(x) and z
25
26 /// Create a problem with inactive boxes @f$ (-\infty, +\infty) @f$, with
27 /// no @f$ \ell_1 @f$-regularization, and all general constraints handled
28 /// using ALM.
29 BoxConstrProblem(length_t n, ///< Number of decision variables
30 length_t m) ///< Number of constraints
31 : n{n}, m{m} {}
32 /// @copybrief BoxConstrProblem(length_t, length_t)
33 /// @param dims Number of variables and number of constraints.
34 BoxConstrProblem(std::tuple<length_t, length_t> dims)
35 : BoxConstrProblem{get<0>(dims), get<1>(dims)} {}
36
38 : n{C.lowerbound.size()}, m{D.lowerbound.size()}, C{std::move(C)}, D{std::move(D)},
40
41 /// Change the dimensions of the problem (number of decision variables and
42 /// number of constaints).
43 /// Destructive: resizes and/or resets the members @ref C, @ref D,
44 /// @ref l1_reg and @ref penalty_alm_split.
45 void resize(length_t n, ///< Number of decision variables
46 length_t m) ///< Number of constraints
47 {
48 if (std::exchange(this->n, n) != n) {
49 C = Box{n};
50 if (l1_reg.size() > 1)
51 l1_reg.resize(0);
52 }
53 if (std::exchange(this->m, m) != m) {
54 D = Box{m};
56 }
57 }
58
63
64 /// Constraints of the decision variables, @f$ x \in C @f$
65 Box C{this->n};
66 /// Other constraints, @f$ g(x) \in D @f$
67 Box D{this->m};
68 /// @f$ \ell_1 @f$ (1-norm) regularization parameter.
69 /// Possible dimensions are: @f$ 0 @f$ (no regularization), @f$ 1 @f$ (a
70 /// single scalar factor), or @f$ n @f$ (a different factor for each
71 /// variable).
73
74 /// Components of the constraint function with indices below this number are
75 /// handled using a quadratic penalty method rather than using an
76 /// augmented Lagrangian method. Specifically, the Lagrange multipliers for
77 /// these components (which determine the shifts in ALM) are kept at zero.
79
80 /// Number of decision variables, @ref n
81 length_t get_n() const { return n; }
82 /// Number of constraints, @ref m
83 length_t get_m() const { return m; }
84
85 /** Projected gradient step for rectangular box C.
86 * @f[ \begin{aligned} \hat x &= \Pi_C(x - \gamma\nabla\psi(x)) \\
87 * p &= \hat x - x \\
88 * &= \max(\underline x - x, \;\min(-\gamma\nabla\psi(x), \overline x - x)
89 * \end{aligned} @f] */
90 static real_t eval_proj_grad_step_box(const Box &C, real_t γ, crvec x, crvec grad_ψ, rvec x̂,
91 rvec p) {
92 p = (-γ * grad_ψ).cwiseMax(C.lowerbound - x).cwiseMin(C.upperbound - x);
93 x̂ = x + p;
94 return real_t(0);
95 }
96
97 /** Proximal gradient step for rectangular box C with ℓ₁-regularization.
98 * @f[ \begin{aligned} h(x) &= \|x\|_1 + \delta_C(x) \\
99 * \hat x &= \prox_{\gamma h}(x - \gamma\nabla\psi(x)) \\
100 * &= -\max\big(
101 * x - \overline x,
102 * \;\min\big(
103 * x - \underline x,
104 * \;\min\big(
105 * \gamma(\nabla\psi(x) + \lambda),
106 * \;\max\big(
107 * \gamma(\nabla\psi(x) - \lambda),
108 * x
109 * \big)
110 * \big)
111 * \big)
112 * \big) \end{aligned} @f] */
113 static void eval_prox_grad_step_box_l1_impl(const Box &C, const auto &λ, real_t γ, crvec x,
114 crvec grad_ψ, rvec x̂, rvec p) {
115 p = -x.cwiseMax(γ * (grad_ψ - λ))
116 .cwiseMin(γ * (grad_ψ + λ))
117 .cwiseMin(x - C.lowerbound)
118 .cwiseMax(x - C.upperbound);
119 x̂ = x + p;
120 }
121 /// @copydoc eval_prox_grad_step_box_l1_impl
122 static real_t eval_prox_grad_step_box_l1(const Box &C, const auto &λ, real_t γ, crvec x,
123 crvec grad_ψ, rvec x̂, rvec p) {
124 eval_prox_grad_step_box_l1_impl(C, λ, γ, x, grad_ψ, x̂, p);
125 using vec_util::norm_1;
126 return norm_1(x̂.cwiseProduct(λ));
127 }
128
129 /// @copydoc eval_prox_grad_step_box_l1_impl
131 crvec grad_ψ, rvec x̂, rvec p) {
132 auto n = x.size();
133 auto λ_vec = vec::Constant(n, λ);
134 eval_prox_grad_step_box_l1_impl(C, λ_vec, γ, x, grad_ψ, x̂, p);
135 using vec_util::norm_1;
136 return λ * norm_1(x̂);
137 }
138
139 /// @see @ref TypeErasedProblem::eval_prox_grad_step
140 real_t eval_prox_grad_step(real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) const {
141 if (l1_reg.size() == 0)
142 return eval_proj_grad_step_box(C, γ, x, grad_ψ, x̂, p);
143 else if constexpr (requires { l1_reg(0); })
144 if (l1_reg.size() == 1)
145 return eval_prox_grad_step_box_l1_scal(C, l1_reg(0), γ, x, grad_ψ, x̂, p);
146 return eval_prox_grad_step_box_l1(C, l1_reg, γ, x, grad_ψ, x̂, p);
147 }
148
149 /// @see @ref TypeErasedProblem::eval_proj_diff_g
150 void eval_proj_diff_g(crvec z, rvec p) const { p = projecting_difference(z, D); }
151
152 static void eval_proj_multipliers_box(const Box &D, rvec y, real_t M,
154 auto num_alm = y.size() - penalty_alm_split;
155 auto y_qpm = y.topRows(penalty_alm_split);
156 auto y_alm = y.bottomRows(num_alm);
157 auto z_alm_lb = D.lowerbound.bottomRows(num_alm);
158 auto z_alm_ub = D.upperbound.bottomRows(num_alm);
159 y_qpm.setZero();
160 // If there's no lower bound, the multipliers can only be positive
161 auto y_alm_lb = (z_alm_lb.array() == -alpaqa::inf<config_t>).select(vec::Zero(num_alm), -M);
162 // If there's no upper bound, the multipliers can only be negative
163 auto y_alm_ub = (z_alm_ub.array() == +alpaqa::inf<config_t>).select(vec::Zero(num_alm), +M);
164 y_alm = y_alm.cwiseMax(y_alm_lb).cwiseMin(y_alm_ub);
165 }
166
167 /// @see @ref TypeErasedProblem::eval_proj_multipliers
171
172 /// @see @ref TypeErasedProblem::get_box_C
173 const Box &get_box_C() const { return C; }
174 /// @see @ref TypeErasedProblem::get_box_D
175 const Box &get_box_D() const { return D; }
176
177 /// Only supported if the ℓ₁-regularization term is zero.
178 /// @see @ref TypeErasedProblem::provides_get_box_C
179 [[nodiscard]] bool provides_get_box_C() const {
180 const auto = l1_reg.size();
181 if ( == 0)
182 return true;
183 if constexpr (requires { l1_reg(0); })
184 return ( == 1 && l1_reg(0) == 0);
185 return false;
186 }
187
188 /// @see @ref TypeErasedProblem::eval_inactive_indices_res_lna
189 index_t eval_inactive_indices_res_lna(real_t γ, crvec x, crvec grad_ψ, rindexvec J) const
190 requires config_t::supports_indexvec
191 {
192 index_t nJ = 0;
193 // Helper that adds i to index set J if x ∊ C
194 const auto add_to_J_if_in_box_interior = [&](real_t x_fw, index_t i) {
195 if (C.lowerbound(i) < x_fw && x_fw < C.upperbound(i))
196 J(nJ++) = i;
197 };
198 // Update the index set J for the general box + l1 case
199 const auto update_J_general = [&](real_t λ, real_t x_fw, index_t i) {
200 if (λ == 0) {
202 } else {
203 if (x_fw > γ * λ)
204 add_to_J_if_in_box_interior(x_fw - γ * λ, i);
205 else if (x_fw < -γ * λ)
206 add_to_J_if_in_box_interior(x_fw + γ * λ, i);
207 }
208 };
209 const auto = l1_reg.size();
210 const bool λ_is_0 = == 0 || ( == 1 && l1_reg(0) == 0);
211 // Only box constraints
212 if (λ_is_0)
213 for (index_t i = 0; i < n; ++i) {
214 real_t x_fw = x(i) - γ * grad_ψ(i);
216 }
217 // Box constraints and l1
218 else
219 for (index_t i = 0; i < n; ++i) {
220 real_t λi = == 0 ? 0 : == 1 ? l1_reg(0) : l1_reg(i);
221 real_t x_fw = x(i) - γ * grad_ψ(i);
223 }
224 return nJ;
225 }
226
227 /// @see @ref TypeErasedProblem::check
228 void check() const {
230 "Length of problem.C.lowerbound does not match problem size problem.n");
232 "Length of problem.C.upperbound does not match problem size problem.n");
234 "Length of problem.D.lowerbound does not match problem size problem.m");
236 "Length of problem.D.upperbound does not match problem size problem.m");
237 if (l1_reg.size() > 1)
239 l1_reg, n,
240 "Length of problem.l1_reg does not match problem size problem.n, 1 or 0");
242 throw std::invalid_argument("Invalid penalty_alm_split");
243 }
244
245 /// @see @ref TypeErasedProblem::get_name
246 [[nodiscard]] std::string get_name() const { return "BoxConstrProblem"; }
247};
248
249} // namespace alpaqa
Implements common problem functions for minimization problems with box constraints.
Box C
Constraints of the decision variables, .
void eval_proj_diff_g(crvec z, rvec p) const
static real_t eval_prox_grad_step_box_l1(const Box &C, const auto &λ, real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p)
Proximal gradient step for rectangular box C with ℓ₁-regularization.
static void eval_proj_multipliers_box(const Box &D, rvec y, real_t M, index_t penalty_alm_split)
BoxConstrProblem(const BoxConstrProblem &)=default
bool provides_get_box_C() const
Only supported if the ℓ₁-regularization term is zero.
length_t m
Number of constraints, dimension of g(x) and z.
static void eval_prox_grad_step_box_l1_impl(const Box &C, const auto &λ, real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p)
Proximal gradient step for rectangular box C with ℓ₁-regularization.
BoxConstrProblem & operator=(const BoxConstrProblem &)=default
length_t get_m() const
Number of constraints, m.
index_t penalty_alm_split
Components of the constraint function with indices below this number are handled using a quadratic pe...
length_t n
Number of decision variables, dimension of x.
BoxConstrProblem(Box C, Box D, vec l1_reg=vec(0), index_t penalty_alm_split=0)
Box D
Other constraints, .
void eval_proj_multipliers(rvec y, real_t M) const
BoxConstrProblem(std::tuple< length_t, length_t > dims)
Create a problem with inactive boxes , with no -regularization, and all general constraints handled u...
BoxConstrProblem(BoxConstrProblem &&) noexcept=default
real_t eval_prox_grad_step(real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) const
void resize(length_t n, length_t m)
Change the dimensions of the problem (number of decision variables and number of constaints).
BoxConstrProblem(length_t n, length_t m)
Create a problem with inactive boxes , with no -regularization, and all general constraints handled u...
static real_t eval_prox_grad_step_box_l1_scal(const Box &C, real_t λ, real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p)
Proximal gradient step for rectangular box C with ℓ₁-regularization.
length_t get_n() const
Number of decision variables, n.
vec l1_reg
(1-norm) regularization parameter.
static real_t eval_proj_grad_step_box(const Box &C, real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p)
Projected gradient step for rectangular box C.
#define USING_ALPAQA_CONFIG(Conf)
Definition config.hpp:77
void check_dim_msg(const V &v, auto sz, std::string msg)
Definition check-dim.hpp:11
auto norm_1(const Eigen::MatrixBase< Derived > &v)
Get the 1-norm of the given vector.
Definition config.hpp:212
typename Conf::real_t real_t
Definition config.hpp:86
typename Conf::rindexvec rindexvec
Definition config.hpp:106
typename Conf::index_t index_t
Definition config.hpp:104
typename Conf::length_t length_t
Definition config.hpp:103
constexpr const auto inf
Definition config.hpp:112
typename Conf::rvec rvec
Definition config.hpp:91
typename Conf::crvec crvec
Definition config.hpp:92
typename Conf::vec vec
Definition config.hpp:88