Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add smoothness metric #72

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions nigsp/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def __init__(
surr_split=None,
sdi=None,
gsdi=None,
smoothness=None,
fc=None,
fc_split=None,
):
Expand Down Expand Up @@ -82,6 +83,7 @@ def __init__(
self.surr_split = deepcopy(surr_split)
self.sdi = deepcopy(sdi)
self.gsdi = deepcopy(gsdi)
self.smoothness = deepcopy(smoothness)
self.fc = deepcopy(fc)
self.fc_split = deepcopy(fc_split)

Expand Down Expand Up @@ -168,6 +170,11 @@ def compute_gsdi(self, mean=False, keys=None): # pragma: no cover
self.gsdi = operations.gsdi(self.ts_split, mean, keys)
return self

def compute_smoothness(self, signal): # pragma: no cover
"""Implement metrics.smoothness as class method."""
self.smoothness = operations.smoothness(self.lapl_mtx, signal)
return self

def create_surrogates(self, sc_type="informed", n_surr=1000, seed=None):
"""Implement surrogates.sc_informed and sc_uninformed as class method."""
sc_args = {"timeseries": self.timeseries, "n_surr": n_surr}
Expand Down
2 changes: 1 addition & 1 deletion nigsp/operations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# Import all operations.
from .graph import nodestrength, zerocross
from .laplacian import decomposition, symmetric_normalised_laplacian
from .metrics import functional_connectivity, gsdi, sdi
from .metrics import functional_connectivity, gsdi, sdi, smoothness
from .nifti import apply_atlas, apply_mask, mat_to_vol, unfold_atlas, unmask, vol_to_mat
from .surrogates import random_sign, sc_informed, sc_uninformed, test_significance
from .timeseries import (
Expand Down
19 changes: 19 additions & 0 deletions nigsp/operations/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,25 @@ def _fc(timeseries, mean=False):
return fc


def smoothness(laplacian, signal):
"""Compute the smoothness of a signal over the graph corresponding to given laplacian.

Parameters
----------
node_signal : numpy.ndarray
any signal defined with one value per node.
laplacian : numpy.ndarray
graph laplacian to use.

Returns
-------
smoothness : float
the smoothness of the signal.
"""
LGR.info("Compute signal smoothness.")
return np.dot(signal.T, np.dot(laplacian, signal))


"""
Copyright 2022, Stefano Moia.

Expand Down
10 changes: 10 additions & 0 deletions nigsp/tests/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ def test_gsdi():
assert (gsdi_out["beta_over_alpha"] == gsdi_in).all()


def test_smoothness():
signal = rand(10, 1)
laplacian = rand(10, 10)

expected_smoothness = np.dot(signal.T, np.dot(laplacian, signal))
computed_smoothness = metrics.smoothness(laplacian, signal)

assert np.isclose(expected_smoothness, computed_smoothness, rtol=1e-10)

Copy link
Collaborator

@smoia smoia Aug 22, 2023

Choose a reason for hiding this comment

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

You can add an assert here with signal = rand(10) and expected_smoothness computed on signal[..., np.newaxis]. And another one with signal = rand(2,10) and expected smoothness on signal.T.

Or use chatGPT 🤣


# ### Break tests
def test_break_sdi():
ts1 = np.arange(1, 3)[..., np.newaxis]
Expand Down