gtsam
Loading...
Searching...
No Matches
FrobeniusFactor.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
20
21#pragma once
22
23#include <gtsam/constrained/QcqpProblem.h>
24#include <gtsam/constrained/QpCost.h>
27#include <gtsam/geometry/Rot2.h>
28#include <gtsam/geometry/Rot3.h>
29#include <gtsam/geometry/SOn.h>
32
33#include <memory>
34#include <stdexcept>
35#include <type_traits>
36#include <vector>
37
38namespace gtsam {
39
59 size_t n,
60 bool defaultToUnit = true);
61
75template <class T, size_t Dim>
77 if (!model) {
79 }
80 if (model->dim() == Dim) {
81 return model;
82 }
83 if (model->dim() != T::dimension) {
84 throw std::runtime_error(
85 "Noise model dimension does not match expected dimension for T.");
86 }
87 return ConvertNoiseModel(model, Dim);
88}
89
90template <class T>
91using FrobeniusErrorVector = Eigen::Matrix<
92 double, T::LieAlgebra::RowsAtCompileTime * T::LieAlgebra::RowsAtCompileTime,
93 1>;
94
99template <class T>
101 GTSAM_CONCEPT_ASSERT(IsMatrixLieGroup<T>);
102 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
103 inline constexpr static auto Dim = N * N;
104
105 public:
106 using MatrixN = Eigen::Matrix<double, N, N>;
107
108 private:
109 Eigen::Matrix<double, Dim, 1> vecM_;
110
111 public:
112 // Provide access to the Matrix& version of evaluateError:
113 using NoiseModelFactor1<T>::evaluateError;
114
116 FrobeniusPrior(Key j, const MatrixN& M,
117 const SharedNoiseModel& model = nullptr)
118 : NoiseModelFactorN<T>(ConvertModel<T, Dim>(model), j) {
119 vecM_ << Eigen::Map<const Matrix>(M.data(), Dim, 1);
120 }
121
123 MatrixN priorMatrix() const {
124 return Eigen::Map<const MatrixN>(vecM_.data());
125 }
126
128 Vector evaluateError(const T& g, OptionalMatrixType H) const override {
129 return traits<T>::Vec(g, H) -
130 vecM_; // Jacobian is computed only when needed.
131 }
132
142 NonlinearEqualityConstraints* constraints,
143 size_t columnDimension = 1) const override {
144 if (columnDimension == 0) {
145 throw std::invalid_argument(
146 "FrobeniusPrior::qcqpFactors: columnDimension must be >= 1");
147 }
148 if (columnDimension == 1) {
149 qcqpFactorsForVec(costs, constraints);
150 } else {
151 qcqpFactorsForMatrix(costs, constraints, columnDimension);
152 }
153 }
154
155 private:
160 void qcqpFactorsForVec(NonlinearFactorGraph* costs,
161 NonlinearEqualityConstraints* constraints) const {
162 if constexpr (!internal::HasQcqpVariableTraits<T, 1>::value) {
163 (void)costs;
164 (void)constraints;
165 throw std::runtime_error(
166 "FrobeniusPrior::qcqpFactors requires QCQP variable traits for this "
167 "type and column dimension 1.");
168 } else if constexpr (!(std::is_same_v<T, Rot2> || std::is_same_v<T, Rot3> ||
169 std::is_same_v<T, Pose2> ||
170 std::is_same_v<T, Pose3>)) {
171 (void)costs;
172 (void)constraints;
173 throw std::runtime_error(
174 "FrobeniusPrior::qcqpFactors D=1 is implemented only for Rot2, "
175 "Rot3, Pose2, and Pose3.");
176 } else {
177 (void)costs;
178 if (this->noiseModel_->isConstrained()) {
179 InsertQcqpConstraints<T, 1>(this->key(), constraints);
180
181 constexpr int LiftedDim = traits<T>::QcqpVectorDim;
182 Vector target = Vector::Zero(LiftedDim);
183 target(0) = 1.0;
184 if constexpr (std::is_same_v<T, Rot2>) {
185 // vec(R)=[c,s,-s,c], so the compact lift retains its first column.
186 target.segment<2>(1) = vecM_.template head<2>();
187 } else {
188 static_assert((LiftedDim - 1) % N == 0);
189 constexpr int M = (LiftedDim - 1) / N;
190 // Build the homogeneous target while omitting fixed pose-matrix rows.
191 for (int column = 0; column < N; ++column) {
192 target.segment<M>(1 + column * M) =
193 vecM_.template segment<M>(column * N);
194 }
195 }
196
197 constraints->push_back(
198 LinearConstraint::Equal(
199 JacobianFactor(this->key(),
200 Matrix::Identity(LiftedDim, LiftedDim), target))
201 .createEqualityFactor());
202 } else {
203 throw std::runtime_error(
204 "FrobeniusPrior::qcqpFactors D=1 non-constrained noise is not yet "
205 "implemented.");
206 }
207 }
208 }
209
215 void qcqpFactorsForMatrix(NonlinearFactorGraph* costs,
216 NonlinearEqualityConstraints* constraints,
217 size_t columnDimension) const {
218 (void)costs;
219 (void)constraints;
220 (void)columnDimension;
221 throw std::runtime_error(
222 "FrobeniusPrior::qcqpFactors does not support matrix-form priors; "
223 "a Burer--Monteiro-compatible prior requires an anchor block.");
224 }
225};
226
231template <class T>
233 : public NoiseModelFactorT<FrobeniusErrorVector<T>, T, T> {
234 GTSAM_CONCEPT_ASSERT(IsMatrixLieGroup<T>);
235 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
236 inline constexpr static auto Dim = N * N;
238 using VectorD = FrobeniusErrorVector<T>;
239
240 public:
241 // Provide access to the Matrix& version of evaluateError:
243
245 FrobeniusFactor(Key j1, Key j2, const SharedNoiseModel& model = nullptr)
246 : Base(ConvertModel<T, Dim>(model), j1, j2) {}
247
249 VectorD evaluateError(const T& T1, const T& T2, OptionalMatrixType H1,
250 OptionalMatrixType H2) const override {
251 VectorD error = traits<T>::Vec(T2, H2) - traits<T>::Vec(T1, H1);
252 if (H1) *H1 = -*H1;
253 return error;
254 }
255};
256
268template <class T>
270 : public NoiseModelFactorT<FrobeniusErrorVector<T>, T, T> {
271 GTSAM_CONCEPT_ASSERT(IsMatrixLieGroup<T>);
272 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
273 inline constexpr static auto Dim = N * N;
274 static_assert(N > 0, "The Lie algebra dimension N must be greater than 0.");
275
276 protected:
278
279 using MatrixN = Eigen::Matrix<double, N, N>;
280 using VectorD = Eigen::Matrix<double, Dim, 1>;
282
283 public:
284 // Provide access to the Matrix& version of evaluateError:
286
289
291 FrobeniusBetweenFactorNL(Key j1, Key j2, const T& T12,
292 const SharedNoiseModel& model = nullptr)
293 : Base(ConvertModel<T, Dim>(model), j1, j2), T12_(T12) {}
294
296 const T& measured() const { return T12_; }
297
301
303 void print(const std::string& s, const KeyFormatter& keyFormatter =
304 DefaultKeyFormatter) const override {
305 std::cout << s << "FrobeniusBetweenFactorNL<" << demangle(typeid(T).name())
306 << ">(" << keyFormatter(this->key1()) << ","
307 << keyFormatter(this->key2()) << ")\n";
308 traits<T>::Print(T12_, " T12: ");
309 this->noiseModel_->print(" noise model: ");
310 }
311
313 bool equals(const NonlinearFactor& expected,
314 double tol = 1e-9) const override {
315 const auto* e = dynamic_cast<const FrobeniusBetweenFactorNL*>(&expected);
316 return e != nullptr && Base::equals(*e, tol) &&
317 traits<T>::Equals(this->T12_, e->T12_, tol);
318 }
319
323
325 VectorD evaluateError(const T& T1, const T& T2, OptionalMatrixType H1,
326 OptionalMatrixType H2) const override {
327 const bool computeJacobians = H1 || H2;
328
329 // Compute the predicted inverse-relative transform inv(T2) * T1.
330 typename T::Jacobian H_T21_T2;
331 const T hatT21 =
332 traits<T>::Between(T2, T1, computeJacobians ? &H_T21_T2 : nullptr);
333
334 // Compose with the measurement; the result should be identity.
335 typename T::Jacobian H_pred_hat = T::Jacobian::Zero();
336 const T pred = traits<T>::Compose(hatT21, T12_,
337 computeJacobians ? &H_pred_hat : nullptr);
338
339 // Cache the fixed-size vectorization of the identity matrix.
340 static const VectorD vecI = [] {
341 const MatrixN I = MatrixN::Identity();
342 return VectorD(Eigen::Map<const VectorD>(I.data()));
343 }();
344
345 // Vectorize the residual and retain its derivative for the chain rule.
346 Eigen::Matrix<double, Dim, T::dimension> H_vec_pred;
347 VectorD error =
348 traits<T>::Vec(pred, computeJacobians ? &H_vec_pred : nullptr) - vecI;
349
350 // Propagate derivatives through Between and Compose.
351 if (computeJacobians) {
352 const auto H_error_hat21 = H_vec_pred * H_pred_hat;
353 if (H1) *H1 = H_error_hat21; // H_pred_T1 is identity
354 if (H2) *H2 = H_error_hat21 * H_T21_T2;
355 }
356 return error;
357 }
358
359};
360
365template <class T>
367 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
368 inline constexpr static auto Dim = N * N;
369 using Base = FrobeniusBetweenFactorNL<T>;
370 using MatrixN = typename Base::MatrixN;
371 using VectorD = FrobeniusErrorVector<T>;
372
373 typename T::Jacobian T2hat_H_T1_;
374
375 public:
376 // Provide access to the Matrix& version of evaluateError:
378
380 FrobeniusBetweenFactor(Key j1, Key j2, const T& T12,
381 const SharedNoiseModel& model = nullptr)
382 : FrobeniusBetweenFactorNL<T>(j1, j2, T12, model),
383 T2hat_H_T1_(traits<T>::AdjointMap(traits<T>::Inverse(T12))) {}
384
386 VectorD evaluateError(const T& T1, const T& T2, OptionalMatrixType H1,
387 OptionalMatrixType H2) const override {
388 const T T2hat = traits<T>::Compose(T1, this->T12_);
389 Eigen::Matrix<double, Dim, T::dimension> vec_H_T2hat;
390 VectorD error = traits<T>::Vec(T2, H2) -
391 traits<T>::Vec(T2hat, H1 ? &vec_H_T2hat : nullptr);
392 if (H1) *H1 = -vec_H_T2hat * T2hat_H_T1_;
393 return error;
394 }
395
404 NonlinearEqualityConstraints* constraints,
405 size_t columnDimension = 1) const override {
406 if (columnDimension == 0) {
407 throw std::invalid_argument(
408 "FrobeniusBetweenFactor::qcqpFactors: columnDimension must be >= 1");
409 }
410 if (columnDimension == 1) {
411 qcqpFactorsForVec(costs, constraints);
412 } else {
413 qcqpFactorsForMatrix(costs, constraints, columnDimension);
414 }
415 }
416
417 private:
420 static Matrix internalKroneckerProduct(const Matrix& left,
421 const Matrix& right) {
422 const DenseIndex resultRows = left.rows() * right.rows();
423 const DenseIndex resultColumns = left.cols() * right.cols();
424 Matrix result = Matrix::Zero(resultRows, resultColumns);
425
426 for (DenseIndex row = 0; row < left.rows(); ++row) {
427 for (DenseIndex column = 0; column < left.cols(); ++column) {
428 result.block(row * right.rows(), column * right.cols(), right.rows(),
429 right.cols()) = left(row, column) * right;
430 }
431 }
432 return result;
433 }
434
437 void qcqpFactorsForVec(NonlinearFactorGraph* costs,
438 NonlinearEqualityConstraints* constraints) const {
439 if constexpr (!internal::HasQcqpVariableTraits<T, 1>::value) {
440 (void)costs;
441 (void)constraints;
442 throw std::runtime_error(
443 "FrobeniusBetweenFactor::qcqpFactors requires QCQP variable traits "
444 "for this type and column dimension 1.");
445 } else if constexpr (!(std::is_same_v<T, Rot2> || std::is_same_v<T, Rot3> ||
446 std::is_same_v<T, Pose2> ||
447 std::is_same_v<T, Pose3>)) {
448 (void)costs;
449 (void)constraints;
450 throw std::runtime_error(
451 "FrobeniusBetweenFactor::qcqpFactors D=1 is implemented only for "
452 "Rot2, Rot3, Pose2, and Pose3.");
453 } else {
454 if (!costs) {
455 throw std::invalid_argument(
456 "FrobeniusBetweenFactor::qcqpFactors costs is null");
457 }
458 if (std::dynamic_pointer_cast<noiseModel::Robust>(this->noiseModel_) ||
459 this->noiseModel_->isConstrained()) {
460 throw std::runtime_error(
461 "FrobeniusBetweenFactor::qcqpFactors requires a "
462 "non-robust/non-hard quadratic noise model");
463 }
464
465 constexpr int LiftedDim = traits<T>::QcqpVectorDim;
466 Matrix Q_trunc_hom;
467 if constexpr (std::is_same_v<T, Rot2>) {
468 // vec(R)=L[c,s]' exactly. Since SO(2) is abelian,
469 // vec(R2-R1*M)=L(q2-M*q1).
470 Matrix L(4, 2);
471 L << 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 1.0, 0.0;
472 Matrix fullB = Matrix::Zero(4, 2 * LiftedDim);
473 fullB.block<4, 2>(0, 1) = -L * this->T12_.matrix();
474 fullB.block<4, 2>(0, LiftedDim + 1) = L;
475 const Matrix whitenedB = this->noiseModel_->Whiten(fullB);
476 Q_trunc_hom = whitenedB.transpose() * whitenedB;
477 } else {
478 constexpr int MN = LiftedDim - 1;
479 static_assert(MN % N == 0);
480 constexpr int M = MN / N;
481 using MatrixM = Eigen::Matrix<double, M, M>;
482
483 const MatrixN measurement = this->T12_.matrix();
484 const MatrixM I_M = MatrixM::Identity();
485 // Column-major vec(XM) = (M.transpose() kron I) vec(X).
486 const Matrix A = internalKroneckerProduct(measurement.transpose(), I_M);
487
488 Matrix B = Matrix::Zero(MN, 2 * MN);
489 B.block<MN, MN>(0, 0) = -A;
490 B.block<MN, MN>(0, MN).setIdentity();
491
492 Matrix fullB = Matrix::Zero(Dim, 2 * MN);
493 for (int column = 0; column < N; ++column) {
494 fullB.block<M, 2 * MN>(column * N, 0) =
495 B.block<M, 2 * MN>(column * M, 0);
496 }
497
498 const Matrix whitenedB = this->noiseModel_->Whiten(fullB);
499 const Matrix Q = whitenedB.transpose() * whitenedB;
500 Q_trunc_hom = Matrix::Zero(2 * LiftedDim, 2 * LiftedDim);
501 Q_trunc_hom.block<MN, MN>(1, 1) = Q.block<MN, MN>(0, 0);
502 Q_trunc_hom.block<MN, MN>(1, LiftedDim + 1) = Q.block<MN, MN>(0, MN);
503 Q_trunc_hom.block<MN, MN>(LiftedDim + 1, 1) = Q.block<MN, MN>(MN, 0);
504 Q_trunc_hom.block<MN, MN>(LiftedDim + 1, LiftedDim + 1) =
505 Q.block<MN, MN>(MN, MN);
506 }
507
508 InsertQcqpConstraints<T, 1>(this->key1(), constraints);
509 InsertQcqpConstraints<T, 1>(this->key2(), constraints);
510
511 const SymmetricBlockMatrix blockQ(
512 std::vector<DenseIndex>{LiftedDim, LiftedDim}, Q_trunc_hom);
513 costs->push_back(std::make_shared<QpCost>(
514 KeyVector{this->key1(), this->key2()}, blockQ));
515 }
516 }
517
519 void qcqpFactorsForMatrix(NonlinearFactorGraph* costs,
520 NonlinearEqualityConstraints* constraints,
521 size_t columnDimension) const {
522 if constexpr (!internal::HasQcqpVariableTraits<T, N>::value) {
523 (void)costs;
524 (void)constraints;
525 (void)columnDimension;
526 throw std::runtime_error(
527 "FrobeniusBetweenFactor::qcqpFactors requires QCQP variable traits "
528 "for this type and matrix-form column dimensions (>= 2).");
529 } else {
530 if (columnDimension < static_cast<size_t>(N)) {
531 throw std::invalid_argument(
532 "FrobeniusBetweenFactor::qcqpFactors: columnDimension must be "
533 ">= the variable's intrinsic row dimension (e.g. >= 3 for Rot3).");
534 }
535 if (!costs) {
536 throw std::invalid_argument(
537 "FrobeniusBetweenFactor::qcqpFactors costs is null");
538 }
539 if (std::dynamic_pointer_cast<noiseModel::Robust>(this->noiseModel_) ||
540 this->noiseModel_->isConstrained()) {
541 throw std::runtime_error(
542 "FrobeniusBetweenFactor::qcqpFactors requires a "
543 "non-robust quadratic noise model");
544 }
545 const auto isotropic =
546 std::dynamic_pointer_cast<noiseModel::Isotropic>(this->noiseModel_);
547 if (!isotropic) {
548 throw std::runtime_error(
549 "FrobeniusBetweenFactor::qcqpFactors with column dimension > 1 "
550 "requires an isotropic noise model");
551 }
552
553 InsertQcqpConstraints<T, N>(this->key1(), constraints);
554 InsertQcqpConstraints<T, N>(this->key2(), constraints);
555
556 const MatrixN measurement = this->T12_.matrix();
557 const MatrixN I = MatrixN::Identity();
558 const double weight = 1.0 / (isotropic->sigma() * isotropic->sigma());
559
560 // Build B = [-M' I] and its isotropically weighted Hessian.
561 Matrix B = Matrix::Zero(N, 2 * N);
562 B.block<N, N>(0, 0) = -measurement.transpose();
563 B.block<N, N>(0, N) = I;
564
565 const Matrix Q = weight * B.transpose() * B;
566 const SymmetricBlockMatrix blockQ(std::vector<DenseIndex>{N, N}, Q);
567 costs->push_back(std::make_shared<QpCost>(
568 KeyVector{this->key1(), this->key2()}, blockQ, columnDimension));
569 }
570 }
571};
572
580template <class T>
582 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
583 inline constexpr static auto Dim = N * N;
584 using Base = FrobeniusBetweenFactorNL<T>;
586 using MatrixN = typename Base::MatrixN;
587 using VectorD = FrobeniusErrorVector<T>;
588
589 public:
590 // Provide access to the Matrix& version of evaluateError:
592
597 FrobeniusLeftBetweenFactor(Key j1, Key j2, const T& iTj,
598 const SharedNoiseModel& model = nullptr)
599 : Base(j1, j2, iTj, ConvertLeftModel(model)) {}
600
601 NonlinearFactor::shared_ptr clone() const override {
602 return std::make_shared<This>(*this);
603 }
604
606 void print(const std::string& s, const KeyFormatter& keyFormatter =
607 DefaultKeyFormatter) const override {
608 std::cout << s << "FrobeniusLeftBetweenFactor<"
609 << demangle(typeid(T).name()) << ">("
610 << keyFormatter(this->key1()) << "," << keyFormatter(this->key2())
611 << ")\n";
612 traits<T>::Print(this->T12_, " measured iTj: ");
613 this->noiseModel_->print(" noise model: ");
614 }
615
617 bool equals(const NonlinearFactor& expected,
618 double tol = 1e-9) const override {
619 const auto* e = dynamic_cast<const FrobeniusLeftBetweenFactor*>(&expected);
620 return e != nullptr && Base::equals(*e, tol);
621 }
622
628 VectorD evaluateError(const T& iTw, const T& jTw, OptionalMatrixType H1,
629 OptionalMatrixType H2) const override {
630 typename T::Jacobian predicted_H_jTw;
631 const T predicted_iTw = traits<T>::Compose(this->T12_, jTw, {},
632 H2 ? &predicted_H_jTw : nullptr);
633
634 Eigen::Matrix<double, Dim, T::dimension> vec_H_predicted;
635 VectorD error =
636 traits<T>::Vec(iTw, H1) -
637 traits<T>::Vec(predicted_iTw, H2 ? &vec_H_predicted : nullptr);
638 if (H2) *H2 = -vec_H_predicted * predicted_H_jTw;
639 return error;
640 }
641
646 NonlinearEqualityConstraints* constraints,
647 size_t columnDimension = 1) const override {
648 if (columnDimension != 1) {
649 throw std::invalid_argument(
650 "FrobeniusLeftBetweenFactor::qcqpFactors only supports column "
651 "dimension 1");
652 }
653 qcqpFactorsForVec(costs, constraints);
654 }
655
656 private:
657 static SharedNoiseModel ConvertLeftModel(const SharedNoiseModel& model) {
658 if (!model || model->dim() != T::dimension) return model;
659
660 try {
661 return ConvertNoiseModel(model, Dim, false);
662 } catch (const std::runtime_error&) {
663 throw std::invalid_argument(
664 "FrobeniusLeftBetweenFactor cannot convert an anisotropic "
665 "manifold-dimensional noise model to the ambient residual");
666 }
667 }
668
670 static Matrix internalKroneckerProduct(const Matrix& left,
671 const Matrix& right) {
672 const DenseIndex resultRows = left.rows() * right.rows();
673 const DenseIndex resultColumns = left.cols() * right.cols();
674 Matrix result = Matrix::Zero(resultRows, resultColumns);
675
676 for (DenseIndex row = 0; row < left.rows(); ++row) {
677 for (DenseIndex column = 0; column < left.cols(); ++column) {
678 result.block(row * right.rows(), column * right.cols(), right.rows(),
679 right.cols()) = left(row, column) * right;
680 }
681 }
682 return result;
683 }
684
686 void qcqpFactorsForVec(NonlinearFactorGraph* costs,
687 NonlinearEqualityConstraints* constraints) const {
688 if constexpr (!internal::HasQcqpVariableTraits<T, 1>::value) {
689 (void)costs;
690 (void)constraints;
691 throw std::runtime_error(
692 "FrobeniusLeftBetweenFactor::qcqpFactors requires QCQP variable "
693 "traits for this type and column dimension 1.");
694 } else if constexpr (!(std::is_same_v<T, Rot2> || std::is_same_v<T, Rot3> ||
695 std::is_same_v<T, Pose2> ||
696 std::is_same_v<T, Pose3>)) {
697 (void)costs;
698 (void)constraints;
699 throw std::runtime_error(
700 "FrobeniusLeftBetweenFactor::qcqpFactors D=1 is implemented only "
701 "for Rot2, Rot3, Pose2, and Pose3.");
702 } else {
703 if (!costs) {
704 throw std::invalid_argument(
705 "FrobeniusLeftBetweenFactor::qcqpFactors costs is null");
706 }
707 if (std::dynamic_pointer_cast<noiseModel::Robust>(this->noiseModel_) ||
708 this->noiseModel_->isConstrained()) {
709 throw std::runtime_error(
710 "FrobeniusLeftBetweenFactor::qcqpFactors requires a "
711 "non-robust/non-hard quadratic noise model");
712 }
713
714 constexpr int LiftedDim = traits<T>::QcqpVectorDim;
715 Matrix Q;
716 if constexpr (std::is_same_v<T, Rot2>) {
717 Matrix L(4, 2);
718 L << 1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 1.0, 0.0;
719 Matrix fullB = Matrix::Zero(4, 2 * LiftedDim);
720 fullB.block<4, 2>(0, 1) = L;
721 fullB.block<4, 2>(0, LiftedDim + 1) = -L * this->T12_.matrix();
722 const Matrix whitenedB = this->noiseModel_->Whiten(fullB);
723 Q = whitenedB.transpose() * whitenedB;
724 } else {
725 constexpr int MN = LiftedDim - 1;
726 static_assert(MN % N == 0);
727 constexpr int M = MN / N;
728 using MatrixM = Eigen::Matrix<double, M, M>;
729
730 const MatrixN iTj = this->T12_.matrix();
731 const MatrixM linearPart = iTj.template topLeftCorner<M, M>();
732 const Matrix leftAction =
733 internalKroneckerProduct(MatrixN::Identity(), linearPart);
734
735 Vector offset = Vector::Zero(MN);
736 if constexpr (M < N) {
737 offset.segment((N - 1) * M, M) = iTj.block(0, N - 1, M, 1);
738 }
739
740 Matrix retainedB = Matrix::Zero(MN, 2 * LiftedDim);
741 retainedB.block(0, 1, MN, MN).setIdentity();
742 retainedB.col(LiftedDim) = -offset;
743 retainedB.block(0, LiftedDim + 1, MN, MN) = -leftAction;
744
745 Matrix fullB = Matrix::Zero(Dim, 2 * LiftedDim);
746 for (int column = 0; column < N; ++column) {
747 fullB.block(column * N, 0, M, 2 * LiftedDim) =
748 retainedB.block(column * M, 0, M, 2 * LiftedDim);
749 }
750
751 const Matrix whitenedB = this->noiseModel_->Whiten(fullB);
752 Q = whitenedB.transpose() * whitenedB;
753 }
754
755 InsertQcqpConstraints<T, 1>(this->key1(), constraints);
756 InsertQcqpConstraints<T, 1>(this->key2(), constraints);
757
758 const SymmetricBlockMatrix blockQ(
759 std::vector<DenseIndex>{LiftedDim, LiftedDim}, Q);
760 costs->push_back(std::make_shared<QpCost>(
761 KeyVector{this->key1(), this->key2()}, blockQ));
762 }
763 }
764};
765
766} // namespace gtsam
N*N matrix representation of SO(N).
2D rotation
3D Pose manifold SO(3) x R^3 and group SE(3)
2D Pose
3D rotation represented as a rotation matrix or quaternion
Base class for noise model factors with N variables.
Non-linear factor base classes.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
KeyFormatter DefaultKeyFormatter
Assign default key formatter.
Definition Key.cpp:30
std::string demangle(const char *name)
Pretty print Value type name.
Definition types.cpp:37
ptrdiff_t DenseIndex
The index type for Eigen objects.
Definition types.h:49
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition Key.h:91
Matrix * OptionalMatrixType
This typedef will be used everywhere boost::optional<Matrix&> reference was used previously.
Definition NonlinearFactor.h:57
NoiseModelFactorT< Vector, ValueTypes... > NoiseModelFactorN
Noise model factor with N value types and dynamic-sized error vector.
Definition NoiseModelFactorN.h:561
std::function< std::string(Key)> KeyFormatter
Typedef for a function to format a key, i.e. to convert it to a string.
Definition Key.h:35
SharedNoiseModel ConvertNoiseModel(const SharedNoiseModel &model, size_t dimension, bool defaultToUnit)
Convert a possibly robust noise model to an isotropic model for Frobenius factors.
Definition FrobeniusFactor.cpp:25
noiseModel::Base::shared_ptr SharedNoiseModel
Aliases.
Definition NoiseModel.h:846
std::uint64_t Key
Integer nonlinear key type.
Definition types.h:43
SharedNoiseModel ConvertModel(const SharedNoiseModel &model)
Ensure a noise model has the correct dimension for a given type.
Definition FrobeniusFactor.h:76
A manifold defines a space in which there is a notion of a linear tangent space that can be centered ...
Definition Group.h:37
Matrix Lie Group Concept.
Definition MatrixLieGroup.h:358
bool equals(const This &other, double tol=1e-9) const
check equality
Definition Factor.cpp:42
static shared_ptr Create(size_t dim)
Create a unit covariance noise model.
Definition NoiseModel.h:673
NoiseModelFactorT()
Definition NoiseModelFactorN.h:257
virtual FrobeniusErrorVector< T > evaluateError(const ValueTypes &... x, OptionalMatrixTypeT< ValueTypes >... H) const=0
Key key() const
Definition NoiseModelFactorN.h:307
Nonlinear factor base class.
Definition NonlinearFactor.h:70
size_t dim() const override
get the dimension of the factor (number of rows on linearization)
Definition NonlinearFactor.h:251
double weight(const Values &c) const
Compute the effective weight of the factor from the noise model.
Definition NonlinearFactor.cpp:131
double error(const Values &c) const override
Calculate the error of the factor.
Definition NonlinearFactor.cpp:146
Definition NonlinearFactorGraph.h:57
Vector evaluateError(const T &g, OptionalMatrixType H) const override
Error is just Frobenius norm between T element and vectorized matrix M.
Definition FrobeniusFactor.h:128
MatrixN priorMatrix() const
Return the fixed ambient matrix targeted by this prior.
Definition FrobeniusFactor.h:123
void qcqpFactors(NonlinearFactorGraph *costs, NonlinearEqualityConstraints *constraints, size_t columnDimension=1) const override
Add this Frobenius prior to the QCQP graph.
Definition FrobeniusFactor.h:141
FrobeniusPrior(Key j, const MatrixN &M, const SharedNoiseModel &model=nullptr)
Constructor.
Definition FrobeniusFactor.h:116
FrobeniusFactor(Key j1, Key j2, const SharedNoiseModel &model=nullptr)
Constructor.
Definition FrobeniusFactor.h:245
VectorD evaluateError(const T &T1, const T &T2, OptionalMatrixType H1, OptionalMatrixType H2) const override
Error is the vectorized matrix difference between the two group elements.
Definition FrobeniusFactor.h:249
FrobeniusBetweenFactorNL(Key j1, Key j2, const T &T12, const SharedNoiseModel &model=nullptr)
Construct from two keys and a measured transformation.
Definition FrobeniusFactor.h:291
VectorD evaluateError(const T &T1, const T &T2, OptionalMatrixType H1, OptionalMatrixType H2) const override
Error is |inv(T2)*T1*T12_ - I|_F.
Definition FrobeniusFactor.h:325
T T12_
measured transformation between T1 and T2
Definition FrobeniusFactor.h:277
const T & measured() const
Return the measured transformation from the first key to the second.
Definition FrobeniusFactor.h:296
bool equals(const NonlinearFactor &expected, double tol=1e-9) const override
assert equality up to a tolerance
Definition FrobeniusFactor.h:313
void print(const std::string &s, const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
print with optional string
Definition FrobeniusFactor.h:303
FrobeniusBetweenFactor(Key j1, Key j2, const T &T12, const SharedNoiseModel &model=nullptr)
Construct from two keys and a measured transformation.
Definition FrobeniusFactor.h:380
void qcqpFactors(NonlinearFactorGraph *costs, NonlinearEqualityConstraints *constraints, size_t columnDimension=1) const override
Add this Frobenius between factor as a QCQP cost when traits exist.
Definition FrobeniusFactor.h:403
VectorD evaluateError(const T &T1, const T &T2, OptionalMatrixType H1, OptionalMatrixType H2) const override
Error is Frobenius norm between T1*T12 and T2.
Definition FrobeniusFactor.h:386
void print(const std::string &s, const KeyFormatter &keyFormatter=DefaultKeyFormatter) const override
Print the factor and its measured transformation.
Definition FrobeniusFactor.h:606
bool equals(const NonlinearFactor &expected, double tol=1e-9) const override
Check equality with another left-composed factor.
Definition FrobeniusFactor.h:617
void qcqpFactors(NonlinearFactorGraph *costs, NonlinearEqualityConstraints *constraints, size_t columnDimension=1) const override
Add the exact D=1 homogeneous left-composed between cost to a QCQP.
Definition FrobeniusFactor.h:645
NonlinearFactor::shared_ptr clone() const override
Creates a shared_ptr clone of the factor - needs to be specialized to allow for subclasses.
Definition FrobeniusFactor.h:601
FrobeniusLeftBetweenFactor(Key j1, Key j2, const T &iTj, const SharedNoiseModel &model=nullptr)
Construct from two keys and a measured left-composed transformation.
Definition FrobeniusFactor.h:597
VectorD evaluateError(const T &iTw, const T &jTw, OptionalMatrixType H1, OptionalMatrixType H2) const override
Evaluate iTw - iTj*jTw.
Definition FrobeniusFactor.h:628