// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // 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/.
// clang-format off #include"main.h" #include <Eigen/CXX11/Tensor> // clang-format on
using Eigen::internal::TensorBlockDescriptor; using Eigen::internal::TensorExecutor;
// -------------------------------------------------------------------------- // // Utility functions to generate random tensors, blocks, and evaluate them.
template <int NumDims> static DSizes<Index, NumDims> RandomDims(Index min, Index max) {
DSizes<Index, NumDims> dims; for (int i = 0; i < NumDims; ++i) {
dims[i] = internal::random<Index>(min, max);
} return DSizes<Index, NumDims>(dims);
}
// Block offsets and extents allows to construct a TensorSlicingOp corresponding // to a TensorBlockDescriptor. template <int NumDims> struct TensorBlockParams {
DSizes<Index, NumDims> offsets;
DSizes<Index, NumDims> sizes;
TensorBlockDescriptor<NumDims, Index> desc;
};
template <int Layout, int NumDims> static TensorBlockParams<NumDims> RandomBlock(DSizes<Index, NumDims> dims,
Index min, Index max) { // Choose random offsets and sizes along all tensor dimensions.
DSizes<Index, NumDims> offsets(RandomDims<NumDims>(min, max));
DSizes<Index, NumDims> sizes(RandomDims<NumDims>(min, max));
// Make sure that offset + size do not overflow dims. for (int i = 0; i < NumDims; ++i) {
offsets[i] = numext::mini(dims[i] - 1, offsets[i]);
sizes[i] = numext::mini(sizes[i], dims[i] - offsets[i]);
}
Index offset = 0;
DSizes<Index, NumDims> strides = Eigen::internal::strides<Layout>(dims); for (int i = 0; i < NumDims; ++i) {
offset += strides[i] * offsets[i];
}
// Generate block with block sizes skewed towards inner dimensions. This type of // block is required for evaluating broadcast expressions. template <int Layout, int NumDims> static TensorBlockParams<NumDims> SkewedInnerBlock(
DSizes<Index, NumDims> dims) { using BlockMapper = internal::TensorBlockMapper<NumDims, Layout, Index>;
BlockMapper block_mapper(dims,
{internal::TensorBlockShapeType::kSkewedInnerDims,
internal::random<size_t>(1, dims.TotalSize()),
{0, 0, 0}});
Index total_blocks = block_mapper.blockCount();
Index block_index = internal::random<Index>(0, total_blocks - 1); auto block = block_mapper.blockDescriptor(block_index);
DSizes<Index, NumDims> sizes = block.dimensions();
auto strides = internal::strides<Layout>(dims);
DSizes<Index, NumDims> offsets;
// Compute offsets for the first block coefficient.
Index index = block.offset(); if (static_cast<int>(Layout) == static_cast<int>(ColMajor)) { for (int i = NumDims - 1; i > 0; --i) { const Index idx = index / strides[i];
index -= idx * strides[i];
offsets[i] = idx;
} if (NumDims > 0) offsets[0] = index;
} else { for (int i = 0; i < NumDims - 1; ++i) { const Index idx = index / strides[i];
index -= idx * strides[i];
offsets[i] = idx;
} if (NumDims > 0) offsets[NumDims - 1] = index;
}
return {offsets, sizes, block};
}
template <int NumDims> static TensorBlockParams<NumDims> FixedSizeBlock(DSizes<Index, NumDims> dims) {
DSizes<Index, NumDims> offsets; for (int i = 0; i < NumDims; ++i) offsets[i] = 0;
// -------------------------------------------------------------------------- // // Verify that block expression evaluation produces the same result as a // TensorSliceOp (reading a tensor block is same to taking a tensor slice).
template <typename T, int NumDims, int Layout, typename Expression, typename GenBlockParams> staticvoid VerifyBlockEvaluator(Expression expr, GenBlockParams gen_block) { using Device = DefaultDevice; auto d = Device();
// TensorEvaluator is needed to produce tensor blocks of the expression. auto eval = TensorEvaluator<const decltype(expr), Device>(expr, d);
eval.evalSubExprsIfNeeded(nullptr);
// Choose a random offsets, sizes and TensorBlockDescriptor.
TensorBlockParams<NumDims> block_params = gen_block();
// Evaluate TensorBlock expression into a tensor.
Tensor<T, NumDims, Layout> block(block_params.desc.dimensions());
// Dimensions for the potential destination buffer.
DSizes<Index, NumDims> dst_dims; if (internal::random<bool>()) {
dst_dims = block_params.desc.dimensions();
} else { for (int i = 0; i < NumDims; ++i) {
Index extent = internal::random<Index>(0, 5);
dst_dims[i] = block_params.desc.dimension(i) + extent;
}
}
// Maybe use this tensor as a block desc destination.
Tensor<T, NumDims, Layout> dst(dst_dims);
dst.setZero(); if (internal::random<bool>()) {
block_params.desc.template AddDestinationBuffer<Layout>(
dst.data(), internal::strides<Layout>(dst.dimensions()));
}
constbool root_of_expr = internal::random<bool>(); auto tensor_block = eval.block(block_params.desc, scratch, root_of_expr);
if (tensor_block.kind() == internal::TensorBlockKind::kMaterializedInOutput) { // Copy data from destination buffer. if (dimensions_match(dst.dimensions(), block.dimensions())) {
block = dst;
} else {
DSizes<Index, NumDims> offsets; for (int i = 0; i < NumDims; ++i) offsets[i] = 0;
block = dst.slice(offsets, block.dimensions());
}
} else { // Assign to block from expression. auto b_expr = tensor_block.expr();
// We explicitly disable vectorization and tiling, to run a simple coefficient // wise assignment loop, because it's very simple and should be correct. using BlockAssign = TensorAssignOp<decltype(block), const decltype(b_expr)>; using BlockExecutor = TensorExecutor<const BlockAssign, Device, false,
internal::TiledEvaluation::Off>;
BlockExecutor::run(BlockAssign(block, b_expr), d);
}
// Cleanup temporary buffers owned by a tensor block.
tensor_block.cleanup();
// Compute a Tensor slice corresponding to a Tensor block.
Tensor<T, NumDims, Layout> slice(block_params.desc.dimensions()); auto s_expr = expr.slice(block_params.offsets, block_params.sizes);
// Explicitly use coefficient assignment to evaluate slice expression. using SliceAssign = TensorAssignOp<decltype(slice), const decltype(s_expr)>; using SliceExecutor = TensorExecutor<const SliceAssign, Device, false,
internal::TiledEvaluation::Off>;
SliceExecutor::run(SliceAssign(slice, s_expr), d);
// Tensor block and tensor slice must be the same. for (Index i = 0; i < block.dimensions().TotalSize(); ++i) {
VERIFY_IS_EQUAL(block.coeff(i), slice.coeff(i));
}
}
// Check that desc.destination() memory is not shared between two broadcast // materializations.
VerifyBlockEvaluator<T, NumDims, Layout>(
input.broadcast(bcast) * input.abs().broadcast(bcast),
[&bcasted_dims]() { return SkewedInnerBlock<Layout>(bcasted_dims); });
}
template <typename T, int NumDims, int Layout> staticvoid test_eval_tensor_reshape() {
DSizes<Index, NumDims> dims = RandomDims<NumDims>(1, 10);
template<typename T, int NumDims> struct SimpleTensorGenerator {
T operator()(const array<Index, NumDims>& coords) const {
T result = static_cast<T>(0); for (int i = 0; i < NumDims; ++i) {
result += static_cast<T>((i + 1) * coords[i]);
} return result;
}
};
// Boolean specialization to avoid -Wint-in-bool-context warnings on GCC. template<int NumDims> struct SimpleTensorGenerator<bool, NumDims> { booloperator()(const array<Index, NumDims>& coords) const { bool result = false; for (int i = 0; i < NumDims; ++i) {
result ^= coords[i];
} return result;
}
};
// -------------------------------------------------------------------------- // // Verify that assigning block to a Tensor expression produces the same result // as an assignment to TensorSliceOp (writing a block is is identical to // assigning one tensor to a slice of another tensor).
template <typename T, int NumDims, int Layout, int NumExprDims = NumDims, typename Expression, typename GenBlockParams> staticvoid VerifyBlockAssignment(Tensor<T, NumDims, Layout>& tensor,
Expression expr, GenBlockParams gen_block) { using Device = DefaultDevice; auto d = Device();
// We use tensor evaluator as a target for block and slice assignments. auto eval = TensorEvaluator<decltype(expr), Device>(expr, d);
// Generate a random block, or choose a block that fits in full expression.
TensorBlockParams<NumExprDims> block_params = gen_block();
// Generate random data of the selected block size.
Tensor<T, NumExprDims, Layout> block(block_params.desc.dimensions());
block.setRandom();
// ************************************************************************ // // (1) Assignment from a block.
// Construct a materialize block from a random generated block tensor.
internal::TensorMaterializedBlock<T, NumExprDims, Layout> blk(
internal::TensorBlockKind::kView, block.data(), block.dimensions());
// Reset all underlying tensor values to zero.
tensor.setZero();
// Use evaluator to write block into a tensor.
eval.writeBlock(block_params.desc, blk);
// Make a copy of the result after assignment.
Tensor<T, NumDims, Layout> block_assigned = tensor;
// ************************************************************************ // // (2) Assignment to a slice
// Reset all underlying tensor values to zero.
tensor.setZero();
// Assign block to a slice of original expression auto s_expr = expr.slice(block_params.offsets, block_params.sizes);
// Explicitly use coefficient assignment to evaluate slice expression. using SliceAssign = TensorAssignOp<decltype(s_expr), const decltype(block)>; using SliceExecutor = TensorExecutor<const SliceAssign, Device, false,
internal::TiledEvaluation::Off>;
SliceExecutor::run(SliceAssign(s_expr, block), d);
// Make a copy of the result after assignment.
Tensor<T, NumDims, Layout> slice_assigned = tensor;
for (Index i = 0; i < tensor.dimensions().TotalSize(); ++i) {
VERIFY_IS_EQUAL(block_assigned.coeff(i), slice_assigned.coeff(i));
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.