//===- NNZ.cpp - NNZ-statistics for direct sparse2sparse conversion -------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains method definitions for `SparseTensorNNZ`. // // This file is part of the lightweight runtime support library for sparse // tensor manipulations. The functionality of the support library is meant // to simplify benchmarking, testing, and debugging MLIR code operating on // sparse tensors. However, the provided functionality is **not** part of // core MLIR itself. // //===----------------------------------------------------------------------===// #include "mlir/ExecutionEngine/SparseTensor/Storage.h" using namespace mlir::sparse_tensor; //===----------------------------------------------------------------------===// /// Allocate the statistics structure for the desired sizes and /// sparsity (in the target tensor's storage-order). This constructor /// does not actually populate the statistics, however; for that see /// `initialize`. /// /// Precondition: `dimSizes` must not contain zeros. SparseTensorNNZ::SparseTensorNNZ(const std::vector &dimSizes, const std::vector &sparsity) : dimSizes(dimSizes), dimTypes(sparsity), nnz(getRank()) { assert(dimSizes.size() == dimTypes.size() && "Rank mismatch"); bool alreadyCompressed = false; (void)alreadyCompressed; uint64_t sz = 1; // the product of all `dimSizes` strictly less than `r`. for (uint64_t rank = getRank(), r = 0; r < rank; r++) { const DimLevelType dlt = sparsity[r]; if (isCompressedDLT(dlt)) { if (alreadyCompressed) MLIR_SPARSETENSOR_FATAL( "Multiple compressed layers not currently supported"); alreadyCompressed = true; nnz[r].resize(sz, 0); // Both allocate and zero-initialize. } else if (isDenseDLT(dlt)) { if (alreadyCompressed) MLIR_SPARSETENSOR_FATAL( "Dense after compressed not currently supported"); } else if (isSingletonDLT(dlt)) { // Singleton after Compressed causes no problems for allocating // `nnz` nor for the yieldPos loop. This remains true even // when adding support for multiple compressed dimensions or // for dense-after-compressed. } else { MLIR_SPARSETENSOR_FATAL("unsupported dimension level type: %d\n", static_cast(dlt)); } sz = detail::checkedMul(sz, dimSizes[r]); } } /// Lexicographically enumerates all indicies for dimensions strictly /// less than `stopDim`, and passes their nnz statistic to the callback. /// Since our use-case only requires the statistic not the coordinates /// themselves, we do not bother to construct those coordinates. void SparseTensorNNZ::forallIndices(uint64_t stopDim, SparseTensorNNZ::NNZConsumer yield) const { assert(stopDim < getRank() && "Dimension out of bounds"); assert(isCompressedDLT(dimTypes[stopDim]) && "Cannot look up non-compressed dimensions"); forallIndices(yield, stopDim, 0, 0); } /// Adds a new element (i.e., increment its statistics). We use /// a method rather than inlining into the lambda in `initialize`, /// to avoid spurious templating over `V`. And this method is private /// to avoid needing to re-assert validity of `ind` (which is guaranteed /// by `forallElements`). void SparseTensorNNZ::add(const std::vector &ind) { uint64_t parentPos = 0; for (uint64_t rank = getRank(), r = 0; r < rank; ++r) { if (isCompressedDLT(dimTypes[r])) nnz[r][parentPos]++; parentPos = parentPos * dimSizes[r] + ind[r]; } } /// Recursive component of the public `forallIndices`. void SparseTensorNNZ::forallIndices(SparseTensorNNZ::NNZConsumer yield, uint64_t stopDim, uint64_t parentPos, uint64_t d) const { assert(d <= stopDim); if (d == stopDim) { assert(parentPos < nnz[d].size() && "Cursor is out of range"); yield(nnz[d][parentPos]); } else { const uint64_t sz = dimSizes[d]; const uint64_t pstart = parentPos * sz; for (uint64_t i = 0; i < sz; i++) forallIndices(yield, stopDim, pstart + i, d + 1); } }