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
41fiff_int_t fiff_type_base(fiff_int_t type)
42{
43 return type & FIFFTS_BASE_MASK;
44}
45
46fiff_int_t fiff_type_fundamental(fiff_int_t type)
47{
48 return type & FIFFTS_FS_MASK;
49}
50
51fiff_int_t fiff_type_matrix_coding(fiff_int_t type)
52{
53 return type & FIFFTS_MC_MASK;
54}
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 }
101 else if (fiff_type_matrix_coding(tag->type) == FIFFTS_MC_CCS ||
103 if (tsize < (ndim+2)*sizeof(fiff_int_t)) {
104 qCritical("fiff_get_matrix_sparse_dims: too small matrix data!");
105 return {};
106 }
107 std::vector<int> res(ndim + 2);
108 res[0] = ndim;
109 dims = ((fiff_int_t *)((fiff_byte_t *)(tag->data())+tag->size())) - ndim - 1;
110 for (int k = 0; k < ndim; k++)
111 res[k+1] = dims[k];
112 res[ndim+1] = dims[-1];
113 return res;
114 }
115 else {
116 qCritical("fiff_get_matrix_dims: unknown matrix coding.");
117 return {};
118 }
119}
120
121//=============================================================================================================
122// DEFINE MEMBER METHODS
123//=============================================================================================================
124
129
130//=============================================================================================================
131
132FiffSparseMatrix::FiffSparseMatrix(Eigen::SparseMatrix<float>&& mat,
133 FIFFLIB::fiff_int_t coding)
134: coding(coding)
135, m_eigen(std::move(mat))
136{
137}
138
139//=============================================================================================================
140
142{
143 return fiff_get_matrix_dims(tag);
144}
145
146//=============================================================================================================
147
149{
150 int m,n,nz;
151 int cod,correct_size;
152
153 if ( fiff_type_fundamental(tag->type) != FIFFT_MATRIX ||
154 fiff_type_base(tag->type) != FIFFT_FLOAT ||
155 (fiff_type_matrix_coding(tag->type) != FIFFTS_MC_CCS &&
156 fiff_type_matrix_coding(tag->type) != FIFFTS_MC_RCS) ) {
157 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong data type!");
158 return nullptr;
159 }
160
161 auto dims = fiff_get_matrix_sparse_dims(tag);
162 if (dims.empty())
163 return nullptr;
164
165 if (dims[0] != 2) {
166 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong # of dimensions!");
167 return nullptr;
168 }
169
170 m = dims[1];
171 n = dims[2];
172 nz = dims[3];
173
174 cod = fiff_type_matrix_coding(tag->type);
175 if (cod == FIFFTS_MC_CCS)
176 correct_size = nz*(sizeof(fiff_float_t) + sizeof(fiff_int_t)) +
177 (n+1+dims[0]+2)*(sizeof(fiff_int_t));
178 else if (cod == FIFFTS_MC_RCS)
179 correct_size = nz*(sizeof(fiff_float_t) + sizeof(fiff_int_t)) +
180 (m+1+dims[0]+2)*(sizeof(fiff_int_t));
181 else {
182 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] Incomprehensible sparse matrix coding");
183 return nullptr;
184 }
185 if (tag->size() != correct_size) {
186 qWarning("[FiffSparseMatrix::fiff_get_float_sparse_matrix] wrong data size!");
187 return nullptr;
188 }
189 /*
190 * Parse tag data into triplets and build Eigen sparse matrix directly
191 */
192 const float* src_data = reinterpret_cast<const float*>(tag->data());
193 const int* src_inds = reinterpret_cast<const int*>(src_data + nz);
194 const int* src_ptrs = src_inds + nz;
195
196 using T = Eigen::Triplet<float>;
197 std::vector<T> triplets;
198 triplets.reserve(nz);
199
200 if (cod == FIFFTS_MC_RCS) {
201 for (int row = 0; row < m; ++row) {
202 for (int j = src_ptrs[row]; j < src_ptrs[row + 1]; ++j) {
203 triplets.push_back(T(row, src_inds[j], src_data[j]));
204 }
205 }
206 } else { // CCS
207 for (int col = 0; col < n; ++col) {
208 for (int j = src_ptrs[col]; j < src_ptrs[col + 1]; ++j) {
209 triplets.push_back(T(src_inds[j], col, src_data[j]));
210 }
211 }
212 }
213
214 Eigen::SparseMatrix<float> eigenMat(m, n);
215 eigenMat.setFromTriplets(triplets.begin(), triplets.end());
216 eigenMat.makeCompressed();
217
218 auto res = std::make_unique<FiffSparseMatrix>(std::move(eigenMat), cod);
219 return res;
220}
221
222//=============================================================================================================
223
224FiffSparseMatrix::UPtr FiffSparseMatrix::create_sparse_rcs(int nrow, int ncol, int *nnz, int **colindex, float **vals)
225{
226 int j,k,totalNz;
227
228 for (j = 0, totalNz = 0; j < nrow; j++)
229 totalNz = totalNz + nnz[j];
230
231 if (totalNz <= 0) {
232 qWarning("[FiffSparseMatrix::create_sparse_rcs] No nonzero elements specified.");
233 return nullptr;
234 }
235
236 using T = Eigen::Triplet<float>;
237 std::vector<T> triplets;
238 triplets.reserve(totalNz);
239
240 for (j = 0; j < nrow; j++) {
241 for (k = 0; k < nnz[j]; k++) {
242 int col = colindex[j][k];
243 if (col < 0 || col >= ncol) {
244 qWarning("[FiffSparseMatrix::create_sparse_rcs] Column index out of range");
245 return nullptr;
246 }
247 float val = vals ? vals[j][k] : 0.0f;
248 triplets.push_back(T(j, col, val));
249 }
250 }
251
252 Eigen::SparseMatrix<float> eigenMat(nrow, ncol);
253 eigenMat.setFromTriplets(triplets.begin(), triplets.end());
254 eigenMat.makeCompressed();
255
256 return std::make_unique<FiffSparseMatrix>(std::move(eigenMat), FIFFTS_MC_RCS);
257}
258
259//=============================================================================================================
260
262/*
263 * Fill in upper triangle with the lower triangle values
264 */
265{
266 int nRows = rows();
267 int nCols = cols();
268
269 if (nRows != nCols) {
270 qWarning("[FiffSparseMatrix::mne_add_upper_triangle_rcs] input must be square");
271 return nullptr;
272 }
273
274 // Build full (lower + upper) by adding transpose
275 Eigen::SparseMatrix<float> full = m_eigen + Eigen::SparseMatrix<float>(m_eigen.transpose());
276
277 // The diagonal was counted twice — fix by subtracting the diagonal once
278 for (int k = 0; k < full.outerSize(); ++k) {
279 for (Eigen::SparseMatrix<float>::InnerIterator it(full, k); it; ++it) {
280 if (it.row() == it.col()) {
281 // Original diagonal value is in m_eigen; full has 2x, so set back to 1x
282 it.valueRef() = 0.5f * it.value();
283 }
284 }
285 }
286
287 full.makeCompressed();
288 return std::make_unique<FiffSparseMatrix>(std::move(full), FIFFTS_MC_RCS);
289}
290
291//=============================================================================================================
292
293FiffSparseMatrix FiffSparseMatrix::fromEigenSparse(const Eigen::SparseMatrix<double>& mat)
294{
295 FiffSparseMatrix result;
296 if (mat.nonZeros() == 0)
297 return result;
298
299 result.coding = FIFFTS_MC_RCS;
300 result.m_eigen = mat.cast<float>();
301 result.m_eigen.makeCompressed();
302 return result;
303}
304
305//=============================================================================================================
306
307FiffSparseMatrix FiffSparseMatrix::fromEigenSparse(const Eigen::SparseMatrix<float>& mat)
308{
309 FiffSparseMatrix result;
310 if (mat.nonZeros() == 0)
311 return result;
312
313 result.coding = FIFFTS_MC_RCS;
314 result.m_eigen = mat;
315 result.m_eigen.makeCompressed();
316 return result;
317}
318
319//=============================================================================================================
320
322{
323 int nRows = rows();
324 int nCols = cols();
325
326 if (nRows != nCols) {
327 qWarning("[FiffSparseMatrix::pickLowerTriangleRcs] input must be square");
328 return nullptr;
329 }
330
331 using T = Eigen::Triplet<float>;
332 std::vector<T> triplets;
333 triplets.reserve(m_eigen.nonZeros());
334
335 for (int k = 0; k < m_eigen.outerSize(); ++k) {
336 for (Eigen::SparseMatrix<float>::InnerIterator it(m_eigen, k); it; ++it) {
337 if (it.row() >= it.col()) { // lower triangle including diagonal
338 triplets.push_back(T(it.row(), it.col(), it.value()));
339 }
340 }
341 }
342
343 Eigen::SparseMatrix<float> lower(nRows, nCols);
344 lower.setFromTriplets(triplets.begin(), triplets.end());
345 lower.makeCompressed();
346
347 return std::make_unique<FiffSparseMatrix>(std::move(lower), FIFFTS_MC_RCS);
348}
FIFF sparse matrix: column / row-compressed sparse storage backed by Eigen::SparseMatrix.
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 tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
#define FIFFTS_FS_MATRIX
Definition fiff_file.h:259
#define FIFFTS_MC_RCS
Definition fiff_file.h:263
#define FIFFTS_MC_MASK
Definition fiff_file.h:256
#define FIFFT_MATRIX
Definition fiff_file.h:265
#define FIFFC_MATRIX_MAX_DIM
Definition fiff_file.h:252
#define FIFFTS_FS_MASK
Definition fiff_file.h:254
#define FIFFTS_MC_CCS
Definition fiff_file.h:262
#define FIFFT_FLOAT
Definition fiff_file.h:225
#define FIFFTS_MC_DENSE
Definition fiff_file.h:261
#define FIFFTS_BASE_MASK
Definition fiff_file.h:255
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.
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:164