| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com> | |
| // | |
| // 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 <typename Tensor, std::size_t rank, typename Format, typename EnableIf = void> | |
| struct TensorPrinter; | |
| } | |
| template <typename Derived_> | |
| struct TensorIOFormatBase { | |
| using Derived = Derived_; | |
| TensorIOFormatBase(const std::vector<std::string>& separator, const std::vector<std::string>& prefix, | |
| const std::vector<std::string>& 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<std::string> prefix; | |
| std::vector<std::string> suffix; | |
| std::vector<std::string> separator; | |
| char fill; | |
| int precision; | |
| int flags; | |
| std::vector<std::string> spacer{}; | |
| }; | |
| struct TensorIOFormatNumpy : public TensorIOFormatBase<TensorIOFormatNumpy> { | |
| using Base = TensorIOFormatBase<TensorIOFormatNumpy>; | |
| TensorIOFormatNumpy() | |
| : Base(/*separator=*/{" ", "\n"}, /*prefix=*/{"", "["}, /*suffix=*/{"", "]"}, /*precision=*/StreamPrecision, | |
| /*flags=*/0, /*tenPrefix=*/"[", /*tenSuffix=*/"]") {} | |
| }; | |
| struct TensorIOFormatNative : public TensorIOFormatBase<TensorIOFormatNative> { | |
| using Base = TensorIOFormatBase<TensorIOFormatNative>; | |
| TensorIOFormatNative() | |
| : Base(/*separator=*/{", ", ",\n", "\n"}, /*prefix=*/{"", "{"}, /*suffix=*/{"", "}"}, | |
| /*precision=*/StreamPrecision, /*flags=*/0, /*tenPrefix=*/"{", /*tenSuffix=*/"}") {} | |
| }; | |
| struct TensorIOFormatPlain : public TensorIOFormatBase<TensorIOFormatPlain> { | |
| using Base = TensorIOFormatBase<TensorIOFormatPlain>; | |
| TensorIOFormatPlain() | |
| : Base(/*separator=*/{" ", "\n", "\n", ""}, /*prefix=*/{""}, /*suffix=*/{""}, /*precision=*/StreamPrecision, | |
| /*flags=*/0, /*tenPrefix=*/"", /*tenSuffix=*/"") {} | |
| }; | |
| struct TensorIOFormatLegacy : public TensorIOFormatBase<TensorIOFormatLegacy> { | |
| using Base = TensorIOFormatBase<TensorIOFormatLegacy>; | |
| TensorIOFormatLegacy() | |
| : Base(/*separator=*/{", ", "\n"}, /*prefix=*/{"", "["}, /*suffix=*/{"", "]"}, /*precision=*/StreamPrecision, | |
| /*flags=*/0, /*tenPrefix=*/"", /*tenSuffix=*/"") {} | |
| }; | |
| struct TensorIOFormat : public TensorIOFormatBase<TensorIOFormat> { | |
| using Base = TensorIOFormatBase<TensorIOFormat>; | |
| TensorIOFormat(const std::vector<std::string>& separator, const std::vector<std::string>& prefix, | |
| const std::vector<std::string>& 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 <typename T, int Layout, int rank, typename Format> | |
| class TensorWithFormat; | |
| // specialize for Layout=ColMajor, Layout=RowMajor and rank=0. | |
| template <typename T, int rank, typename Format> | |
| class TensorWithFormat<T, RowMajor, rank, Format> { | |
| public: | |
| TensorWithFormat(const T& tensor, const Format& format) : t_tensor(tensor), t_format(format) {} | |
| friend std::ostream& operator<<(std::ostream& os, const TensorWithFormat<T, RowMajor, rank, Format>& wf) { | |
| // Evaluate the expression if needed | |
| typedef TensorEvaluator<const TensorForcedEvalOp<const T>, DefaultDevice> Evaluator; | |
| TensorForcedEvalOp<const T> eval = wf.t_tensor.eval(); | |
| Evaluator tensor(eval, DefaultDevice()); | |
| tensor.evalSubExprsIfNeeded(NULL); | |
| internal::TensorPrinter<Evaluator, rank, Format>::run(os, tensor, wf.t_format); | |
| // Cleanup. | |
| tensor.cleanup(); | |
| return os; | |
| } | |
| protected: | |
| T t_tensor; | |
| Format t_format; | |
| }; | |
| template <typename T, int rank, typename Format> | |
| class TensorWithFormat<T, ColMajor, rank, Format> { | |
| public: | |
| TensorWithFormat(const T& tensor, const Format& format) : t_tensor(tensor), t_format(format) {} | |
| friend std::ostream& operator<<(std::ostream& os, const TensorWithFormat<T, ColMajor, rank, Format>& wf) { | |
| // Switch to RowMajor storage and print afterwards | |
| typedef typename T::Index IndexType; | |
| std::array<IndexType, rank> shuffle; | |
| std::array<IndexType, rank> 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<const TensorForcedEvalOp<const decltype(tensor_row_major)>, DefaultDevice> Evaluator; | |
| TensorForcedEvalOp<const decltype(tensor_row_major)> eval = tensor_row_major.eval(); | |
| Evaluator tensor(eval, DefaultDevice()); | |
| tensor.evalSubExprsIfNeeded(NULL); | |
| internal::TensorPrinter<Evaluator, rank, Format>::run(os, tensor, wf.t_format); | |
| // Cleanup. | |
| tensor.cleanup(); | |
| return os; | |
| } | |
| protected: | |
| T t_tensor; | |
| Format t_format; | |
| }; | |
| template <typename T, typename Format> | |
| class TensorWithFormat<T, ColMajor, 0, Format> { | |
| public: | |
| TensorWithFormat(const T& tensor, const Format& format) : t_tensor(tensor), t_format(format) {} | |
| friend std::ostream& operator<<(std::ostream& os, const TensorWithFormat<T, ColMajor, 0, Format>& wf) { | |
| // Evaluate the expression if needed | |
| typedef TensorEvaluator<const TensorForcedEvalOp<const T>, DefaultDevice> Evaluator; | |
| TensorForcedEvalOp<const T> eval = wf.t_tensor.eval(); | |
| Evaluator tensor(eval, DefaultDevice()); | |
| tensor.evalSubExprsIfNeeded(NULL); | |
| internal::TensorPrinter<Evaluator, 0, Format>::run(os, tensor, wf.t_format); | |
| // Cleanup. | |
| tensor.cleanup(); | |
| return os; | |
| } | |
| protected: | |
| T t_tensor; | |
| Format t_format; | |
| }; | |
| namespace internal { | |
| // Default scalar printer. | |
| template <typename Scalar, typename Format, typename EnableIf = void> | |
| struct ScalarPrinter { | |
| static void run(std::ostream& stream, const Scalar& scalar, const Format&) { stream << scalar; } | |
| }; | |
| template <typename Scalar> | |
| struct ScalarPrinter<Scalar, TensorIOFormatNumpy, std::enable_if_t<NumTraits<Scalar>::IsComplex>> { | |
| static void run(std::ostream& stream, const Scalar& scalar, const TensorIOFormatNumpy&) { | |
| stream << numext::real(scalar) << "+" << numext::imag(scalar) << "j"; | |
| } | |
| }; | |
| template <typename Scalar> | |
| struct ScalarPrinter<Scalar, TensorIOFormatNative, std::enable_if_t<NumTraits<Scalar>::IsComplex>> { | |
| static void run(std::ostream& stream, const Scalar& scalar, const TensorIOFormatNative&) { | |
| stream << "{" << numext::real(scalar) << ", " << numext::imag(scalar) << "}"; | |
| } | |
| }; | |
| template <typename Tensor, std::size_t rank, typename Format, typename EnableIf> | |
| struct TensorPrinter { | |
| using Scalar = std::remove_const_t<typename Tensor::Scalar>; | |
| 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<is_same<Scalar, char>::value || is_same<Scalar, unsigned char>::value || | |
| is_same<Scalar, numext::int8_t>::value || is_same<Scalar, numext::uint8_t>::value, | |
| int, | |
| std::conditional_t<is_same<Scalar, std::complex<char>>::value || | |
| is_same<Scalar, std::complex<unsigned char>>::value || | |
| is_same<Scalar, std::complex<numext::int8_t>>::value || | |
| is_same<Scalar, std::complex<numext::uint8_t>>::value, | |
| std::complex<int>, 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<Scalar>::IsInteger) { | |
| explicit_precision = 0; | |
| } else { | |
| explicit_precision = significant_decimals_impl<Scalar>::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<Scalar, Format>::run(sstr, static_cast<PrintType>(tensor.data()[i]), fmt); | |
| width = std::max<IndexType>(width, IndexType(sstr.str().length())); | |
| } | |
| } | |
| s << fmt.tenPrefix; | |
| for (IndexType i = 0; i < total_size; i++) { | |
| std::array<bool, rank> is_at_end{}; | |
| std::array<bool, rank> 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<IndexType>())) == | |
| 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<IndexType>())) == | |
| 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<std::size_t>(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<Scalar, Format>::run(sstr, static_cast<PrintType>(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 <typename Tensor, std::size_t rank> | |
| struct TensorPrinter<Tensor, rank, TensorIOFormatLegacy, std::enable_if_t<rank != 0>> { | |
| using Format = TensorIOFormatLegacy; | |
| using Scalar = std::remove_const_t<typename Tensor::Scalar>; | |
| 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<const Array<Scalar, Dynamic, Dynamic, Tensor::Layout>> matrix(tensor.data(), first_dim, | |
| total_size / first_dim); | |
| s << matrix; | |
| return; | |
| } | |
| } | |
| }; | |
| template <typename Tensor, typename Format> | |
| struct TensorPrinter<Tensor, 0, Format> { | |
| static void run(std::ostream& s, const Tensor& tensor, const Format& fmt) { | |
| using Scalar = std::remove_const_t<typename Tensor::Scalar>; | |
| std::streamsize explicit_precision; | |
| if (fmt.precision == StreamPrecision) { | |
| explicit_precision = 0; | |
| } else if (fmt.precision == FullPrecision) { | |
| if (NumTraits<Scalar>::IsInteger) { | |
| explicit_precision = 0; | |
| } else { | |
| explicit_precision = significant_decimals_impl<Scalar>::run(); | |
| } | |
| } else { | |
| explicit_precision = fmt.precision; | |
| } | |
| std::streamsize old_precision = 0; | |
| if (explicit_precision) old_precision = s.precision(explicit_precision); | |
| s << fmt.tenPrefix; | |
| ScalarPrinter<Scalar, Format>::run(s, tensor.coeff(0), fmt); | |
| s << fmt.tenSuffix; | |
| if (explicit_precision) s.precision(old_precision); | |
| } | |
| }; | |
| } // end namespace internal | |
| template <typename T> | |
| std::ostream& operator<<(std::ostream& s, const TensorBase<T, ReadOnlyAccessors>& t) { | |
| s << t.format(TensorIOFormat::Plain()); | |
| return s; | |
| } | |
| } // end namespace Eigen | |
| #endif // EIGEN_CXX11_TENSOR_TENSOR_IO_H |