18a91bc7bSHarrietAkot //===- SparseTensorUtils.cpp - Sparse Tensor Utils for MLIR execution -----===// 28a91bc7bSHarrietAkot // 38a91bc7bSHarrietAkot // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 48a91bc7bSHarrietAkot // See https://llvm.org/LICENSE.txt for license information. 58a91bc7bSHarrietAkot // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 68a91bc7bSHarrietAkot // 78a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 88a91bc7bSHarrietAkot // 98a91bc7bSHarrietAkot // This file implements a light-weight runtime support library that is useful 108a91bc7bSHarrietAkot // for sparse tensor manipulations. The functionality provided in this library 118a91bc7bSHarrietAkot // is meant to simplify benchmarking, testing, and debugging MLIR code that 128a91bc7bSHarrietAkot // operates on sparse tensors. The provided functionality is **not** part 138a91bc7bSHarrietAkot // of core MLIR, however. 148a91bc7bSHarrietAkot // 158a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 168a91bc7bSHarrietAkot 17845561ecSwren romano #include "mlir/ExecutionEngine/SparseTensorUtils.h" 188a91bc7bSHarrietAkot #include "mlir/ExecutionEngine/CRunnerUtils.h" 198a91bc7bSHarrietAkot 208a91bc7bSHarrietAkot #ifdef MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS 218a91bc7bSHarrietAkot 228a91bc7bSHarrietAkot #include <algorithm> 238a91bc7bSHarrietAkot #include <cassert> 248a91bc7bSHarrietAkot #include <cctype> 258a91bc7bSHarrietAkot #include <cinttypes> 268a91bc7bSHarrietAkot #include <cstdio> 278a91bc7bSHarrietAkot #include <cstdlib> 288a91bc7bSHarrietAkot #include <cstring> 29efa15f41SAart Bik #include <fstream> 30*753fe330Swren romano #include <functional> 31efa15f41SAart Bik #include <iostream> 324d0a18d0Swren romano #include <limits> 338a91bc7bSHarrietAkot #include <numeric> 348a91bc7bSHarrietAkot #include <vector> 358a91bc7bSHarrietAkot 368a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 378a91bc7bSHarrietAkot // 388a91bc7bSHarrietAkot // Internal support for storing and reading sparse tensors. 398a91bc7bSHarrietAkot // 408a91bc7bSHarrietAkot // The following memory-resident sparse storage schemes are supported: 418a91bc7bSHarrietAkot // 428a91bc7bSHarrietAkot // (a) A coordinate scheme for temporarily storing and lexicographically 438a91bc7bSHarrietAkot // sorting a sparse tensor by index (SparseTensorCOO). 448a91bc7bSHarrietAkot // 458a91bc7bSHarrietAkot // (b) A "one-size-fits-all" sparse tensor storage scheme defined by 468a91bc7bSHarrietAkot // per-dimension sparse/dense annnotations together with a dimension 478a91bc7bSHarrietAkot // ordering used by MLIR compiler-generated code (SparseTensorStorage). 488a91bc7bSHarrietAkot // 498a91bc7bSHarrietAkot // The following external formats are supported: 508a91bc7bSHarrietAkot // 518a91bc7bSHarrietAkot // (1) Matrix Market Exchange (MME): *.mtx 528a91bc7bSHarrietAkot // https://math.nist.gov/MatrixMarket/formats.html 538a91bc7bSHarrietAkot // 548a91bc7bSHarrietAkot // (2) Formidable Repository of Open Sparse Tensors and Tools (FROSTT): *.tns 558a91bc7bSHarrietAkot // http://frostt.io/tensors/file-formats.html 568a91bc7bSHarrietAkot // 578a91bc7bSHarrietAkot // Two public APIs are supported: 588a91bc7bSHarrietAkot // 598a91bc7bSHarrietAkot // (I) Methods operating on MLIR buffers (memrefs) to interact with sparse 608a91bc7bSHarrietAkot // tensors. These methods should be used exclusively by MLIR 618a91bc7bSHarrietAkot // compiler-generated code. 628a91bc7bSHarrietAkot // 638a91bc7bSHarrietAkot // (II) Methods that accept C-style data structures to interact with sparse 648a91bc7bSHarrietAkot // tensors. These methods can be used by any external runtime that wants 658a91bc7bSHarrietAkot // to interact with MLIR compiler-generated code. 668a91bc7bSHarrietAkot // 678a91bc7bSHarrietAkot // In both cases (I) and (II), the SparseTensorStorage format is externally 688a91bc7bSHarrietAkot // only visible as an opaque pointer. 698a91bc7bSHarrietAkot // 708a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 718a91bc7bSHarrietAkot 728a91bc7bSHarrietAkot namespace { 738a91bc7bSHarrietAkot 7403fe15ceSAart Bik static constexpr int kColWidth = 1025; 7503fe15ceSAart Bik 7672ec2f76Swren romano /// A version of `operator*` on `uint64_t` which checks for overflows. 7772ec2f76Swren romano static inline uint64_t checkedMul(uint64_t lhs, uint64_t rhs) { 7872ec2f76Swren romano assert((lhs == 0 || rhs <= std::numeric_limits<uint64_t>::max() / lhs) && 7972ec2f76Swren romano "Integer overflow"); 8072ec2f76Swren romano return lhs * rhs; 8172ec2f76Swren romano } 8272ec2f76Swren romano 838a91bc7bSHarrietAkot /// A sparse tensor element in coordinate scheme (value and indices). 848a91bc7bSHarrietAkot /// For example, a rank-1 vector element would look like 858a91bc7bSHarrietAkot /// ({i}, a[i]) 868a91bc7bSHarrietAkot /// and a rank-5 tensor element like 878a91bc7bSHarrietAkot /// ({i,j,k,l,m}, a[i,j,k,l,m]) 88ccd047cbSAart Bik /// We use pointer to a shared index pool rather than e.g. a direct 89ccd047cbSAart Bik /// vector since that (1) reduces the per-element memory footprint, and 90ccd047cbSAart Bik /// (2) centralizes the memory reservation and (re)allocation to one place. 918a91bc7bSHarrietAkot template <typename V> 928a91bc7bSHarrietAkot struct Element { 93ccd047cbSAart Bik Element(uint64_t *ind, V val) : indices(ind), value(val){}; 94ccd047cbSAart Bik uint64_t *indices; // pointer into shared index pool 958a91bc7bSHarrietAkot V value; 968a91bc7bSHarrietAkot }; 978a91bc7bSHarrietAkot 98*753fe330Swren romano /// The type of callback functions which receive an element. We avoid 99*753fe330Swren romano /// packaging the coordinates and value together as an `Element` object 100*753fe330Swren romano /// because this helps keep code somewhat cleaner. 101*753fe330Swren romano template <typename V> 102*753fe330Swren romano using ElementConsumer = 103*753fe330Swren romano const std::function<void(const std::vector<uint64_t> &, V)> &; 104*753fe330Swren romano 1058a91bc7bSHarrietAkot /// A memory-resident sparse tensor in coordinate scheme (collection of 1068a91bc7bSHarrietAkot /// elements). This data structure is used to read a sparse tensor from 1078a91bc7bSHarrietAkot /// any external format into memory and sort the elements lexicographically 1088a91bc7bSHarrietAkot /// by indices before passing it back to the client (most packed storage 1098a91bc7bSHarrietAkot /// formats require the elements to appear in lexicographic index order). 1108a91bc7bSHarrietAkot template <typename V> 1118a91bc7bSHarrietAkot struct SparseTensorCOO { 1128a91bc7bSHarrietAkot public: 1138a91bc7bSHarrietAkot SparseTensorCOO(const std::vector<uint64_t> &szs, uint64_t capacity) 114db6796dfSMehdi Amini : sizes(szs) { 115ccd047cbSAart Bik if (capacity) { 1168a91bc7bSHarrietAkot elements.reserve(capacity); 117ccd047cbSAart Bik indices.reserve(capacity * getRank()); 1188a91bc7bSHarrietAkot } 119ccd047cbSAart Bik } 120ccd047cbSAart Bik 1218a91bc7bSHarrietAkot /// Adds element as indices and value. 1228a91bc7bSHarrietAkot void add(const std::vector<uint64_t> &ind, V val) { 1238a91bc7bSHarrietAkot assert(!iteratorLocked && "Attempt to add() after startIterator()"); 124ccd047cbSAart Bik uint64_t *base = indices.data(); 125ccd047cbSAart Bik uint64_t size = indices.size(); 1268a91bc7bSHarrietAkot uint64_t rank = getRank(); 1278a91bc7bSHarrietAkot assert(rank == ind.size()); 128ccd047cbSAart Bik for (uint64_t r = 0; r < rank; r++) { 1298a91bc7bSHarrietAkot assert(ind[r] < sizes[r]); // within bounds 130ccd047cbSAart Bik indices.push_back(ind[r]); 1318a91bc7bSHarrietAkot } 132ccd047cbSAart Bik // This base only changes if indices were reallocated. In that case, we 133ccd047cbSAart Bik // need to correct all previous pointers into the vector. Note that this 134ccd047cbSAart Bik // only happens if we did not set the initial capacity right, and then only 135ccd047cbSAart Bik // for every internal vector reallocation (which with the doubling rule 136ccd047cbSAart Bik // should only incur an amortized linear overhead). 137298d2fa1SMehdi Amini uint64_t *newBase = indices.data(); 138298d2fa1SMehdi Amini if (newBase != base) { 139ccd047cbSAart Bik for (uint64_t i = 0, n = elements.size(); i < n; i++) 140298d2fa1SMehdi Amini elements[i].indices = newBase + (elements[i].indices - base); 141298d2fa1SMehdi Amini base = newBase; 142ccd047cbSAart Bik } 143ccd047cbSAart Bik // Add element as (pointer into shared index pool, value) pair. 144ccd047cbSAart Bik elements.emplace_back(base + size, val); 145ccd047cbSAart Bik } 146ccd047cbSAart Bik 1478a91bc7bSHarrietAkot /// Sorts elements lexicographically by index. 1488a91bc7bSHarrietAkot void sort() { 1498a91bc7bSHarrietAkot assert(!iteratorLocked && "Attempt to sort() after startIterator()"); 150cf358253Swren romano // TODO: we may want to cache an `isSorted` bit, to avoid 151cf358253Swren romano // unnecessary/redundant sorting. 152ccd047cbSAart Bik std::sort(elements.begin(), elements.end(), 153ccd047cbSAart Bik [this](const Element<V> &e1, const Element<V> &e2) { 154ccd047cbSAart Bik uint64_t rank = getRank(); 155ccd047cbSAart Bik for (uint64_t r = 0; r < rank; r++) { 156ccd047cbSAart Bik if (e1.indices[r] == e2.indices[r]) 157ccd047cbSAart Bik continue; 158ccd047cbSAart Bik return e1.indices[r] < e2.indices[r]; 1598a91bc7bSHarrietAkot } 160ccd047cbSAart Bik return false; 161ccd047cbSAart Bik }); 162ccd047cbSAart Bik } 163ccd047cbSAart Bik 1648a91bc7bSHarrietAkot /// Returns rank. 1658a91bc7bSHarrietAkot uint64_t getRank() const { return sizes.size(); } 166ccd047cbSAart Bik 1678a91bc7bSHarrietAkot /// Getter for sizes array. 1688a91bc7bSHarrietAkot const std::vector<uint64_t> &getSizes() const { return sizes; } 169ccd047cbSAart Bik 1708a91bc7bSHarrietAkot /// Getter for elements array. 1718a91bc7bSHarrietAkot const std::vector<Element<V>> &getElements() const { return elements; } 1728a91bc7bSHarrietAkot 1738a91bc7bSHarrietAkot /// Switch into iterator mode. 1748a91bc7bSHarrietAkot void startIterator() { 1758a91bc7bSHarrietAkot iteratorLocked = true; 1768a91bc7bSHarrietAkot iteratorPos = 0; 1778a91bc7bSHarrietAkot } 178ccd047cbSAart Bik 1798a91bc7bSHarrietAkot /// Get the next element. 1808a91bc7bSHarrietAkot const Element<V> *getNext() { 1818a91bc7bSHarrietAkot assert(iteratorLocked && "Attempt to getNext() before startIterator()"); 1828a91bc7bSHarrietAkot if (iteratorPos < elements.size()) 1838a91bc7bSHarrietAkot return &(elements[iteratorPos++]); 1848a91bc7bSHarrietAkot iteratorLocked = false; 1858a91bc7bSHarrietAkot return nullptr; 1868a91bc7bSHarrietAkot } 1878a91bc7bSHarrietAkot 1888a91bc7bSHarrietAkot /// Factory method. Permutes the original dimensions according to 1898a91bc7bSHarrietAkot /// the given ordering and expects subsequent add() calls to honor 1908a91bc7bSHarrietAkot /// that same ordering for the given indices. The result is a 1918a91bc7bSHarrietAkot /// fully permuted coordinate scheme. 1928d8b566fSwren romano /// 1938d8b566fSwren romano /// Precondition: `sizes` and `perm` must be valid for `rank`. 1948a91bc7bSHarrietAkot static SparseTensorCOO<V> *newSparseTensorCOO(uint64_t rank, 1958a91bc7bSHarrietAkot const uint64_t *sizes, 1968a91bc7bSHarrietAkot const uint64_t *perm, 1978a91bc7bSHarrietAkot uint64_t capacity = 0) { 1988a91bc7bSHarrietAkot std::vector<uint64_t> permsz(rank); 199d83a7068Swren romano for (uint64_t r = 0; r < rank; r++) { 200d83a7068Swren romano assert(sizes[r] > 0 && "Dimension size zero has trivial storage"); 2018a91bc7bSHarrietAkot permsz[perm[r]] = sizes[r]; 202d83a7068Swren romano } 2038a91bc7bSHarrietAkot return new SparseTensorCOO<V>(permsz, capacity); 2048a91bc7bSHarrietAkot } 2058a91bc7bSHarrietAkot 2068a91bc7bSHarrietAkot private: 2078a91bc7bSHarrietAkot const std::vector<uint64_t> sizes; // per-dimension sizes 208ccd047cbSAart Bik std::vector<Element<V>> elements; // all COO elements 209ccd047cbSAart Bik std::vector<uint64_t> indices; // shared index pool 210db6796dfSMehdi Amini bool iteratorLocked = false; 211db6796dfSMehdi Amini unsigned iteratorPos = 0; 2128a91bc7bSHarrietAkot }; 2138a91bc7bSHarrietAkot 2148d8b566fSwren romano /// Abstract base class for `SparseTensorStorage<P,I,V>`. This class 2158d8b566fSwren romano /// takes responsibility for all the `<P,I,V>`-independent aspects 2168d8b566fSwren romano /// of the tensor (e.g., shape, sparsity, permutation). In addition, 2178d8b566fSwren romano /// we use function overloading to implement "partial" method 2188d8b566fSwren romano /// specialization, which the C-API relies on to catch type errors 2198d8b566fSwren romano /// arising from our use of opaque pointers. 2208a91bc7bSHarrietAkot class SparseTensorStorageBase { 2218a91bc7bSHarrietAkot public: 2228d8b566fSwren romano /// Constructs a new storage object. The `perm` maps the tensor's 2238d8b566fSwren romano /// semantic-ordering of dimensions to this object's storage-order. 2248d8b566fSwren romano /// The `szs` and `sparsity` arrays are already in storage-order. 2258d8b566fSwren romano /// 2268d8b566fSwren romano /// Precondition: `perm` and `sparsity` must be valid for `szs.size()`. 2278d8b566fSwren romano SparseTensorStorageBase(const std::vector<uint64_t> &szs, 2288d8b566fSwren romano const uint64_t *perm, const DimLevelType *sparsity) 2298d8b566fSwren romano : dimSizes(szs), rev(getRank()), 2308d8b566fSwren romano dimTypes(sparsity, sparsity + getRank()) { 231*753fe330Swren romano assert(perm && sparsity); 2328d8b566fSwren romano const uint64_t rank = getRank(); 2338d8b566fSwren romano // Validate parameters. 2348d8b566fSwren romano assert(rank > 0 && "Trivial shape is unsupported"); 2358d8b566fSwren romano for (uint64_t r = 0; r < rank; r++) { 2368d8b566fSwren romano assert(dimSizes[r] > 0 && "Dimension size zero has trivial storage"); 2378d8b566fSwren romano assert((dimTypes[r] == DimLevelType::kDense || 2388d8b566fSwren romano dimTypes[r] == DimLevelType::kCompressed) && 2398d8b566fSwren romano "Unsupported DimLevelType"); 2408d8b566fSwren romano } 2418d8b566fSwren romano // Construct the "reverse" (i.e., inverse) permutation. 2428d8b566fSwren romano for (uint64_t r = 0; r < rank; r++) 2438d8b566fSwren romano rev[perm[r]] = r; 2448d8b566fSwren romano } 2458d8b566fSwren romano 2468d8b566fSwren romano virtual ~SparseTensorStorageBase() = default; 2478d8b566fSwren romano 2488d8b566fSwren romano /// Get the rank of the tensor. 2498d8b566fSwren romano uint64_t getRank() const { return dimSizes.size(); } 2508d8b566fSwren romano 2518d8b566fSwren romano /// Getter for the dimension-sizes array, in storage-order. 2528d8b566fSwren romano const std::vector<uint64_t> &getDimSizes() const { return dimSizes; } 2538d8b566fSwren romano 2548d8b566fSwren romano /// Safely lookup the size of the given (storage-order) dimension. 2558d8b566fSwren romano uint64_t getDimSize(uint64_t d) const { 2568d8b566fSwren romano assert(d < getRank()); 2578d8b566fSwren romano return dimSizes[d]; 2588d8b566fSwren romano } 2598d8b566fSwren romano 2608d8b566fSwren romano /// Getter for the "reverse" permutation, which maps this object's 2618d8b566fSwren romano /// storage-order to the tensor's semantic-order. 2628d8b566fSwren romano const std::vector<uint64_t> &getRev() const { return rev; } 2638d8b566fSwren romano 2648d8b566fSwren romano /// Getter for the dimension-types array, in storage-order. 2658d8b566fSwren romano const std::vector<DimLevelType> &getDimTypes() const { return dimTypes; } 2668d8b566fSwren romano 2678d8b566fSwren romano /// Safely check if the (storage-order) dimension uses compressed storage. 2688d8b566fSwren romano bool isCompressedDim(uint64_t d) const { 2698d8b566fSwren romano assert(d < getRank()); 2708d8b566fSwren romano return (dimTypes[d] == DimLevelType::kCompressed); 2718d8b566fSwren romano } 2728a91bc7bSHarrietAkot 2734f2ec7f9SAart Bik /// Overhead storage. 2748a91bc7bSHarrietAkot virtual void getPointers(std::vector<uint64_t> **, uint64_t) { fatal("p64"); } 2758a91bc7bSHarrietAkot virtual void getPointers(std::vector<uint32_t> **, uint64_t) { fatal("p32"); } 2768a91bc7bSHarrietAkot virtual void getPointers(std::vector<uint16_t> **, uint64_t) { fatal("p16"); } 2778a91bc7bSHarrietAkot virtual void getPointers(std::vector<uint8_t> **, uint64_t) { fatal("p8"); } 2788a91bc7bSHarrietAkot virtual void getIndices(std::vector<uint64_t> **, uint64_t) { fatal("i64"); } 2798a91bc7bSHarrietAkot virtual void getIndices(std::vector<uint32_t> **, uint64_t) { fatal("i32"); } 2808a91bc7bSHarrietAkot virtual void getIndices(std::vector<uint16_t> **, uint64_t) { fatal("i16"); } 2818a91bc7bSHarrietAkot virtual void getIndices(std::vector<uint8_t> **, uint64_t) { fatal("i8"); } 2828a91bc7bSHarrietAkot 2834f2ec7f9SAart Bik /// Primary storage. 2848a91bc7bSHarrietAkot virtual void getValues(std::vector<double> **) { fatal("valf64"); } 2858a91bc7bSHarrietAkot virtual void getValues(std::vector<float> **) { fatal("valf32"); } 2868a91bc7bSHarrietAkot virtual void getValues(std::vector<int64_t> **) { fatal("vali64"); } 2878a91bc7bSHarrietAkot virtual void getValues(std::vector<int32_t> **) { fatal("vali32"); } 2888a91bc7bSHarrietAkot virtual void getValues(std::vector<int16_t> **) { fatal("vali16"); } 2898a91bc7bSHarrietAkot virtual void getValues(std::vector<int8_t> **) { fatal("vali8"); } 2908a91bc7bSHarrietAkot 2914f2ec7f9SAart Bik /// Element-wise insertion in lexicographic index order. 292c03fd1e6Swren romano virtual void lexInsert(const uint64_t *, double) { fatal("insf64"); } 293c03fd1e6Swren romano virtual void lexInsert(const uint64_t *, float) { fatal("insf32"); } 294c03fd1e6Swren romano virtual void lexInsert(const uint64_t *, int64_t) { fatal("insi64"); } 295c03fd1e6Swren romano virtual void lexInsert(const uint64_t *, int32_t) { fatal("insi32"); } 296c03fd1e6Swren romano virtual void lexInsert(const uint64_t *, int16_t) { fatal("ins16"); } 297c03fd1e6Swren romano virtual void lexInsert(const uint64_t *, int8_t) { fatal("insi8"); } 2984f2ec7f9SAart Bik 2994f2ec7f9SAart Bik /// Expanded insertion. 3004f2ec7f9SAart Bik virtual void expInsert(uint64_t *, double *, bool *, uint64_t *, uint64_t) { 3014f2ec7f9SAart Bik fatal("expf64"); 3024f2ec7f9SAart Bik } 3034f2ec7f9SAart Bik virtual void expInsert(uint64_t *, float *, bool *, uint64_t *, uint64_t) { 3044f2ec7f9SAart Bik fatal("expf32"); 3054f2ec7f9SAart Bik } 3064f2ec7f9SAart Bik virtual void expInsert(uint64_t *, int64_t *, bool *, uint64_t *, uint64_t) { 3074f2ec7f9SAart Bik fatal("expi64"); 3084f2ec7f9SAart Bik } 3094f2ec7f9SAart Bik virtual void expInsert(uint64_t *, int32_t *, bool *, uint64_t *, uint64_t) { 3104f2ec7f9SAart Bik fatal("expi32"); 3114f2ec7f9SAart Bik } 3124f2ec7f9SAart Bik virtual void expInsert(uint64_t *, int16_t *, bool *, uint64_t *, uint64_t) { 3134f2ec7f9SAart Bik fatal("expi16"); 3144f2ec7f9SAart Bik } 3154f2ec7f9SAart Bik virtual void expInsert(uint64_t *, int8_t *, bool *, uint64_t *, uint64_t) { 3164f2ec7f9SAart Bik fatal("expi8"); 3174f2ec7f9SAart Bik } 3184f2ec7f9SAart Bik 3194f2ec7f9SAart Bik /// Finishes insertion. 320f66e5769SAart Bik virtual void endInsert() = 0; 321f66e5769SAart Bik 322*753fe330Swren romano protected: 323*753fe330Swren romano // Since this class is virtual, we must disallow public copying in 324*753fe330Swren romano // order to avoid "slicing". Since this class has data members, 325*753fe330Swren romano // that means making copying protected. 326*753fe330Swren romano // <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual> 327*753fe330Swren romano SparseTensorStorageBase(const SparseTensorStorageBase &) = default; 328*753fe330Swren romano // Copy-assignment would be implicitly deleted (because `dimSizes` 329*753fe330Swren romano // is const), so we explicitly delete it for clarity. 330*753fe330Swren romano SparseTensorStorageBase &operator=(const SparseTensorStorageBase &) = delete; 331*753fe330Swren romano 3328a91bc7bSHarrietAkot private: 33346bdacaaSwren romano static void fatal(const char *tp) { 3348a91bc7bSHarrietAkot fprintf(stderr, "unsupported %s\n", tp); 3358a91bc7bSHarrietAkot exit(1); 3368a91bc7bSHarrietAkot } 3378d8b566fSwren romano 3388d8b566fSwren romano const std::vector<uint64_t> dimSizes; 3398d8b566fSwren romano std::vector<uint64_t> rev; 3408d8b566fSwren romano const std::vector<DimLevelType> dimTypes; 3418a91bc7bSHarrietAkot }; 3428a91bc7bSHarrietAkot 343*753fe330Swren romano // Forward. 344*753fe330Swren romano template <typename P, typename I, typename V> 345*753fe330Swren romano class SparseTensorEnumerator; 346*753fe330Swren romano 3478a91bc7bSHarrietAkot /// A memory-resident sparse tensor using a storage scheme based on 3488a91bc7bSHarrietAkot /// per-dimension sparse/dense annotations. This data structure provides a 3498a91bc7bSHarrietAkot /// bufferized form of a sparse tensor type. In contrast to generating setup 3508a91bc7bSHarrietAkot /// methods for each differently annotated sparse tensor, this method provides 3518a91bc7bSHarrietAkot /// a convenient "one-size-fits-all" solution that simply takes an input tensor 3528a91bc7bSHarrietAkot /// and annotations to implement all required setup in a general manner. 3538a91bc7bSHarrietAkot template <typename P, typename I, typename V> 3548a91bc7bSHarrietAkot class SparseTensorStorage : public SparseTensorStorageBase { 3558a91bc7bSHarrietAkot public: 3568a91bc7bSHarrietAkot /// Constructs a sparse tensor storage scheme with the given dimensions, 3578a91bc7bSHarrietAkot /// permutation, and per-dimension dense/sparse annotations, using 3588a91bc7bSHarrietAkot /// the coordinate scheme tensor for the initial contents if provided. 3598d8b566fSwren romano /// 3608d8b566fSwren romano /// Precondition: `perm` and `sparsity` must be valid for `szs.size()`. 3618a91bc7bSHarrietAkot SparseTensorStorage(const std::vector<uint64_t> &szs, const uint64_t *perm, 362f66e5769SAart Bik const DimLevelType *sparsity, 3638d8b566fSwren romano SparseTensorCOO<V> *coo = nullptr) 3648d8b566fSwren romano : SparseTensorStorageBase(szs, perm, sparsity), pointers(getRank()), 3658d8b566fSwren romano indices(getRank()), idx(getRank()) { 3668a91bc7bSHarrietAkot // Provide hints on capacity of pointers and indices. 367175b9af4SAart Bik // TODO: needs much fine-tuning based on actual sparsity; currently 368175b9af4SAart Bik // we reserve pointer/index space based on all previous dense 369175b9af4SAart Bik // dimensions, which works well up to first sparse dim; but 370175b9af4SAart Bik // we should really use nnz and dense/sparse distribution. 371f66e5769SAart Bik bool allDense = true; 372f66e5769SAart Bik uint64_t sz = 1; 3738d8b566fSwren romano for (uint64_t r = 0, rank = getRank(); r < rank; r++) { 3748d8b566fSwren romano if (isCompressedDim(r)) { 3758d8b566fSwren romano // TODO: Take a parameter between 1 and `sizes[r]`, and multiply 3768d8b566fSwren romano // `sz` by that before reserving. (For now we just use 1.) 377f66e5769SAart Bik pointers[r].reserve(sz + 1); 3788d8b566fSwren romano pointers[r].push_back(0); 379f66e5769SAart Bik indices[r].reserve(sz); 380f66e5769SAart Bik sz = 1; 381f66e5769SAart Bik allDense = false; 3828d8b566fSwren romano } else { // Dense dimension. 3838d8b566fSwren romano sz = checkedMul(sz, getDimSizes()[r]); 3848a91bc7bSHarrietAkot } 3858a91bc7bSHarrietAkot } 3868a91bc7bSHarrietAkot // Then assign contents from coordinate scheme tensor if provided. 3878d8b566fSwren romano if (coo) { 3884d0a18d0Swren romano // Ensure both preconditions of `fromCOO`. 3898d8b566fSwren romano assert(coo->getSizes() == getDimSizes() && "Tensor size mismatch"); 3908d8b566fSwren romano coo->sort(); 3914d0a18d0Swren romano // Now actually insert the `elements`. 3928d8b566fSwren romano const std::vector<Element<V>> &elements = coo->getElements(); 393ceda1ae9Swren romano uint64_t nnz = elements.size(); 3948a91bc7bSHarrietAkot values.reserve(nnz); 395ceda1ae9Swren romano fromCOO(elements, 0, nnz, 0); 3961ce77b56SAart Bik } else if (allDense) { 397f66e5769SAart Bik values.resize(sz, 0); 3988a91bc7bSHarrietAkot } 3998a91bc7bSHarrietAkot } 4008a91bc7bSHarrietAkot 4010ae2e958SMehdi Amini ~SparseTensorStorage() override = default; 4028a91bc7bSHarrietAkot 403f66e5769SAart Bik /// Partially specialize these getter methods based on template types. 4048a91bc7bSHarrietAkot void getPointers(std::vector<P> **out, uint64_t d) override { 4058a91bc7bSHarrietAkot assert(d < getRank()); 4068a91bc7bSHarrietAkot *out = &pointers[d]; 4078a91bc7bSHarrietAkot } 4088a91bc7bSHarrietAkot void getIndices(std::vector<I> **out, uint64_t d) override { 4098a91bc7bSHarrietAkot assert(d < getRank()); 4108a91bc7bSHarrietAkot *out = &indices[d]; 4118a91bc7bSHarrietAkot } 4128a91bc7bSHarrietAkot void getValues(std::vector<V> **out) override { *out = &values; } 4138a91bc7bSHarrietAkot 41403fe15ceSAart Bik /// Partially specialize lexicographical insertions based on template types. 415c03fd1e6Swren romano void lexInsert(const uint64_t *cursor, V val) override { 4161ce77b56SAart Bik // First, wrap up pending insertion path. 4171ce77b56SAart Bik uint64_t diff = 0; 4181ce77b56SAart Bik uint64_t top = 0; 4191ce77b56SAart Bik if (!values.empty()) { 4201ce77b56SAart Bik diff = lexDiff(cursor); 4211ce77b56SAart Bik endPath(diff + 1); 4221ce77b56SAart Bik top = idx[diff] + 1; 4231ce77b56SAart Bik } 4241ce77b56SAart Bik // Then continue with insertion path. 4251ce77b56SAart Bik insPath(cursor, diff, top, val); 426f66e5769SAart Bik } 427f66e5769SAart Bik 4284f2ec7f9SAart Bik /// Partially specialize expanded insertions based on template types. 4294f2ec7f9SAart Bik /// Note that this method resets the values/filled-switch array back 4304f2ec7f9SAart Bik /// to all-zero/false while only iterating over the nonzero elements. 4314f2ec7f9SAart Bik void expInsert(uint64_t *cursor, V *values, bool *filled, uint64_t *added, 4324f2ec7f9SAart Bik uint64_t count) override { 4334f2ec7f9SAart Bik if (count == 0) 4344f2ec7f9SAart Bik return; 4354f2ec7f9SAart Bik // Sort. 4364f2ec7f9SAart Bik std::sort(added, added + count); 4374f2ec7f9SAart Bik // Restore insertion path for first insert. 4383bf2ba3bSwren romano const uint64_t lastDim = getRank() - 1; 4394f2ec7f9SAart Bik uint64_t index = added[0]; 4403bf2ba3bSwren romano cursor[lastDim] = index; 4414f2ec7f9SAart Bik lexInsert(cursor, values[index]); 4424f2ec7f9SAart Bik assert(filled[index]); 4434f2ec7f9SAart Bik values[index] = 0; 4444f2ec7f9SAart Bik filled[index] = false; 4454f2ec7f9SAart Bik // Subsequent insertions are quick. 4464f2ec7f9SAart Bik for (uint64_t i = 1; i < count; i++) { 4474f2ec7f9SAart Bik assert(index < added[i] && "non-lexicographic insertion"); 4484f2ec7f9SAart Bik index = added[i]; 4493bf2ba3bSwren romano cursor[lastDim] = index; 4503bf2ba3bSwren romano insPath(cursor, lastDim, added[i - 1] + 1, values[index]); 4514f2ec7f9SAart Bik assert(filled[index]); 4523bf2ba3bSwren romano values[index] = 0; 4534f2ec7f9SAart Bik filled[index] = false; 4544f2ec7f9SAart Bik } 4554f2ec7f9SAart Bik } 4564f2ec7f9SAart Bik 457f66e5769SAart Bik /// Finalizes lexicographic insertions. 4581ce77b56SAart Bik void endInsert() override { 4591ce77b56SAart Bik if (values.empty()) 46072ec2f76Swren romano finalizeSegment(0); 4611ce77b56SAart Bik else 4621ce77b56SAart Bik endPath(0); 4631ce77b56SAart Bik } 464f66e5769SAart Bik 4658a91bc7bSHarrietAkot /// Returns this sparse tensor storage scheme as a new memory-resident 4668a91bc7bSHarrietAkot /// sparse tensor in coordinate scheme with the given dimension order. 4678d8b566fSwren romano /// 4688d8b566fSwren romano /// Precondition: `perm` must be valid for `getRank()`. 469*753fe330Swren romano SparseTensorCOO<V> *toCOO(const uint64_t *perm) const { 470*753fe330Swren romano SparseTensorEnumerator<P, I, V> enumerator(*this, getRank(), perm); 471*753fe330Swren romano SparseTensorCOO<V> *coo = 472*753fe330Swren romano new SparseTensorCOO<V>(enumerator.permutedSizes(), values.size()); 473*753fe330Swren romano enumerator.forallElements([&coo](const std::vector<uint64_t> &ind, V val) { 474*753fe330Swren romano coo->add(ind, val); 475*753fe330Swren romano }); 4768d8b566fSwren romano // TODO: This assertion assumes there are no stored zeros, 4778d8b566fSwren romano // or if there are then that we don't filter them out. 4788d8b566fSwren romano // Cf., <https://github.com/llvm/llvm-project/issues/54179> 4798d8b566fSwren romano assert(coo->getElements().size() == values.size()); 4808d8b566fSwren romano return coo; 4818a91bc7bSHarrietAkot } 4828a91bc7bSHarrietAkot 4838a91bc7bSHarrietAkot /// Factory method. Constructs a sparse tensor storage scheme with the given 4848a91bc7bSHarrietAkot /// dimensions, permutation, and per-dimension dense/sparse annotations, 4858a91bc7bSHarrietAkot /// using the coordinate scheme tensor for the initial contents if provided. 4868a91bc7bSHarrietAkot /// In the latter case, the coordinate scheme must respect the same 4878a91bc7bSHarrietAkot /// permutation as is desired for the new sparse tensor storage. 4888d8b566fSwren romano /// 4898d8b566fSwren romano /// Precondition: `shape`, `perm`, and `sparsity` must be valid for `rank`. 4908a91bc7bSHarrietAkot static SparseTensorStorage<P, I, V> * 491d83a7068Swren romano newSparseTensor(uint64_t rank, const uint64_t *shape, const uint64_t *perm, 4928d8b566fSwren romano const DimLevelType *sparsity, SparseTensorCOO<V> *coo) { 4938a91bc7bSHarrietAkot SparseTensorStorage<P, I, V> *n = nullptr; 4948d8b566fSwren romano if (coo) { 4958d8b566fSwren romano assert(coo->getRank() == rank && "Tensor rank mismatch"); 4968d8b566fSwren romano const auto &coosz = coo->getSizes(); 4978a91bc7bSHarrietAkot for (uint64_t r = 0; r < rank; r++) 4988d8b566fSwren romano assert(shape[r] == 0 || shape[r] == coosz[perm[r]]); 4998d8b566fSwren romano n = new SparseTensorStorage<P, I, V>(coosz, perm, sparsity, coo); 5008a91bc7bSHarrietAkot } else { 5018a91bc7bSHarrietAkot std::vector<uint64_t> permsz(rank); 502d83a7068Swren romano for (uint64_t r = 0; r < rank; r++) { 503d83a7068Swren romano assert(shape[r] > 0 && "Dimension size zero has trivial storage"); 504d83a7068Swren romano permsz[perm[r]] = shape[r]; 505d83a7068Swren romano } 506f66e5769SAart Bik n = new SparseTensorStorage<P, I, V>(permsz, perm, sparsity); 5078a91bc7bSHarrietAkot } 5088a91bc7bSHarrietAkot return n; 5098a91bc7bSHarrietAkot } 5108a91bc7bSHarrietAkot 5118a91bc7bSHarrietAkot private: 51272ec2f76Swren romano /// Appends an arbitrary new position to `pointers[d]`. This method 51372ec2f76Swren romano /// checks that `pos` is representable in the `P` type; however, it 51472ec2f76Swren romano /// does not check that `pos` is semantically valid (i.e., larger than 51572ec2f76Swren romano /// the previous position and smaller than `indices[d].capacity()`). 5168d8b566fSwren romano void appendPointer(uint64_t d, uint64_t pos, uint64_t count = 1) { 51772ec2f76Swren romano assert(isCompressedDim(d)); 51872ec2f76Swren romano assert(pos <= std::numeric_limits<P>::max() && 5194d0a18d0Swren romano "Pointer value is too large for the P-type"); 52072ec2f76Swren romano pointers[d].insert(pointers[d].end(), count, static_cast<P>(pos)); 5214d0a18d0Swren romano } 5224d0a18d0Swren romano 52372ec2f76Swren romano /// Appends index `i` to dimension `d`, in the semantically general 52472ec2f76Swren romano /// sense. For non-dense dimensions, that means appending to the 52572ec2f76Swren romano /// `indices[d]` array, checking that `i` is representable in the `I` 52672ec2f76Swren romano /// type; however, we do not verify other semantic requirements (e.g., 52772ec2f76Swren romano /// that `i` is in bounds for `sizes[d]`, and not previously occurring 52872ec2f76Swren romano /// in the same segment). For dense dimensions, this method instead 52972ec2f76Swren romano /// appends the appropriate number of zeros to the `values` array, 53072ec2f76Swren romano /// where `full` is the number of "entries" already written to `values` 53172ec2f76Swren romano /// for this segment (aka one after the highest index previously appended). 53272ec2f76Swren romano void appendIndex(uint64_t d, uint64_t full, uint64_t i) { 53372ec2f76Swren romano if (isCompressedDim(d)) { 5344d0a18d0Swren romano assert(i <= std::numeric_limits<I>::max() && 5354d0a18d0Swren romano "Index value is too large for the I-type"); 53672ec2f76Swren romano indices[d].push_back(static_cast<I>(i)); 53772ec2f76Swren romano } else { // Dense dimension. 53872ec2f76Swren romano assert(i >= full && "Index was already filled"); 53972ec2f76Swren romano if (i == full) 54072ec2f76Swren romano return; // Short-circuit, since it'll be a nop. 54172ec2f76Swren romano if (d + 1 == getRank()) 54272ec2f76Swren romano values.insert(values.end(), i - full, 0); 54372ec2f76Swren romano else 54472ec2f76Swren romano finalizeSegment(d + 1, 0, i - full); 54572ec2f76Swren romano } 5464d0a18d0Swren romano } 5474d0a18d0Swren romano 5488a91bc7bSHarrietAkot /// Initializes sparse tensor storage scheme from a memory-resident sparse 5498a91bc7bSHarrietAkot /// tensor in coordinate scheme. This method prepares the pointers and 5508a91bc7bSHarrietAkot /// indices arrays under the given per-dimension dense/sparse annotations. 5514d0a18d0Swren romano /// 5524d0a18d0Swren romano /// Preconditions: 5534d0a18d0Swren romano /// (1) the `elements` must be lexicographically sorted. 5544d0a18d0Swren romano /// (2) the indices of every element are valid for `sizes` (equal rank 5554d0a18d0Swren romano /// and pointwise less-than). 556ceda1ae9Swren romano void fromCOO(const std::vector<Element<V>> &elements, uint64_t lo, 557ceda1ae9Swren romano uint64_t hi, uint64_t d) { 558*753fe330Swren romano uint64_t rank = getRank(); 559*753fe330Swren romano assert(d <= rank && hi <= elements.size()); 5608a91bc7bSHarrietAkot // Once dimensions are exhausted, insert the numerical values. 561*753fe330Swren romano if (d == rank) { 562c4017f9dSwren romano assert(lo < hi); 5631ce77b56SAart Bik values.push_back(elements[lo].value); 5648a91bc7bSHarrietAkot return; 5658a91bc7bSHarrietAkot } 5668a91bc7bSHarrietAkot // Visit all elements in this interval. 5678a91bc7bSHarrietAkot uint64_t full = 0; 568c4017f9dSwren romano while (lo < hi) { // If `hi` is unchanged, then `lo < elements.size()`. 5698a91bc7bSHarrietAkot // Find segment in interval with same index elements in this dimension. 570f66e5769SAart Bik uint64_t i = elements[lo].indices[d]; 5718a91bc7bSHarrietAkot uint64_t seg = lo + 1; 572f66e5769SAart Bik while (seg < hi && elements[seg].indices[d] == i) 5738a91bc7bSHarrietAkot seg++; 5748a91bc7bSHarrietAkot // Handle segment in interval for sparse or dense dimension. 57572ec2f76Swren romano appendIndex(d, full, i); 57672ec2f76Swren romano full = i + 1; 577ceda1ae9Swren romano fromCOO(elements, lo, seg, d + 1); 5788a91bc7bSHarrietAkot // And move on to next segment in interval. 5798a91bc7bSHarrietAkot lo = seg; 5808a91bc7bSHarrietAkot } 5818a91bc7bSHarrietAkot // Finalize the sparse pointer structure at this dimension. 58272ec2f76Swren romano finalizeSegment(d, full); 5838a91bc7bSHarrietAkot } 5848a91bc7bSHarrietAkot 58572ec2f76Swren romano /// Finalize the sparse pointer structure at this dimension. 58672ec2f76Swren romano void finalizeSegment(uint64_t d, uint64_t full = 0, uint64_t count = 1) { 58772ec2f76Swren romano if (count == 0) 58872ec2f76Swren romano return; // Short-circuit, since it'll be a nop. 58972ec2f76Swren romano if (isCompressedDim(d)) { 59072ec2f76Swren romano appendPointer(d, indices[d].size(), count); 59172ec2f76Swren romano } else { // Dense dimension. 5928d8b566fSwren romano const uint64_t sz = getDimSizes()[d]; 59372ec2f76Swren romano assert(sz >= full && "Segment is overfull"); 5948d8b566fSwren romano count = checkedMul(count, sz - full); 59572ec2f76Swren romano // For dense storage we must enumerate all the remaining coordinates 59672ec2f76Swren romano // in this dimension (i.e., coordinates after the last non-zero 59772ec2f76Swren romano // element), and either fill in their zero values or else recurse 59872ec2f76Swren romano // to finalize some deeper dimension. 59972ec2f76Swren romano if (d + 1 == getRank()) 60072ec2f76Swren romano values.insert(values.end(), count, 0); 60172ec2f76Swren romano else 60272ec2f76Swren romano finalizeSegment(d + 1, 0, count); 6031ce77b56SAart Bik } 6041ce77b56SAart Bik } 6051ce77b56SAart Bik 6061ce77b56SAart Bik /// Wraps up a single insertion path, inner to outer. 6071ce77b56SAart Bik void endPath(uint64_t diff) { 6081ce77b56SAart Bik uint64_t rank = getRank(); 6091ce77b56SAart Bik assert(diff <= rank); 6101ce77b56SAart Bik for (uint64_t i = 0; i < rank - diff; i++) { 61172ec2f76Swren romano const uint64_t d = rank - i - 1; 61272ec2f76Swren romano finalizeSegment(d, idx[d] + 1); 6131ce77b56SAart Bik } 6141ce77b56SAart Bik } 6151ce77b56SAart Bik 6161ce77b56SAart Bik /// Continues a single insertion path, outer to inner. 617c03fd1e6Swren romano void insPath(const uint64_t *cursor, uint64_t diff, uint64_t top, V val) { 6181ce77b56SAart Bik uint64_t rank = getRank(); 6191ce77b56SAart Bik assert(diff < rank); 6201ce77b56SAart Bik for (uint64_t d = diff; d < rank; d++) { 6211ce77b56SAart Bik uint64_t i = cursor[d]; 62272ec2f76Swren romano appendIndex(d, top, i); 6231ce77b56SAart Bik top = 0; 6241ce77b56SAart Bik idx[d] = i; 6251ce77b56SAart Bik } 6261ce77b56SAart Bik values.push_back(val); 6271ce77b56SAart Bik } 6281ce77b56SAart Bik 6291ce77b56SAart Bik /// Finds the lexicographic differing dimension. 63046bdacaaSwren romano uint64_t lexDiff(const uint64_t *cursor) const { 6311ce77b56SAart Bik for (uint64_t r = 0, rank = getRank(); r < rank; r++) 6321ce77b56SAart Bik if (cursor[r] > idx[r]) 6331ce77b56SAart Bik return r; 6341ce77b56SAart Bik else 6351ce77b56SAart Bik assert(cursor[r] == idx[r] && "non-lexicographic insertion"); 6361ce77b56SAart Bik assert(0 && "duplication insertion"); 6371ce77b56SAart Bik return -1u; 6381ce77b56SAart Bik } 6391ce77b56SAart Bik 640*753fe330Swren romano // Allow `SparseTensorEnumerator` to access the data-members (to avoid 641*753fe330Swren romano // the cost of virtual-function dispatch in inner loops), without 642*753fe330Swren romano // making them public to other client code. 643*753fe330Swren romano friend class SparseTensorEnumerator<P, I, V>; 644*753fe330Swren romano 6458a91bc7bSHarrietAkot std::vector<std::vector<P>> pointers; 6468a91bc7bSHarrietAkot std::vector<std::vector<I>> indices; 6478a91bc7bSHarrietAkot std::vector<V> values; 6488d8b566fSwren romano std::vector<uint64_t> idx; // index cursor for lexicographic insertion. 6498a91bc7bSHarrietAkot }; 6508a91bc7bSHarrietAkot 651*753fe330Swren romano /// A (higher-order) function object for enumerating the elements of some 652*753fe330Swren romano /// `SparseTensorStorage` under a permutation. That is, the `forallElements` 653*753fe330Swren romano /// method encapsulates the loop-nest for enumerating the elements of 654*753fe330Swren romano /// the source tensor (in whatever order is best for the source tensor), 655*753fe330Swren romano /// and applies a permutation to the coordinates/indices before handing 656*753fe330Swren romano /// each element to the callback. A single enumerator object can be 657*753fe330Swren romano /// freely reused for several calls to `forallElements`, just so long 658*753fe330Swren romano /// as each call is sequential with respect to one another. 659*753fe330Swren romano /// 660*753fe330Swren romano /// N.B., this class stores a reference to the `SparseTensorStorageBase` 661*753fe330Swren romano /// passed to the constructor; thus, objects of this class must not 662*753fe330Swren romano /// outlive the sparse tensor they depend on. 663*753fe330Swren romano /// 664*753fe330Swren romano /// Design Note: The reason we define this class instead of simply using 665*753fe330Swren romano /// `SparseTensorEnumerator<P,I,V>` is because we need to hide/generalize 666*753fe330Swren romano /// the `<P,I>` template parameters from MLIR client code (to simplify the 667*753fe330Swren romano /// type parameters used for direct sparse-to-sparse conversion). And the 668*753fe330Swren romano /// reason we define the `SparseTensorEnumerator<P,I,V>` subclasses rather 669*753fe330Swren romano /// than simply using this class, is to avoid the cost of virtual-method 670*753fe330Swren romano /// dispatch within the loop-nest. 671*753fe330Swren romano template <typename V> 672*753fe330Swren romano class SparseTensorEnumeratorBase { 673*753fe330Swren romano public: 674*753fe330Swren romano /// Constructs an enumerator with the given permutation for mapping 675*753fe330Swren romano /// the semantic-ordering of dimensions to the desired target-ordering. 676*753fe330Swren romano /// 677*753fe330Swren romano /// Preconditions: 678*753fe330Swren romano /// * the `tensor` must have the same `V` value type. 679*753fe330Swren romano /// * `perm` must be valid for `rank`. 680*753fe330Swren romano SparseTensorEnumeratorBase(const SparseTensorStorageBase &tensor, 681*753fe330Swren romano uint64_t rank, const uint64_t *perm) 682*753fe330Swren romano : src(tensor), permsz(src.getRev().size()), reord(getRank()), 683*753fe330Swren romano cursor(getRank()) { 684*753fe330Swren romano assert(perm && "Received nullptr for permutation"); 685*753fe330Swren romano assert(rank == getRank() && "Permutation rank mismatch"); 686*753fe330Swren romano const auto &rev = src.getRev(); // source stg-order -> semantic-order 687*753fe330Swren romano const auto &sizes = src.getDimSizes(); // in source storage-order 688*753fe330Swren romano for (uint64_t s = 0; s < rank; s++) { // `s` source storage-order 689*753fe330Swren romano uint64_t t = perm[rev[s]]; // `t` target-order 690*753fe330Swren romano reord[s] = t; 691*753fe330Swren romano permsz[t] = sizes[s]; 692*753fe330Swren romano } 693*753fe330Swren romano } 694*753fe330Swren romano 695*753fe330Swren romano virtual ~SparseTensorEnumeratorBase() = default; 696*753fe330Swren romano 697*753fe330Swren romano // We disallow copying to help avoid leaking the `src` reference. 698*753fe330Swren romano // (In addition to avoiding the problem of slicing.) 699*753fe330Swren romano SparseTensorEnumeratorBase(const SparseTensorEnumeratorBase &) = delete; 700*753fe330Swren romano SparseTensorEnumeratorBase & 701*753fe330Swren romano operator=(const SparseTensorEnumeratorBase &) = delete; 702*753fe330Swren romano 703*753fe330Swren romano /// Returns the source/target tensor's rank. (The source-rank and 704*753fe330Swren romano /// target-rank are always equal since we only support permutations. 705*753fe330Swren romano /// Though once we add support for other dimension mappings, this 706*753fe330Swren romano /// method will have to be split in two.) 707*753fe330Swren romano uint64_t getRank() const { return permsz.size(); } 708*753fe330Swren romano 709*753fe330Swren romano /// Returns the target tensor's dimension sizes. 710*753fe330Swren romano const std::vector<uint64_t> &permutedSizes() const { return permsz; } 711*753fe330Swren romano 712*753fe330Swren romano /// Enumerates all elements of the source tensor, permutes their 713*753fe330Swren romano /// indices, and passes the permuted element to the callback. 714*753fe330Swren romano /// The callback must not store the cursor reference directly, 715*753fe330Swren romano /// since this function reuses the storage. Instead, the callback 716*753fe330Swren romano /// must copy it if they want to keep it. 717*753fe330Swren romano virtual void forallElements(ElementConsumer<V> yield) = 0; 718*753fe330Swren romano 719*753fe330Swren romano protected: 720*753fe330Swren romano const SparseTensorStorageBase &src; 721*753fe330Swren romano std::vector<uint64_t> permsz; // in target order. 722*753fe330Swren romano std::vector<uint64_t> reord; // source storage-order -> target order. 723*753fe330Swren romano std::vector<uint64_t> cursor; // in target order. 724*753fe330Swren romano }; 725*753fe330Swren romano 726*753fe330Swren romano template <typename P, typename I, typename V> 727*753fe330Swren romano class SparseTensorEnumerator final : public SparseTensorEnumeratorBase<V> { 728*753fe330Swren romano using Base = SparseTensorEnumeratorBase<V>; 729*753fe330Swren romano 730*753fe330Swren romano public: 731*753fe330Swren romano /// Constructs an enumerator with the given permutation for mapping 732*753fe330Swren romano /// the semantic-ordering of dimensions to the desired target-ordering. 733*753fe330Swren romano /// 734*753fe330Swren romano /// Precondition: `perm` must be valid for `rank`. 735*753fe330Swren romano SparseTensorEnumerator(const SparseTensorStorage<P, I, V> &tensor, 736*753fe330Swren romano uint64_t rank, const uint64_t *perm) 737*753fe330Swren romano : Base(tensor, rank, perm) {} 738*753fe330Swren romano 739*753fe330Swren romano ~SparseTensorEnumerator() final override = default; 740*753fe330Swren romano 741*753fe330Swren romano void forallElements(ElementConsumer<V> yield) final override { 742*753fe330Swren romano forallElements(yield, 0, 0); 743*753fe330Swren romano } 744*753fe330Swren romano 745*753fe330Swren romano private: 746*753fe330Swren romano /// The recursive component of the public `forallElements`. 747*753fe330Swren romano void forallElements(ElementConsumer<V> yield, uint64_t parentPos, 748*753fe330Swren romano uint64_t d) { 749*753fe330Swren romano // Recover the `<P,I,V>` type parameters of `src`. 750*753fe330Swren romano const auto &src = 751*753fe330Swren romano static_cast<const SparseTensorStorage<P, I, V> &>(this->src); 752*753fe330Swren romano if (d == Base::getRank()) { 753*753fe330Swren romano assert(parentPos < src.values.size() && 754*753fe330Swren romano "Value position is out of bounds"); 755*753fe330Swren romano // TODO: <https://github.com/llvm/llvm-project/issues/54179> 756*753fe330Swren romano yield(this->cursor, src.values[parentPos]); 757*753fe330Swren romano } else if (src.isCompressedDim(d)) { 758*753fe330Swren romano // Look up the bounds of the `d`-level segment determined by the 759*753fe330Swren romano // `d-1`-level position `parentPos`. 760*753fe330Swren romano const std::vector<P> &pointers_d = src.pointers[d]; 761*753fe330Swren romano assert(parentPos + 1 < pointers_d.size() && 762*753fe330Swren romano "Parent pointer position is out of bounds"); 763*753fe330Swren romano const uint64_t pstart = static_cast<uint64_t>(pointers_d[parentPos]); 764*753fe330Swren romano const uint64_t pstop = static_cast<uint64_t>(pointers_d[parentPos + 1]); 765*753fe330Swren romano // Loop-invariant code for looking up the `d`-level coordinates/indices. 766*753fe330Swren romano const std::vector<I> &indices_d = src.indices[d]; 767*753fe330Swren romano assert(pstop - 1 < indices_d.size() && "Index position is out of bounds"); 768*753fe330Swren romano uint64_t &cursor_reord_d = this->cursor[this->reord[d]]; 769*753fe330Swren romano for (uint64_t pos = pstart; pos < pstop; pos++) { 770*753fe330Swren romano cursor_reord_d = static_cast<uint64_t>(indices_d[pos]); 771*753fe330Swren romano forallElements(yield, pos, d + 1); 772*753fe330Swren romano } 773*753fe330Swren romano } else { // Dense dimension. 774*753fe330Swren romano const uint64_t sz = src.getDimSizes()[d]; 775*753fe330Swren romano const uint64_t pstart = parentPos * sz; 776*753fe330Swren romano uint64_t &cursor_reord_d = this->cursor[this->reord[d]]; 777*753fe330Swren romano for (uint64_t i = 0; i < sz; i++) { 778*753fe330Swren romano cursor_reord_d = i; 779*753fe330Swren romano forallElements(yield, pstart + i, d + 1); 780*753fe330Swren romano } 781*753fe330Swren romano } 782*753fe330Swren romano } 783*753fe330Swren romano }; 784*753fe330Swren romano 7858a91bc7bSHarrietAkot /// Helper to convert string to lower case. 7868a91bc7bSHarrietAkot static char *toLower(char *token) { 7878a91bc7bSHarrietAkot for (char *c = token; *c; c++) 7888a91bc7bSHarrietAkot *c = tolower(*c); 7898a91bc7bSHarrietAkot return token; 7908a91bc7bSHarrietAkot } 7918a91bc7bSHarrietAkot 7928a91bc7bSHarrietAkot /// Read the MME header of a general sparse matrix of type real. 79303fe15ceSAart Bik static void readMMEHeader(FILE *file, char *filename, char *line, 79433e8ab8eSAart Bik uint64_t *idata, bool *isPattern, bool *isSymmetric) { 7958a91bc7bSHarrietAkot char header[64]; 7968a91bc7bSHarrietAkot char object[64]; 7978a91bc7bSHarrietAkot char format[64]; 7988a91bc7bSHarrietAkot char field[64]; 7998a91bc7bSHarrietAkot char symmetry[64]; 8008a91bc7bSHarrietAkot // Read header line. 8018a91bc7bSHarrietAkot if (fscanf(file, "%63s %63s %63s %63s %63s\n", header, object, format, field, 8028a91bc7bSHarrietAkot symmetry) != 5) { 80303fe15ceSAart Bik fprintf(stderr, "Corrupt header in %s\n", filename); 8048a91bc7bSHarrietAkot exit(1); 8058a91bc7bSHarrietAkot } 80633e8ab8eSAart Bik // Set properties 80733e8ab8eSAart Bik *isPattern = (strcmp(toLower(field), "pattern") == 0); 808bb56c2b3SMehdi Amini *isSymmetric = (strcmp(toLower(symmetry), "symmetric") == 0); 8098a91bc7bSHarrietAkot // Make sure this is a general sparse matrix. 8108a91bc7bSHarrietAkot if (strcmp(toLower(header), "%%matrixmarket") || 8118a91bc7bSHarrietAkot strcmp(toLower(object), "matrix") || 81233e8ab8eSAart Bik strcmp(toLower(format), "coordinate") || 81333e8ab8eSAart Bik (strcmp(toLower(field), "real") && !(*isPattern)) || 814bb56c2b3SMehdi Amini (strcmp(toLower(symmetry), "general") && !(*isSymmetric))) { 81533e8ab8eSAart Bik fprintf(stderr, "Cannot find a general sparse matrix in %s\n", filename); 8168a91bc7bSHarrietAkot exit(1); 8178a91bc7bSHarrietAkot } 8188a91bc7bSHarrietAkot // Skip comments. 819e5639b3fSMehdi Amini while (true) { 82003fe15ceSAart Bik if (!fgets(line, kColWidth, file)) { 82103fe15ceSAart Bik fprintf(stderr, "Cannot find data in %s\n", filename); 8228a91bc7bSHarrietAkot exit(1); 8238a91bc7bSHarrietAkot } 8248a91bc7bSHarrietAkot if (line[0] != '%') 8258a91bc7bSHarrietAkot break; 8268a91bc7bSHarrietAkot } 8278a91bc7bSHarrietAkot // Next line contains M N NNZ. 8288a91bc7bSHarrietAkot idata[0] = 2; // rank 8298a91bc7bSHarrietAkot if (sscanf(line, "%" PRIu64 "%" PRIu64 "%" PRIu64 "\n", idata + 2, idata + 3, 8308a91bc7bSHarrietAkot idata + 1) != 3) { 83103fe15ceSAart Bik fprintf(stderr, "Cannot find size in %s\n", filename); 8328a91bc7bSHarrietAkot exit(1); 8338a91bc7bSHarrietAkot } 8348a91bc7bSHarrietAkot } 8358a91bc7bSHarrietAkot 8368a91bc7bSHarrietAkot /// Read the "extended" FROSTT header. Although not part of the documented 8378a91bc7bSHarrietAkot /// format, we assume that the file starts with optional comments followed 8388a91bc7bSHarrietAkot /// by two lines that define the rank, the number of nonzeros, and the 8398a91bc7bSHarrietAkot /// dimensions sizes (one per rank) of the sparse tensor. 84003fe15ceSAart Bik static void readExtFROSTTHeader(FILE *file, char *filename, char *line, 84103fe15ceSAart Bik uint64_t *idata) { 8428a91bc7bSHarrietAkot // Skip comments. 843e5639b3fSMehdi Amini while (true) { 84403fe15ceSAart Bik if (!fgets(line, kColWidth, file)) { 84503fe15ceSAart Bik fprintf(stderr, "Cannot find data in %s\n", filename); 8468a91bc7bSHarrietAkot exit(1); 8478a91bc7bSHarrietAkot } 8488a91bc7bSHarrietAkot if (line[0] != '#') 8498a91bc7bSHarrietAkot break; 8508a91bc7bSHarrietAkot } 8518a91bc7bSHarrietAkot // Next line contains RANK and NNZ. 8528a91bc7bSHarrietAkot if (sscanf(line, "%" PRIu64 "%" PRIu64 "\n", idata, idata + 1) != 2) { 85303fe15ceSAart Bik fprintf(stderr, "Cannot find metadata in %s\n", filename); 8548a91bc7bSHarrietAkot exit(1); 8558a91bc7bSHarrietAkot } 8568a91bc7bSHarrietAkot // Followed by a line with the dimension sizes (one per rank). 8578a91bc7bSHarrietAkot for (uint64_t r = 0; r < idata[0]; r++) { 8588a91bc7bSHarrietAkot if (fscanf(file, "%" PRIu64, idata + 2 + r) != 1) { 85903fe15ceSAart Bik fprintf(stderr, "Cannot find dimension size %s\n", filename); 8608a91bc7bSHarrietAkot exit(1); 8618a91bc7bSHarrietAkot } 8628a91bc7bSHarrietAkot } 86303fe15ceSAart Bik fgets(line, kColWidth, file); // end of line 8648a91bc7bSHarrietAkot } 8658a91bc7bSHarrietAkot 8668a91bc7bSHarrietAkot /// Reads a sparse tensor with the given filename into a memory-resident 8678a91bc7bSHarrietAkot /// sparse tensor in coordinate scheme. 8688a91bc7bSHarrietAkot template <typename V> 8698a91bc7bSHarrietAkot static SparseTensorCOO<V> *openSparseTensorCOO(char *filename, uint64_t rank, 870d83a7068Swren romano const uint64_t *shape, 8718a91bc7bSHarrietAkot const uint64_t *perm) { 8728a91bc7bSHarrietAkot // Open the file. 8738a91bc7bSHarrietAkot FILE *file = fopen(filename, "r"); 8748a91bc7bSHarrietAkot if (!file) { 8753734c078Swren romano assert(filename && "Received nullptr for filename"); 8763734c078Swren romano fprintf(stderr, "Cannot find file %s\n", filename); 8778a91bc7bSHarrietAkot exit(1); 8788a91bc7bSHarrietAkot } 8798a91bc7bSHarrietAkot // Perform some file format dependent set up. 88003fe15ceSAart Bik char line[kColWidth]; 8818a91bc7bSHarrietAkot uint64_t idata[512]; 88233e8ab8eSAart Bik bool isPattern = false; 883bb56c2b3SMehdi Amini bool isSymmetric = false; 8848a91bc7bSHarrietAkot if (strstr(filename, ".mtx")) { 88533e8ab8eSAart Bik readMMEHeader(file, filename, line, idata, &isPattern, &isSymmetric); 8868a91bc7bSHarrietAkot } else if (strstr(filename, ".tns")) { 88703fe15ceSAart Bik readExtFROSTTHeader(file, filename, line, idata); 8888a91bc7bSHarrietAkot } else { 8898a91bc7bSHarrietAkot fprintf(stderr, "Unknown format %s\n", filename); 8908a91bc7bSHarrietAkot exit(1); 8918a91bc7bSHarrietAkot } 8928a91bc7bSHarrietAkot // Prepare sparse tensor object with per-dimension sizes 8938a91bc7bSHarrietAkot // and the number of nonzeros as initial capacity. 8948a91bc7bSHarrietAkot assert(rank == idata[0] && "rank mismatch"); 8958a91bc7bSHarrietAkot uint64_t nnz = idata[1]; 8968a91bc7bSHarrietAkot for (uint64_t r = 0; r < rank; r++) 897d83a7068Swren romano assert((shape[r] == 0 || shape[r] == idata[2 + r]) && 8988a91bc7bSHarrietAkot "dimension size mismatch"); 8998a91bc7bSHarrietAkot SparseTensorCOO<V> *tensor = 9008a91bc7bSHarrietAkot SparseTensorCOO<V>::newSparseTensorCOO(rank, idata + 2, perm, nnz); 9018a91bc7bSHarrietAkot // Read all nonzero elements. 9028a91bc7bSHarrietAkot std::vector<uint64_t> indices(rank); 9038a91bc7bSHarrietAkot for (uint64_t k = 0; k < nnz; k++) { 90403fe15ceSAart Bik if (!fgets(line, kColWidth, file)) { 90503fe15ceSAart Bik fprintf(stderr, "Cannot find next line of data in %s\n", filename); 9068a91bc7bSHarrietAkot exit(1); 9078a91bc7bSHarrietAkot } 90803fe15ceSAart Bik char *linePtr = line; 90903fe15ceSAart Bik for (uint64_t r = 0; r < rank; r++) { 91003fe15ceSAart Bik uint64_t idx = strtoul(linePtr, &linePtr, 10); 9118a91bc7bSHarrietAkot // Add 0-based index. 9128a91bc7bSHarrietAkot indices[perm[r]] = idx - 1; 9138a91bc7bSHarrietAkot } 9148a91bc7bSHarrietAkot // The external formats always store the numerical values with the type 9158a91bc7bSHarrietAkot // double, but we cast these values to the sparse tensor object type. 91633e8ab8eSAart Bik // For a pattern tensor, we arbitrarily pick the value 1 for all entries. 91733e8ab8eSAart Bik double value = isPattern ? 1.0 : strtod(linePtr, &linePtr); 9188a91bc7bSHarrietAkot tensor->add(indices, value); 91902710413SBixia Zheng // We currently chose to deal with symmetric matrices by fully constructing 92002710413SBixia Zheng // them. In the future, we may want to make symmetry implicit for storage 92102710413SBixia Zheng // reasons. 922bb56c2b3SMehdi Amini if (isSymmetric && indices[0] != indices[1]) 92302710413SBixia Zheng tensor->add({indices[1], indices[0]}, value); 9248a91bc7bSHarrietAkot } 9258a91bc7bSHarrietAkot // Close the file and return tensor. 9268a91bc7bSHarrietAkot fclose(file); 9278a91bc7bSHarrietAkot return tensor; 9288a91bc7bSHarrietAkot } 9298a91bc7bSHarrietAkot 930efa15f41SAart Bik /// Writes the sparse tensor to extended FROSTT format. 931efa15f41SAart Bik template <typename V> 93246bdacaaSwren romano static void outSparseTensor(void *tensor, void *dest, bool sort) { 9336438783fSAart Bik assert(tensor && dest); 9346438783fSAart Bik auto coo = static_cast<SparseTensorCOO<V> *>(tensor); 9356438783fSAart Bik if (sort) 9366438783fSAart Bik coo->sort(); 9376438783fSAart Bik char *filename = static_cast<char *>(dest); 9386438783fSAart Bik auto &sizes = coo->getSizes(); 9396438783fSAart Bik auto &elements = coo->getElements(); 9406438783fSAart Bik uint64_t rank = coo->getRank(); 941efa15f41SAart Bik uint64_t nnz = elements.size(); 942efa15f41SAart Bik std::fstream file; 943efa15f41SAart Bik file.open(filename, std::ios_base::out | std::ios_base::trunc); 944efa15f41SAart Bik assert(file.is_open()); 945efa15f41SAart Bik file << "; extended FROSTT format\n" << rank << " " << nnz << std::endl; 946efa15f41SAart Bik for (uint64_t r = 0; r < rank - 1; r++) 947efa15f41SAart Bik file << sizes[r] << " "; 948efa15f41SAart Bik file << sizes[rank - 1] << std::endl; 949efa15f41SAart Bik for (uint64_t i = 0; i < nnz; i++) { 950efa15f41SAart Bik auto &idx = elements[i].indices; 951efa15f41SAart Bik for (uint64_t r = 0; r < rank; r++) 952efa15f41SAart Bik file << (idx[r] + 1) << " "; 953efa15f41SAart Bik file << elements[i].value << std::endl; 954efa15f41SAart Bik } 955efa15f41SAart Bik file.flush(); 956efa15f41SAart Bik file.close(); 957efa15f41SAart Bik assert(file.good()); 9586438783fSAart Bik } 9596438783fSAart Bik 9606438783fSAart Bik /// Initializes sparse tensor from an external COO-flavored format. 9616438783fSAart Bik template <typename V> 96246bdacaaSwren romano static SparseTensorStorage<uint64_t, uint64_t, V> * 9636438783fSAart Bik toMLIRSparseTensor(uint64_t rank, uint64_t nse, uint64_t *shape, V *values, 96420eaa88fSBixia Zheng uint64_t *indices, uint64_t *perm, uint8_t *sparse) { 96520eaa88fSBixia Zheng const DimLevelType *sparsity = (DimLevelType *)(sparse); 96620eaa88fSBixia Zheng #ifndef NDEBUG 96720eaa88fSBixia Zheng // Verify that perm is a permutation of 0..(rank-1). 96820eaa88fSBixia Zheng std::vector<uint64_t> order(perm, perm + rank); 96920eaa88fSBixia Zheng std::sort(order.begin(), order.end()); 9701e47888dSAart Bik for (uint64_t i = 0; i < rank; ++i) { 97120eaa88fSBixia Zheng if (i != order[i]) { 972988d4b0dSAart Bik fprintf(stderr, "Not a permutation of 0..%" PRIu64 "\n", rank); 97320eaa88fSBixia Zheng exit(1); 97420eaa88fSBixia Zheng } 97520eaa88fSBixia Zheng } 97620eaa88fSBixia Zheng 97720eaa88fSBixia Zheng // Verify that the sparsity values are supported. 9781e47888dSAart Bik for (uint64_t i = 0; i < rank; ++i) { 97920eaa88fSBixia Zheng if (sparsity[i] != DimLevelType::kDense && 98020eaa88fSBixia Zheng sparsity[i] != DimLevelType::kCompressed) { 98120eaa88fSBixia Zheng fprintf(stderr, "Unsupported sparsity value %d\n", 98220eaa88fSBixia Zheng static_cast<int>(sparsity[i])); 98320eaa88fSBixia Zheng exit(1); 98420eaa88fSBixia Zheng } 98520eaa88fSBixia Zheng } 98620eaa88fSBixia Zheng #endif 98720eaa88fSBixia Zheng 9886438783fSAart Bik // Convert external format to internal COO. 98963bdcaf9Swren romano auto *coo = SparseTensorCOO<V>::newSparseTensorCOO(rank, shape, perm, nse); 9906438783fSAart Bik std::vector<uint64_t> idx(rank); 9916438783fSAart Bik for (uint64_t i = 0, base = 0; i < nse; i++) { 9926438783fSAart Bik for (uint64_t r = 0; r < rank; r++) 993d8b229a1SAart Bik idx[perm[r]] = indices[base + r]; 99463bdcaf9Swren romano coo->add(idx, values[i]); 9956438783fSAart Bik base += rank; 9966438783fSAart Bik } 9976438783fSAart Bik // Return sparse tensor storage format as opaque pointer. 99863bdcaf9Swren romano auto *tensor = SparseTensorStorage<uint64_t, uint64_t, V>::newSparseTensor( 99963bdcaf9Swren romano rank, shape, perm, sparsity, coo); 100063bdcaf9Swren romano delete coo; 100163bdcaf9Swren romano return tensor; 10026438783fSAart Bik } 10036438783fSAart Bik 10046438783fSAart Bik /// Converts a sparse tensor to an external COO-flavored format. 10056438783fSAart Bik template <typename V> 100646bdacaaSwren romano static void fromMLIRSparseTensor(void *tensor, uint64_t *pRank, uint64_t *pNse, 100746bdacaaSwren romano uint64_t **pShape, V **pValues, 100846bdacaaSwren romano uint64_t **pIndices) { 10096438783fSAart Bik auto sparseTensor = 10106438783fSAart Bik static_cast<SparseTensorStorage<uint64_t, uint64_t, V> *>(tensor); 10116438783fSAart Bik uint64_t rank = sparseTensor->getRank(); 10126438783fSAart Bik std::vector<uint64_t> perm(rank); 10136438783fSAart Bik std::iota(perm.begin(), perm.end(), 0); 10146438783fSAart Bik SparseTensorCOO<V> *coo = sparseTensor->toCOO(perm.data()); 10156438783fSAart Bik 10166438783fSAart Bik const std::vector<Element<V>> &elements = coo->getElements(); 10176438783fSAart Bik uint64_t nse = elements.size(); 10186438783fSAart Bik 10196438783fSAart Bik uint64_t *shape = new uint64_t[rank]; 10206438783fSAart Bik for (uint64_t i = 0; i < rank; i++) 10216438783fSAart Bik shape[i] = coo->getSizes()[i]; 10226438783fSAart Bik 10236438783fSAart Bik V *values = new V[nse]; 10246438783fSAart Bik uint64_t *indices = new uint64_t[rank * nse]; 10256438783fSAart Bik 10266438783fSAart Bik for (uint64_t i = 0, base = 0; i < nse; i++) { 10276438783fSAart Bik values[i] = elements[i].value; 10286438783fSAart Bik for (uint64_t j = 0; j < rank; j++) 10296438783fSAart Bik indices[base + j] = elements[i].indices[j]; 10306438783fSAart Bik base += rank; 10316438783fSAart Bik } 10326438783fSAart Bik 10336438783fSAart Bik delete coo; 10346438783fSAart Bik *pRank = rank; 10356438783fSAart Bik *pNse = nse; 10366438783fSAart Bik *pShape = shape; 10376438783fSAart Bik *pValues = values; 10386438783fSAart Bik *pIndices = indices; 1039efa15f41SAart Bik } 1040efa15f41SAart Bik 1041be0a7e9fSMehdi Amini } // namespace 10428a91bc7bSHarrietAkot 10438a91bc7bSHarrietAkot extern "C" { 10448a91bc7bSHarrietAkot 10458a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 10468a91bc7bSHarrietAkot // 10478a91bc7bSHarrietAkot // Public API with methods that operate on MLIR buffers (memrefs) to interact 10488a91bc7bSHarrietAkot // with sparse tensors, which are only visible as opaque pointers externally. 10498a91bc7bSHarrietAkot // These methods should be used exclusively by MLIR compiler-generated code. 10508a91bc7bSHarrietAkot // 10518a91bc7bSHarrietAkot // Some macro magic is used to generate implementations for all required type 10528a91bc7bSHarrietAkot // combinations that can be called from MLIR compiler-generated code. 10538a91bc7bSHarrietAkot // 10548a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 10558a91bc7bSHarrietAkot 10568a91bc7bSHarrietAkot #define CASE(p, i, v, P, I, V) \ 10578a91bc7bSHarrietAkot if (ptrTp == (p) && indTp == (i) && valTp == (v)) { \ 105863bdcaf9Swren romano SparseTensorCOO<V> *coo = nullptr; \ 1059845561ecSwren romano if (action <= Action::kFromCOO) { \ 1060845561ecSwren romano if (action == Action::kFromFile) { \ 10618a91bc7bSHarrietAkot char *filename = static_cast<char *>(ptr); \ 106263bdcaf9Swren romano coo = openSparseTensorCOO<V>(filename, rank, shape, perm); \ 1063845561ecSwren romano } else if (action == Action::kFromCOO) { \ 106463bdcaf9Swren romano coo = static_cast<SparseTensorCOO<V> *>(ptr); \ 10658a91bc7bSHarrietAkot } else { \ 1066845561ecSwren romano assert(action == Action::kEmpty); \ 10678a91bc7bSHarrietAkot } \ 106863bdcaf9Swren romano auto *tensor = SparseTensorStorage<P, I, V>::newSparseTensor( \ 106963bdcaf9Swren romano rank, shape, perm, sparsity, coo); \ 107063bdcaf9Swren romano if (action == Action::kFromFile) \ 107163bdcaf9Swren romano delete coo; \ 107263bdcaf9Swren romano return tensor; \ 1073bb56c2b3SMehdi Amini } \ 1074bb56c2b3SMehdi Amini if (action == Action::kEmptyCOO) \ 1075d83a7068Swren romano return SparseTensorCOO<V>::newSparseTensorCOO(rank, shape, perm); \ 107663bdcaf9Swren romano coo = static_cast<SparseTensorStorage<P, I, V> *>(ptr)->toCOO(perm); \ 1077845561ecSwren romano if (action == Action::kToIterator) { \ 107863bdcaf9Swren romano coo->startIterator(); \ 10798a91bc7bSHarrietAkot } else { \ 1080845561ecSwren romano assert(action == Action::kToCOO); \ 10818a91bc7bSHarrietAkot } \ 108263bdcaf9Swren romano return coo; \ 10838a91bc7bSHarrietAkot } 10848a91bc7bSHarrietAkot 1085845561ecSwren romano #define CASE_SECSAME(p, v, P, V) CASE(p, p, v, P, P, V) 1086845561ecSwren romano 10878a91bc7bSHarrietAkot #define IMPL_SPARSEVALUES(NAME, TYPE, LIB) \ 10888a91bc7bSHarrietAkot void _mlir_ciface_##NAME(StridedMemRefType<TYPE, 1> *ref, void *tensor) { \ 10894f2ec7f9SAart Bik assert(ref &&tensor); \ 10908a91bc7bSHarrietAkot std::vector<TYPE> *v; \ 10918a91bc7bSHarrietAkot static_cast<SparseTensorStorageBase *>(tensor)->LIB(&v); \ 10928a91bc7bSHarrietAkot ref->basePtr = ref->data = v->data(); \ 10938a91bc7bSHarrietAkot ref->offset = 0; \ 10948a91bc7bSHarrietAkot ref->sizes[0] = v->size(); \ 10958a91bc7bSHarrietAkot ref->strides[0] = 1; \ 10968a91bc7bSHarrietAkot } 10978a91bc7bSHarrietAkot 10988a91bc7bSHarrietAkot #define IMPL_GETOVERHEAD(NAME, TYPE, LIB) \ 10998a91bc7bSHarrietAkot void _mlir_ciface_##NAME(StridedMemRefType<TYPE, 1> *ref, void *tensor, \ 1100d2215e79SRainer Orth index_type d) { \ 11014f2ec7f9SAart Bik assert(ref &&tensor); \ 11028a91bc7bSHarrietAkot std::vector<TYPE> *v; \ 11038a91bc7bSHarrietAkot static_cast<SparseTensorStorageBase *>(tensor)->LIB(&v, d); \ 11048a91bc7bSHarrietAkot ref->basePtr = ref->data = v->data(); \ 11058a91bc7bSHarrietAkot ref->offset = 0; \ 11068a91bc7bSHarrietAkot ref->sizes[0] = v->size(); \ 11078a91bc7bSHarrietAkot ref->strides[0] = 1; \ 11088a91bc7bSHarrietAkot } 11098a91bc7bSHarrietAkot 11108a91bc7bSHarrietAkot #define IMPL_ADDELT(NAME, TYPE) \ 11118a91bc7bSHarrietAkot void *_mlir_ciface_##NAME(void *tensor, TYPE value, \ 1112d2215e79SRainer Orth StridedMemRefType<index_type, 1> *iref, \ 1113d2215e79SRainer Orth StridedMemRefType<index_type, 1> *pref) { \ 11144f2ec7f9SAart Bik assert(tensor &&iref &&pref); \ 11158a91bc7bSHarrietAkot assert(iref->strides[0] == 1 && pref->strides[0] == 1); \ 11168a91bc7bSHarrietAkot assert(iref->sizes[0] == pref->sizes[0]); \ 1117d2215e79SRainer Orth const index_type *indx = iref->data + iref->offset; \ 1118d2215e79SRainer Orth const index_type *perm = pref->data + pref->offset; \ 11198a91bc7bSHarrietAkot uint64_t isize = iref->sizes[0]; \ 1120d2215e79SRainer Orth std::vector<index_type> indices(isize); \ 11218a91bc7bSHarrietAkot for (uint64_t r = 0; r < isize; r++) \ 11228a91bc7bSHarrietAkot indices[perm[r]] = indx[r]; \ 11238a91bc7bSHarrietAkot static_cast<SparseTensorCOO<TYPE> *>(tensor)->add(indices, value); \ 11248a91bc7bSHarrietAkot return tensor; \ 11258a91bc7bSHarrietAkot } 11268a91bc7bSHarrietAkot 11278a91bc7bSHarrietAkot #define IMPL_GETNEXT(NAME, V) \ 1128d2215e79SRainer Orth bool _mlir_ciface_##NAME(void *tensor, \ 1129d2215e79SRainer Orth StridedMemRefType<index_type, 1> *iref, \ 11308a91bc7bSHarrietAkot StridedMemRefType<V, 0> *vref) { \ 11314f2ec7f9SAart Bik assert(tensor &&iref &&vref); \ 11328a91bc7bSHarrietAkot assert(iref->strides[0] == 1); \ 1133d2215e79SRainer Orth index_type *indx = iref->data + iref->offset; \ 1134c9f2beffSMehdi Amini V *value = vref->data + vref->offset; \ 11358a91bc7bSHarrietAkot const uint64_t isize = iref->sizes[0]; \ 11368a91bc7bSHarrietAkot auto iter = static_cast<SparseTensorCOO<V> *>(tensor); \ 11378a91bc7bSHarrietAkot const Element<V> *elem = iter->getNext(); \ 113863bdcaf9Swren romano if (elem == nullptr) \ 11398a91bc7bSHarrietAkot return false; \ 11408a91bc7bSHarrietAkot for (uint64_t r = 0; r < isize; r++) \ 11418a91bc7bSHarrietAkot indx[r] = elem->indices[r]; \ 11428a91bc7bSHarrietAkot *value = elem->value; \ 11438a91bc7bSHarrietAkot return true; \ 11448a91bc7bSHarrietAkot } 11458a91bc7bSHarrietAkot 1146f66e5769SAart Bik #define IMPL_LEXINSERT(NAME, V) \ 1147d2215e79SRainer Orth void _mlir_ciface_##NAME(void *tensor, \ 1148d2215e79SRainer Orth StridedMemRefType<index_type, 1> *cref, V val) { \ 11494f2ec7f9SAart Bik assert(tensor &&cref); \ 1150f66e5769SAart Bik assert(cref->strides[0] == 1); \ 1151d2215e79SRainer Orth index_type *cursor = cref->data + cref->offset; \ 1152f66e5769SAart Bik assert(cursor); \ 1153f66e5769SAart Bik static_cast<SparseTensorStorageBase *>(tensor)->lexInsert(cursor, val); \ 1154f66e5769SAart Bik } 1155f66e5769SAart Bik 11564f2ec7f9SAart Bik #define IMPL_EXPINSERT(NAME, V) \ 11574f2ec7f9SAart Bik void _mlir_ciface_##NAME( \ 1158d2215e79SRainer Orth void *tensor, StridedMemRefType<index_type, 1> *cref, \ 11594f2ec7f9SAart Bik StridedMemRefType<V, 1> *vref, StridedMemRefType<bool, 1> *fref, \ 1160d2215e79SRainer Orth StridedMemRefType<index_type, 1> *aref, index_type count) { \ 11614f2ec7f9SAart Bik assert(tensor &&cref &&vref &&fref &&aref); \ 11624f2ec7f9SAart Bik assert(cref->strides[0] == 1); \ 11634f2ec7f9SAart Bik assert(vref->strides[0] == 1); \ 11644f2ec7f9SAart Bik assert(fref->strides[0] == 1); \ 11654f2ec7f9SAart Bik assert(aref->strides[0] == 1); \ 11664f2ec7f9SAart Bik assert(vref->sizes[0] == fref->sizes[0]); \ 1167d2215e79SRainer Orth index_type *cursor = cref->data + cref->offset; \ 1168c9f2beffSMehdi Amini V *values = vref->data + vref->offset; \ 11694f2ec7f9SAart Bik bool *filled = fref->data + fref->offset; \ 1170d2215e79SRainer Orth index_type *added = aref->data + aref->offset; \ 11714f2ec7f9SAart Bik static_cast<SparseTensorStorageBase *>(tensor)->expInsert( \ 11724f2ec7f9SAart Bik cursor, values, filled, added, count); \ 11734f2ec7f9SAart Bik } 11744f2ec7f9SAart Bik 1175d2215e79SRainer Orth // Assume index_type is in fact uint64_t, so that _mlir_ciface_newSparseTensor 1176bc04a470Swren romano // can safely rewrite kIndex to kU64. We make this assertion to guarantee 1177bc04a470Swren romano // that this file cannot get out of sync with its header. 1178d2215e79SRainer Orth static_assert(std::is_same<index_type, uint64_t>::value, 1179d2215e79SRainer Orth "Expected index_type == uint64_t"); 1180bc04a470Swren romano 11818a91bc7bSHarrietAkot /// Constructs a new sparse tensor. This is the "swiss army knife" 11828a91bc7bSHarrietAkot /// method for materializing sparse tensors into the computation. 11838a91bc7bSHarrietAkot /// 1184845561ecSwren romano /// Action: 11858a91bc7bSHarrietAkot /// kEmpty = returns empty storage to fill later 11868a91bc7bSHarrietAkot /// kFromFile = returns storage, where ptr contains filename to read 11878a91bc7bSHarrietAkot /// kFromCOO = returns storage, where ptr contains coordinate scheme to assign 11888a91bc7bSHarrietAkot /// kEmptyCOO = returns empty coordinate scheme to fill and use with kFromCOO 11898a91bc7bSHarrietAkot /// kToCOO = returns coordinate scheme from storage in ptr to use with kFromCOO 1190845561ecSwren romano /// kToIterator = returns iterator from storage in ptr (call getNext() to use) 11918a91bc7bSHarrietAkot void * 1192845561ecSwren romano _mlir_ciface_newSparseTensor(StridedMemRefType<DimLevelType, 1> *aref, // NOLINT 1193d2215e79SRainer Orth StridedMemRefType<index_type, 1> *sref, 1194d2215e79SRainer Orth StridedMemRefType<index_type, 1> *pref, 1195845561ecSwren romano OverheadType ptrTp, OverheadType indTp, 1196845561ecSwren romano PrimaryType valTp, Action action, void *ptr) { 11978a91bc7bSHarrietAkot assert(aref && sref && pref); 11988a91bc7bSHarrietAkot assert(aref->strides[0] == 1 && sref->strides[0] == 1 && 11998a91bc7bSHarrietAkot pref->strides[0] == 1); 12008a91bc7bSHarrietAkot assert(aref->sizes[0] == sref->sizes[0] && sref->sizes[0] == pref->sizes[0]); 1201845561ecSwren romano const DimLevelType *sparsity = aref->data + aref->offset; 1202d83a7068Swren romano const index_type *shape = sref->data + sref->offset; 1203d2215e79SRainer Orth const index_type *perm = pref->data + pref->offset; 12048a91bc7bSHarrietAkot uint64_t rank = aref->sizes[0]; 12058a91bc7bSHarrietAkot 1206bc04a470Swren romano // Rewrite kIndex to kU64, to avoid introducing a bunch of new cases. 1207bc04a470Swren romano // This is safe because of the static_assert above. 1208bc04a470Swren romano if (ptrTp == OverheadType::kIndex) 1209bc04a470Swren romano ptrTp = OverheadType::kU64; 1210bc04a470Swren romano if (indTp == OverheadType::kIndex) 1211bc04a470Swren romano indTp = OverheadType::kU64; 1212bc04a470Swren romano 12138a91bc7bSHarrietAkot // Double matrices with all combinations of overhead storage. 1214845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU64, PrimaryType::kF64, uint64_t, 1215845561ecSwren romano uint64_t, double); 1216845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU32, PrimaryType::kF64, uint64_t, 1217845561ecSwren romano uint32_t, double); 1218845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU16, PrimaryType::kF64, uint64_t, 1219845561ecSwren romano uint16_t, double); 1220845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU8, PrimaryType::kF64, uint64_t, 1221845561ecSwren romano uint8_t, double); 1222845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU64, PrimaryType::kF64, uint32_t, 1223845561ecSwren romano uint64_t, double); 1224845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU32, PrimaryType::kF64, uint32_t, 1225845561ecSwren romano uint32_t, double); 1226845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU16, PrimaryType::kF64, uint32_t, 1227845561ecSwren romano uint16_t, double); 1228845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU8, PrimaryType::kF64, uint32_t, 1229845561ecSwren romano uint8_t, double); 1230845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU64, PrimaryType::kF64, uint16_t, 1231845561ecSwren romano uint64_t, double); 1232845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU32, PrimaryType::kF64, uint16_t, 1233845561ecSwren romano uint32_t, double); 1234845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU16, PrimaryType::kF64, uint16_t, 1235845561ecSwren romano uint16_t, double); 1236845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU8, PrimaryType::kF64, uint16_t, 1237845561ecSwren romano uint8_t, double); 1238845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU64, PrimaryType::kF64, uint8_t, 1239845561ecSwren romano uint64_t, double); 1240845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU32, PrimaryType::kF64, uint8_t, 1241845561ecSwren romano uint32_t, double); 1242845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU16, PrimaryType::kF64, uint8_t, 1243845561ecSwren romano uint16_t, double); 1244845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU8, PrimaryType::kF64, uint8_t, 1245845561ecSwren romano uint8_t, double); 12468a91bc7bSHarrietAkot 12478a91bc7bSHarrietAkot // Float matrices with all combinations of overhead storage. 1248845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU64, PrimaryType::kF32, uint64_t, 1249845561ecSwren romano uint64_t, float); 1250845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU32, PrimaryType::kF32, uint64_t, 1251845561ecSwren romano uint32_t, float); 1252845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU16, PrimaryType::kF32, uint64_t, 1253845561ecSwren romano uint16_t, float); 1254845561ecSwren romano CASE(OverheadType::kU64, OverheadType::kU8, PrimaryType::kF32, uint64_t, 1255845561ecSwren romano uint8_t, float); 1256845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU64, PrimaryType::kF32, uint32_t, 1257845561ecSwren romano uint64_t, float); 1258845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU32, PrimaryType::kF32, uint32_t, 1259845561ecSwren romano uint32_t, float); 1260845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU16, PrimaryType::kF32, uint32_t, 1261845561ecSwren romano uint16_t, float); 1262845561ecSwren romano CASE(OverheadType::kU32, OverheadType::kU8, PrimaryType::kF32, uint32_t, 1263845561ecSwren romano uint8_t, float); 1264845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU64, PrimaryType::kF32, uint16_t, 1265845561ecSwren romano uint64_t, float); 1266845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU32, PrimaryType::kF32, uint16_t, 1267845561ecSwren romano uint32_t, float); 1268845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU16, PrimaryType::kF32, uint16_t, 1269845561ecSwren romano uint16_t, float); 1270845561ecSwren romano CASE(OverheadType::kU16, OverheadType::kU8, PrimaryType::kF32, uint16_t, 1271845561ecSwren romano uint8_t, float); 1272845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU64, PrimaryType::kF32, uint8_t, 1273845561ecSwren romano uint64_t, float); 1274845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU32, PrimaryType::kF32, uint8_t, 1275845561ecSwren romano uint32_t, float); 1276845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU16, PrimaryType::kF32, uint8_t, 1277845561ecSwren romano uint16_t, float); 1278845561ecSwren romano CASE(OverheadType::kU8, OverheadType::kU8, PrimaryType::kF32, uint8_t, 1279845561ecSwren romano uint8_t, float); 12808a91bc7bSHarrietAkot 1281845561ecSwren romano // Integral matrices with both overheads of the same type. 1282845561ecSwren romano CASE_SECSAME(OverheadType::kU64, PrimaryType::kI64, uint64_t, int64_t); 1283845561ecSwren romano CASE_SECSAME(OverheadType::kU64, PrimaryType::kI32, uint64_t, int32_t); 1284845561ecSwren romano CASE_SECSAME(OverheadType::kU64, PrimaryType::kI16, uint64_t, int16_t); 1285845561ecSwren romano CASE_SECSAME(OverheadType::kU64, PrimaryType::kI8, uint64_t, int8_t); 1286845561ecSwren romano CASE_SECSAME(OverheadType::kU32, PrimaryType::kI32, uint32_t, int32_t); 1287845561ecSwren romano CASE_SECSAME(OverheadType::kU32, PrimaryType::kI16, uint32_t, int16_t); 1288845561ecSwren romano CASE_SECSAME(OverheadType::kU32, PrimaryType::kI8, uint32_t, int8_t); 1289845561ecSwren romano CASE_SECSAME(OverheadType::kU16, PrimaryType::kI32, uint16_t, int32_t); 1290845561ecSwren romano CASE_SECSAME(OverheadType::kU16, PrimaryType::kI16, uint16_t, int16_t); 1291845561ecSwren romano CASE_SECSAME(OverheadType::kU16, PrimaryType::kI8, uint16_t, int8_t); 1292845561ecSwren romano CASE_SECSAME(OverheadType::kU8, PrimaryType::kI32, uint8_t, int32_t); 1293845561ecSwren romano CASE_SECSAME(OverheadType::kU8, PrimaryType::kI16, uint8_t, int16_t); 1294845561ecSwren romano CASE_SECSAME(OverheadType::kU8, PrimaryType::kI8, uint8_t, int8_t); 12958a91bc7bSHarrietAkot 12968a91bc7bSHarrietAkot // Unsupported case (add above if needed). 12978a91bc7bSHarrietAkot fputs("unsupported combination of types\n", stderr); 12988a91bc7bSHarrietAkot exit(1); 12998a91bc7bSHarrietAkot } 13008a91bc7bSHarrietAkot 13018a91bc7bSHarrietAkot /// Methods that provide direct access to pointers. 1302d2215e79SRainer Orth IMPL_GETOVERHEAD(sparsePointers, index_type, getPointers) 13038a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparsePointers64, uint64_t, getPointers) 13048a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparsePointers32, uint32_t, getPointers) 13058a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparsePointers16, uint16_t, getPointers) 13068a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparsePointers8, uint8_t, getPointers) 13078a91bc7bSHarrietAkot 13088a91bc7bSHarrietAkot /// Methods that provide direct access to indices. 1309d2215e79SRainer Orth IMPL_GETOVERHEAD(sparseIndices, index_type, getIndices) 13108a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparseIndices64, uint64_t, getIndices) 13118a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparseIndices32, uint32_t, getIndices) 13128a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparseIndices16, uint16_t, getIndices) 13138a91bc7bSHarrietAkot IMPL_GETOVERHEAD(sparseIndices8, uint8_t, getIndices) 13148a91bc7bSHarrietAkot 13158a91bc7bSHarrietAkot /// Methods that provide direct access to values. 13168a91bc7bSHarrietAkot IMPL_SPARSEVALUES(sparseValuesF64, double, getValues) 13178a91bc7bSHarrietAkot IMPL_SPARSEVALUES(sparseValuesF32, float, getValues) 13188a91bc7bSHarrietAkot IMPL_SPARSEVALUES(sparseValuesI64, int64_t, getValues) 13198a91bc7bSHarrietAkot IMPL_SPARSEVALUES(sparseValuesI32, int32_t, getValues) 13208a91bc7bSHarrietAkot IMPL_SPARSEVALUES(sparseValuesI16, int16_t, getValues) 13218a91bc7bSHarrietAkot IMPL_SPARSEVALUES(sparseValuesI8, int8_t, getValues) 13228a91bc7bSHarrietAkot 13238a91bc7bSHarrietAkot /// Helper to add value to coordinate scheme, one per value type. 13248a91bc7bSHarrietAkot IMPL_ADDELT(addEltF64, double) 13258a91bc7bSHarrietAkot IMPL_ADDELT(addEltF32, float) 13268a91bc7bSHarrietAkot IMPL_ADDELT(addEltI64, int64_t) 13278a91bc7bSHarrietAkot IMPL_ADDELT(addEltI32, int32_t) 13288a91bc7bSHarrietAkot IMPL_ADDELT(addEltI16, int16_t) 13298a91bc7bSHarrietAkot IMPL_ADDELT(addEltI8, int8_t) 13308a91bc7bSHarrietAkot 13318a91bc7bSHarrietAkot /// Helper to enumerate elements of coordinate scheme, one per value type. 13328a91bc7bSHarrietAkot IMPL_GETNEXT(getNextF64, double) 13338a91bc7bSHarrietAkot IMPL_GETNEXT(getNextF32, float) 13348a91bc7bSHarrietAkot IMPL_GETNEXT(getNextI64, int64_t) 13358a91bc7bSHarrietAkot IMPL_GETNEXT(getNextI32, int32_t) 13368a91bc7bSHarrietAkot IMPL_GETNEXT(getNextI16, int16_t) 13378a91bc7bSHarrietAkot IMPL_GETNEXT(getNextI8, int8_t) 13388a91bc7bSHarrietAkot 13396438783fSAart Bik /// Insert elements in lexicographical index order, one per value type. 1340f66e5769SAart Bik IMPL_LEXINSERT(lexInsertF64, double) 1341f66e5769SAart Bik IMPL_LEXINSERT(lexInsertF32, float) 1342f66e5769SAart Bik IMPL_LEXINSERT(lexInsertI64, int64_t) 1343f66e5769SAart Bik IMPL_LEXINSERT(lexInsertI32, int32_t) 1344f66e5769SAart Bik IMPL_LEXINSERT(lexInsertI16, int16_t) 1345f66e5769SAart Bik IMPL_LEXINSERT(lexInsertI8, int8_t) 1346f66e5769SAart Bik 13476438783fSAart Bik /// Insert using expansion, one per value type. 13484f2ec7f9SAart Bik IMPL_EXPINSERT(expInsertF64, double) 13494f2ec7f9SAart Bik IMPL_EXPINSERT(expInsertF32, float) 13504f2ec7f9SAart Bik IMPL_EXPINSERT(expInsertI64, int64_t) 13514f2ec7f9SAart Bik IMPL_EXPINSERT(expInsertI32, int32_t) 13524f2ec7f9SAart Bik IMPL_EXPINSERT(expInsertI16, int16_t) 13534f2ec7f9SAart Bik IMPL_EXPINSERT(expInsertI8, int8_t) 13544f2ec7f9SAart Bik 13558a91bc7bSHarrietAkot #undef CASE 13568a91bc7bSHarrietAkot #undef IMPL_SPARSEVALUES 13578a91bc7bSHarrietAkot #undef IMPL_GETOVERHEAD 13588a91bc7bSHarrietAkot #undef IMPL_ADDELT 13598a91bc7bSHarrietAkot #undef IMPL_GETNEXT 13604f2ec7f9SAart Bik #undef IMPL_LEXINSERT 13614f2ec7f9SAart Bik #undef IMPL_EXPINSERT 13626438783fSAart Bik 13636438783fSAart Bik /// Output a sparse tensor, one per value type. 13646438783fSAart Bik void outSparseTensorF64(void *tensor, void *dest, bool sort) { 13656438783fSAart Bik return outSparseTensor<double>(tensor, dest, sort); 13666438783fSAart Bik } 13676438783fSAart Bik void outSparseTensorF32(void *tensor, void *dest, bool sort) { 13686438783fSAart Bik return outSparseTensor<float>(tensor, dest, sort); 13696438783fSAart Bik } 13706438783fSAart Bik void outSparseTensorI64(void *tensor, void *dest, bool sort) { 13716438783fSAart Bik return outSparseTensor<int64_t>(tensor, dest, sort); 13726438783fSAart Bik } 13736438783fSAart Bik void outSparseTensorI32(void *tensor, void *dest, bool sort) { 13746438783fSAart Bik return outSparseTensor<int32_t>(tensor, dest, sort); 13756438783fSAart Bik } 13766438783fSAart Bik void outSparseTensorI16(void *tensor, void *dest, bool sort) { 13776438783fSAart Bik return outSparseTensor<int16_t>(tensor, dest, sort); 13786438783fSAart Bik } 13796438783fSAart Bik void outSparseTensorI8(void *tensor, void *dest, bool sort) { 13806438783fSAart Bik return outSparseTensor<int8_t>(tensor, dest, sort); 13816438783fSAart Bik } 13828a91bc7bSHarrietAkot 13838a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 13848a91bc7bSHarrietAkot // 13858a91bc7bSHarrietAkot // Public API with methods that accept C-style data structures to interact 13868a91bc7bSHarrietAkot // with sparse tensors, which are only visible as opaque pointers externally. 13878a91bc7bSHarrietAkot // These methods can be used both by MLIR compiler-generated code as well as by 13888a91bc7bSHarrietAkot // an external runtime that wants to interact with MLIR compiler-generated code. 13898a91bc7bSHarrietAkot // 13908a91bc7bSHarrietAkot //===----------------------------------------------------------------------===// 13918a91bc7bSHarrietAkot 13928a91bc7bSHarrietAkot /// Helper method to read a sparse tensor filename from the environment, 13938a91bc7bSHarrietAkot /// defined with the naming convention ${TENSOR0}, ${TENSOR1}, etc. 1394d2215e79SRainer Orth char *getTensorFilename(index_type id) { 13958a91bc7bSHarrietAkot char var[80]; 13968a91bc7bSHarrietAkot sprintf(var, "TENSOR%" PRIu64, id); 13978a91bc7bSHarrietAkot char *env = getenv(var); 13983734c078Swren romano if (!env) { 13993734c078Swren romano fprintf(stderr, "Environment variable %s is not set\n", var); 14003734c078Swren romano exit(1); 14013734c078Swren romano } 14028a91bc7bSHarrietAkot return env; 14038a91bc7bSHarrietAkot } 14048a91bc7bSHarrietAkot 14058a91bc7bSHarrietAkot /// Returns size of sparse tensor in given dimension. 1406d2215e79SRainer Orth index_type sparseDimSize(void *tensor, index_type d) { 14078a91bc7bSHarrietAkot return static_cast<SparseTensorStorageBase *>(tensor)->getDimSize(d); 14088a91bc7bSHarrietAkot } 14098a91bc7bSHarrietAkot 1410f66e5769SAart Bik /// Finalizes lexicographic insertions. 1411f66e5769SAart Bik void endInsert(void *tensor) { 1412f66e5769SAart Bik return static_cast<SparseTensorStorageBase *>(tensor)->endInsert(); 1413f66e5769SAart Bik } 1414f66e5769SAart Bik 14158a91bc7bSHarrietAkot /// Releases sparse tensor storage. 14168a91bc7bSHarrietAkot void delSparseTensor(void *tensor) { 14178a91bc7bSHarrietAkot delete static_cast<SparseTensorStorageBase *>(tensor); 14188a91bc7bSHarrietAkot } 14198a91bc7bSHarrietAkot 142063bdcaf9Swren romano /// Releases sparse tensor coordinate scheme. 142163bdcaf9Swren romano #define IMPL_DELCOO(VNAME, V) \ 142263bdcaf9Swren romano void delSparseTensorCOO##VNAME(void *coo) { \ 142363bdcaf9Swren romano delete static_cast<SparseTensorCOO<V> *>(coo); \ 142463bdcaf9Swren romano } 142563bdcaf9Swren romano IMPL_DELCOO(F64, double) 142663bdcaf9Swren romano IMPL_DELCOO(F32, float) 142763bdcaf9Swren romano IMPL_DELCOO(I64, int64_t) 142863bdcaf9Swren romano IMPL_DELCOO(I32, int32_t) 142963bdcaf9Swren romano IMPL_DELCOO(I16, int16_t) 143063bdcaf9Swren romano IMPL_DELCOO(I8, int8_t) 143163bdcaf9Swren romano #undef IMPL_DELCOO 143263bdcaf9Swren romano 14338a91bc7bSHarrietAkot /// Initializes sparse tensor from a COO-flavored format expressed using C-style 14348a91bc7bSHarrietAkot /// data structures. The expected parameters are: 14358a91bc7bSHarrietAkot /// 14368a91bc7bSHarrietAkot /// rank: rank of tensor 14378a91bc7bSHarrietAkot /// nse: number of specified elements (usually the nonzeros) 14388a91bc7bSHarrietAkot /// shape: array with dimension size for each rank 14398a91bc7bSHarrietAkot /// values: a "nse" array with values for all specified elements 14408a91bc7bSHarrietAkot /// indices: a flat "nse x rank" array with indices for all specified elements 144120eaa88fSBixia Zheng /// perm: the permutation of the dimensions in the storage 144220eaa88fSBixia Zheng /// sparse: the sparsity for the dimensions 14438a91bc7bSHarrietAkot /// 14448a91bc7bSHarrietAkot /// For example, the sparse matrix 14458a91bc7bSHarrietAkot /// | 1.0 0.0 0.0 | 14468a91bc7bSHarrietAkot /// | 0.0 5.0 3.0 | 14478a91bc7bSHarrietAkot /// can be passed as 14488a91bc7bSHarrietAkot /// rank = 2 14498a91bc7bSHarrietAkot /// nse = 3 14508a91bc7bSHarrietAkot /// shape = [2, 3] 14518a91bc7bSHarrietAkot /// values = [1.0, 5.0, 3.0] 14528a91bc7bSHarrietAkot /// indices = [ 0, 0, 1, 1, 1, 2] 14538a91bc7bSHarrietAkot // 145420eaa88fSBixia Zheng // TODO: generalize beyond 64-bit indices. 14558a91bc7bSHarrietAkot // 14566438783fSAart Bik void *convertToMLIRSparseTensorF64(uint64_t rank, uint64_t nse, uint64_t *shape, 145720eaa88fSBixia Zheng double *values, uint64_t *indices, 145820eaa88fSBixia Zheng uint64_t *perm, uint8_t *sparse) { 145920eaa88fSBixia Zheng return toMLIRSparseTensor<double>(rank, nse, shape, values, indices, perm, 146020eaa88fSBixia Zheng sparse); 14618a91bc7bSHarrietAkot } 14626438783fSAart Bik void *convertToMLIRSparseTensorF32(uint64_t rank, uint64_t nse, uint64_t *shape, 146320eaa88fSBixia Zheng float *values, uint64_t *indices, 146420eaa88fSBixia Zheng uint64_t *perm, uint8_t *sparse) { 146520eaa88fSBixia Zheng return toMLIRSparseTensor<float>(rank, nse, shape, values, indices, perm, 146620eaa88fSBixia Zheng sparse); 14678a91bc7bSHarrietAkot } 14681abcdc67SAart Bik void *convertToMLIRSparseTensorI64(uint64_t rank, uint64_t nse, uint64_t *shape, 14691abcdc67SAart Bik int64_t *values, uint64_t *indices, 14701abcdc67SAart Bik uint64_t *perm, uint8_t *sparse) { 14711abcdc67SAart Bik return toMLIRSparseTensor<int64_t>(rank, nse, shape, values, indices, perm, 14721abcdc67SAart Bik sparse); 14731abcdc67SAart Bik } 14741abcdc67SAart Bik void *convertToMLIRSparseTensorI32(uint64_t rank, uint64_t nse, uint64_t *shape, 14751abcdc67SAart Bik int32_t *values, uint64_t *indices, 14761abcdc67SAart Bik uint64_t *perm, uint8_t *sparse) { 14771abcdc67SAart Bik return toMLIRSparseTensor<int32_t>(rank, nse, shape, values, indices, perm, 14781abcdc67SAart Bik sparse); 14791abcdc67SAart Bik } 14801abcdc67SAart Bik void *convertToMLIRSparseTensorI16(uint64_t rank, uint64_t nse, uint64_t *shape, 14811abcdc67SAart Bik int16_t *values, uint64_t *indices, 14821abcdc67SAart Bik uint64_t *perm, uint8_t *sparse) { 14831abcdc67SAart Bik return toMLIRSparseTensor<int16_t>(rank, nse, shape, values, indices, perm, 14841abcdc67SAart Bik sparse); 14851abcdc67SAart Bik } 14861abcdc67SAart Bik void *convertToMLIRSparseTensorI8(uint64_t rank, uint64_t nse, uint64_t *shape, 14871abcdc67SAart Bik int8_t *values, uint64_t *indices, 14881abcdc67SAart Bik uint64_t *perm, uint8_t *sparse) { 14891abcdc67SAart Bik return toMLIRSparseTensor<int8_t>(rank, nse, shape, values, indices, perm, 14901abcdc67SAart Bik sparse); 14911abcdc67SAart Bik } 14928a91bc7bSHarrietAkot 14932f49e6b0SBixia Zheng /// Converts a sparse tensor to COO-flavored format expressed using C-style 14942f49e6b0SBixia Zheng /// data structures. The expected output parameters are pointers for these 14952f49e6b0SBixia Zheng /// values: 14962f49e6b0SBixia Zheng /// 14972f49e6b0SBixia Zheng /// rank: rank of tensor 14982f49e6b0SBixia Zheng /// nse: number of specified elements (usually the nonzeros) 14992f49e6b0SBixia Zheng /// shape: array with dimension size for each rank 15002f49e6b0SBixia Zheng /// values: a "nse" array with values for all specified elements 15012f49e6b0SBixia Zheng /// indices: a flat "nse x rank" array with indices for all specified elements 15022f49e6b0SBixia Zheng /// 15032f49e6b0SBixia Zheng /// The input is a pointer to SparseTensorStorage<P, I, V>, typically returned 15042f49e6b0SBixia Zheng /// from convertToMLIRSparseTensor. 15052f49e6b0SBixia Zheng /// 15062f49e6b0SBixia Zheng // TODO: Currently, values are copied from SparseTensorStorage to 15072f49e6b0SBixia Zheng // SparseTensorCOO, then to the output. We may want to reduce the number of 15082f49e6b0SBixia Zheng // copies. 15092f49e6b0SBixia Zheng // 15106438783fSAart Bik // TODO: generalize beyond 64-bit indices, no dim ordering, all dimensions 15116438783fSAart Bik // compressed 15122f49e6b0SBixia Zheng // 15136438783fSAart Bik void convertFromMLIRSparseTensorF64(void *tensor, uint64_t *pRank, 15146438783fSAart Bik uint64_t *pNse, uint64_t **pShape, 15156438783fSAart Bik double **pValues, uint64_t **pIndices) { 15166438783fSAart Bik fromMLIRSparseTensor<double>(tensor, pRank, pNse, pShape, pValues, pIndices); 15172f49e6b0SBixia Zheng } 15186438783fSAart Bik void convertFromMLIRSparseTensorF32(void *tensor, uint64_t *pRank, 15196438783fSAart Bik uint64_t *pNse, uint64_t **pShape, 15206438783fSAart Bik float **pValues, uint64_t **pIndices) { 15216438783fSAart Bik fromMLIRSparseTensor<float>(tensor, pRank, pNse, pShape, pValues, pIndices); 15222f49e6b0SBixia Zheng } 15231abcdc67SAart Bik void convertFromMLIRSparseTensorI64(void *tensor, uint64_t *pRank, 15241abcdc67SAart Bik uint64_t *pNse, uint64_t **pShape, 15251abcdc67SAart Bik int64_t **pValues, uint64_t **pIndices) { 15261abcdc67SAart Bik fromMLIRSparseTensor<int64_t>(tensor, pRank, pNse, pShape, pValues, pIndices); 15271abcdc67SAart Bik } 15281abcdc67SAart Bik void convertFromMLIRSparseTensorI32(void *tensor, uint64_t *pRank, 15291abcdc67SAart Bik uint64_t *pNse, uint64_t **pShape, 15301abcdc67SAart Bik int32_t **pValues, uint64_t **pIndices) { 15311abcdc67SAart Bik fromMLIRSparseTensor<int32_t>(tensor, pRank, pNse, pShape, pValues, pIndices); 15321abcdc67SAart Bik } 15331abcdc67SAart Bik void convertFromMLIRSparseTensorI16(void *tensor, uint64_t *pRank, 15341abcdc67SAart Bik uint64_t *pNse, uint64_t **pShape, 15351abcdc67SAart Bik int16_t **pValues, uint64_t **pIndices) { 15361abcdc67SAart Bik fromMLIRSparseTensor<int16_t>(tensor, pRank, pNse, pShape, pValues, pIndices); 15371abcdc67SAart Bik } 15381abcdc67SAart Bik void convertFromMLIRSparseTensorI8(void *tensor, uint64_t *pRank, 15391abcdc67SAart Bik uint64_t *pNse, uint64_t **pShape, 15401abcdc67SAart Bik int8_t **pValues, uint64_t **pIndices) { 15411abcdc67SAart Bik fromMLIRSparseTensor<int8_t>(tensor, pRank, pNse, pShape, pValues, pIndices); 15421abcdc67SAart Bik } 1543efa15f41SAart Bik 15448a91bc7bSHarrietAkot } // extern "C" 15458a91bc7bSHarrietAkot 15468a91bc7bSHarrietAkot #endif // MLIR_CRUNNERUTILS_DEFINE_FUNCTIONS 1547