alpaqa dll
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 num_variables, ///< Number of decision variables
30 length_t num_constraints) ///< Number of constraints
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
42
43 /// Change the dimensions of the problem (number of decision variables and
44 /// number of constraints).
45 /// Destructive: resizes and/or resets the members @ref variable_bounds,
46 /// @ref general_bounds, @ref l1_reg and @ref penalty_alm_split.
47 void resize(length_t num_variables, ///< Number of decision variables
48 length_t num_constraints) ///< Number of constraints
49 {
50 if (std::exchange(this->num_variables, num_variables) != num_variables) {
52 if (l1_reg.size() > 1)
53 l1_reg.resize(0);
54 }
55 if (std::exchange(this->num_constraints, num_constraints) != num_constraints) {
58 }
59 }
60
63 BoxConstrProblem(BoxConstrProblem &&) noexcept = default;
64 BoxConstrProblem &operator=(BoxConstrProblem &&) noexcept = default;
65
66 /// Constraints of the decision variables, @f$ x \in C @f$
67 Box variable_bounds{this->num_variables};
68 /// Other constraints, @f$ g(x) \in D @f$
69 Box general_bounds{this->num_constraints};
70 /// @f$ \ell_1 @f$ (1-norm) regularization parameter.
71 /// Possible dimensions are: @f$ 0 @f$ (no regularization), @f$ 1 @f$ (a
72 /// single scalar factor), or @f$ n @f$ (a different factor for each
73 /// variable).
75
76 /// Components of the constraint function with indices below this number are
77 /// handled using a quadratic penalty method rather than using an
78 /// augmented Lagrangian method. Specifically, the Lagrange multipliers for
79 /// these components (which determine the shifts in ALM) are kept at zero.
81
82 /// Number of decision variables @f$ n @f$, @ref num_variables
84 /// Number of constraints @f$ m @f$, @ref num_constraints
86
87 /** Projected gradient step for rectangular box C.
88 * @f[ \begin{aligned} \hat x &= \Pi_C(x - \gamma\nabla\psi(x)) \\
89 * p &= \hat x - x \\
90 * &= \max(\underline x - x, \;\min(-\gamma\nabla\psi(x), \overline x - x)
91 * \end{aligned} @f] */
92 static real_t eval_proj_grad_step_box(const Box &C, real_t γ, crvec x, crvec grad_ψ, rvec x̂,
93 rvec p) {
94 p = (-γ * grad_ψ).cwiseMax(C.lower - x).cwiseMin(C.upper - x);
95 x̂ = x + p;
96 return real_t(0);
97 }
98
99 /** Proximal gradient step for rectangular box C with ℓ₁-regularization.
100 * @f[ \begin{aligned} h(x) &= \|x\|_1 + \delta_C(x) \\
101 * \hat x &= \prox_{\gamma h}(x - \gamma\nabla\psi(x)) \\
102 * &= -\max\big(
103 * x - \overline x,
104 * \;\min\big(
105 * x - \underline x,
106 * \;\min\big(
107 * \gamma(\nabla\psi(x) + \lambda),
108 * \;\max\big(
109 * \gamma(\nabla\psi(x) - \lambda),
110 * x
111 * \big)
112 * \big)
113 * \big)
114 * \big) \end{aligned} @f] */
115 static void eval_prox_grad_step_box_l1_impl(const Box &C, const auto &λ, real_t γ, crvec x,
116 crvec grad_ψ, rvec x̂, rvec p) {
117 p = -x.cwiseMax(γ * (grad_ψ - λ))
118 .cwiseMin(γ * (grad_ψ + λ))
119 .cwiseMin(x - C.lower)
120 .cwiseMax(x - C.upper);
121 x̂ = x + p;
122 }
123 /// @copydoc eval_prox_grad_step_box_l1_impl
124 static real_t eval_prox_grad_step_box_l1(const Box &C, const auto &λ, real_t γ, crvec x,
125 crvec grad_ψ, rvec x̂, rvec p) {
126 eval_prox_grad_step_box_l1_impl(C, λ, γ, x, grad_ψ, x̂, p);
127 using vec_util::norm_1;
128 return norm_1(x̂.cwiseProduct(λ));
129 }
130
131 /// @copydoc eval_prox_grad_step_box_l1_impl
133 crvec grad_ψ, rvec x̂, rvec p) {
134 auto n = x.size();
135 auto λ_vec = vec::Constant(n, λ);
136 eval_prox_grad_step_box_l1_impl(C, λ_vec, γ, x, grad_ψ, x̂, p);
137 using vec_util::norm_1;
138 return λ * norm_1(x̂);
139 }
140
141 /// @see @ref TypeErasedProblem::eval_proximal_gradient_step
143 if (l1_reg.size() == 0)
144 return eval_proj_grad_step_box(variable_bounds, γ, x, grad_ψ, x̂, p);
145 else if constexpr (requires { l1_reg(0); })
146 if (l1_reg.size() == 1)
147 return eval_prox_grad_step_box_l1_scal(variable_bounds, l1_reg(0), γ, x, grad_ψ, x̂,
148 p);
149 return eval_prox_grad_step_box_l1(variable_bounds, l1_reg, γ, x, grad_ψ, x̂, p);
150 }
151
152 /// @see @ref TypeErasedProblem::eval_projecting_difference_constraints
154 p = projecting_difference(z, general_bounds);
155 }
156
157 static void eval_proj_multipliers_box(const Box &D, rvec y, real_t M,
159 auto num_alm = y.size() - penalty_alm_split;
160 auto y_qpm = y.topRows(penalty_alm_split);
161 auto y_alm = y.bottomRows(num_alm);
162 auto z_alm_lb = D.lower.bottomRows(num_alm);
163 auto z_alm_ub = D.upper.bottomRows(num_alm);
164 y_qpm.setZero();
165 // If there's no lower bound, the multipliers can only be positive
166 auto y_alm_lb = (z_alm_lb.array() == -alpaqa::inf<config_t>).select(vec::Zero(num_alm), -M);
167 // If there's no upper bound, the multipliers can only be negative
168 auto y_alm_ub = (z_alm_ub.array() == +alpaqa::inf<config_t>).select(vec::Zero(num_alm), +M);
169 y_alm = y_alm.cwiseMax(y_alm_lb).cwiseMin(y_alm_ub);
170 }
171
172 /// @see @ref TypeErasedProblem::eval_projection_multipliers
176
177 /// @see @ref TypeErasedProblem::get_variable_bounds
178 const Box &get_variable_bounds() const { return variable_bounds; }
179 /// @see @ref TypeErasedProblem::get_general_bounds
180 const Box &get_general_bounds() const { return general_bounds; }
181
182 /// Only supported if the ℓ₁-regularization term is zero.
183 /// @see @ref TypeErasedProblem::provides_get_variable_bounds
184 [[nodiscard]] bool provides_get_variable_bounds() const {
185 const auto nλ = l1_reg.size();
186 if (nλ == 0)
187 return true;
188 if constexpr (requires { l1_reg(0); })
189 return (nλ == 1 && l1_reg(0) == 0);
190 return false;
191 }
192
193 /// @see @ref TypeErasedProblem::eval_inactive_indices_res_lna
195 requires config_t::supports_indexvec
196 {
197 index_t nJ = 0;
198 // Helper that adds i to index set J if x ∊ C
199 const auto add_to_J_if_in_box_interior = [&](real_t x_fw, index_t i) {
200 if (variable_bounds.lower(i) < x_fw && x_fw < variable_bounds.upper(i))
201 J(nJ++) = i;
202 };
203 // Update the index set J for the general box + l1 case
204 const auto update_J_general = [&](real_t λ, real_t x_fw, index_t i) {
205 if (λ == 0) {
206 add_to_J_if_in_box_interior(x_fw, i);
207 } else {
208 if (x_fw > γ * λ)
209 add_to_J_if_in_box_interior(x_fw - γ * λ, i);
210 else if (x_fw < -γ * λ)
211 add_to_J_if_in_box_interior(x_fw + γ * λ, i);
212 }
213 };
214 const auto nλ = l1_reg.size();
215 const bool λ_is_0 = nλ == 0 || (nλ == 1 && l1_reg(0) == 0);
216 // Only box constraints
217 if (λ_is_0)
218 for (index_t i = 0; i < num_variables; ++i) {
219 real_t x_fw = x(i) - γ * grad_ψ(i);
220 add_to_J_if_in_box_interior(x_fw, i);
221 }
222 // Box constraints and l1
223 else
224 for (index_t i = 0; i < num_variables; ++i) {
225 real_t λi = nλ == 0 ? 0 : nλ == 1 ? l1_reg(0) : l1_reg(i);
226 real_t x_fw = x(i) - γ * grad_ψ(i);
227 update_J_general(λi, x_fw, i);
228 }
229 return nJ;
230 }
231
232 /// @see @ref TypeErasedProblem::check
233 void check() const {
235 "Length of problem.variable_bounds.lower does not match "
236 "problem size problem.num_variables");
238 "Length of problem.variable_bounds.upper does not match "
239 "problem size problem.num_variables");
241 "Length of problem.general_bounds.lower does not match "
242 "problem size problem.num_constraints");
244 "Length of problem.general_bounds.upper does not match "
245 "problem size problem.num_constraints");
246 if (l1_reg.size() > 1)
248 "Length of problem.l1_reg does not match "
249 "problem size problem.num_variables, 1 or 0");
251 throw std::invalid_argument("Invalid penalty_alm_split");
252 }
253
254 /// @see @ref TypeErasedProblem::get_name
255 [[nodiscard]] std::string get_name() const { return "BoxConstrProblem"; }
256};
257
258} // namespace alpaqa
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)
const Box & get_general_bounds() const
void resize(length_t num_variables, length_t num_constraints)
Change the dimensions of the problem (number of decision variables and number of constraints).
BoxConstrProblem(const BoxConstrProblem &)=default
index_t eval_inactive_indices_res_lna(real_t γ, crvec x, crvec grad_ψ, rindexvec J) const
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.
void eval_projection_multipliers(rvec y, real_t M) const
Box general_bounds
Other constraints, .
length_t num_variables
Number of decision variables, dimension of x.
Box variable_bounds
Constraints of the decision variables, .
BoxConstrProblem & operator=(const BoxConstrProblem &)=default
BoxConstrProblem(length_t num_variables, length_t num_constraints)
Create a problem with inactive boxes , with no -regularization, and all general constraints handled u...
index_t penalty_alm_split
Components of the constraint function with indices below this number are handled using a quadratic pe...
const Box & get_variable_bounds() const
real_t eval_proximal_gradient_step(real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) const
alpaqa::Box< config_t > Box
void eval_projecting_difference_constraints(crvec z, rvec p) const
bool provides_get_variable_bounds() const
Only supported if the ℓ₁-regularization term is zero.
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
BoxConstrProblem(Box variable_bounds, Box general_bounds, vec l1_reg=vec(0), index_t penalty_alm_split=0)
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_num_constraints() const
Number of constraints , num_constraints.
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.
length_t get_num_variables() const
Number of decision variables , num_variables.
length_t num_constraints
Number of constraints, dimension of g(x) and z.
#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