23#include <gtsam/constrained/QcqpProblem.h>
24#include <gtsam/constrained/QpCost.h>
60 bool defaultToUnit =
true);
75template <
class T,
size_t Dim>
80 if (model->dim() == Dim) {
83 if (model->dim() != T::dimension) {
84 throw std::runtime_error(
85 "Noise model dimension does not match expected dimension for T.");
91using FrobeniusErrorVector = Eigen::Matrix<
92 double, T::LieAlgebra::RowsAtCompileTime * T::LieAlgebra::RowsAtCompileTime,
102 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
103 inline constexpr static auto Dim = N * N;
106 using MatrixN = Eigen::Matrix<double, N, N>;
109 Eigen::Matrix<double, Dim, 1> vecM_;
119 vecM_ << Eigen::Map<const Matrix>(M.data(), Dim, 1);
124 return Eigen::Map<const MatrixN>(vecM_.data());
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");
148 if (columnDimension == 1) {
149 qcqpFactorsForVec(costs, constraints);
151 qcqpFactorsForMatrix(costs, constraints, columnDimension);
161 NonlinearEqualityConstraints* constraints)
const {
162 if constexpr (!internal::HasQcqpVariableTraits<T, 1>::value) {
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>)) {
173 throw std::runtime_error(
174 "FrobeniusPrior::qcqpFactors D=1 is implemented only for Rot2, "
175 "Rot3, Pose2, and Pose3.");
178 if (this->noiseModel_->isConstrained()) {
179 InsertQcqpConstraints<T, 1>(this->
key(), constraints);
181 constexpr int LiftedDim = traits<T>::QcqpVectorDim;
182 Vector target = Vector::Zero(LiftedDim);
184 if constexpr (std::is_same_v<T, Rot2>) {
186 target.segment<2>(1) = vecM_.template head<2>();
188 static_assert((LiftedDim - 1) % N == 0);
189 constexpr int M = (LiftedDim - 1) / N;
191 for (
int column = 0; column < N; ++column) {
192 target.segment<M>(1 + column * M) =
193 vecM_.template segment<M>(column * N);
197 constraints->push_back(
198 LinearConstraint::Equal(
199 JacobianFactor(this->
key(),
200 Matrix::Identity(LiftedDim, LiftedDim), target))
201 .createEqualityFactor());
203 throw std::runtime_error(
204 "FrobeniusPrior::qcqpFactors D=1 non-constrained noise is not yet "
215 void qcqpFactorsForMatrix(NonlinearFactorGraph* costs,
216 NonlinearEqualityConstraints* constraints,
217 size_t columnDimension)
const {
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.");
235 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
236 inline constexpr static auto Dim = N * N;
238 using VectorD = FrobeniusErrorVector<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.");
279 using MatrixN = Eigen::Matrix<double, N, N>;
280 using VectorD = Eigen::Matrix<double, Dim, 1>;
305 std::cout << s <<
"FrobeniusBetweenFactorNL<" <<
demangle(
typeid(T).name())
306 <<
">(" << keyFormatter(this->key1()) <<
","
307 << keyFormatter(this->key2()) <<
")\n";
309 this->noiseModel_->print(
" noise model: ");
314 double tol = 1e-9)
const override {
327 const bool computeJacobians = H1 || H2;
330 typename T::Jacobian H_T21_T2;
335 typename T::Jacobian H_pred_hat = T::Jacobian::Zero();
337 computeJacobians ? &H_pred_hat :
nullptr);
340 static const VectorD vecI = [] {
341 const MatrixN I = MatrixN::Identity();
342 return VectorD(Eigen::Map<const VectorD>(I.data()));
346 Eigen::Matrix<double, Dim, T::dimension> H_vec_pred;
348 traits<T>::Vec(pred, computeJacobians ? &H_vec_pred :
nullptr) - vecI;
351 if (computeJacobians) {
352 const auto H_error_hat21 = H_vec_pred * H_pred_hat;
353 if (H1) *H1 = H_error_hat21;
354 if (H2) *H2 = H_error_hat21 * H_T21_T2;
367 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
368 inline constexpr static auto Dim = N * N;
370 using MatrixN =
typename Base::MatrixN;
371 using VectorD = FrobeniusErrorVector<T>;
373 typename T::Jacobian T2hat_H_T1_;
383 T2hat_H_T1_(
traits<T>::AdjointMap(
traits<T>::Inverse(T12))) {}
389 Eigen::Matrix<double, Dim, T::dimension> vec_H_T2hat;
392 if (H1) *H1 = -vec_H_T2hat * T2hat_H_T1_;
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");
410 if (columnDimension == 1) {
411 qcqpFactorsForVec(costs, constraints);
413 qcqpFactorsForMatrix(costs, constraints, columnDimension);
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);
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;
437 void qcqpFactorsForVec(NonlinearFactorGraph* costs,
438 NonlinearEqualityConstraints* constraints)
const {
439 if constexpr (!internal::HasQcqpVariableTraits<T, 1>::value) {
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>)) {
450 throw std::runtime_error(
451 "FrobeniusBetweenFactor::qcqpFactors D=1 is implemented only for "
452 "Rot2, Rot3, Pose2, and Pose3.");
455 throw std::invalid_argument(
456 "FrobeniusBetweenFactor::qcqpFactors costs is null");
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");
465 constexpr int LiftedDim = traits<T>::QcqpVectorDim;
467 if constexpr (std::is_same_v<T, Rot2>) {
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;
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>;
483 const MatrixN measurement = this->
T12_.matrix();
484 const MatrixM I_M = MatrixM::Identity();
486 const Matrix A = internalKroneckerProduct(measurement.transpose(), I_M);
488 Matrix B = Matrix::Zero(MN, 2 * MN);
489 B.block<MN, MN>(0, 0) = -A;
490 B.block<MN, MN>(0, MN).setIdentity();
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);
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);
508 InsertQcqpConstraints<T, 1>(this->key1(), constraints);
509 InsertQcqpConstraints<T, 1>(this->key2(), constraints);
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));
519 void qcqpFactorsForMatrix(NonlinearFactorGraph* costs,
520 NonlinearEqualityConstraints* constraints,
521 size_t columnDimension)
const {
522 if constexpr (!internal::HasQcqpVariableTraits<T, N>::value) {
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).");
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).");
536 throw std::invalid_argument(
537 "FrobeniusBetweenFactor::qcqpFactors costs is null");
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");
545 const auto isotropic =
546 std::dynamic_pointer_cast<noiseModel::Isotropic>(this->noiseModel_);
548 throw std::runtime_error(
549 "FrobeniusBetweenFactor::qcqpFactors with column dimension > 1 "
550 "requires an isotropic noise model");
553 InsertQcqpConstraints<T, N>(this->key1(), constraints);
554 InsertQcqpConstraints<T, N>(this->key2(), constraints);
556 const MatrixN measurement = this->
T12_.matrix();
557 const MatrixN I = MatrixN::Identity();
558 const double weight = 1.0 / (isotropic->sigma() * isotropic->sigma());
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;
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));
582 inline constexpr static auto N = T::LieAlgebra::RowsAtCompileTime;
583 inline constexpr static auto Dim = N * N;
586 using MatrixN =
typename Base::MatrixN;
587 using VectorD = FrobeniusErrorVector<T>;
599 : Base(j1, j2, iTj, ConvertLeftModel(model)) {}
601 NonlinearFactor::shared_ptr
clone()
const override {
602 return std::make_shared<This>(*
this);
608 std::cout << s <<
"FrobeniusLeftBetweenFactor<"
609 <<
demangle(
typeid(T).name()) <<
">("
610 << keyFormatter(this->key1()) <<
"," << keyFormatter(this->key2())
613 this->noiseModel_->print(
" noise model: ");
618 double tol = 1e-9)
const override {
630 typename T::Jacobian predicted_H_jTw;
632 H2 ? &predicted_H_jTw :
nullptr);
634 Eigen::Matrix<double, Dim, T::dimension> vec_H_predicted;
638 if (H2) *H2 = -vec_H_predicted * predicted_H_jTw;
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 "
653 qcqpFactorsForVec(costs, constraints);
658 if (!model || model->
dim() != T::dimension)
return model;
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");
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);
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;
686 void qcqpFactorsForVec(NonlinearFactorGraph* costs,
687 NonlinearEqualityConstraints* constraints)
const {
688 if constexpr (!internal::HasQcqpVariableTraits<T, 1>::value) {
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>)) {
699 throw std::runtime_error(
700 "FrobeniusLeftBetweenFactor::qcqpFactors D=1 is implemented only "
701 "for Rot2, Rot3, Pose2, and Pose3.");
704 throw std::invalid_argument(
705 "FrobeniusLeftBetweenFactor::qcqpFactors costs is null");
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");
714 constexpr int LiftedDim = traits<T>::QcqpVectorDim;
716 if constexpr (std::is_same_v<T, Rot2>) {
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;
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>;
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);
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);
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;
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);
751 const Matrix whitenedB = this->noiseModel_->Whiten(fullB);
752 Q = whitenedB.transpose() * whitenedB;
755 InsertQcqpConstraints<T, 1>(this->key1(), constraints);
756 InsertQcqpConstraints<T, 1>(this->key2(), constraints);
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));
N*N matrix representation of SO(N).
3D Pose manifold SO(3) x R^3 and group SE(3)
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