v2.0.0
Loading...
Searching...
No Matches
inv_source_estimate_token.cpp
Go to the documentation of this file.
1//=============================================================================================================
22
23//=============================================================================================================
24// INCLUDES
25//=============================================================================================================
26
28#include "inv_source_estimate.h"
29
30#include <algorithm>
31#include <cmath>
32
33//=============================================================================================================
34// USED NAMESPACES
35//=============================================================================================================
36
37using namespace INVLIB;
38using namespace Eigen;
39
40//=============================================================================================================
41// LOCAL HELPERS — enum ↔ token mapping
42//=============================================================================================================
43
44namespace
45{
46
47InvTokenId methodToTokenId(InvEstimateMethod m)
48{
49 switch (m) {
74 default:
76 }
77}
78
79InvEstimateMethod tokenIdToMethod(InvTokenId id)
80{
81 switch (id) {
106 default:
108 }
109}
110
111InvTokenId spaceToTokenId(InvSourceSpaceType s)
112{
113 switch (s) {
122 default:
124 }
125}
126
127InvSourceSpaceType tokenIdToSpace(InvTokenId id)
128{
129 switch (id) {
138 default:
140 }
141}
142
143InvTokenId orientToTokenId(InvOrientationType o)
144{
145 switch (o) {
152 default:
154 }
155}
156
157InvOrientationType tokenIdToOrient(InvTokenId id)
158{
159 switch (id) {
166 default:
168 }
169}
170
171InvTokenId measureToTokenId(const std::string& m)
172{
173 if (m == "coh")
174 return InvTokenId::MeasCoh;
175 if (m == "imcoh")
177 if (m == "plv")
178 return InvTokenId::MeasPlv;
179 if (m == "pli")
180 return InvTokenId::MeasPli;
181 if (m == "wpli")
183 if (m == "granger")
185 if (m == "pdc")
186 return InvTokenId::MeasPdc;
187 if (m == "dtf")
188 return InvTokenId::MeasDtf;
189 if (m == "correlation")
191 if (m == "crosscorrelation")
194}
195
196std::string tokenIdToMeasure(InvTokenId id)
197{
198 switch (id) {
200 return "coh";
202 return "imcoh";
204 return "plv";
206 return "pli";
208 return "wpli";
210 return "granger";
212 return "pdc";
214 return "dtf";
216 return "correlation";
218 return "crosscorrelation";
219 default:
220 return "";
221 }
222}
223
224// Check whether a token ID falls in the method label range
225bool isMethodToken(InvTokenId id)
226{
227 int v = static_cast<int>(id);
228 return v >= 100 && v <= 112;
229}
230
231// Check whether a token ID falls in the source-space label range
232bool isSpaceToken(InvTokenId id)
233{
234 int v = static_cast<int>(id);
235 return v >= 150 && v <= 154;
236}
237
238// Check whether a token ID falls in the orientation label range
239bool isOrientToken(InvTokenId id)
240{
241 int v = static_cast<int>(id);
242 return v >= 170 && v <= 173;
243}
244
245// Check whether a token ID falls in the connectivity measure label range
246bool isMeasureToken(InvTokenId id)
247{
248 int v = static_cast<int>(id);
249 return v >= 300 && v <= 310;
250}
251
252// Compute sub-sampling stride
253int stride(int total, int max)
254{
255 if (max <= 0 || max >= total)
256 return 1;
257 return (total + max - 1) / max; // ceiling, so at most max entries are kept
258}
259
260} // anonymous namespace
261
262namespace INVLIB
263{
264
265//=============================================================================================================
266// TOKENIZE
267//=============================================================================================================
268
269std::vector<InvToken> tokenize(const InvSourceEstimate& estimate, const InvTokenizeOptions& options)
270{
271 std::vector<InvToken> tokens;
272
273 // Rough capacity estimate to avoid excessive reallocations
274 int nSrc = static_cast<int>(estimate.data.rows());
275 int nTimes = static_cast<int>(estimate.data.cols());
276 int srcStride = stride(nSrc, options.maxSources);
277 int timeStride = stride(nTimes, options.maxTimePoints);
278 int effSrc = (nSrc + srcStride - 1) / srcStride;
279 int effTime = (nTimes + timeStride - 1) / timeStride;
280
281 size_t est = 10; // structural overhead
282 if (options.includeGridData && estimate.hasGridData())
283 est += static_cast<size_t>(effSrc) * (1 + effTime) + effSrc + 6;
284 est += estimate.focalDipoles.size() * 16;
285 est += estimate.couplings.size() * 20;
286 est += estimate.connectivity.size() * 10;
287 if (estimate.hasPositions())
288 est += static_cast<size_t>(estimate.positions.rows()) * 3 + 2;
289 tokens.reserve(est);
290
291 // --- BOS ---
292 tokens.emplace_back(InvTokenId::Bos);
293
294 // --- Metadata ---
295 tokens.emplace_back(InvTokenId::MetaBegin);
296 tokens.emplace_back(methodToTokenId(estimate.method));
297 tokens.emplace_back(spaceToTokenId(estimate.sourceSpaceType));
298 tokens.emplace_back(orientToTokenId(estimate.orientationType));
299 tokens.emplace_back(InvTokenId::MetaEnd);
300
301 // --- Grid data ---
302 if (options.includeGridData && estimate.hasGridData()) {
303 tokens.emplace_back(InvTokenId::GridBegin);
304 tokens.emplace_back(InvTokenId::NSources, static_cast<float>(effSrc));
305 tokens.emplace_back(InvTokenId::NTimes, static_cast<float>(effTime));
306 tokens.emplace_back(InvTokenId::TimeVal, estimate.tmin);
307 tokens.emplace_back(InvTokenId::TStep, estimate.tstep * static_cast<float>(timeStride));
308
309 // Vertex indices
310 for (int s = 0; s < nSrc; s += srcStride)
311 tokens.emplace_back(InvTokenId::Vertex, static_cast<float>(estimate.vertices[s]));
312
313 // Amplitude data row by row
314 for (int s = 0; s < nSrc; s += srcStride) {
315 tokens.emplace_back(InvTokenId::GridRow);
316 for (int t = 0; t < nTimes; t += timeStride)
317 tokens.emplace_back(InvTokenId::Amplitude, static_cast<float>(estimate.data(s, t)));
318 }
319 tokens.emplace_back(InvTokenId::GridEnd);
320 }
321
322 // --- Positions ---
323 if (options.includePositions && estimate.hasPositions()) {
324 tokens.emplace_back(InvTokenId::PosBegin);
325 int nPos = static_cast<int>(estimate.positions.rows());
326 for (int i = 0; i < nPos; i += srcStride) {
327 tokens.emplace_back(InvTokenId::PosX, estimate.positions(i, 0));
328 tokens.emplace_back(InvTokenId::PosY, estimate.positions(i, 1));
329 tokens.emplace_back(InvTokenId::PosZ, estimate.positions(i, 2));
330 }
331 tokens.emplace_back(InvTokenId::PosEnd);
332 }
333
334 // --- Couplings ---
335 if (options.includeCouplings && estimate.hasCouplings()) {
336 tokens.emplace_back(InvTokenId::CouplingBegin);
337 tokens.emplace_back(InvTokenId::NGroups, static_cast<float>(estimate.couplings.size()));
338
339 for (const auto& grp : estimate.couplings) {
340 tokens.emplace_back(InvTokenId::GroupBegin);
341 tokens.emplace_back(InvTokenId::TimeVal, grp.tmin);
342 tokens.emplace_back(InvTokenId::TimeVal, grp.tmax);
343 tokens.emplace_back(InvTokenId::NIndices, static_cast<float>(grp.gridIndices.size()));
344
345 for (size_t k = 0; k < grp.gridIndices.size(); ++k) {
346 tokens.emplace_back(InvTokenId::GridIndex, static_cast<float>(grp.gridIndices[k]));
347 if (k < grp.moments.size()) {
348 tokens.emplace_back(InvTokenId::MomX, static_cast<float>(grp.moments[k].x()));
349 tokens.emplace_back(InvTokenId::MomY, static_cast<float>(grp.moments[k].y()));
350 tokens.emplace_back(InvTokenId::MomZ, static_cast<float>(grp.moments[k].z()));
351 }
352 }
353
354 // Upper-triangle of N×N correlation matrix
355 int n = static_cast<int>(grp.gridIndices.size());
356 for (int r = 0; r < n && r < grp.correlations.rows(); ++r)
357 for (int c = r; c < n && c < grp.correlations.cols(); ++c)
358 tokens.emplace_back(InvTokenId::Correlation, static_cast<float>(grp.correlations(r, c)));
359
360 tokens.emplace_back(InvTokenId::GroupEnd);
361 }
362 tokens.emplace_back(InvTokenId::CouplingEnd);
363 }
364
365 // --- Focal dipoles ---
366 if (options.includeFocalDipoles && estimate.hasFocalDipoles()) {
367 tokens.emplace_back(InvTokenId::FocalBegin);
368 tokens.emplace_back(InvTokenId::NDipoles, static_cast<float>(estimate.focalDipoles.size()));
369
370 for (const auto& dip : estimate.focalDipoles) {
371 tokens.emplace_back(InvTokenId::DipoleBegin);
372 tokens.emplace_back(InvTokenId::TimeVal, dip.tmin);
373 tokens.emplace_back(InvTokenId::TimeVal, dip.tmax);
374 tokens.emplace_back(InvTokenId::PosX, dip.position.x());
375 tokens.emplace_back(InvTokenId::PosY, dip.position.y());
376 tokens.emplace_back(InvTokenId::PosZ, dip.position.z());
377 tokens.emplace_back(InvTokenId::MomX, dip.moment.x());
378 tokens.emplace_back(InvTokenId::MomY, dip.moment.y());
379 tokens.emplace_back(InvTokenId::MomZ, dip.moment.z());
380 tokens.emplace_back(InvTokenId::GridIndex, static_cast<float>(dip.gridIndex));
381 tokens.emplace_back(InvTokenId::Goodness, dip.goodness);
382 tokens.emplace_back(InvTokenId::ChiSquared, dip.khi2);
383 tokens.emplace_back(InvTokenId::NFreeDof, static_cast<float>(dip.nfree));
384 tokens.emplace_back(dip.valid ? InvTokenId::ValidTrue : InvTokenId::ValidFalse);
385 tokens.emplace_back(InvTokenId::DipoleEnd);
386 }
387 tokens.emplace_back(InvTokenId::FocalEnd);
388 }
389
390 // --- Connectivity ---
391 if (options.includeConnectivity && estimate.hasConnectivity()) {
392 tokens.emplace_back(InvTokenId::ConnBegin);
393 tokens.emplace_back(InvTokenId::NMeasures, static_cast<float>(estimate.connectivity.size()));
394
395 for (const auto& conn : estimate.connectivity) {
396 tokens.emplace_back(InvTokenId::ConnEntryBegin);
397 tokens.emplace_back(measureToTokenId(conn.measure));
398 tokens.emplace_back(conn.directed ? InvTokenId::DirectedTrue : InvTokenId::DirectedFalse);
399 tokens.emplace_back(InvTokenId::FreqVal, conn.fmin);
400 tokens.emplace_back(InvTokenId::FreqVal, conn.fmax);
401 tokens.emplace_back(InvTokenId::TimeVal, conn.tmin);
402 tokens.emplace_back(InvTokenId::TimeVal, conn.tmax);
403
404 int n = static_cast<int>(conn.matrix.rows());
405 tokens.emplace_back(InvTokenId::NSources, static_cast<float>(n));
406 for (int r = 0; r < n; ++r)
407 for (int c = 0; c < n; ++c)
408 tokens.emplace_back(InvTokenId::ConnValue, static_cast<float>(conn.matrix(r, c)));
409
410 tokens.emplace_back(InvTokenId::ConnEntryEnd);
411 }
412 tokens.emplace_back(InvTokenId::ConnEnd);
413 }
414
415 // --- EOS ---
416 tokens.emplace_back(InvTokenId::Eos);
417 return tokens;
418}
419
420//=============================================================================================================
421// FROM TOKENS — reconstruct InvSourceEstimate from a token sequence
422//=============================================================================================================
423
424InvSourceEstimate fromTokens(const std::vector<InvToken>& tokens)
425{
427 size_t pos = 0;
428 const size_t len = tokens.size();
429
430 auto advance = [&]() -> const InvToken& {
431 return tokens[pos++];
432 };
433
434 auto peek = [&]() -> InvTokenId {
435 return (pos < len) ? tokens[pos].id : InvTokenId::Eos;
436 };
437
438 // Skip BOS
439 if (pos < len && tokens[pos].id == InvTokenId::Bos)
440 ++pos;
441
442 while (pos < len && tokens[pos].id != InvTokenId::Eos) {
443 const InvToken& tok = tokens[pos];
444
445 // --- Metadata ---
446 if (tok.id == InvTokenId::MetaBegin) {
447 ++pos;
448 while (pos < len && peek() != InvTokenId::MetaEnd) {
449 if (isMethodToken(peek()))
450 est.method = tokenIdToMethod(advance().id);
451 else if (isSpaceToken(peek()))
452 est.sourceSpaceType = tokenIdToSpace(advance().id);
453 else if (isOrientToken(peek()))
454 est.orientationType = tokenIdToOrient(advance().id);
455 else
456 ++pos;
457 }
458 if (pos < len)
459 ++pos; // skip MetaEnd
460 }
461
462 // --- Grid data ---
463 else if (tok.id == InvTokenId::GridBegin) {
464 ++pos;
465 std::vector<int> verts;
466 std::vector<std::vector<float>> rows;
467
468 while (pos < len && peek() != InvTokenId::GridEnd) {
469 const InvToken& g = tokens[pos];
470
471 if (g.id == InvTokenId::NSources) {
472 ++pos;
473 } else if (g.id == InvTokenId::NTimes) {
474 ++pos;
475 } else if (g.id == InvTokenId::TimeVal && est.tmin == 0 && est.tstep == -1) {
476 est.tmin = g.value;
477 ++pos;
478 } else if (g.id == InvTokenId::TStep) {
479 est.tstep = g.value;
480 ++pos;
481 } else if (g.id == InvTokenId::TimeVal) {
482 // Second TimeVal inside grid is still tmin if tstep wasn't set yet
483 ++pos;
484 } else if (g.id == InvTokenId::Vertex) {
485 verts.push_back(static_cast<int>(g.value));
486 ++pos;
487 } else if (g.id == InvTokenId::GridRow) {
488 ++pos;
489 std::vector<float> row;
490 while (pos < len && tokens[pos].id == InvTokenId::Amplitude) {
491 row.push_back(tokens[pos].value);
492 ++pos;
493 }
494 rows.push_back(std::move(row));
495 } else {
496 ++pos;
497 }
498 }
499 if (pos < len)
500 ++pos; // skip GridEnd
501
502 // Fill Eigen structures
503 int effSrc = static_cast<int>(rows.size());
504 int effTime = effSrc > 0 ? static_cast<int>(rows[0].size()) : 0;
505 if (effSrc > 0 && effTime > 0) {
506 est.data = MatrixXd(effSrc, effTime);
507 for (int s = 0; s < effSrc; ++s)
508 for (int t = 0; t < effTime && t < static_cast<int>(rows[s].size()); ++t)
509 est.data(s, t) = static_cast<double>(rows[s][t]);
510 }
511 est.vertices = VectorXi(static_cast<int>(verts.size()));
512 for (int i = 0; i < static_cast<int>(verts.size()); ++i)
513 est.vertices[i] = verts[i];
514 }
515
516 // --- Positions ---
517 else if (tok.id == InvTokenId::PosBegin) {
518 ++pos;
519 std::vector<Vector3f> posVec;
520 while (pos + 2 < len && peek() != InvTokenId::PosEnd) {
521 if (tokens[pos].id == InvTokenId::PosX) {
522 Vector3f p;
523 p.x() = tokens[pos].value;
524 p.y() = tokens[pos + 1].value;
525 p.z() = tokens[pos + 2].value;
526 posVec.push_back(p);
527 pos += 3;
528 } else {
529 ++pos;
530 }
531 }
532 if (pos < len)
533 ++pos; // skip PosEnd
534
535 est.positions = MatrixX3f(static_cast<int>(posVec.size()), 3);
536 for (int i = 0; i < static_cast<int>(posVec.size()); ++i)
537 est.positions.row(i) = posVec[i].transpose();
538 }
539
540 // --- Couplings ---
541 else if (tok.id == InvTokenId::CouplingBegin) {
542 ++pos;
543 if (pos < len && tokens[pos].id == InvTokenId::NGroups)
544 ++pos; // skip count
545
546 while (pos < len && peek() != InvTokenId::CouplingEnd) {
547 if (peek() == InvTokenId::GroupBegin) {
548 ++pos;
550 int timeIdx = 0;
551
552 while (pos < len && peek() != InvTokenId::GroupEnd) {
553 const InvToken& ct = tokens[pos];
554 if (ct.id == InvTokenId::TimeVal) {
555 if (timeIdx++ == 0)
556 grp.tmin = ct.value;
557 else
558 grp.tmax = ct.value;
559 ++pos;
560 } else if (ct.id == InvTokenId::NIndices) {
561 ++pos;
562 } else if (ct.id == InvTokenId::GridIndex) {
563 grp.gridIndices.push_back(static_cast<int>(ct.value));
564 ++pos;
565 } else if (ct.id == InvTokenId::MomX && pos + 2 < len) {
566 Vector3d m;
567 m.x() = static_cast<double>(tokens[pos].value);
568 m.y() = static_cast<double>(tokens[pos + 1].value);
569 m.z() = static_cast<double>(tokens[pos + 2].value);
570 grp.moments.push_back(m);
571 pos += 3;
572 } else if (ct.id == InvTokenId::Correlation) {
573 // Reconstruct upper-triangle
574 int n = static_cast<int>(grp.gridIndices.size());
575 if (n > 0 && grp.correlations.size() == 0) {
576 grp.correlations = MatrixXd::Zero(n, n);
577 for (int r = 0; r < n; ++r) {
578 for (int c = r; c < n && pos < len && tokens[pos].id == InvTokenId::Correlation; ++c) {
579 double v = static_cast<double>(tokens[pos].value);
580 grp.correlations(r, c) = v;
581 grp.correlations(c, r) = v;
582 ++pos;
583 }
584 }
585 } else {
586 ++pos;
587 }
588 } else {
589 ++pos;
590 }
591 }
592 if (pos < len)
593 ++pos; // skip GroupEnd
594 est.couplings.push_back(std::move(grp));
595 } else {
596 ++pos;
597 }
598 }
599 if (pos < len)
600 ++pos; // skip CouplingEnd
601 }
602
603 // --- Focal dipoles ---
604 else if (tok.id == InvTokenId::FocalBegin) {
605 ++pos;
606 if (pos < len && tokens[pos].id == InvTokenId::NDipoles)
607 ++pos;
608
609 while (pos < len && peek() != InvTokenId::FocalEnd) {
610 if (peek() == InvTokenId::DipoleBegin) {
611 ++pos;
612 InvFocalDipole dip;
613 int timeIdx = 0;
614
615 while (pos < len && peek() != InvTokenId::DipoleEnd) {
616 const InvToken& dt = tokens[pos];
617 if (dt.id == InvTokenId::TimeVal) {
618 if (timeIdx == 0)
619 dip.tmin = dt.value;
620 else
621 dip.tmax = dt.value;
622 ++timeIdx;
623 ++pos;
624 } else if (dt.id == InvTokenId::PosX) {
625 dip.position.x() = dt.value;
626 ++pos;
627 } else if (dt.id == InvTokenId::PosY) {
628 dip.position.y() = dt.value;
629 ++pos;
630 } else if (dt.id == InvTokenId::PosZ) {
631 dip.position.z() = dt.value;
632 ++pos;
633 } else if (dt.id == InvTokenId::MomX) {
634 dip.moment.x() = dt.value;
635 ++pos;
636 } else if (dt.id == InvTokenId::MomY) {
637 dip.moment.y() = dt.value;
638 ++pos;
639 } else if (dt.id == InvTokenId::MomZ) {
640 dip.moment.z() = dt.value;
641 ++pos;
642 } else if (dt.id == InvTokenId::GridIndex) {
643 dip.gridIndex = static_cast<int>(dt.value);
644 ++pos;
645 } else if (dt.id == InvTokenId::Goodness) {
646 dip.goodness = dt.value;
647 ++pos;
648 } else if (dt.id == InvTokenId::ChiSquared) {
649 dip.khi2 = dt.value;
650 ++pos;
651 } else if (dt.id == InvTokenId::NFreeDof) {
652 dip.nfree = static_cast<int>(dt.value);
653 ++pos;
654 } else if (dt.id == InvTokenId::ValidTrue) {
655 dip.valid = true;
656 ++pos;
657 } else if (dt.id == InvTokenId::ValidFalse) {
658 dip.valid = false;
659 ++pos;
660 } else {
661 ++pos;
662 }
663 }
664 if (pos < len)
665 ++pos; // skip DipoleEnd
666 est.focalDipoles.push_back(dip);
667 } else {
668 ++pos;
669 }
670 }
671 if (pos < len)
672 ++pos; // skip FocalEnd
673 }
674
675 // --- Connectivity ---
676 else if (tok.id == InvTokenId::ConnBegin) {
677 ++pos;
678 if (pos < len && tokens[pos].id == InvTokenId::NMeasures)
679 ++pos;
680
681 while (pos < len && peek() != InvTokenId::ConnEnd) {
682 if (peek() == InvTokenId::ConnEntryBegin) {
683 ++pos;
684 InvConnectivity conn;
685 int n = 0;
686 int freqIdx = 0, timeIdx = 0;
687
688 while (pos < len && peek() != InvTokenId::ConnEntryEnd) {
689 const InvToken& ct = tokens[pos];
690 if (isMeasureToken(ct.id)) {
691 conn.measure = tokenIdToMeasure(ct.id);
692 ++pos;
693 } else if (ct.id == InvTokenId::DirectedTrue) {
694 conn.directed = true;
695 ++pos;
696 } else if (ct.id == InvTokenId::DirectedFalse) {
697 conn.directed = false;
698 ++pos;
699 } else if (ct.id == InvTokenId::FreqVal) {
700 if (freqIdx == 0)
701 conn.fmin = ct.value;
702 else
703 conn.fmax = ct.value;
704 ++freqIdx;
705 ++pos;
706 } else if (ct.id == InvTokenId::TimeVal) {
707 if (timeIdx == 0)
708 conn.tmin = ct.value;
709 else
710 conn.tmax = ct.value;
711 ++timeIdx;
712 ++pos;
713 } else if (ct.id == InvTokenId::NSources) {
714 n = static_cast<int>(ct.value);
715 ++pos;
716 } else if (ct.id == InvTokenId::ConnValue && n > 0) {
717 conn.matrix = MatrixXd(n, n);
718 for (int r = 0; r < n; ++r)
719 for (int c = 0; c < n && pos < len && tokens[pos].id == InvTokenId::ConnValue; ++c, ++pos)
720 conn.matrix(r, c) = static_cast<double>(tokens[pos].value);
721 } else {
722 ++pos;
723 }
724 }
725 if (pos < len)
726 ++pos; // skip ConnEntryEnd
727 est.connectivity.push_back(std::move(conn));
728 } else {
729 ++pos;
730 }
731 }
732 if (pos < len)
733 ++pos; // skip ConnEnd
734 }
735
736 else {
737 ++pos; // skip unrecognised tokens
738 }
739 }
740
741 // Rebuild times vector
742 if (est.data.cols() > 0 && est.tstep > 0) {
743 est.times = RowVectorXf(est.data.cols());
744 est.times[0] = est.tmin;
745 for (int i = 1; i < est.times.size(); ++i)
746 est.times[i] = est.times[i - 1] + est.tstep;
747 }
748
749 return est;
750}
751
752} // namespace INVLIB
Tokeniser and de-tokeniser that turn an INVLIB::InvSourceEstimate into a flat sequence consumable by ...
InvSourceEstimate value type — central source-space data container produced by every INVLIB inverse s...
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
InvEstimateMethod
Definition inv_types.h:37
InvSourceEstimate fromTokens(const std::vector< InvToken > &tokens)
Reconstruct an InvSourceEstimate from a token sequence.
InvOrientationType
Definition inv_types.h:71
InvSourceSpaceType
Definition inv_types.h:58
std::vector< InvToken > tokenize(const InvSourceEstimate &estimate, const InvTokenizeOptions &options)
Serialise an InvSourceEstimate into a flat token sequence.
Pairwise source connectivity matrix with measure, directionality, and frequency/time metadata.
Single focal dipole with free 3D position, moment, and fit-quality metrics.
N-tuple of correlated grid sources with orientations and correlation matrix.
std::vector< Eigen::Vector3d > moments
Source-space inverse-solution container with dense grid plus optional focal-dipole,...
std::vector< InvSourceCoupling > couplings
std::vector< InvFocalDipole > focalDipoles
InvSourceSpaceType sourceSpaceType
InvOrientationType orientationType
std::vector< InvConnectivity > connectivity
One element of a tokenised neural-source representation.
Definition inv_token.h:189
InvTokenId id
Definition inv_token.h:190
Tokenization options controlling layer inclusion and sub-sampling.
Definition inv_token.h:222