From 269025de168198ed833b41942f3174a13e99bcfc Mon Sep 17 00:00:00 2001 From: Agade09 Date: Thu, 24 Jul 2025 13:18:12 +0200 Subject: [PATCH 1/7] Fixed LFM getting stuck finding the same communities over and over. This was less likely to happen in the unweighted form, but after the weighted LFM implementation this issue was observed and fixed. --- cdlib/algorithms/internal/lfm.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cdlib/algorithms/internal/lfm.py b/cdlib/algorithms/internal/lfm.py index fc9bf1d..1b7fa1c 100644 --- a/cdlib/algorithms/internal/lfm.py +++ b/cdlib/algorithms/internal/lfm.py @@ -100,7 +100,10 @@ def __init__(self, G, alpha, weight="weight"): def execute(self): communities = [] + accepted_communities = set() + node_not_include = list(self.g.nodes())[:] + while len(node_not_include) != 0: c = Community(self.g, self.alpha, self.weight) # randomly select a seed node @@ -128,8 +131,16 @@ def execute(self): to_be_examined = c.get_neighbors() - for node in c.nodes: - if node in node_not_include: - node_not_include.remove(node) - communities.append(list(c.nodes)) - return list(communities) + community_as_frozenset = frozenset(c.nodes) + + if community_as_frozenset in accepted_communities: + node_not_include.remove(seed) + else: + accepted_communities.add(community_as_frozenset) + communities.append(list(c.nodes)) + + for node in c.nodes: + if node in node_not_include: + node_not_include.remove(node) + + return list(communities) \ No newline at end of file From 832f71244747cfd58017d5103f27b944c5c45243 Mon Sep 17 00:00:00 2001 From: Agade09 Date: Fri, 25 Jul 2025 14:05:34 +0200 Subject: [PATCH 2/7] Fixed typo in documentation of sbm_dl_nested --- cdlib/algorithms/crisp_partition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdlib/algorithms/crisp_partition.py b/cdlib/algorithms/crisp_partition.py index 1819b9b..ee001b3 100644 --- a/cdlib/algorithms/crisp_partition.py +++ b/cdlib/algorithms/crisp_partition.py @@ -1607,7 +1607,7 @@ def sbm_dl_nested( >>> from cdlib import algorithms >>> import networkx as nx >>> G = nx.karate_club_graph() - >>> coms = algorithms.sbm_dl(G) + >>> coms = algorithms.sbm_dl_nested(G) :References: From 3d6d3d06dd4bbf40a90429d03c1f72b96bf0fe2c Mon Sep 17 00:00:00 2001 From: Agade09 Date: Fri, 25 Jul 2025 14:08:06 +0200 Subject: [PATCH 3/7] Fixed utility function used by sbm_dl and sbm_dl_nested which recognized np.int32 but not np.int64 --- cdlib/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdlib/utils.py b/cdlib/utils.py index 17a6e03..132df62 100644 --- a/cdlib/utils.py +++ b/cdlib/utils.py @@ -278,7 +278,7 @@ def affiliations2nodesets(affiliations: dict) -> dict: return asNodeSets for n, coms in affiliations.items(): - if isinstance(coms, str) or isinstance(coms, int) or isinstance(coms, np.int32): + if isinstance(coms, (str, int, np.integer)): coms = [coms] for c in coms: asNodeSets.setdefault(c, set()) From 3530fcb8a6c2653e594ea382c4ed03b4b9659d90 Mon Sep 17 00:00:00 2001 From: Agade09 Date: Mon, 28 Jul 2025 13:25:37 +0200 Subject: [PATCH 4/7] level==-1 returns the highest modularity communities for girvan-newman --- cdlib/algorithms/crisp_partition.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/cdlib/algorithms/crisp_partition.py b/cdlib/algorithms/crisp_partition.py index ee001b3..b08d4f3 100644 --- a/cdlib/algorithms/crisp_partition.py +++ b/cdlib/algorithms/crisp_partition.py @@ -175,7 +175,7 @@ def girvan_newman(g_original: object, level: int) -> NodeClustering: ========== ======== ======== :param g_original: a networkx/igraph object - :param level: the level where to cut the dendrogram + :param level: the level where to cut the dendrogram (-1 for highest modularity) :return: NodeClustering object :Example: @@ -194,8 +194,18 @@ def girvan_newman(g_original: object, level: int) -> NodeClustering: gn_hierarchy = nx.algorithms.community.girvan_newman(g) coms = [] - for _ in range(level): - coms = next(gn_hierarchy) + if level==-1: + max_modularity = -float('inf') + + for current_coms in gn_hierarchy: + mod = nx.algorithms.community.modularity(g, current_coms) + if mod > max_modularity: + max_modularity = mod + coms = current_coms + else: + for _ in range(level): + coms = next(gn_hierarchy) + communities = [] From e06c00b7209a71d60a4f5a232e10ed5ea205ef06 Mon Sep 17 00:00:00 2001 From: Agade09 Date: Mon, 28 Jul 2025 14:02:42 +0200 Subject: [PATCH 5/7] Fixed typo in LPAM implementation causing crash if distance=='cm' --- cdlib/algorithms/internal/LPAM.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdlib/algorithms/internal/LPAM.py b/cdlib/algorithms/internal/LPAM.py index 147f6f6..16be98b 100644 --- a/cdlib/algorithms/internal/LPAM.py +++ b/cdlib/algorithms/internal/LPAM.py @@ -112,7 +112,7 @@ def getAmp(G): if distance == "amp": D = getAmp(line_graph) if distance == "cm": - D = getCommuteDistace + D = getCommuteDistace(line_graph) if isinstance(distance, np.ndarray): D = distance distance_name = "custom" From 17acdfe9a27d019e27017e6ae3bb99b83f1ec740 Mon Sep 17 00:00:00 2001 From: Agade09 Date: Mon, 28 Jul 2025 14:03:38 +0200 Subject: [PATCH 6/7] Fixed typo in LPAM implementation causing seed parameter to be ignored, default value of 0 was always used --- cdlib/algorithms/internal/LPAM.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdlib/algorithms/internal/LPAM.py b/cdlib/algorithms/internal/LPAM.py index 16be98b..150e4b7 100644 --- a/cdlib/algorithms/internal/LPAM.py +++ b/cdlib/algorithms/internal/LPAM.py @@ -119,7 +119,7 @@ def getAmp(G): if D is None: raise TypeError('Parameter distance should be "amp"/"cm", or numpy.ndarray') _n = len(line_graph.nodes()) - np.random.seed(0) + np.random.seed(seed) initial_medoids = np.random.choice(_n, k, replace=False) kmedoids_instance = kmedoids(D, initial_medoids, data_type="distance_matrix") # run cluster analysis and obtain results From afa2fb43c79263bd0d3667e07187279605dba76a Mon Sep 17 00:00:00 2001 From: Agade09 Date: Mon, 18 Aug 2025 08:18:13 +0200 Subject: [PATCH 7/7] Fixed typo is LPAM implementation. distace instead of distance. --- cdlib/algorithms/internal/LPAM.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cdlib/algorithms/internal/LPAM.py b/cdlib/algorithms/internal/LPAM.py index 150e4b7..3bdfdfb 100644 --- a/cdlib/algorithms/internal/LPAM.py +++ b/cdlib/algorithms/internal/LPAM.py @@ -39,7 +39,7 @@ def LPAM(graph, k=2, threshold=0.5, distance="amp", seed=0): Alexander Ponomarenko, Leonidas Pitsoulis, Marat Shamshetdinov """ - def getCommuteDistace(G): + def getCommuteDistance(G): """ Returns commute distance matrix """ @@ -112,7 +112,7 @@ def getAmp(G): if distance == "amp": D = getAmp(line_graph) if distance == "cm": - D = getCommuteDistace(line_graph) + D = getCommuteDistance(line_graph) if isinstance(distance, np.ndarray): D = distance distance_name = "custom"