Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions brainspace/null_models/moran.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,12 @@ def compute_mem(w, n_ring=1, spectrum='nonzero', tol=1e-10):
mask_zero = ev_abs < tol
n_zero = np.count_nonzero(mask_zero)

if n_zero == 0:
raise ValueError('Weight matrix has no zero eigenvalue.')
if spectrum == 'all' and n_zero == 0:
raise ValueError(
"Weight matrix has no zero eigenvalue; spectrum='all' "
"requires at least one. Use spectrum='nonzero' for matrices "
"without a kernel (e.g., surfaces with no medial wall)."
)

# Multiple zero eigenvalues
if spectrum == 'all':
Expand Down
25 changes: 25 additions & 0 deletions brainspace/tests/test_null_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ def test_moran():
assert np.allclose(r2, msr.randomize(feats))


def test_compute_mem_no_zero_eigvals_nonzero_branch():
"""spectrum='nonzero' must accept matrices with no zero eigenvalues (#112).

Surfaces with no medial wall (e.g. hippocampal subfields) produce weight
matrices with no kernel; the previous unconditional ValueError blocked
that legitimate use case.
"""
rs = np.random.RandomState(0)
a = rs.randn(20, 20)
w = a @ a.T # positive definite -> no zero eigenvalues
mem, ev = compute_mem(w, spectrum='nonzero', tol=1e-10)
assert mem.shape[0] == 20
assert ev.shape[0] == mem.shape[1]
assert np.all(np.abs(ev) > 1e-10)


def test_compute_mem_no_zero_eigvals_all_branch_still_raises():
"""spectrum='all' still requires a zero eigenvalue (#112)."""
rs = np.random.RandomState(1)
a = rs.randn(20, 20)
w = a @ a.T
with pytest.raises(ValueError, match="spectrum='all'"):
compute_mem(w, spectrum='all', tol=1e-10)


def test_spin():
# Create dummy spheres or left and right hemispheres
sphere_lh = wrap_vtk(vtk.vtkSphereSource, radius=20, thetaResolution=10,
Expand Down
Loading