netlistx 0.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.
netlistx/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ import sys
2
+
3
+ if sys.version_info[:2] >= (3, 8):
4
+ # TODO: Import directly (no need for conditional) when `python_requires = >= 3.9`
5
+ from importlib.metadata import PackageNotFoundError, version # pragma: no cover
6
+ else:
7
+ from importlib_metadata import PackageNotFoundError, version # pragma: no cover
8
+
9
+ try:
10
+ # Change here if project is renamed and does not equal the package name
11
+ dist_name = __name__
12
+ __version__ = version(dist_name)
13
+ except PackageNotFoundError: # pragma: no cover
14
+ __version__ = "unknown"
15
+ finally:
16
+ del version, PackageNotFoundError
netlistx/cover.py ADDED
@@ -0,0 +1,412 @@
1
+ """
2
+ Cover.py
3
+
4
+ This code implements several algorithms for solving different types of covering problems in graphs. The main purpose is to find minimal sets of vertices or edges that "cover" certain structures in a graph, such as all edges, cycles, or odd cycles.
5
+
6
+ The code takes various inputs depending on the specific function being used. Generally, it requires a graph structure (either a regular graph or a hypergraph), a weight mapping for the vertices, and sometimes an optional initial cover set. The graphs are typically represented using the NetworkX library (nx.Graph).
7
+
8
+ The outputs produced by these functions are usually a tuple containing two elements: a set representing the minimal cover found, and a number representing the total weight or cost of that cover.
9
+
10
+ The code achieves its purpose through several different algorithms, but they all follow a similar pattern called the primal-dual approximation method. This method iteratively builds a solution by selecting elements that violate certain conditions and adding them to the cover set.
11
+
12
+ Here's a breakdown of the main functions:
13
+
14
+ 1. pd_cover: This is the core function that implements the primal-dual approximation algorithm. It takes a "violate" function that generates sets of violating elements, a weight mapping, and an initial solution set. It iteratively adds elements to the solution until no violations remain.
15
+
16
+ 2. min_vertex_cover: This function finds a minimum weighted vertex cover in a graph. It uses pd_cover with a violate function that yields edges not covered by the current solution.
17
+
18
+ 3. min_hyper_vertex_cover: Similar to min_vertex_cover, but works on hypergraphs where edges can connect more than two vertices.
19
+
20
+ 4. min_cycle_cover: This function finds a minimum weighted set of vertices that cover all cycles in a graph. It uses a breadth-first search to find cycles and then uses pd_cover to select vertices that break these cycles.
21
+
22
+ 5. min_odd_cycle_cover: Similar to min_cycle_cover, but specifically targets odd cycles in the graph.
23
+
24
+ The code uses several important data structures and algorithms. Graphs are represented using NetworkX, which provides efficient graph operations. The algorithms make heavy use of sets for storing covers and dictionaries for storing weights and other information. The cycle-finding algorithms use breadth-first search and clever bookkeeping to efficiently detect cycles in the graph.
25
+
26
+ Overall, this code provides a toolkit for solving various covering problems on graphs, which have applications in many areas of computer science and operations research. The algorithms implemented here provide approximate solutions to these problems, which are often NP-hard and thus difficult to solve exactly for large instances.
27
+ """
28
+
29
+ import copy
30
+ from collections import deque
31
+ from typing import (
32
+ Callable,
33
+ Deque,
34
+ Dict,
35
+ Generator,
36
+ MutableMapping,
37
+ Optional,
38
+ Set,
39
+ Tuple,
40
+ Union,
41
+ )
42
+
43
+ import networkx as nx
44
+
45
+
46
+ def pd_cover(
47
+ violate: Callable, weight: MutableMapping, soln: Set
48
+ ) -> Tuple[Set, Union[int, float]]:
49
+ """
50
+ The function `pd_cover` implements a primal-dual approximation algorithm for covering problems.
51
+
52
+ :param violate: The `violate` parameter is a callable function or oracle that returns a set of
53
+ violate elements. It is used to generate sets of elements that violate the current solution. Each
54
+ set represents a potential improvement to the solution
55
+
56
+ :type violate: Callable
57
+
58
+ :param weight: The `weight` parameter is a dictionary that represents the weight of each element.
59
+ The keys of the dictionary are the elements, and the values are their corresponding weights
60
+
61
+ :type weight: MutableMapping
62
+
63
+ :param soln: The `soln` parameter is a set that represents the current solution set. It initially
64
+ contains no elements, and elements are added to it during the algorithm
65
+
66
+ :type soln: Set
67
+
68
+ :return: a tuple containing the updated solution set and the total primal cost.
69
+
70
+ Examples:
71
+ >>> def violate_graph() -> Generator:
72
+ ... yield [0, 1]
73
+ ... yield [0, 2]
74
+ ... yield [1, 2]
75
+ >>> weight = {0: 1, 1: 2, 2: 3}
76
+ >>> soln = set()
77
+ >>> pd_cover(violate_graph, weight, soln)
78
+ ({0, 1}, 4)
79
+ """
80
+ total_prml_cost = 0
81
+ total_dual_cost = 0
82
+ gap = copy.copy(weight)
83
+ for S in violate():
84
+ min_vtx = min(S, key=lambda vtx: gap[vtx])
85
+ min_val = gap[min_vtx]
86
+ soln.add(min_vtx)
87
+ total_prml_cost += weight[min_vtx]
88
+ total_dual_cost += min_val
89
+ for vtx in S:
90
+ gap[vtx] -= min_val
91
+ assert total_dual_cost <= total_prml_cost
92
+ return soln, total_prml_cost
93
+
94
+
95
+ def min_vertex_cover(
96
+ ugraph: nx.Graph, weight: MutableMapping, coverset: Optional[Set] = None
97
+ ) -> Tuple[Set, Union[int, float]]:
98
+ r"""
99
+ The `min_vertex_cover` function performs minimum weighted vertex cover using a primal-dual
100
+ approximation algorithm (without post-processing).
101
+
102
+ :param ugraph: The parameter `ugraph` is a `nx.Graph` object, which represents the input graph. It is an
103
+ undirected graph where each edge represents a connection between two vertices
104
+
105
+ :type ugraph: nx.Graph
106
+
107
+ :param weight: The `weight` parameter is a dictionary that assigns a weight to each vertex in the
108
+ graph. The weights are used to determine the minimum weighted vertex cover
109
+
110
+ :type weight: MutableMapping
111
+
112
+ :param coverset: The `coverset` parameter is an optional set that represents the current vertex
113
+ cover solution. It is used to keep track of the vertices that are included in the cover. If no
114
+ `coverset` is provided, an empty set is used as the initial cover
115
+
116
+ :type coverset: Optional[Set]
117
+
118
+ :return: The function `min_vertex_cover` returns a tuple containing two elements. The first element
119
+ is a set representing the minimum weighted vertex cover, and the second element is either an integer
120
+ or a float representing the weight of the minimum vertex cover.
121
+
122
+ .. svgbob::
123
+ :align: center
124
+
125
+ "({b, d, e}, 3)"
126
+
127
+ b c d e
128
+ #-----o-----#-----o
129
+ | \ / \
130
+ | \ / \
131
+ o #-----o
132
+ a e f
133
+
134
+ Examples:
135
+ >>> ugraph = nx.Graph()
136
+ >>> ugraph.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)])
137
+ >>> weight = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
138
+ >>> soln = set()
139
+ >>> min_vertex_cover(ugraph, weight, soln)
140
+ ({0, 1, 2, 3}, 4)
141
+ """
142
+ if coverset is None:
143
+ coverset = set()
144
+
145
+ def violate_graph() -> Generator:
146
+ for utx, vtx in ugraph.edges():
147
+ if utx in coverset or vtx in coverset:
148
+ continue
149
+ yield [utx, vtx]
150
+
151
+ return pd_cover(violate_graph, weight, coverset)
152
+
153
+
154
+ def min_hyper_vertex_cover(
155
+ hyprgraph, weight: MutableMapping, coverset: Optional[Set] = None
156
+ ) -> Tuple[Set, Union[int, float]]:
157
+ """
158
+ The `min_hyper_vertex_cover` function performs minimum weighted vertex cover using a primal-dual
159
+ approximation algorithm (without post-processing).
160
+
161
+ :param hyprgraph: The `hyprgraph` parameter represents a hypergraph, which is a generalization of a
162
+ graph where an edge can connect more than two vertices. It is likely represented as a data structure
163
+ that contains information about the vertices and edges of the hypergraph
164
+
165
+ :param weight: The `weight` parameter is a mutable mapping that assigns a weight to each vertex in
166
+ the hypergraph. It is used to determine the minimum weighted vertex cover
167
+
168
+ :type weight: MutableMapping
169
+
170
+ :param coverset: The `coverset` parameter is an optional set that represents the current vertex
171
+ cover. It contains the vertices that have been selected as part of the cover. If no `coverset` is
172
+ provided, it defaults to an empty set
173
+
174
+ :type coverset: Optional[Set]
175
+
176
+ :return: The function `min_hyper_vertex_cover` returns a tuple containing two elements. The first
177
+ element is a set representing the minimum weighted vertex cover, and the second element is either an
178
+ integer or a float representing the weight of the vertex cover.
179
+
180
+ .. svgbob::
181
+ :align: center
182
+
183
+ "({b, d, g, h}, 4)"
184
+
185
+ a b e g
186
+ o-------#-----+--o-------#
187
+ | |
188
+ ,--)--'
189
+ | |
190
+ | `--.
191
+ | |
192
+ o-------#--+-----o-------#
193
+ c d f h
194
+
195
+ """
196
+ if coverset is None:
197
+ coverset = set()
198
+
199
+ def violate_netlist() -> Generator:
200
+ for net in hyprgraph.nets:
201
+ if any(vtx in coverset for vtx in hyprgraph.ugraph[net]):
202
+ continue
203
+ yield hyprgraph.ugraph[net]
204
+
205
+ return pd_cover(violate_netlist, weight, coverset)
206
+
207
+
208
+ def _construct_cycle(info: Dict, parent, child) -> Deque:
209
+ """
210
+ The `_construct_cycle` function constructs a cycle by traversing the parent-child relationship in a
211
+ dictionary.
212
+
213
+ :param info: The `info` parameter is a dictionary that contains information about the nodes in a
214
+ graph. Each key in the dictionary represents a node, and the corresponding value is a tuple
215
+ containing two elements: the parent node and the depth of the node
216
+
217
+ :type info: Dict
218
+
219
+ :param parent: The parent parameter represents the parent node in a graph or tree structure
220
+
221
+ :param child: The `child` parameter represents a node in a graph that is connected to the `parent`
222
+ node
223
+
224
+ :return: a deque object.
225
+ """
226
+ _, depth_now = info[parent]
227
+ _, depth_child = info[child]
228
+ if depth_now < depth_child:
229
+ node_a, depth_a = parent, depth_now
230
+ node_b, depth_b = child, depth_child
231
+ else:
232
+ node_a, depth_a = child, depth_child
233
+ node_b, depth_b = parent, depth_now
234
+ S: Deque = deque()
235
+ while depth_a < depth_b:
236
+ S.append(node_a)
237
+ node_a, depth_a = info[node_a]
238
+ # depth_now == depth
239
+ while node_a != node_b:
240
+ S.append(node_a)
241
+ S.appendleft(node_b)
242
+ node_a, _ = info[node_a]
243
+ node_b, _ = info[node_b]
244
+ S.appendleft(node_b)
245
+ return S
246
+
247
+
248
+ def min_cycle_cover(
249
+ ugraph: nx.Graph, weight: MutableMapping, coverset: Optional[Set] = None
250
+ ) -> Tuple[Set, Union[int, float]]:
251
+ r"""
252
+ The `min_cycle_cover` function performs minimum cycle cover using a primal-dual approximation
253
+ algorithm (without post-processing).
254
+
255
+ :param ugraph: The `ugraph` parameter is a `nx.Graph` object representing the input graph. It contains the
256
+ nodes and edges of the graph
257
+
258
+ :type ugraph: nx.Graph
259
+
260
+ :param weight: The `weight` parameter is a dictionary that assigns a weight to each node in the
261
+ graph. The weights are used to determine the minimum cycle cover
262
+
263
+ :type weight: MutableMapping
264
+
265
+ :param coverset: The `coverset` parameter is an optional set that contains the nodes that are
266
+ already covered by previous cycles. It is used to keep track of the nodes that have already been
267
+ included in the minimum cycle cover. If no `coverset` is provided, it is initialized as an empty set
268
+
269
+ :type coverset: Optional[Set]
270
+
271
+ :return: The function `min_cycle_cover` returns a tuple containing a set and either an integer or a
272
+ float. The set represents the minimum cycle cover, and the integer or float represents the weight of
273
+ the minimum cycle cover.
274
+
275
+ .. svgbob::
276
+ :align: center
277
+
278
+ "({c, d}, 2)"
279
+
280
+ a b c
281
+ o-----o-----#
282
+ \ / \ \
283
+ \ / \ \
284
+ #-----o-----o
285
+ d e f
286
+
287
+ Examples:
288
+ >>> ugraph = nx.Graph()
289
+ >>> ugraph.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)])
290
+ >>> weight = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
291
+ >>> soln = set()
292
+ >>> min_cycle_cover(ugraph, weight, soln)
293
+ ({0, 1, 2}, 3)
294
+ """
295
+ if coverset is None:
296
+ coverset = set()
297
+
298
+ def find_cycle():
299
+ for info, parent, child in _generic_bfs_cycle(ugraph, coverset):
300
+ return _construct_cycle(info, parent, child)
301
+
302
+ def violate() -> Generator:
303
+ while True:
304
+ S = find_cycle()
305
+ if S is None:
306
+ break
307
+ yield S
308
+
309
+ return pd_cover(violate, weight, coverset)
310
+
311
+
312
+ def _generic_bfs_cycle(ugraph: nx.Graph, coverset: Set) -> Generator:
313
+ """
314
+ The function `_generic_bfs_cycle` performs a breadth-first search on a graph to find cycles,
315
+ excluding nodes in a given `coverset`.
316
+
317
+ :param ugraph: The parameter `ugraph` is a graph object that represents a directed graph. It should have a
318
+ method `neighbors(node)` that returns the neighbors of a given node in the graph. The graph can be
319
+ represented using any graph library or data structure that supports this method
320
+
321
+ :param coverset: The `coverset` parameter is a set of nodes that should be excluded from the BFS
322
+ traversal. These nodes will not be considered as potential starting points for the BFS algorithm
323
+ """
324
+ depth_limit = len(ugraph)
325
+ neighbors = ugraph.neighbors
326
+ nodelist = list(ugraph.nodes())
327
+ for source in nodelist:
328
+ if source in coverset:
329
+ continue
330
+ info = {source: (source, depth_limit)}
331
+ queue = deque([source])
332
+ while queue:
333
+ parent = queue.popleft()
334
+ succ, depth_now = info[parent]
335
+ for child in neighbors(parent):
336
+ if child in coverset:
337
+ continue
338
+ if child not in info:
339
+ info[child] = (parent, depth_now - 1)
340
+ queue.append(child)
341
+ continue
342
+ if succ == child:
343
+ continue
344
+ # cycle found
345
+ yield info, parent, child
346
+
347
+
348
+ def min_odd_cycle_cover(
349
+ ugraph: nx.Graph, weight: MutableMapping, coverset: Optional[Set] = None
350
+ ) -> Tuple[Set, Union[int, float]]:
351
+ r"""
352
+ The `min_odd_cycle_cover` function performs minimum odd cycle cover using a primal-dual
353
+ approximation algorithm (without post-processing).
354
+
355
+ :param ugraph: The `ugraph` parameter is a `nx.Graph` object representing the input graph. It is used to
356
+ define the graph structure and find cycles in the graph
357
+
358
+ :type ugraph: nx.Graph
359
+
360
+ :param weight: The `weight` parameter is a dictionary that assigns a weight to each node in the
361
+ graph
362
+
363
+ :type weight: MutableMapping
364
+
365
+ :param coverset: The `coverset` parameter is an optional set that represents the initial set of
366
+ vertices that are covered by the minimum odd cycle cover. This set can be empty if no vertices are
367
+ initially covered
368
+
369
+ :type coverset: Optional[Set]
370
+
371
+ :return: The function `min_odd_cycle_cover` returns a tuple containing a set and either an integer
372
+ or a float. The set represents the minimum odd cycle cover, and the integer or float represents the
373
+ weight of the cover.
374
+
375
+ .. svgbob::
376
+ :align: center
377
+
378
+ "({d}, 1)"
379
+
380
+ a b c
381
+ o-----o-----o
382
+ \ / \ \
383
+ \ / \ \
384
+ #-----o-----o
385
+ d e f
386
+
387
+ Examples:
388
+ >>> ugraph = nx.Graph()
389
+ >>> ugraph.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)])
390
+ >>> weight = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
391
+ >>> soln = set()
392
+ >>> min_odd_cycle_cover(ugraph, weight, soln)
393
+ ({0, 1, 2}, 3)
394
+ """
395
+ if coverset is None:
396
+ coverset = set()
397
+
398
+ def find_odd_cycle():
399
+ for info, parent, child in _generic_bfs_cycle(ugraph, coverset):
400
+ _, depth_child = info[child]
401
+ _, depth_parent = info[parent]
402
+ if (depth_parent - depth_child) % 2 == 0:
403
+ return _construct_cycle(info, parent, child)
404
+
405
+ def violate() -> Generator:
406
+ while True:
407
+ S = find_odd_cycle()
408
+ if S is None:
409
+ break
410
+ yield S
411
+
412
+ return pd_cover(violate, weight, coverset)
netlistx/graph_algo.py ADDED
@@ -0,0 +1,210 @@
1
+ """
2
+ Graph Algorithms (graph_algo.py)
3
+
4
+ This code contains two main functions that work with graphs to solve optimization problems. Let's break down what each function does in simple terms.
5
+
6
+ The first function, min_vertex_cover_fast, finds a minimum weighted vertex cover in a graph. A vertex cover is a set of vertices that includes at least one endpoint of every edge in the graph. The "weighted" part means that each vertex has a weight, and we want to find a cover with the lowest total weight.
7
+
8
+ This function takes three inputs:
9
+
10
+ 1. A graph (ugraph)
11
+ 2. A dictionary of weights for each vertex (weight)
12
+ 3. An optional set of vertices to start with (coverset)
13
+
14
+ It outputs two things:
15
+
16
+ 1. The set of vertices that form the cover
17
+ 2. The total weight of this cover
18
+
19
+ The function works by looking at each edge in the graph. If neither end of the edge is in the cover yet, it adds the end with the higher weight to the cover. It keeps track of the total weight and updates the remaining "gap" for each vertex. This process continues until all edges are covered.
20
+
21
+ The second function, min_maximal_independant_set, finds a minimum weighted maximal independent set in a graph. An independent set is a set of vertices where no two vertices are connected by an edge. "Maximal" means we can't add any more vertices to the set without breaking this rule. Like before, we want to find such a set with the lowest total weight.
22
+
23
+ This function takes four inputs:
24
+
25
+ 1. A graph (ugraph)
26
+ 2. A dictionary of weights for each vertex (weight)
27
+ 3. An optional set to start the independent set (indset)
28
+ 4. An optional set of dependent vertices (dep)
29
+
30
+ It outputs:
31
+
32
+ 1. The independent set of vertices
33
+ 2. The total weight of this set
34
+
35
+ The function works by looking at each vertex in the graph. For each vertex and its neighbors, it chooses the one with the lowest remaining weight to add to the independent set. It then marks this vertex and all its neighbors as "dependent" (they can't be added to the independent set). This process continues until all vertices are either in the independent set or marked as dependent.
36
+
37
+ Both functions use a technique called a primal-dual algorithm, which is a way of solving optimization problems. They both keep track of a "gap" for each vertex, which helps ensure that the solution is close to optimal.
38
+
39
+ These functions are useful in various graph theory applications, such as network design, scheduling problems, or resource allocation, where we need to find efficient ways to cover a graph or select non-adjacent elements.
40
+ """
41
+
42
+ import copy
43
+ from typing import MutableMapping, Optional, Set, Tuple, Union
44
+
45
+
46
+ def min_vertex_cover_fast(
47
+ ugraph, weight: MutableMapping, coverset: Optional[Set] = None
48
+ ) -> Tuple[Set, Union[int, float]]:
49
+ r"""
50
+ The `min_vertex_cover_fast` function performs minimum weighted vertex cover using a primal-dual
51
+ approximation algorithm (without post-processing).
52
+
53
+ :param ugraph: ugraph is a NetworkX graph object representing the graph on which the minimum weighted
54
+ vertex cover algorithm will be performed. It contains the nodes and edges of the graph
55
+
56
+ :param weight: The `weight` parameter is a mutable mapping that represents the weight of each vertex
57
+ in the graph. It is used to determine the minimum weighted vertex cover. The keys of the mapping are
58
+ the vertices of the graph, and the values are the corresponding weights
59
+
60
+ :type weight: MutableMapping
61
+
62
+ :param coverset: The `coverset` parameter is an optional set that represents the current vertex
63
+ cover. It is used to keep track of the vertices that are included in the cover. If no coverset is
64
+ provided, a new empty set is created
65
+
66
+ :type coverset: Optional[Set]
67
+
68
+ :return: The function `min_vertex_cover_fast` returns a tuple containing the vertex cover set and
69
+ the total weight of the vertex cover.
70
+
71
+ .. svgbob::
72
+ :align: center
73
+
74
+ "({b, d, e}, 3)"
75
+
76
+ b c d e
77
+ #-----o-----#-----o
78
+ | \ / \
79
+ | \ / \
80
+ o #-----o
81
+ a e f
82
+
83
+ Examples:
84
+ >>> import networkx as nx
85
+ >>> from netlistx.graph_algo import min_vertex_cover_fast
86
+ >>> ugraph = nx.Graph()
87
+ >>> ugraph.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)])
88
+ >>> weight = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
89
+ >>> coverset = set()
90
+ >>> min_vertex_cover_fast(ugraph, weight, coverset)
91
+ ({0, 1, 2, 3}, 4)
92
+ """
93
+ if coverset is None:
94
+ coverset = set()
95
+
96
+ total_dual_cost = 0 # for assertion
97
+ total_prml_cost = 0
98
+ gap = copy.copy(weight)
99
+
100
+ for utx, vtx in ugraph.edges():
101
+ if utx in coverset or vtx in coverset:
102
+ continue
103
+ if gap[utx] < gap[vtx]:
104
+ utx, vtx = vtx, utx # swap
105
+ coverset.add(vtx)
106
+ total_dual_cost += gap[vtx]
107
+ total_prml_cost += weight[vtx]
108
+ gap[utx] -= gap[vtx]
109
+ gap[vtx] = 0
110
+
111
+ assert total_dual_cost <= total_prml_cost
112
+ return coverset, total_prml_cost
113
+
114
+
115
+ def min_maximal_independant_set(
116
+ ugraph,
117
+ weight: MutableMapping,
118
+ indset: Optional[Set] = None,
119
+ dep: Optional[Set] = None,
120
+ ) -> Tuple[Set, Union[int, float]]:
121
+ r"""
122
+ The `min_maximal_independant_set` function performs minimum weighted maximal independent set using
123
+ primal-dual algorithm.
124
+
125
+ :param ugraph: ugraph is an undirected graph represented using the NetworkX library. It represents the
126
+ graph structure and contains the vertices and edges of the graph
127
+
128
+ :param weight: The `weight` parameter is a dictionary-like object that assigns a weight to each
129
+ vertex in the graph. The keys of the dictionary represent the vertices, and the values represent
130
+ their corresponding weights
131
+
132
+ :type weight: MutableMapping
133
+
134
+ :param indset: The `indset` parameter is a set that represents the current independent set. It is
135
+ initially set to `None` and is updated during the execution of the `min_maximal_independent_set`
136
+ function
137
+
138
+ :type indset: Optional[Set]
139
+
140
+ :param dep: The `dep` parameter is a set that represents the dependent vertices in the graph. These
141
+ are the vertices that are not included in the independent set and are adjacent to vertices in the
142
+ independent set. The `coverset` function is used to add a vertex and its adjacent vertices to the
143
+ dependent set
144
+
145
+ :type dep: Optional[Set]
146
+
147
+ :return: The function `min_maximal_independant_set` returns a tuple containing the minimum weighted
148
+ maximal independent set (indset) and the total primal cost (total_prml_cost).
149
+
150
+ .. svgbob::
151
+ :align: center
152
+
153
+ "({0, 3}, 2)"
154
+
155
+ 0 2 4
156
+ #-----o-----o
157
+ \ / \ /
158
+ \ / \ /
159
+ o-----#
160
+ 1 3
161
+
162
+ Examples:
163
+ >>> import networkx as nx
164
+ >>> from netlistx.graph_algo import min_maximal_independant_set
165
+ >>> ugraph = nx.Graph()
166
+ >>> ugraph.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)])
167
+ >>> weight = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}
168
+ >>> indset = set()
169
+ >>> dep = set()
170
+ >>> min_maximal_independant_set(ugraph, weight, indset, dep)
171
+ ({0, 3}, 2)
172
+ """
173
+ if indset is None:
174
+ indset = set()
175
+ if dep is None:
176
+ dep = set()
177
+
178
+ def coverset(utx):
179
+ dep.add(utx)
180
+ for vtx in ugraph[utx]:
181
+ dep.add(vtx)
182
+
183
+ gap = copy.copy(weight)
184
+ total_prml_cost = 0
185
+ total_dual_cost = 0
186
+ for utx in ugraph:
187
+ if utx in dep:
188
+ continue
189
+ if utx in indset: # pre-define indepentant
190
+ # coverset(utx)
191
+ continue
192
+ min_val = gap[utx]
193
+ min_vtx = utx
194
+ for vtx in ugraph[utx]:
195
+ if vtx in dep:
196
+ continue
197
+ if min_val > gap[vtx]:
198
+ min_val = gap[vtx]
199
+ min_vtx = vtx
200
+ indset.add(min_vtx)
201
+ coverset(min_vtx)
202
+ total_prml_cost += weight[min_vtx]
203
+ total_dual_cost += min_val
204
+ if min_vtx == utx:
205
+ continue
206
+ for vtx in ugraph[utx]:
207
+ gap[vtx] -= min_val
208
+
209
+ assert total_dual_cost <= total_prml_cost
210
+ return indset, total_prml_cost