WPILibC++ 2027.0.0-alpha-5
Loading...
Searching...
No Matches
filter.hpp
Go to the documentation of this file.
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
17/// Filter entry consisting of cost and constraint violation.
18///
19/// @tparam Scalar Scalar type.
20template <typename Scalar>
22 /// Type alias for dense vector.
23 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
24
25 /// The cost function's value
26 Scalar cost{0};
27
28 /// The constraint violation
30
31 constexpr FilterEntry() = default;
32
33 /// Constructs a FilterEntry.
34 ///
35 /// @param cost The cost function's value.
36 /// @param constraint_violation The constraint violation.
37 explicit constexpr FilterEntry(Scalar cost,
38 Scalar constraint_violation = Scalar(0))
40
41 /// Constructs a Sequential Quadratic Programming filter entry.
42 ///
43 /// @param f The cost function value.
44 /// @param c_e The equality constraint values (nonzero means violation).
45 FilterEntry(Scalar f, const DenseVector& c_e)
46 : FilterEntry{f, c_e.template lpNorm<1>()} {}
47
48 /// Constructs an interior-point method filter entry.
49 ///
50 /// @param f The cost function value.
51 /// @param s The inequality constraint slack variables.
52 /// @param c_e The equality constraint values (nonzero means violation).
53 /// @param c_i The inequality constraint values (negative means violation).
54 /// @param μ The barrier parameter.
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
61 /// Returns true if this filter entry is dominated by another.
62 ///
63 /// @param entry The other entry.
64 /// @return True if this filter entry is dominated by another.
65 constexpr bool dominated_by(const FilterEntry<Scalar>& entry) const {
66 return entry.cost <= cost &&
68 }
69};
70
71/// Step filter.
72///
73/// See the section on filters in chapter 15 of [1].
74///
75/// @tparam Scalar Scalar type.
76template <typename Scalar>
77class Filter {
78 public:
79 /// Type alias for dense vector.
80 using DenseVector = Eigen::Vector<Scalar, Eigen::Dynamic>;
81
82 /// Type alias for sparse vector.
83 using SparseVector = Eigen::SparseVector<Scalar>;
84
85 /// The minimum constraint violation
87
88 /// The maximum constraint violation
90
91 /// Constructs an empty filter.
92 ///
93 /// @param initial_constraint_violation The optimization problem's initial
94 /// constraint violation.
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
104 /// Resets the filter.
105 void reset() {
106 m_filter.clear();
107 m_last_rejection_due_to_filter = false;
108 }
109
110 /// Returns true if the given trial entry is acceptable to the filter.
111 ///
112 /// @param current_entry The entry corresponding to the current iterate.
113 /// @param trial_entry The entry corresponding to the trial iterate.
114 /// @param p_x Decision variable primal step.
115 /// @param g Cost function gradient.
116 /// @param α The step size (0, 1].
117 /// @return True if the given entry is acceptable to the filter.
118 bool try_add(const FilterEntry<Scalar>& current_entry,
119 const FilterEntry<Scalar>& trial_entry, const DenseVector& p_x,
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) ||
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);
135 bool switching_condition =
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));
148 bool sufficient_decrease =
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 &&
159 switching_condition) {
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
177 if (!switching_condition || !armijo_condition) {
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
186 /// Returns true if the most recent trial entry rejection was due to the
187 /// filter.
188 ///
189 /// @return True if the most recent trial entry rejection was due to the
190 /// filter.
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
200
201 bool m_last_rejection_due_to_filter = false;
202
203 /// Adds a new entry to the filter.
204 ///
205 /// @param entry The entry to add to the filter.
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
214 /// Returns true if the given entry is in the filter.
215 ///
216 /// @param entry The entry.
217 /// @return True if the given entry is in the filter.
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
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
Returns true if the most recent trial entry rejection was due to the filter.
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))
Constructs an empty filter.
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 α)
Returns true if the given trial entry is acceptable to the filter.
Definition filter.hpp:118
wpi::util::SmallVector< T > small_vector
Definition small_vector.hpp:10
Definition to_underlying.hpp:7
Variable< Scalar > log(const Variable< Scalar > &x)
log() for Variables.
Definition variable.hpp:521
Variable< Scalar > pow(const ScalarLike auto &base, const Variable< Scalar > &power)
pow() for Variables.
Definition variable.hpp:612
Variable< Scalar > max(const ScalarLike auto &a, const Variable< Scalar > &b)
max() for Variables.
Definition variable.hpp:542
Filter entry consisting of cost and constraint violation.
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 μ)
Constructs an interior-point method filter entry.
Definition filter.hpp:55
constexpr bool dominated_by(const FilterEntry< Scalar > &entry) const
Returns true if this filter entry is dominated by another.
Definition filter.hpp:65
Eigen::Vector< Scalar, Eigen::Dynamic > DenseVector
Type alias for dense vector.
Definition filter.hpp:23
constexpr FilterEntry()=default
Scalar constraint_violation
The constraint violation.
Definition filter.hpp:29
FilterEntry(Scalar f, const DenseVector &c_e)
Constructs a Sequential Quadratic Programming filter entry.
Definition filter.hpp:45
constexpr FilterEntry(Scalar cost, Scalar constraint_violation=Scalar(0))
Constructs a FilterEntry.
Definition filter.hpp:37