14
14
#![ forbid( unsafe_code) ]
15
15
#![ warn( missing_docs) ]
16
16
17
-
18
17
extern crate petgraph as pg;
19
18
20
-
21
- pub use pg as petgraph;
22
- pub use pg:: graph:: { EdgeIndex , NodeIndex , EdgeWeightsMut , NodeWeightsMut } ;
23
- pub use walker:: Walker ;
24
19
use pg:: graph:: { DefIndex , GraphIndex , IndexType } ;
25
20
use std:: marker:: PhantomData ;
26
21
use std:: ops:: { Index , IndexMut } ;
27
22
23
+ pub use pg as petgraph;
24
+ pub use pg:: graph:: { EdgeIndex , NodeIndex , EdgeWeightsMut , NodeWeightsMut } ;
25
+ pub use walker:: Walker ;
28
26
29
27
pub mod walker;
30
28
@@ -37,6 +35,7 @@ pub type RawNodes<'a, N, Ix> = &'a [pg::graph::Node<N, Ix>];
37
35
/// Read only access into a **Dag**'s internal edge array.
38
36
pub type RawEdges < ' a , E , Ix > = & ' a [ pg:: graph:: Edge < E , Ix > ] ;
39
37
38
+
40
39
/// A Directed acyclic graph (DAG) data structure.
41
40
///
42
41
/// Dag is a thin wrapper around petgraph's `Graph` data structure, providing a refined API for
@@ -166,10 +165,9 @@ impl<N, E, Ix = DefIndex> Dag<N, E, Ix> where Ix: IndexType {
166
165
/// If adding the edge **would** cause the graph to cycle, the edge will not be added and
167
166
/// instead a `WouldCycle<E>` error with the given weight will be returned.
168
167
///
169
- /// Computes in **O(t)** time where "t" is the time taken to check if adding the edge would
170
- /// cause a cycle in the graph. See petgraph's [`is_cyclic_directed`]
168
+ /// In the worst case, petgraph's [`is_cyclic_directed`]
171
169
/// (http://bluss.github.io/petulant-avenger-graphlibrary/doc/petgraph/algo/fn.is_cyclic_directed.html)
172
- /// function for more details .
170
+ /// function is used to check whether or not adding the edge would create a cycle .
173
171
///
174
172
/// **Note:** Dag allows adding parallel ("duplicate") edges. If you want to avoid this, use
175
173
/// [`update_edge`](./struct.Dag.html#method.update_edge) instead.
@@ -178,18 +176,24 @@ impl<N, E, Ix = DefIndex> Dag<N, E, Ix> where Ix: IndexType {
178
176
/// some other node, consider using the [add_child](./struct.Dag.html#method.add_child) or
179
177
/// [add_parent](./struct.Dag.html#method.add_parent) methods instead for better performance.
180
178
///
181
- /// **Panics** if the Graph is at the maximum number of nodes for its index type.
179
+ /// **Panics** if either `a` or `b` do not exist within the **Dag**.
180
+ ///
181
+ /// **Panics** if the Graph is at the maximum number of edges for its index type.
182
182
pub fn add_edge ( & mut self , a : NodeIndex < Ix > , b : NodeIndex < Ix > , weight : E )
183
183
-> Result < EdgeIndex < Ix > , WouldCycle < E > >
184
184
{
185
+ let should_check_for_cycle = must_check_for_cycle ( self , a, b) ;
186
+
185
187
let idx = self . graph . add_edge ( a, b, weight) ;
186
188
187
189
// Check if adding the edge has created a cycle.
188
- // TODO: Once petgraph adds support for re-using visit stack/maps, use that so that we
189
- // don't have to re-allocate every time `add_edge` is called.
190
- if pg:: algo:: is_cyclic_directed ( & self . graph ) {
190
+ //
191
+ // TODO: Re-use a `pg::visit::Topo` for this. Perhaps store this in the `Dag` itself?
192
+ if should_check_for_cycle && pg:: algo:: is_cyclic_directed ( & self . graph ) {
191
193
let weight = self . graph . remove_edge ( idx) . expect ( "No edge for index" ) ;
192
194
Err ( WouldCycle ( weight) )
195
+
196
+ // If no cycles were found, we're done.
193
197
} else {
194
198
Ok ( idx)
195
199
}
@@ -223,26 +227,34 @@ impl<N, E, Ix = DefIndex> Dag<N, E, Ix> where Ix: IndexType {
223
227
///
224
228
/// **Note:** If you're adding a series of new nodes and edges to a single node, consider using
225
229
/// the [add_child](./struct.Dag.html#method.add_child) or [add_parent]
226
- /// (./struct.Dag.html#method.add_parent) methods instead for better performance. These
227
- /// perform better as there is no need to check for cycles.
230
+ /// (./struct.Dag.html#method.add_parent) methods instead for greater convenience.
228
231
///
229
232
/// **Panics** if the Graph is at the maximum number of nodes for its index type.
230
233
pub fn add_edges < I > ( & mut self , edges : I ) -> Result < EdgeIndices < Ix > , WouldCycle < Vec < E > > > where
231
- I : :: std :: iter :: IntoIterator < Item =( NodeIndex < Ix > , NodeIndex < Ix > , E ) > ,
234
+ I : IntoIterator < Item =( NodeIndex < Ix > , NodeIndex < Ix > , E ) > ,
232
235
{
233
236
let mut num_edges = 0 ;
237
+ let mut should_check_for_cycle = false ;
238
+
234
239
for ( a, b, weight) in edges {
240
+ // Check whether or not we'll need to check for cycles.
241
+ if !should_check_for_cycle {
242
+ if must_check_for_cycle ( self , a, b) {
243
+ should_check_for_cycle = true ;
244
+ }
245
+ }
246
+
235
247
self . graph . add_edge ( a, b, weight) ;
236
248
num_edges += 1 ;
237
249
}
238
250
239
251
let total_edges = self . edge_count ( ) ;
240
- let new_edges_range = total_edges-num_edges .. total_edges;
252
+ let new_edges_range = total_edges-num_edges.. total_edges;
241
253
242
254
// Check if adding the edges has created a cycle.
243
- // TODO: Once petgraph adds support for re-using visit stack/maps, use that so that we
244
- // don't have to re-allocate every time `add_edges` is called.
245
- if pg:: algo:: is_cyclic_directed ( & self . graph ) {
255
+ //
256
+ // TODO: Re-use a `pg::visit::Topo` for this. Perhaps store this in the `Dag` itself?
257
+ if should_check_for_cycle && pg:: algo:: is_cyclic_directed ( & self . graph ) {
246
258
let removed_edges = new_edges_range. rev ( ) . filter_map ( |i| {
247
259
let idx = EdgeIndex :: new ( i) ;
248
260
self . graph . remove_edge ( idx)
@@ -269,7 +281,7 @@ impl<N, E, Ix = DefIndex> Dag<N, E, Ix> where Ix: IndexType {
269
281
///
270
282
/// **Note:** If you're adding a new node and immediately adding a single edge to that node from
271
283
/// some parent node, consider using the [`add_child`](./struct.Dag.html#method.add_child)
272
- /// method instead for better performance .
284
+ /// method instead for greater convenience .
273
285
///
274
286
/// **Panics** if the Graph is at the maximum number of nodes for its index type.
275
287
pub fn update_edge ( & mut self , a : NodeIndex < Ix > , b : NodeIndex < Ix > , weight : E )
@@ -469,6 +481,19 @@ impl<N, E, Ix = DefIndex> Dag<N, E, Ix> where Ix: IndexType {
469
481
}
470
482
471
483
484
+ /// After adding a new edge to the graph, we use this function immediately after to check whether
485
+ /// or not we need to check for a cycle.
486
+ ///
487
+ /// If our parent *a* has no parents or our child *b* has no children, or if there was already an
488
+ /// edge connecting *a* to *b*, we know that adding this edge has not caused the graph to cycle.
489
+ fn must_check_for_cycle < N , E , Ix > ( dag : & Dag < N , E , Ix > , a : NodeIndex < Ix > , b : NodeIndex < Ix > ) -> bool
490
+ where Ix : IndexType ,
491
+ {
492
+ dag. parents ( a) . next ( dag) . is_some ( ) && dag. children ( b) . next ( dag) . is_some ( )
493
+ && dag. find_edge ( a, b) . is_none ( )
494
+ }
495
+
496
+
472
497
impl < N , E , Ix > Index < NodeIndex < Ix > > for Dag < N , E , Ix > where Ix : IndexType {
473
498
type Output = N ;
474
499
fn index ( & self , index : NodeIndex < Ix > ) -> & N {
0 commit comments