forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsymbolic_chebyshev_basis_element.h
156 lines (134 loc) · 6.53 KB
/
symbolic_chebyshev_basis_element.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#pragma once
#ifndef DRAKE_COMMON_SYMBOLIC_HEADER
#error Do not directly include this file. Include "drake/common/symbolic.h".
#endif
#include <map>
#include <utility>
#include "drake/common/drake_copyable.h"
#include "drake/common/hash.h"
#include "drake/common/symbolic.h"
namespace drake {
namespace symbolic {
/**
* ChebyshevBasisElement represents an element of Chebyshev polynomial basis,
* written as the product of Chebyshev polynomials, in the form
* Tₚ₀(x₀)Tₚ₁(x₁)...Tₚₙ(xₙ), where each Tₚᵢ(xᵢ) is a (univariate) Chebyshev
* polynomial of degree pᵢ.
*/
class ChebyshevBasisElement : public PolynomialBasisElement {
public:
DRAKE_DEFAULT_COPY_AND_MOVE_AND_ASSIGN(ChebyshevBasisElement)
/** Constructs a ChebyshevBasisElement equals to 1. */
ChebyshevBasisElement();
explicit ChebyshevBasisElement(
const std::map<Variable, int>& var_to_degree_map);
/** Constructs a Chebyshev polynomial T₁(var). */
explicit ChebyshevBasisElement(const Variable& var);
/** Constructs a Chebyshev polynomial Tₙ(var) where n = degree. */
ChebyshevBasisElement(const Variable& var, int degree);
/** Constructs a default value 1. This overload is used by Eigen when
* EIGEN_INITIALIZE_MATRICES_BY_ZERO is enabled.
*/
explicit ChebyshevBasisElement(std::nullptr_t);
ChebyshevBasisElement(const Eigen::Ref<const VectorX<Variable>>& vars,
const Eigen::Ref<const Eigen::VectorXi>& degrees);
~ChebyshevBasisElement() override = default;
/**
* Compares two ChebyshevBasisElement in lexicographic order.
*/
bool operator<(const ChebyshevBasisElement& other) const;
/**
* Differentiates the ChebyshevBasisElement with respect to a variable.
* We use the fact that
* - If n is even dTₙ(x)/dx = 2n ∑ⱼ Tⱼ(x), j is odd and 1 <= j <= n-1
* - If n is odd dTₙ(x)/dx = 2n ∑ⱼ Tⱼ(x) - n, j is even and 0 <= j <= n-1
* We return `result`, a map from ChebyshevBasisElement to double, such that
* sum(result.key() * result[key]) is the differentiation of `this` w.r.t the
* variable.
* For example if n is even, dTₙ(x)Tₘ(y)/dx = 2n∑ⱼ Tⱼ(x)Tₘ(y), j is odd and
* 1 <= j <= n-1, then the returned result is {T₁(x)Tₘ(y), 2n}, {T₃(x)Tₘ(y),
* 2n}, ..., {T₂ₙ₋₁(x)Tₘ(y), 2n}. A special case is that @p var is not a
* variable in `this`, then we return an empty map.
* @param var A variable to differentiate with.
*/
[[nodiscard]] std::map<ChebyshevBasisElement, double> Differentiate(
const Variable& var) const;
/**
* Integrates a ChebyshevBasisElement for a variable.
* We use the fact that
* ∫ Tₙ(x)dx = 1/(2n+2)Tₙ₊₁(x) − 1/(2n−2)Tₙ₋₁(x)
* A special case is ∫ T₀(x)dx = T₁(x)
* @param var The variable to integrate. If @param var is not a variable in
* this ChebyshevBasisElement, then the integration result is *this * T₁(var).
* @retval result sum(key * result[key]) is the integration result. For
* example, ∫ T₂(x)T₃(y)dx = 1/6*T₃(x)T₃(y) − 1/2 * T₁(x)T₃(y), then the
* result is the map containing {T₃(x)T₃(y), 1/6} and {T₁(x)T₃(y), -1/2}.
*/
[[nodiscard]] std::map<ChebyshevBasisElement, double> Integrate(
const Variable& var) const;
/** Merges this Chebyshev basis element with another Chebyshev basis element
* @p other by merging their var_to_degree_map. After merging, the degree of
* each variable is raised to the sum of the degree in each basis element (if
* a variable does not show up in either one of the basis element, we regard
* its degree to be 0). For example, merging T₁(x)T₃(y) and T₂(x)T₄(z) gets
* T₃(x)T₃(y)T₄(z).
*/
void MergeBasisElementInPlace(const ChebyshevBasisElement& other);
/** Partially evaluates using a given environment @p env. The evaluation
* result is of type pair<double, ChebyshevBasisElement>. The first component
* (: double) represents the coefficient part while the second component
* represents the remaining parts of the ChebyshevBasisElement which was not
* evaluated, the product of the first and the second component is the result
* of the partial evaluation. For example, if this ChebyshevBasisElement is
* T₂(x)T₃(y)T₁(z), and @p env stores x→ 3, y→ 2, then the partial evaluation
* is T₂(3)*T₃(2)*T₁(z) = 17 * 26 * T₁(z) = 442*T₁(z), then we return the pair
* (442, T₁(z)).
*/
[[nodiscard]] std::pair<double, ChebyshevBasisElement> EvaluatePartial(
const Environment& env) const;
/** Implements the @ref hash_append concept. */
template <class HashAlgorithm>
friend void hash_append(HashAlgorithm& hasher,
const ChebyshevBasisElement& item) noexcept {
using drake::hash_append;
// We do not send total_degree_ to the hasher, because it is already fully
// represented by var_to_degree_map_ -- it is just a cached tally of the
// exponents.
hash_append(hasher, item.var_to_degree_map());
}
private:
[[nodiscard]] double DoEvaluate(double variable_val,
int degree) const override;
[[nodiscard]] Expression DoToExpression() const override;
};
/**
* Returns the product of two Chebyshev basis elements.
* Since Tₘ(x) * Tₙ(x) = 0.5 (Tₘ₊ₙ(x) + Tₘ₋ₙ(x)) if m >= n, the product of
* Chebyshev basis elements is the weighted sum of several Chebyshev basis
* elements. For example T₁(x)T₂(y) * T₃(x)T₁(y) = 0.25*(T₄(x)T₃(y) + T₂(x)T₃(y)
* + T₄(x)T₁(y) + T₂(x)T₁(y))
* @return the result of the product, from each ChebyshevBasisElement to its
* coefficient. In the example above, it returns (T₄(x)T₃(y) -> 0.25),
* (T₂(x)T₃(y) -> 0.25), (T₄(x)T₁(y) -> 0.25) and (T₂(x)T₁(y) -> 0.25)
*/
std::map<ChebyshevBasisElement, double> operator*(
const ChebyshevBasisElement& a, const ChebyshevBasisElement& b);
std::ostream& operator<<(std::ostream& out, const ChebyshevBasisElement& m);
} // namespace symbolic
} // namespace drake
namespace std {
/* Provides std::hash<drake::symbolic::ChebyshevBasisElement>. */
template <>
struct hash<drake::symbolic::ChebyshevBasisElement>
: public drake::DefaultHash {};
} // namespace std
#if !defined(DRAKE_DOXYGEN_CXX)
namespace Eigen {
// Eigen scalar type traits for Matrix<drake::symbolic::ChebyshevBasisElement>.
template <>
struct NumTraits<drake::symbolic::ChebyshevBasisElement>
: GenericNumTraits<drake::symbolic::ChebyshevBasisElement> {
static inline int digits10() { return 0; }
};
} // namespace Eigen
#endif // !defined(DRAKE_DOXYGEN_CXX)