modularitypruning 2.0.0__py3-none-any.whl → 2.0.2__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.
- modularitypruning/champ_utilities.py +62 -61
- modularitypruning/parameter_estimation_utilities.py +26 -21
- modularitypruning/plotting.py +1 -1
- modularitypruning/types.py +4 -0
- {modularitypruning-2.0.0.dist-info → modularitypruning-2.0.2.dist-info}/METADATA +4 -4
- modularitypruning-2.0.2.dist-info/RECORD +15 -0
- {modularitypruning-2.0.0.dist-info → modularitypruning-2.0.2.dist-info}/WHEEL +1 -1
- modularitypruning-2.0.0.dist-info/RECORD +0 -15
- {modularitypruning-2.0.0.dist-info → modularitypruning-2.0.2.dist-info}/licenses/LICENSE +0 -0
- {modularitypruning-2.0.0.dist-info → modularitypruning-2.0.2.dist-info}/top_level.txt +0 -0
|
@@ -35,68 +35,69 @@ def get_interior_point(
|
|
|
35
35
|
# We suppress these two warnings to avoid cluttering output, some of these warnings are expected as the result is
|
|
36
36
|
# converged to and we've checked the consistency of results in our own tests. Moreover, we explicitly check the
|
|
37
37
|
# interior point's validity prior to returning.
|
|
38
|
-
warnings.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
raise ValueError(f"get_interior_point received unhandled dimension {normals.shape[1]}!")
|
|
51
|
-
|
|
52
|
-
num_retries: int = 0
|
|
53
|
-
|
|
54
|
-
while True: # retry until success
|
|
55
|
-
# randomly sample some of the halfspaces
|
|
56
|
-
sample_len: int = min(initial_num_sampled, len(interior_hs)) # len(interior_hs)
|
|
57
|
-
sampled_hs: FloatArray = np.vstack((
|
|
58
|
-
interior_hs[choice(interior_hs.shape[0], sample_len, replace=False)], boundaries))
|
|
59
|
-
|
|
60
|
-
# compute the Chebyshev center of the sampled halfspaces' intersection
|
|
61
|
-
norm_vector: FloatArray = np.reshape(
|
|
62
|
-
np.linalg.norm(sampled_hs[:, :-1], axis=1), (sampled_hs.shape[0], 1))
|
|
63
|
-
c: FloatArray = np.zeros((sampled_hs.shape[1],))
|
|
64
|
-
c[-1] = -1
|
|
65
|
-
A: FloatArray = np.hstack((sampled_hs[:, :-1], norm_vector))
|
|
66
|
-
b: FloatArray = -sampled_hs[:, -1:]
|
|
67
|
-
|
|
68
|
-
res = linprog(c, A_ub=A, b_ub=b, bounds=(-np.inf, np.inf), method='highs-ipm')
|
|
69
|
-
|
|
70
|
-
# check solution if optimization succeeded, even with difficulties
|
|
71
|
-
# res.status codes
|
|
72
|
-
# 0 : Optimization terminated successfully.
|
|
73
|
-
# 1 : Iteration limit reached.
|
|
74
|
-
# 2 : Problem appears to be infeasible.
|
|
75
|
-
# 3 : Problem appears to be unbounded.
|
|
76
|
-
# 4 : Numerical difficulties encountered.
|
|
77
|
-
if res.success or res.status in {0, 1, 4}:
|
|
78
|
-
if res.x is None:
|
|
79
|
-
# satisfying mypy, this should never occur
|
|
80
|
-
raise ValueError(f"Scipy's linprog claimed success but returned no solution! {res}")
|
|
81
|
-
intpt: FloatArray = res.x[:-1] # res.x contains [interior_point, distance to enclosing polyhedron]
|
|
82
|
-
|
|
83
|
-
# ensure that the computed point is actually interior to all halfspaces
|
|
84
|
-
if (np.dot(normals, intpt) + np.transpose(offsets) < 0).all():
|
|
85
|
-
break
|
|
86
|
-
elif res.status in {2, 3}:
|
|
87
|
-
# if we ever fail, the linear program seems impossible
|
|
88
|
-
raise ValueError("get_interior_point problem is impossible or degenerate!")
|
|
89
|
-
|
|
90
|
-
if num_retries > full_retry_limit:
|
|
91
|
-
raise ValueError("get_interior_point is unable to find a well-conditioned solution, "
|
|
92
|
-
"but the problem does not appear impossible or degenerate?")
|
|
93
|
-
|
|
94
|
-
if initial_num_sampled >= len(interior_hs):
|
|
95
|
-
num_retries += 1
|
|
38
|
+
with warnings.catch_warnings(): # revert our filters once we're done
|
|
39
|
+
warnings.filterwarnings("ignore", category=LinAlgWarning)
|
|
40
|
+
warnings.filterwarnings("ignore", category=OptimizeWarning)
|
|
41
|
+
|
|
42
|
+
normals, offsets = np.split(halfspaces, [-1], axis=1)
|
|
43
|
+
|
|
44
|
+
# in our singlelayer case, the last four halfspaces are boundary halfspaces (a square)
|
|
45
|
+
# for multilayer, the last six are boundaries (a cube)
|
|
46
|
+
if normals.shape[1] == 2:
|
|
47
|
+
interior_hs, boundaries = np.split(halfspaces, [-4], axis=0)
|
|
48
|
+
elif normals.shape[1] == 3:
|
|
49
|
+
interior_hs, boundaries = np.split(halfspaces, [-6], axis=0)
|
|
96
50
|
else:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
51
|
+
raise ValueError(f"get_interior_point received unhandled dimension {normals.shape[1]}!")
|
|
52
|
+
|
|
53
|
+
num_retries: int = 0
|
|
54
|
+
|
|
55
|
+
while True: # retry until success
|
|
56
|
+
# randomly sample some of the halfspaces
|
|
57
|
+
sample_len: int = min(initial_num_sampled, len(interior_hs)) # len(interior_hs)
|
|
58
|
+
sampled_hs: FloatArray = np.vstack((
|
|
59
|
+
interior_hs[choice(interior_hs.shape[0], sample_len, replace=False)], boundaries))
|
|
60
|
+
|
|
61
|
+
# compute the Chebyshev center of the sampled halfspaces' intersection
|
|
62
|
+
norm_vector: FloatArray = np.reshape(
|
|
63
|
+
np.linalg.norm(sampled_hs[:, :-1], axis=1), (sampled_hs.shape[0], 1))
|
|
64
|
+
c: FloatArray = np.zeros((sampled_hs.shape[1],))
|
|
65
|
+
c[-1] = -1
|
|
66
|
+
A: FloatArray = np.hstack((sampled_hs[:, :-1], norm_vector))
|
|
67
|
+
b: FloatArray = -sampled_hs[:, -1:]
|
|
68
|
+
|
|
69
|
+
res = linprog(c, A_ub=A, b_ub=b, bounds=(-np.inf, np.inf), method='highs-ipm')
|
|
70
|
+
|
|
71
|
+
# check solution if optimization succeeded, even with difficulties
|
|
72
|
+
# res.status codes
|
|
73
|
+
# 0 : Optimization terminated successfully.
|
|
74
|
+
# 1 : Iteration limit reached.
|
|
75
|
+
# 2 : Problem appears to be infeasible.
|
|
76
|
+
# 3 : Problem appears to be unbounded.
|
|
77
|
+
# 4 : Numerical difficulties encountered.
|
|
78
|
+
if res.success or res.status in {0, 1, 4}:
|
|
79
|
+
if res.x is None:
|
|
80
|
+
# satisfying mypy, this should never occur
|
|
81
|
+
raise ValueError(f"Scipy's linprog claimed success but returned no solution! {res}")
|
|
82
|
+
intpt: FloatArray = res.x[:-1] # res.x contains [interior_point, distance to enclosing polyhedron]
|
|
83
|
+
|
|
84
|
+
# ensure that the computed point is actually interior to all halfspaces
|
|
85
|
+
if (np.dot(normals, intpt) + np.transpose(offsets) < 0).all():
|
|
86
|
+
break
|
|
87
|
+
elif res.status in {2, 3}:
|
|
88
|
+
# if we ever fail, the linear program seems impossible
|
|
89
|
+
raise ValueError("get_interior_point problem is impossible or degenerate!")
|
|
90
|
+
|
|
91
|
+
if num_retries > full_retry_limit:
|
|
92
|
+
raise ValueError("get_interior_point is unable to find a well-conditioned solution, "
|
|
93
|
+
"but the problem does not appear impossible or degenerate?")
|
|
94
|
+
|
|
95
|
+
if initial_num_sampled >= len(interior_hs):
|
|
96
|
+
num_retries += 1
|
|
97
|
+
else:
|
|
98
|
+
initial_num_sampled *= 2 # try again and sample more halfspaces this time
|
|
99
|
+
|
|
100
|
+
return intpt
|
|
100
101
|
|
|
101
102
|
|
|
102
103
|
def CHAMP_2D(
|
|
@@ -13,7 +13,7 @@ from .partition_utilities import num_communities
|
|
|
13
13
|
from .types import (Domain, DomainWithEstimates, FloatArray, GammaEstimate,
|
|
14
14
|
GammaEstimateValue, GammaOmegaEstimate, GammaRange, GammaUpdateFunc, LayerVector,
|
|
15
15
|
LeidenPartition, Membership, MultilayerSBMParameters, OmegaUpdateFunc,
|
|
16
|
-
PersistenceFunc, SinglelayerSBMParameters)
|
|
16
|
+
PersistenceFunc, PrunablePartitions, SinglelayerSBMParameters)
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def estimate_singlelayer_SBM_parameters(
|
|
@@ -353,7 +353,7 @@ def omega_function_from_model(
|
|
|
353
353
|
:type model: str
|
|
354
354
|
:param omega_max: maximum allowed value for omega
|
|
355
355
|
:type omega_max: float
|
|
356
|
-
:param T: number of layers in input graph
|
|
356
|
+
:param T: number of layers in input graph
|
|
357
357
|
:type T: int
|
|
358
358
|
:return: an "update_omega" function
|
|
359
359
|
:rtype: function
|
|
@@ -575,7 +575,6 @@ def gamma_omega_estimates_to_stable_partitions(
|
|
|
575
575
|
|
|
576
576
|
for polyverts, membership, gamma_est, omega_est in domains_with_estimates:
|
|
577
577
|
if gamma_est is None or omega_est is None:
|
|
578
|
-
print(gamma_est, omega_est)
|
|
579
578
|
continue
|
|
580
579
|
|
|
581
580
|
centroid_x: float = np.mean([x[0] for x in polyverts])
|
|
@@ -604,7 +603,7 @@ def gamma_omega_estimates_to_stable_partitions(
|
|
|
604
603
|
|
|
605
604
|
|
|
606
605
|
def prune_to_stable_partitions(
|
|
607
|
-
G: ig.Graph, parts:
|
|
606
|
+
G: ig.Graph, parts: PrunablePartitions,
|
|
608
607
|
gamma_start: float, gamma_end: float,
|
|
609
608
|
restrict_num_communities: int | None = None,
|
|
610
609
|
single_threaded: bool = False
|
|
@@ -620,7 +619,7 @@ def prune_to_stable_partitions(
|
|
|
620
619
|
:param G: graph of interest
|
|
621
620
|
:type G: igraph.Graph
|
|
622
621
|
:param parts: partitions to prune
|
|
623
|
-
:type parts: set
|
|
622
|
+
:type parts: list or set of membership tuples or leidenalg.RBConfigurationVertexPartitions
|
|
624
623
|
:param gamma_start: starting gamma value for CHAMP
|
|
625
624
|
:type gamma_start: float
|
|
626
625
|
:param gamma_end: ending gamma value for CHAMP
|
|
@@ -633,7 +632,7 @@ def prune_to_stable_partitions(
|
|
|
633
632
|
:rtype: list[tuple[int, ...]]
|
|
634
633
|
"""
|
|
635
634
|
|
|
636
|
-
if not G.is_connected():
|
|
635
|
+
if not G.is_connected(mode='weak'):
|
|
637
636
|
warnings.warn("The pruning pipeline has not been thoroughly tested on disconnected graphs. If you run into "
|
|
638
637
|
"problems, consider using the largest connected component of your graph.")
|
|
639
638
|
|
|
@@ -643,12 +642,15 @@ def prune_to_stable_partitions(
|
|
|
643
642
|
if sum(G.es['weight']) <= 0:
|
|
644
643
|
raise ValueError("Total edge weight must be positive.")
|
|
645
644
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
645
|
+
canonical_parts: set[Membership] = set()
|
|
646
|
+
for part in parts:
|
|
647
|
+
if isinstance(part, leidenalg.RBConfigurationVertexPartition):
|
|
648
|
+
# convert to (canonically represented) membership vectors if necessary
|
|
649
|
+
canonical_parts.add(sorted_tuple(tuple(part.membership)))
|
|
650
|
+
else:
|
|
651
|
+
# assume parts contains membership vectors
|
|
652
|
+
canonical_parts.add(sorted_tuple(part))
|
|
653
|
+
parts = canonical_parts
|
|
652
654
|
|
|
653
655
|
if restrict_num_communities is not None:
|
|
654
656
|
parts = {part for part in parts if num_communities(part) == restrict_num_communities}
|
|
@@ -668,7 +670,7 @@ def prune_to_stable_partitions(
|
|
|
668
670
|
def prune_to_multilayer_stable_partitions(
|
|
669
671
|
G_intralayer: ig.Graph, G_interlayer: ig.Graph,
|
|
670
672
|
layer_vec: LayerVector, model: str,
|
|
671
|
-
parts:
|
|
673
|
+
parts: PrunablePartitions, gamma_start: float, gamma_end: float,
|
|
672
674
|
omega_start: float, omega_end: float,
|
|
673
675
|
restrict_num_communities: int | None = None, single_threaded: bool = False
|
|
674
676
|
) -> list[Membership]:
|
|
@@ -713,7 +715,7 @@ def prune_to_multilayer_stable_partitions(
|
|
|
713
715
|
:param model: network layer topology (temporal, multilevel, multiplex)
|
|
714
716
|
:type model: str
|
|
715
717
|
:param parts: partitions to prune
|
|
716
|
-
:type parts: set
|
|
718
|
+
:type parts: list or set of membership tuples or leidenalg.RBConfigurationVertexPartitions
|
|
717
719
|
:param gamma_start: starting gamma value for CHAMP
|
|
718
720
|
:type gamma_start: float
|
|
719
721
|
:param gamma_end: ending gamma value for CHAMP
|
|
@@ -736,7 +738,7 @@ def prune_to_multilayer_stable_partitions(
|
|
|
736
738
|
# check graph is connected (cannot just check intra/interlayer graphs since they will both be disconnected)
|
|
737
739
|
G_combined: ig.Graph = ig.Graph(edges=[(e.source, e.target) for e in G_intralayer.es] +
|
|
738
740
|
[(e.source, e.target) for e in G_interlayer.es])
|
|
739
|
-
if not G_combined.is_connected() or G_combined.vcount() != G_interlayer.vcount():
|
|
741
|
+
if not G_combined.is_connected(mode='weak') or G_combined.vcount() != G_interlayer.vcount():
|
|
740
742
|
warnings.warn("The pruning pipeline has not been thoroughly tested on disconnected graphs. If you run into "
|
|
741
743
|
"problems, consider using the largest connected component of your graph.")
|
|
742
744
|
|
|
@@ -751,12 +753,15 @@ def prune_to_multilayer_stable_partitions(
|
|
|
751
753
|
warnings.warn("Parameter estimation treats interlayer edges as unweighted and will proceed as though all "
|
|
752
754
|
"interlayer weights are 1.0, but CHAMP will still use the actual interlayer weights!")
|
|
753
755
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
756
|
+
canonical_parts: set[Membership] = set()
|
|
757
|
+
for part in parts:
|
|
758
|
+
if isinstance(part, leidenalg.RBConfigurationVertexPartition):
|
|
759
|
+
# convert to (canonically represented) membership vectors if necessary
|
|
760
|
+
canonical_parts.add(sorted_tuple(tuple(part.membership)))
|
|
761
|
+
else:
|
|
762
|
+
# assume parts contains membership vectors
|
|
763
|
+
canonical_parts.add(sorted_tuple(part))
|
|
764
|
+
parts = canonical_parts
|
|
760
765
|
|
|
761
766
|
if restrict_num_communities is not None:
|
|
762
767
|
parts = {part for part in parts if num_communities(part) == restrict_num_communities}
|
modularitypruning/plotting.py
CHANGED
|
@@ -89,7 +89,7 @@ def plot_2d_domains(
|
|
|
89
89
|
) -> None:
|
|
90
90
|
"""Plot partition dominance ranges in the (gamma, omega) plane, using the domains from CHAMP_3D.
|
|
91
91
|
|
|
92
|
-
Limits output to xlim and ylim dimensions. Note that the plotting here has x=
|
|
92
|
+
Limits output to xlim and ylim dimensions. Note that the plotting here has x=omega and y=gamma by default.
|
|
93
93
|
|
|
94
94
|
:param domains: CHAMP domains as returned from :meth:`~modularitypruning.champ_utilities.CHAMP_3D`.
|
|
95
95
|
:param xlim: plotting x limits
|
modularitypruning/types.py
CHANGED
|
@@ -16,6 +16,7 @@ IntSequence = Sequence[int] | IntArray
|
|
|
16
16
|
FloatSequence = Sequence[float] | FloatArray
|
|
17
17
|
|
|
18
18
|
# community membership vector and layer membership vector
|
|
19
|
+
# note: some parts of the code do actually support list[int] for membership, but this is simpler and more consistent
|
|
19
20
|
Membership = tuple[int, ...]
|
|
20
21
|
LayerVector = list[int]
|
|
21
22
|
LayerTuple = tuple[int, ...] # needed for hashing
|
|
@@ -29,6 +30,9 @@ MultilayerLeidenParts: TypeAlias = tuple[list[LeidenPartition], leidenalg.CPMVer
|
|
|
29
30
|
SingleLayerLeidenResult: TypeAlias = LeidenPartition | Membership
|
|
30
31
|
MultilayerLeidenResult: TypeAlias = list[LeidenPartition] | Membership
|
|
31
32
|
|
|
33
|
+
# collections of partitions for pruning (membership vectors or leiden partitions)
|
|
34
|
+
PrunablePartitions = Sequence[LeidenPartition | Membership] | set[LeidenPartition | Membership]
|
|
35
|
+
|
|
32
36
|
# CHAMP 2D domains and parameter estimates
|
|
33
37
|
GammaEstimateValue = np.float64 | None
|
|
34
38
|
GammaEstimate = tuple[np.float64, np.float64, Membership, GammaEstimateValue]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modularitypruning
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Pruning tool to identify small subsets of network partitions that are significant from the perspective of stochastic block model inference.
|
|
5
5
|
Author-email: Ryan Gibson <ryan.alex.gibson@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -105,6 +105,6 @@ which is the stable 4-community split of the Karate Club network.
|
|
|
105
105
|
|
|
106
106
|
## More Information
|
|
107
107
|
|
|
108
|
-
The [issues
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
The [issues](https://github.com/ragibson/ModularityPruning/issues) and
|
|
109
|
+
[figure generation runtimes README](figure_generation_runtimes.md) may also be
|
|
110
|
+
of interest.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
modularitypruning/__init__.py,sha256=eYablsXHzunDPuF96E3PFk3Zkmq_4jlG2p1wtC_Rxdk,397
|
|
2
|
+
modularitypruning/champ_utilities.py,sha256=KHHAOJjotD1EmCRESchOgdPiMZtDaohFJrZSXUzBHs0,19451
|
|
3
|
+
modularitypruning/leiden_utilities.py,sha256=ZTEklT0hmqDqAmZW85kkVq-zqjnFABbwFUt0IFzELB4,13376
|
|
4
|
+
modularitypruning/louvain_utilities.py,sha256=gRRXTLbxYP6z2FMN5F1BjGv2vsC0Cg5qezLUYlAJMRs,1184
|
|
5
|
+
modularitypruning/parameter_estimation.py,sha256=nMq2qzDIlPDKzAos3_eJzNKayrjUDC28s4MrhnoFgJw,11541
|
|
6
|
+
modularitypruning/parameter_estimation_utilities.py,sha256=DWWZHrwMHHzC5q6wlacAoYAw4fkwmDTsNu-4ZGyT0HM,35961
|
|
7
|
+
modularitypruning/partition_utilities.py,sha256=_QAFKPUQlt-v_D7fydu-CbPvFZM9lw3qpxfX09Gv7Zw,1914
|
|
8
|
+
modularitypruning/plotting.py,sha256=2pwyQQweObJ86dAVySwpLzpHXn746k7nBLJLQ-2Dghs,12260
|
|
9
|
+
modularitypruning/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
modularitypruning/types.py,sha256=Y0PdwtI_0lsolXo38brelOzH7Xdr0bPBqJxr6NXoABc,2740
|
|
11
|
+
modularitypruning-2.0.2.dist-info/licenses/LICENSE,sha256=eWz3HIQQxg7p1iSpUOUDKdDhGcuMPuVDDlcXf9F12D8,1068
|
|
12
|
+
modularitypruning-2.0.2.dist-info/METADATA,sha256=SHmgeE2J6CEYSDDxEoaffURESRp7dTsZzkDKSyFLB7o,3928
|
|
13
|
+
modularitypruning-2.0.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
14
|
+
modularitypruning-2.0.2.dist-info/top_level.txt,sha256=ZPOx3a-ek0Ge0ZMq-uvbySSaAL9MZ-t23-JkuHZXo9E,18
|
|
15
|
+
modularitypruning-2.0.2.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
modularitypruning/__init__.py,sha256=eYablsXHzunDPuF96E3PFk3Zkmq_4jlG2p1wtC_Rxdk,397
|
|
2
|
-
modularitypruning/champ_utilities.py,sha256=ak3qklwqS_rLX3J4qK2fA05hjBuBTMmUWR3-DDaurDw,19173
|
|
3
|
-
modularitypruning/leiden_utilities.py,sha256=ZTEklT0hmqDqAmZW85kkVq-zqjnFABbwFUt0IFzELB4,13376
|
|
4
|
-
modularitypruning/louvain_utilities.py,sha256=gRRXTLbxYP6z2FMN5F1BjGv2vsC0Cg5qezLUYlAJMRs,1184
|
|
5
|
-
modularitypruning/parameter_estimation.py,sha256=nMq2qzDIlPDKzAos3_eJzNKayrjUDC28s4MrhnoFgJw,11541
|
|
6
|
-
modularitypruning/parameter_estimation_utilities.py,sha256=BtnlzLm8JcCL8suDUXf5m-ShCosrOFgAbL2qT5t6atM,35648
|
|
7
|
-
modularitypruning/partition_utilities.py,sha256=_QAFKPUQlt-v_D7fydu-CbPvFZM9lw3qpxfX09Gv7Zw,1914
|
|
8
|
-
modularitypruning/plotting.py,sha256=eQkqmx1fEfiT9AtYo1l63cm8B2I914I3XScoQv0UjhY,12249
|
|
9
|
-
modularitypruning/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
modularitypruning/types.py,sha256=VdaNyxaqEFeJJuIp14X-pvv893yTfbxnq5MepBQEui4,2444
|
|
11
|
-
modularitypruning-2.0.0.dist-info/licenses/LICENSE,sha256=eWz3HIQQxg7p1iSpUOUDKdDhGcuMPuVDDlcXf9F12D8,1068
|
|
12
|
-
modularitypruning-2.0.0.dist-info/METADATA,sha256=Sx24A8K9GupCvLgBOepaMFJPV_FmhavGI_TA-ccGyLs,3972
|
|
13
|
-
modularitypruning-2.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
14
|
-
modularitypruning-2.0.0.dist-info/top_level.txt,sha256=ZPOx3a-ek0Ge0ZMq-uvbySSaAL9MZ-t23-JkuHZXo9E,18
|
|
15
|
-
modularitypruning-2.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|