v2.0.0
Loading...
Searching...
No Matches
sts_adjacency.cpp
Go to the documentation of this file.
1//=============================================================================================================
24
25//=============================================================================================================
26// INCLUDES
27//=============================================================================================================
28
29#include "sts_adjacency.h"
30
31#include <fiff/fiff_ch_info.h>
32
33#include <algorithm>
34#include <vector>
35#include <cmath>
36
37//=============================================================================================================
38// USED NAMESPACES
39//=============================================================================================================
40
41using namespace STSLIB;
42using namespace FIFFLIB;
43using namespace Eigen;
44
45//=============================================================================================================
46// DEFINE METHODS
47//=============================================================================================================
48
49SparseMatrix<int> StatsAdjacency::fromChannelPositions(const FiffInfo& info, const QStringList& picks)
50{
51 // Gather channel indices and 3D positions
52 std::vector<int> chIdx;
53 if (picks.isEmpty()) {
54 for (int i = 0; i < info.chs.size(); ++i) {
55 chIdx.push_back(i);
56 }
57 } else {
58 for (int i = 0; i < info.chs.size(); ++i) {
59 if (picks.contains(info.chs[i].ch_name)) {
60 chIdx.push_back(i);
61 }
62 }
63 }
64
65 const int nCh = static_cast<int>(chIdx.size());
66 if (nCh == 0) {
67 return SparseMatrix<int>(0, 0);
68 }
69
70 // Extract 3D positions
71 MatrixXd pos(nCh, 3);
72 for (int i = 0; i < nCh; ++i) {
73 const Vector3f& r0 = info.chs[chIdx[i]].chpos.r0;
74 pos(i, 0) = static_cast<double>(r0(0));
75 pos(i, 1) = static_cast<double>(r0(1));
76 pos(i, 2) = static_cast<double>(r0(2));
77 }
78
79 // Compute pairwise distances and find nearest-neighbor distance for each channel
80 std::vector<double> nnDist(nCh, std::numeric_limits<double>::max());
81 MatrixXd distMat(nCh, nCh);
82 for (int i = 0; i < nCh; ++i) {
83 distMat(i, i) = 0.0;
84 for (int j = i + 1; j < nCh; ++j) {
85 double d = (pos.row(i) - pos.row(j)).norm();
86 distMat(i, j) = d;
87 distMat(j, i) = d;
88 if (d < nnDist[i])
89 nnDist[i] = d;
90 if (d < nnDist[j])
91 nnDist[j] = d;
92 }
93 }
94
95 // Median nearest-neighbor distance
96 std::vector<double> sortedNN = nnDist;
97 std::sort(sortedNN.begin(), sortedNN.end());
98 double medianNN;
99 if (nCh % 2 == 0) {
100 medianNN = (sortedNN[nCh / 2 - 1] + sortedNN[nCh / 2]) / 2.0;
101 } else {
102 medianNN = sortedNN[nCh / 2];
103 }
104
105 double threshold = 3.0 * medianNN;
106
107 // Build adjacency
108 std::vector<Triplet<int>> triplets;
109 for (int i = 0; i < nCh; ++i) {
110 for (int j = i + 1; j < nCh; ++j) {
111 if (distMat(i, j) <= threshold) {
112 triplets.emplace_back(i, j, 1);
113 triplets.emplace_back(j, i, 1);
114 }
115 }
116 }
117
118 SparseMatrix<int> adj(nCh, nCh);
119 adj.setFromTriplets(triplets.begin(), triplets.end());
120 return adj;
121}
122
123//=============================================================================================================
124
125SparseMatrix<int> StatsAdjacency::fromSourceSpace(const MatrixX3i& tris, int nVertices)
126{
127 std::vector<Triplet<int>> triplets;
128
129 for (int t = 0; t < tris.rows(); ++t) {
130 int v0 = tris(t, 0);
131 int v1 = tris(t, 1);
132 int v2 = tris(t, 2);
133
134 // Mark all 3 pairs as adjacent (symmetric)
135 triplets.emplace_back(v0, v1, 1);
136 triplets.emplace_back(v1, v0, 1);
137 triplets.emplace_back(v0, v2, 1);
138 triplets.emplace_back(v2, v0, 1);
139 triplets.emplace_back(v1, v2, 1);
140 triplets.emplace_back(v2, v1, 1);
141 }
142
143 SparseMatrix<int> adj(nVertices, nVertices);
144 adj.setFromTriplets(triplets.begin(), triplets.end());
145 return adj;
146}
147
148//=============================================================================================================
149
151 const MatrixX3i& tris, int nVertices, int nTimes)
152{
153 // Build spatial adjacency first
154 SparseMatrix<int> spatialAdj = fromSourceSpace(tris, nVertices);
155
156 const int nTotal = nVertices * nTimes;
157 std::vector<Triplet<int>> triplets;
158
159 // Reserve approximate capacity: spatial edges * nTimes + temporal edges
160 triplets.reserve(static_cast<size_t>(spatialAdj.nonZeros()) * nTimes + static_cast<size_t>(nVertices) * (nTimes - 1) * 2);
161
162 // Spatial neighbors repeated for each time point
163 // Linear index: v * nTimes + t
164 for (int t = 0; t < nTimes; ++t) {
165 for (int k = 0; k < spatialAdj.outerSize(); ++k) {
166 for (SparseMatrix<int>::InnerIterator it(spatialAdj, k); it; ++it) {
167 int row = static_cast<int>(it.row()) * nTimes + t;
168 int col = static_cast<int>(it.col()) * nTimes + t;
169 triplets.emplace_back(row, col, 1);
170 }
171 }
172 }
173
174 // Temporal neighbors: vertex v at time t adjacent to v at t-1 and t+1
175 for (int v = 0; v < nVertices; ++v) {
176 for (int t = 0; t < nTimes - 1; ++t) {
177 int idx0 = v * nTimes + t;
178 int idx1 = v * nTimes + t + 1;
179 triplets.emplace_back(idx0, idx1, 1);
180 triplets.emplace_back(idx1, idx0, 1);
181 }
182 }
183
184 SparseMatrix<int> adj(nTotal, nTotal);
185 adj.setFromTriplets(triplets.begin(), triplets.end());
186 return adj;
187}
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
Construction of the sensor- and source-space neighbourhood graphs that define the cluster support for...
FIFF file I/O, in-memory data structures and high-level readers/writers.
Statistical testing (t-tests, F-tests, cluster permutation, multiple comparison correction).
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Definition fiff_info.h:90
QList< FiffChInfo > chs
static Eigen::SparseMatrix< int > fromSourceSpace(const Eigen::MatrixX3i &tris, int nVertices)
static Eigen::SparseMatrix< int > fromChannelPositions(const FIFFLIB::FiffInfo &info, const QStringList &picks=QStringList())
static Eigen::SparseMatrix< int > fromSourceSpaceTemporal(const Eigen::MatrixX3i &tris, int nVertices, int nTimes)