sgtlib 3.4.0__py3-none-any.whl → 3.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
StructuralGT/__init__.py CHANGED
@@ -24,7 +24,7 @@ of the GNU General Public License along with this program. If not, see <https:/
24
24
 
25
25
 
26
26
  # Project Details
27
- __version__ = "3.4.0"
27
+ __version__ = "3.4.1"
28
28
  __install_version__ = "3.3.9"
29
29
  __title__ = f"StructuralGT (v{__version__})"
30
30
  __author__ = "Dickson Owuor"
@@ -15,6 +15,7 @@ import multiprocessing
15
15
  import numpy as np
16
16
  import scipy as sp
17
17
  import pandas as pd
18
+ import igraph as ig
18
19
  import networkx as nx
19
20
  import matplotlib.table as tbl
20
21
  import matplotlib.pyplot as plt
@@ -67,6 +68,15 @@ except cp.cuda.runtime.CUDARuntimeError:
67
68
  """
68
69
 
69
70
 
71
+ def _worker_vertex_connectivity(ig_graph_obj, i, j):
72
+ try:
73
+ lnc = ig_graph_obj.vertex_connectivity(source=i, target=j, neighbors="negative")
74
+ return lnc if lnc != -1 else None
75
+ except Exception as err:
76
+ logging.exception("Computing iGraph ANC Error: %s", err, extra={'user': 'SGT Logs'})
77
+ return None
78
+
79
+
70
80
  class GraphAnalyzer(ProgressUpdate):
71
81
  """
72
82
  A class that computes all the user-selected graph theory metrics and writes the results in a PDF file.
@@ -247,22 +257,7 @@ class GraphAnalyzer(ProgressUpdate):
247
257
  self.update_status([-1, "Task aborted."])
248
258
  return None
249
259
  self.update_status([15, "Computing node connectivity..."])
250
- if connected_graph:
251
- # use_igraph = opt_gtc["compute_lang == 'C'"]["value"]
252
- if self.use_igraph:
253
- # use iGraph Lib in C
254
- self.update_status([15, "Using iGraph library..."])
255
- avg_node_con = self.igraph_average_node_connectivity(graph)
256
- else:
257
- # Use NetworkX Lib in Python
258
- self.update_status([15, "Using NetworkX library..."])
259
- if self.allow_mp: # Multi-processing
260
- avg_node_con = self.average_node_connectivity(graph)
261
- else:
262
- avg_node_con = average_node_connectivity(graph)
263
- avg_node_con = round(avg_node_con, 5)
264
- else:
265
- avg_node_con = np.nan
260
+ avg_node_con = self.compute_average_node_connectivity(graph, connected_graph)
266
261
  data_dict["parameter"].append("Average node connectivity")
267
262
  data_dict["value"].append(avg_node_con)
268
263
 
@@ -586,88 +581,147 @@ class GraphAnalyzer(ProgressUpdate):
586
581
 
587
582
  return ohms_dict, res
588
583
 
589
- def average_node_connectivity(self, nx_graph: nx.Graph, flow_func=None):
584
+ def compute_average_node_connectivity(self, nx_graph: nx.Graph, is_graph_connected=False):
590
585
  r"""Returns the average connectivity of a graph G.
591
586
 
592
587
  The average connectivity `\bar{\kappa}` of a graph G is the average
593
588
  of local node connectivity over all pairs of the nx_graph nodes.
594
589
 
595
- https://networkx.org/documentation/stable/_modules/networkx/algorithms/connectivity/connectivity.html#average_node_connectivity
596
-
597
- Parameters
598
- ----------
599
590
  :param nx_graph: NetworkX graph object.
600
- :param flow_func : Function
601
- A function for computing the maximum flow between a pair of nodes.
602
- The function has to accept at least three parameters: a Digraph,
603
- a source node, and a target node. And return a residual network
604
- that follows NetworkX conventions (see: meth:`maximum_flow` for
605
- details). If flow_func is None, the default maximum flow function
606
- (:meth:`edmonds_karp`) is used. See :meth:`local_node_connectivity`
607
- for details. The choice of the default function may change from
608
- version to version and should not be relied on. Default value: None.
609
-
610
- Returns
611
- -------
612
- K : float
613
- Average node connectivity
614
-
615
- References
616
- ----------
617
- [1] Beineke, L., O. Oellermann, and r_network. Pippert (2002). The average
618
- connectivity of a graph. Discrete mathematics 252(1-3), 31-45.
619
- https://www.sciencedirect.com/science/article/pii/S0012365X01001807
620
-
591
+ :param is_graph_connected: Boolean
621
592
  """
622
593
 
623
- if nx_graph.is_directed():
624
- iter_func = itertools.permutations
625
- else:
626
- iter_func = itertools.combinations
627
-
628
- # Reuse the auxiliary digraph and the residual network
629
- a_digraph = nx.algorithms.connectivity.build_auxiliary_node_connectivity(nx_graph)
630
- r_network = nx.algorithms.flow.build_residual_network(a_digraph, "capacity")
631
- # kwargs = {"flow_func": flow_func, "auxiliary": a_digraph, "residual": r_network}
632
-
633
- num, den = 0, 0
634
- with multiprocessing.Pool() as pool:
635
- items = [(nx_graph, u, v, flow_func, a_digraph, r_network) for u, v in iter_func(nx_graph, 2)]
636
- for n in pool.starmap(nx.algorithms.connectivity.local_node_connectivity, items):
637
- num += n
638
- den += 1
639
- if self.abort:
640
- self.update_status([-1, "Task aborted."])
641
- return 0
642
- if den == 0:
643
- return 0
644
- return num / den
645
-
646
- def igraph_average_node_connectivity(self, nx_graph: nx.Graph):
647
- r"""Returns the average connectivity of a graph G.
594
+ def nx_average_node_connectivity(flow_func=None):
595
+ r"""Returns the average connectivity of a graph G.
596
+
597
+ The average connectivity `\bar{\kappa}` of a graph G is the average
598
+ of local node connectivity over all pairs of the nx_graph nodes.
599
+
600
+ https://networkx.org/documentation/stable/_modules/networkx/algorithms/connectivity/connectivity.html#average_node_connectivity
601
+
602
+ Parameters
603
+ ----------
604
+ :param flow_func : Function
605
+ A function for computing the maximum flow between a pair of nodes.
606
+ The function has to accept at least three parameters: a Digraph,
607
+ a source node, and a target node. And return a residual network
608
+ that follows NetworkX conventions (see: meth:`maximum_flow` for
609
+ details). If flow_func is None, the default maximum flow function
610
+ (:meth:`edmonds_karp`) is used. See :meth:`local_node_connectivity`
611
+ for details. The choice of the default function may change from
612
+ version to version and should not be relied on. Default value: None.
613
+
614
+ Returns
615
+ -------
616
+ K : float
617
+ Average node connectivity
618
+
619
+ References
620
+ ----------
621
+ [1] Beineke, L., O. Oellermann, and r_network. Pippert (2002). The average
622
+ connectivity of a graph. Discrete mathematics 252(1-3), 31-45.
623
+ https://www.sciencedirect.com/science/article/pii/S0012365X01001807
648
624
 
649
- The average connectivity of a graph G is the average
650
- of local node connectivity over all pairs of the Graph (G) nodes.
625
+ """
651
626
 
652
- Parameters
653
- ---------
654
- :param nx_graph: NetworkX graph object.
655
- """
656
- from .c_lang import sgt_c_module as sgt
627
+ if nx_graph.is_directed():
628
+ iter_func = itertools.permutations
629
+ else:
630
+ iter_func = itertools.combinations
631
+
632
+ # Reuse the auxiliary digraph and the residual network
633
+ a_digraph = nx.algorithms.connectivity.build_auxiliary_node_connectivity(nx_graph)
634
+ r_network = nx.algorithms.flow.build_residual_network(a_digraph, "capacity")
635
+ # kwargs = {"flow_func": flow_func, "auxiliary": a_digraph, "residual": r_network}
636
+
637
+ total, count = 0, 0
638
+ with multiprocessing.Pool() as pool:
639
+ items = [(nx_graph, u, v, flow_func, a_digraph, r_network) for u, v in iter_func(nx_graph, 2)]
640
+ async_result = pool.starmap_async(nx.algorithms.connectivity.local_node_connectivity, items)
641
+ for n in async_result.get():
642
+ total += n
643
+ count += 1
644
+ if self.abort:
645
+ self.update_status([-1, "Task aborted."])
646
+ pool.terminate()
647
+ pool.join()
648
+ return 0
649
+ if n is not None:
650
+ total += n
651
+ count += 1
652
+ anc = total / count if count > 0 else 0
653
+ return anc
654
+
655
+ def igraph_average_node_connectivity():
656
+ r"""
657
+ Returns the average connectivity of a graph G.
658
+
659
+ The average connectivity of a graph G is the average
660
+ of local node connectivity over all pairs of the Graph (G) nodes.
661
+ """
657
662
 
658
- cpu_count = get_num_cores()
659
- num_threads = cpu_count if nx.number_of_nodes(nx_graph) < 2000 else cpu_count * 2
660
- anc = 0
663
+ ig_graph = ig.Graph.from_networkx(nx_graph)
664
+ num_nodes = ig_graph.vcount()
665
+ total, count = 0, 0
666
+ with multiprocessing.Pool() as pool:
667
+ # Prepare all node pairs (i < j)
668
+ items = [(ig_graph, i, j) for i in range(num_nodes) for j in range(i + 1, num_nodes)]
669
+ async_result = pool.starmap_async(_worker_vertex_connectivity, items)
670
+ for n in async_result.get():
671
+ if self.abort:
672
+ self.update_status([-1, "Task aborted."])
673
+ pool.terminate()
674
+ pool.join()
675
+ return 0
676
+ if n is not None:
677
+ total += n
678
+ count += 1
679
+ anc = total / count if count > 0 else 0
680
+ return anc
681
+
682
+ def igraph_clang_average_node_connectivity():
683
+ r"""Returns the average connectivity of a graph G.
684
+
685
+ The average connectivity of a graph G is the average
686
+ of local node connectivity over all pairs of the Graph (G) nodes.
661
687
 
662
- try:
663
- filename, output_location = self.ntwk_p.get_filenames()
664
- g_filename = filename + "_graph.txt"
665
- graph_file = os.path.join(output_location, g_filename)
666
- nx.write_edgelist(nx_graph, graph_file, data=False)
667
- anc = sgt.compute_anc(graph_file, num_threads, self.allow_mp)
668
- except Exception as err:
669
- logging.exception("Computing ANC Error: %s", err, extra={'user': 'SGT Logs'})
670
- return anc
688
+ """
689
+ from .c_lang import sgt_c_module as sgt
690
+
691
+ cpu_count = get_num_cores()
692
+ num_threads = cpu_count if nx.number_of_nodes(nx_graph) < 2000 else cpu_count * 2
693
+ anc = 0
694
+
695
+ try:
696
+ filename, output_location = self.ntwk_p.get_filenames()
697
+ g_filename = filename + "_graph.txt"
698
+ graph_file = os.path.join(output_location, g_filename)
699
+ nx.write_edgelist(nx_graph, graph_file, data=False)
700
+ anc = sgt.compute_anc(graph_file, num_threads, self.allow_mp)
701
+ except Exception as err:
702
+ logging.exception("Computing ANC Error: %s", err, extra={'user': 'SGT Logs'})
703
+ return anc
704
+
705
+ if is_graph_connected:
706
+ # use_igraph = opt_gtc["compute_lang == 'C'"]["value"]
707
+ if self.use_igraph:
708
+ # use iGraph Lib in C
709
+ self.update_status([15, "Using iGraph library..."])
710
+ try:
711
+ avg_node_con = igraph_clang_average_node_connectivity()
712
+ except ImportError:
713
+ avg_node_con = igraph_average_node_connectivity()
714
+ else:
715
+ # Use NetworkX Lib in Python
716
+ self.update_status([15, "Using NetworkX library..."])
717
+ if self.allow_mp: # Multi-processing
718
+ avg_node_con = nx_average_node_connectivity()
719
+ else:
720
+ avg_node_con = average_node_connectivity(nx_graph)
721
+ avg_node_con = round(avg_node_con, 5)
722
+ else:
723
+ avg_node_con = np.nan
724
+ return avg_node_con
671
725
 
672
726
  def compute_graph_conductance(self, graph_obj):
673
727
  """
@@ -5,9 +5,9 @@ Builds a graph network from nanoscale microscopy images.
5
5
  """
6
6
 
7
7
  import os
8
- import igraph
9
8
  import itertools
10
9
  import numpy as np
10
+ import igraph as ig
11
11
  import networkx as nx
12
12
  import matplotlib.pyplot as plt
13
13
  from PIL import Image, ImageQt
@@ -43,7 +43,7 @@ class FiberNetworkBuilder(ProgressUpdate):
43
43
  self.img_ntwk: MatLike | None = None
44
44
  self.nx_giant_graph: nx.Graph | None = None
45
45
  self.nx_graph: nx.Graph | None = None
46
- self.ig_graph: igraph.Graph | None = None
46
+ self.ig_graph: ig.Graph | None = None
47
47
  self.gsd_file: str | None = None
48
48
  self.skel_obj: GraphSkeleton | None = None
49
49
 
@@ -149,7 +149,7 @@ class FiberNetworkBuilder(ProgressUpdate):
149
149
  nx_graph[s][e]['weight'] = wt
150
150
  # print(f"{nx_graph[s][e]}\n")
151
151
  self.nx_graph = nx_graph
152
- self.ig_graph = igraph.Graph.from_networkx(nx_graph)
152
+ self.ig_graph = ig.Graph.from_networkx(nx_graph)
153
153
  return True
154
154
 
155
155
  def plot_graph_network(self, image_arr: MatLike, giant_only: bool = False, plot_nodes: bool = False, a4_size: bool = False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sgtlib
3
- Version: 3.4.0
3
+ Version: 3.4.1
4
4
  Summary: A software tool for graph theory analysis of microscopy images.
5
5
  Author-email: Dickson Owuor <owuordickson@gmail.com>, "Drew A. Vecchio" <vecdrew@umich.edu>, Kody Whisnant <kgwhis@umich.edu>, Alain Kadar <alaink@umich.edu>, Xiong Ye Xiao <xiongyex@usc.edu>, Nicholas Kotov <kotov@umich.edu>
6
6
  Maintainer-email: Dickson Owuor <owuordickson@gmail.com>
@@ -1,4 +1,4 @@
1
- StructuralGT/__init__.py,sha256=8O4pI5ydEdxVMuojfGLzMWBjt7dVw2shSmAmw52F9tg,1254
1
+ StructuralGT/__init__.py,sha256=UoEP1e5QcjHqzVbt7cUCTL1rRksz3Qsze5OTmSAzeZo,1254
2
2
  StructuralGT/entrypoints.py,sha256=s2upsOVsy9zsoorxiZXaZKiLw7fUOQwHjwup3C089Hs,1413
3
3
  StructuralGT/modules.py,sha256=CYudBMwP70cRFWj_Dppr_Kdc08V_-9wWKVd48bEHkS0,743
4
4
  StructuralGT/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -50,15 +50,13 @@ StructuralGT/apps/sgt_qml/widgets/RescaleControlWidget.qml,sha256=qb6fXok5C1HJjb
50
50
  StructuralGT/apps/sgt_qml/widgets/RibbonWidget.qml,sha256=POaPwXh7vE4UwU82x-80BuI2625FLWbmJQsZliy618w,13990
51
51
  StructuralGT/apps/sgt_qml/widgets/StatusBarWidget.qml,sha256=YRTSg_4aYA3FO69wT1NnX0Ri_qRx3AHhVrS1n3pVwd4,5969
52
52
  StructuralGT/compute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- StructuralGT/compute/graph_analyzer.py,sha256=E2vIaIcApy3VI7gM2rLZ30X8SGtOhzhEkVufMcL-ngA,69859
54
- StructuralGT/compute/c_lang/sgt_base.c,sha256=VFgwVDALjWl_zAP5NqNwG5pLv6no91_93RYPziHT2hY,1953
55
- StructuralGT/compute/c_lang/sgtmodule.c,sha256=85VhQCxNow49yslayaCnCFwseS6vXVvJ77RjypkHG1o,5492
53
+ StructuralGT/compute/graph_analyzer.py,sha256=JeUlfeC0phR5JqAqDXAhrUq8amDXNekcIYfvbIUl0q8,72274
56
54
  StructuralGT/compute/c_lang/include/sgt_base.h,sha256=jkCpNy_01bSKk_ay6_1TilNhribMmw-p8r8FRhamstM,530
57
55
  StructuralGT/imaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
56
  StructuralGT/imaging/base_image.py,sha256=Cc4xeVOxpID6SukVolYtD6OiEQ4tsUGQ4mYjE3QF_2E,16555
59
57
  StructuralGT/imaging/image_processor.py,sha256=dElOT51scHbEwsRpfnUUbLQ68twiGAhALgxcqTJBzzM,32878
60
58
  StructuralGT/networks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
- StructuralGT/networks/fiber_network.py,sha256=Sno9fD5JsdhB0hLsi6CM7EPRKZAuxnbWBTwkhlkc8AA,20060
59
+ StructuralGT/networks/fiber_network.py,sha256=dr-JCfAGi52Lkcn_L4bT1939hXwg-Jp9-90VHMv324I,20058
62
60
  StructuralGT/networks/graph_skeleton.py,sha256=_uWDvoHG4Pu-iKIRbIx0FyYqkiQZDk73md9-bOnWswQ,19246
63
61
  StructuralGT/networks/sknw_mod.py,sha256=rBLdBmiawj1eURGKreuUJjKeEg5v76WNHkNxVEZhQWY,5542
64
62
  StructuralGT/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -66,9 +64,9 @@ StructuralGT/utils/config_loader.py,sha256=8px8iGOFCGz8qIZCfyGuAFfvvaVdOjogf9PqS
66
64
  StructuralGT/utils/configs.ini,sha256=sKPsAd15_YubgGhLMVuse1JRuU7S6bFYjLbzkuoD5JI,2326
67
65
  StructuralGT/utils/progress_update.py,sha256=9X_9mGLgTMF5dDSTNKpELQcmFv8r1wz6YZQCqy4tCl8,1621
68
66
  StructuralGT/utils/sgt_utils.py,sha256=0vg7I4P6PIao_H8-KYgkCJ6TM9eESW-RwGZB4sGgCI4,9109
69
- sgtlib-3.4.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
70
- sgtlib-3.4.0.dist-info/METADATA,sha256=KLud3ApS4KMRC6hYaHiseM1P4kQ57Oy8EhNvQ9B9ANU,45278
71
- sgtlib-3.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
72
- sgtlib-3.4.0.dist-info/entry_points.txt,sha256=zY3v_U9Qi4L965BPrURXn84yF_TxdKDZI3VcTUbTh0M,120
73
- sgtlib-3.4.0.dist-info/top_level.txt,sha256=daVVcqfvgGeWXFnHArFOFbRmtWxL93fxmZjF19Sg69I,13
74
- sgtlib-3.4.0.dist-info/RECORD,,
67
+ sgtlib-3.4.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
68
+ sgtlib-3.4.1.dist-info/METADATA,sha256=VPPMOWhLdj4PB4XvU5Gj5CN_-UJTQx-NJZw0Gv_UIGk,45278
69
+ sgtlib-3.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
70
+ sgtlib-3.4.1.dist-info/entry_points.txt,sha256=zY3v_U9Qi4L965BPrURXn84yF_TxdKDZI3VcTUbTh0M,120
71
+ sgtlib-3.4.1.dist-info/top_level.txt,sha256=daVVcqfvgGeWXFnHArFOFbRmtWxL93fxmZjF19Sg69I,13
72
+ sgtlib-3.4.1.dist-info/RECORD,,
@@ -1,61 +0,0 @@
1
- //#define _POSIX_C_SOURCE 200809L // Linux
2
- #include <stdio.h>
3
- #include <stdarg.h>
4
- #include <string.h>
5
- //#include "sgt_base.h"
6
- #include "include/sgt_base.h"
7
-
8
- // Function to compute Local Node Connectivity
9
- void* compute_lnc(void *arg) {
10
- ThreadArgsLNC *args = (ThreadArgsLNC*)arg;
11
- igraph_integer_t lnc;
12
-
13
- igraph_st_vertex_connectivity(args->graph, &lnc, args->i, args->j, IGRAPH_VCONN_NEI_NEGATIVE);
14
-
15
- // Update shared data under mutex lock
16
- pthread_mutex_lock(args->mutex);
17
- if (lnc != -1){
18
- *(args->total_nc) += lnc;
19
- *(args->total_count) += 1;
20
- //printf("got %d\n", lnc);
21
- //printf("NC:%d Count:%d \n", *(args->total_nc), *(args->total_count));
22
- }
23
- pthread_mutex_unlock(args->mutex);
24
-
25
- pthread_exit(NULL);
26
- }
27
-
28
- // Function to convert string representation of adjacency matrix to 2D matrix
29
- igraph_matrix_t* str_to_matrix(char* str_adj_mat, igraph_integer_t num_vertices) {
30
- // Allocate memory for the matrix
31
- igraph_matrix_t* mat = (igraph_matrix_t*)malloc(sizeof(igraph_matrix_t));
32
- if (!mat) {
33
- fprintf(stderr, "Failed to allocate memory for matrix structure\n");
34
- exit(EXIT_FAILURE);
35
- }
36
- igraph_matrix_init(mat, num_vertices, num_vertices);
37
-
38
- // Parse string and populate matrix
39
- char* token;
40
- char* nextToken;
41
- const char delimiters[] = ",";
42
-
43
- // Get the first token
44
- // strtok_r - MacOs
45
- //token = strtok_r(str_adj_mat, delimiters, &nextToken);
46
- token = strtok_s(str_adj_mat, delimiters, &nextToken);
47
-
48
- // Iterate through the remaining tokens
49
- for (igraph_integer_t i = 0; i < num_vertices; i++) {
50
- for (igraph_integer_t j = 0; j < num_vertices; j++) {
51
- MATRIX(*mat, i, j) = atoi(token);
52
- // Get the next token
53
- // strtok_r - MacOs
54
- //token = strtok_r(NULL, delimiters, &nextToken);
55
- token = strtok_s(NULL, delimiters, &nextToken);
56
- }
57
- }
58
-
59
- return mat;
60
- }
61
-
@@ -1,183 +0,0 @@
1
- #include <stdlib.h>
2
- #include <stdio.h>
3
- #include <stdarg.h>
4
-
5
- #define PY_SSIZE_T_CLEAN
6
- #include <Python.h>
7
- //#include "sgt_base.h"
8
- #include "include/sgt_base.h"
9
-
10
-
11
- static PyObject *ErrorObject;
12
- static PyObject *
13
- compute_anc(PyObject *self, PyObject *args)
14
- {
15
- int num_cpus;
16
- int allow_mp;
17
- char *f_name;
18
-
19
- // Consider passing graph as String
20
- if (!PyArg_ParseTuple(args, "sii:compute_anc", &f_name, &num_cpus, &allow_mp)){
21
- return NULL;
22
- }
23
-
24
- /*if ( f_name == ''){
25
- PyErr_SetString(ErrorObject, "Unable to retrieve graph.");
26
- return NULL;
27
- }*/
28
-
29
- if ( num_cpus <= 0 || allow_mp < 0){
30
- PyErr_SetString(ErrorObject, "Invalid CPU parameters.");
31
- return NULL;
32
- }
33
-
34
- // Declare required variables
35
- FILE *file;
36
-
37
- igraph_t graph;
38
- igraph_integer_t num_nodes;
39
- igraph_integer_t count_nc = 0;
40
- igraph_integer_t sum_nc = 0;
41
- igraph_real_t anc = 0;
42
-
43
- // Open the file containing the serialized graph
44
- file = fopen(f_name, "r");
45
- // Read the graph from the file
46
- igraph_read_graph_edgelist(&graph, file, 0, IGRAPH_UNDIRECTED);
47
- fclose(file);
48
- // printf("Nodes: %d\nEdges: %d\n", (int)igraph_vcount(&graph), (int)igraph_ecount(&graph));
49
-
50
- num_nodes = igraph_vcount(&graph);
51
- if (allow_mp == 0){
52
- printf("Using single processing\n");
53
- igraph_integer_t lnc;
54
- for (igraph_integer_t i=0; i<num_nodes; i++) {
55
- for (igraph_integer_t j=i+1; j<num_nodes; j++){
56
- igraph_st_vertex_connectivity(&graph, &lnc, i, j, IGRAPH_VCONN_NEI_NEGATIVE);
57
- if (lnc == -1) { continue; }
58
- sum_nc += lnc;
59
- count_nc += 1;
60
- }
61
- }
62
-
63
- }
64
- else {
65
- printf("Using multiprocessing\n");
66
- // Initialize mutex
67
- pthread_mutex_t mutex;
68
- pthread_mutex_init(&mutex, NULL);
69
-
70
- // Create thread pool
71
- const int MAX_THREAD_COUNT = num_cpus;
72
-
73
- // Allocate memory for threads and args arrays
74
- pthread_t *threads = (pthread_t *)malloc(MAX_THREAD_COUNT * sizeof(pthread_t));
75
- ThreadArgsLNC *args = (ThreadArgsLNC *)malloc(MAX_THREAD_COUNT * sizeof(ThreadArgsLNC));
76
-
77
- if (threads == NULL || args == NULL) {
78
- PyErr_SetString(ErrorObject, "Memory allocation failed\n");
79
- return NULL;
80
- }
81
-
82
- // Initialize thread pool
83
- for (int i = 0; i < MAX_THREAD_COUNT; i++) {
84
- args[i].graph = &graph;
85
- args[i].mutex = &mutex;
86
- args[i].total_nc = &sum_nc;
87
- args[i].total_count = &count_nc;
88
- }
89
-
90
- // Create threads for computing LNC
91
- int idx = 0;
92
- int thread_count = 0;
93
- for (igraph_integer_t i = 0; i < num_nodes; i++) {
94
- for (igraph_integer_t j = i + 1; j < num_nodes; j++) {
95
- idx = (int)(thread_count % MAX_THREAD_COUNT);
96
- if (thread_count >= MAX_THREAD_COUNT) {
97
- // Wait for a thread to finish before starting a new one
98
- pthread_join(threads[idx], NULL);
99
- thread_count++;
100
- }
101
- args[idx].i = (int)i;
102
- args[idx].j = (int)j;
103
- pthread_create(&threads[idx], NULL, compute_lnc, &args[idx]);
104
- thread_count++;
105
- // printf("thread %d running...\n", (idx));
106
- }
107
- }
108
-
109
- // Join threads
110
- for (int i = 0; i < MAX_THREAD_COUNT && i < thread_count; i++) {
111
- pthread_join(threads[i], NULL);
112
- }
113
-
114
- // Destroy mutex
115
- pthread_mutex_destroy(&mutex);
116
- // Free dynamically allocated memory
117
- free(threads);
118
- free(args);
119
- }
120
-
121
- // Compute ANC
122
- anc = (float) sum_nc / count_nc;
123
-
124
- // Destroy graph
125
- igraph_destroy(&graph);
126
-
127
- return PyFloat_FromDouble((double) anc);
128
-
129
- }
130
- static char compute_anc_doc[] =
131
- "A C method that uses iGraph library to compute average node connectivity of a graph.\n"
132
- "\n"
133
- "Args:\n"
134
- " file (string): CSV file with edge list of graph A.\n"
135
- " cpus (int): number of available CPUs.\n"
136
- " mp (int): allow multi-processing (0: No, 1: Yes).\n"
137
- "\n"
138
- "Returns:\n"
139
- " ANC (float): Average Node Connectivity as a float value.\n";
140
-
141
-
142
- static char sgt_doc[] =
143
- "A C language module leveraging the iGraph library to compute Graph Theory (GT) metrics,"
144
- "enhanced with multi-threading capabilities for accelerated computation.\n";
145
-
146
- /* Method Table: ist of functions defined in the module */
147
- static PyMethodDef sgt_methods[] = {
148
- {"compute_anc", compute_anc, METH_VARARGS, compute_anc_doc },
149
- //{"compute_lnc", compute_lnc, METH_VARARGS, "Compute local node connectivity." },
150
- {NULL, NULL, 0, NULL} /* Sentinel */
151
- };
152
-
153
- /* Create module */
154
- static struct PyModuleDef sgt_c_module = {
155
- PyModuleDef_HEAD_INIT,
156
- "sgt_c_module", /* name of module */
157
- sgt_doc, /* module documentation, may be NULL */
158
- -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
159
- sgt_methods
160
- };
161
-
162
- /* Initialization function for the module */
163
- PyMODINIT_FUNC
164
- PyInit_sgt_c_module(void)
165
- {
166
- PyObject *m;
167
-
168
- m = PyModule_Create(&sgt_c_module);
169
- if (m == NULL)
170
- return NULL;
171
-
172
- ErrorObject = PyErr_NewException("sgt_c_module.error", NULL, NULL);
173
- Py_XINCREF(ErrorObject);
174
- if (PyModule_AddObject(m, "error", ErrorObject) < 0) {
175
- Py_XDECREF(ErrorObject);
176
- Py_CLEAR(ErrorObject);
177
- Py_DECREF(m);
178
- return NULL;
179
- }
180
-
181
- return m;
182
- }
183
-
File without changes