Program Listing for File tensor.h¶
↰ Return to documentation for file (src/functional/tensor.h
)
#pragma once
#include "functional/array.h"
#include "functional/shape.h"
#include "tensors/tensor.h"
namespace marian {
namespace functional {
// By default for single valued types like float do nothing. Usually the number of elements in a tensor
// is correctly mirrored in the shape object. Only special multi-element types like float32x4 (4 floats),
// float32x8 (8 floats) and half2 (2 half) require special handling done by specializations below.
// Similar for multi-element integer types to be added later.
template <typename T>
inline marian::Shape adapt(const marian::Shape& shape) {
return shape;
}
#ifndef __CUDACC__ // vectorized types not available from .cu files
// modify last shape dimension to automatically map to a larger stride. We are moving now by 4 floats
// at once and need to stop earlier. This is a shallow typecast to bascially an array of 4 floats.
template <>
inline marian::Shape adapt<float32x4>(const marian::Shape& shape) {
ABORT_IF(shape[-1] % 4 != 0,
"Last dim ({}) is not a multiple of 4 while converting to Tensor<float32x4>",
shape[-1]);
marian::Shape x4Shape = shape;
x4Shape.set(-1, shape[-1] / 4);
return x4Shape;
}
#ifdef __AVX__
// as above, but for a stride of 8, since we are processing 8 floats at once
template <>
inline marian::Shape adapt<float32x8>(const marian::Shape& shape) {
ABORT_IF(shape[-1] % 8 != 0,
"Last dim ({}) is not a multiple of 8 while converting to Tensor<float32x8>",
shape[-1]);
marian::Shape x8Shape = shape;
x8Shape.set(-1, shape[-1] / 8);
return x8Shape;
}
#endif
#endif
#if COMPILE_FP16
// as above, but for a stride of 2, since we are processing 2 half floats at once. Works on GPU.
template <>
inline marian::Shape adapt<halfx2>(const marian::Shape& shape) {
ABORT_IF(shape[-1] % 2 != 0,
"Last dim ({}) is not a multiple of 2 while converting to Tensor<halfx2>",
shape[-1]);
marian::Shape x2Shape = shape;
x2Shape.set(-1, shape[-1] / 2);
return x2Shape;
}
#endif
template <typename T, const int D>
struct View {
T* data_;
ConstantShape<D> shape_;
HOST_DEVICE View() {}
HOST_DEVICE View(T* ptr, const ConstantShape<D>& shape)
: data_(ptr), shape_(shape) {}
HOST View(marian::Tensor t) : data_(t->data<T>()), shape_(adapt<T>(t->shape())) {}
HOST_DEVICE_INLINE T& operator[](size_t i) {
return data_[shape_.index((int)i)];
}
HOST_DEVICE_INLINE const T& operator[](size_t i) const {
return data_[shape_.index(i)];
}
HOST_DEVICE_INLINE T& operator[](const Array<int, D>& indices) {
return data_[shape_.index(indices)];
}
HOST_DEVICE_INLINE const T& operator[](const Array<int, D>& indices) const {
return data_[shape_.index(indices)];
}
HOST_DEVICE_INLINE T* data() { return data_; }
HOST_DEVICE_INLINE const T* data() const { return data_; }
HOST_DEVICE_INLINE ConstantShape<D>& shape() { return shape_; }
HOST_DEVICE_INLINE const ConstantShape<D>& shape() const { return shape_; }
HOST_DEVICE_INLINE size_t size() const { return shape_.elements(); }
// @TODO: This is code duplication from marian::Tensor
std::string debug(int precision = 8, int dispCols = 5) {
std::stringstream strm;
assert(shape_.size());
strm << shape_;
strm << " type=" << request<T>();
strm << " ptr=" << (size_t)data_;
strm << std::endl;
size_t totSize = shape_.elements();
std::vector<T> values(totSize);
for(int i = 0; i < size(); ++i)
values[i] = operator[](i);
int colWidth = precision + 4;
strm << std::fixed << std::setprecision(precision) << std::setfill(' ');
for(int i = 0; i < values.size(); ++i) {
Array<int, D> dims;
shape().dims(i, dims);
bool disp = true;
for(int j = 0; j < dims.size(); ++j)
disp = disp && (dims[j] < dispCols || dims[j] >= shape()[j] - dispCols);
if(disp) {
if(dims.back() == 0) {
bool par = true;
std::vector<std::string> p;
for(int j = (int)dims.size() - 1; j >= 0; --j) {
if(dims[j] != 0)
par = false;
p.push_back(par ? "[" : " ");
}
for(auto it = p.rbegin(); it != p.rend(); ++it)
strm << *it;
strm << " ";
}
strm << std::setw(colWidth);
strm << values[i];
strm << " ";
if(dims.back() + 1 == shape().back()) {
for(int j = (int)dims.size() - 1; j >= 0; --j) {
if(dims[j] + 1 != shape()[j])
break;
strm << "]";
}
strm << std::endl;
}
bool prev = true;
for(int j = (int)dims.size() - 1; j >= 0; --j) {
if(j < (int)dims.size() - 1)
prev = prev && dims[j + 1] + 1 == shape()[j + 1];
if(prev && dims[j] + 1 == dispCols && shape()[j] > 2 * dispCols) {
if(j < (int)dims.size() - 1)
for(int k = 0; k <= j; ++k)
strm << " ";
strm << "... ";
if(j < (int)dims.size() - 1)
strm << std::endl;
break;
}
}
}
}
strm << std::endl;
return strm.str();
}
};
// @TODO: Attempts at correct slicing, not supported anywhere yet.
#if 0
template <typename T, const int D>
HOST_DEVICE_INLINE View<T, D> slice(View<T, D> view, const Array<Slice, D>& slices) {
const auto& slicedShape = view.shape().slice(slices);
return View<T, D>(view.data(), slicedShape);
}
// template <typename T, const int D, class ...Slices>
// View<T, D> slice(View<T, D> view,
// const Slices&... slices) {
// return slice(view, {slices...});
// }
template <typename T>
HOST_DEVICE_INLINE View<T, 1> slice(View<T, 1>& view,
const Slice& slice0) {
return slice(view, {slice0});
}
template <typename T>
HOST_DEVICE_INLINE View<T, 2> slice(View<T, 2>& view,
const Slice& slice0,
const Slice& slice1) {
return slice(view, {slice0, slice1});
}
template <typename T>
HOST_DEVICE_INLINE View<T, 3> slice(View<T, 3>& view,
const Slice& slice0,
const Slice& slice1,
const Slice& slice2) {
return slice(view, {slice0, slice1, slice2});
}
template <typename T>
HOST_DEVICE_INLINE View<T, 4> slice(View<T, 4>& view,
const Slice& slice0,
const Slice& slice1,
const Slice& slice2,
const Slice& slice3) {
return slice(view, {slice0, slice1, slice2, slice3});
}
// template <typename T, const int D1, const int D2>
// View<T, D2> reshape(View<T, D1>& view, const ConstantShape<D2>& shape) {
// auto reshaped = view.shape().reshape(shape);
// return View<T, D2>(view.data(), reshaped);
// }
#endif
template <typename T>
using Tensor = View<T, CONST_SHAPE_DIMS>;
} // namespace functional
} // namespace marian