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
133 //
134 // See equation (2.13) of [4].
135 Scalar ϕ = pow(α, Scalar(1.5));
137 trial_entry.cost <=
138 current_entry.cost -
139 ϕ * γ_cost * current_entry.constraint_violation ||
140 trial_entry.constraint_violation <=
141 (Scalar(1) - ϕ * γ_constraint) * current_entry.constraint_violation;
142
143 // If constraint violation is below threshold and switching condition is
144 // true, check Armijo condition for step rejection. Otherwise, check
145 // sufficient decrease condition.
146 if (current_entry.constraint_violation <= min_constraint_violation &&
148 if (!armijo_condition) {
149 m_last_rejection_due_to_filter = false;
150 return false;
151 }
152 } else if (!sufficient_decrease) {
153 m_last_rejection_due_to_filter = false;
154 return false;
155 }
156
157 // Reject steps in filter (i.e., dominated by any filter entry)
158 if (in_filter(trial_entry)) {
159 m_last_rejection_due_to_filter = true;
160 return false;
161 }
162
163 // Augment filter with accepted iterate if switching condition or Armijo
164 // condition are false
166 add(FilterEntry{
167 current_entry.cost - ϕ * γ_cost * current_entry.constraint_violation,
168 (Scalar(1) - ϕ * γ_constraint) * current_entry.constraint_violation});
169 }
170
171 return true;
172 }
173
180 return m_last_rejection_due_to_filter;
181 }
182
183 private:
184 static constexpr Scalar γ_cost{1e-8};
185 static constexpr Scalar γ_constraint{1e-5};
186
187 gch::small_vector<FilterEntry<Scalar>> m_filter;
188
189 bool m_last_rejection_due_to_filter = false;
190
194 void add(const FilterEntry<Scalar>& entry) {
195 // Remove dominated entries
196 erase_if(m_filter,
197 [&](const auto& elem) { return elem.dominated_by(entry); });
198
199 m_filter.push_back(entry);
200 }
201
206 bool in_filter(const FilterEntry<Scalar>& entry) const {
207 // An entry is in the filter if it's dominated by any filter entry
208 return std::any_of(m_filter.begin(), m_filter.end(), [&](const auto& elem) {
209 return entry.dominated_by(elem);
210 });
211 }
212};
213
214} // 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:179
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: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