v2.0.0
Loading...
Searching...
No Matches
fiff_sparse_matrix.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "fiff_sparse_matrix.h"
42#include <fiff/fiff_file.h>
43#include <fiff/fiff_types.h>
44
45#include <vector>
46
47//=============================================================================================================
48// USED NAMESPACES
49//=============================================================================================================
50
51using namespace Eigen;
52using namespace FIFFLIB;
53
54//============================= fiff_type_spec.h =============================
55
56/*
57 * These return information about a fiff type.
58 */
59
61{
62 return type & FIFFTS_BASE_MASK;
63}
64
69
74
75//============================= fiff_matrix.c =============================
76
77std::vector<int> fiff_get_matrix_dims(FiffTag::SPtr& tag)
78/*
79 * Interpret dimensions from matrix data (dense and sparse)
80 */
81{
82 int ndim;
83 int *dims;
84 unsigned int tsize = tag->size();
85 /*
86 * Initial checks
87 */
88 if (tag->data() == nullptr) {
89 qCritical("fiff_get_matrix_dims: no data available!");
90 return {};
91 }
92 if (fiff_type_fundamental(tag->type) != FIFFTS_FS_MATRIX) {
93 qCritical("fiff_get_matrix_dims: tag does not contain a matrix!");
94 return {};
95 }
96 if (tsize < sizeof(fiff_int_t)) {
97 qCritical("fiff_get_matrix_dims: too small matrix data!");
98 return {};
99 }
100 /*
101 * Get the number of dimensions and check
102 */
103 ndim = *((fiff_int_t *)((fiff_byte_t *)(tag->data())+tag->size()-sizeof(fiff_int_t)));
104 if (ndim <= 0 || ndim > FIFFC_MATRIX_MAX_DIM) {
105 qCritical("fiff_get_matrix_dims: unreasonable # of dimensions!");
106 return {};
107 }
108 if (fiff_type_matrix_coding(tag->type) == FIFFTS_MC_DENSE) {
109 if (tsize < (ndim+1)*sizeof(fiff_int_t)) {
110 qCritical("fiff_get_matrix_dims: too small matrix data!");
111 return {};
112 }
113 std::vector<int> res(ndim + 1);
114 res[0] = ndim;
115 dims = ((fiff_int_t *)((fiff_byte_t *)(tag->data())+tag->size())) - ndim - 1;
116 for (int k = 0; k < ndim; k++)
117 res[k+1] = dims[k];
118 return res;
119 }
120 else if (fiff_type_matrix_coding(tag->type) == FIFFTS_MC_CCS ||
122 if (tsize < (ndim+2)*sizeof(fiff_int_t)) {
123 qCritical("fiff_get_matrix_sparse_dims: too small matrix data!");
124 return {};
125 }
126 std::vector<int> res(ndim + 2);
127 res[0] = ndim;
128 dims = ((fiff_int_t *)((fiff_byte_t *)(tag->data())+tag->size())) - ndim - 1;
129 for (int k = 0; k < ndim; k++)
130 res[k+1] = dims[k];
131 res[ndim+1] = dims[-1];
132 return res;
133 }
134 else {
135 qCritical("fiff_get_matrix_dims: unknown matrix coding.");
136 return {};
137 }
138}
139
140//=============================================================================================================
141// DEFINE MEMBER METHODS
142//=============================================================================================================
143
145: coding(0)
146, m(0)
147, n(0)
148, nz(0)
149{
150}
151
152//=============================================================================================================
153
158
159//=============================================================================================================
160
162{
163 int m,n,nz;
164 int coding,correct_size;
165
166 if ( fiff_type_fundamental(tag->type) != FIFFT_MATRIX ||
167 fiff_type_base(tag->type) != FIFFT_FLOAT ||
168 (fiff_type_matrix_coding(tag->type) != FIFFTS_MC_CCS &&
169 fiff_type_matrix_coding(tag->type) != FIFFTS_MC_RCS) ) {
170 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong data type!");
171 return nullptr;
172 }
173
174 auto dims = fiff_get_matrix_sparse_dims(tag);
175 if (dims.empty())
176 return nullptr;
177
178 if (dims[0] != 2) {
179 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong # of dimensions!");
180 return nullptr;
181 }
182
183 m = dims[1];
184 n = dims[2];
185 nz = dims[3];
186
187 coding = fiff_type_matrix_coding(tag->type);
188 if (coding == FIFFTS_MC_CCS)
189 correct_size = nz*(sizeof(fiff_float_t) + sizeof(fiff_int_t)) +
190 (n+1+dims[0]+2)*(sizeof(fiff_int_t));
191 else if (coding == FIFFTS_MC_RCS)
192 correct_size = nz*(sizeof(fiff_float_t) + sizeof(fiff_int_t)) +
193 (m+1+dims[0]+2)*(sizeof(fiff_int_t));
194 else {
195 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] Incomprehensible sparse matrix coding");
196 return nullptr;
197 }
198 if (tag->size() != correct_size) {
199 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong data size!");
200 return nullptr;
201 }
202 /*
203 * Parse tag data into Eigen vectors via Map (zero-copy wrap + assignment copy)
204 */
205 auto res = std::make_unique<FiffSparseMatrix>();
206 res->m = m;
207 res->n = n;
208 res->nz = nz;
209 res->coding = coding;
210
211 const float* src_data = reinterpret_cast<const float*>(tag->data());
212 const int* src_inds = reinterpret_cast<const int*>(src_data + nz);
213 const int* src_ptrs = src_inds + nz;
214 int ptrs_count = (coding == FIFFTS_MC_CCS) ? (n + 1) : (m + 1);
215
216 res->data = Eigen::Map<const Eigen::VectorXf>(src_data, nz);
217 res->inds = Eigen::Map<const Eigen::VectorXi>(src_inds, nz);
218 res->ptrs = Eigen::Map<const Eigen::VectorXi>(src_ptrs, ptrs_count);
219
220 return res;
221}
222
223//=============================================================================================================
224
225FiffSparseMatrix::UPtr FiffSparseMatrix::create_sparse_rcs(int nrow, int ncol, int *nnz, int **colindex, float **vals)
226{
227 int j,k,nz,ptr,ind;
228
229 for (j = 0, nz = 0; j < nrow; j++)
230 nz = nz + nnz[j];
231
232 if (nz <= 0) {
233 qWarning("[FiffSparseMatrix::create_sparse_rcs] No nonzero elements specified.");
234 return nullptr;
235 }
236
237 auto sparse = std::make_unique<FiffSparseMatrix>();
238 sparse->coding = FIFFTS_MC_RCS;
239 sparse->m = nrow;
240 sparse->n = ncol;
241 sparse->nz = nz;
242 sparse->data = Eigen::VectorXf::Zero(nz);
243 sparse->inds = Eigen::VectorXi::Zero(nz);
244 sparse->ptrs = Eigen::VectorXi::Zero(nrow + 1);
245
246 for (j = 0, nz = 0; j < nrow; j++) {
247 ptr = -1;
248 for (k = 0; k < nnz[j]; k++) {
249 if (ptr < 0)
250 ptr = nz;
251 ind = sparse->inds[nz] = colindex[j][k];
252 if (ind < 0 || ind >= ncol) {
253 qWarning("[FiffSparseMatrix::create_sparse_rcs] Column index out of range");
254 return nullptr;
255 }
256 if (vals)
257 sparse->data[nz] = vals[j][k];
258 else
259 sparse->data[nz] = 0.0;
260 nz++;
261 }
262 sparse->ptrs[j] = ptr;
263 }
264 sparse->ptrs[nrow] = nz;
265 for (j = nrow-1; j >= 0; j--) /* Take care of the empty rows */
266 if (sparse->ptrs[j] < 0)
267 sparse->ptrs[j] = sparse->ptrs[j+1];
268 return sparse;
269}
270
271//=============================================================================================================
272
274/*
275 * Fill in upper triangle with the lower triangle values
276 */
277{
278 if (this->coding != FIFFTS_MC_RCS) {
279 qWarning("[FiffSparseMatrix::mne_add_upper_triangle_rcs] input must be in RCS format");
280 return nullptr;
281 }
282 if (this->m != this->n) {
283 qWarning("[FiffSparseMatrix::mne_add_upper_triangle_rcs] input must be square");
284 return nullptr;
285 }
286
287 // Build per-row data from existing lower triangle
288 std::vector<int> nnz_vec(this->m);
289 std::vector<std::vector<int>> colindex(this->m);
290 std::vector<std::vector<float>> vals(this->m);
291
292 for (int i = 0; i < this->m; i++) {
293 nnz_vec[i] = this->ptrs[i+1] - this->ptrs[i];
294 if (nnz_vec[i] > 0) {
295 colindex[i].resize(nnz_vec[i]);
296 vals[i].resize(nnz_vec[i]);
297 for (int j = this->ptrs[i], k = 0; j < this->ptrs[i+1]; j++, k++) {
298 vals[i][k] = this->data[j];
299 colindex[i][k] = this->inds[j];
300 }
301 }
302 }
303
304 // Count additional upper-triangle entries per row
305 std::vector<int> nadd(this->m, 0);
306 for (int i = 0; i < this->m; i++)
307 for (int j = this->ptrs[i]; j < this->ptrs[i+1]; j++)
308 nadd[this->inds[j]]++;
309
310 // Expand per-row storage and add upper-triangle entries
311 for (int i = 0; i < this->m; i++) {
312 colindex[i].resize(nnz_vec[i] + nadd[i]);
313 vals[i].resize(nnz_vec[i] + nadd[i]);
314 }
315 for (int i = 0; i < this->m; i++)
316 for (int j = this->ptrs[i]; j < this->ptrs[i+1]; j++) {
317 int row = this->inds[j];
318 colindex[row][nnz_vec[row]] = i;
319 vals[row][nnz_vec[row]] = this->data[j];
320 nnz_vec[row]++;
321 }
322
323 // Build raw pointer arrays for create_sparse_rcs
324 std::vector<int*> ci_ptrs(this->m);
325 std::vector<float*> val_ptrs(this->m);
326 for (int i = 0; i < this->m; i++) {
327 ci_ptrs[i] = colindex[i].data();
328 val_ptrs[i] = vals[i].data();
329 }
330
331 return create_sparse_rcs(this->m, this->n, nnz_vec.data(), ci_ptrs.data(), val_ptrs.data());
332}
333
334//=============================================================================================================
335
336Eigen::SparseMatrix<double> FiffSparseMatrix::toEigenSparse() const
337{
338 if (is_empty())
339 return Eigen::SparseMatrix<double>();
340
341 typedef Eigen::Triplet<double> T;
342 std::vector<T> tripletList;
343 tripletList.reserve(nz);
344
345 if (coding == FIFFTS_MC_RCS) {
346 for (int row = 0; row < m; ++row) {
347 for (int j = ptrs[row]; j < ptrs[row + 1]; ++j) {
348 tripletList.push_back(T(row, inds[j], static_cast<double>(data[j])));
349 }
350 }
351 } else if (coding == FIFFTS_MC_CCS) {
352 for (int col = 0; col < n; ++col) {
353 for (int j = ptrs[col]; j < ptrs[col + 1]; ++j) {
354 tripletList.push_back(T(inds[j], col, static_cast<double>(data[j])));
355 }
356 }
357 } else {
358 qWarning("[FiffSparseMatrix::toEigenSparse] Unknown coding type: %d", coding);
359 return Eigen::SparseMatrix<double>();
360 }
361
362 Eigen::SparseMatrix<double> result(m, n);
363 result.setFromTriplets(tripletList.begin(), tripletList.end());
364 return result;
365}
366
367//=============================================================================================================
368
369FiffSparseMatrix FiffSparseMatrix::fromEigenSparse(const Eigen::SparseMatrix<double>& mat)
370{
371 FiffSparseMatrix result;
372 if (mat.nonZeros() == 0) {
373 return result;
374 }
375
376 // Store in RCS (row-compressed) format
377 // Eigen's default SparseMatrix is column-major, so we iterate by row
378 result.coding = FIFFTS_MC_RCS;
379 result.m = static_cast<fiff_int_t>(mat.rows());
380 result.n = static_cast<fiff_int_t>(mat.cols());
381 result.nz = static_cast<fiff_int_t>(mat.nonZeros());
382 result.data = Eigen::VectorXf::Zero(result.nz);
383 result.inds = Eigen::VectorXi::Zero(result.nz);
384 result.ptrs = Eigen::VectorXi::Zero(result.m + 1);
385
386 // Collect triplets sorted by row
387 typedef Eigen::Triplet<double> T;
388 std::vector<T> triplets;
389 triplets.reserve(mat.nonZeros());
390 for (int k = 0; k < mat.outerSize(); ++k) {
391 for (Eigen::SparseMatrix<double>::InnerIterator it(mat, k); it; ++it) {
392 triplets.push_back(T(it.row(), it.col(), it.value()));
393 }
394 }
395 std::sort(triplets.begin(), triplets.end(),
396 [](const T& a, const T& b) {
397 return a.row() < b.row() || (a.row() == b.row() && a.col() < b.col());
398 });
399
400 int idx = 0;
401 int row = 0;
402 result.ptrs[0] = 0;
403 for (const auto& t : triplets) {
404 while (row < t.row()) {
405 result.ptrs[++row] = idx;
406 }
407 result.data[idx] = static_cast<float>(t.value());
408 result.inds[idx] = static_cast<int>(t.col());
409 ++idx;
410 }
411 while (row < result.m) {
412 result.ptrs[++row] = idx;
413 }
414
415 return result;
416}
fiff_int_t fiff_type_base(fiff_int_t type)
std::vector< int > fiff_get_matrix_dims(FiffTag::SPtr &tag)
fiff_int_t fiff_type_matrix_coding(fiff_int_t type)
fiff_int_t fiff_type_fundamental(fiff_int_t type)
Header file describing the numerical values used in fif files.
#define FIFFTS_FS_MATRIX
Definition fiff_file.h:266
#define FIFFTS_MC_RCS
Definition fiff_file.h:270
#define FIFFTS_MC_MASK
Definition fiff_file.h:263
#define FIFFT_MATRIX
Definition fiff_file.h:272
#define FIFFC_MATRIX_MAX_DIM
Definition fiff_file.h:259
#define FIFFTS_FS_MASK
Definition fiff_file.h:261
#define FIFFTS_MC_CCS
Definition fiff_file.h:269
#define FIFFT_FLOAT
Definition fiff_file.h:232
#define FIFFTS_MC_DENSE
Definition fiff_file.h:268
#define FIFFTS_BASE_MASK
Definition fiff_file.h:262
Old fiff_type declarations - replace them.
FiffSparseMatrix class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
qint32 fiff_int_t
Definition fiff_types.h:89
float fiff_float_t
Definition fiff_types.h:93
unsigned char fiff_byte_t
Definition fiff_types.h:85
std::unique_ptr< FiffSparseMatrix > UPtr
static FiffSparseMatrix fromEigenSparse(const Eigen::SparseMatrix< double > &mat)
static FiffSparseMatrix::UPtr fiff_get_float_sparse_matrix(FIFFLIB::FiffTag::SPtr &tag)
static FiffSparseMatrix::UPtr create_sparse_rcs(int nrow, int ncol, int *nnz, int **colindex, float **vals)
static std::vector< int > fiff_get_matrix_sparse_dims(FIFFLIB::FiffTag::SPtr &tag)
FiffSparseMatrix::UPtr mne_add_upper_triangle_rcs()
Eigen::SparseMatrix< double > toEigenSparse() const
QSharedPointer< FiffTag > SPtr
Definition fiff_tag.h:155