Bertini 2 d882924 (2026-10-06 19:56 UTC)
Numerical algebraic geometry library (C++ core)
Loading...
Searching...
No Matches
eigen_extensions.hpp
Go to the documentation of this file.
1//This file is part of Bertini 2.
2//
3//eigen_extensions.hpp is free software: you can redistribute it and/or modify
4//it under the terms of the GNU General Public License as published by
5//the Free Software Foundation, either version 3 of the License, or
6//(at your option) any later version.
7//
8//eigen_extensions.hpp is distributed in the hope that it will be useful,
9//but WITHOUT ANY WARRANTY; without even the implied warranty of
10//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11//GNU General Public License for more details.
12//
13//You should have received a copy of the GNU General Public License
14//along with eigen_extensions.hpp. If not, see <http://www.gnu.org/licenses/>.
15//
16// Copyright(C) Bertini2 Development Team
17//
18// See <http://www.gnu.org/licenses/> for a copy of the license,
19// as well as COPYING. Bertini2 is provided with permitted
20// additional terms in the b2/licenses/ directory.
21
27
28#ifndef BERTINI_EIGEN_EXTENSIONS_HPP
29#define BERTINI_EIGEN_EXTENSIONS_HPP
30
31
33
34#include <boost/serialization/complex.hpp>
35#include <boost/serialization/array.hpp>
36#include <boost/serialization/split_member.hpp>
37
39#define EIGEN_DENSEBASE_PLUGIN "bertini2/eigen_serialization_addon.hpp"
40
41#include <Eigen/Core>
42
43namespace {
44using mpfr_real = bertini::real_mp;
45using complex_mp = bertini::complex_mp;
46}
47
48namespace Eigen {
49
51 template<> struct NumTraits<mpfr_real> : GenericNumTraits<mpfr_real> // permits to get the epsilon, dummy_precision, lowest, highest functions
52 {
53
54 typedef mpfr_real Real;
55 typedef mpfr_real NonInteger;
56 typedef mpfr_real Nested;
57 enum {
58 IsComplex = 0,
59 IsInteger = 0,
60 IsSigned = 1,
61 RequireInitialization = 1, // yes, require initialization, otherwise get crashes
62 ReadCost = 20,
63 AddCost = 30,
64 MulCost = 40
65 };
66
67
68 inline static Real highest() {
69
70 return (mpfr_real(1) - epsilon()) * pow(mpfr_real(2),mpfr_get_emax()-1);//);//DefaultPrecision());
71 }
72
73 inline static Real lowest() {
74 return -highest();
75 }
76
77 // ThreadPrecision (thread-local), not DefaultPrecision (global): these
78 // tolerances are consumed inside tracking (LU solves, norms, convergence
79 // checks), which may run on std::thread workers whose precision is set
80 // thread-locally. On the main thread the two agree.
81 // epsilon() and dummy_precision() are pow(10, -precision): expensive (transcendental,
82 // heap-allocating) yet a pure function of the thread precision. They are called per
83 // LU-diagonal element, per Newton iteration, per predictor stage, per step, so recomputing
84 // them dominated the multiprecision path. Memoize per thread, keyed on the current
85 // precision -- the cached value is bit-identical to a fresh pow at that precision.
86 inline static Real dummy_precision()
87 {
89 thread_local unsigned cached_prec = 0;
90 thread_local Real cached_val;
91 const unsigned p = ThreadPrecision();
92 if (p != cached_prec)
93 {
94 cached_val = pow(mpfr_real(10), -int(p-3));
95 cached_prec = p;
96 }
97 return cached_val;
98 }
99
100 inline static Real epsilon()
101 {
103 thread_local unsigned cached_prec = 0;
104 thread_local Real cached_val;
105 const unsigned p = ThreadPrecision();
106 if (p != cached_prec)
107 {
108 cached_val = pow(mpfr_real(10), -int(p));
109 cached_prec = p;
110 }
111 return cached_val;
112 }
113
114 static inline int digits10()
115 {
116 return static_cast<int>(bertini::ThreadPrecision());
117 // return internal::default_digits10_impl<T>::run();
118 }
119 //http://www.manpagez.com/info/mpfr/mpfr-2.3.2/mpfr_31.php
120 };
121
122
123 template<typename Expr1,typename Expr2, typename Expr3>
124 struct NumTraits<
125 boost::multiprecision::detail::expression<Expr1,
126 Expr2, Expr3, void, void>
127 > : NumTraits<mpfr_real> // permits to get the epsilon, dummy_precision, lowest, highest functions
128 {
129
130 typedef mpfr_real Real;
131 typedef mpfr_real NonInteger;
132 typedef mpfr_real Nested;
133
134 //http://www.manpagez.com/info/mpfr/mpfr-2.3.2/mpfr_31.php
135 };
136
137
138
144 template<> struct NumTraits<complex_mp> : NumTraits<mpfr_real>
145 {
146 typedef mpfr_real Real;
147 typedef mpfr_real NonInteger;
148 typedef complex_mp Nested;// Nested;
149 enum {
150 IsComplex = 1,
151 IsInteger = 0,
152 IsSigned = 1,
153 RequireInitialization = 1, // yes, require initialization, otherwise get crashes
154 ReadCost = 2 * NumTraits<Real>::ReadCost,
155 AddCost = 2 * NumTraits<Real>::AddCost,
156 MulCost = 4 * NumTraits<Real>::MulCost + 2 * NumTraits<Real>::AddCost
157 };
158
159 };
160
161 namespace internal {
162 template<>
163 struct abs2_impl<complex_mp>
164 {
165 static inline mpfr_real run(const complex_mp& x)
166 {
167 return real(x)*real(x) + imag(x)*imag(x);
168 }
169 };
170
171
172 template<> inline complex_mp random<complex_mp>()
173 {
175 }
176
177 template<> inline complex_mp random<complex_mp>(const complex_mp& a, const complex_mp& b)
178 {
179 return a + (b-a) * random<complex_mp>();
180 }
181
182 template<>
183 struct conj_helper<complex_mp, complex_mp, false, true>
184 {
185 typedef complex_mp Scalar;
186 EIGEN_STRONG_INLINE Scalar pmadd(const Scalar& x, const Scalar& y, const Scalar& c) const
187 { return c + pmul(x,y); }
188
189 EIGEN_STRONG_INLINE Scalar pmul(const Scalar& x, const Scalar& y) const
190 { return Scalar(real(x)*real(y) + imag(x)*imag(y), imag(x)*real(y) - real(x)*imag(y)); }
191 };
192
193 template<>
194 struct conj_helper<complex_mp, complex_mp, true, false>
195 {
196 typedef complex_mp Scalar;
197 EIGEN_STRONG_INLINE Scalar pmadd(const Scalar& x, const Scalar& y, const Scalar& c) const
198 { return c + pmul(x,y); }
199
200 EIGEN_STRONG_INLINE Scalar pmul(const Scalar& x, const Scalar& y) const
201 { return Scalar(real(x)*real(y) + imag(x)*imag(y), real(x)*imag(y) - imag(x)*real(y)); }
202 };
203
204 // see https://forum.kde.org/viewtopic.php?f=74&t=111176
205
206 //int
207 template<>
208 struct scalar_product_traits<int,complex_mp>
209 {
210 enum { Defined = 1 };
211 typedef complex_mp ReturnType;
212 };
213
214 template<>
215 struct scalar_product_traits<complex_mp, int>
216 {
217 enum { Defined = 1 };
218 typedef complex_mp ReturnType;
219 };
220
221 //long
222 template<>
223 struct scalar_product_traits<long,complex_mp>
224 {
225 enum { Defined = 1 };
226 typedef complex_mp ReturnType;
227 };
228
229 template<>
230 struct scalar_product_traits<complex_mp, long>
231 {
232 enum { Defined = 1 };
233 typedef complex_mp ReturnType;
234 };
235
236 //long long
237 template<>
238 struct scalar_product_traits<long long,complex_mp>
239 {
240 enum { Defined = 1 };
241 typedef complex_mp ReturnType;
242 };
243
244 template<>
245 struct scalar_product_traits<complex_mp, long long>
246 {
247 enum { Defined = 1 };
248 typedef complex_mp ReturnType;
249 };
250
251
252 //mpfr_real
253 template<>
254 struct scalar_product_traits<mpfr_real,complex_mp>
255 {
256 enum { Defined = 1 };
257 typedef complex_mp ReturnType;
258 };
259
260 template<>
261 struct scalar_product_traits<complex_mp, mpfr_real>
262 {
263 enum { Defined = 1 };
264 typedef complex_mp ReturnType;
265 };
266
267 //mpz_int
268 template<>
269 struct scalar_product_traits<bertini::mpz_int,complex_mp>
270 {
271 enum { Defined = 1 };
272 typedef complex_mp ReturnType;
273 };
274
275 template<>
276 struct scalar_product_traits<complex_mp, bertini::mpz_int>
277 {
278 enum { Defined = 1 };
279 typedef complex_mp ReturnType;
280 };
281
282 } // re: namespace internal
284} // re: namespace Eigen
285
286
287#include <Eigen/Core>
288#include <Eigen/Geometry>
289#include <Eigen/Eigenvalues>
290#include <Eigen/SVD>
291#include <Eigen/Dense>
292
293namespace bertini {
294
296 template<typename NumT> using Vec = Eigen::Matrix<NumT, Eigen::Dynamic, 1>;
298 template<typename NumT> using Mat = Eigen::Matrix<NumT, Eigen::Dynamic, Eigen::Dynamic>;
299
300
304 template<typename Derived>
305 inline
306 bool IsEmpty(Eigen::MatrixBase<Derived> const & v)
307 {
308 return (v.rows()==0) || (v.cols()==0);
309 }
310
311
319 template<typename Derived>
320 inline
321 bool ContainsNaN(Eigen::MatrixBase<Derived> const & v)
322 {
323 using std::isnan;
324 for (Eigen::Index ii = 0; ii < v.rows(); ++ii)
325 for (Eigen::Index jj = 0; jj < v.cols(); ++jj)
326 if (isnan(v(ii,jj).real()) || isnan(v(ii,jj).imag()))
327 return true;
328 return false;
329 }
330
331
337 template<typename Derived>
338 inline
339 unsigned Precision(Eigen::MatrixBase<Derived> const & v)
340 {
341 if (IsEmpty(v))
342 throw std::runtime_error("getting precision of empty object");
343
344 return Precision(v(0,0));
345 }
346
347
354 template<typename Derived>
355 unsigned PrecisionRequireUniform(Eigen::MatrixBase<Derived> const & v)
356 {
357 const auto a = Precision(v(0,0));
358 for (int ii=0; ii<v.cols(); ++ii)
359 for (int jj=0; jj<v.rows(); ++jj)
360 {
361 if (a!=Precision(v(jj,ii)))
362 {
363 std::stringstream ss;
364 ss << "non-uniform precision in object! (" << a << "!=" << Precision(v(jj,ii)) << ") at position " << jj << "," << ii;
365 throw std::runtime_error(ss.str());
366 }
367 }
368 return a;
369 }
370
371
378 template<typename Derived>
379 void Precision(Eigen::MatrixBase<Derived> & v, unsigned prec)
380 {
381 using bertini::Precision;
382 for (int ii=0; ii<v.cols(); ++ii)
383 for (int jj=0; jj<v.rows(); ++jj)
384 Precision(v(jj,ii),prec);
385 }
386
387
388
400 template<typename T>
401 inline
402 bool IsSmallValue(T const& testme)
403 {
404 // |testme| <= eps*100 <=> abs2(testme) <= (eps*100)^2. Comparing squared magnitudes uses
405 // abs2 (re^2+im^2 for complex; see abs2_impl<complex_mp>) and avoids the allocating, sqrt-based
406 // std::abs/hypot on the multiprecision complex type. eps*100 is non-negative, so the squared
407 // comparison is exactly equivalent.
408 using Real = typename Eigen::NumTraits<T>::Real;
409 const Real thresh = Eigen::NumTraits<T>::epsilon() * 100;
410 return Eigen::numext::abs2(testme) <= thresh * thresh;
411 }
412
423 template<typename T>
424 inline
425 bool IsSymmRelDiffSmall(T const& a, T const& b, typename Eigen::NumTraits<T>::Real const& e)
426 {
427 using std::abs;
428 if (a==b)
429 return true;
430
431 typename Eigen::NumTraits<T>::Real c = abs(a-b);
432 return (c/abs(a) <= e) || (c/abs(b) <= e) ;
433 }
434
447 template<typename T>
448 inline
449 bool IsLargeChange(T const& numerator, T const& denomenator)
450 {
451 static_assert(!Eigen::NumTraits<T>::IsInteger, "IsLargeChange cannot be used safely on non-integral types");
452 // |num/den| >= 1/dummy <=> |num|*dummy >= |den| <=> abs2(num)*dummy^2 >= abs2(den).
453 // The squared form drops BOTH the complex division (num/den) and the sqrt-based abs/hypot --
454 // each of which allocates multiprecision temporaries -- and is exact since all quantities are
455 // non-negative. (In its hot caller, LUPartialPivotDecompositionSuccessful, the denominator has
456 // already passed IsSmallValue, so it is non-tiny here.)
457 using Real = typename Eigen::NumTraits<T>::Real;
458 const Real d = Eigen::NumTraits<T>::dummy_precision();
459 return Eigen::numext::abs2(numerator) * (d * d) >= Eigen::numext::abs2(denomenator);
460 }
461
469
479 template <typename Derived>
480 MatrixSuccessCode LUPartialPivotDecompositionSuccessful(Eigen::MatrixBase<Derived> const& LU)
481 {
482 #ifndef BERTINI_DISABLE_ASSERTS
483 assert(LU.rows()==LU.cols() && "non-square matrix in LUPartialPivotDecompositionSuccessful");
484 assert(LU.rows()>0 && "empty matrix in LUPartialPivotDecompositionSuccessful");
485 #endif
486
487 // this loop won't test entry (0,0). it's tested separately after.
488 for (Eigen::Index ii = LU.rows()-1; ii > 0; ii--)
489 {
490 if (IsSmallValue(LU(ii,ii)))
491 {
493 }
494
495 if (IsLargeChange(LU(ii-1,ii-1),LU(ii,ii)))
496 {
498 }
499 }
500
501 // this line is the reason for the above assert on non-empty matrix.
502 if (IsSmallValue(LU(0,0)))
503 {
505 }
506
508 }
509
513 template <typename NumberType>
514 Eigen::Matrix<NumberType, Eigen::Dynamic, Eigen::Dynamic> KahanMatrix(unsigned int mat_size, NumberType c)
515 {
516 using std::sqrt;
517 NumberType s, scale(1.0);
518 s = sqrt( (NumberType(1.0)-c) * (NumberType(1.0)+c) );
519
520
521 Eigen::Matrix<NumberType, Eigen::Dynamic, Eigen::Dynamic> A(mat_size,mat_size);
522
523
524 for (unsigned int ii=0; ii<mat_size; ii++) {
525 for (unsigned int jj=0; jj<ii; jj++) {
526 A(ii,jj) = NumberType(0.0);
527 }
528 for (unsigned int jj=ii; jj<mat_size; jj++) {
529 A(ii,jj) = -c * NumberType(1.0);
530 }
531 }
532
533
534 for (unsigned int ii=0; ii<mat_size; ii++) {
535 A(ii,ii) += NumberType(1)+c;
536 }
537
538 for (unsigned int jj=0; jj<mat_size; jj++){
539 for (unsigned int kk=0;kk<mat_size;kk++){
540 A(jj,kk) *= scale;
541 }
542 scale *= s;
543 }
544
545 for (unsigned int jj=0;jj<mat_size;jj++){
546 for (unsigned int kk=0;kk<mat_size;kk++){
547 A(kk,jj)/= NumberType(jj) + NumberType(1);
548 }
549 }
550 return A;
551 }
552
553
563 template <typename NumberType>
564 inline
565 Mat<NumberType> RandomOfUnits(unsigned int rows, unsigned int cols)
566 {
567 return Mat<NumberType>(rows,cols).unaryExpr([](NumberType const& /*x*/) { return RandomUnit<NumberType>(); });
568 }
569
570
600 template <typename NumberType>
601 inline
602 Mat<NumberType> RandomConjugateOrthonormalMatrix(unsigned int rows, unsigned int cols)
603 {
604 using std::max;
605 using std::min;
606 const unsigned int tall = max(rows, cols);
607 const unsigned int thin = min(rows, cols);
608 Mat<NumberType> seed = Mat<NumberType>(tall, thin).unaryExpr(
609 [](NumberType const& /*x*/) { return RandomOrthonormalSeed<NumberType>(); });
610 Eigen::HouseholderQR<Mat<NumberType> > qr(seed);
611 Mat<NumberType> Q = qr.householderQ() * Mat<NumberType>::Identity(tall, thin); // tall x thin, orthonormal columns
612 if (rows >= cols)
613 return Q;
614 return Q.transpose(); // rows x cols with orthonormal rows: M M^H = (Q^H Q)^T = I
615 }
616
625 template <typename NumberType>
626 inline
628 {
629 return Vec<NumberType>(size).unaryExpr([](NumberType const&) { return RandomUnit<NumberType>(); });
630 }
631
632
644 template <typename DerivedA, typename DerivedB>
645 inline
646 bool IsSamePoint(Eigen::MatrixBase<DerivedA> const& a, Eigen::MatrixBase<DerivedB> const& b, NumErrorT tol)
647 {
648 if (a.size() != b.size())
649 return false;
650 if (a.size() == 0)
651 return true;
652 return (a - b).template lpNorm<Eigen::Infinity>() <= tol;
653 }
654
663 template <typename DerivedA, typename DerivedB>
664 inline
665 bool IsDistinct(Eigen::MatrixBase<DerivedA> const& a, Eigen::MatrixBase<DerivedB> const& b, NumErrorT tol)
666 {
667 return !IsSamePoint(a, b, tol);
668 }
669
670}
671
672
673// // the following code comes from
674// // https://stackoverflow.com/questions/18382457/eigen-and-boostserialize
675// // and adds support for serialization of Eigen types
676// //
677// // question asked by user Gabriel and answered by Shmuel Levine
678// // answer code repeated here verbatim.
679// // please update this comment if this code is changed,
680// // and post the modifications to the above referenced post on SO.
681// namespace boost{
682// namespace serialization{
683
684// template< class Archive,
685// class S,
686// int Rows_,
687// int Cols_,
688// int Ops_,
689// int MaxRows_,
690// int MaxCols_>
691// inline void save(
692// Archive & ar,
693// const Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g,
694// const unsigned int version)
695// {
696// int rows = g.rows();
697// int cols = g.cols();
698
699// ar & rows;
700// ar & cols;
701// ar & boost::serialization::make_array(g.data(), rows * cols);
702// }
703
704// template< class Archive,
705// class S,
706// int Rows_,
707// int Cols_,
708// int Ops_,
709// int MaxRows_,
710// int MaxCols_>
711// inline void load(
712// Archive & ar,
713// Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g,
714// const unsigned int version)
715// {
716// int rows, cols;
717// ar & rows;
718// ar & cols;
719// g.resize(rows, cols);
720// ar & boost::serialization::make_array(g.data(), rows * cols);
721// }
722
723// template< class Archive,
724// class S,
725// int Rows_,
726// int Cols_,
727// int Ops_,
728// int MaxRows_,
729// int MaxCols_>
730// inline void serialize(
731// Archive & ar,
732// Eigen::Matrix<S, Rows_, Cols_, Ops_, MaxRows_, MaxCols_> & g,
733// const unsigned int version)
734// {
735// split_free(ar, g, version);
736// }
737
738
739// } // namespace serialization
740// } // namespace boost
741
742
743
744
745#endif
Definition eigen_extensions.hpp:48
complex rand()
Produce a random complex number, to default precision.
Definition random.hpp:288
Definition algorithm_builder.hpp:36
MatrixSuccessCode
Outcome of a matrix-decomposition sanity check (e.g. LU pivot magnitude).
Definition eigen_extensions.hpp:464
@ Success
The decomposition looks healthy.
Definition eigen_extensions.hpp:465
@ SmallValue
A pivot is suspiciously small (near-singular).
Definition eigen_extensions.hpp:467
@ LargeChange
A pivot changed by a suspiciously large amount.
Definition eigen_extensions.hpp:466
bool isnan(std::complex< double > const &z)
an overload of isnan, for std::complex<double>
Definition double_extensions.hpp:63
bool IsLargeChange(T const &numerator, T const &denomenator)
Test two numbers for having large ratio.
Definition eigen_extensions.hpp:449
bool IsSymmRelDiffSmall(T const &a, T const &b, typename Eigen::NumTraits< T >::Real const &e)
Check whether two values are very close to each other.
Definition eigen_extensions.hpp:425
bool IsSamePoint(Eigen::MatrixBase< DerivedA > const &a, Eigen::MatrixBase< DerivedB > const &b, NumErrorT tol)
True if two points are the same to within tol in the infinity norm.
Definition eigen_extensions.hpp:646
Eigen::Matrix< NumT, Eigen::Dynamic, 1 > Vec
A dynamically-sized column vector of NumT.
Definition eigen_extensions.hpp:296
T RandomUnit()
Get a random complex number of unit modulus, in number type T.
Mat< NumberType > RandomOfUnits(unsigned int rows, unsigned int cols)
Make a random matrix of units (numbers with norm 1).
Definition eigen_extensions.hpp:565
NumberType RandomOrthonormalSeed()
The entry a random orthonormal matrix is seeded with before its QR factorization: a unit draw for the...
Definition num_traits.hpp:280
MatrixSuccessCode LUPartialPivotDecompositionSuccessful(Eigen::MatrixBase< Derived > const &LU)
Check the diagonal elements of an LU decomposition for small values and large ratios.
Definition eigen_extensions.hpp:480
Eigen::Matrix< NumT, Eigen::Dynamic, Eigen::Dynamic > Mat
A dynamically-sized matrix of NumT.
Definition eigen_extensions.hpp:298
double NumErrorT
The numeric type used to hold error/tolerance magnitudes throughout Bertini2 – tracking tolerances,...
Definition num_traits.hpp:48
unsigned PrecisionRequireUniform(Eigen::MatrixBase< Derived > const &v)
Query the precision of an Eigen object, requiring it to be non-empty, and of uniform precision.
Definition eigen_extensions.hpp:355
bmp::number< mpc_complex_backend< 0 >, bmp::et_on > complex_mp
The arbitrary-precision complex number type.
Definition mpfr_complex.hpp:58
@ Success
Definition config.hpp:51
boost::multiprecision::number< boost::multiprecision::mpfr_float_backend< 0 >, boost::multiprecision::et_on > real_mp
The arbitrary-precision real number type.
Definition mpfr_extensions.hpp:53
bool IsSmallValue(T const &testme)
Test a numbers being very small.
Definition eigen_extensions.hpp:402
bool IsEmpty(Eigen::MatrixBase< Derived > const &v)
Checks whether the number of rows and columns are either 0.
Definition eigen_extensions.hpp:306
bool IsDistinct(Eigen::MatrixBase< DerivedA > const &a, Eigen::MatrixBase< DerivedB > const &b, NumErrorT tol)
True if two points differ by more than tol in the infinity norm (the negation of :func:IsSamePoint).
Definition eigen_extensions.hpp:665
unsigned ThreadPrecision()
Get the current thread's precision (digits).
Definition mpfr_complex.hpp:183
Mat< NumberType > RandomConjugateOrthonormalMatrix(unsigned int rows, unsigned int cols)
Make a random conjugate-orthonormal matrix (orthonormal rows when rows<=cols, orthonormal columns whe...
Definition eigen_extensions.hpp:602
unsigned Precision(Eigen::MatrixBase< Derived > const &v)
Get the precision of an Eigen object.
Definition eigen_extensions.hpp:339
T max(T const &a, T const &b, T const &c)
Three-argument form of max.
Definition mpfr_extensions.hpp:96
@ Real
Definition straight_line_program.hpp:165
Eigen::Matrix< NumberType, Eigen::Dynamic, Eigen::Dynamic > KahanMatrix(unsigned int mat_size, NumberType c)
Make a Kahan matrix with a given number type.
Definition eigen_extensions.hpp:514
bool ContainsNaN(Eigen::MatrixBase< Derived > const &v)
True when any entry of a complex-valued Eigen object has a NaN real or imaginary part.
Definition eigen_extensions.hpp:321
Definition forbid_mixed_arithmetic.hpp:27
Provides an Eigen-like NumTraits struct for querying traits of a number type.