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. .. code:: cpp // 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 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] .. code:: cpp // 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. ``occ`` and ``virt`` are used frequently in this documentation, although they can be any strings, such as ``sub0`` or ``sub1``. .. code:: cpp // 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 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). .. code:: cpp // 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. .. code:: cpp // 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 ``all`` is for accessing the whole index space. .. code:: cpp // 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. .. code:: cpp // 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. .. code:: cpp // 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 ``TiledIndexSpace``\ s (as well as ``TiledIndexLabel``\ s) 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. .. code:: cpp // 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. .. code:: cpp // 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] .. code:: cpp IndexSpace AUX_is{/*...*/} IndexSpace AO_is{/*...*/}; IndexSpace MO_is{/*...*/}; size_t tile_size = /*some positive value*/; std::vector 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. .. code:: cpp // 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. .. code:: cpp // 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* ``TiledIndexSpace`` constructors, that will construct relation between different ``TiledIndexSpace``\ s. The main constructor requires a reference ``TiledIndexSpace`` which 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 this ``TiledIndexSpace``. Second argument for the constructor is a set of ``TiledIndexSpace``\ s 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 dependent ``TiledIndexSpace`` is the dependency map description (of type ``std::map``). **Note that** the dependency map is defined over the tile indices, not actual indices in the ``IndexSpace`` definition. .. code:: cpp // @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 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& dep_spaces, // const std::map 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 .. code:: cpp // Tiling dependent IndexSpaces TiledIndexSpace dep_tis{subMO_atom, 5}; // Dependency map from TiledIndexSpace const std::map& t_dep_relation = dep_tis.tiled_dep_map(); // New sub dependency relation std::map 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(...)`` and ``label(...)``. (Note that ``labels(...)`` method starts label value ``0`` by default if no value is provided, this might end up problematic label creation if used on the same tiled index space multiple times.) .. code:: cpp // 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. .. code:: cpp // Construction of dependent TiledIndexSpace is same as below std::map 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")