~tfardet/nngt-developers

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH NNGT v2] Bugfixes: to_undirected and SWP

Details
Message ID
<WopixzP5xXIMOEWQW9vB3XSfvFwYwMNFrHQRyMMs@cp3-web-016.plabs.ch>
DKIM signature
pass
Download raw message
Patch: +86 -26
This patch corrects the deviation clip to [0, 1] **before** taking the
square in small-world propensity (SWP).
It also corrects the to_undirected method so that it properly works with
non-positive attributes and self-loops.
---
 nngt/analysis/graph_analysis.py |  4 +-
 nngt/core/graph.py              | 74 ++++++++++++++++++++++++---------
 nngt/lib/graph_helpers.py       |  8 ++--
 testing/test_analysis.py        |  2 +-
 testing/test_attributes.py      | 24 +++++++++++
 5 files changed, 86 insertions(+), 26 deletions(-)

diff --git a/nngt/analysis/graph_analysis.py b/nngt/analysis/graph_analysis.py
index 8e40d71..d6ba002 100755
--- a/nngt/analysis/graph_analysis.py
+++ b/nngt/analysis/graph_analysis.py
@@ -440,11 +440,11 @@ def small_world_propensity(g, directed=None, use_global_clustering=False,

    if return_deviations:
        return 1 - np.sqrt(
            0.5*(np.clip(delta_l**2, 0, 1) + np.clip(delta_c**2, 0, 1))), \
            0.5*(np.clip(delta_l, 0, 1)**2 + np.clip(delta_c, 0, 1)**2)), \
            delta_l, delta_c
    else:
        return 1 - np.sqrt(
            0.5*(np.clip(delta_l**2, 0, 1) + np.clip(delta_c**2, 0, 1)))
            0.5*(np.clip(delta_l, 0, 1)**2 + np.clip(delta_c**2, 0, 1)**2))


def shortest_path(g, source, target, directed=True, weights=None):
diff --git a/nngt/core/graph.py b/nngt/core/graph.py
index 883eda1..42c6159 100644
--- a/nngt/core/graph.py
+++ b/nngt/core/graph.py
@@ -702,29 +702,29 @@ class Graph(nngt.core.GraphObject):
                lambda: "sum", **combine_numeric_eattr)

        # find integer eattr
        integer_eattr = "weight" if "weight" in eattrs else None
        integer_types = ("int", "double")
        numeric_eattr = "weight" if "weight" in eattrs else None
        numeric_types = ("int", "double")

        if integer_eattr is None:
        if numeric_eattr is None:
            for eattr in eattrs:
                if self.get_attribute_type(eattr, "edge") in integer_types:
                    integer_eattr = eattr
                if self.get_attribute_type(eattr, "edge") in numeric_types:
                    numeric_eattr = eattr
                    break

        if integer_eattr is not None:
            eattrs.discard(integer_eattr)
        if numeric_eattr is not None:
            eattrs.discard(numeric_eattr)

            combine = combine_numeric_eattr[integer_eattr]
            combine = combine_numeric_eattr[numeric_eattr]

            _, umat = _get_matrices(
                self, directed=False, weights=integer_eattr, weighted=True,
                combine_weights=combine)
                self, directed=False, weights=numeric_eattr, weighted=True,
                combine_weights=combine, remove_self_loops=False)

            umat = ssp.tril(umat, format="csr")

            # create the initial edge attribute
            g.new_edge_attribute(
                integer_eattr, self.get_attribute_type(integer_eattr, "edge"))
                numeric_eattr, self.get_attribute_type(numeric_eattr, "edge"))

            indptr = umat.indptr

@@ -740,18 +740,52 @@ class Graph(nngt.core.GraphObject):

            # add all other edge attributes
            for eattr in eattrs:
                if self.get_attribute_type(eattr, "edge") in integer_types:
                    combine = combine_numeric_eattr[eattr]
                etype = self.get_attribute_type(eattr, "edge")

                    _, umat = _get_matrices(
                        self, directed=False, weights=eattr, weighted=True,
                        combine_weights=combine)
                combine = combine_numeric_eattr[eattr]

                    umat = ssp.tril(umat, format="csr")
                if etype in numeric_types:
                    if np.all(self.edge_attributes[eattr] > 0):
                        _, umat = _get_matrices(
                            self, directed=False, weights=eattr, weighted=True,
                            combine_weights=combine, remove_self_loops=False)

                    g.new_edge_attribute(
                        eattr, self.get_attribute_type(eattr, "edge"),
                        values=umat.data)
                        umat = ssp.tril(umat, format="csr")

                        g.new_edge_attribute(
                            eattr, self.get_attribute_type(eattr, "edge"),
                            values=umat.data)
                    else:
                        aa = list(self.edge_attributes[eattr])

                        adict = {
                            tuple(e): val
                            for e, val in zip(self.edges_array, aa)
                        }

                        f = None

                        if combine == "max":
                            f = np.max
                        elif combine == "min":
                            f = np.min
                        elif combine == "mean":
                            f = np.mean
                        elif combine == "sum":
                            f = np.sum
                        else:
                            raise ValueError(
                                "Invalid combination mode '{}'.".format(
                                    combine))

                        values = [
                            f([adict[e] for e in {tuple(e0), tuple(e0[::-1])}
                               if e in adict]) for e0 in g.edges_array
                        ]

                        g.new_edge_attribute(
                            eattr, self.get_attribute_type(eattr, "edge"),
                            values=values)
        else:
            # hide existing edge warning
            from nngt.lib.connect_tools import logger as lg
diff --git a/nngt/lib/graph_helpers.py b/nngt/lib/graph_helpers.py
index 7af402b..b069a99 100755
--- a/nngt/lib/graph_helpers.py
+++ b/nngt/lib/graph_helpers.py
@@ -206,7 +206,7 @@ def _post_del_update(graph, nodes, remapping=None):


def _get_matrices(g, directed, weights, weighted, combine_weights,
                  normed=False, exponent=None):
                  normed=False, exponent=None, remove_self_loops=True):
    '''
    Return the relevant matrices:
    * W, Wu if weighted
@@ -220,7 +220,8 @@ def _get_matrices(g, directed, weights, weighted, combine_weights,
            W /= W.max()

        # remove potential self-loops
        W.setdiag(0.)
        if remove_self_loops:
            W.setdiag(0.)

        if exponent is not None:
            W = W.power(exponent)
@@ -262,7 +263,8 @@ def _get_matrices(g, directed, weights, weighted, combine_weights,
    A = g.adjacency_matrix()

    # remove potential self-loops
    A.setdiag(0.)
    if remove_self_loops:
        A.setdiag(0.)

    Au = A

diff --git a/testing/test_analysis.py b/testing/test_analysis.py
index 07162d7..bd9af54 100644
--- a/testing/test_analysis.py
+++ b/testing/test_analysis.py
@@ -697,7 +697,7 @@ def test_swp():

    if not (np.isnan(swp_g) or np.isnan(swp_u)):
        # check is necessary as the rewired graph might be unconnected
        assert np.isclose(swp_g, swp_u, 0.01)
        assert np.isclose(swp_g, swp_u, 0.02)


@pytest.mark.mpi_skip
diff --git a/testing/test_attributes.py b/testing/test_attributes.py
index 673f1d7..d107a1c 100755
--- a/testing/test_attributes.py
+++ b/testing/test_attributes.py
@@ -420,6 +420,29 @@ def test_attributes_are_copied():
    assert not np.all(np.isclose(vv, ntest))


@pytest.mark.mpi_skip
def test_combined_attr():
    ''' Check combined attributes '''
    g = nngt.Graph(3)
    g.new_edges(((0, 1), (1, 1), (1, 2), (2, 1)), check_self_loops=False)

    ww = (0.1, 1, 0.5, 0.2)
    dd = (2., 0., 0.3, 0.3)
    rr = (0.8, 0.4, -0.5, 0.5)

    g.set_weights(ww)
    g.new_edge_attribute("distance", "double", dd)
    g.new_edge_attribute("rnd", "double", rr)

    combine = {"weight": "max", "distance": "mean", "rnd": "sum"}

    u = g.to_undirected(combine)

    assert np.all(u.get_weights() == (0.1, 1, 0.5))
    assert np.all(u.edge_attributes["distance"] == (2, 0, 0.3))
    assert np.all(u.edge_attributes["rnd"] == (0.8, 0.4, 0))


# ---------- #
# Test suite #
# ---------- #
@@ -432,3 +455,4 @@ if not nngt.get_config('mpi'):
        test_str_attr()
        test_delays()
        test_attributes_are_copied()
        test_combined_attr()
-- 
2.31.1

[NNGT/patches/.build.yml] build success

builds.sr.ht
Details
Message ID
<CBU4XB0M9XIP.3T50K3J9LX3H8@cirno>
In-Reply-To
<WopixzP5xXIMOEWQW9vB3XSfvFwYwMNFrHQRyMMs@cp3-web-016.plabs.ch> (view parent)
DKIM signature
missing
Download raw message
NNGT/patches/.build.yml: SUCCESS in 25m29s

[Bugfixes: to_undirected and SWP][0] v2 from [Tanguy Fardet][1]

[0]: https://lists.sr.ht/~tfardet/nngt-developers/patches/23148
[1]: tanguyfardet@protonmail.com

✓ #518929 SUCCESS NNGT/patches/.build.yml https://builds.sr.ht/~tfardet/job/518929
Reply to thread Export thread (mbox)