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 <gch/small_vector.hpp>
10
11// See docs/algorithms.md#Works_cited for citation definitions.
12
13namespace slp {
14
18template <typename Scalar>
21 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
22
24 Scalar cost{0};
25
28
29 constexpr FilterEntry() = default;
30
35 explicit constexpr FilterEntry(Scalar cost,
36 Scalar constraint_violation = Scalar(0))
38
43 FilterEntry(Scalar f, const DenseVector& c_e)
44 : FilterEntry{f, c_e.template lpNorm<1>()} {}
45
53 FilterEntry(Scalar f, DenseVector& s, const DenseVector& c_e,
54 const DenseVector& c_i, Scalar μ)
55 : FilterEntry{f - μ * s.array().log().sum(),
56 c_e.template lpNorm<1>() + (c_i - s).template lpNorm<1>()} {
57 }
58
63 constexpr bool dominated_by(const FilterEntry<Scalar>& entry) const {
64 return entry.cost <= cost &&
65 entry.constraint_violation <= constraint_violation;
66 }
67};
68
74template <typename Scalar>
75class Filter {
76 public:
79
82
87 explicit constexpr Filter(Scalar initial_constraint_violation = Scalar(0)) {
88 using std::max;
89
91 Scalar(1e-4) * max(Scalar(1), initial_constraint_violation);
93 Scalar(1e4) * max(Scalar(1), initial_constraint_violation);
94 }
95
97 void reset() {
98 m_filter.clear();
99 m_last_rejection_due_to_filter = false;
100 }
101
110 const FilterEntry<Scalar>& trial_entry, Scalar D_ϕ, Scalar α) {
111 using std::isfinite;
112 using std::pow;
113
114 // Reject steps with nonfinite cost or constraint violation above maximum
115 if (!isfinite(trial_entry.cost) ||
116 trial_entry.constraint_violation > max_constraint_violation) {
117 return false;
118 }
119
120 // Switching condition
121 constexpr Scalar s_ϕ(2.3);
122 constexpr Scalar s_θ(1.1);
124 D_ϕ < Scalar(0) &&
125 α * pow(-D_ϕ, s_ϕ) > pow(current_entry.constraint_violation, s_θ);
126
127 // Armijo condition
128 constexpr Scalar η_ϕ(1e-8);
129 bool armijo_condition =
130 trial_entry.cost <= current_entry.cost + η_ϕ * α * D_ϕ;
131
132 // Sufficient decrease condition
134 trial_entry.cost <=
135 current_entry.cost - γ_cost * current_entry.constraint_violation ||
136 trial_entry.constraint_violation <=
137 (Scalar(1) - γ_constraint) * current_entry.constraint_violation;
138
139 // If constraint violation is below threshold and switching condition is
140 // true, check Armijo condition for step rejection. Otherwise, check
141 // sufficient decrease condition.
142 if (current_entry.constraint_violation <= min_constraint_violation &&
144 if (!armijo_condition) {
145 m_last_rejection_due_to_filter = false;
146 return false;
147 }
148 } else if (!sufficient_decrease) {
149 m_last_rejection_due_to_filter = false;
150 return false;
151 }
152
153 // Reject steps in filter (i.e., dominated by any filter entry)
154 if (in_filter(trial_entry)) {
155 m_last_rejection_due_to_filter = true;
156 return false;
157 }
158
159 // Augment filter with accepted iterate if switching condition or Armijo
160 // condition are false
162 add(FilterEntry{
163 current_entry.cost - γ_cost * current_entry.constraint_violation,
164 (Scalar(1) - γ_constraint) * current_entry.constraint_violation});
165 }
166
167 return true;
168 }
169
176 return m_last_rejection_due_to_filter;
177 }
178
179 private:
180 static constexpr Scalar γ_cost{1e-8};
181 static constexpr Scalar γ_constraint{1e-5};
182
183 gch::small_vector<FilterEntry<Scalar>> m_filter;
184
185 bool m_last_rejection_due_to_filter = false;
186
190 void add(const FilterEntry<Scalar>& entry) {
191 // Remove dominated entries
192 erase_if(m_filter,
193 [&](const auto& elem) { return elem.dominated_by(entry); });
194
195 m_filter.push_back(entry);
196 }
197
202 bool in_filter(const FilterEntry<Scalar>& entry) const {
203 // An entry is in the filter if it's dominated by any filter entry
204 return std::any_of(m_filter.begin(), m_filter.end(), [&](const auto& elem) {
205 return entry.dominated_by(elem);
206 });
207 }
208};
209
213template <typename Scalar>
215 public:
225 const FilterEntry<Scalar>& trial_entry, Scalar α,
226 Scalar min_constraint_violation) {
227 // If step is tiny, current iterate is infeasible, and accepted trial
228 // iterate made insufficient progress toward feasibility, increment stalled
229 // step counter
230 if (α < Scalar(1e-3) &&
231 current_entry.constraint_violation > min_constraint_violation &&
232 trial_entry.constraint_violation >
233 Scalar(0.99) * current_entry.constraint_violation) {
234 ++m_stalled_step_counter;
235
236 // If there were too many consecutive tiny steps, report a stall
237 if (m_stalled_step_counter >= 5) {
238 m_stalled_step_counter = 0;
239 return true;
240 }
241 } else {
242 // A break in stalled steps resets the counter
243 m_stalled_step_counter = 0;
244 }
245
246 return false;
247 }
248
249 private:
250 int m_stalled_step_counter = 0;
251};
252
253} // namespace slp
Definition filter.hpp:75
bool try_add(const FilterEntry< Scalar > &current_entry, const FilterEntry< Scalar > &trial_entry, Scalar D_ϕ, Scalar α)
Definition filter.hpp:109
Scalar min_constraint_violation
The minimum constraint violation.
Definition filter.hpp:78
bool last_rejection_due_to_filter() const
Definition filter.hpp:175
constexpr Filter(Scalar initial_constraint_violation=Scalar(0))
Definition filter.hpp:87
Scalar max_constraint_violation
The maximum constraint violation.
Definition filter.hpp:81
void reset()
Resets the filter.
Definition filter.hpp:97
Definition intrusive_shared_ptr.hpp:27
Definition filter.hpp:214
bool update(const FilterEntry< Scalar > &current_entry, const FilterEntry< Scalar > &trial_entry, Scalar α, Scalar min_constraint_violation)
Definition filter.hpp:224
Definition filter.hpp:19
Scalar cost
The cost function's value.
Definition filter.hpp:24
FilterEntry(Scalar f, DenseVector &s, const DenseVector &c_e, const DenseVector &c_i, Scalar μ)
Definition filter.hpp:53
constexpr bool dominated_by(const FilterEntry< Scalar > &entry) const
Definition filter.hpp:63
Eigen::Vector< Scalar, Eigen::Dynamic > DenseVector
Type alias for dense vector.
Definition filter.hpp:21
Scalar constraint_violation
The constraint violation.
Definition filter.hpp:27
FilterEntry(Scalar f, const DenseVector &c_e)
Definition filter.hpp:43
constexpr FilterEntry(Scalar cost, Scalar constraint_violation=Scalar(0))
Definition filter.hpp:35