alonso 0.0.6__tar.gz → 0.0.8__tar.gz
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.
- {alonso-0.0.6 → alonso-0.0.8}/PKG-INFO +2 -2
- {alonso-0.0.6 → alonso-0.0.8}/README.md +1 -1
- {alonso-0.0.6 → alonso-0.0.8}/alonso/algorithm.py +3 -2
- {alonso-0.0.6 → alonso-0.0.8}/alonso/app.py +1 -1
- {alonso-0.0.6 → alonso-0.0.8}/alonso/batch.py +1 -1
- alonso-0.0.8/alonso/partition.py +223 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/test.py +1 -1
- {alonso-0.0.6 → alonso-0.0.8}/alonso.egg-info/PKG-INFO +2 -2
- {alonso-0.0.6 → alonso-0.0.8}/setup.py +1 -1
- alonso-0.0.6/alonso/partition.py +0 -216
- {alonso-0.0.6 → alonso-0.0.8}/LICENSE +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/__init__.py +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/applogger.py +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/merge.py +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/parser.py +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/stable.py +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso/utils.py +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso.egg-info/SOURCES.txt +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso.egg-info/dependency_links.txt +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso.egg-info/entry_points.txt +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso.egg-info/requires.txt +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/alonso.egg-info/top_level.txt +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/pyproject.toml +0 -0
- {alonso-0.0.6 → alonso-0.0.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: alonso
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.8
|
4
4
|
Summary: Compute an Approximate Vertex Cover for undirected graph encoded in DIMACS format.
|
5
5
|
Home-page: https://github.com/frankvegadelgado/alonso
|
6
6
|
Author: Frank Vega
|
@@ -69,7 +69,7 @@ Given an undirected graph $G = (V, E)$, a **vertex cover** is a subset $V' \subs
|
|
69
69
|
|
70
70
|
# Overview of the Algorithm and Its Running Time
|
71
71
|
|
72
|
-
The `find_vertex_cover` algorithm approximates a minimum vertex cover for an undirected graph $G = (V, E)$ by partitioning its edges into two claw-free subgraphs using the Burr-Erdős-Lovász (1976) method, computing exact vertex covers for these subgraphs with the Faenza, Oriolo, and Stauffer (2011) approach, and recursively refining the solution on residual edges. This process prevents the ratio from reaching 2, leveraging overlap between subgraphs and minimal additions in recursion. The algorithm begins by cleaning the graph (removing self-loops and isolates in
|
72
|
+
The `find_vertex_cover` algorithm approximates a minimum vertex cover for an undirected graph $G = (V, E)$ by partitioning its edges into two claw-free subgraphs using the Burr-Erdős-Lovász (1976) method, computing exact vertex covers for these subgraphs with the Faenza, Oriolo, and Stauffer (2011) approach, and recursively refining the solution on residual edges. This process prevents the ratio from reaching 2, leveraging overlap between subgraphs and minimal additions in recursion. The algorithm begins by cleaning the graph (removing self-loops and isolates in $\mathcal{O}(n + m)$), checking for claw-free in $\mathcal{O}(m \cdot \Delta)$ where $\Delta$ is the maximum degree, partitions edges in $\mathcal{O}(n^3)$, computes vertex covers in $\mathcal{O}(n^3)$ per subgraph (total $\mathcal{O}(n^3)$), merges covers in $\mathcal{O}(n \cdot \log n)$, and constructs the residual graph in $\mathcal{O}(m)$. The recursion depth never exceeds a small constant, most commonly 2. This yields a total runtime of $\mathcal{O}(n^3)$, per a constant time of recursion levels.
|
73
73
|
|
74
74
|
---
|
75
75
|
|
@@ -31,7 +31,7 @@ Given an undirected graph $G = (V, E)$, a **vertex cover** is a subset $V' \subs
|
|
31
31
|
|
32
32
|
# Overview of the Algorithm and Its Running Time
|
33
33
|
|
34
|
-
The `find_vertex_cover` algorithm approximates a minimum vertex cover for an undirected graph $G = (V, E)$ by partitioning its edges into two claw-free subgraphs using the Burr-Erdős-Lovász (1976) method, computing exact vertex covers for these subgraphs with the Faenza, Oriolo, and Stauffer (2011) approach, and recursively refining the solution on residual edges. This process prevents the ratio from reaching 2, leveraging overlap between subgraphs and minimal additions in recursion. The algorithm begins by cleaning the graph (removing self-loops and isolates in
|
34
|
+
The `find_vertex_cover` algorithm approximates a minimum vertex cover for an undirected graph $G = (V, E)$ by partitioning its edges into two claw-free subgraphs using the Burr-Erdős-Lovász (1976) method, computing exact vertex covers for these subgraphs with the Faenza, Oriolo, and Stauffer (2011) approach, and recursively refining the solution on residual edges. This process prevents the ratio from reaching 2, leveraging overlap between subgraphs and minimal additions in recursion. The algorithm begins by cleaning the graph (removing self-loops and isolates in $\mathcal{O}(n + m)$), checking for claw-free in $\mathcal{O}(m \cdot \Delta)$ where $\Delta$ is the maximum degree, partitions edges in $\mathcal{O}(n^3)$, computes vertex covers in $\mathcal{O}(n^3)$ per subgraph (total $\mathcal{O}(n^3)$), merges covers in $\mathcal{O}(n \cdot \log n)$, and constructs the residual graph in $\mathcal{O}(m)$. The recursion depth never exceeds a small constant, most commonly 2. This yields a total runtime of $\mathcal{O}(n^3)$, per a constant time of recursion levels.
|
35
35
|
|
36
36
|
---
|
37
37
|
|
@@ -58,8 +58,9 @@ def find_vertex_cover(graph):
|
|
58
58
|
|
59
59
|
# Step 1: Edge partitioning using enhanced Burr-Erdos-Lovasz technique
|
60
60
|
# Partition edges E = E1 union E2 such that both induced subgraphs G[E1] and G[E2] are claw-free
|
61
|
-
|
62
|
-
|
61
|
+
partitioner = partition.ClawFreePartitioner(working_graph)
|
62
|
+
E1, E2 = partitioner.partition_edges()
|
63
|
+
|
63
64
|
# Step 2: Solve subproblems optimally on claw-free partitions
|
64
65
|
# Each partition can be solved exactly using polynomial-time algorithms
|
65
66
|
vertex_cover_1 = stable.minimum_vertex_cover_claw_free(E1)
|
@@ -82,7 +82,7 @@ def main():
|
|
82
82
|
helper.add_argument('-c', '--count', action='store_true', help='calculate the size of the vertex cover')
|
83
83
|
helper.add_argument('-v', '--verbose', action='store_true', help='anable verbose output')
|
84
84
|
helper.add_argument('-l', '--log', action='store_true', help='enable file logging')
|
85
|
-
helper.add_argument('--version', action='version', version='%(prog)s 0.0.
|
85
|
+
helper.add_argument('--version', action='version', version='%(prog)s 0.0.8')
|
86
86
|
|
87
87
|
# Initialize the parameters
|
88
88
|
args = helper.parse_args()
|
@@ -36,7 +36,7 @@ def main():
|
|
36
36
|
helper.add_argument('-c', '--count', action='store_true', help='calculate the size of the vertex cover')
|
37
37
|
helper.add_argument('-v', '--verbose', action='store_true', help='anable verbose output')
|
38
38
|
helper.add_argument('-l', '--log', action='store_true', help='enable file logging')
|
39
|
-
helper.add_argument('--version', action='version', version='%(prog)s 0.0.
|
39
|
+
helper.add_argument('--version', action='version', version='%(prog)s 0.0.8')
|
40
40
|
|
41
41
|
|
42
42
|
# Initialize the parameters
|
@@ -0,0 +1,223 @@
|
|
1
|
+
from collections import defaultdict
|
2
|
+
import networkx as nx
|
3
|
+
from typing import Set, Tuple, List
|
4
|
+
import mendive.algorithm as algo
|
5
|
+
class ClawFreePartitioner:
|
6
|
+
"""
|
7
|
+
Implements a polynomial-time algorithm to partition graph edges into two sets
|
8
|
+
E1 and E2 such that both induced subgraphs are claw-free.
|
9
|
+
|
10
|
+
Based on principles from Burr-Erdős-Lovász approach for Ramsey-type problems.
|
11
|
+
A claw is a star graph K_{1,3} (one central vertex connected to 3 others).
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, graph):
|
15
|
+
"""
|
16
|
+
Initialize with a NetworkX graph.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
graph: NetworkX Graph object
|
20
|
+
"""
|
21
|
+
self.G = graph.copy()
|
22
|
+
self.n = len(self.G.nodes())
|
23
|
+
self.m = len(self.G.edges())
|
24
|
+
|
25
|
+
def find_potential_claw_centers(self) -> Set[int]:
|
26
|
+
"""
|
27
|
+
Find vertices that could potentially be centers of claws.
|
28
|
+
A vertex can be a claw center only if it has degree >= 3.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
Set of vertices with degree >= 3
|
32
|
+
"""
|
33
|
+
return {v for v in self.G.nodes() if self.G.degree(v) >= 3}
|
34
|
+
|
35
|
+
def get_neighborhood_edges(self, vertex: int) -> List[Tuple[int, int]]:
|
36
|
+
"""
|
37
|
+
Get all edges incident to a given vertex.
|
38
|
+
|
39
|
+
Args:
|
40
|
+
vertex: The vertex to get incident edges for
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
List of edges (as tuples) incident to the vertex
|
44
|
+
"""
|
45
|
+
return [(vertex, neighbor) for neighbor in self.G.neighbors(vertex)]
|
46
|
+
|
47
|
+
def would_create_claw(self, edges_in_partition: Set[Tuple[int, int]],
|
48
|
+
new_edge: Tuple[int, int]) -> bool:
|
49
|
+
"""
|
50
|
+
Check if adding a new edge to a partition would create a claw.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
edges_in_partition: Current edges in the partition
|
54
|
+
new_edge: Edge to potentially add
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
True if adding the edge would create a claw, False otherwise
|
58
|
+
"""
|
59
|
+
# Build adjacency list for current partition
|
60
|
+
adj = defaultdict(set)
|
61
|
+
for u, v in edges_in_partition:
|
62
|
+
adj[u].add(v)
|
63
|
+
adj[v].add(u)
|
64
|
+
|
65
|
+
# Add the new edge temporarily
|
66
|
+
u, v = new_edge
|
67
|
+
adj[u].add(v)
|
68
|
+
adj[v].add(u)
|
69
|
+
|
70
|
+
# Check if any vertex now forms a claw
|
71
|
+
for vertex in [u, v]:
|
72
|
+
neighbors = list(adj[vertex])
|
73
|
+
if len(neighbors) >= 3:
|
74
|
+
# Check all combinations of 3 neighbors
|
75
|
+
|
76
|
+
for i in range(len(neighbors)):
|
77
|
+
for j in range(i + 1, len(neighbors)):
|
78
|
+
for k in range(j + 1, len(neighbors)):
|
79
|
+
n1, n2, n3 = neighbors[i], neighbors[j], neighbors[k]
|
80
|
+
# Check if these 3 neighbors are not connected to each other
|
81
|
+
# (which would make vertex the center of a claw)
|
82
|
+
if (n1 not in adj[n2] and n2 not in adj[n1] and
|
83
|
+
n1 not in adj[n3] and n3 not in adj[n1] and
|
84
|
+
n2 not in adj[n3] and n3 not in adj[n2]):
|
85
|
+
return True
|
86
|
+
|
87
|
+
return False
|
88
|
+
|
89
|
+
def greedy_partition(self) -> Tuple[Set[Tuple[int, int]], Set[Tuple[int, int]]]:
|
90
|
+
"""
|
91
|
+
Greedily partition edges into two claw-free sets.
|
92
|
+
|
93
|
+
Strategy:
|
94
|
+
1. Process vertices in order of decreasing degree
|
95
|
+
2. For each vertex, try to distribute its incident edges
|
96
|
+
between the two partitions to avoid creating claws
|
97
|
+
3. Use a greedy approach that prioritizes balance
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
Tuple of (E1, E2) - two sets of edges
|
101
|
+
"""
|
102
|
+
E1 = set()
|
103
|
+
E2 = set()
|
104
|
+
processed_edges = set()
|
105
|
+
|
106
|
+
# Sort vertices by degree (descending) to handle high-degree vertices first
|
107
|
+
vertices_by_degree = sorted(self.G.nodes(),
|
108
|
+
key=lambda v: self.G.degree(v),
|
109
|
+
reverse=True)
|
110
|
+
|
111
|
+
for vertex in vertices_by_degree:
|
112
|
+
if self.G.degree(vertex) < 3:
|
113
|
+
continue # Can't be center of a claw
|
114
|
+
|
115
|
+
incident_edges = self.get_neighborhood_edges(vertex)
|
116
|
+
unprocessed_edges = [e for e in incident_edges if e not in processed_edges]
|
117
|
+
|
118
|
+
if len(unprocessed_edges) < 3:
|
119
|
+
continue # Not enough edges to potentially form a claw
|
120
|
+
|
121
|
+
# Try to distribute edges to avoid claws
|
122
|
+
for edge in unprocessed_edges:
|
123
|
+
if edge in processed_edges:
|
124
|
+
continue
|
125
|
+
|
126
|
+
# Try adding to E1 first
|
127
|
+
if not self.would_create_claw(E1, edge):
|
128
|
+
E1.add(edge)
|
129
|
+
processed_edges.add(edge)
|
130
|
+
elif not self.would_create_claw(E2, edge):
|
131
|
+
E2.add(edge)
|
132
|
+
processed_edges.add(edge)
|
133
|
+
else:
|
134
|
+
# If adding to either partition would create a claw,
|
135
|
+
# add to the smaller partition (balance heuristic)
|
136
|
+
if len(E1) <= len(E2):
|
137
|
+
E1.add(edge)
|
138
|
+
else:
|
139
|
+
E2.add(edge)
|
140
|
+
processed_edges.add(edge)
|
141
|
+
|
142
|
+
# Add remaining unprocessed edges using simple alternating strategy
|
143
|
+
remaining_edges = set(self.G.edges()) - processed_edges
|
144
|
+
for i, edge in enumerate(remaining_edges):
|
145
|
+
if i % 2 == 0:
|
146
|
+
E1.add(edge)
|
147
|
+
else:
|
148
|
+
E2.add(edge)
|
149
|
+
|
150
|
+
return E1, E2
|
151
|
+
|
152
|
+
def verify_claw_free(self, edge_set: Set[Tuple[int, int]]) -> bool:
|
153
|
+
"""
|
154
|
+
Verify that a given edge set induces a claw-free graph.
|
155
|
+
|
156
|
+
Args:
|
157
|
+
edge_set: Set of edges to check
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
True if the induced graph is claw-free, False otherwise
|
161
|
+
"""
|
162
|
+
# Build adjacency list
|
163
|
+
G = nx.Graph()
|
164
|
+
G.add_edges_from(edge_set)
|
165
|
+
claw = algo.find_claw_coordinates(G, first_claw=True)
|
166
|
+
if claw is None:
|
167
|
+
return True
|
168
|
+
else:
|
169
|
+
return False
|
170
|
+
|
171
|
+
def partition_edges(self) -> Tuple[Set[Tuple[int, int]], Set[Tuple[int, int]]]:
|
172
|
+
"""
|
173
|
+
Main method to partition graph edges into two claw-free sets.
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
Tuple of (E1, E2) where both induce claw-free graphs
|
177
|
+
"""
|
178
|
+
if self.m == 0:
|
179
|
+
return set(), set()
|
180
|
+
|
181
|
+
# Try the greedy approach
|
182
|
+
E1, E2 = self.greedy_partition()
|
183
|
+
|
184
|
+
# Verify the result
|
185
|
+
if self.verify_claw_free(E1) and self.verify_claw_free(E2):
|
186
|
+
return E1, E2
|
187
|
+
|
188
|
+
# If greedy fails, use a more conservative approach
|
189
|
+
return self.fallback_partition()
|
190
|
+
|
191
|
+
def fallback_partition(self) -> Tuple[Set[Tuple[int, int]], Set[Tuple[int, int]]]:
|
192
|
+
"""
|
193
|
+
Fallback method: Create a more conservative partition by ensuring
|
194
|
+
no vertex has degree > 2 in either partition (guarantees claw-free).
|
195
|
+
|
196
|
+
Returns:
|
197
|
+
Tuple of (E1, E2) - two claw-free edge sets
|
198
|
+
"""
|
199
|
+
E1 = set()
|
200
|
+
E2 = set()
|
201
|
+
|
202
|
+
degree1 = defaultdict(int)
|
203
|
+
degree2 = defaultdict(int)
|
204
|
+
|
205
|
+
for edge in self.G.edges():
|
206
|
+
u, v = edge
|
207
|
+
# Add to partition where both endpoints have degree < 2
|
208
|
+
if degree1[u] < 2 and degree1[v] < 2:
|
209
|
+
E1.add(edge)
|
210
|
+
degree1[u] += 1
|
211
|
+
degree1[v] += 1
|
212
|
+
elif degree2[u] < 2 and degree2[v] < 2:
|
213
|
+
E2.add(edge)
|
214
|
+
degree2[u] += 1
|
215
|
+
degree2[v] += 1
|
216
|
+
else:
|
217
|
+
# Add to the partition with smaller total degree
|
218
|
+
if sum(degree1.values()) <= sum(degree2.values()):
|
219
|
+
E1.add(edge)
|
220
|
+
else:
|
221
|
+
E2.add(edge)
|
222
|
+
|
223
|
+
return E1, E2
|
@@ -34,7 +34,7 @@ def main():
|
|
34
34
|
helper.add_argument('-w', '--write', action='store_true', help='write the generated random matrix to a file in the current directory')
|
35
35
|
helper.add_argument('-v', '--verbose', action='store_true', help='anable verbose output')
|
36
36
|
helper.add_argument('-l', '--log', action='store_true', help='enable file logging')
|
37
|
-
helper.add_argument('--version', action='version', version='%(prog)s 0.0.
|
37
|
+
helper.add_argument('--version', action='version', version='%(prog)s 0.0.8')
|
38
38
|
|
39
39
|
# Initialize the parameters
|
40
40
|
args = helper.parse_args()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: alonso
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.8
|
4
4
|
Summary: Compute an Approximate Vertex Cover for undirected graph encoded in DIMACS format.
|
5
5
|
Home-page: https://github.com/frankvegadelgado/alonso
|
6
6
|
Author: Frank Vega
|
@@ -69,7 +69,7 @@ Given an undirected graph $G = (V, E)$, a **vertex cover** is a subset $V' \subs
|
|
69
69
|
|
70
70
|
# Overview of the Algorithm and Its Running Time
|
71
71
|
|
72
|
-
The `find_vertex_cover` algorithm approximates a minimum vertex cover for an undirected graph $G = (V, E)$ by partitioning its edges into two claw-free subgraphs using the Burr-Erdős-Lovász (1976) method, computing exact vertex covers for these subgraphs with the Faenza, Oriolo, and Stauffer (2011) approach, and recursively refining the solution on residual edges. This process prevents the ratio from reaching 2, leveraging overlap between subgraphs and minimal additions in recursion. The algorithm begins by cleaning the graph (removing self-loops and isolates in
|
72
|
+
The `find_vertex_cover` algorithm approximates a minimum vertex cover for an undirected graph $G = (V, E)$ by partitioning its edges into two claw-free subgraphs using the Burr-Erdős-Lovász (1976) method, computing exact vertex covers for these subgraphs with the Faenza, Oriolo, and Stauffer (2011) approach, and recursively refining the solution on residual edges. This process prevents the ratio from reaching 2, leveraging overlap between subgraphs and minimal additions in recursion. The algorithm begins by cleaning the graph (removing self-loops and isolates in $\mathcal{O}(n + m)$), checking for claw-free in $\mathcal{O}(m \cdot \Delta)$ where $\Delta$ is the maximum degree, partitions edges in $\mathcal{O}(n^3)$, computes vertex covers in $\mathcal{O}(n^3)$ per subgraph (total $\mathcal{O}(n^3)$), merges covers in $\mathcal{O}(n \cdot \log n)$, and constructs the residual graph in $\mathcal{O}(m)$. The recursion depth never exceeds a small constant, most commonly 2. This yields a total runtime of $\mathcal{O}(n^3)$, per a constant time of recursion levels.
|
73
73
|
|
74
74
|
---
|
75
75
|
|
alonso-0.0.6/alonso/partition.py
DELETED
@@ -1,216 +0,0 @@
|
|
1
|
-
import networkx as nx
|
2
|
-
from typing import Tuple, Set, List
|
3
|
-
from collections import defaultdict
|
4
|
-
import itertools
|
5
|
-
import mendive.algorithm as algo
|
6
|
-
|
7
|
-
class BurrErdosLovaszPartitioner:
|
8
|
-
"""
|
9
|
-
Implementation of the Burr-Erdős-Lovász (1976) algorithm for partitioning
|
10
|
-
edges of a graph into two subsets such that each subset induces a k-star-free subgraph.
|
11
|
-
|
12
|
-
For k=3 (claw-free case), we partition edges to avoid 3-stars in each partition.
|
13
|
-
|
14
|
-
The algorithm works by maintaining two edge sets and for each new edge,
|
15
|
-
assigning it to the partition where it creates the fewest violations,
|
16
|
-
then locally repairing any violations that arise.
|
17
|
-
"""
|
18
|
-
|
19
|
-
def __init__(self, graph: nx.Graph, k: int = 3):
|
20
|
-
self.graph = graph.copy()
|
21
|
-
self.k = k # k=3 for claw-free (3-star-free)
|
22
|
-
self.n = len(graph.nodes())
|
23
|
-
|
24
|
-
def partition_edges(self) -> Tuple[Set[Tuple[int, int]], Set[Tuple[int, int]]]:
|
25
|
-
"""
|
26
|
-
Partition edges into two k-star-free subgraphs using BEL algorithm.
|
27
|
-
"""
|
28
|
-
E1 = set()
|
29
|
-
E2 = set()
|
30
|
-
|
31
|
-
# Convert edges to consistent format
|
32
|
-
edges = [(min(u, v), max(u, v)) for u, v in self.graph.edges()]
|
33
|
-
|
34
|
-
# Main algorithm: assign each edge to minimize violations
|
35
|
-
for edge in edges:
|
36
|
-
# Count violations if we add edge to E1
|
37
|
-
violations_E1 = self._count_violations_after_adding(E1, edge)
|
38
|
-
violations_E2 = self._count_violations_after_adding(E2, edge)
|
39
|
-
|
40
|
-
# Add to partition with fewer violations
|
41
|
-
if violations_E1 <= violations_E2:
|
42
|
-
E1.add(edge)
|
43
|
-
# Repair violations in E1
|
44
|
-
E1 = self._repair_violations(E1)
|
45
|
-
else:
|
46
|
-
E2.add(edge)
|
47
|
-
# Repair violations in E2
|
48
|
-
E2 = self._repair_violations(E2)
|
49
|
-
|
50
|
-
return E1, E2
|
51
|
-
|
52
|
-
def _count_violations_after_adding(self, edge_set: Set[Tuple[int, int]],
|
53
|
-
new_edge: Tuple[int, int]) -> int:
|
54
|
-
"""Count number of k-stars that would be created by adding new_edge."""
|
55
|
-
temp_set = edge_set | {new_edge}
|
56
|
-
return self._count_k_stars(temp_set)
|
57
|
-
|
58
|
-
def _is_claw_free(self, edge_set: Set[Tuple[int, int]]) -> bool:
|
59
|
-
"""Count number of k-stars in the edge set."""
|
60
|
-
if not edge_set:
|
61
|
-
return True
|
62
|
-
|
63
|
-
# Build graph from edge set
|
64
|
-
G = nx.Graph()
|
65
|
-
G.add_edges_from(edge_set)
|
66
|
-
|
67
|
-
claw = algo.find_claw_coordinates(G, first_claw=True)
|
68
|
-
if claw is None:
|
69
|
-
return True
|
70
|
-
else:
|
71
|
-
return False
|
72
|
-
|
73
|
-
|
74
|
-
def _count_k_stars(self, edge_set: Set[Tuple[int, int]]) -> int:
|
75
|
-
"""Count number of k-stars in the edge set."""
|
76
|
-
if not edge_set:
|
77
|
-
return 0
|
78
|
-
|
79
|
-
# Build graph from edge set
|
80
|
-
G = nx.Graph()
|
81
|
-
G.add_edges_from(edge_set)
|
82
|
-
|
83
|
-
k_star_count = 0
|
84
|
-
|
85
|
-
if self.k == 3:
|
86
|
-
all_claws = algo.find_claw_coordinates(G, first_claw=False)
|
87
|
-
if all_claws is not None:
|
88
|
-
k_star_count = len(all_claws)
|
89
|
-
else:
|
90
|
-
# Check each vertex as potential center of k-star
|
91
|
-
for center in G.nodes():
|
92
|
-
neighbors = list(G.neighbors(center))
|
93
|
-
if len(neighbors) >= self.k:
|
94
|
-
# Count k-subsets of neighbors that form independent sets
|
95
|
-
for k_subset in itertools.combinations(neighbors, self.k):
|
96
|
-
# Check if this k-subset is independent
|
97
|
-
is_independent = True
|
98
|
-
for i in range(self.k):
|
99
|
-
for j in range(i + 1, self.k):
|
100
|
-
if G.has_edge(k_subset[i], k_subset[j]):
|
101
|
-
is_independent = False
|
102
|
-
break
|
103
|
-
if not is_independent:
|
104
|
-
break
|
105
|
-
|
106
|
-
if is_independent:
|
107
|
-
k_star_count += 1
|
108
|
-
|
109
|
-
return k_star_count
|
110
|
-
|
111
|
-
def _repair_violations(self, edge_set: Set[Tuple[int, int]]) -> Set[Tuple[int, int]]:
|
112
|
-
"""
|
113
|
-
Repair k-star violations in edge_set by removing minimal edges.
|
114
|
-
This is the key part of the BEL algorithm.
|
115
|
-
"""
|
116
|
-
if not edge_set:
|
117
|
-
return edge_set
|
118
|
-
|
119
|
-
current_set = edge_set.copy()
|
120
|
-
|
121
|
-
while True:
|
122
|
-
if self.k == 3:
|
123
|
-
is_claw_free = self._is_claw_free(current_set)
|
124
|
-
if is_claw_free:
|
125
|
-
break # No more violations
|
126
|
-
violations = self._find_k_stars(current_set)
|
127
|
-
else:
|
128
|
-
violations = self._find_k_stars(current_set)
|
129
|
-
if not violations:
|
130
|
-
break # No more violations
|
131
|
-
|
132
|
-
# Find the edge that appears in most violations
|
133
|
-
edge_violation_count = defaultdict(int)
|
134
|
-
for k_star in violations:
|
135
|
-
center, leaves = k_star
|
136
|
-
# Each k-star consists of k edges from center to leaves
|
137
|
-
for leaf in leaves:
|
138
|
-
edge = (min(center, leaf), max(center, leaf))
|
139
|
-
if edge in current_set:
|
140
|
-
edge_violation_count[edge] += 1
|
141
|
-
|
142
|
-
if not edge_violation_count:
|
143
|
-
break
|
144
|
-
|
145
|
-
# Remove the edge that appears in most violations
|
146
|
-
most_violating_edge = max(edge_violation_count.keys(),
|
147
|
-
key=lambda e: edge_violation_count[e])
|
148
|
-
current_set.remove(most_violating_edge)
|
149
|
-
|
150
|
-
return current_set
|
151
|
-
|
152
|
-
def _find_k_stars(self, edge_set: Set[Tuple[int, int]]) -> List[Tuple[int, Tuple]]:
|
153
|
-
"""Find all k-stars in the edge set. Returns list of (center, leaves) tuples."""
|
154
|
-
if not edge_set:
|
155
|
-
return []
|
156
|
-
|
157
|
-
# Build graph from edge set
|
158
|
-
G = nx.Graph()
|
159
|
-
G.add_edges_from(edge_set)
|
160
|
-
|
161
|
-
k_stars = []
|
162
|
-
|
163
|
-
if self.k == 3:
|
164
|
-
all_claws = algo.find_claw_coordinates(G, first_claw=False)
|
165
|
-
if all_claws is not None:
|
166
|
-
for subset in all_claws:
|
167
|
-
subgraph = G.subgraph(subset)
|
168
|
-
sorted_nodes = sorted(list(subset), key=lambda x: subgraph.degree(x))
|
169
|
-
center = sorted_nodes.pop()
|
170
|
-
k_subset = tuple(sorted_nodes)
|
171
|
-
k_stars.append((center, k_subset))
|
172
|
-
else:
|
173
|
-
# Check each vertex as potential center of k-star
|
174
|
-
for center in G.nodes():
|
175
|
-
neighbors = list(G.neighbors(center))
|
176
|
-
if len(neighbors) >= self.k:
|
177
|
-
# Find all k-subsets of neighbors that form independent sets
|
178
|
-
for k_subset in itertools.combinations(neighbors, self.k):
|
179
|
-
# Check if this k-subset is independent
|
180
|
-
is_independent = True
|
181
|
-
for i in range(self.k):
|
182
|
-
for j in range(i + 1, self.k):
|
183
|
-
if G.has_edge(k_subset[i], k_subset[j]):
|
184
|
-
is_independent = False
|
185
|
-
break
|
186
|
-
if not is_independent:
|
187
|
-
break
|
188
|
-
|
189
|
-
if is_independent:
|
190
|
-
k_stars.append((center, k_subset))
|
191
|
-
|
192
|
-
return k_stars
|
193
|
-
|
194
|
-
def verify_partition(self, E1: Set[Tuple[int, int]], E2: Set[Tuple[int, int]]) -> Tuple[bool, bool]:
|
195
|
-
"""Verify that both partitions are k-star-free."""
|
196
|
-
if self.k == 3:
|
197
|
-
return (self._is_claw_free(E1), self._is_claw_free(E2))
|
198
|
-
else:
|
199
|
-
return (self._count_k_stars(E1) == 0, self._count_k_stars(E2) == 0)
|
200
|
-
|
201
|
-
|
202
|
-
def partition_edges_claw_free(G: nx.Graph) -> Tuple[Set[Tuple[int, int]], Set[Tuple[int, int]]]:
|
203
|
-
"""
|
204
|
-
Partition edges of graph G into two sets such that each induces a claw-free subgraph.
|
205
|
-
|
206
|
-
Implementation of Burr, Erdős, Lovász (1976) algorithm for k=3 (claw-free case).
|
207
|
-
|
208
|
-
Args:
|
209
|
-
G: Undirected NetworkX graph
|
210
|
-
|
211
|
-
Returns:
|
212
|
-
(E1, E2): Two edge sets that induce claw-free subgraphs
|
213
|
-
"""
|
214
|
-
partitioner = BurrErdosLovaszPartitioner(G, k=3)
|
215
|
-
return partitioner.partition_edges()
|
216
|
-
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|