User Defined Tiles
The default tile type of TiledArray::DistArray
is TiledArray::Tensor
. However, TiledArray supports using user-defined types as tiles. There are few scenarios where one would like to provide a non-standard type as a tile; for example, user wants to provide a more efficient implementation of certain operations on tiles. There are two modes of user-defined types that can be used as tiles: types that store the data elements explicitly (“data tiles”) and types that generate a data tile as needed (“lazy evaluation tiles”).
User-Defined Data Tiles
Any user-defined tensor type can play a role of a data tile provided it matches the same concept as TiledArray::Tensor
. For brevity, instead of an actual concept spec here is an example of a custom tile type that meets the concept spec.
class MyTensor {
public:
typedef MyTensor eval_type;
typedef ... value_type;
typedef ... numeric_type;
typedef ... size_type;
public:
MyTensor();
MyTensor(const MyTensor& other);
MyTensor& operator=(const MyTensor& other);
const range_type& range() const;
size_type size() const;
template <typename Archive>
void serialize(Archive& ar);
MyTensor
scale(
const numeric_type factor)
const;
MyTensor&
scale_to(
const numeric_type factor)
const;
MyTensor
add(
const MyTensor& right)
const;
MyTensor
add(
const MyTensor& right,
const numeric_type factor)
const;
MyTensor
add(
const value_type& value)
const;
MyTensor&
add_to(
const MyTensor& right)
const;
MyTensor&
add_to(
const MyTensor& right,
const numeric_type factor)
const;
MyTensor&
add_to(
const value_type& value)
const;
MyTensor
subt(
const MyTensor& right)
const;
MyTensor
subt(
const MyTensor& right,
const numeric_type factor)
const;
MyTensor
subt(
const value_type& value)
const;
MyTensor&
subt_to(
const MyTensor& right);
MyTensor&
subt_to(
const MyTensor& right,
const numeric_type factor);
MyTensor&
subt_to(
const value_type& value);
MyTensor
mult(
const MyTensor& right)
const;
MyTensor
mult(
const MyTensor& right,
const numeric_type factor)
const;
MyTensor&
mult_to(
const MyTensor& right);
MyTensor&
mult_to(
const MyTensor& right,
const numeric_type factor);
MyTensor
gemm(
const MyTensor& other,
const numeric_type factor,
MyTensor&
gemm(
const MyTensor& left,
const MyTensor& right,
const numeric_type factor,
numeric_type
trace()
const;
numeric_type
sum()
const;
numeric_type
norm()
const;
numeric_type
max()
const;
numeric_type
min()
const;
}
It is also possible to implement most of the concept requirements non-intrusively, by providing free functions. This can be helpful if you want to use an existing tensor class as a tile. Here’s an example of how to implement MyTensor without member functions:
class MyTensor {
public:
typedef ... value_type;
typedef ... numeric_type;
typedef ... size_type;
public:
MyTensor();
MyTensor(const MyTensor& other);
MyTensor& operator=(const MyTensor& other);
const range_type& range() const;
size_type size() const;
template <typename Archive>
void serialize(Archive& ar);
MyTensor
scale(
const numeric_type factor)
const;
MyTensor&
scale_to(
const numeric_type factor)
const;
}
struct eval_trait<MyTensor> {
typedef MyTensor type;
};
namespace math {
MyTensor
permute(
const MyTensor& tile,
MyTensor
add(
const MyTensor& arg1,
const MyTensor& arg2);
MyTensor
add(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::value_type factor);
MyTensor
add(
const MyTensor& arg,
const MyTensor::value_type& value);
MyTensor
add(
const MyTensor& arg1,
const MyTensor& arg2,
MyTensor
add(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor,
MyTensor
add(
const MyTensor& arg,
const MyTensor::value_type& value,
const MyTensor& arg);
const MyTensor& arg,
const MyTensor::numeric_type factor);
const MyTensor::value_type& value);
MyTensor
subt(
const MyTensor& arg1,
const MyTensor& arg2);
MyTensor
subt(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor);
MyTensor
subt(
const MyTensor& arg,
const MyTensor::value_type& value);
MyTensor
subt(
const MyTensor& arg1,
const MyTensor& arg2,
MyTensor
subt(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor,
MyTensor
subt(
const MyTensor& arg,
const MyTensor::value_type value,
const MyTensor& arg);
const MyTensor& arg,
const MyTensor::numeric_type factor);
const MyTensor::value_type& value);
MyTensor
mult(
const MyTensor& arg1,
const MyTensor& arg2);
MyTensor
mult(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor);
MyTensor
mult(
const MyTensor& arg1,
const MyTensor& arg2,
MyTensor
mult(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor,
const MyTensor& arg);
const MyTensor& arg,
const MyTensor::numeric_type factor);
MyTensor
neg(
const MyTensor& arg);
MyTensor
neg(
const MyTensor& arg,
void neg_to(MyTensor& result);
MyTensor
gemm(
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor,
void gemm(MyTensor& result,
const MyTensor& arg1,
const MyTensor& arg2,
const MyTensor::numeric_type factor,
MyTensor::numeric_type
trace(
const MyTensor& arg);
MyTensor::numeric_type
sum(
const MyTensor& arg);
MyTensor::numeric_type
product(
const MyTensor& arg);
MyTensor::numeric_type
norm(
const MyTensor& arg);
MyTensor::numeric_type
max(
const MyTensor& arg);
MyTensor::numeric_type
min(
const MyTensor& arg);
MyTensor::numeric_type
abs_max(
const MyTensor& arg);
MyTensor::numeric_type
abs_min(
const MyTensor& arg);
User-Defined Lazy Tiles
Lazy tiles are generated only when they are needed and discarded immediately after use. Common uses for lazy tiles include computing arrays on-the-fly, reading them from disk, etc. Lazy tiles are used internally by TiledArray::DistArray
to generate data tiles that are then fed into arithmetic operations.
The main requirements of lazy tiles are:
typedef ... eval_type
, which is the data tile type (e.g. TiledArray::Tensor).
eval_type
cannot be the same object type as the lazy tile itself.
explicit operator eval_type() const
, which is the function used to generate the data tile.
Lazy tiles should have the following interface.
class MyLazyTile {
public:
typedef ... eval_type;
MyLazyTile();
MyLazyTile(const MyLazyTile& other);
MyLazyTile& operator=(const MyLazyTile& other);
explicit operator eval_type() const;
template <typename Archive>
void serialize(const Archive&);
};
User Defined Shapes
You can define a shape object for your Array
object, which defines the sparsity of an array. A shape object is a replicated object, so you should design your shape object accordingly. You may implement an initialization algorithm for your shape that communicates with other processes. However, communication is not allowed after the object has been initialized, shape arithmetic operations must be completely local (non-communicating) operations.
class MyShape {
public:
bool validate(const Range& shape);
template <typename Index>
bool is_zero(const Index&);
bool is_dense();
template <typename Scalar>
MyShape
scale(
const Scalar factor);
template <typename Scalar>
MyShape
add(
const MyShape& right);
template <typename Scalar>
MyShape
add(
const MyShape& right,
const Scalar factor);
template <typename Scalar>
template <typename Scalar>
MyShape
add(
const Scalar value)
template <typename Scalar>
MyShape
subt(
const MyShape& right);
template <typename Scalar>
MyShape
subt(
const MyShape& right,
const Scalar factor);
template <typename Scalar>
template <typename Scalar>
MyShape
subt(
const Scalar value);
template <typename Scalar>
MyShape
mult(
const MyShape& right);
template <typename Scalar>
MyShape
mult(
const MyShape& right,
const Scalar factor);
template <typename Scalar>
template <typename Scalar>
MyShape
gemm(
const MyShape& right,
const Scalar factor,
template <typename Scalar>
MyShape
gemm(
const MyShape& right,
const Scalar factor,
};
User Defined Process Map
You can also create process maps for your Array
object, which is used by TiledArray to determine the process that owns a tile for a given Array
object. For a process map to be valid, all tiles are owned by exactly one process and all processes must agree on this tile ownership. The exception to these rules is a replicated process map. In addition, a process map must maintain a list of local tiles.
protected:
public:
MyPmap(madness::World& world, size_type size);
virtual ~MyPmap();
virtual size_type
owner(
const size_type tile)
const;
virtual bool is_local(
const size_type tile)
const;
};