I am working on a static multidimensional array contraction framework, and I have encountered a problem which is somewhat difficult to explain but I will try my best. Suppose we have a N
dimensional array class
template<typename T, int ... dims>
class Array {}
which could be instantiated as
Array<double> scalar;
Array<double,4> vector_of_4s;
Array<float,2,3> matrix_of_2_by_3;
// and so on
Now we have another class called Indices
template<int ... Idx>
struct Indices {}
I have a function contraction
now whose signature should look like the following
template<T, int ... Dims, int ... Idx,
typename std::enable_if<sizeof...(Dims)==sizeof...(Idx),bool>::type=0>
Array<T,apply_to_dims<Dims...,do_contract<Idx...>>>
contraction(const Indices<Idx...> &idx, const Array<T,Dims...> &a)
I may not have gotten the syntax right here, but I essentially want the returned Array
to have a dimension based on the entries of Indices
. Let me provide examples of what a contraction
can perform. Note that, in this context, contraction means removal of dimensions for which the parameters in index list is equal.
auto arr = contraction(Indices<0,0>, Array<double,3,3>)
// arr is Array<double> as both indices contract 0==0
auto arr = contraction(Indices<0,1>, Array<double,3,3>)
// arr is Array<double,3,3> as no contraction happens here, 0!=1
auto arr = contraction(Indices<0,1,0>, Array<double,3,4,3>)
// arr is Array<double,4> as 1st and 3rd indices contract 0==0
auto arr = contraction(Indices<0,1,0,7,7,2>, Array<double,3,4,3,5,5,6>)
// arr is Array<double,4,6> as (1st and 3rd, 0==0) and (4th and 5th, 7==7) indices contract
auto arr = contraction(Indices<10,10,2,3>, Array<double,5,6,4,4>
// should not compile as contraction between 1st and 2nd arguments
// requested but dimensions don't match 5!=6
// The parameters of Indices really do not matter as long as
// we can identify contractions. They are typically expressed as enums, I,J,K...
So essentially, given Idx...
and Dims...
which should both be of equal size, check which values in Idx...
are equal, get the positions at which they occur and remove the corresponding entries (positions) in Dims...
. This is essentially a tensor contraction rule.
Rules of array contraction:
- The number of parameters of indices and the dimension/rank of the array should be the same, i.e.
sizeof...(Idx)==sizeof...(Dims)
- There is one-to-one correspondence bewteen
Idx
andDims
i.e. if we haveIndices<0,1,2>
andArray<double,4,5,6>
,0
maps to4
,1
maps to5
and2
maps to6
. - If there are identical/equal values in
Idx
, that means a contraction, meaning the corresponding dimensions inDims
should vanish, for example, if we haveIndices<0,0,3>
andArray<double,4,4,6>
, then0==0
and the corresponding dimensions that these values map to which are4
and4
both need to vanish and the resulting array should beArray<double,6>
- If
Idx
has identical values, but the correspondingDims
don't match, then a compile time error should be triggered, for instance,Indices<0,0,3>
andArray<double,4,5,6>
is not possible as4!=5
, similarlyIndices<0,1,0>
would not be possible as4!=6
, which leads to - No contraction is possible for arrays with different dimensions, for instance
Array<double,4,5,6>
cannot be contracted in any which way. - Multiple pairs, triplets, quadruplets and so on, is allowed for
Idx
as long as the correspondingDims
also match, for instanceIndices<0,0,0,0,1,1,4,3,3,7,7,7>
would contract to anArray<double,6>
, given the input array wasArray<double,2,2,2,2,3,3,6,2,2,3,3,3>
.
My knowledge of metaprogamming does not go that far to achieve this functionality, but I hope I have made the intent clear, for someone to guide me in the right direction.