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