Skip to content

Conversation

@hshose
Copy link
Contributor

@hshose hshose commented Nov 22, 2025

Simple lbuild include for Eigen and modm::IOStream for printing of Eigen::MatrixBase

Example tested on Nucleo G474 board.

Niklas modified some things in the integration:

  • Eigen doesn't seem to support a config file, so I inject the config into the Eigen/Version file, which is included by every file indirectly.
  • I moved the iostream support to it's own file and inject it into the :io module.
  • I made the no malloc macro dependent on whether the :platform:heap module is included.
  • Redirect eigen_assert to modm_assert otherwise you won't get any output.
  • Add more documentation about eigen
  • Somehow use the built-in Eigen formatting functions instead of rolling our own.
  • Fix the compilation regarding conflicting declaration of C function 'int __cxa_guard_acquire(guard_type*)'. Was an issue with circular imports due to assert.hpp relying on register.hpp.

@hshose
Copy link
Contributor Author

hshose commented Nov 22, 2025

#1005

@salkinium
Copy link
Member

salkinium commented Nov 23, 2025

I added GitLab support to partial and then added the eigen-partial repository.

@salkinium salkinium force-pushed the feature/eigen branch 3 times, most recently from dab4e2a to 73b844c Compare November 23, 2025 18:56
@hshose
Copy link
Contributor Author

hshose commented Nov 23, 2025

Wow! Pretty cool! Thank you @salkinium

Regarding the printing and reuse of the Eigen::print_matrix function:
I think we could string-replace the std::ostream with modm::IOStream (sed -i 's/std::ostream/modm::IOStream/g' Eigen/src/Core/IO.h in the github actions repo update)
and then patch with the following patch to fix the use of std::stringstream to determine the print width:

diff --git a/Eigen/src/Core/IO.h b/Eigen/src/Core/IO.h
index 0a1b583..5dc6d34 100644
--- a/Eigen/src/Core/IO.h
+++ b/Eigen/src/Core/IO.h
@@ -13,7 +13,7 @@
 
 // IWYU pragma: private
 #include "./InternalHeaderCheck.h"
-
+#include <modm/io.hpp>
 namespace Eigen {
 
 enum { DontAlignCols = 1 };
@@ -167,10 +167,19 @@ std::ostream& print_matrix(std::ostream& s, const Derived& _m, const IOFormat& f
     // compute the largest width
     for (Index j = 0; j < m.cols(); ++j)
       for (Index i = 0; i < m.rows(); ++i) {
-        std::stringstream sstr;
-        sstr.copyfmt(s);
-        sstr << static_cast<PrintType>(m.coeff(i, j));
-        width = std::max<Index>(width, Index(sstr.str().length()));
+        char tmpbuf[64];
+        std::streamsize prec  = s.precision();
+        int len = 0;
+        if constexpr (std::is_floating_point_v<PrintType>) {
+            if (prec > 0)
+                len = snprintf(tmpbuf, sizeof(tmpbuf), "%.*g", (int)prec, (double)static_cast<PrintType>(m.coeff(i, j)));
+            else
+                len = snprintf(tmpbuf, sizeof(tmpbuf), "%g", (double)static_cast<PrintType>(m.coeff(i, j)));
+        } else {
+            len = snprintf(tmpbuf, sizeof(tmpbuf), "%lld", (long long)static_cast<PrintType>(m.coeff(i, j)));
+        }
+        if (len > width)
+            width = len;
       }
   }
   std::streamsize old_width = s.width();

I added a commit with the required changes to the modm::IOStream:

  • dummy setter/getter for fill and width as this is currently not implemented
  • setter/getter for precision in float/double printing

@hshose hshose force-pushed the feature/eigen branch 2 times, most recently from 87f3c57 to fcd93bf Compare November 23, 2025 23:54
@salkinium
Copy link
Member

Yes, go for it. I added you as maintainer for the eigen-partial repo, remember to use the partial cli --patch option.

@hshose
Copy link
Contributor Author

hshose commented Nov 30, 2025

okay, added the patch to the CI and bumped the submodule.
Hope this is how you intended this.

Copy link
Member

@salkinium salkinium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope this is how you intended this.

Yes, absolutely!

salkinium and others added 4 commits December 4, 2025 21:04
This caused issues with circular imports and it is not necessary to have.
Co-authored-by: Niklas Hauser <niklas.hauser@rwth-aachen.de>
Copy link
Member

@salkinium salkinium left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed the AVR compilation, but I need to test the rest of the integration and document it.

@salkinium
Copy link
Member

FYI: Doing a search of std::ostream yields a lot more print functions that need to be adapted.

 $ rg "std::ostream"
Eigen/src/Geometry/Quaternion.h
251:  friend std::ostream& operator<<(std::ostream& s, const QuaternionBase<Derived>& q) {

Eigen/src/SparseCore/SparseVector.h
342:  friend std::ostream& operator<<(std::ostream& s, const SparseVector& m) {

Eigen/src/SparseCore/SparseMatrixBase.h
226:  friend std::ostream& operator<<(std::ostream& s, const SparseMatrixBase& m) {

Eigen/src/SparseCore/SparseMatrix.h
896:  friend std::ostream& operator<<(std::ostream& s, const SparseMatrix& m) {

Eigen/src/Core/IO.h
24:std::ostream& print_matrix(std::ostream& s, const Derived& _m, const IOFormat& fmt);
105:  friend std::ostream& operator<<(std::ostream& s, const WithFormat& wf) {
127:std::ostream& print_matrix(std::ostream& s, const Derived& _m, const IOFormat& fmt) {
222:std::ostream& operator<<(std::ostream& s, const DenseBase<Derived>& m) {
227:std::ostream& operator<<(std::ostream& s, const DiagonalBase<Derived>& m) {

Eigen/src/Core/arch/AltiVec/PacketMath.h
3220:inline std::ostream& operator<<(std::ostream& s, const Packet2l& v) {
3230:inline std::ostream& operator<<(std::ostream& s, const Packet2d& v) {

Eigen/src/Core/arch/Default/BFloat16.h
682:EIGEN_ALWAYS_INLINE std::ostream& operator<<(std::ostream& os, const bfloat16& v) {

Eigen/src/Core/arch/MSA/Complex.h
81:inline std::ostream& operator<<(std::ostream& os, const Packet2cf& value) {
304:inline std::ostream& operator<<(std::ostream& os, const PacketBlock<Packet2cf, 2>& value) {
384:inline std::ostream& operator<<(std::ostream& os, const Packet1cd& value) {
598:inline std::ostream& operator<<(std::ostream& os, const PacketBlock<Packet1cd, 2>& value) {

Eigen/src/Core/arch/Default/Half.h
864:EIGEN_ALWAYS_INLINE std::ostream& operator<<(std::ostream& os, const half& v) {

Eigen/src/Core/arch/MSA/PacketMath.h
61:inline std::ostream& operator<<(std::ostream& os, const Packet4f& value) {
66:inline std::ostream& operator<<(std::ostream& os, const Packet4i& value) {
71:inline std::ostream& operator<<(std::ostream& os, const Packet4ui& value) {
679:inline std::ostream& operator<<(std::ostream& os, const PacketBlock<Packet4f, 4>& value) {
703:inline std::ostream& operator<<(std::ostream& os, const PacketBlock<Packet4i, 4>& value) {
831:inline std::ostream& operator<<(std::ostream& os, const Packet2d& value) {
836:inline std::ostream& operator<<(std::ostream& os, const Packet2l& value) {
841:inline std::ostream& operator<<(std::ostream& os, const Packet2ul& value) {
1152:inline std::ostream& operator<<(std::ostream& os, const PacketBlock<Packet2d, 2>& value) {

Eigen/src/Core/GenericPacketMath.h
1675:  friend std::ostream& operator<<(std::ostream& os, const StreamablePacket& packet) {

Eigen/src/Core/arch/ZVector/PacketMath.h
273:inline std::ostream& operator<<(std::ostream& s, const Packet4i& v) {
280:inline std::ostream& operator<<(std::ostream& s, const Packet4ui& v) {
287:inline std::ostream& operator<<(std::ostream& s, const Packet2l& v) {
294:inline std::ostream& operator<<(std::ostream& s, const Packet2ul& v) {
301:inline std::ostream& operator<<(std::ostream& s, const Packet2d& v) {
309:inline std::ostream& operator<<(std::ostream& s, const Packet4f& v) {

@hshose
Copy link
Contributor Author

hshose commented Dec 8, 2025

@salkinium do you want to keep a patchfile for all of these?
Will probably be outdated pretty quickly.
Or should we implement a custom partial script that does some automatic string replacement?

Would also mean we have to write our own test for all of these, no?
Some of these are probably rarely used / experimental (like the sparse matrices or BFloat16)...

Not sure I still like using the Eigen formatting :-(

@salkinium
Copy link
Member

I used sed and it almost works, except for that stupid copyfmt problem.

 $ rg copyfmt Eigen
Eigen/src/SparseCore/SparseMatrixBase.h
238:        ss0.copyfmt(s);
244:            ss.copyfmt(s);
278:          ss0.copyfmt(s);
283:            ss.copyfmt(s);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

[math] math::Matrix class size limitation

2 participants