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]) nnDist[i] = d;
89 if (d < nnDist[j]) nnDist[j] = d;
90 }
91 }
92
93 // Median nearest-neighbor distance
94 std::vector<double> sortedNN = nnDist;
95 std::sort(sortedNN.begin(), sortedNN.end());
96 double medianNN;
97 if (nCh % 2 == 0) {
98 medianNN = (sortedNN[nCh / 2 - 1] + sortedNN[nCh / 2]) / 2.0;
99 } else {
100 medianNN = sortedNN[nCh / 2];
101 }
102
103 double threshold = 3.0 * medianNN;
104
105 // Build adjacency
106 std::vector<Triplet<int>> triplets;
107 for (int i = 0; i < nCh; ++i) {
108 for (int j = i + 1; j < nCh; ++j) {
109 if (distMat(i, j) <= threshold) {
110 triplets.emplace_back(i, j, 1);
111 triplets.emplace_back(j, i, 1);
112 }
113 }
114 }
115
116 SparseMatrix<int> adj(nCh, nCh);
117 adj.setFromTriplets(triplets.begin(), triplets.end());
118 return adj;
119}
120
121//=============================================================================================================
122
123SparseMatrix<int> StatsAdjacency::fromSourceSpace(const MatrixX3i& tris, int nVertices)
124{
125 std::vector<Triplet<int>> triplets;
126
127 for (int t = 0; t < tris.rows(); ++t) {
128 int v0 = tris(t, 0);
129 int v1 = tris(t, 1);
130 int v2 = tris(t, 2);
131
132 // Mark all 3 pairs as adjacent (symmetric)
133 triplets.emplace_back(v0, v1, 1);
134 triplets.emplace_back(v1, v0, 1);
135 triplets.emplace_back(v0, v2, 1);
136 triplets.emplace_back(v2, v0, 1);
137 triplets.emplace_back(v1, v2, 1);
138 triplets.emplace_back(v2, v1, 1);
139 }
140
141 SparseMatrix<int> adj(nVertices, nVertices);
142 adj.setFromTriplets(triplets.begin(), triplets.end());
143 return adj;
144}
145
146//=============================================================================================================
147
149 const MatrixX3i& tris, int nVertices, int nTimes)
150{
151 // Build spatial adjacency first
152 SparseMatrix<int> spatialAdj = fromSourceSpace(tris, nVertices);
153
154 const int nTotal = nVertices * nTimes;
155 std::vector<Triplet<int>> triplets;
156
157 // Reserve approximate capacity: spatial edges * nTimes + temporal edges
158 triplets.reserve(static_cast<size_t>(spatialAdj.nonZeros()) * nTimes
159 + static_cast<size_t>(nVertices) * (nTimes - 1) * 2);
160
161 // Spatial neighbors repeated for each time point
162 // Linear index: v * nTimes + t
163 for (int t = 0; t < nTimes; ++t) {
164 for (int k = 0; k < spatialAdj.outerSize(); ++k) {
165 for (SparseMatrix<int>::InnerIterator it(spatialAdj, k); it; ++it) {
166 int row = static_cast<int>(it.row()) * nTimes + t;
167 int col = static_cast<int>(it.col()) * nTimes + t;
168 triplets.emplace_back(row, col, 1);
169 }
170 }
171 }
172
173 // Temporal neighbors: vertex v at time t adjacent to v at t-1 and t+1
174 for (int v = 0; v < nVertices; ++v) {
175 for (int t = 0; t < nTimes - 1; ++t) {
176 int idx0 = v * nTimes + t;
177 int idx1 = v * nTimes + t + 1;
178 triplets.emplace_back(idx0, idx1, 1);
179 triplets.emplace_back(idx1, idx0, 1);
180 }
181 }
182
183 SparseMatrix<int> adj(nTotal, nTotal);
184 adj.setFromTriplets(triplets.begin(), triplets.end());
185 return adj;
186}
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:88
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)