Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Eigen3 library with C++20 format

Tags:

c++

c++20

eigen3

I am trying to print Eigen::Array or Eigen::Matrix with c++20 format, instead of Eigen::IOFormat. I would like to control the precision and alignment of elements in the matrix with specifiers, for example,

#include <Eigen/Core>
#include <format>
#include <iostream>

int main()
{
    Eigen::ArrayXXd mat( 3, 4 );
    mat.setZero();
    std::cout << std::format( "mat={:9.4f}\n", mat );
    return 0;
}

How can I get the following expected result?

mat=   0.0000   0.0000   0.0000   0.0000
       0.0000   0.0000   0.0000   0.0000
       0.0000   0.0000   0.0000   0.0000
like image 517
Arsennnic Avatar asked Oct 26 '25 16:10

Arsennnic


2 Answers

I use a solution based on Concepts that worked for me. Tested with Visual Studio 2022 so far.

#include <string>
#include <iostream>
#include <format>
#include <Eigen/Eigen>


template <typename EigenExprTypeT>
concept EigenTypeMatExpr = requires(const EigenExprTypeT t) {
    std::remove_cvref_t<EigenExprTypeT>::RowsAtCompileTime;
    std::remove_cvref_t<EigenExprTypeT>::ColsAtCompileTime;
    typename std::remove_cvref_t<EigenExprTypeT>::Scalar;
    {
        t.size()
    } -> std::same_as<typename Eigen::Index>;
    {
        t.rows()
    } -> std::same_as<typename Eigen::Index>;
    {
        t.cols()
    } -> std::same_as<typename Eigen::Index>;
};

enum class EigenCustomFormats {
    Default,                //
    CleanFormat,            // cf
    HeavyFormat,            // hf
    SingleLineFormat,       // sfl
    HighPrecisionFormat,    // hpf
    DebuggingFormat         // df
};

static const auto defaultFormat = Eigen::IOFormat();
static const auto cleanFormat = Eigen::IOFormat(4, 0, ", ", "\n", "[", "]");
static const auto heavyFormat = Eigen::IOFormat(Eigen::FullPrecision, 0, ", ", ";\n", "[", "]", "[", "]");
static const auto singleLineFormat =
Eigen::IOFormat(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", ", ", "", "", "", "");
static const auto highPrecisionFormat =
Eigen::IOFormat(Eigen::FullPrecision, Eigen::DontAlignCols, " ", "\n", "", "", "", "");
static const auto debuggingFormat =
Eigen::IOFormat(Eigen::FullPrecision, Eigen::DontAlignCols, " ", "\n", "", "", "\n", "");


template <EigenTypeMatExpr MatT>
struct std::formatter<MatT> {
    constexpr auto parse(format_parse_context& ctx) {
        const std::string_view fmt(ctx.begin(), ctx.end());
        if (fmt.starts_with("cf"))
            _format = EigenCustomFormats::CleanFormat;
        if (fmt.starts_with("hf"))
            _format = EigenCustomFormats::HeavyFormat;
        if (fmt.starts_with("sfl"))
            _format = EigenCustomFormats::SingleLineFormat;
        if (fmt.starts_with("hpf"))
            _format = EigenCustomFormats::HighPrecisionFormat;
        if (fmt.starts_with("df"))
            _format = EigenCustomFormats::DebuggingFormat;
        return ctx.begin() + fmt.find_first_of('}');

    }

    // Format the type for output
    template <typename FormatContext>
    auto format(const MatT& m, FormatContext& ctx) const {
        switch (_format) {
        case EigenCustomFormats::CleanFormat:         return std::format_to(ctx.out(), "{}", (std::stringstream{} << std::fixed << m.format(cleanFormat)).str());
        case EigenCustomFormats::HeavyFormat:         return std::format_to(ctx.out(), "{}", (std::stringstream{} << std::fixed << m.format(heavyFormat)).str());
        case EigenCustomFormats::SingleLineFormat:    return std::format_to(ctx.out(), "{}", (std::stringstream{} << std::fixed << m.format(singleLineFormat)).str());
        case EigenCustomFormats::HighPrecisionFormat: return std::format_to(ctx.out(), "{}", (std::stringstream{} << std::fixed << m.format(highPrecisionFormat)).str());
        case EigenCustomFormats::DebuggingFormat:     return std::format_to(ctx.out(), "{}", (std::stringstream{} << std::fixed << m.format(debuggingFormat)).str());
        default:                                      return std::format_to(ctx.out(), "{}", (std::stringstream{} << std::fixed << m.format(defaultFormat)).str());
        }

    }

private:
    EigenCustomFormats _format{ EigenCustomFormats::Default };
};

template <EigenTypeMatExpr MatT>
std::ostream& operator<<(std::ostream& os, const MatT& mat)
{
    return os << std::format("{:hf}", mat);
}

int main()
{

    Eigen::Matrix2f m = Eigen::Matrix2f::Random();
    std::cout << Eigen::Matrix2f::Random() << std::endl << std::endl << std::endl;
    std::cout << std::format("{}\n", Eigen::Matrix2f::Random()) << std::endl;
    std::cout << std::format("{:cf}\n", Eigen::Matrix2f::Random()) << std::endl;
    std::cout << std::format("{:hf}\n", Eigen::Matrix2f::Random()) << std::endl;
    std::cout << std::format("{:sfl}\n", Eigen::Matrix2f::Random()) << std::endl;
    std::cout << std::format("{:hpf}\n", Eigen::Matrix2f::Random()) << std::endl;
    std::cout << std::format("{:df}\n", Eigen::Matrix2f::Random()) << std::endl;

    return 0;
}
like image 131
Benjamin Jähn Avatar answered Oct 28 '25 07:10

Benjamin Jähn


You may look at https://gite.lirmm.fr/rpc/utils/eigen-fmt and see if it helps you out.

You basically need to include this header file in order to have eigen support for print/format using the fmt library. Since std::format was based from fmt, that code should be a good start in order to achieve what you want. Note that you may need to make a few adjustments.

like image 45
A. Soprana Avatar answered Oct 28 '25 06:10

A. Soprana