// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2014 Benoit Steiner // // This Source Code Form is subject to the terms of the Mozilla // Public License v. 2.0. If a copy of the MPL was not distributed // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef EIGEN_CXX11_TENSOR_TENSOR_IO_H #define EIGEN_CXX11_TENSOR_TENSOR_IO_H // IWYU pragma: private #include "./InternalHeaderCheck.h" namespace Eigen { struct TensorIOFormat; namespace internal { template struct TensorPrinter; } template struct TensorIOFormatBase { using Derived = Derived_; TensorIOFormatBase(const std::vector& separator, const std::vector& prefix, const std::vector& suffix, int precision = StreamPrecision, int flags = 0, const std::string& tenPrefix = "", const std::string& tenSuffix = "", const char fill = ' ') : tenPrefix(tenPrefix), tenSuffix(tenSuffix), prefix(prefix), suffix(suffix), separator(separator), fill(fill), precision(precision), flags(flags) { init_spacer(); } void init_spacer() { if ((flags & DontAlignCols)) return; spacer.resize(prefix.size()); spacer[0] = ""; int i = int(tenPrefix.length()) - 1; while (i >= 0 && tenPrefix[i] != '\n') { spacer[0] += ' '; i--; } for (std::size_t k = 1; k < prefix.size(); k++) { int j = int(prefix[k].length()) - 1; while (j >= 0 && prefix[k][j] != '\n') { spacer[k] += ' '; j--; } } } std::string tenPrefix; std::string tenSuffix; std::vector prefix; std::vector suffix; std::vector separator; char fill; int precision; int flags; std::vector spacer{}; }; struct TensorIOFormatNumpy : public TensorIOFormatBase { using Base = TensorIOFormatBase; TensorIOFormatNumpy() : Base(/*separator=*/{" ", "\n"}, /*prefix=*/{"", "["}, /*suffix=*/{"", "]"}, /*precision=*/StreamPrecision, /*flags=*/0, /*tenPrefix=*/"[", /*tenSuffix=*/"]") {} }; struct TensorIOFormatNative : public TensorIOFormatBase { using Base = TensorIOFormatBase; TensorIOFormatNative() : Base(/*separator=*/{", ", ",\n", "\n"}, /*prefix=*/{"", "{"}, /*suffix=*/{"", "}"}, /*precision=*/StreamPrecision, /*flags=*/0, /*tenPrefix=*/"{", /*tenSuffix=*/"}") {} }; struct TensorIOFormatPlain : public TensorIOFormatBase { using Base = TensorIOFormatBase; TensorIOFormatPlain() : Base(/*separator=*/{" ", "\n", "\n", ""}, /*prefix=*/{""}, /*suffix=*/{""}, /*precision=*/StreamPrecision, /*flags=*/0, /*tenPrefix=*/"", /*tenSuffix=*/"") {} }; struct TensorIOFormatLegacy : public TensorIOFormatBase { using Base = TensorIOFormatBase; TensorIOFormatLegacy() : Base(/*separator=*/{", ", "\n"}, /*prefix=*/{"", "["}, /*suffix=*/{"", "]"}, /*precision=*/StreamPrecision, /*flags=*/0, /*tenPrefix=*/"", /*tenSuffix=*/"") {} }; struct TensorIOFormat : public TensorIOFormatBase { using Base = TensorIOFormatBase; TensorIOFormat(const std::vector& separator, const std::vector& prefix, const std::vector& suffix, int precision = StreamPrecision, int flags = 0, const std::string& tenPrefix = "", const std::string& tenSuffix = "", const char fill = ' ') : Base(separator, prefix, suffix, precision, flags, tenPrefix, tenSuffix, fill) {} static inline const TensorIOFormatNumpy Numpy() { return TensorIOFormatNumpy{}; } static inline const TensorIOFormatPlain Plain() { return TensorIOFormatPlain{}; } static inline const TensorIOFormatNative Native() { return TensorIOFormatNative{}; } static inline const TensorIOFormatLegacy Legacy() { return TensorIOFormatLegacy{}; } }; template class TensorWithFormat; // specialize for Layout=ColMajor, Layout=RowMajor and rank=0. template class TensorWithFormat { public: TensorWithFormat(const T& tensor, const Format& format) : t_tensor(tensor), t_format(format) {} friend std::ostream& operator<<(std::ostream& os, const TensorWithFormat& wf) { // Evaluate the expression if needed typedef TensorEvaluator, DefaultDevice> Evaluator; TensorForcedEvalOp eval = wf.t_tensor.eval(); Evaluator tensor(eval, DefaultDevice()); tensor.evalSubExprsIfNeeded(NULL); internal::TensorPrinter::run(os, tensor, wf.t_format); // Cleanup. tensor.cleanup(); return os; } protected: T t_tensor; Format t_format; }; template class TensorWithFormat { public: TensorWithFormat(const T& tensor, const Format& format) : t_tensor(tensor), t_format(format) {} friend std::ostream& operator<<(std::ostream& os, const TensorWithFormat& wf) { // Switch to RowMajor storage and print afterwards typedef typename T::Index IndexType; std::array shuffle; std::array id; std::iota(id.begin(), id.end(), IndexType(0)); std::copy(id.begin(), id.end(), shuffle.rbegin()); auto tensor_row_major = wf.t_tensor.swap_layout().shuffle(shuffle); // Evaluate the expression if needed typedef TensorEvaluator, DefaultDevice> Evaluator; TensorForcedEvalOp eval = tensor_row_major.eval(); Evaluator tensor(eval, DefaultDevice()); tensor.evalSubExprsIfNeeded(NULL); internal::TensorPrinter::run(os, tensor, wf.t_format); // Cleanup. tensor.cleanup(); return os; } protected: T t_tensor; Format t_format; }; template class TensorWithFormat { public: TensorWithFormat(const T& tensor, const Format& format) : t_tensor(tensor), t_format(format) {} friend std::ostream& operator<<(std::ostream& os, const TensorWithFormat& wf) { // Evaluate the expression if needed typedef TensorEvaluator, DefaultDevice> Evaluator; TensorForcedEvalOp eval = wf.t_tensor.eval(); Evaluator tensor(eval, DefaultDevice()); tensor.evalSubExprsIfNeeded(NULL); internal::TensorPrinter::run(os, tensor, wf.t_format); // Cleanup. tensor.cleanup(); return os; } protected: T t_tensor; Format t_format; }; namespace internal { // Default scalar printer. template struct ScalarPrinter { static void run(std::ostream& stream, const Scalar& scalar, const Format&) { stream << scalar; } }; template struct ScalarPrinter::IsComplex>> { static void run(std::ostream& stream, const Scalar& scalar, const TensorIOFormatNumpy&) { stream << numext::real(scalar) << "+" << numext::imag(scalar) << "j"; } }; template struct ScalarPrinter::IsComplex>> { static void run(std::ostream& stream, const Scalar& scalar, const TensorIOFormatNative&) { stream << "{" << numext::real(scalar) << ", " << numext::imag(scalar) << "}"; } }; template struct TensorPrinter { using Scalar = std::remove_const_t; static void run(std::ostream& s, const Tensor& tensor, const Format& fmt) { typedef typename Tensor::Index IndexType; eigen_assert(Tensor::Layout == RowMajor); typedef std::conditional_t::value || is_same::value || is_same::value || is_same::value, int, std::conditional_t>::value || is_same>::value || is_same>::value || is_same>::value, std::complex, const Scalar&>> PrintType; const IndexType total_size = array_prod(tensor.dimensions()); std::streamsize explicit_precision; if (fmt.precision == StreamPrecision) { explicit_precision = 0; } else if (fmt.precision == FullPrecision) { if (NumTraits::IsInteger) { explicit_precision = 0; } else { explicit_precision = significant_decimals_impl::run(); } } else { explicit_precision = fmt.precision; } std::streamsize old_precision = 0; if (explicit_precision) old_precision = s.precision(explicit_precision); IndexType width = 0; bool align_cols = !(fmt.flags & DontAlignCols); if (align_cols) { // compute the largest width for (IndexType i = 0; i < total_size; i++) { std::stringstream sstr; sstr.copyfmt(s); ScalarPrinter::run(sstr, static_cast(tensor.data()[i]), fmt); width = std::max(width, IndexType(sstr.str().length())); } } s << fmt.tenPrefix; for (IndexType i = 0; i < total_size; i++) { std::array is_at_end{}; std::array is_at_begin{}; // is the ith element the end of an coeff (always true), of a row, of a matrix, ...? for (std::size_t k = 0; k < rank; k++) { if ((i + 1) % (std::accumulate(tensor.dimensions().rbegin(), tensor.dimensions().rbegin() + k, 1, std::multiplies())) == 0) { is_at_end[k] = true; } } // is the ith element the begin of an coeff (always true), of a row, of a matrix, ...? for (std::size_t k = 0; k < rank; k++) { if (i % (std::accumulate(tensor.dimensions().rbegin(), tensor.dimensions().rbegin() + k, 1, std::multiplies())) == 0) { is_at_begin[k] = true; } } // do we have a line break? bool is_at_begin_after_newline = false; for (std::size_t k = 0; k < rank; k++) { if (is_at_begin[k]) { std::size_t separator_index = (k < fmt.separator.size()) ? k : fmt.separator.size() - 1; if (fmt.separator[separator_index].find('\n') != std::string::npos) { is_at_begin_after_newline = true; } } } bool is_at_end_before_newline = false; for (std::size_t k = 0; k < rank; k++) { if (is_at_end[k]) { std::size_t separator_index = (k < fmt.separator.size()) ? k : fmt.separator.size() - 1; if (fmt.separator[separator_index].find('\n') != std::string::npos) { is_at_end_before_newline = true; } } } std::stringstream suffix, prefix, separator; for (std::size_t k = 0; k < rank; k++) { std::size_t suffix_index = (k < fmt.suffix.size()) ? k : fmt.suffix.size() - 1; if (is_at_end[k]) { suffix << fmt.suffix[suffix_index]; } } for (std::size_t k = 0; k < rank; k++) { std::size_t separator_index = (k < fmt.separator.size()) ? k : fmt.separator.size() - 1; if (is_at_end[k] && (!is_at_end_before_newline || fmt.separator[separator_index].find('\n') != std::string::npos)) { separator << fmt.separator[separator_index]; } } for (std::size_t k = 0; k < rank; k++) { std::size_t spacer_index = (k < fmt.spacer.size()) ? k : fmt.spacer.size() - 1; if (i != 0 && is_at_begin_after_newline && (!is_at_begin[k] || k == 0)) { prefix << fmt.spacer[spacer_index]; } } for (int k = rank - 1; k >= 0; k--) { std::size_t prefix_index = (static_cast(k) < fmt.prefix.size()) ? k : fmt.prefix.size() - 1; if (is_at_begin[k]) { prefix << fmt.prefix[prefix_index]; } } s << prefix.str(); // So we don't mess around with formatting, output scalar to a string stream, and adjust the width/fill manually. std::stringstream sstr; sstr.copyfmt(s); ScalarPrinter::run(sstr, static_cast(tensor.data()[i]), fmt); std::string scalar_str = sstr.str(); IndexType scalar_width = scalar_str.length(); if (width && scalar_width < width) { std::string filler; for (IndexType j = scalar_width; j < width; ++j) { filler.push_back(fmt.fill); } s << filler; } s << scalar_str; s << suffix.str(); if (i < total_size - 1) { s << separator.str(); } } s << fmt.tenSuffix; if (explicit_precision) s.precision(old_precision); } }; template struct TensorPrinter> { using Format = TensorIOFormatLegacy; using Scalar = std::remove_const_t; static void run(std::ostream& s, const Tensor& tensor, const Format&) { typedef typename Tensor::Index IndexType; // backwards compatibility case: print tensor after reshaping to matrix of size dim(0) x // (dim(1)*dim(2)*...*dim(rank-1)). const IndexType total_size = internal::array_prod(tensor.dimensions()); if (total_size > 0) { const IndexType first_dim = Eigen::internal::array_get<0>(tensor.dimensions()); Map> matrix(tensor.data(), first_dim, total_size / first_dim); s << matrix; return; } } }; template struct TensorPrinter { static void run(std::ostream& s, const Tensor& tensor, const Format& fmt) { using Scalar = std::remove_const_t; std::streamsize explicit_precision; if (fmt.precision == StreamPrecision) { explicit_precision = 0; } else if (fmt.precision == FullPrecision) { if (NumTraits::IsInteger) { explicit_precision = 0; } else { explicit_precision = significant_decimals_impl::run(); } } else { explicit_precision = fmt.precision; } std::streamsize old_precision = 0; if (explicit_precision) old_precision = s.precision(explicit_precision); s << fmt.tenPrefix; ScalarPrinter::run(s, tensor.coeff(0), fmt); s << fmt.tenSuffix; if (explicit_precision) s.precision(old_precision); } }; } // end namespace internal template std::ostream& operator<<(std::ostream& s, const TensorBase& t) { s << t.format(TensorIOFormat::Plain()); return s; } } // end namespace Eigen #endif // EIGEN_CXX11_TENSOR_TENSOR_IO_H