Skip to content

Commit

Permalink
Improve UnconstrProblem API
Browse files Browse the repository at this point in the history
  • Loading branch information
tttapa committed Aug 28, 2023
1 parent 3431484 commit 6e086d3
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 19 deletions.
6 changes: 3 additions & 3 deletions examples/C++/SimpleUnconstrProblem/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
// Problem specification
// minimize (a - x)² + b(y - x²)²
struct RosenbrockProblem : alpaqa::UnconstrProblem<alpaqa::DefaultConfig> {
// Specify the number of unknowns
RosenbrockProblem() : UnconstrProblem{2} {}

// Problem parameters
real_t a = 2, b = 100;

// Number of unknowns
length_t get_n() const { return 2; }

// Cost
real_t eval_f(crvec xy) const {
auto x = xy(0), y = xy(1);
Expand Down
24 changes: 15 additions & 9 deletions interfaces/python/src/problem/problems.py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,10 @@ void register_problems(py::module_ &m) {
py::cast<index_t>(t[3]),
};
}))
.def_readonly("n", &BoxConstrProblem::n,
"Number of decision variables, dimension of :math:`x`")
.def_readonly("m", &BoxConstrProblem::m,
"Number of general constraints, dimension of :math:`g(x)`")
.def_property_readonly("n", &BoxConstrProblem::get_n,
"Number of decision variables, dimension of :math:`x`")
.def_property_readonly("m", &BoxConstrProblem::get_m,
"Number of general constraints, dimension of :math:`g(x)`")
.def("resize", &BoxConstrProblem::resize, "n"_a, "m"_a)
.def_readwrite("C", &BoxConstrProblem::C, "Box constraints on :math:`x`")
.def_readwrite("D", &BoxConstrProblem::D, "Box constraints on :math:`g(x)`")
Expand All @@ -293,16 +293,22 @@ void register_problems(py::module_ &m) {
m, "UnconstrProblem", "C++ documentation: :cpp:class:`alpaqa::UnconstrProblem`");
default_copy_methods(unconstr_problem);
unconstr_problem //
.def(py::init<>())
.def(py::init<length_t>(), "n"_a,
":param n: Number of unknowns")
.def(py::pickle(
[](const UnconstrProblem &) { // __getstate__
return py::make_tuple();
[](const UnconstrProblem &self) { // __getstate__
return py::make_tuple(self.n);
},
[](py::tuple t) { // __setstate__
if (t.size() != 0)
if (t.size() != 1)
throw std::runtime_error("Invalid state!");
return UnconstrProblem{};
return UnconstrProblem{py::cast<length_t>(t[0])};
}))
.def_property_readonly("n", &UnconstrProblem::get_n,
"Number of decision variables, dimension of :math:`x`")
.def_property_readonly("m", &UnconstrProblem::get_m,
"Number of general constraints, dimension of :math:`g(x)`")
.def("resize", &UnconstrProblem::resize, "n"_a)
.def("eval_g", &UnconstrProblem::eval_g, "x"_a, "g"_a)
.def("eval_grad_g_prod", &UnconstrProblem::eval_grad_g_prod, "x"_a, "y"_a, "grad_gxy"_a)
.def("eval_jac_g", &UnconstrProblem::eval_jac_g, "x"_a, "inner_idx"_a, "outer_ptr"_a,
Expand Down
25 changes: 20 additions & 5 deletions src/alpaqa/include/alpaqa/problem/box-constr-problem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
#include <alpaqa/problem/box.hpp>
#include <alpaqa/util/check-dim.hpp>

#include <utility>

namespace alpaqa {

/// Implements common problem functions for minimization problems with box
/// constraints. Meant to be used as a base class for custom problem
/// implementations.
/// implementations.
/// Supports optional @f$ \ell_1 @f$-regularization.
/// @ingroup grp_Problems
template <Config Conf>
class BoxConstrProblem {
Expand All @@ -20,6 +23,9 @@ class BoxConstrProblem {
/// Number of constraints, dimension of g(x) and z
length_t m;

/// Create a problem with inactive boxes @f$ (-\infty, +\infty) @f$, with
/// no @f$ \ell_1 @f$-regularization, and all general constraints handled
/// using ALM.
BoxConstrProblem(length_t n, ///< Number of decision variables
length_t m) ///< Number of constraints
: n{n}, m{m} {}
Expand All @@ -28,11 +34,20 @@ class BoxConstrProblem {
: n{C.lowerbound.size()}, m{D.lowerbound.size()}, C{std::move(C)}, D{std::move(D)},
l1_reg{std::move(l1_reg)}, penalty_alm_split{penalty_alm_split} {}

/// Change the dimensions of the problem (number of decision variables and
/// number of constaints).
/// Destructive: resizes and/or resets the members @ref C, @ref D,
/// @ref l1_reg and @ref penalty_alm_split.
void resize(length_t n, length_t m) {
this->n = n;
this->m = m;
C = Box{n};
D = Box{m};
if (std::exchange(this->n, n) != n) {
C = Box{n};
if (l1_reg.size() > 1)
l1_reg.resize(0);
}
if (std::exchange(this->m, m) != m) {
D = Box{m};
penalty_alm_split = 0;
}
}

BoxConstrProblem(const BoxConstrProblem &) = default;
Expand Down
28 changes: 27 additions & 1 deletion src/alpaqa/include/alpaqa/problem/unconstr-problem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,40 @@ template <Config Conf>
class UnconstrProblem {
public:
USING_ALPAQA_CONFIG(Conf);
/// Number of constraints

/// Number of decision variables, dimension of x
length_t n;

/// @param n Number of decision variables
UnconstrProblem(length_t n) : n{n} {}

/// Change the number of decision variables.
void resize(length_t n) { this->n = n; }

UnconstrProblem(const UnconstrProblem &) = default;
UnconstrProblem &operator=(const UnconstrProblem &) = default;
UnconstrProblem(UnconstrProblem &&) noexcept = default;
UnconstrProblem &operator=(UnconstrProblem &&) noexcept = default;

/// Number of decision variables, @ref n
length_t get_n() const { return n; }
/// Number of constraints (always zero)
length_t get_m() const { return 0; }

/// No-op, no constraints.
/// @see @ref TypeErasedProblem::eval_g
void eval_g(crvec, rvec) const {}
/// Constraint gradient is always zero.
/// @see @ref TypeErasedProblem::eval_grad_g_prod
void eval_grad_g_prod(crvec, crvec, rvec grad) const { grad.setZero(); }
/// Constraint Jacobian is always empty.
/// @see @ref TypeErasedProblem::eval_jac_g
void eval_jac_g(crvec, rindexvec, rindexvec, rvec) const {}
/// Constraint gradient is always zero.
/// @see @ref TypeErasedProblem::eval_grad_gi
void eval_grad_gi(crvec, index_t, rvec grad_gi) const { grad_gi.setZero(); }

/// No proximal mapping, just a forward (gradient) step.
/// @see @ref TypeErasedProblem::eval_prox_grad_step
real_t eval_prox_grad_step(real_t γ, crvec x, crvec grad_ψ, rvec x̂, rvec p) const {
p = -γ * grad_ψ;
Expand Down
2 changes: 1 addition & 1 deletion test_package/src/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ int main() {
USING_ALPAQA_CONFIG(alpaqa::DefaultConfig);

struct Problem : alpaqa::UnconstrProblem<config_t> {
length_t get_n() const { return 1; }
Problem() : UnconstrProblem{1} {}
real_t eval_f(crvec) const { return 0; }
void eval_grad_f(crvec, rvec gr) const { gr.setZero(); }
};
Expand Down

0 comments on commit 6e086d3

Please sign in to comment.