Index Spaces
An Index is an integral type to index into a tensor. Typically,
an int or a typed version thereof.
IndexSpace
An index space maps the values (referred to as indices) in an integer interval to a collection of indices.
// Construct an index space spanning from 0 to N-1 => [0,N) // By giving a count - IndexSpace(Range r) IndexSpace is1{range(10)}; // indices => {0,1,2,3,4,5,6,7,8,9} // Range based constructor with named sub-spaces IndexSpace is2{range(10), // is2("all) => {0,1,2,3,4,5,6,7,8,9} {{"occ", {range(0,5)}}, // is2("occ") => {0,1,2,3,4} {"virt", {range(5,10)}}}}; // is2("virt") => {5,6,7,8,9} // By specifying the indices it represents - IndexSpace(std::initializer_list<Index> list) IndexSpace is3{0,1,2,3,4} // indices => {0,1,2,3,4} // By giving a range - IndexSpace(Range r) IndexSpace is4{range(0, 10)}; // indices => {0,1,2,3,4,5,6,7,8,9} IndexSpace is5{range(5, 10)}; // indices => {5,6,7,8,9}
An index space can be queried to get a point at an index, or the index for a point. [Note the discussion below about non-disjoint index spaces and getting an index for a point]
// Reference IndexSpace objects IndexSpace is4{range(0, 10)}; // indices => {0,1,2,3,4,5,6,7,8,9} IndexSpace is5{range(5, 10)}; // indices => {5,6,7,8,9} // Get the index value from an index space // By using point method - Index IndexSpace::index(Index i) Index i = is4.index(Index{4}); // index i => 4 // By using operator[] - Index IndexSpace::operator[](Index i) Index j = is5[Index{4}]; // index j => 9
An index space can be constructed by aggregating/concatenating other index spaces. In this case, the index spaces maps an interval [0,N-1], where N is the sum of sizes of all aggregated index spaces, to the points in the index spaces aggregated.
occandvirtare used frequently in this documentation, although they can be any strings, such assub0orsub1.// Reference IndexSpace objects IndexSpace is3{0,1,2,3,4} // indices => {0,1,2,3,4} IndexSpace is5{range(5, 10)}; // indices => {5,6,7,8,9} // Constructing index spaces from other index spaces // IndexSpace(std::vector<IndexSpace> index_spaces) IndexSpace is6{{is3, is5}}; // indices => {0,1,2,3,4,5,6,7,8,9} IndexSpace is7{{is5, is3}}; // indices => {5,6,7,8,9,0,1,2,3,4} // IndexSpace aggregation with named sub-spaces IndexSpace is9{{is3, is5}, // indices => {0,1,2,3,4,5,6,7,8,9} {"occ", "virt"} // is9("occ") => {0,1,2,3,4} }; // is9("virt")=> {5,6,7,8,9}
The aggregation might be disjoint or non-disjoint. The same point might appear multiple times in non-disjoint index space. Some operations might not be defined on such an index space (e.g. getting the index for a particular point in the index space).
// Reference IndexSpace objects IndexSpace is3{0,1,2,3,4} // indices => {0,1,2,3,4} IndexSpace is5{range(5, 10)}; // indices => {5,6,7,8,9} // Disjoint aggregation IndexSpace is9{{is3, is5}}; // indices => {0,1,2,3,4,5,6,7,8,9} // Non-disjoint aggregation IndexSpace is10{{is3, is3}}; // indices => {0,1,2,3,4,0,1,2,3,4}
Sub-Space: An index space can be constructed from a permuted sub-list of indices in another index space (referred to as the parent index space). In this case, the domain is [0,N-1], where N is the size of the sub-list, and the points are the points in the parent index space.
// Reference IndexSpace object IndexSpace is1{range(10)}; // indices => {0,1,2,3,4,5,6,7,8,9} // Sub-space by permuting the indices of another index space // By specifying sub-space with range IndexSpace is11{is1, range(0, 4)}; // indices => {0,1,2,3} // By specifying range using the reference index space IndexSpace is12{is1, range(4, is1.num_indices())}; // indices => {4,5,6,7,8,9} // Constructing from the full index space IndexSpace is13{is1}; // indices => {0,1,2,3,4,5,6,7,8,9} // Sub-index space construction with name sub-spaces IndexSpace is14{is1, range(0,10,2), // indices => {0,2,4,6,8} {{"occ", {range(0,3)}}, // is14("occ") => {0,2,4} {"virt", {range(3,5)}}}}; // is14("virt")=> {6,8}
Accessing sub-spaces: Sub-spaces of an index space can be accessed using the names used to describe them. Keyword
allis for accessing the whole index space.// Accessing the named sub-spaces of an index space auto is14_all = is14("all"); // indices => {0,2,4,6,8} auto is14_occ = is14("occ"); // indices => {0,2,4} auto is14_virt = is14("virt"); // indices => {6,8}
NOTE: An index space is treated as a read-only object after it is constructed.
IndexSpace Specialization
Attributes: An index space might partition its indices into groups, each of which is associated with a set of attributes. All indices in a group have the same attribute values. Attribute specification is part of the constructor.
// Index space constructor with spin specialization // Combine index spaces with different spin attributes IndexSpace is15{range(100), // is15("all") => {0,...,99} {{"occ", {range(0,50)}}, // is15("occ") => {0,...,49} {"virt", {range(50,100)}}, // is15("virt") => {50,...,99} {"alpha", {range(0,25), range(50,75)}} // is15("alpha") => {0,...,25,50,...,74} {"beta", {range(25,50), range(75,100)}}}, // is15("beta") => {25,...,49,75,...,100} {{Spin{1}, {range(0,25), range(50,75)}}, {Spin{2}, {range(25,50), range(75,100)}}}};
Aggregation: An index space might be constructed from other index spaces and can be partitioned using the available partitions in the existing index spaces.
// Index space construction (will be used for aggregation) IndexSpace is16{range(100,200), // is16("all") => {100,...,199} {{"occ", {range(100,140)}}, // is16("occ") => {100,...,139} {"virt", {range(140,200)}}, // is16("virt") => {140,...,199} {"alpha", {range(100,125), range(150,175)}}, // is16("alpha") => {100,...,124,150,...,175} {"beta", {range(125,150), range(175,200)}}}, // is16("beta") => {125,...,149,175,...,199} {{Spin{1}, {range(100,125), range(150,175)}}, {Spin{2}, {range(125,150), range(175,200)}}}}}; // Construction of aggregated index space with subspace names IndexSpace is17{{is15, is16}, {"first", "second"}, {{"occ", {"first:occ", "second:occ"}}, {"virt", {"first:virt", "second:virt"}}, {"alpha",{"first:alpha", "second:alpha"}}, {"beta", {"first:beta", "second:beta"}}} }; // is17("occ") => is1 ~ {0,...,49,100,...,139} // is17("virt") => is2 ~ {50,...,99,140,...,199} // is17("alpha") => is15("alpha") + is16("alpha") ~ {25,...,49,75,...,99,125,...,149,175,...,199} // is17("beta") => is15("beta") + is16("beta") ~ {0,...,24,50,...,74,100,...,124,...,150,...,174}
Tiled Index Spaces
This section describes the TiledIndexSpace (both independent and dependent) as it will be used
in Tensor construction. Given an IndexSpace and a tiling size (this can be single tile or
a list of tile sizes with full coverage on the indices),
TiledIndexSpace, is the tiled version of the index space where each
tile has multiple indices. An IndexSpace is simply a single
tiled TiledIndexSpace. By default independent TiledIndexSpaces
(as well as TiledIndexLabels) are used to construct dense
tensors.
TiledIndexSpace: A tiled index space segments an index space. Specifically, it maps values (referred to as tile indices) in an integer to a index interval. A valid tiling ensures that all indices in a tile have the same attribute values.
Default tiling A TiledIndexSpace can be constructed from any IndexSpace where all tiles are of size 1.
// Reference IndexSpace IndexSpace is1{range(10)}; // indices = {0,1,2,3,4,5,6,7,8,9} // Constructing tiled index spaces - TiledIndexSpace(IndexSpace& is, size_t tile_size = 1) // Construction with default tiling size TiledIndexSpace tis1{is1}; // tiles = [0,1,2,3,4,5,6,7,8,9] && tile_size = 1 // Construction with specific tiling size TiledIndexSpace tis2{is1, /*blocked tile size*/ 5}; // indices = [{0,1,2,3,4},{5,6,7,8,9}] && tile_size = 5
Specialized tiling: A TiledIndexSpace can be constructed using a single tile size or a set of tile sizes which tiles the underlying IndexSpace completely (without gaps). NOTE: User provided set of tile sizes should also consider the named sub-spaces and any attributes related to input IndexSpace. AO and MO appear frequently as TiledIndexSpaces. While they have domain-specific meaning, it is not relevant to this documentation.
// TiledIndexSpace construction with single tile size TiledIndexSpace tis3{is1, 4}; // tiles = [{0,1,2,3}, {4,5,6,7}, {8,9}] && tile_size = 4 // TiledIndexSpace construction with a set of tile sizes TiledIndexSpace tis4{is1, {2,5,3}} // tiles = [{0,1}, {2,3,4,5,6}, {7,8,9}] && tile_sizes = [2,5,3]
IndexSpace AUX_is{/*...*/} IndexSpace AO_is{/*...*/}; IndexSpace MO_is{/*...*/}; size_t tile_size = /*some positive value*/; std::vector<size_t> tile_sizes = {/*multiple positive values*/}; TiledIndexSpace AUX{AUX_is, tile_size}; TiledIndexSpace AO{AO_is, tile_size}; TiledIndexSpace MO{MO_is, tile_sizes};
Sub-space: A TiledIndexSpace can be a constructed from another TiledIndexSpace by choosing a permuted sub-list of tiles in the parent TiledIndexSpace. NOTE: Tiling of a sub-index space is not the same the sub-space of a TiledIndexSpace.
// Constructing tiled sub-spaces from tiled index spaces // TiledIndexSpace(TiledIndexSpace& ref, range r) TiledIndexSpace tis5{tis1, range(0,5)}; // tiles = [{0},{1},{2},{3},{4}] && tile_size = 1 // By specifying range TiledIndexSpace tis6{tis2, range(1, tis2.num_tiles())} ; // indices = [{5,6,7,8,9}] && tile_size = 5
Convenience tiled index sub-spaces: A TiledIndexSpace stores and returns commonly used named sub-spaces of that TiledIndexSpace. Tiling is applied to all named sub index spaces. NOTE: An index space can be queried to be an index sub-space or an index range.
// Reference IndexSpace IndexSpace is2{range(10), // is2("all) => {0,1,2,3,4,5,6,7,8,9} {{"occ", {range(0,5)}}, // is2("occ") => {0,1,2,3,4} {"virt", {range(5,10)}}}}; // is2("virt") => {5,6,7,8,9} // Apply default tiling TiledIndexSpace tis_mo{is2, 3}; // Get a specific sub-space by identifier TiledIndexSpace& O = tis_mo("occ"); // tis_mo("occ") => [{0,1,2},{3,4}] TiledIndexSpace& V = tis_mo("virt"); // tis_mo("virt") => [{5,6,7},{8,9}] // Identifier "all" will implicitly return itself TiledIndexSpace& N = tis_mo("all"); // tis_mo("all") => [{0,1,2},{3,4},{5,6,7},{8,9}]
Dependent index space
Dependent index space: An index space can depend on other tiled index spaces. In this case, the index space becomes a relation that, given a specific value of its dependent index space tiles, returns an index space. Note that the dependency map used to construct the dependent index space is based on tiles from a tiled index space to another index space.
Constructing sparse tensors needs extra information to represent the sparsity as a dependency map between indices on different dimensions of the tensors. For this purpose, TAMM has dependent
TiledIndexSpaceconstructors, that will construct relation between differentTiledIndexSpaces. The main constructor requires a referenceTiledIndexSpacewhich will be the root/parent for the constructed relation. In other words this will be the domain of the dependency relation, for each indices in the dependency relation the domain will be a subset of thisTiledIndexSpace. Second argument for the constructor is a set ofTiledIndexSpaces where the dependencies are defined on, in other words this will be the range of the dependency relation. And as the final argument for constructing the dependentTiledIndexSpaceis the dependency map description (of typestd::map<IndexVector, TiledIndexSpace>). Note that the dependency map is defined over the tile indices, not actual indices in theIndexSpacedefinition.// @summary // In this example, we try to explain a MO space where the // span of indices are dependent on the Atom tiled space. // Creating index spaces MO, AO, and Atom IndexSpace MO{range(0, 100), {{"occ", range(0, 50)}, {"virt", range(50, 100)}}}; IndexSpace AO{range(100,200)}; IndexSpace Atom{range(0, 5)}; // Tile Atom space with default tiling TiledIndexSpace T_Atom{Atom}; // Construct dependency relation for Atom tiled indices // as the tiles are of size 1, we describe the dependency // over each index in Atom space. std::map<IndexVector, IndexSpace> dep_relation{ {IndexVector{0}, MO("occ")}, {IndexVector{1}, MO("virt")}, {IndexVector{2}, MO("occ")}, {IndexVector{3}, MO("virt")}, {IndexVector{4}, MO("occ")}}; // IndexSpace(const std::vector<TiledIndexSpace>& dep_spaces, // const std::map<IndexVector, IndexSpace> dep_relation) // Constructed index space will span over different portions // of MO space IndexSpace subMO_atom{{T_Atom}, dep_relation};
Tiling a DependentIndex: If the input IndexSpace to a TiledIndexSpace is a dependent IndexSpace, the tiling spans over the dependency relation. While constructing a sub-TiledIndexSpace from tiled dependent index space, users will have to construct the new dependency out of the tiled dependency
// Tiling dependent IndexSpaces TiledIndexSpace dep_tis{subMO_atom, 5}; // Dependency map from TiledIndexSpace const std::map<IndexVector, TiledIndexSpace>& t_dep_relation = dep_tis.tiled_dep_map(); // New sub dependency relation std::map<IndexVector, TiledIndexSpace> sub_relation{ {IndexVector{0}, TiledIndexSpace{t_dep_relation[IndexVector{0}], range(1)}}, {IndexVector{3}, TiledIndexSpace{t_dep_relation[IndexVector{3}], range(1,2)}} }; // Constructing a sub-TiledIndexSpace // Internally the new sub relation will checked for compatibility with the // reference dependency relation in the parent TiledIndexSpace TiledIndexSpace sub_dep_tis{dep_tis, sub_relation};
Labeling on Tiled IndexSpaces
TiledIndexLabel: A TiledIndexLabel pairs a TiledIndexSpace with an integer label. These labels can be created using TiledIndexSpace methods:
labels<N>(...)andlabel(...). (Note thatlabels<N>(...)method starts label value0by default if no value is provided, this might end up problematic label creation if used on the same tiled index space multiple times.)// Generate TiledIndexLabels for a specific sub-space TiledIndexLabel i, j, k, l, m, n; std::tie(i,j) = tis_mo.labels<2>("occ"); std::tie(k,l) = tis_mo.labels<2>("virt"); m = tis_mo.label("all", 9); n = tis_mo.label("all", 10);
Dependent TiledIndexLabel: To construct a dependent index label, TiledIndexLabel provides an
operator()overload where you can specify the dependency which later will be used for constructing and/or applying operations on tensors.// Construction of dependent TiledIndexSpace is same as below std::map<IndexVector, IndexSpace> dep_relation{}; // Tile Atom space with default tiling TiledIndexSpace T_Atom{Atom}; // Construct dependent TiledIndexSpace DependentIndexSpace subMO_atom{{T_Atom}, dep_relation}; // Construct TiledIndexLabels from TiledIndexSpaces TiledIndexLabel i, a; i = T_Atom.label("all"); a = subMO_atom.label("all")