Sleipnir C++ API
Loading...
Searching...
No Matches
filter.hpp
1// Copyright (c) Sleipnir contributors
2
3#pragma once
4
5#include <algorithm>
6#include <cmath>
7
8#include <Eigen/Core>
9#include <Eigen/SparseCore>
10#include <gch/small_vector.hpp>
11
12// See docs/algorithms.md#Works_cited for citation definitions.
13
14namespace slp {
15
19template <typename Scalar>
22 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
23
25 Scalar cost{0};
26
29
30 constexpr FilterEntry() = default;
31
36 explicit constexpr FilterEntry(Scalar cost,
37 Scalar constraint_violation = Scalar(0))
39
44 FilterEntry(Scalar f, const DenseVector& c_e)
45 : FilterEntry{f, c_e.template lpNorm<1>()} {}
46
54 FilterEntry(Scalar f, DenseVector& s, const DenseVector& c_e,
55 const DenseVector& c_i, Scalar μ)
56 : FilterEntry{f - μ * s.array().log().sum(),
57 c_e.template lpNorm<1>() + (c_i - s).template lpNorm<1>()} {
58 }
59
64 constexpr bool dominated_by(const FilterEntry<Scalar>& entry) const {
65 return entry.cost <= cost &&
66 entry.constraint_violation <= constraint_violation;
67 }
68};
69
75template <typename Scalar>
76class Filter {
77 public:
79 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
80
82 using SparseVector = Eigen::SparseVector<Scalar>;
83
86
89
94 explicit constexpr Filter(Scalar initial_constraint_violation = Scalar(0)) {
95 using std::max;
96
98 Scalar(1e-4) * max(Scalar(1), initial_constraint_violation);
100 Scalar(1e4) * max(Scalar(1), initial_constraint_violation);
101 }
102
104 void reset() {
105 m_filter.clear();
106 m_last_rejection_due_to_filter = false;
107 }
108
119 const SparseVector& g, Scalar α) {
120 using std::isfinite;
121 using std::pow;
122
123 // Reject steps with nonfinite cost or constraint violation above maximum
124 if (!isfinite(trial_entry.cost) ||
125 trial_entry.constraint_violation > max_constraint_violation) {
126 return false;
127 }
128
129 Scalar g_p_x = g.transpose() * p_x;
130
131 // Switching condition
132 constexpr Scalar s_ϕ(2.3);
133 constexpr Scalar s_θ(1.1);
135 g_p_x < Scalar(0) &&
136 α * pow(-g_p_x, s_ϕ) > pow(current_entry.constraint_violation, s_θ);
137
138 // Armijo condition
139 constexpr Scalar η_ϕ(1e-8);
140 bool armijo_condition =
141 trial_entry.cost <= current_entry.cost + η_ϕ * α * g_p_x;
142
143 // Sufficient decrease condition
144 //
145 // See equation (2.13) of [4].
146 Scalar ϕ = pow(α, Scalar(1.5));
148 trial_entry.cost <=
149 current_entry.cost -
150 ϕ * γ_cost * current_entry.constraint_violation ||
151 trial_entry.constraint_violation <=
152 (Scalar(1) - ϕ * γ_constraint) * current_entry.constraint_violation;
153
154 // If constraint violation is below threshold and switching condition is
155 // true, check Armijo condition for step rejection. Otherwise, check
156 // sufficient decrease condition.
157 if (current_entry.constraint_violation <= min_constraint_violation &&
159 if (!armijo_condition) {
160 m_last_rejection_due_to_filter = false;
161 return false;
162 }
163 } else if (!sufficient_decrease) {
164 m_last_rejection_due_to_filter = false;
165 return false;
166 }
167
168 // Reject steps in filter (i.e., dominated by any filter entry)
169 if (in_filter(trial_entry)) {
170 m_last_rejection_due_to_filter = true;
171 return false;
172 }
173
174 // Augment filter with accepted iterate if switching condition or Armijo
175 // condition are false
177 add(FilterEntry{
178 current_entry.cost - ϕ * γ_cost * current_entry.constraint_violation,
179 (Scalar(1) - ϕ * γ_constraint) * current_entry.constraint_violation});
180 }
181
182 return true;
183 }
184
191 return m_last_rejection_due_to_filter;
192 }
193
194 private:
195 static constexpr Scalar γ_cost{1e-8};
196 static constexpr Scalar γ_constraint{1e-5};
197
198 gch::small_vector<FilterEntry<Scalar>> m_filter;
199
200 bool m_last_rejection_due_to_filter = false;
201
205 void add(const FilterEntry<Scalar>& entry) {
206 // Remove dominated entries
207 erase_if(m_filter,
208 [&](const auto& elem) { return elem.dominated_by(entry); });
209
210 m_filter.push_back(entry);
211 }
212
217 bool in_filter(const FilterEntry<Scalar>& entry) const {
218 // An entry is in the filter if it's dominated by any filter entry
219 return std::any_of(m_filter.begin(), m_filter.end(), [&](const auto& elem) {
220 return entry.dominated_by(elem);
221 });
222 }
223};
224
225} // namespace slp
Definition filter.hpp:76
Scalar min_constraint_violation
The minimum constraint violation.
Definition filter.hpp:85
Eigen::Vector< Scalar, Eigen::Dynamic > DenseVector
Type alias for dense vector.
Definition filter.hpp:79
bool last_rejection_due_to_filter() const
Definition filter.hpp:190
Eigen::SparseVector< Scalar > SparseVector
Type alias for sparse vector.
Definition filter.hpp:82
constexpr Filter(Scalar initial_constraint_violation=Scalar(0))
Definition filter.hpp:94
Scalar max_constraint_violation
The maximum constraint violation.
Definition filter.hpp:88
void reset()
Resets the filter.
Definition filter.hpp:104
bool try_add(const FilterEntry< Scalar > &current_entry, const FilterEntry< Scalar > &trial_entry, const DenseVector &p_x, const SparseVector &g, Scalar α)
Definition filter.hpp:117
Definition intrusive_shared_ptr.hpp:27
Definition filter.hpp:20
Scalar cost
The cost function's value.
Definition filter.hpp:25
FilterEntry(Scalar f, DenseVector &s, const DenseVector &c_e, const DenseVector &c_i, Scalar μ)
Definition filter.hpp:54
constexpr bool dominated_by(const FilterEntry< Scalar > &entry) const
Definition filter.hpp:64
Eigen::Vector< Scalar, Eigen::Dynamic > DenseVector
Type alias for dense vector.
Definition filter.hpp:22
Scalar constraint_violation
The constraint violation.
Definition filter.hpp:28
FilterEntry(Scalar f, const DenseVector &c_e)
Definition filter.hpp:44
constexpr FilterEntry(Scalar cost, Scalar constraint_violation=Scalar(0))
Definition filter.hpp:36