v2.0.0
Loading...
Searching...
No Matches
fiff_sparse_matrix.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
21#include "fiff_sparse_matrix.h"
22#include <fiff/fiff_file.h>
23#include <fiff/fiff_types.h>
24
25#include <vector>
26#include <QDebug>
27
28//=============================================================================================================
29// USED NAMESPACES
30//=============================================================================================================
31
32using namespace Eigen;
33using namespace FIFFLIB;
34
35//============================= fiff_type_spec.h =============================
36
37/*
38 * These return information about a fiff type.
39 */
40
42{
43 return type & FIFFTS_BASE_MASK;
44}
45
50
55
56//============================= fiff_matrix.c =============================
57
58std::vector<int> fiff_get_matrix_dims(const FiffTag::UPtr& tag)
59/*
60 * Interpret dimensions from matrix data (dense and sparse)
61 */
62{
63 int ndim;
64 int* dims;
65 unsigned int tsize = tag->size();
66 /*
67 * Initial checks
68 */
69 if (tag->data() == nullptr) {
70 qCritical("fiff_get_matrix_dims: no data available!");
71 return {};
72 }
73 if (fiff_type_fundamental(tag->type) != FIFFTS_FS_MATRIX) {
74 qCritical("fiff_get_matrix_dims: tag does not contain a matrix!");
75 return {};
76 }
77 if (tsize < sizeof(fiff_int_t)) {
78 qCritical("fiff_get_matrix_dims: too small matrix data!");
79 return {};
80 }
81 /*
82 * Get the number of dimensions and check
83 */
84 ndim = *((fiff_int_t*)((fiff_byte_t*)(tag->data()) + tag->size() - sizeof(fiff_int_t)));
85 if (ndim <= 0 || ndim > FIFFC_MATRIX_MAX_DIM) {
86 qCritical("fiff_get_matrix_dims: unreasonable # of dimensions!");
87 return {};
88 }
89 if (fiff_type_matrix_coding(tag->type) == FIFFTS_MC_DENSE) {
90 if (tsize < (ndim + 1) * sizeof(fiff_int_t)) {
91 qCritical("fiff_get_matrix_dims: too small matrix data!");
92 return {};
93 }
94 std::vector<int> res(ndim + 1);
95 res[0] = ndim;
96 dims = ((fiff_int_t*)((fiff_byte_t*)(tag->data()) + tag->size())) - ndim - 1;
97 for (int k = 0; k < ndim; k++)
98 res[k + 1] = dims[k];
99 return res;
100 } else if (fiff_type_matrix_coding(tag->type) == FIFFTS_MC_CCS ||
102 if (tsize < (ndim + 2) * sizeof(fiff_int_t)) {
103 qCritical("fiff_get_matrix_sparse_dims: too small matrix data!");
104 return {};
105 }
106 std::vector<int> res(ndim + 2);
107 res[0] = ndim;
108 dims = ((fiff_int_t*)((fiff_byte_t*)(tag->data()) + tag->size())) - ndim - 1;
109 for (int k = 0; k < ndim; k++)
110 res[k + 1] = dims[k];
111 res[ndim + 1] = dims[-1];
112 return res;
113 } else {
114 qCritical("fiff_get_matrix_dims: unknown matrix coding.");
115 return {};
116 }
117}
118
119//=============================================================================================================
120// DEFINE MEMBER METHODS
121//=============================================================================================================
122
127
128//=============================================================================================================
129
130FiffSparseMatrix::FiffSparseMatrix(Eigen::SparseMatrix<float>&& mat,
132: coding(coding)
133, m_eigen(std::move(mat))
134{
135}
136
137//=============================================================================================================
138
140{
141 return fiff_get_matrix_dims(tag);
142}
143
144//=============================================================================================================
145
147{
148 int m, n, nz;
149 int cod, correct_size;
150
151 if (fiff_type_fundamental(tag->type) != FIFFT_MATRIX ||
152 fiff_type_base(tag->type) != FIFFT_FLOAT ||
153 (fiff_type_matrix_coding(tag->type) != FIFFTS_MC_CCS &&
154 fiff_type_matrix_coding(tag->type) != FIFFTS_MC_RCS)) {
155 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong data type!");
156 return nullptr;
157 }
158
159 auto dims = fiff_get_matrix_sparse_dims(tag);
160 if (dims.empty())
161 return nullptr;
162
163 if (dims[0] != 2) {
164 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong # of dimensions!");
165 return nullptr;
166 }
167
168 m = dims[1];
169 n = dims[2];
170 nz = dims[3];
171
172 cod = fiff_type_matrix_coding(tag->type);
173 if (cod == FIFFTS_MC_CCS)
174 correct_size = nz * (sizeof(fiff_float_t) + sizeof(fiff_int_t)) +
175 (n + 1 + dims[0] + 2) * (sizeof(fiff_int_t));
176 else if (cod == FIFFTS_MC_RCS)
177 correct_size = nz * (sizeof(fiff_float_t) + sizeof(fiff_int_t)) +
178 (m + 1 + dims[0] + 2) * (sizeof(fiff_int_t));
179 else {
180 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] Incomprehensible sparse matrix coding");
181 return nullptr;
182 }
183 if (tag->size() != correct_size) {
184 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong data size!");
185 return nullptr;
186 }
187 /*
188 * Parse tag data into triplets and build Eigen sparse matrix directly
189 */
190 const float* src_data = reinterpret_cast<const float*>(tag->data());
191 const int* src_inds = reinterpret_cast<const int*>(src_data + nz);
192 const int* src_ptrs = src_inds + nz;
193
194 // A corrupt file must not index outside the matrix: the pointers run from 0 to nz without going
195 // back, and every index lies within the other dimension
196 const int nOuter = (cod == FIFFTS_MC_RCS) ? m : n;
197 const int nInner = (cod == FIFFTS_MC_RCS) ? n : m;
198 bool valid = m >= 0 && n >= 0 && nz >= 0 && src_ptrs[0] == 0 && src_ptrs[nOuter] == nz;
199 for (int k = 0; valid && k < nOuter; ++k)
200 valid = src_ptrs[k] <= src_ptrs[k + 1];
201 for (int j = 0; valid && j < nz; ++j)
202 valid = src_inds[j] >= 0 && src_inds[j] < nInner;
203 if (!valid) {
204 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] index or pointer outside the matrix!");
205 return nullptr;
206 }
207
208 using T = Eigen::Triplet<float>;
209 std::vector<T> triplets;
210 triplets.reserve(nz);
211
212 if (cod == FIFFTS_MC_RCS) {
213 for (int row = 0; row < m; ++row) {
214 for (int j = src_ptrs[row]; j < src_ptrs[row + 1]; ++j) {
215 triplets.push_back(T(row, src_inds[j], src_data[j]));
216 }
217 }
218 } else { // CCS
219 for (int col = 0; col < n; ++col) {
220 for (int j = src_ptrs[col]; j < src_ptrs[col + 1]; ++j) {
221 triplets.push_back(T(src_inds[j], col, src_data[j]));
222 }
223 }
224 }
225
226 Eigen::SparseMatrix<float> eigenMat(m, n);
227 eigenMat.setFromTriplets(triplets.begin(), triplets.end());
228 eigenMat.makeCompressed();
229
230 auto res = std::make_unique<FiffSparseMatrix>(std::move(eigenMat), cod);
231 return res;
232}
233
234//=============================================================================================================
235
236FiffSparseMatrix::UPtr FiffSparseMatrix::create_sparse_rcs(int nrow, int ncol, int* nnz, int** colindex, float** vals)
237{
238 int j, k, totalNz;
239
240 for (j = 0, totalNz = 0; j < nrow; j++)
241 totalNz = totalNz + nnz[j];
242
243 if (totalNz <= 0) {
244 qWarning("[FiffSparseMatrix::create_sparse_rcs] No nonzero elements specified.");
245 return nullptr;
246 }
247
248 using T = Eigen::Triplet<float>;
249 std::vector<T> triplets;
250 triplets.reserve(totalNz);
251
252 for (j = 0; j < nrow; j++) {
253 for (k = 0; k < nnz[j]; k++) {
254 int col = colindex[j][k];
255 if (col < 0 || col >= ncol) {
256 qWarning("[FiffSparseMatrix::create_sparse_rcs] Column index out of range");
257 return nullptr;
258 }
259 float val = vals ? vals[j][k] : 0.0f;
260 triplets.push_back(T(j, col, val));
261 }
262 }
263
264 Eigen::SparseMatrix<float> eigenMat(nrow, ncol);
265 eigenMat.setFromTriplets(triplets.begin(), triplets.end());
266 eigenMat.makeCompressed();
267
268 return std::make_unique<FiffSparseMatrix>(std::move(eigenMat), FIFFTS_MC_RCS);
269}
270
271//=============================================================================================================
272
274/*
275 * Fill in upper triangle with the lower triangle values
276 */
277{
278 int nRows = rows();
279 int nCols = cols();
280
281 if (nRows != nCols) {
282 qWarning("[FiffSparseMatrix::mne_add_upper_triangle_rcs] input must be square");
283 return nullptr;
284 }
285
286 // Build full (lower + upper) by adding transpose
287 Eigen::SparseMatrix<float> full = m_eigen + Eigen::SparseMatrix<float>(m_eigen.transpose());
288
289 // The diagonal was counted twice — fix by subtracting the diagonal once
290 for (int k = 0; k < full.outerSize(); ++k) {
291 for (Eigen::SparseMatrix<float>::InnerIterator it(full, k); it; ++it) {
292 if (it.row() == it.col()) {
293 // Original diagonal value is in m_eigen; full has 2x, so set back to 1x
294 it.valueRef() = 0.5f * it.value();
295 }
296 }
297 }
298
299 full.makeCompressed();
300 return std::make_unique<FiffSparseMatrix>(std::move(full), FIFFTS_MC_RCS);
301}
302
303//=============================================================================================================
304
305FiffSparseMatrix FiffSparseMatrix::fromEigenSparse(const Eigen::SparseMatrix<double>& mat)
306{
307 FiffSparseMatrix result;
308 if (mat.nonZeros() == 0)
309 return result;
310
311 result.coding = FIFFTS_MC_RCS;
312 result.m_eigen = mat.cast<float>();
313 result.m_eigen.makeCompressed();
314 return result;
315}
316
317//=============================================================================================================
318
319FiffSparseMatrix FiffSparseMatrix::fromEigenSparse(const Eigen::SparseMatrix<float>& mat)
320{
321 FiffSparseMatrix result;
322 if (mat.nonZeros() == 0)
323 return result;
324
325 result.coding = FIFFTS_MC_RCS;
326 result.m_eigen = mat;
327 result.m_eigen.makeCompressed();
328 return result;
329}
330
331//=============================================================================================================
332
334{
335 int nRows = rows();
336 int nCols = cols();
337
338 if (nRows != nCols) {
339 qWarning("[FiffSparseMatrix::pickLowerTriangleRcs] input must be square");
340 return nullptr;
341 }
342
343 using T = Eigen::Triplet<float>;
344 std::vector<T> triplets;
345 triplets.reserve(m_eigen.nonZeros());
346
347 for (int k = 0; k < m_eigen.outerSize(); ++k) {
348 for (Eigen::SparseMatrix<float>::InnerIterator it(m_eigen, k); it; ++it) {
349 if (it.row() >= it.col()) { // lower triangle including diagonal
350 triplets.push_back(T(it.row(), it.col(), it.value()));
351 }
352 }
353 }
354
355 Eigen::SparseMatrix<float> lower(nRows, nCols);
356 lower.setFromTriplets(triplets.begin(), triplets.end());
357 lower.makeCompressed();
358
359 return std::make_unique<FiffSparseMatrix>(std::move(lower), FIFFTS_MC_RCS);
360}
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
constexpr auto FIFFTS_MC_DENSE
Definition fiff_file.h:259
constexpr auto FIFFTS_MC_RCS
Definition fiff_file.h:261
constexpr auto FIFFTS_FS_MATRIX
Definition fiff_file.h:257
#define FIFFT_MATRIX
Definition fiff_file.h:263
constexpr auto FIFFTS_MC_MASK
Definition fiff_file.h:254
constexpr auto FIFFTS_MC_CCS
Definition fiff_file.h:260
constexpr auto FIFFC_MATRIX_MAX_DIM
Definition fiff_file.h:250
constexpr auto FIFFT_FLOAT
Definition fiff_file.h:223
constexpr auto FIFFTS_FS_MASK
Definition fiff_file.h:252
constexpr auto FIFFTS_BASE_MASK
Definition fiff_file.h:253
fiff_int_t fiff_type_base(fiff_int_t type)
std::vector< int > fiff_get_matrix_dims(const FiffTag::UPtr &tag)
fiff_int_t fiff_type_matrix_coding(fiff_int_t type)
fiff_int_t fiff_type_fundamental(fiff_int_t type)
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
Primitive scalar typedefs and forward-compatible aliases backing the FIFF type system.
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
float fiff_float_t
Definition fiff_types.h:90
unsigned char fiff_byte_t
Definition fiff_types.h:82
static FiffSparseMatrix fromEigenSparse(const Eigen::SparseMatrix< double > &mat)
static std::vector< int > fiff_get_matrix_sparse_dims(const FIFFLIB::FiffTag::UPtr &tag)
FiffSparseMatrix::UPtr pickLowerTriangleRcs() const
static FiffSparseMatrix::UPtr create_sparse_rcs(int nrow, int ncol, int *nnz, int **colindex, float **vals)
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(const FIFFLIB::FiffTag::UPtr &tag)
std::unique_ptr< FiffSparseMatrix > UPtr
FiffSparseMatrix::UPtr mne_add_upper_triangle_rcs()
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:165