Directory: | ./ |
---|---|
File: | include/ndcurves/linear_variable.h |
Date: | 2025-03-05 17:18:30 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 98 | 140 | 70.0% |
Branches: | 85 | 216 | 39.4% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * \file linear_variable.h | ||
3 | * \brief storage for variable points of the form p_i = B_i x + c_i | ||
4 | * \author Steve T. | ||
5 | * \version 0.1 | ||
6 | * \date 07/02/2019 | ||
7 | */ | ||
8 | |||
9 | #ifndef _CLASS_LINEAR_VARIABLE | ||
10 | #define _CLASS_LINEAR_VARIABLE | ||
11 | |||
12 | #include <math.h> | ||
13 | |||
14 | #include <Eigen/Core> | ||
15 | #include <stdexcept> | ||
16 | #include <vector> | ||
17 | |||
18 | #include "MathDefs.h" | ||
19 | #include "bezier_curve.h" | ||
20 | #include "curve_abc.h" | ||
21 | #include "serialization/archive.hpp" | ||
22 | #include "serialization/eigen-matrix.hpp" | ||
23 | |||
24 | namespace ndcurves { | ||
25 | template <typename Numeric = double, bool Safe = true> | ||
26 | struct linear_variable : public serialization::Serializable { | ||
27 | typedef Eigen::Matrix<Numeric, Eigen::Dynamic, 1> vector_x_t; | ||
28 | typedef Eigen::Matrix<Numeric, Eigen::Dynamic, Eigen::Dynamic> matrix_x_t; | ||
29 | typedef Eigen::Matrix<Numeric, 3, 1> vector_3_t; | ||
30 | typedef Eigen::Matrix<Numeric, 3, 3> matrix_3_t; | ||
31 | typedef linear_variable<Numeric> linear_variable_t; | ||
32 | |||
33 | 155 | linear_variable() | |
34 |
1/2✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
|
155 | : B_(matrix_x_t::Identity(0, 0)), |
35 |
2/4✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
|
155 | c_(vector_x_t::Zero(0)), |
36 | 155 | zero(true) {} // variable | |
37 | 55 | linear_variable(const vector_x_t& c) | |
38 |
1/2✓ Branch 4 taken 55 times.
✗ Branch 5 not taken.
|
55 | : B_(matrix_x_t::Zero(c.size(), c.size())), |
39 |
1/2✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
|
55 | c_(c), |
40 | 55 | zero(false) {} // constant | |
41 | 3277 | linear_variable(const matrix_x_t& B, const vector_x_t& c) | |
42 |
1/2✓ Branch 2 taken 3277 times.
✗ Branch 3 not taken.
|
3277 | : B_(B), c_(c), zero(false) {} // mixed |
43 | 5231 | linear_variable(const linear_variable_t& other) | |
44 | 5231 | : B_(other.B()), | |
45 |
1/2✓ Branch 2 taken 5231 times.
✗ Branch 3 not taken.
|
5231 | c_(other.c()), |
46 | 5231 | zero(other.isZero()) {} // copy constructor | |
47 | |||
48 | 8718 | ~linear_variable() {} | |
49 | |||
50 | /// \brief Linear evaluation for vector x. | ||
51 | /// \param val : vector to evaluate the linear variable. | ||
52 | /// \return Evaluation of linear variable for vector x. | ||
53 | /// | ||
54 | 170 | vector_x_t operator()(const Eigen::Ref<const vector_x_t>& val) const { | |
55 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 170 times.
|
170 | if (isZero()) return c(); |
56 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 170 times.
|
170 | if (Safe && B().cols() != val.rows()) |
57 | ✗ | throw std::length_error( | |
58 | "Cannot evaluate linear variable, variable value does not have the " | ||
59 | "correct dimension"); | ||
60 |
2/4✓ Branch 4 taken 170 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 170 times.
✗ Branch 8 not taken.
|
170 | return B() * val + c(); |
61 | } | ||
62 | |||
63 | /// \brief Add another linear variable. | ||
64 | /// \param w1 : linear variable to add. | ||
65 | /// \return Linear variable after operation. | ||
66 | /// | ||
67 | 934 | linear_variable_t& operator+=(const linear_variable_t& w1) { | |
68 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 934 times.
|
934 | if (w1.isZero()) return *this; |
69 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 934 times.
|
934 | if (isZero()) { |
70 | ✗ | this->B_ = w1.B_; | |
71 | ✗ | this->c_ = w1.c_; | |
72 | ✗ | zero = w1.isZero(); | |
73 | } else { | ||
74 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 934 times.
|
934 | if (Safe && B().rows() != w1.B().rows()) |
75 | ✗ | throw std::length_error( | |
76 | "Cannot add linear variables, variables do not have the same " | ||
77 | "dimension"); | ||
78 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 934 times.
|
1868 | else if (B().cols() > |
79 | 934 | w1.B().cols()) { // new variables added left for primitive | |
80 | ✗ | B_.block(0, B().cols() - w1.B().cols(), B().rows(), w1.B().cols()) += | |
81 | ✗ | w1.B(); | |
82 | ✗ | c_.tail(w1.c().rows()) += w1.c(); | |
83 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 934 times.
|
1868 | } else if (B().cols() < |
84 | 934 | w1.B().cols()) { // new variables added left for primitive | |
85 | ✗ | linear_variable_t opp = w1 + (*this); | |
86 | ✗ | this->B_ = opp.B_; | |
87 | ✗ | this->c_ = opp.c_; | |
88 | ✗ | } else { | |
89 | 934 | this->B_ += w1.B_; | |
90 | 934 | this->c_ += w1.c_; | |
91 | } | ||
92 | } | ||
93 | 934 | return *this; | |
94 | } | ||
95 | |||
96 | /// \brief Substract another linear variable. | ||
97 | /// \param w1 : linear variable to substract. | ||
98 | /// \return Linear variable after operation. | ||
99 | /// | ||
100 | 16 | linear_variable_t& operator-=(const linear_variable_t& w1) { | |
101 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if (w1.isZero()) return *this; |
102 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
16 | if (isZero()) { |
103 | ✗ | this->B_ = -w1.B_; | |
104 | ✗ | this->c_ = -w1.c_; | |
105 | ✗ | zero = w1.isZero(); | |
106 | } else { | ||
107 |
1/2✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
|
16 | if (Safe && B().rows() != w1.B().rows()) |
108 | ✗ | throw std::length_error( | |
109 | "Cannot add linear variables, variables do not have the same " | ||
110 | "dimension"); | ||
111 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
32 | else if (B().cols() > |
112 | 16 | w1.B().cols()) { // new variables added left for primitive | |
113 | ✗ | B_.block(0, B().cols() - w1.B().cols(), B().rows(), w1.B().cols()) -= | |
114 | ✗ | w1.B(); | |
115 | ✗ | c_.tail(w1.c().rows()) -= w1.c(); | |
116 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
32 | } else if (B().cols() < |
117 | 16 | w1.B().cols()) { // new variables added left for primitive | |
118 | ✗ | linear_variable_t opp = -w1 + (*this); | |
119 | ✗ | this->B_ = opp.B_; | |
120 | ✗ | this->c_ = opp.c_; | |
121 | ✗ | } else { | |
122 | 16 | this->B_ -= w1.B_; | |
123 | 16 | this->c_ -= w1.c_; | |
124 | } | ||
125 | } | ||
126 | 16 | return *this; | |
127 | } | ||
128 | |||
129 | /// \brief Divide by a constant : p_i / d = B_i*x/d + c_i/d. | ||
130 | /// \param d : constant. | ||
131 | /// \return Linear variable after operation. | ||
132 | /// | ||
133 | ✗ | linear_variable_t& operator/=(const double d) { | |
134 | ✗ | B_ /= d; | |
135 | ✗ | c_ /= d; | |
136 | ✗ | return *this; | |
137 | } | ||
138 | |||
139 | /// \brief Multiply by a constant : p_i / d = B_i*x*d + c_i*d. | ||
140 | /// \param d : constant. | ||
141 | /// \return Linear variable after operation. | ||
142 | /// | ||
143 | 1915 | linear_variable_t& operator*=(const double d) { | |
144 | 1915 | B_ *= d; | |
145 | 1915 | c_ *= d; | |
146 | 1915 | return *this; | |
147 | } | ||
148 | |||
149 | /// \brief Compute the cross product of the current linear_variable and the | ||
150 | /// other. | ||
151 | /// This method of course only makes sense for dimension 3 curves and | ||
152 | /// dimension 3 unknown, since otherwise the result is non-linear. It assumes | ||
153 | /// that a method point_t cross(const point_t&, const point_t&) has been | ||
154 | /// defined | ||
155 | /// \param pOther other polynomial to compute the cross product with. | ||
156 | /// \return a new polynomial defining the cross product between this and | ||
157 | /// other | ||
158 | 33 | linear_variable_t cross(const linear_variable_t& other) const { | |
159 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
|
33 | if (B().rows() != 3) |
160 | ✗ | throw std::invalid_argument( | |
161 | "Can't perform cross product on linear variables with dimensions != " | ||
162 | "3 "); | ||
163 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
|
33 | if (B().cols() != 3) |
164 | ✗ | throw std::invalid_argument( | |
165 | "Can't perform cross product on linear variables more than one " | ||
166 | "unknown "); | ||
167 |
6/8✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 15 times.
✓ Branch 7 taken 18 times.
✓ Branch 9 taken 15 times.
✗ Branch 10 not taken.
|
33 | if (isZero() || other.isZero()) return linear_variable_t::Zero(3); |
168 |
5/8✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 18 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 17 times.
✓ Branch 12 taken 1 times.
|
35 | if ((B().squaredNorm() - B().diagonal().squaredNorm() > MARGIN) || |
169 |
7/10✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 17 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 17 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 16 times.
✓ Branch 13 taken 2 times.
✓ Branch 14 taken 16 times.
|
35 | (other.B().squaredNorm() - other.B().diagonal().squaredNorm() > MARGIN)) |
170 |
1/2✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
2 | throw std::invalid_argument( |
171 | "Can't perform cross product on linear variables if B is not " | ||
172 | "diagonal "); | ||
173 | // (B1 x + c1) X (B2 x + c2) = (-c2X B1) x + (bX B2) x + b1Xb2 | ||
174 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
16 | typename linear_variable_t::matrix_3_t newB = |
175 | skew<typename linear_variable_t::matrix_3_t, | ||
176 |
4/8✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 16 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 16 times.
✗ Branch 12 not taken.
|
16 | typename linear_variable_t::vector_3_t>(-other.c()) * |
177 | 16 | B() + | |
178 | skew<typename linear_variable_t::matrix_3_t, | ||
179 |
3/6✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 16 times.
✗ Branch 9 not taken.
|
16 | typename linear_variable_t::vector_3_t>(c()) * |
180 | 16 | other.B(); | |
181 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | typename linear_variable_t::vector_3_t newC = |
182 | 16 | ndcurves::cross(c(), other.c()); | |
183 |
3/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 16 times.
✗ Branch 8 not taken.
|
16 | return linear_variable_t(newB, newC); |
184 | } | ||
185 | |||
186 | /// \brief Get a linear variable equal to zero. | ||
187 | /// \param dim : Dimension of linear variable. | ||
188 | /// \return Linear variable equal to zero. | ||
189 | /// | ||
190 | 30 | static linear_variable_t Zero(size_t dim = 0) { | |
191 |
4/8✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 30 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 30 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 30 times.
✗ Branch 12 not taken.
|
30 | return linear_variable_t(matrix_x_t::Zero(dim, dim), vector_x_t::Zero(dim)); |
192 | } | ||
193 | |||
194 | /// \brief Get a linear variable equal to the variable | ||
195 | /// \param dim : Dimension of linear variable. | ||
196 | /// \return Linear variable equal to the variable. | ||
197 | /// | ||
198 | 168 | static linear_variable_t X(size_t dim = 0) { | |
199 | return linear_variable_t(matrix_x_t::Identity(dim, dim), | ||
200 |
4/8✓ Branch 2 taken 168 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 168 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 168 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 168 times.
✗ Branch 12 not taken.
|
168 | vector_x_t::Zero(dim)); |
201 | } | ||
202 | |||
203 | /// \brief Get dimension of linear variable. | ||
204 | /// \return Dimension of linear variable. | ||
205 | /// | ||
206 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 701 times.
|
705 | std::size_t size() const { return zero ? 0 : std::max(B_.rows(), c_.size()); } |
207 | |||
208 | /// \brief Get norm of linear variable (Norm of B plus norm of C). | ||
209 | /// \return Norm of linear variable. | ||
210 | ✗ | Numeric norm() const { return isZero() ? 0 : (B_.norm() + c_.norm()); } | |
211 | |||
212 | /// \brief Check if actual linear variable and other are approximately equal | ||
213 | /// given a precision threshold. Only two curves of the same class can be | ||
214 | /// approximately equal, \param prec : the precision threshold, default | ||
215 | /// Eigen::NumTraits<Numeric>::dummy_precision() \return true if the two | ||
216 | /// linear variables are approximately equal. | ||
217 | ✗ | bool isApprox( | |
218 | const linear_variable_t& other, | ||
219 | const double prec = Eigen::NumTraits<Numeric>::dummy_precision()) const { | ||
220 | ✗ | return (*this - other).norm() < prec; | |
221 | } | ||
222 | |||
223 | 14763 | const matrix_x_t& B() const { return B_; } | |
224 | 8874 | const vector_x_t& c() const { return c_; } | |
225 | 7484 | bool isZero() const { return zero; } | |
226 | |||
227 | // Serialization of the class | ||
228 | friend class boost::serialization::access; | ||
229 | |||
230 | template <class Archive> | ||
231 | ✗ | void serialize(Archive& ar, const unsigned int version) { | |
232 | if (version) { | ||
233 | // Do something depending on version ? | ||
234 | } | ||
235 | ✗ | ar& boost::serialization::make_nvp("B_", B_); | |
236 | ✗ | ar& boost::serialization::make_nvp("c_", c_); | |
237 | ✗ | ar& boost::serialization::make_nvp("zero", zero); | |
238 | } | ||
239 | |||
240 | 905 | linear_variable& operator=(const linear_variable& other) { | |
241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 905 times.
|
905 | if (this == &other) { |
242 | ✗ | return *this; | |
243 | } | ||
244 | // Perform a deep copy here to copy all necessary data. | ||
245 | // Make sure to handle memory allocation properly. | ||
246 | // You may need to copy the data contained within the linear_variable. | ||
247 | 905 | this->B_ = other.B_; | |
248 | 905 | this->c_ = other.c_; | |
249 | 905 | this->zero = other.zero; | |
250 | 905 | return *this; | |
251 | } | ||
252 | |||
253 | private: | ||
254 | matrix_x_t B_; | ||
255 | vector_x_t c_; | ||
256 | bool zero; | ||
257 | }; | ||
258 | |||
259 | template <typename N, bool S> | ||
260 | 897 | inline linear_variable<N, S> operator+(const linear_variable<N, S>& w1, | |
261 | const linear_variable<N, S>& w2) { | ||
262 |
1/2✓ Branch 3 taken 897 times.
✗ Branch 4 not taken.
|
897 | linear_variable<N, S> res(w1.B(), w1.c()); |
263 |
2/4✓ Branch 1 taken 897 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 897 times.
✗ Branch 5 not taken.
|
1794 | return res += w2; |
264 | 897 | } | |
265 | |||
266 | template <typename N, bool S> | ||
267 | 16 | linear_variable<N, S> operator-(const linear_variable<N, S>& w1, | |
268 | const linear_variable<N, S>& w2) { | ||
269 |
1/2✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
16 | linear_variable<N, S> res(w1.B(), w1.c()); |
270 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
32 | return res -= w2; |
271 | 16 | } | |
272 | |||
273 | template <typename N, bool S> | ||
274 | ✗ | linear_variable<N, S> operator-(const linear_variable<N, S>& w1) { | |
275 | ✗ | return linear_variable<N, S>(-w1.B(), -w1.c()); | |
276 | } | ||
277 | |||
278 | template <typename N, bool S> | ||
279 | 955 | linear_variable<N, S> operator*(const double k, | |
280 | const linear_variable<N, S>& w) { | ||
281 |
1/2✓ Branch 3 taken 955 times.
✗ Branch 4 not taken.
|
955 | linear_variable<N, S> res(w.B(), w.c()); |
282 |
2/4✓ Branch 1 taken 955 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 955 times.
✗ Branch 5 not taken.
|
1910 | return res *= k; |
283 | 955 | } | |
284 | |||
285 | template <typename N, bool S> | ||
286 | 960 | linear_variable<N, S> operator*(const linear_variable<N, S>& w, | |
287 | const double k) { | ||
288 |
1/2✓ Branch 3 taken 960 times.
✗ Branch 4 not taken.
|
960 | linear_variable<N, S> res(w.B(), w.c()); |
289 |
2/4✓ Branch 1 taken 960 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 960 times.
✗ Branch 5 not taken.
|
1920 | return res *= k; |
290 | 960 | } | |
291 | |||
292 | template <typename N, bool S> | ||
293 | ✗ | linear_variable<N, S> operator/(const linear_variable<N, S>& w, | |
294 | const double k) { | ||
295 | ✗ | linear_variable<N, S> res(w.B(), w.c()); | |
296 | ✗ | return res /= k; | |
297 | } | ||
298 | |||
299 | template <typename BezierFixed, typename BezierLinear, typename X> | ||
300 | 16 | BezierFixed evaluateLinear(const BezierLinear& bIn, const X x) { | |
301 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | typename BezierFixed::t_point_t fixed_wps; |
302 | 16 | for (typename BezierLinear::cit_point_t cit = bIn.waypoints().begin(); | |
303 |
2/2✓ Branch 3 taken 140 times.
✓ Branch 4 taken 16 times.
|
156 | cit != bIn.waypoints().end(); ++cit) |
304 |
3/6✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 140 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 140 times.
✗ Branch 9 not taken.
|
140 | fixed_wps.push_back(cit->operator()(x)); |
305 | 16 | return BezierFixed(fixed_wps.begin(), fixed_wps.end(), bIn.T_min_, | |
306 |
1/2✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
32 | bIn.T_max_); |
307 | 16 | } | |
308 | |||
309 | template <typename N, bool S> | ||
310 | std::ostream& operator<<(std::ostream& os, const linear_variable<N, S>& l) { | ||
311 | return os << "linear_variable: \n \t B:\n" | ||
312 | << l.B() << "\t c: \n" | ||
313 | << l.c().transpose(); | ||
314 | } | ||
315 | |||
316 | } // namespace ndcurves | ||
317 | |||
318 | DEFINE_CLASS_TEMPLATE_VERSION( | ||
319 | SINGLE_ARG(typename Numeric, bool Safe), | ||
320 | SINGLE_ARG(ndcurves::linear_variable<Numeric, Safe>)) | ||
321 | #endif //_CLASS_LINEAR_VARIABLE | ||
322 |