Skip to content

Surface Matrix Test#144

Open
tamsinrogers wants to merge 42 commits into
mainfrom
test/surf-matrix
Open

Surface Matrix Test#144
tamsinrogers wants to merge 42 commits into
mainfrom
test/surf-matrix

Conversation

@tamsinrogers
Copy link
Copy Markdown
Collaborator

@tamsinrogers tamsinrogers commented May 12, 2026

Surface Matrix Error Test (tests/regression/test_surf_matrix.py)

Compute a directed surface-to-surface transform error matrix by measuring median vertex-wise signed-distance error after resampling midthickness cortical surfaces between template spaces using sphere-based barycentric registration.

The resulting matrix captures both directional (asymmetric) and symmetric geometric dissimilarity between brain surface atlases.

uv run pytest /Users/tamsin.rogers/Desktop/github/neuromaps-prime/tests/regression/test_surf_matrix.py -v -x -s --data-dir /Users/tamsin.rogers/Desktop/github/neuromaps-prime -o log_cli=true -o log_cli_level=DEBUG
=== TRANSFORM ERROR MATRIX ===
INFO     transforms.test_surf_matrix:test_surf_matrix.py:171 median surface-to-surface registration error (A → B)
INFO     transforms.test_surf_matrix:test_surf_matrix.py:172 
              fsLR   CIVETNMT        D99   MEBRAINS    NMT2Sym   Yerkes19
fsLR      0.000000  32.959244  32.494068  34.262169  32.517956  32.488098
CIVETNMT  5.823475   0.000000   0.358552   1.758073   5.664001   0.830897
D99       5.718718   0.409281   0.000000   1.774168   6.196968   0.880568
MEBRAINS  5.186776   1.348235   1.263126   0.000000   7.739522   1.287712
NMT2Sym   7.531346   5.008170   4.650307   7.188214   0.000000   4.494304
Yerkes19  4.869173   0.909239   0.854384   1.525678   6.231878   0.000000

Transform error matrix (directed): shows pairwise median surface registration error when mapping each source cortical space (rows) to each target space (columns) using sphere-based resampling, revealing overall compatibility structure across atlases.

=== ASYMMETRIC MATRIX ===
INFO     transforms.test_surf_matrix:test_surf_matrix.py:205 directionality bias in mapping; A → B vs B → A
INFO     transforms.test_surf_matrix:test_surf_matrix.py:206 
               fsLR   CIVETNMT        D99   MEBRAINS    NMT2Sym   Yerkes19
fsLR       0.000000  27.135768  26.775350  29.075393  24.986609  27.618926
CIVETNMT -27.135768   0.000000  -0.050729   0.409838   0.655831  -0.078342
D99      -26.775350   0.050729   0.000000   0.511043   1.546661   0.026183
MEBRAINS -29.075393  -0.409838  -0.511043   0.000000   0.551308  -0.237966
NMT2Sym  -24.986609  -0.655831  -1.546661  -0.551308   0.000000  -1.737575
Yerkes19 -27.618926   0.078342  -0.026183   0.237966   1.737575   0.000000

Asymmetry matrix: quantifies directional bias in registration by subtracting reverse mappings (A→B − B→A), highlighting strong non-reciprocity particularly for fsLR, where mapping into fsLR is substantially more distorted than mapping out.

=== SYMMETRIC MATRIX ===
INFO     transforms.test_surf_matrix:test_surf_matrix.py:210 average bidirectional transform error between spaces (A ↔ B)
INFO     transforms.test_surf_matrix:test_surf_matrix.py:211 
               fsLR   CIVETNMT        D99   MEBRAINS    NMT2Sym   Yerkes19
fsLR       0.000000  19.391360  19.106393  19.724473  20.024651  18.678635
CIVETNMT  19.391360   0.000000   0.383917   1.553154   5.336086   0.870068
D99       19.106393   0.383917   0.000000   1.518647   5.423637   0.867476
MEBRAINS  19.724473   1.553154   1.518647   0.000000   7.463868   1.406695
NMT2Sym   20.024651   5.336086   5.423637   7.463868   0.000000   5.363091
Yerkes19  18.678635   0.870068   0.867476   1.406695   5.363091   0.000000

Symmetric matrix: summarizes intrinsic geometric dissimilarity between atlas spaces by averaging bidirectional errors ((A→B + B→A)/2), revealing a coherent low-distortion cluster among CIVETNMT, D99, MEBRAINS, NMT2Sym, and Yerkes19, with fsLR as a clear outlier.

=== OFF-DIAGONAL TRANSFORM ERROR STATS ===
INFO     transforms.test_surf_matrix:test_surf_matrix.py:259 global median: 4.93867
INFO     transforms.test_surf_matrix:test_surf_matrix.py:261 global min: 0.35855 (CIVETNMT → D99)
INFO     transforms.test_surf_matrix:test_surf_matrix.py:262 global max: 34.26217 (fsLR → MEBRAINS)
INFO     transforms.test_surf_matrix:test_surf_matrix.py:264 NHP-only median: 1.64188
INFO     transforms.test_surf_matrix:test_surf_matrix.py:265 Human↔NHP median: 20.00972
Saved CSV → /Users/tamsin.rogers/Desktop/github/neuromaps-prime/tests/regression/outputs/surface_transform_matrix.csv
INFO     transforms.test_surf_matrix:test_surf_matrix.py:324 Saved full-scale heatmap → /Users/tamsin.rogers/Desktop/github/neuromaps-prime/tests/regression/outputs/surface_transform_matrix_full.png
INFO     transforms.test_surf_matrix:test_surf_matrix.py:354 Saved NHP-scaled heatmap → /Users/tamsin.rogers/Desktop/github/neuromaps-prime/tests/regression/outputs/surface_transform_matrix_nhp_scaled.png
image image

The full-scale heatmap shows the complete range of transform errors across all atlas spaces, including fsLR, which allows for a direct comparison of absolute magnitude differences but is visually dominated by high-error outliers. The NHP-scaled heatmap uses a rescaled color limit based on the NPPatlas subset, suppressing fsLR extremes to reveal relative differences within NHP.

INFO     transforms.test_surf_matrix:test_surf_matrix.py:369 Saved global histogram → /Users/tamsin.rogers/Desktop/github/neuromaps-prime/tests/regression/outputs/surface_transform_histogram.png
image work in progress?

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 12, 2026

Coverage

Coverage Report
FileStmtsMissCoverMissing
neuromaps_prime
   __init__.py00100% 
   niwrap.py560100% 
neuromaps_prime/graph
   __init__.py80100% 
   builder.py110298%329, 397
   cache.py1130100% 
   core.py890100% 
   models.py760100% 
   utils.py530100% 
neuromaps_prime/graph/transforms
   __init__.py00100% 
   surface.py970100% 
   volume.py400100% 
neuromaps_prime/resources
   __init__.py120100% 
neuromaps_prime/transforms
   __init__.py00100% 
   surface.py350100% 
   utils.py290100% 
   volume.py190100% 
TOTAL737299% 

Tests Skipped Failures Errors Time
222 2 💤 0 ❌ 0 🔥 16.793s ⏱️

@tamsinrogers tamsinrogers marked this pull request as ready for review May 12, 2026 20:28
@tamsinrogers tamsinrogers requested review from kaitj and tfunck May 12, 2026 20:28
Copy link
Copy Markdown
Contributor

@kaitj kaitj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to split this into parts:

  1. Technical
    • Are we considering these to be integration tests (that is the functions we are calling work with real data) or are we planning for these to be part of regression testing (to see if adding / changing features change performance)? If the former, I would leave out all of the outputs proposed to be merged in here and share those offline or in a different repository. If the latter, I would leave out the matrices, but retain the .csv file and perhaps give the output directory a more meaningful name. In either case, I would update the .gitignore file appropriately.
    • A few type casting nitpicks
    • Explicitly adding a dependency that is being called (pandas)
  2. Scientific (we can also take this discussion offline if it is easier)
    • At a glance, the distances seem greater than I would have expected, in particular between the different macaque templates. This may just be a result of the transforms that we are being used, but we should double check to make sure we aren't doing anything unexpected. In any case, we've at least provided a framework now that we can perform these tests and substitute in other values.
    • Would also be interesting to have histograms of the distance errors from workbench to gauge how much the vertices have moved

Overall, I think it looks good, just a few changes to be made.

Comment thread tests/integration/transforms/outputs/surface_transform_matrix_nhp_scaled.png Outdated
Comment thread tests/regression/test_surf_matrix.py
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
Comment thread tests/regression/test_surf_matrix.py
Comment thread tests/regression/test_surf_matrix.py
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
Copy link
Copy Markdown

@tfunck tfunck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might make more sense to only to the surface mesh comparison within a given species. I'll check with Ting, but I don't think her cross-species transformation is really meant to produce a human-like surface mesh from a macaque one.

I think NMT2Sym should have better results, so that might indicate that there is a mistake along the way.

@tamsinrogers tamsinrogers requested a review from kaitj May 15, 2026 04:13
Copy link
Copy Markdown
Contributor

@kaitj kaitj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good (code-wise). A few minor things to change, and looking at this more, this is a better fit as a regression test

Comment thread .gitignore Outdated
Comment thread pyproject.toml
Comment thread tests/regression/test_surf_matrix.py
Comment thread tests/integration/transforms/test_surf_matrix.py Outdated
@tamsinrogers tamsinrogers requested a review from kaitj May 21, 2026 16:33
Copy link
Copy Markdown
Contributor

@kaitj kaitj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost, there. There is also still the set path here for the graph initialization.

Other than that, it might also make sense to wait and potentially incorporate any feedback / changes stemming from @tfunck's previous comment.

Comment thread .github/workflows/test.yaml Outdated
@tfunck
Copy link
Copy Markdown

tfunck commented May 22, 2026

Thinking about the results of the human to macaque transformation, I think there's a good chance there's a mistake in the human -> macaque transformation because the errors are so much higher than for macaque -> human. Granted the matrix may not be symmetric but I think the error level should be similar between two spaces. Is it possible the wrong transformation file was used for human -> macaque?

EDIT: Actually, the above reasoning might not be correct. Ting's xspecies alignment optimizes the functional similarity between the macaque and human brain based on fMRI and doesn't care at all about morphology. If for example a human brain has an extra sulcus that the macaque doesn't, the alignment doesn't try to flatten out the human sulcus when fitting to the macaque. The macaque brains, by contrast, are aligned based on cortical curvature and sulcal depth which is explitly morphological. So I guess it could be possible that the transformation is much worse in one direction versus the other. However, in either case, it doesn't make sense to use the vertex distance (which is a measure of morphological alignment) on transformations that aren't meant to optimize this. So like I was saying before, we should only apply this test within species where morphological transformations have been calculated.

@tamsinrogers
Copy link
Copy Markdown
Collaborator Author

@tfunck here is some log output related to your above comment-

fsLR → Yerkes19

src_surface:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/fsLR/src-fsLR_den-32k_hemi-R_midthickness.surf.gii
dst_surface:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_midthickness.surf.gii
src_sphere:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/S1200/src-S1200_den-32k_hemi-R_sphere.surf.gii
dst_sphere:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_sphere.surf.gii

median=32.48810 mean=29.88887 std=14.22251

Yerkes19 → fsLR

src_surface:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_midthickness.surf.gii
dst_surface:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/fsLR/src-fsLR_den-32k_hemi-R_midthickness.surf.gii
src_sphere:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_sphere.surf.gii
dst_sphere:
/Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/S1200/src-S1200_den-32k_hemi-R_sphere.surf.gii
median=4.86917 mean=5.94540 std=4.60708

So the large error does seem to be symmetry-related.

I inspected the relevant files here:

(.venv) (base) CMI-RSCH-MBP118:neuromaps-prime tamsin.rogers$ wb_command -file-information /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_sphere.surf.gii
Name:                       /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_sphere.surf.gii
Type:                       Surface
Structure:                  CortexRight 
Data Size:                  1.17 Megabytes
Maps to Surface:            true
Maps to Volume:             false
Maps with LabelTable:       false
Maps with Palette:          false
Number of Vertices:         32492
Number of Triangles:        64980
Normal Vectors Correct:     true
Surface Type (Primary):     Spherical
Surface Type (Secondary):   Invalid
X-minimum:                  -99.995
X-maximum:                  99.995
Y-minimum:                  -99.995
Y-maximum:                  99.995
Z-minimum:                  -99.995
Z-maximum:                  99.995
Spherical Radius:           99.995
Surface Area:               125657.219
Spacing Mean:               2.122
Spacing Std Dev:            0.111
Spacing Minimum:            1.907
Spacing Maximum:            2.365

(.venv) (base) CMI-RSCH-MBP118:neuromaps-prime tamsin.rogers$ wb_command -file-information /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/S1200/src-S1200_den-32k_hemi-R_sphere.surf.gii
Name:                       /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/S1200/src-S1200_den-32k_hemi-R_sphere.surf.gii
Type:                       Surface
Structure:                  CortexRight 
Data Size:                  1.17 Megabytes
Maps to Surface:            true
Maps to Volume:             false
Maps with LabelTable:       false
Maps with Palette:          false
Number of Vertices:         32492
Number of Triangles:        64980
Normal Vectors Correct:     true
Surface Type (Primary):     Spherical
Surface Type (Secondary):   Invalid
X-minimum:                  -99.995
X-maximum:                  99.995
Y-minimum:                  -99.995
Y-maximum:                  99.995
Z-minimum:                  -99.995
Z-maximum:                  99.995
Spherical Radius:           99.995
Surface Area:               125657.219
Spacing Mean:               2.122
Spacing Std Dev:            0.111
Spacing Minimum:            1.907
Spacing Maximum:            2.365

(.venv) (base) CMI-RSCH-MBP118:neuromaps-prime tamsin.rogers$ wb_command -file-information /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_midthickness.surf.gii
Name:                       /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_midthickness.surf.gii
Type:                       Surface
Structure:                  CortexRight 
Data Size:                  1.17 Megabytes
Maps to Surface:            true
Maps to Volume:             false
Maps with LabelTable:       false
Maps with Palette:          false
Number of Vertices:         32492
Number of Triangles:        64980
Normal Vectors Correct:     true
Surface Type (Primary):     Anatomical
Surface Type (Secondary):   Midthickness
X-minimum:                  -1.265
X-maximum:                  29.351
Y-minimum:                  -46.124
Y-maximum:                  27.280
Z-minimum:                  -17.396
Z-maximum:                  25.466
Surface Area:               11604.327
Spacing Mean:               0.681
Spacing Std Dev:            0.209
Spacing Minimum:            0.149
Spacing Maximum:            1.787

(.venv) (base) CMI-RSCH-MBP118:neuromaps-prime tamsin.rogers$ wb_command -file-information /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_sphere.surf.gii
Name:                       /Users/tamsin.rogers/Desktop/github/neuromaps-prime/share/Inputs/Yerkes19/src-Yerkes19_den-32k_hemi-R_sphere.surf.gii
Type:                       Surface
Structure:                  CortexRight 
Data Size:                  1.17 Megabytes
Maps to Surface:            true
Maps to Volume:             false
Maps with LabelTable:       false
Maps with Palette:          false
Number of Vertices:         32492
Number of Triangles:        64980
Normal Vectors Correct:     true
Surface Type (Primary):     Spherical
Surface Type (Secondary):   Invalid
X-minimum:                  -99.995
X-maximum:                  99.995
Y-minimum:                  -99.995
Y-maximum:                  99.995
Z-minimum:                  -99.995
Z-maximum:                  99.995
Spherical Radius:           99.995
Surface Area:               125657.219
Spacing Mean:               2.122
Spacing Std Dev:            0.111
Spacing Minimum:            1.907
Spacing Maximum:            2.365

does anything stand out to you or @kaitj ?

@kaitj
Copy link
Copy Markdown
Contributor

kaitj commented May 26, 2026

Nothing at a glance, but if the alignment is based on function rather than morphology than I think it makes sense for the large errors. We should then make a note in the graph edge about this if this is indeed the case.

@tfunck
Copy link
Copy Markdown

tfunck commented May 26, 2026

It could be useful to visualize the transformed surfaces versus their targets. It'll give us a sense of what's going on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants