InfluenceDiffusion 0.0.3__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.
@@ -0,0 +1,478 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from typing import List, Tuple, Union, Dict
4
+ import networkx as nx
5
+
6
+
7
+ class Graph(nx.DiGraph):
8
+ def __init__(self, edge_list: List[Tuple[int, int]],
9
+ directed: bool = True,
10
+ weights: Union[np.array, List[float]] = None):
11
+ """
12
+ Initialize a directed or undirected graph from an edge list.
13
+
14
+ Parameters
15
+ ----------
16
+ edge_list : List[Tuple[int, int]]
17
+ List of tuples (source, sink) representing edges of the graph.
18
+ directed : bool, optional
19
+ Flag to indicate whether the graph is directed (default is True).
20
+ weights : Union[None, List[float]], optional
21
+ Optional weights for each edge. If None, all edges are assigned a weight of 1.
22
+ """
23
+ self.directed = directed
24
+ self.edge_array = np.array(edge_list, dtype=int)
25
+
26
+ if not self.directed:
27
+ reverse_edge_array = self.edge_array[:, [1, 0]]
28
+ self.edge_array = np.concatenate([self.edge_array, reverse_edge_array])
29
+
30
+ super().__init__(self.edge_array.tolist())
31
+
32
+ if weights is not None:
33
+ assert len(weights) == len(edge_list), "number of edges is different from number of weights"
34
+ else:
35
+ weights = np.ones(len(edge_list))
36
+ self.weights = np.array(weights) if directed else np.hstack([weights] * 2)
37
+ self.edge_2_index = {tuple(edge): idx for idx, edge in enumerate(self.edge_array)}
38
+
39
+ def rename_graph_vertices(self, old_2_new_vertex_name_dict: Union[None, Dict] = None) -> "Graph":
40
+ """
41
+ Rename graph vertices according to the provided mapping or re-index them.
42
+
43
+ Parameters
44
+ ----------
45
+ old_2_new_vertex_name_dict : Union[None, Dict], optional
46
+ Mapping of old vertex names to new names.
47
+ If None, re-indexes vertices to [0, 1, ..., |V|-1].
48
+
49
+ Returns
50
+ -------
51
+ Graph
52
+ The updated graph instance with renamed vertices.
53
+ """
54
+ if old_2_new_vertex_name_dict is None:
55
+ old_vertex_names = sorted(list(self.get_vertices()))
56
+ old_2_new_vertex_name_dict = {name: idx for idx, name in enumerate(old_vertex_names)}
57
+
58
+ self.edge_array[:, 0] = list(map(lambda v: old_2_new_vertex_name_dict[v], self.edge_array[:, 0]))
59
+ self.edge_array[:, 1] = list(map(lambda v: old_2_new_vertex_name_dict[v], self.edge_array[:, 1]))
60
+ return self
61
+
62
+ def set_weights(self, weights: Union[str, np.array]) -> "Graph":
63
+ """
64
+ Set weights for the edges in the graph.
65
+
66
+ Parameters
67
+ ----------
68
+ weights : Union[str, np.array]
69
+ Array of weights for the edges.
70
+
71
+ Returns
72
+ -------
73
+ Graph
74
+ The current graph instance with updated weights.
75
+
76
+ Raises
77
+ ------
78
+ AssertionError
79
+ If the number of weights does not match the number of edges.
80
+ """
81
+ assert len(weights) == self.count_edges(), "number of weights different from the number of edges"
82
+ self.weights = np.array(weights)
83
+ return self
84
+
85
+ def get_edges(self, as_array: bool = False) -> Union[np.array, List[Tuple[int, int]]]:
86
+ """
87
+ Retrieve the edges of the graph.
88
+
89
+ Parameters
90
+ ----------
91
+ as_array : bool, optional
92
+ If True, returns edges as a numpy array. If False, returns as a list of tuples (default is False).
93
+
94
+ Returns
95
+ -------
96
+ Union[np.array, List[Tuple[int, int]]]
97
+ The edges of the graph in the specified format.
98
+ """
99
+ if as_array:
100
+ return self.edge_array
101
+ return [tuple(edge) for edge in self.edge_array]
102
+
103
+ def get_vertices(self) -> set:
104
+ """
105
+ Get the set of all vertices in the graph.
106
+
107
+ Returns
108
+ -------
109
+ set
110
+ A set containing all vertex identifiers.
111
+ """
112
+ return set(self.nodes)
113
+
114
+ def get_sources(self) -> set:
115
+ """
116
+ Get the set of source vertices in the graph.
117
+
118
+ Returns
119
+ -------
120
+ set
121
+ A set containing all source vertices.
122
+ """
123
+ return set(self.edge_array[:, 0])
124
+
125
+ def get_sinks(self) -> set:
126
+ """
127
+ Get the set of sink vertices in the graph.
128
+
129
+ Returns
130
+ -------
131
+ set
132
+ A set containing all sink vertices.
133
+ """
134
+ return set(self.edge_array[:, 1])
135
+
136
+ def count_edges(self) -> int:
137
+ """
138
+ Count the number of edges in the graph.
139
+
140
+ Returns
141
+ -------
142
+ int
143
+ The total number of edges.
144
+ """
145
+ return len(self.edge_array)
146
+
147
+ def count_vertices(self) -> int:
148
+ """
149
+ Count the number of vertices in the graph.
150
+
151
+ Returns
152
+ -------
153
+ int
154
+ The total number of vertices.
155
+ """
156
+ return len(self.get_vertices())
157
+
158
+ def get_children_mask(self, vertex: int) -> np.array:
159
+ """
160
+ Create a mask for edges originating from a specified vertex.
161
+
162
+ Parameters
163
+ ----------
164
+ vertex : int
165
+ The vertex for which to find children.
166
+
167
+ Returns
168
+ -------
169
+ np.array
170
+ A boolean array mask indicating edges originating from the vertex.
171
+ """
172
+ return self.edge_array[:, 0] == vertex
173
+
174
+ def get_children(self, vertex: int, out_type: Union[set, list, np.array] = set) -> Union[set, list, np.array]:
175
+ """
176
+ Get the children (outgoing edges) of a specified vertex.
177
+
178
+ Parameters
179
+ ----------
180
+ vertex : int
181
+ The vertex for which to find children.
182
+ out_type : Union[set, list, np.array], optional
183
+ The type of output to return (default is set).
184
+
185
+ Returns
186
+ -------
187
+ Union[set, list, np.array]
188
+ The children of the vertex in the specified output type.
189
+ """
190
+ return out_type(self.edge_array[:, 1][self.get_children_mask(vertex)])
191
+
192
+ def get_parents_mask(self, vertex: int) -> np.array:
193
+ """
194
+ Create a mask for edges leading to a specified vertex.
195
+
196
+ Parameters
197
+ ----------
198
+ vertex : int
199
+ The vertex for which to find parents.
200
+
201
+ Returns
202
+ -------
203
+ np.array
204
+ A boolean array mask indicating edges leading to the vertex.
205
+ """
206
+ return self.edge_array[:, 1] == vertex
207
+
208
+ def get_parents(self, vertex: int, out_type: Union[set, list, np.array] = set) -> Union[set, list, np.array]:
209
+ """
210
+ Get the parents (incoming edges) of a specified vertex.
211
+
212
+ Parameters
213
+ ----------
214
+ vertex : int
215
+ The vertex for which to find parents.
216
+ out_type : Union[set, list, np.array], optional
217
+ The type of output to return (default is set).
218
+
219
+ Returns
220
+ -------
221
+ Union[set, list, np.array]
222
+ The parents of the vertex in the specified output type.
223
+ """
224
+ return out_type(self.edge_array[:, 0][self.get_parents_mask(vertex)])
225
+
226
+ def get_vertex_2_indegree_dict(self, weighted: bool = False) -> Dict[int, Union[float, int]]:
227
+ """
228
+ Get a dictionary mapping vertices to their indegrees.
229
+
230
+ Parameters
231
+ ----------
232
+ weighted : bool, optional
233
+ If True, returns weighted indegrees; otherwise, returns simple counts (default is False).
234
+
235
+ Returns
236
+ -------
237
+ Dict[int, Union[float, int]]
238
+ A dictionary where keys are vertex identifiers and values are their indegrees.
239
+ """
240
+ return {vertex: self.get_indegree(vertex, weighted=weighted) for vertex in self.get_vertices()}
241
+
242
+ def get_outdegree(self, vertex: int, weighted: bool = False) -> Union[float, int]:
243
+ """
244
+ Get the outdegree of a specified vertex.
245
+
246
+ Parameters
247
+ ----------
248
+ vertex : int
249
+ The vertex for which to find the outdegree.
250
+ weighted : bool, optional
251
+ If True, returns the weighted outdegree; otherwise, returns simple counts (default is False).
252
+
253
+ Returns
254
+ -------
255
+ Union[float, int]
256
+ The outdegree of the vertex.
257
+ """
258
+ children_weights = self.weights[self.edge_array[:, 0] == vertex]
259
+ return np.sum(children_weights) if weighted else len(children_weights)
260
+
261
+ def get_all_outdegrees(self, weighted: bool = False) -> np.array:
262
+ """
263
+ Get outdegrees for all vertices in the graph.
264
+
265
+ Parameters
266
+ ----------
267
+ weighted : bool, optional
268
+ If True, returns weighted outdegrees; otherwise, returns simple counts (default is False).
269
+
270
+ Returns
271
+ -------
272
+ np.array
273
+ An array of outdegrees for all vertices.
274
+ """
275
+ return np.array([self.get_outdegree(v, weighted=weighted) for v in self.get_vertices()])
276
+
277
+ def get_avg_outdegree(self, weighted: bool = False) -> Union[float, int]:
278
+ """
279
+ Calculate the average outdegree of all vertices.
280
+
281
+ Parameters
282
+ ----------
283
+ weighted : bool, optional
284
+ If True, uses weighted outdegrees; otherwise, uses simple counts (default is False).
285
+
286
+ Returns
287
+ -------
288
+ Union[float, int]
289
+ The average outdegree of the vertices.
290
+ """
291
+ return np.mean(self.get_all_outdegrees(weighted=weighted))
292
+
293
+ def get_max_outdegree(self, weighted: bool = False) -> Union[float, int]:
294
+ """
295
+ Get the maximum outdegree among all vertices.
296
+
297
+ Parameters
298
+ ----------
299
+ weighted : bool, optional
300
+ If True, considers weighted outdegrees; otherwise, uses simple counts (default is False).
301
+
302
+ Returns
303
+ -------
304
+ Union[float, int]
305
+ The maximum outdegree.
306
+ """
307
+ return np.max(self.get_all_outdegrees(weighted))
308
+
309
+ def get_indegree(self, vertex: int, weighted: bool = False) -> Union[float, int]:
310
+ """
311
+ Get the indegree of a specified vertex.
312
+
313
+ Parameters
314
+ ----------
315
+ vertex : int
316
+ The vertex for which to find the indegree.
317
+ weighted : bool, optional
318
+ If True, returns the weighted indegree; otherwise, returns simple counts (default is False).
319
+
320
+ Returns
321
+ -------
322
+ Union[float, int]
323
+ The indegree of the vertex.
324
+ """
325
+ parent_weights = self.weights[self.edge_array[:, 1] == vertex]
326
+ return np.sum(parent_weights) if weighted else len(parent_weights)
327
+
328
+ def get_indegrees_dict(self, weighted: bool = False) -> Dict[int, Union[int, float]]:
329
+ """
330
+ Get a mapping from vertex to its indegree for all vertices in the graph.
331
+
332
+ Parameters
333
+ ----------
334
+ weighted : bool, optional
335
+ If True, returns weighted indegrees; otherwise, returns simple counts (default is False).
336
+
337
+ Returns
338
+ -------
339
+ np.array
340
+ An array of indegrees for all vertices.
341
+ """
342
+ return {v: self.get_indegree(v, weighted=weighted) for v in self.get_vertices()}
343
+
344
+ def get_all_indegrees(self, weighted: bool = False) -> np.array:
345
+ """
346
+ Get indegrees for all vertices in the graph.
347
+
348
+ Parameters
349
+ ----------
350
+ weighted : bool, optional
351
+ If True, returns weighted indegrees; otherwise, returns simple counts (default is False).
352
+
353
+ Returns
354
+ -------
355
+ np.array
356
+ An array of indegrees for all vertices.
357
+ """
358
+ indegrees_dict = self.get_indegrees_dict(weighted=weighted)
359
+ return np.array(list(indegrees_dict.values()))
360
+
361
+ def get_max_indegree(self, weighted: bool = False) -> Union[int, float]:
362
+ """
363
+ Get the maximum indegree among all vertices.
364
+
365
+ Parameters
366
+ ----------
367
+ weighted : bool, optional
368
+ If True, considers weighted indegrees; otherwise, uses simple counts (default is False).
369
+
370
+ Returns
371
+ -------
372
+ Union[int, float]
373
+ The maximum indegree.
374
+ """
375
+ return np.max(self.get_all_indegrees(weighted))
376
+
377
+ def get_avg_indegree(self, weighted: bool = False) -> float:
378
+ """
379
+ Calculate the average indegree of all vertices.
380
+
381
+ Parameters
382
+ ----------
383
+ weighted : bool, optional
384
+ If True, uses weighted indegrees; otherwise, uses simple counts (default is False).
385
+
386
+ Returns
387
+ -------
388
+ float
389
+ The average indegree of the vertices.
390
+ """
391
+ return np.mean(self.get_all_indegrees(weighted))
392
+
393
+ def get_edge_index(self, edge: Tuple[int, int]) -> int:
394
+ """
395
+ Get the index of a specified edge.
396
+
397
+ Parameters
398
+ ----------
399
+ edge : Tuple[int, int]
400
+ The edge for which to find the index.
401
+
402
+ Returns
403
+ -------
404
+ int
405
+ The index of the edge in the graph.
406
+ """
407
+ return self.edge_2_index[tuple(edge)]
408
+
409
+ def get_edge_weight(self, edge: Tuple[int, int]) -> float:
410
+ """
411
+ Get the weight of a specified edge.
412
+
413
+ Parameters
414
+ ----------
415
+ edge : Tuple[int, int]
416
+ The edge for which to retrieve the weight.
417
+
418
+ Returns
419
+ -------
420
+ float
421
+ The weight of the edge.
422
+ """
423
+ return self.weights[self.get_edge_index(edge)]
424
+
425
+ def get_edges_mask_from_set_to_vertex(self, vertex_set: set, vertex: int) -> np.array:
426
+ """
427
+ Create a mask for edges leading from a set of vertices to a specified vertex.
428
+
429
+ Parameters
430
+ ----------
431
+ vertex_set : set
432
+ A set of source vertices.
433
+ vertex : int
434
+ The target vertex.
435
+
436
+ Returns
437
+ -------
438
+ np.array
439
+ A boolean array mask indicating which edges lead from vertices in vertex_set to the specified vertex.
440
+ """
441
+ parent_edges_mask = self.get_parents_mask(vertex)
442
+ parent_vertices = self.edge_array[:, 0][parent_edges_mask]
443
+ parent_edges_mask[parent_edges_mask] = [(parent in vertex_set) for parent in parent_vertices]
444
+ return parent_edges_mask
445
+
446
+ def get_adj_matrix(self) -> np.array:
447
+ """
448
+ Generate the adjacency matrix of the graph.
449
+
450
+ Returns
451
+ -------
452
+ np.array
453
+ The adjacency matrix as a numpy array.
454
+ """
455
+ vertices = list(self.get_vertices())
456
+ n_vertex = len(vertices)
457
+ adj_matrix = pd.DataFrame(np.zeros((n_vertex, n_vertex)), columns=vertices, index=vertices)
458
+ for (v1, v2), weight in zip(self.edge_array, self.weights):
459
+ adj_matrix.at[v1, v2] = weight
460
+
461
+ return adj_matrix
462
+
463
+ def is_edge_in_graph(self, edge: Tuple[int, int]) -> bool:
464
+ """
465
+ Check if a specified edge exists in the graph.
466
+
467
+ Parameters
468
+ ----------
469
+ edge : Tuple[int, int]
470
+ The edge to check.
471
+
472
+ Returns
473
+ -------
474
+ bool
475
+ True if the edge exists in the graph, False otherwise.
476
+ """
477
+ source, sink = edge
478
+ return sink in self.get_children(source)