MLIR 24.0.0git
File.h
Go to the documentation of this file.
1//===- File.h - Reading sparse tensors from files ---------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements reading sparse tensor from files in one of the
10// following external formats:
11//
12// (1) Matrix Market Exchange (MME): *.mtx
13// https://math.nist.gov/MatrixMarket/formats.html
14//
15// (2) Formidable Repository of Open Sparse Tensors and Tools (FROSTT): *.tns
16// http://frostt.io/tensors/file-formats.html
17//
18//===----------------------------------------------------------------------===//
19
20#ifndef MLIR_EXECUTIONENGINE_SPARSETENSOR_FILE_H
21#define MLIR_EXECUTIONENGINE_SPARSETENSOR_FILE_H
22
26
27#include <cctype>
28#include <cerrno>
29#include <fstream>
30#include <limits>
31
32namespace mlir {
33namespace sparse_tensor {
34
35namespace detail {
36
37template <typename T>
38struct is_complex final : public std::false_type {};
39
40template <typename T>
41struct is_complex<std::complex<T>> final : public std::true_type {};
42
43template <typename T>
44struct is_complex<mlir::NonFloatComplex<T>> final : public std::true_type {};
45
46/// Returns an element-value of non-complex type. If `IsPattern` is true,
47/// then returns an arbitrary value. If `IsPattern` is false, then
48/// reads the value from the current line buffer beginning at `linePtr`.
49template <typename V, bool IsPattern>
50inline std::enable_if_t<!is_complex<V>::value, V> readValue(char **linePtr) {
51 // The external formats always store these numerical values with the type
52 // double, but we cast these values to the sparse tensor object type.
53 // For a pattern tensor, we arbitrarily pick the value 1 for all entries.
54 if constexpr (IsPattern)
55 return 1.0;
56 return strtod(*linePtr, linePtr);
57}
58
59/// Returns an element-value of complex type. If `IsPattern` is true,
60/// then returns an arbitrary value. If `IsPattern` is false, then reads
61/// the value from the current line buffer beginning at `linePtr`.
62template <typename V, bool IsPattern>
63inline std::enable_if_t<is_complex<V>::value, V> readValue(char **linePtr) {
64 // Read two values to make a complex. The external formats always store
65 // numerical values with the type double, but we cast these values to the
66 // sparse tensor object type. For a pattern tensor, we arbitrarily pick the
67 // value 1 for all entries.
68 if constexpr (IsPattern)
69 return V(1.0, 1.0);
70 double re = strtod(*linePtr, linePtr);
71 double im = strtod(*linePtr, linePtr);
72 // Avoiding brace-notation since that forbids narrowing to `float`.
73 return V(re, im);
74}
75
76/// Returns an element-value. If `isPattern` is true, then returns an
77/// arbitrary value. If `isPattern` is false, then reads the value from
78/// the current line buffer beginning at `linePtr`.
79template <typename V>
80inline V readValue(char **linePtr, bool isPattern) {
81 return isPattern ? readValue<V, true>(linePtr) : readValue<V, false>(linePtr);
82}
83
84} // namespace detail
85
86//===----------------------------------------------------------------------===//
87//
88// Reader class.
89//
90//===----------------------------------------------------------------------===//
91
92/// This class abstracts over the information stored in file headers,
93/// as well as providing the buffers and methods for parsing those headers.
94class SparseTensorReader final {
95public:
96 enum class ValueKind : uint8_t {
97 // The value before calling `readHeader`.
99 // Values that can be set by `readMMEHeader`.
101 kReal = 2,
104 // The value set by `readExtFROSTTHeader`.
106 };
107
108 explicit SparseTensorReader(const char *filename) : filename(filename) {
109 assert(filename && "Received nullptr for filename");
110 }
111
112 // Disallows copying, to avoid duplicating the `file` pointer.
115
116 /// Factory method to allocate a new reader, open the file, read the
117 /// header, and validate that the actual contents of the file match
118 /// the expected `dimShape` and `valTp`.
119 static SparseTensorReader *create(const char *filename, uint64_t dimRank,
120 const uint64_t *dimShape,
121 PrimaryType valTp) {
122 SparseTensorReader *reader = new SparseTensorReader(filename);
123 reader->openFile();
124 reader->readHeader();
125 if (!reader->canReadAs(valTp)) {
126 fprintf(stderr,
127 "Tensor element type %d not compatible with values in file %s\n",
128 static_cast<int>(valTp), filename);
129 exit(1);
130 }
131 reader->assertMatchesShape(dimRank, dimShape);
132 return reader;
133 }
134
135 // This dtor tries to avoid leaking the `file`. (Though it's better
136 // to call `closeFile` explicitly when possible, since there are
137 // circumstances where dtors are not called reliably.)
139
140 /// Opens the file for reading.
141 void openFile();
142
143 /// Closes the file.
144 void closeFile();
145
146 /// Reads and parses the file's header.
147 void readHeader();
148
149 /// Returns the stored value kind.
150 ValueKind getValueKind() const { return valueKind_; }
151
152 /// Checks if a header has been successfully read.
153 bool isValid() const { return valueKind_ != ValueKind::kInvalid; }
154
155 /// Checks if the file's ValueKind can be converted into the given
156 /// tensor PrimaryType. Is only valid after parsing the header.
157 bool canReadAs(PrimaryType valTy) const;
158
159 /// Gets the MME "pattern" property setting. Is only valid after
160 /// parsing the header.
161 bool isPattern() const {
162 assert(isValid() && "Attempt to isPattern() before readHeader()");
163 return valueKind_ == ValueKind::kPattern;
164 }
165
166 /// Gets the MME "symmetric" property setting. Is only valid after
167 /// parsing the header.
168 bool isSymmetric() const {
169 assert(isValid() && "Attempt to isSymmetric() before readHeader()");
170 return isSymmetric_;
171 }
172
173 /// Gets the dimension-rank of the tensor. Is only valid after parsing
174 /// the header.
175 uint64_t getRank() const {
176 assert(isValid() && "Attempt to getRank() before readHeader()");
177 return idata[0];
178 }
179
180 /// Gets the number of stored elements. Is only valid after parsing
181 /// the header.
182 uint64_t getNSE() const {
183 assert(isValid() && "Attempt to getNSE() before readHeader()");
184 return idata[1];
185 }
186
187 /// Gets the dimension-sizes array. The pointer itself is always
188 /// valid; however, the values stored therein are only valid after
189 /// parsing the header.
190 const uint64_t *getDimSizes() const { return idata + 2; }
191
192 /// Safely gets the size of the given dimension. Is only valid
193 /// after parsing the header.
194 uint64_t getDimSize(uint64_t d) const {
195 assert(d < getRank() && "Dimension out of bounds");
196 return idata[2 + d];
197 }
198
199 /// Asserts the shape subsumes the actual dimension sizes. Is only
200 /// valid after parsing the header.
201 void assertMatchesShape(uint64_t rank, const uint64_t *shape) const;
202
203 /// Allocates a new sparse-tensor storage object with the given encoding,
204 /// initializes it by reading all the elements from the file, and then
205 /// closes the file. Templated on P, C, and V.
206 template <typename P, typename C, typename V>
208 readSparseTensor(uint64_t lvlRank, const uint64_t *lvlSizes,
209 const LevelType *lvlTypes, const uint64_t *dim2lvl,
210 const uint64_t *lvl2dim) {
211 const uint64_t dimRank = getRank();
212 MapRef map(dimRank, lvlRank, dim2lvl, lvl2dim);
213 auto *lvlCOO = readCOO<V>(map, lvlSizes);
215 dimRank, getDimSizes(), lvlRank, lvlSizes, lvlTypes, dim2lvl, lvl2dim,
216 lvlCOO);
217 delete lvlCOO;
218 return tensor;
219 }
220
221 /// Reads the COO tensor from the file, stores the coordinates and values to
222 /// the given buffers, returns a boolean value to indicate whether the COO
223 /// elements are sorted.
224 template <typename C, typename V>
225 bool readToBuffers(uint64_t lvlRank, const uint64_t *dim2lvl,
226 const uint64_t *lvl2dim, C *lvlCoordinates, V *values);
227
228private:
229 /// Attempts to read a line from the file.
230 void readLine();
231
232 /// Reads the next line of the input file and parses the coordinates
233 /// into the `dimCoords` argument. Returns the position in the `line`
234 /// buffer where the element's value should be parsed from.
235 template <typename C>
236 char *readCoords(C *dimCoords) {
237 readLine();
238 // Local variable for tracking the parser's position in the `line` buffer.
239 char *linePtr = line;
240 for (uint64_t dimRank = getRank(), d = 0; d < dimRank; ++d) {
241 // Parse the 1-based coordinate.
242 while (std::isspace(static_cast<unsigned char>(*linePtr)))
243 ++linePtr;
244 errno = 0;
245 char *coordinateEnd = nullptr;
246 unsigned long long coordinate = strtoull(linePtr, &coordinateEnd, 10);
247 if (*linePtr == '-' || coordinateEnd == linePtr || errno == ERANGE ||
248 coordinate > std::numeric_limits<uint64_t>::max()) {
249 fprintf(stderr,
250 "Cannot parse coordinate for dimension %" PRIu64 " in %s\n", d,
251 filename);
252 exit(1);
253 }
254 linePtr = coordinateEnd;
255 uint64_t c = coordinate;
256 if (c == 0 || c > getDimSizes()[d]) {
257 fprintf(stderr,
258 "Coordinate %" PRIu64 " is out of bounds for dimension %" PRIu64
259 " with size %" PRIu64 " in %s\n",
260 c, d, getDimSizes()[d], filename);
261 exit(1);
262 }
263 if (c - 1 > std::numeric_limits<C>::max()) {
264 fprintf(stderr,
265 "Coordinate %" PRIu64
266 " cannot be represented by the requested coordinate type in "
267 "%s\n",
268 c, filename);
269 exit(1);
270 }
271 // Store the 0-based coordinate.
272 dimCoords[d] = static_cast<C>(c - 1);
273 }
274 return linePtr;
275 }
276
277 /// Reads all the elements from the file while applying the given map.
278 template <typename V>
279 SparseTensorCOO<V> *readCOO(const MapRef &map, const uint64_t *lvlSizes);
280
281 /// The implementation of `readCOO` that is templated `IsPattern` in order
282 /// to perform LICM without needing to duplicate the source code.
283 template <typename V, bool IsPattern>
284 void readCOOLoop(const MapRef &map, SparseTensorCOO<V> *coo);
285
286 /// The internal implementation of `readToBuffers`. We template over
287 /// `IsPattern` in order to perform LICM without needing to duplicate
288 /// the source code.
289 template <typename C, typename V, bool IsPattern>
290 bool readToBuffersLoop(const MapRef &map, C *lvlCoordinates, V *values);
291
292 /// Reads the MME header of a general sparse matrix of type real.
293 void readMMEHeader();
294
295 /// Reads the "extended" FROSTT header. Although not part of the
296 /// documented format, we assume that the file starts with optional
297 /// comments followed by two lines that define the rank, the number of
298 /// nonzeros, and the dimensions sizes (one per rank) of the sparse tensor.
299 void readExtFROSTTHeader();
300
301 static constexpr uint64_t kMaxRank = 510;
302 static constexpr int kColWidth = 1025;
303 const char *const filename;
304 FILE *file = nullptr;
305 ValueKind valueKind_ = ValueKind::kInvalid;
306 bool isSymmetric_ = false;
307 uint64_t idata[kMaxRank + 2];
308 char line[kColWidth];
309};
310
311//===----------------------------------------------------------------------===//
312//
313// Reader class methods.
314//
315//===----------------------------------------------------------------------===//
316
317template <typename V>
318SparseTensorCOO<V> *SparseTensorReader::readCOO(const MapRef &map,
319 const uint64_t *lvlSizes) {
320 assert(isValid() && "Attempt to readCOO() before readHeader()");
321 // Prepare a COO object with the number of stored elems as initial capacity.
322 auto *coo = new SparseTensorCOO<V>(map.getLvlRank(), lvlSizes, getNSE());
323 // Enter the reading loop.
324 if (isPattern())
325 readCOOLoop<V, true>(map, coo);
326 else
327 readCOOLoop<V, false>(map, coo);
328 // Close the file and return the COO.
329 closeFile();
330 return coo;
331}
332
333template <typename V, bool IsPattern>
334void SparseTensorReader::readCOOLoop(const MapRef &map,
335 SparseTensorCOO<V> *coo) {
336 const uint64_t dimRank = map.getDimRank();
337 const uint64_t lvlRank = map.getLvlRank();
338 assert(dimRank == getRank());
339 std::vector<uint64_t> dimCoords(dimRank);
340 std::vector<uint64_t> lvlCoords(lvlRank);
341 for (uint64_t k = 0, nse = getNSE(); k < nse; k++) {
342 char *linePtr = readCoords(dimCoords.data());
343 const V value = detail::readValue<V, IsPattern>(&linePtr);
344 map.pushforward(dimCoords.data(), lvlCoords.data());
345 coo->add(lvlCoords, value);
346 }
347}
348
349template <typename C, typename V>
351 const uint64_t *dim2lvl,
352 const uint64_t *lvl2dim,
353 C *lvlCoordinates, V *values) {
354 assert(isValid() && "Attempt to readCOO() before readHeader()");
355 MapRef map(getRank(), lvlRank, dim2lvl, lvl2dim);
356 bool isSorted =
357 isPattern() ? readToBuffersLoop<C, V, true>(map, lvlCoordinates, values)
358 : readToBuffersLoop<C, V, false>(map, lvlCoordinates, values);
359 closeFile();
360 return isSorted;
361}
362
363template <typename C, typename V, bool IsPattern>
364bool SparseTensorReader::readToBuffersLoop(const MapRef &map, C *lvlCoordinates,
365 V *values) {
366 const uint64_t dimRank = map.getDimRank();
367 const uint64_t lvlRank = map.getLvlRank();
368 const uint64_t nse = getNSE();
369 assert(dimRank == getRank());
370 std::vector<C> dimCoords(dimRank);
371 bool isSorted = false;
372 char *linePtr;
373 const auto readNextElement = [&]() {
374 linePtr = readCoords<C>(dimCoords.data());
375 map.pushforward(dimCoords.data(), lvlCoordinates);
376 *values = detail::readValue<V, IsPattern>(&linePtr);
377 if (isSorted) {
378 // Note that isSorted is set to false when reading the first element,
379 // to guarantee the safeness of using prevLvlCoords.
380 C *prevLvlCoords = lvlCoordinates - lvlRank;
381 for (uint64_t l = 0; l < lvlRank; ++l) {
382 if (prevLvlCoords[l] != lvlCoordinates[l]) {
383 if (prevLvlCoords[l] > lvlCoordinates[l])
384 isSorted = false;
385 break;
386 }
387 }
388 }
389 lvlCoordinates += lvlRank;
390 ++values;
391 };
392 readNextElement();
393 isSorted = true;
394 for (uint64_t n = 1; n < nse; ++n)
395 readNextElement();
396 return isSorted;
397}
398
399} // namespace sparse_tensor
400} // namespace mlir
401
402#endif // MLIR_EXECUTIONENGINE_SPARSETENSOR_FILE_H
A class for capturing the sparse tensor type map with a compact encoding.
Definition MapRef.h:32
void pushforward(const T *in, T *out) const
Definition MapRef.h:42
uint64_t getLvlRank() const
Definition MapRef.h:82
uint64_t getDimRank() const
Definition MapRef.h:81
A memory-resident sparse tensor in coordinate-scheme representation (a collection of Elements).
Definition COO.h:66
void assertMatchesShape(uint64_t rank, const uint64_t *shape) const
Asserts the shape subsumes the actual dimension sizes.
Definition File.cpp:65
bool isPattern() const
Gets the MME "pattern" property setting.
Definition File.h:161
void closeFile()
Closes the file.
Definition File.cpp:34
SparseTensorStorage< P, C, V > * readSparseTensor(uint64_t lvlRank, const uint64_t *lvlSizes, const LevelType *lvlTypes, const uint64_t *dim2lvl, const uint64_t *lvl2dim)
Allocates a new sparse-tensor storage object with the given encoding, initializes it by reading all t...
Definition File.h:208
uint64_t getDimSize(uint64_t d) const
Safely gets the size of the given dimension.
Definition File.h:194
SparseTensorReader(const SparseTensorReader &)=delete
void readHeader()
Reads and parses the file's header.
Definition File.cpp:50
bool canReadAs(PrimaryType valTy) const
Checks if the file's ValueKind can be converted into the given tensor PrimaryType.
Definition File.cpp:73
uint64_t getNSE() const
Gets the number of stored elements.
Definition File.h:182
bool isValid() const
Checks if a header has been successfully read.
Definition File.h:153
ValueKind getValueKind() const
Returns the stored value kind.
Definition File.h:150
const uint64_t * getDimSizes() const
Gets the dimension-sizes array.
Definition File.h:190
bool readToBuffers(uint64_t lvlRank, const uint64_t *dim2lvl, const uint64_t *lvl2dim, C *lvlCoordinates, V *values)
Reads the COO tensor from the file, stores the coordinates and values to the given buffers,...
Definition File.h:350
bool isSymmetric() const
Gets the MME "symmetric" property setting.
Definition File.h:168
SparseTensorReader & operator=(const SparseTensorReader &)=delete
uint64_t getRank() const
Gets the dimension-rank of the tensor.
Definition File.h:175
static SparseTensorReader * create(const char *filename, uint64_t dimRank, const uint64_t *dimShape, PrimaryType valTp)
Factory method to allocate a new reader, open the file, read the header, and validate that the actual...
Definition File.h:119
SparseTensorReader(const char *filename)
Definition File.h:108
void openFile()
Opens the file for reading.
Definition File.cpp:21
A memory-resident sparse tensor using a storage scheme based on per-level sparse/dense annotations.
Definition Storage.h:195
static SparseTensorStorage< P, C, V > * newFromCOO(uint64_t dimRank, const uint64_t *dimSizes, uint64_t lvlRank, const uint64_t *lvlSizes, const LevelType *lvlTypes, const uint64_t *dim2lvl, const uint64_t *lvl2dim, SparseTensorCOO< V > *lvlCOO)
Allocates a new sparse tensor and initializes it from the given COO.
Definition Storage.h:590
This file contains the declaration of the mlir::NonFloatComplex type and mlir::Complex type alias.
std::enable_if_t<!is_complex< V >::value, V > readValue(char **linePtr)
Returns an element-value of non-complex type.
Definition File.h:50
PrimaryType
Encoding of the elemental type, for "overloading" @newSparseTensor.
Definition Enums.h:82
Include the generated interface declarations.
This enum defines all the sparse representations supportable by the SparseTensor dialect.
Definition Enums.h:238