alpaqa 1.0.0a8
Nonconvex constrained optimization
Loading...
Searching...
No Matches
box-constr-problem.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace alpaqa {
7
8/// Implements common problem functions for minimization problems with box
9/// constraints. Meant to be used as a base class for custom problem
10/// implementations.
11/// @ingroup grp_Problems
12template <Config Conf>
14 public:
17
18 /// Number of decision variables, dimension of x
20 /// Number of constraints, dimension of g(x) and z
22
23 /// Components of the constraint function with indices below this number are
24 /// handled using a quadratic penalty method rather than using an
25 /// augmented Lagrangian method. Specifically, the Lagrange multipliers for
26 /// these components (which determine the shifts in ALM) are kept at zero.
28
29 BoxConstrProblem(length_t n, ///< Number of decision variables
30 length_t m) ///< Number of constraints
31 : n{n}, m{m} {}
32
34 : n{C.lowerbound.size()}, m{D.lowerbound.size()}, C{std::move(C)}, D{std::move(D)},
35 l1_reg{std::move(l1_reg)} {}
36
38 this->n = n;
39 this->m = m;
40 C = Box{n};
41 D = Box{m};
42 }
43
46 BoxConstrProblem(BoxConstrProblem &&) noexcept = default;
47 BoxConstrProblem &operator=(BoxConstrProblem &&) noexcept = default;
48
49 /// Constraints of the decision variables, @f$ x \in C @f$
50 Box C{this->n};
51 /// Other constraints, @f$ g(x) \in D @f$
52 Box D{this->m};
53 /// @f$ \ell_1 @f$ (1-norm) regularization parameter.
54 /// Possible dimensions are: @f$ 0 @f$ (no regularization), @f$ 1 @f$ (a
55 /// single scalar factor), or @f$ n @f$ (a different factor for each
56 /// variable).
58
59 /// Number of decision variables, @ref n
60 length_t get_n() const { return n; }
61 /// Number of constraints, @ref m
62 length_t get_m() const { return m; }
63
64 /** Projected gradient step for rectangular box C.
65 * @f[ \begin{aligned} \hat x &= \Pi_C(x - \gamma\nabla\psi(x)) \\
66 * p &= \hat x - x \\
67 * &= \max(\underline x - x, \;\min(-\gamma\nabla\psi(x), \overline x - x)
68 * \end{aligned} @f] */
69 static real_t eval_proj_grad_step_box(const Box &C, real_t γ, crvec x, crvec grad_ψ, rvec x̂,
70 rvec p) {
71 p = (-γ * grad_ψ).cwiseMax(C.lowerbound - x).cwiseMin(C.upperbound - x);
72 x̂ = x + p;
73 return real_t(0);
74 }
75
76 /** Proximal gradient step for rectangular box C with ℓ₁-regularization.
77 * @f[ \begin{aligned} h(x) &= \|x\|_1 + \delta_C(x) \\
78 * \hat x &= \prox_{\gamma h}(x - \gamma\nabla\psi(x)) \\
79 * &= -\max\big(
80 * x - \overline x,
81 * \;\min\big(
82 * x - \underline x,
83 * \;\min\big(
84 * \gamma(\nabla\psi(x) + \lambda),
85 * \;\max\big(
86 * \gamma(\nabla\psi(x) - \lambda),
87 * x
88 * \big)
89 * \big)
90 * \big)
91 * \big) \end{aligned} @f] */
92 static void eval_prox_grad_step_box_l1_impl(const Box &C, const auto &λ, real_t γ, crvec x,
93 crvec grad_ψ, rvec x̂, rvec p) {
94 p = -x.cwiseMax(γ * (grad_ψ - λ))
95 .cwiseMin(γ * (grad_ψ + λ))
96 .cwiseMin(x - C.lowerbound)
97 .cwiseMax(x - C.upperbound);
98 x̂ = x + p;
99 }
100 /// @copydoc eval_prox_grad_step_box_l1_impl
101 static real_t eval_prox_grad_step_box_l1(const Box &C, const auto &λ, real_t γ, crvec x,
102 crvec grad_ψ, rvec x̂, rvec p) {
103 eval_prox_grad_step_box_l1_impl(C, λ, γ, x, grad_ψ, x̂, p);
104 return vec_util::norm_1(x̂.cwiseProduct(λ));
105 }
106
107 /// @copydoc eval_prox_grad_step_box_l1_impl
109 crvec grad_ψ, rvec x̂, rvec p) {
110 auto n = x.size();
111 auto λ_vec = vec::Constant(n, λ);
112 eval_prox_grad_step_box_l1_impl(C, λ_vec, γ, x, grad_ψ, x̂, p);
113 return λ * vec_util ::norm_1(x̂);
114 }
115
116 /// @see @ref TypeErasedProblem::eval_prox_grad_step
117 real_t eval_prox_grad_step(real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) const {
118 if (l1_reg.size() == 0)
119 return eval_proj_grad_step_box(C, γ, x, grad_ψ, x̂, p);
120 else if (l1_reg.size() == 1)
121 return eval_prox_grad_step_box_l1_scal(C, l1_reg(0), γ, x, grad_ψ, x̂, p);
122 else
123 return eval_prox_grad_step_box_l1(C, l1_reg, γ, x, grad_ψ, x̂, p);
124 }
125
126 /// @see @ref TypeErasedProblem::eval_proj_diff_g
128
129 static void eval_proj_multipliers_box(const Box &D, rvec y, real_t M,
131 // If there's no lower bound, the multipliers can only be positive
132 auto max_lb = [M](real_t y, real_t z_lb) {
133 real_t y_lb = z_lb == -alpaqa::inf<config_t> ? 0 : -M;
134 return std::max(y, y_lb);
135 };
136 // If there's no upper bound, the multipliers can only be negative
137 auto min_ub = [M](real_t y, real_t z_ub) {
138 real_t y_ub = z_ub == alpaqa::inf<config_t> ? 0 : M;
139 return std::min(y, y_ub);
140 };
141 auto num_alm = y.size() - penalty_alm_split;
142 auto &&y_qpm = y.topRows(penalty_alm_split);
143 auto &&y_alm = y.bottomRows(num_alm);
144 auto &&z_alm_lb = D.lowerbound.bottomRows(num_alm);
145 auto &&z_alm_ub = D.upperbound.bottomRows(num_alm);
146 y_qpm.setZero();
147 y_alm = y_alm.binaryExpr(z_alm_lb, max_lb).binaryExpr(z_alm_ub, min_ub);
148 }
149
150 /// @see @ref TypeErasedProblem::eval_proj_multipliers
153 }
154
155 /// @see @ref TypeErasedProblem::get_box_C
156 const Box &get_box_C() const { return C; }
157 /// @see @ref TypeErasedProblem::get_box_D
158 const Box &get_box_D() const { return D; }
159
160 /// @see @ref TypeErasedProblem::eval_inactive_indices_res_lna
162 index_t nJ = 0;
163 // Helper that adds i to index set J if x ∊ C
164 const auto add_to_J_if_in_box_interior = [&](real_t x_fw, index_t i) {
165 if (C.lowerbound(i) < x_fw && x_fw < C.upperbound(i))
166 J(nJ++) = i;
167 };
168 // Update the index set J for the general box + l1 case
169 const auto update_J_general = [&](real_t λ, real_t x_fw, index_t i) {
170 if (λ == 0) {
171 add_to_J_if_in_box_interior(x_fw, i);
172 } else {
173 if (x_fw > γ * λ)
174 add_to_J_if_in_box_interior(x_fw - γ * λ, i);
175 else if (x_fw < -γ * λ)
176 add_to_J_if_in_box_interior(x_fw + γ * λ, i);
177 }
178 };
179 const auto nλ = l1_reg.size();
180 const bool λ_is_0 = nλ == 0 || (nλ == 1 && l1_reg(0) == 0);
181 // Only box constraints
182 if (λ_is_0)
183 for (index_t i = 0; i < n; ++i) {
184 real_t x_fw = x(i) - γ * grad_ψ(i);
185 add_to_J_if_in_box_interior(x_fw, i);
186 }
187 // Box constraints and l1
188 else
189 for (index_t i = 0; i < n; ++i) {
190 real_t λi = nλ == 0 ? 0 : nλ == 1 ? l1_reg(0) : l1_reg(i);
191 real_t x_fw = x(i) - γ * grad_ψ(i);
192 update_J_general(λi, x_fw, i);
193 }
194 return nJ;
195 }
196
197 /// @see @ref TypeErasedProblem::check
198 void check() const {
199 util::check_dim_msg<config_t>(
200 C.lowerbound, n,
201 "Length of problem.C.lowerbound does not match problem size problem.n");
202 util::check_dim_msg<config_t>(
203 C.upperbound, n,
204 "Length of problem.C.upperbound does not match problem size problem.n");
205 util::check_dim_msg<config_t>(
206 D.lowerbound, m,
207 "Length of problem.D.lowerbound does not match problem size problem.m");
208 util::check_dim_msg<config_t>(
209 D.upperbound, m,
210 "Length of problem.D.upperbound does not match problem size problem.m");
211 if (l1_reg.size() > 1)
212 util::check_dim_msg<config_t>(
213 l1_reg, n,
214 "Length of problem.l1_reg does not match problem size problem.n, 1 or 0");
215 if (penalty_alm_split < 0 || penalty_alm_split > m)
216 throw std::invalid_argument("Invalid penalty_alm_split");
217 }
218};
219
220} // namespace alpaqa
Implements common problem functions for minimization problems with box constraints.
Box C
Constraints of the decision variables, .
const Box & get_box_C() const
index_t eval_inactive_indices_res_lna(real_t γ, crvec x, crvec grad_ψ, rindexvec J) const
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
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(Box C, Box D, vec l1_reg=vec(0))
const Box & get_box_D() const
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.
Box D
Other constraints, .
void eval_proj_multipliers(rvec y, real_t M) const
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)
BoxConstrProblem(length_t n, length_t m)
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:42
auto norm_1(const Eigen::MatrixBase< Derived > &v)
Get the 1-norm of the given vector.
Definition: config.hpp:159
auto projecting_difference(const auto &v, const Box< Conf > &box)
Get the difference between the given vector and its projection.
Definition: box.hpp:49
typename Conf::real_t real_t
Definition: config.hpp:51
typename Conf::rindexvec rindexvec
Definition: config.hpp:65
typename Conf::index_t index_t
Definition: config.hpp:63
typename Conf::length_t length_t
Definition: config.hpp:62
typename Conf::rvec rvec
Definition: config.hpp:55
typename Conf::crvec crvec
Definition: config.hpp:56
typename Conf::vec vec
Definition: config.hpp:52
vec upperbound
Definition: box.hpp:26
vec lowerbound
Definition: box.hpp:25