Skip to content

Commit 349e56a

Browse files
committed
modify code that serves CI/CD
1 parent b2fa7ba commit 349e56a

File tree

4 files changed

+156
-158
lines changed

4 files changed

+156
-158
lines changed

rustworkx-core/src/connectivity/minimal_cycle_basis.rs

Lines changed: 153 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ use std::cmp::Ordering;
1717
use std::convert::Infallible;
1818
use std::hash::Hash;
1919

20+
type SubgraphResult<K, E> = Result<Vec<SubgraphData<K>>, E>;
21+
type SubgraphData<K> = (
22+
Graph<usize, K, Undirected>,
23+
HashMap<usize, NodeIndex>,
24+
HashMap<usize, EdgeIndex>,
25+
);
26+
2027
fn create_subgraphs_from_components<G, F, K, E>(
2128
graph: G,
2229
components: Vec<HashSet<G::NodeId>>,
2330
mut weight_fn: F,
24-
) -> Result<
25-
Vec<(
26-
Graph<usize, K, Undirected>,
27-
HashMap<usize, NodeIndex>,
28-
HashMap<usize, EdgeIndex>,
29-
)>,
30-
E,
31-
>
31+
) -> SubgraphResult<K, E>
3232
where
3333
G: IntoEdgeReferences
3434
+ NodeIndexable
@@ -78,47 +78,118 @@ where
7878
})
7979
.collect()
8080
}
81-
pub fn minimal_cycle_basis<G, F, K, E>(graph: G, mut weight_fn: F) -> Result<Vec<Vec<NodeIndex>>, E>
81+
82+
fn _min_cycle<H, F, K, E>(
83+
subgraph: H,
84+
orth: HashSet<(usize, usize)>,
85+
mut weight_fn: F,
86+
) -> Result<Vec<(usize, usize)>, E>
8287
where
83-
G: EdgeCount
84-
+ IntoNodeIdentifiers
85-
+ NodeIndexable
86-
+ EdgeIndexable
87-
+ DataMap
88-
+ GraphProp
89-
+ IntoNeighborsDirected
90-
+ Visitable
91-
+ IntoEdges,
92-
G::EdgeWeight: Clone,
93-
G::NodeId: Eq + Hash,
94-
F: FnMut(G::EdgeRef) -> Result<K, E>,
88+
H: IntoNodeReferences + IntoEdgeReferences + DataMap + NodeIndexable + EdgeIndexable,
89+
H::NodeId: Eq + Hash,
90+
F: FnMut(H::EdgeRef) -> Result<K, E>,
9591
K: Clone + PartialOrd + Copy + Measure + Default,
9692
{
97-
let conn_components = connected_components(&graph);
98-
let mut min_cycle_basis = Vec::new();
99-
let subgraphs_with_maps =
100-
create_subgraphs_from_components(&graph, conn_components, &mut weight_fn)?;
101-
// Convert weight_fn to a closure that takes a subgraph edge and returns the weight of the original graph edge
102-
for (subgraph, node_subnode_map, edge_subedge_map) in subgraphs_with_maps {
103-
// Find the key of edge_subedge_map that corresponds to the value e.id()
104-
let mut subgraph_weight_fn = |e: <&Graph<usize, K, Undirected, DefaultIx> as IntoEdgeReferences>::EdgeRef| -> Result<K, Infallible> {
105-
// use the edge_subedge_map to find the key that corresponds to the value e.id()
106-
let edge = edge_subedge_map.iter().find(|(_key, &value)| value == e.id()).unwrap().0;
107-
match weight_fn(
108-
graph.edge_references().nth(*edge).unwrap(),
109-
) {
110-
Ok(weight) => Ok(weight),
111-
Err(_) => {
112-
// Handle the error here. Since the error type is Infallible, this branch should never be reached.
113-
unreachable!()
93+
let mut gi = Graph::<_, _, petgraph::Undirected>::default();
94+
let mut subgraph_gi_map = HashMap::new();
95+
let mut gi_subgraph_map = HashMap::new();
96+
for node in subgraph.node_identifiers() {
97+
let gi_node = gi.add_node(node);
98+
let gi_lifted_node = gi.add_node(node);
99+
gi_subgraph_map.insert(gi_node, node);
100+
gi_subgraph_map.insert(gi_lifted_node, node);
101+
subgraph_gi_map.insert(node, (gi_node, gi_lifted_node));
102+
}
103+
// # Add 2 copies of each edge in G to Gi.
104+
// # If edge is in orth, add cross edge; otherwise in-plane edge
105+
106+
for edge in subgraph.edge_references() {
107+
let u_id = edge.source();
108+
let v_id = edge.target();
109+
let u = NodeIndexable::to_index(&subgraph, u_id);
110+
let v = NodeIndexable::to_index(&subgraph, v_id);
111+
let weight = weight_fn(edge)?;
112+
// For each pair of (u, v) from the subgraph, there is a corresponding double pair of (u_node, v_node) and (u_lifted_node, v_lifted_node) in the gi
113+
let (u_node, u_lifted_node) = subgraph_gi_map[&u_id];
114+
let (v_node, v_lifted_node) = subgraph_gi_map[&v_id];
115+
if orth.contains(&(u, v)) || orth.contains(&(v, u)) {
116+
// Add cross edges with weight
117+
gi.add_edge(u_node, v_lifted_node, weight);
118+
gi.add_edge(u_lifted_node, v_node, weight);
119+
} else {
120+
// Add in-plane edges with weight
121+
gi.add_edge(u_node, v_node, weight);
122+
gi.add_edge(u_lifted_node, v_lifted_node, weight);
123+
}
124+
}
125+
// Instead of finding the shortest path between each node and its lifted node, store the shortest paths in a list to find the shortest paths among them
126+
let mut shortest_path_map: HashMap<H::NodeId, K> = HashMap::new();
127+
for subnodeid in subgraph.node_identifiers() {
128+
let (gi_nodeidx, gi_lifted_nodeidx) = subgraph_gi_map[&subnodeid];
129+
130+
let result: Result<DictMap<NodeIndex, K>> = dijkstra(
131+
&gi,
132+
gi_nodeidx,
133+
Some(gi_lifted_nodeidx),
134+
|edge| Ok(*edge.weight()),
135+
None,
136+
);
137+
let spl = result.unwrap()[&gi_lifted_nodeidx];
138+
shortest_path_map.insert(subnodeid, spl);
139+
}
140+
let min_start = shortest_path_map
141+
.iter()
142+
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(Ordering::Equal))
143+
.unwrap()
144+
.0;
145+
let min_start_node = subgraph_gi_map[min_start].0;
146+
let min_start_lifted_node = subgraph_gi_map[min_start].1;
147+
let result: Result<Option<(K, Vec<NodeIndex>)>> = astar(
148+
&gi,
149+
min_start_node,
150+
|finish| Ok(finish == min_start_lifted_node),
151+
|e| Ok(*e.weight()),
152+
|_| Ok(K::default()),
153+
);
154+
155+
let mut min_path: Vec<usize> = Vec::new();
156+
match result {
157+
Ok(Some((_cost, path))) => {
158+
for node in path {
159+
if let Some(&subgraph_nodeid) = gi_subgraph_map.get(&node) {
160+
let subgraph_node = NodeIndexable::to_index(&subgraph, subgraph_nodeid);
161+
min_path.push(subgraph_node.index());
114162
}
115163
}
116-
};
117-
let num_cycles: Result<Vec<Vec<NodeIndex>>, Infallible> =
118-
_min_cycle_basis(&subgraph, &mut subgraph_weight_fn, &node_subnode_map);
119-
min_cycle_basis.extend(num_cycles.unwrap());
164+
}
165+
Ok(None) => {}
166+
Err(_) => {}
120167
}
121-
Ok(min_cycle_basis)
168+
let edgelist = min_path
169+
.windows(2)
170+
.map(|w| (w[0], w[1]))
171+
.collect::<Vec<_>>();
172+
let mut edgeset: HashSet<(usize, usize)> = HashSet::new();
173+
for e in edgelist.iter() {
174+
if edgeset.contains(e) {
175+
edgeset.remove(e);
176+
} else if edgeset.contains(&(e.1, e.0)) {
177+
edgeset.remove(&(e.1, e.0));
178+
} else {
179+
edgeset.insert(*e);
180+
}
181+
}
182+
let mut min_edgelist: Vec<(usize, usize)> = Vec::new();
183+
for e in edgelist.iter() {
184+
if edgeset.contains(e) {
185+
min_edgelist.push(*e);
186+
edgeset.remove(e);
187+
} else if edgeset.contains(&(e.1, e.0)) {
188+
min_edgelist.push((e.1, e.0));
189+
edgeset.remove(&(e.1, e.0));
190+
}
191+
}
192+
Ok(min_edgelist)
122193
}
123194

124195
fn _min_cycle_basis<H, F, K, E>(
@@ -182,7 +253,7 @@ where
182253
}
183254
while let Some(chord_pop) = set_orth.pop() {
184255
let base = chord_pop;
185-
let cycle_edges = _min_cycle(&subgraph, base.clone(), &mut weight_fn)?;
256+
let cycle_edges = _min_cycle(subgraph, base.clone(), &mut weight_fn)?;
186257
let mut cb_temp: Vec<usize> = Vec::new();
187258
for edge in cycle_edges.iter() {
188259
cb_temp.push(edge.1);
@@ -192,7 +263,7 @@ where
192263
let mut new_orth = HashSet::new();
193264
if cycle_edges
194265
.iter()
195-
.filter(|edge| orth.contains(*edge) || orth.contains(&((*edge).1, (*edge).0)))
266+
.filter(|edge| orth.contains(*edge) || orth.contains(&(edge.1, edge.0)))
196267
.count()
197268
% 2
198269
== 1
@@ -209,7 +280,8 @@ where
209280
}
210281
*orth = new_orth;
211282
} else {
212-
*orth = orth.clone();
283+
let orth_clone = orth.clone();
284+
orth.clone_from(&orth_clone);
213285
}
214286
}
215287
}
@@ -235,118 +307,47 @@ where
235307
Ok(cb)
236308
}
237309

238-
fn _min_cycle<H, F, K, E>(
239-
subgraph: H,
240-
orth: HashSet<(usize, usize)>,
241-
mut weight_fn: F,
242-
) -> Result<Vec<(usize, usize)>, E>
310+
pub fn minimal_cycle_basis<G, F, K, E>(graph: G, mut weight_fn: F) -> Result<Vec<Vec<NodeIndex>>, E>
243311
where
244-
H: IntoNodeReferences + IntoEdgeReferences + DataMap + NodeIndexable + EdgeIndexable,
245-
H::NodeId: Eq + Hash,
246-
F: FnMut(H::EdgeRef) -> Result<K, E>,
312+
G: EdgeCount
313+
+ IntoNodeIdentifiers
314+
+ NodeIndexable
315+
+ EdgeIndexable
316+
+ DataMap
317+
+ GraphProp
318+
+ IntoNeighborsDirected
319+
+ Visitable
320+
+ IntoEdges,
321+
G::EdgeWeight: Clone,
322+
G::NodeId: Eq + Hash,
323+
F: FnMut(G::EdgeRef) -> Result<K, E>,
247324
K: Clone + PartialOrd + Copy + Measure + Default,
248325
{
249-
let mut gi = Graph::<_, _, petgraph::Undirected>::default();
250-
let mut subgraph_gi_map = HashMap::new();
251-
let mut gi_subgraph_map = HashMap::new();
252-
for node in subgraph.node_identifiers() {
253-
let gi_node = gi.add_node(node);
254-
let gi_lifted_node = gi.add_node(node);
255-
gi_subgraph_map.insert(gi_node, node);
256-
gi_subgraph_map.insert(gi_lifted_node, node);
257-
subgraph_gi_map.insert(node, (gi_node, gi_lifted_node));
258-
}
259-
// # Add 2 copies of each edge in G to Gi.
260-
// # If edge is in orth, add cross edge; otherwise in-plane edge
261-
262-
for edge in subgraph.edge_references() {
263-
let u_id = edge.source();
264-
let v_id = edge.target();
265-
let u = NodeIndexable::to_index(&subgraph, u_id);
266-
let v = NodeIndexable::to_index(&subgraph, v_id);
267-
let edge_cost = Some(weight_fn(edge)?);
268-
let weight = edge_cost.clone().unwrap();
269-
// For each pair of (u, v) from the subgraph, there is a corresponding double pair of (u_node, v_node) and (u_lifted_node, v_lifted_node) in the gi
270-
let (u_node, u_lifted_node) = subgraph_gi_map[&u_id];
271-
let (v_node, v_lifted_node) = subgraph_gi_map[&v_id];
272-
if orth.contains(&(u, v)) || orth.contains(&(v, u)) {
273-
// Add cross edges with weight
274-
gi.add_edge(u_node, v_lifted_node, weight);
275-
gi.add_edge(u_lifted_node, v_node, weight);
276-
} else {
277-
// Add in-plane edges with weight
278-
gi.add_edge(u_node, v_node, weight);
279-
gi.add_edge(u_lifted_node, v_lifted_node, weight);
280-
}
281-
}
282-
// Instead of finding the shortest path between each node and its lifted node, store the shortest paths in a list to find the shortest paths among them
283-
let mut shortest_path_map: HashMap<H::NodeId, K> = HashMap::new();
284-
for subnodeid in subgraph.node_identifiers() {
285-
let (gi_nodeidx, gi_lifted_nodeidx) = subgraph_gi_map[&subnodeid];
286-
287-
let result: Result<DictMap<NodeIndex, K>> = dijkstra(
288-
&gi,
289-
gi_nodeidx,
290-
Some(gi_lifted_nodeidx),
291-
|edge| Ok(*edge.weight()),
292-
None,
293-
);
294-
let spl = result.unwrap()[&gi_lifted_nodeidx];
295-
shortest_path_map.insert(subnodeid, spl);
296-
}
297-
let min_start = shortest_path_map
298-
.iter()
299-
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(Ordering::Equal))
300-
.unwrap()
301-
.0;
302-
let min_start_node = subgraph_gi_map[min_start].0;
303-
let min_start_lifted_node = subgraph_gi_map[min_start].1;
304-
let result: Result<Option<(K, Vec<NodeIndex>)>> = astar(
305-
&gi,
306-
min_start_node.clone(),
307-
|finish| Ok(finish == min_start_lifted_node.clone()),
308-
|e| Ok(*e.weight()),
309-
|_| Ok(K::default()),
310-
);
311-
312-
let mut min_path: Vec<usize> = Vec::new();
313-
match result {
314-
Ok(Some((_cost, path))) => {
315-
for node in path {
316-
if let Some(&subgraph_nodeid) = gi_subgraph_map.get(&node) {
317-
let subgraph_node = NodeIndexable::to_index(&subgraph, subgraph_nodeid);
318-
min_path.push(subgraph_node.index());
326+
let conn_components = connected_components(&graph);
327+
let mut min_cycle_basis = Vec::new();
328+
let subgraphs_with_maps =
329+
create_subgraphs_from_components(&graph, conn_components, &mut weight_fn)?;
330+
// Convert weight_fn to a closure that takes a subgraph edge and returns the weight of the original graph edge
331+
for (subgraph, node_subnode_map, edge_subedge_map) in subgraphs_with_maps {
332+
// Find the key of edge_subedge_map that corresponds to the value e.id()
333+
let mut subgraph_weight_fn = |e: <&Graph<usize, K, Undirected, DefaultIx> as IntoEdgeReferences>::EdgeRef| -> Result<K, Infallible> {
334+
// use the edge_subedge_map to find the key that corresponds to the value e.id()
335+
let edge = edge_subedge_map.iter().find(|(_key, &value)| value == e.id()).unwrap().0;
336+
match weight_fn(
337+
graph.edge_references().nth(*edge).unwrap(),
338+
) {
339+
Ok(weight) => Ok(weight),
340+
Err(_) => {
341+
// Handle the error here. Since the error type is Infallible, this branch should never be reached.
342+
unreachable!()
319343
}
320344
}
321-
}
322-
Ok(None) => {}
323-
Err(_) => {}
324-
}
325-
let edgelist = min_path
326-
.windows(2)
327-
.map(|w| (w[0], w[1]))
328-
.collect::<Vec<_>>();
329-
let mut edgeset: HashSet<(usize, usize)> = HashSet::new();
330-
for e in edgelist.iter() {
331-
if edgeset.contains(e) {
332-
edgeset.remove(e);
333-
} else if edgeset.contains(&(e.1, e.0)) {
334-
edgeset.remove(&(e.1, e.0));
335-
} else {
336-
edgeset.insert(*e);
337-
}
338-
}
339-
let mut min_edgelist: Vec<(usize, usize)> = Vec::new();
340-
for e in edgelist.iter() {
341-
if edgeset.contains(e) {
342-
min_edgelist.push(*e);
343-
edgeset.remove(e);
344-
} else if edgeset.contains(&(e.1, e.0)) {
345-
min_edgelist.push((e.1, e.0));
346-
edgeset.remove(&(e.1, e.0));
347-
}
345+
};
346+
let num_cycles: Result<Vec<Vec<NodeIndex>>, Infallible> =
347+
_min_cycle_basis(&subgraph, &mut subgraph_weight_fn, &node_subnode_map);
348+
min_cycle_basis.extend(num_cycles.unwrap());
348349
}
349-
Ok(min_edgelist)
350+
Ok(min_cycle_basis)
350351
}
351352

352353
#[cfg(test)]

rustworkx/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ def minimum_cycle_basis(graph, edge_cost_fn):
583583
"""
584584
raise TypeError("Invalid Input Type %s for graph" % type(graph))
585585

586+
586587
@_rustworkx_dispatch
587588
def dijkstra_shortest_path_lengths(graph, node, edge_cost_fn, goal=None):
588589
"""Compute the lengths of the shortest paths for a graph object using

rustworkx/rustworkx.pyi

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,7 @@ def stoer_wagner_min_cut(
248248
weight_fn: Callable[[_T], float] | None = ...,
249249
) -> tuple[float, NodeIndices] | None: ...
250250
def graph_minimum_cycle_basis(
251-
graph: PyGraph[_S, _T],
252-
/,
253-
weight_fn: Callable[[_T], float] | None = ...
251+
graph: PyGraph[_S, _T], /, weight_fn: Callable[[_T], float] | None = ...
254252
) -> list[list[NodeIndices]] | None: ...
255253
def simple_cycles(graph: PyDiGraph, /) -> Iterator[NodeIndices]: ...
256254
def graph_isolates(graph: PyGraph) -> NodeIndices: ...

src/connectivity/minimum_cycle_basis.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ pub fn minimum_cycle_basis_map<Ty: EdgeType + Sync>(
1616
graph: &StablePyGraph<Ty>,
1717
edge_cost_fn: PyObject,
1818
) -> PyResult<Vec<Vec<NodeIndex>>> {
19-
if graph.node_count() == 0 {
20-
return Ok(vec![]);
21-
} else if graph.edge_count() == 0 {
19+
if graph.node_count() == 0 || graph.edge_count() == 0 {
2220
return Ok(vec![]);
2321
}
2422
let edge_cost_callable = CostFn::from(edge_cost_fn);

0 commit comments

Comments
 (0)