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