├── .gitignore ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-MIT ├── README.md └── src ├── graph ├── default_graph.rs ├── error.rs ├── graph.rs └── mod.rs ├── lib.rs ├── matching ├── blossom.rs ├── forest.rs ├── greedy.rs ├── marker.rs ├── maximum_matching.rs ├── mod.rs └── pairing.rs ├── selection ├── components.rs └── mod.rs └── traversal ├── breadth_first.rs ├── depth_first.rs ├── mod.rs └── step.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode 5 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 The Gamma Project Developers 2 | 3 | For full authorship information, see the version control history. 4 | 5 | Except as otherwise noted (below and/or in individual files), Gamma is 6 | licensed under the MIT license or . -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gamma" 3 | version = "0.9.0" 4 | authors = ["The Gamma Project Developers"] 5 | license = "MIT" 6 | edition = "2018" 7 | readme = "README.md" 8 | repository = "https://github.com/metamolecular/gamma" 9 | description = """ 10 | Graph primitives and traversals for Rust. 11 | """ 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 The Gamma Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gamma 2 | 3 | A graph library for Rust. 4 | 5 | Gamma provides primitives and traversals for working with [graphs](https://en.wikipedia.org/wiki/Graph_theory). It is based on ideas presented in *[A Minimal Graph API](https://depth-first.com/articles/2020/01/06/a-minimal-graph-api/)*. 6 | 7 | ## Usage 8 | 9 | Add this to your `Cargo.toml`: 10 | 11 | ```toml 12 | [dependencies] 13 | gamma = 0.9.0 14 | ``` 15 | 16 | ## Examples 17 | 18 | `ArrayGraph` is a reference `Graph` implementation. Node, neighbor, and 19 | edge iteration order are stable and set by the `try_from` method. 20 | 21 | ```rust 22 | use std::convert::TryFrom; 23 | use gamma::graph::{ Graph, DefaultGraph, Error }; 24 | 25 | fn main() -> Result<(), Error> { 26 | let p3 = DefaultGraph::try_from(vec![ 27 | vec![ 1 ], 28 | vec![ 0, 2 ], 29 | vec![ 1 ] 30 | ])?; 31 | 32 | assert_eq!(p3.is_empty(), false); 33 | assert_eq!(p3.order(), 3); 34 | assert_eq!(p3.size(), 2); 35 | assert_eq!(p3.ids().collect::>(), [ 0, 1, 2 ]); 36 | assert_eq!(p3.neighbors(1)?.collect::>(), [ 0, 2 ]); 37 | assert_eq!(p3.has_id(4), false); 38 | assert_eq!(p3.degree(0)?, 1); 39 | assert_eq!(p3.edges().collect::>(), [ 40 | (0, 1), 41 | (1, 2) 42 | ]); 43 | assert_eq!(p3.has_edge(1, 2)?, true); 44 | 45 | let result = DefaultGraph::try_from(vec![ 46 | vec![ 1 ] 47 | ]); 48 | 49 | assert_eq!(result, Err(Error::UnknownId(1))); 50 | 51 | Ok(()) 52 | } 53 | ``` 54 | 55 | Features include: 56 | 57 | - depth-first and breadth-first traversal 58 | - connected components 59 | - maximum matching using [Edmonds' Blossom algorithm](https://depth-first.com/articles/2020/09/28/edmonds-blossom-algorithm-part-1-cast-of-characters/) 60 | 61 | ## Versions 62 | 63 | Gamma is not yet stable. Patch versions never introduce breaking changes, but minor/major versions probably will. 64 | 65 | ## License 66 | 67 | Gamma is distributed under the terms of the MIT License. See 68 | [LICENSE-MIT](LICENSE-MIT) and [COPYRIGHT](COPYRIGHT) for details. -------------------------------------------------------------------------------- /src/graph/default_graph.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use std::collections::HashMap; 3 | use std::collections::hash_map::Entry; 4 | use std::cmp::PartialEq; 5 | 6 | use super::{ Graph, Error }; 7 | use crate::traversal::DepthFirst; 8 | 9 | /// An undirected Graph backed by an adjacency matrix. Nodes and neighbors are 10 | /// iterated in the order in which they're added. 11 | /// 12 | /// ```rust 13 | /// use std::convert::TryFrom; 14 | /// use gamma::graph::{ Graph, Error, DefaultGraph }; 15 | /// 16 | /// fn main() -> Result<(), Error> { 17 | /// let mut c3 = DefaultGraph::try_from(vec![ 18 | /// vec![ 1 ], 19 | /// vec![ 0, 2 ], 20 | /// vec![ 1 ] 21 | /// ])?; 22 | /// 23 | /// assert_eq!(c3.ids().collect::>(), vec![ 0, 1, 2 ]); 24 | /// 25 | /// assert_eq!(c3.add_edge(0, 1), Err(Error::DuplicateEdge(0, 1))); 26 | /// 27 | /// Ok(()) 28 | /// } 29 | /// ``` 30 | #[derive(Debug)] 31 | pub struct DefaultGraph { 32 | indices: HashMap, 33 | adjacency: Vec>, 34 | ids: Vec, 35 | edges: Vec<(usize, usize)> 36 | } 37 | 38 | impl DefaultGraph { 39 | pub fn new() -> Self { 40 | Self { 41 | indices: HashMap::new(), 42 | adjacency: Vec::new(), 43 | ids: Vec::new(), 44 | edges: Vec::new() 45 | } 46 | } 47 | 48 | pub fn add_node(&mut self, id: usize) -> Result<(), Error> { 49 | match self.indices.entry(id) { 50 | Entry::Occupied(_) => return Err(Error::DuplicateId(id)), 51 | Entry::Vacant(entry) => { 52 | entry.insert(self.ids.len()); 53 | } 54 | } 55 | 56 | self.ids.push(id); 57 | self.adjacency.push(vec![ ]); 58 | 59 | Ok(()) 60 | } 61 | 62 | pub fn add_edge(&mut self, sid: usize, tid: usize) -> Result<(), Error> { 63 | let &source_index = match self.indices.get(&sid) { 64 | Some(index) => index, 65 | None => return Err(Error::UnknownId(sid)) 66 | }; 67 | let &target_index = match self.indices.get(&tid) { 68 | Some(index) => index, 69 | None => return Err(Error::UnknownId(tid)) 70 | }; 71 | 72 | if self.adjacency[source_index].contains(&tid) { 73 | return Err(Error::DuplicateEdge(sid, tid)); 74 | } 75 | 76 | self.adjacency[source_index].push(tid); 77 | self.adjacency[target_index].push(sid); 78 | self.edges.push((sid, tid)); 79 | 80 | Ok(()) 81 | } 82 | 83 | fn index_for(&self, id: usize) -> Result { 84 | match self.indices.get(&id) { 85 | Some(index) => Ok(*index), 86 | None => Err(Error::UnknownId(id)) 87 | } 88 | } 89 | } 90 | 91 | impl Graph for DefaultGraph { 92 | fn is_empty(&self) -> bool { 93 | self.ids.is_empty() 94 | } 95 | 96 | fn order(&self) -> usize { 97 | self.ids.len() 98 | } 99 | 100 | fn size(&self) -> usize { 101 | self.edges.len() 102 | } 103 | 104 | fn ids(&self) -> Box + '_> { 105 | Box::new(self.ids.iter().cloned()) 106 | } 107 | 108 | fn neighbors( 109 | &self, id: usize 110 | ) -> Result + '_>, Error> { 111 | let index = self.index_for(id)?; 112 | 113 | Ok(Box::new(self.adjacency[index].iter().cloned())) 114 | } 115 | 116 | fn has_id(&self, id: usize) -> bool { 117 | self.indices.contains_key(&id) 118 | } 119 | 120 | fn degree(&self, id: usize) -> Result { 121 | let index = self.index_for(id)?; 122 | 123 | Ok(self.adjacency[index].len()) 124 | } 125 | 126 | fn edges(&self) -> Box + '_> { 127 | Box::new(self.edges.iter().cloned()) 128 | } 129 | 130 | fn has_edge(&self, sid: usize, tid: usize) -> Result { 131 | let index = self.index_for(sid)?; 132 | 133 | if self.indices.contains_key(&tid) { 134 | Ok(self.adjacency[index].contains(&tid)) 135 | } else { 136 | return Err(Error::UnknownId(tid)); 137 | } 138 | } 139 | } 140 | 141 | impl TryFrom>> for DefaultGraph { 142 | type Error = Error; 143 | 144 | fn try_from(adjacency: Vec>) -> Result { 145 | let mut result = Self::new(); 146 | 147 | for (sid, neighbors) in adjacency.iter().enumerate() { 148 | for (index, &tid) in neighbors.iter().enumerate() { 149 | if tid >= adjacency.len() { 150 | return Err(Error::UnknownId(tid)); 151 | } else if neighbors[index+1..].contains(&tid) { 152 | return Err(Error::DuplicateEdge(sid, tid)); 153 | } else if !adjacency[tid].contains(&sid) { 154 | return Err(Error::MissingEdge(tid, sid)); 155 | } 156 | 157 | if sid < tid { 158 | result.edges.push((sid, tid)); 159 | } 160 | } 161 | 162 | result.ids.push(sid); 163 | result.indices.insert(sid, sid); 164 | } 165 | 166 | result.adjacency = adjacency; 167 | 168 | Ok(result) 169 | } 170 | } 171 | 172 | impl<'a, G: Graph> TryFrom> for DefaultGraph { 173 | type Error = Error; 174 | 175 | fn try_from(traversal: DepthFirst<'a, G>) -> Result { 176 | let mut result = DefaultGraph::new(); 177 | 178 | for step in traversal { 179 | if result.is_empty() { 180 | result.add_node(step.sid)?; 181 | } 182 | 183 | if !step.cut { 184 | result.add_node(step.tid)?; 185 | } 186 | 187 | result.add_edge(step.sid, step.tid)?; 188 | } 189 | 190 | Ok(result) 191 | } 192 | } 193 | 194 | impl TryFrom> for DefaultGraph { 195 | type Error = Error; 196 | 197 | fn try_from(edges: Vec<(usize, usize)>) -> Result { 198 | let mut result = DefaultGraph::new(); 199 | 200 | for (sid, tid) in edges { 201 | if !result.has_id(sid) { 202 | result.add_node(sid)?; 203 | } 204 | 205 | if !result.has_id(tid) { 206 | result.add_node(tid)?; 207 | } 208 | 209 | result.add_edge(sid, tid)?; 210 | } 211 | 212 | Ok(result) 213 | } 214 | } 215 | 216 | impl PartialEq for DefaultGraph { 217 | fn eq(&self, other: &Self) -> bool { 218 | if self.size() != other.size() { 219 | return false; 220 | } else if self.order() != other.order() { 221 | return false; 222 | } 223 | 224 | for id in self.ids() { 225 | if !other.has_id(id) { 226 | return false; 227 | } 228 | } 229 | 230 | for (sid, tid) in self.edges() { 231 | match other.has_edge(sid, tid) { 232 | Ok(result) => { 233 | if !result { 234 | return false 235 | } 236 | }, Err(_) => return false 237 | } 238 | } 239 | 240 | true 241 | } 242 | } 243 | 244 | #[cfg(test)] 245 | mod try_from_adjacency { 246 | use super::*; 247 | 248 | #[test] 249 | fn missing_node() { 250 | let graph = DefaultGraph::try_from(vec![ 251 | vec![ 1 ] 252 | ]); 253 | 254 | assert_eq!(graph, Err(Error::UnknownId(1))) 255 | } 256 | 257 | #[test] 258 | fn duplicate_edge() { 259 | let graph = DefaultGraph::try_from(vec![ 260 | vec![ 1, 1 ], 261 | vec![ 0 ] 262 | ]); 263 | 264 | assert_eq!(graph, Err(Error::DuplicateEdge(0, 1))) 265 | } 266 | 267 | #[test] 268 | fn missing_edge() { 269 | let graph = DefaultGraph::try_from(vec![ 270 | vec![ 1 ], 271 | vec![ ] 272 | ]); 273 | 274 | assert_eq!(graph, Err(Error::MissingEdge(1, 0))) 275 | } 276 | } 277 | 278 | #[cfg(test)] 279 | mod try_from_edges { 280 | use super::*; 281 | 282 | #[test] 283 | fn duplicate_edge() { 284 | let graph = DefaultGraph::try_from(vec![ 285 | (0, 1), 286 | (0, 1) 287 | ]); 288 | 289 | assert_eq!(graph, Err(Error::DuplicateEdge(0, 1))) 290 | } 291 | 292 | #[test] 293 | fn duplicate_edge_reverse() { 294 | let graph = DefaultGraph::try_from(vec![ 295 | (0, 1), 296 | (1, 0) 297 | ]); 298 | 299 | assert_eq!(graph, Err(Error::DuplicateEdge(1, 0))) 300 | } 301 | 302 | #[test] 303 | fn valid() { 304 | let graph = DefaultGraph::try_from(vec![ 305 | (0, 1), 306 | (1, 2), 307 | (3, 4) 308 | ]).unwrap(); 309 | let mut expected = DefaultGraph::new(); 310 | 311 | assert_eq!(expected.add_node(0), Ok(())); 312 | assert_eq!(expected.add_node(1), Ok(())); 313 | assert_eq!(expected.add_node(2), Ok(())); 314 | assert_eq!(expected.add_node(3), Ok(())); 315 | assert_eq!(expected.add_node(4), Ok(())); 316 | assert_eq!(expected.add_edge(0, 1), Ok(())); 317 | assert_eq!(expected.add_edge(1, 2), Ok(())); 318 | assert_eq!(expected.add_edge(3, 4), Ok(())); 319 | 320 | assert_eq!(graph, expected); 321 | } 322 | } 323 | 324 | #[cfg(test)] 325 | mod try_from_depth_first { 326 | use super::*; 327 | 328 | #[test] 329 | fn p3_internal() { 330 | let g1 = DefaultGraph::try_from(vec![ 331 | vec![ 1 ], 332 | vec![ 0, 2 ], 333 | vec![ 1 ] 334 | ]).unwrap(); 335 | let traversal = DepthFirst::new(&g1, 1).unwrap(); 336 | let g2 = DefaultGraph::try_from(traversal).unwrap(); 337 | 338 | assert_eq!(g2.edges().collect::>(), [ (1, 0), (1, 2) ]) 339 | } 340 | 341 | #[test] 342 | fn c3() { 343 | let g1 = DefaultGraph::try_from(vec![ 344 | vec![ 1, 2 ], 345 | vec![ 0, 2 ], 346 | vec![ 1, 0 ] 347 | ]).unwrap(); 348 | let traversal = DepthFirst::new(&g1, 0).unwrap(); 349 | let g2 = DefaultGraph::try_from(traversal).unwrap(); 350 | 351 | assert_eq!(g2.edges().collect::>(), [ (0, 1), (1, 2), (2, 0) ]) 352 | } 353 | } 354 | 355 | #[cfg(test)] 356 | mod add_node { 357 | use super::*; 358 | 359 | #[test] 360 | fn duplicate() { 361 | let mut graph = DefaultGraph::try_from(vec![ 362 | vec![ ] 363 | ]).unwrap(); 364 | 365 | assert_eq!(graph.add_node(0), Err(Error::DuplicateId(0))) 366 | } 367 | } 368 | 369 | #[cfg(test)] 370 | mod add_edge { 371 | use super::*; 372 | 373 | #[test] 374 | fn duplicate() { 375 | let mut graph = DefaultGraph::try_from(vec![ 376 | vec![ 1 ], 377 | vec![ 0 ] 378 | ]).unwrap(); 379 | 380 | assert_eq!(graph.add_edge(0, 1), Err(Error::DuplicateEdge(0, 1))) 381 | } 382 | 383 | #[test] 384 | fn duplicate_reverse() { 385 | let mut graph = DefaultGraph::try_from(vec![ 386 | vec![ 1 ], 387 | vec![ 0 ] 388 | ]).unwrap(); 389 | 390 | assert_eq!(graph.add_edge(1, 0), Err(Error::DuplicateEdge(1, 0))) 391 | } 392 | 393 | #[test] 394 | fn missing_sid() { 395 | let mut graph = DefaultGraph::try_from(vec![ 396 | vec![ ] 397 | ]).unwrap(); 398 | 399 | assert_eq!(graph.add_edge(1, 0), Err(Error::UnknownId(1))) 400 | } 401 | 402 | #[test] 403 | fn missing_tid() { 404 | let mut graph = DefaultGraph::try_from(vec![ 405 | vec![ ] 406 | ]).unwrap(); 407 | 408 | assert_eq!(graph.add_edge(0, 1), Err(Error::UnknownId(1))) 409 | } 410 | } 411 | 412 | #[cfg(test)] 413 | mod is_empty { 414 | use super::*; 415 | 416 | #[test] 417 | fn p0() { 418 | let graph = DefaultGraph::new(); 419 | 420 | assert_eq!(graph.is_empty(), true) 421 | } 422 | 423 | #[test] 424 | fn p1() { 425 | let graph = DefaultGraph::try_from(vec![ 426 | vec![ ] 427 | ]).unwrap(); 428 | 429 | assert_eq!(graph.is_empty(), false) 430 | } 431 | } 432 | 433 | #[cfg(test)] 434 | mod order { 435 | use super::*; 436 | 437 | #[test] 438 | fn p0() { 439 | let graph = DefaultGraph::new(); 440 | 441 | assert_eq!(graph.order(), 0) 442 | } 443 | 444 | #[test] 445 | fn p3() { 446 | let graph = DefaultGraph::try_from(vec![ 447 | vec![ 1 ], 448 | vec![ 0, 2 ], 449 | vec![ 1 ] 450 | ]).unwrap(); 451 | 452 | assert_eq!(graph.order(), 3) 453 | } 454 | } 455 | 456 | #[cfg(test)] 457 | mod size { 458 | use super::*; 459 | 460 | #[test] 461 | fn p0() { 462 | let graph = DefaultGraph::new(); 463 | 464 | assert_eq!(graph.size(), 0) 465 | } 466 | 467 | #[test] 468 | fn p3() { 469 | let graph = DefaultGraph::try_from(vec![ 470 | vec![ 1 ], 471 | vec![ 0, 2 ], 472 | vec![ 1 ] 473 | ]).unwrap(); 474 | 475 | assert_eq!(graph.size(), 2) 476 | } 477 | } 478 | 479 | #[cfg(test)] 480 | mod nodes { 481 | use super::*; 482 | 483 | #[test] 484 | fn p0() { 485 | let graph = DefaultGraph::new(); 486 | 487 | assert_eq!(graph.ids().collect::>(), [ ]) 488 | } 489 | 490 | #[test] 491 | fn p3() { 492 | let graph = DefaultGraph::try_from(vec![ 493 | vec![ 1 ], 494 | vec![ 0, 2 ], 495 | vec![ 1 ] 496 | ]).unwrap(); 497 | 498 | assert_eq!(graph.ids().collect::>(), [ 0, 1, 2 ]) 499 | } 500 | } 501 | 502 | #[cfg(test)] 503 | mod neighbors { 504 | use super::*; 505 | 506 | #[test] 507 | fn given_outside() { 508 | let graph = DefaultGraph::new(); 509 | 510 | assert_eq!(graph.neighbors(1).err(), Some(Error::UnknownId(1))) 511 | } 512 | 513 | #[test] 514 | fn given_inside_p3() { 515 | let graph = DefaultGraph::try_from(vec![ 516 | vec![ 1 ], 517 | vec![ 0, 2 ], 518 | vec![ 1 ] 519 | ]).unwrap(); 520 | 521 | assert_eq!(graph.neighbors(1).unwrap().collect::>(), [ 0, 2 ]) 522 | } 523 | } 524 | 525 | #[cfg(test)] 526 | mod has_node { 527 | use super::*; 528 | 529 | #[test] 530 | fn given_outside() { 531 | let graph = DefaultGraph::new(); 532 | 533 | assert_eq!(graph.has_id(0), false) 534 | } 535 | 536 | #[test] 537 | fn given_inside_p1() { 538 | let graph = DefaultGraph::try_from(vec![ 539 | vec![ ] 540 | ]).unwrap(); 541 | 542 | assert_eq!(graph.has_id(0), true) 543 | } 544 | } 545 | 546 | #[cfg(test)] 547 | mod degree { 548 | use super::*; 549 | 550 | #[test] 551 | fn given_outside() { 552 | let graph = DefaultGraph::new(); 553 | 554 | assert_eq!(graph.degree(0), Err(Error::UnknownId(0))) 555 | } 556 | 557 | #[test] 558 | fn given_inside_p3() { 559 | let graph = DefaultGraph::try_from(vec![ 560 | vec![ 1 ], 561 | vec![ 0, 2 ], 562 | vec![ 1 ] 563 | ]).unwrap(); 564 | 565 | assert_eq!(graph.degree(1), Ok(2)) 566 | } 567 | } 568 | 569 | #[cfg(test)] 570 | mod edges { 571 | use super::*; 572 | 573 | #[test] 574 | fn p0() { 575 | let graph = DefaultGraph::new(); 576 | 577 | assert_eq!(graph.edges().collect::>(), vec![ ]) 578 | } 579 | 580 | #[test] 581 | fn p3() { 582 | let graph = DefaultGraph::try_from(vec![ 583 | vec![ 1 ], 584 | vec![ 0, 2 ], 585 | vec![ 1 ] 586 | ]).unwrap(); 587 | 588 | assert_eq!(graph.edges().collect::>(), [ (0, 1), (1, 2) ]) 589 | } 590 | } 591 | 592 | #[cfg(test)] 593 | mod has_edge { 594 | use super::*; 595 | 596 | #[test] 597 | fn unk_unk() { 598 | let graph = DefaultGraph::new(); 599 | 600 | assert_eq!(graph.has_edge(0, 1), Err(Error::UnknownId(0))) 601 | } 602 | 603 | #[test] 604 | fn sid_unk() { 605 | let graph = DefaultGraph::try_from(vec![ 606 | vec![ ] 607 | ]).unwrap(); 608 | 609 | assert_eq!(graph.has_edge(0, 1), Err(Error::UnknownId(1))) 610 | } 611 | 612 | #[test] 613 | fn sid_tid() { 614 | let graph = DefaultGraph::try_from(vec![ 615 | vec![ 1 ], 616 | vec![ 0 ] 617 | ]).unwrap(); 618 | 619 | assert_eq!(graph.has_edge(0, 1), Ok(true)) 620 | } 621 | 622 | #[test] 623 | fn tid_sid() { 624 | let graph = DefaultGraph::try_from(vec![ 625 | vec![ 1 ], 626 | vec![ 0 ] 627 | ]).unwrap(); 628 | 629 | assert_eq!(graph.has_edge(1, 0), Ok(true)) 630 | } 631 | } 632 | 633 | #[cfg(test)] 634 | mod eq { 635 | use super::*; 636 | 637 | #[test] 638 | fn c3_and_p3() { 639 | let c3 = DefaultGraph::try_from(vec![ 640 | vec![ 1, 2 ], 641 | vec![ 0, 2 ], 642 | vec![ 1, 0 ] 643 | ]).unwrap(); 644 | let p3 = DefaultGraph::try_from(vec![ 645 | vec![ 1 ], 646 | vec![ 0, 2 ], 647 | vec![ 1 ] 648 | ]).unwrap(); 649 | 650 | assert_eq!(c3 == p3, false) 651 | } 652 | 653 | #[test] 654 | fn p2_and_p2_p1() { 655 | let p2 = DefaultGraph::try_from(vec![ 656 | vec![ 1 ], 657 | vec![ 0 ] 658 | ]).unwrap(); 659 | let p2_p1 = DefaultGraph::try_from(vec![ 660 | vec![ 1 ], 661 | vec![ 0 ], 662 | vec![ ], 663 | ]).unwrap(); 664 | 665 | assert_eq!(p2 == p2_p1, false) 666 | } 667 | 668 | #[test] 669 | fn p2_and_p2_reverse() { 670 | let g1 = DefaultGraph::try_from(vec![ 671 | (0, 1) 672 | ]).unwrap(); 673 | let g2 = DefaultGraph::try_from(vec![ 674 | (1, 0) 675 | ]).unwrap(); 676 | 677 | assert_eq!(g1 == g2, true) 678 | } 679 | 680 | #[test] 681 | fn p2_and_p2_different_ids() { 682 | let g1 = DefaultGraph::try_from(vec![ 683 | (0, 1) 684 | ]).unwrap(); 685 | let g2 = DefaultGraph::try_from(vec![ 686 | (0, 2) 687 | ]).unwrap(); 688 | 689 | assert_eq!(g1 == g2, false) 690 | } 691 | } -------------------------------------------------------------------------------- /src/graph/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug,PartialEq,Eq)] 2 | pub enum Error { 3 | UnknownId(usize), 4 | DuplicateId(usize), 5 | MissingEdge(usize, usize), 6 | DuplicateEdge(usize, usize) 7 | } -------------------------------------------------------------------------------- /src/graph/graph.rs: -------------------------------------------------------------------------------- 1 | pub use super::error::Error; 2 | 3 | /// An unweighted graph. 4 | pub trait Graph { 5 | /// Returns true if there are no nodes, or false otherwise. 6 | fn is_empty(&self) -> bool; 7 | 8 | /// Returns the number of nodes in this graph. 9 | fn order(&self) -> usize; 10 | 11 | /// Returns the number of edges in this graph. 12 | fn size(&self) -> usize; 13 | 14 | /// Returns an Iterator over node identifiers. 15 | fn ids(&self) -> Box + '_>; 16 | 17 | /// Returns an iterator over node identifiers for the neighbors at id, 18 | /// or Error if not found. 19 | fn neighbors( 20 | &self, id: usize 21 | ) -> Result + '_>, Error>; 22 | 23 | /// Returns true if id is a member, or false otherwise. 24 | fn has_id(&self, id: usize) -> bool; 25 | 26 | /// Returns the count of neighbors at id, or Error if id not found. 27 | fn degree(&self, id: usize) -> Result; 28 | 29 | /// Returns an iterator over the edges of this graph. 30 | fn edges(&self) -> Box + '_>; 31 | 32 | /// Returns true if the edge (sid, tid) exists, or false otherwise. 33 | /// Returns Error if either sid or tid are not found. 34 | fn has_edge(&self, sid: usize, tid: usize) -> Result; 35 | } -------------------------------------------------------------------------------- /src/graph/mod.rs: -------------------------------------------------------------------------------- 1 | mod graph; 2 | mod error; 3 | mod default_graph; 4 | 5 | pub use graph::Graph; 6 | pub use error::Error; 7 | pub use default_graph::DefaultGraph; -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod graph; 2 | pub mod traversal; 3 | pub mod selection; 4 | pub mod matching; 5 | 6 | // https://github.com/rust-lang/cargo/issues/383#issuecomment-720873790 7 | #[cfg(doctest)] 8 | mod test_readme { 9 | macro_rules! external_doc_test { 10 | ($x:expr) => { 11 | #[doc = $x] 12 | extern {} 13 | }; 14 | } 15 | 16 | external_doc_test!(include_str!("../README.md")); 17 | } -------------------------------------------------------------------------------- /src/matching/blossom.rs: -------------------------------------------------------------------------------- 1 | use crate::graph::{ Graph, DefaultGraph, Error }; 2 | use super::pairing::Pairing; 3 | 4 | #[derive(Debug,PartialEq)] 5 | pub struct Blossom { 6 | id: usize, 7 | path: Vec 8 | } 9 | 10 | impl Blossom { 11 | pub fn new( 12 | id: usize, mut left: Vec, mut right: Vec 13 | ) -> Self { 14 | for i in 0..left.len() { 15 | for j in 0..right.len() { 16 | if left[i] == right[j] { 17 | let root = left[i]; 18 | left = left[0..i].to_vec(); 19 | right = right[0..j].to_vec(); 20 | 21 | right.reverse(); 22 | 23 | left.push(root); 24 | left.append(&mut right); 25 | 26 | return Self { id, path: left } 27 | } 28 | } 29 | } 30 | 31 | panic!("blossom root not found") 32 | } 33 | 34 | pub fn contract_graph<'a, G: Graph>( 35 | &self, graph: &'a G 36 | ) -> Result { 37 | let mut result = DefaultGraph::new(); 38 | 39 | result.add_node(self.id)?; 40 | 41 | for id in graph.ids() { 42 | if !self.path.contains(&id) { 43 | result.add_node(id)?; 44 | } 45 | } 46 | 47 | for (sid, tid) in graph.edges() { 48 | if self.path.contains(&sid) { 49 | if !self.path.contains(&tid) { 50 | if !result.has_edge(self.id, tid)? { 51 | result.add_edge(self.id, tid)?; 52 | } 53 | } 54 | } else if self.path.contains(&tid) { 55 | if !result.has_edge(sid, self.id)? { 56 | result.add_edge(sid, self.id)?; 57 | } 58 | } else { 59 | result.add_edge(sid, tid)?; 60 | } 61 | } 62 | 63 | Ok(result) 64 | } 65 | 66 | pub fn contract_pairing(&self, pairing: &Pairing) -> Pairing { 67 | let mut result = Pairing::new(); 68 | 69 | for (sid, tid) in pairing.edges() { 70 | if self.path.contains(&sid) { 71 | if !self.path.contains(&tid) { 72 | result.pair(self.id, tid); 73 | } 74 | } else if self.path.contains(&tid) { 75 | result.pair(sid, self.id); 76 | } else { 77 | result.pair(sid, tid); 78 | } 79 | } 80 | 81 | result 82 | } 83 | 84 | pub fn lift<'a, G: Graph>( 85 | &self, path: Vec, graph: &'a G 86 | ) -> Vec { 87 | let index = match path.iter().position(|&pid| pid == self.id) { 88 | Some(index) => index, 89 | None => return path 90 | // None => panic!("path missing blossom id: {}", self.id) 91 | }; 92 | let left = path[0..index].to_vec(); 93 | let right = path[(index + 1)..].to_vec(); 94 | 95 | if left.is_empty() && right.is_empty() { 96 | self.path.to_vec() 97 | } else if !left.is_empty() && right.is_empty() { 98 | self.lift_left_blossom(left, graph) 99 | } else if left.is_empty() && !right.is_empty() { 100 | self.lift_blossom_right(right, graph) 101 | } else { 102 | self.lift_left_blossom_right(left, right, graph) 103 | } 104 | } 105 | 106 | fn lift_left_blossom<'a, G: Graph>( 107 | &self, mut left: Vec, graph: &'a G 108 | ) -> Vec { 109 | let sid = left.last().unwrap(); 110 | let mut copy = self.path.to_vec(); 111 | 112 | while !graph.has_edge(*sid, copy[0]).unwrap() { 113 | copy.rotate_right(1); 114 | } 115 | 116 | left.append(&mut copy); 117 | 118 | left 119 | } 120 | 121 | fn lift_blossom_right<'a, G: Graph>( 122 | &self, mut right: Vec, graph: &'a G 123 | ) -> Vec { 124 | let tid = right[0]; 125 | let mut copy = self.path.to_vec(); 126 | 127 | while !graph.has_edge(*copy.last().unwrap(), tid).unwrap() { 128 | copy.rotate_right(1); 129 | } 130 | 131 | copy.append(&mut right); 132 | 133 | copy 134 | } 135 | 136 | fn lift_left_blossom_right<'a, G: Graph>( 137 | &self, left: Vec, right: Vec, graph: &'a G 138 | ) -> Vec { 139 | let &sid = left.last().unwrap(); 140 | let mut forward_blossom = self.path.to_vec(); 141 | let mut forward = left.to_vec(); 142 | 143 | while !graph.has_edge(sid, forward_blossom[0]).unwrap() { 144 | forward_blossom.rotate_right(1); 145 | } 146 | 147 | let &tid = &right[0]; 148 | 149 | for &bid in &forward_blossom { 150 | forward.push(bid); 151 | 152 | if graph.has_edge(bid, tid).unwrap() { 153 | break; 154 | } 155 | } 156 | 157 | forward.extend(right.iter()); 158 | 159 | if forward.len() % 2 == 0 { 160 | return forward 161 | } 162 | 163 | let mut reverse = left.to_vec(); 164 | let mut reverse_blossom = self.path.to_vec(); 165 | 166 | reverse_blossom.reverse(); 167 | 168 | while !graph.has_edge(sid, reverse_blossom[0]).unwrap() { 169 | reverse_blossom.rotate_right(1); 170 | } 171 | 172 | for &bid in &reverse_blossom { 173 | reverse.push(bid); 174 | 175 | if graph.has_edge(bid, tid).unwrap() { 176 | break; 177 | } 178 | } 179 | 180 | reverse.extend(right.iter()); 181 | 182 | reverse 183 | } 184 | } 185 | 186 | #[cfg(test)] 187 | mod new { 188 | use super::*; 189 | 190 | #[test] 191 | #[should_panic(expected="blossom root not found")] 192 | fn different_roots() { 193 | Blossom::new(1, vec![ 2, 1, 0 ], vec![ 5, 4, 3 ]); 194 | } 195 | 196 | #[test] 197 | fn root_at_right() { 198 | let blossom = Blossom::new(1, vec![ 2, 1, 0 ], vec![ 5, 4, 0 ]); 199 | 200 | assert_eq!(blossom.path, vec![ 2, 1, 0, 4, 5 ]) 201 | } 202 | 203 | #[test] 204 | fn root_inside() { 205 | let blossom = Blossom::new(1, vec![ 206 | 4, 3, 2, 1, 0 207 | ], vec![ 208 | 7, 6, 2, 1, 0 209 | ]); 210 | 211 | assert_eq!(blossom.path, vec![ 4, 3, 2, 6, 7 ]) 212 | } 213 | } 214 | 215 | #[cfg(test)] 216 | mod contract_graph { 217 | use std::convert::TryFrom; 218 | use super::*; 219 | 220 | #[test] 221 | fn butterfly_tid_inside() { 222 | let graph = DefaultGraph::try_from(vec![ 223 | (0, 1), (1, 2), (2, 0), (3, 2), (3, 1) 224 | ]).unwrap(); 225 | let blossom = Blossom::new(4, vec![0], vec![ 1, 2, 0 ]); 226 | let contracted = blossom.contract_graph(&graph); 227 | 228 | assert_eq!(contracted, DefaultGraph::try_from(vec![ 229 | (3, 4) 230 | ])) 231 | } 232 | 233 | #[test] 234 | fn butterfly_sid_inside() { 235 | let graph = DefaultGraph::try_from(vec![ 236 | (0, 1), (1, 2), (2, 0), (2, 3), (1, 3) 237 | ]).unwrap(); 238 | let blossom = Blossom::new(4, vec![0], vec![ 1, 2, 0 ]); 239 | let contracted = blossom.contract_graph(&graph); 240 | 241 | assert_eq!(contracted, DefaultGraph::try_from(vec![ 242 | (3, 4) 243 | ])) 244 | } 245 | 246 | #[test] 247 | fn sid_inside() { 248 | let graph = DefaultGraph::try_from(vec![ 249 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (4, 5), (5, 6) 250 | ]).unwrap(); 251 | let blossom = Blossom::new(7, vec![ 4, 0, 1 ], vec![ 3, 2, 1 ]); 252 | let contracted = blossom.contract_graph(&graph); 253 | 254 | assert_eq!(contracted, DefaultGraph::try_from(vec![ 255 | (6, 5), (5, 7) 256 | ])) 257 | } 258 | 259 | #[test] 260 | fn tid_inside() { 261 | let graph = DefaultGraph::try_from(vec![ 262 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (5, 4), (5, 6) 263 | ]).unwrap(); 264 | let blossom = Blossom::new(7, vec![ 4, 0, 1 ], vec![ 3, 2, 1 ]); 265 | let contracted = blossom.contract_graph(&graph); 266 | 267 | assert_eq!(contracted, DefaultGraph::try_from(vec![ 268 | (6, 5), (5, 7) 269 | ])) 270 | } 271 | 272 | #[test] 273 | fn example_causes_double_edge() { 274 | // one way to force a dobule-edge for contracted graph 275 | let graph = DefaultGraph::try_from(vec![ 276 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), 277 | (8, 2), (6, 1) 278 | ]).unwrap(); 279 | let blossom = Blossom::new(9, vec![ 8, 2, 3, 4 ], vec![ 7, 6, 5, 4 ]); 280 | let contracted = blossom.contract_graph(&graph); 281 | 282 | assert_eq!(contracted, DefaultGraph::try_from(vec![ 283 | (0, 1), (1, 9) 284 | ])) 285 | } 286 | } 287 | 288 | #[cfg(test)] 289 | mod contract_pairing { 290 | use super::*; 291 | 292 | #[test] 293 | fn sid_inside() { 294 | let blossom = Blossom::new(5, vec![ 2, 1, 0 ], vec![ 4, 3, 0 ]); 295 | let mut pairing = Pairing::new(); 296 | 297 | pairing.pair(7, 8); 298 | pairing.pair(1, 6); 299 | 300 | let mut expected = Pairing::new(); 301 | 302 | expected.pair(7, 8); 303 | expected.pair(5, 6); 304 | 305 | assert_eq!(blossom.contract_pairing(&pairing), expected); 306 | } 307 | 308 | #[test] 309 | fn tid_inside() { 310 | let blossom = Blossom::new(5, vec![ 2, 1, 0 ], vec![ 4, 3, 0 ]); 311 | let mut pairing = Pairing::new(); 312 | 313 | pairing.pair(7, 8); 314 | pairing.pair(6, 1); 315 | 316 | let mut expected = Pairing::new(); 317 | 318 | expected.pair(7, 8); 319 | expected.pair(6, 5); 320 | 321 | assert_eq!(blossom.contract_pairing(&pairing), expected); 322 | } 323 | 324 | #[test] 325 | fn sid_tid_inside() { 326 | let blossom = Blossom::new(5, vec![ 2, 1, 0 ], vec![ 4, 3, 0 ]); 327 | let mut pairing = Pairing::new(); 328 | 329 | pairing.pair(7, 8); 330 | pairing.pair(2, 1); 331 | 332 | let mut expected = Pairing::new(); 333 | 334 | expected.pair(7, 8); 335 | 336 | assert_eq!(blossom.contract_pairing(&pairing), expected); 337 | } 338 | } 339 | 340 | #[cfg(test)] 341 | mod lift { 342 | use::std::convert::TryFrom; 343 | use super::*; 344 | 345 | #[test] 346 | fn missing_blossom_id() { 347 | let graph = DefaultGraph::try_from(vec![ 348 | (1, 2), (2, 3), (3, 4), (4, 5), (5, 1) 349 | ]).unwrap(); 350 | let blossom = Blossom::new(5, vec![ 2, 1, 0 ], vec![ 4, 3, 0 ]); 351 | let path = vec![ 8, 9, 10, 11 ]; 352 | 353 | assert_eq!(blossom.lift(path, &graph), vec![ 354 | 8, 9, 10, 11 355 | ]) 356 | } 357 | 358 | #[test] 359 | fn none_blossom_none() { 360 | let graph = DefaultGraph::try_from(vec![ 361 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 0) 362 | ]).unwrap(); 363 | let blossom = Blossom::new(5, vec![ 2, 1, 0 ], vec![ 4, 3, 0 ]); 364 | let path = vec![ 5 ]; 365 | 366 | assert_eq!(blossom.lift(path, &graph), vec![ 367 | 2, 1, 0, 3, 4 368 | ]) 369 | } 370 | 371 | #[test] 372 | fn left_blossom_none() { 373 | let graph = DefaultGraph::try_from(vec![ 374 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1) 375 | ]).unwrap(); 376 | let blossom = Blossom::new(6, vec![ 1, 2, 3 ], vec![ 5, 4, 3 ]); 377 | let path = vec![ 0, 6 ]; 378 | 379 | assert_eq!(blossom.lift(path, &graph), vec![ 380 | 0, 1, 2, 3, 4, 5 381 | ]) 382 | } 383 | 384 | #[test] 385 | fn left_blossom_none_rotated_twice() { 386 | let graph = DefaultGraph::try_from(vec![ 387 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1) 388 | ]).unwrap(); 389 | let blossom = Blossom::new(6, vec![ 2, 3, 4 ], vec![ 1, 5, 4 ]); 390 | let path = vec![ 0, 6 ]; 391 | 392 | assert_eq!(blossom.lift(path, &graph), vec![ 393 | 0, 1, 2, 3, 4, 5 394 | ]) 395 | } 396 | 397 | #[test] 398 | fn none_blossom_right() { 399 | let graph = DefaultGraph::try_from(vec![ 400 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1) 401 | ]).unwrap(); 402 | let blossom = Blossom::new(6, vec![ 2, 3, 4 ], vec![ 1, 5, 4 ]); 403 | let path = vec![ 6, 0 ]; 404 | 405 | assert_eq!(blossom.lift(path, &graph), vec![ 406 | 2, 3, 4, 5, 1, 0 407 | ]) 408 | } 409 | 410 | #[test] 411 | fn left_blossom_right() { 412 | let graph = DefaultGraph::try_from(vec![ 413 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1), (3, 6) 414 | ]).unwrap(); 415 | let blossom = Blossom::new(7, vec![ 2, 3, 4 ], vec![ 1, 5, 4 ]); 416 | let path = vec![ 0, 7, 6 ]; 417 | 418 | assert_eq!(blossom.lift(path, &graph), vec![ 419 | 0, 1, 5, 4, 3, 6 420 | ]) 421 | } 422 | 423 | #[test] 424 | fn left_blossom_right_shifted() { 425 | let graph = DefaultGraph::try_from(vec![ 426 | (0, 5), (5, 1), (1, 2), (2, 3), (3, 4), (4, 5), (3, 6) 427 | ]).unwrap(); 428 | let blossom = Blossom::new(7, vec![ 2, 3, 4 ], vec![ 1, 5, 4 ]); 429 | let path = vec![ 0, 7, 6 ]; 430 | 431 | assert_eq!(blossom.lift(path, &graph), vec![ 432 | 0, 5, 1, 2, 3, 6 433 | ]) 434 | } 435 | } -------------------------------------------------------------------------------- /src/matching/forest.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::hash_map::Entry::{ Occupied, Vacant }; 3 | 4 | #[derive(Debug,PartialEq)] 5 | pub struct Forest { 6 | parents: HashMap, 7 | nodes: Vec 8 | } 9 | 10 | impl Forest { 11 | pub fn new() -> Self { 12 | Self { 13 | parents: HashMap::new(), 14 | nodes: Vec::new() 15 | } 16 | } 17 | 18 | pub fn add_root(&mut self, root: usize) { 19 | match self.parents.entry(root) { 20 | Vacant(entry) => { 21 | entry.insert(Entry { parent: None, parity: Parity::Even }); 22 | self.nodes.push(root); 23 | }, 24 | Occupied(_) => panic!("duplicate node: {}", root) 25 | } 26 | } 27 | 28 | pub fn add_edge(&mut self, parent: usize, node: usize) { 29 | let parity = match self.parents.get(&parent) { 30 | Some(entry) => entry.parity.invert(), 31 | None => panic!("missing parent: {}", parent) 32 | }; 33 | 34 | match self.parents.entry(node) { 35 | Vacant(entry) => { 36 | entry.insert(Entry { parent: Some(parent), parity: parity }); 37 | self.nodes.push(node); 38 | }, 39 | Occupied(_) => panic!("duplicate node: {}", node) 40 | } 41 | } 42 | 43 | pub fn even_nodes(&self) -> impl Iterator + '_ { 44 | self.nodes.iter().filter(move |node| { 45 | self.parents.get(node).unwrap().parity == Parity::Even 46 | }).cloned() 47 | } 48 | 49 | pub fn path(&self, node: usize) -> Option> { 50 | let mut parent = match self.parents.get(&node) { 51 | None => return None, 52 | Some(parent) => parent.parent 53 | }; 54 | let mut result = vec![ node ]; 55 | 56 | loop { 57 | match parent { 58 | None => { 59 | return Some(result) 60 | }, 61 | Some(id) => { 62 | result.push(id); 63 | 64 | parent = match self.parents.get(&id) { 65 | None => panic!("missing parent: {}", id), 66 | Some(parent) => parent.parent 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | #[derive(Debug,PartialEq)] 75 | struct Entry { 76 | parent: Option, 77 | parity: Parity 78 | } 79 | 80 | #[derive(Debug,PartialEq)] 81 | enum Parity { 82 | Even, 83 | Odd 84 | } 85 | 86 | impl Parity { 87 | fn invert(&self) -> Self { 88 | match self { 89 | Parity::Even => Parity::Odd, 90 | Parity::Odd => Parity::Even 91 | } 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod add_root { 97 | use super::*; 98 | 99 | #[test] 100 | #[should_panic(expected="duplicate node: 0")] 101 | fn duplicate() { 102 | let mut forest = Forest::new(); 103 | 104 | forest.add_root(0); 105 | forest.add_root(0); 106 | } 107 | } 108 | 109 | #[cfg(test)] 110 | mod add_edge { 111 | use super::*; 112 | 113 | #[test] 114 | #[should_panic(expected="missing parent: 0")] 115 | fn parent_outside() { 116 | let mut forest = Forest::new(); 117 | 118 | forest.add_edge(0, 1); 119 | } 120 | 121 | #[test] 122 | #[should_panic(expected="duplicate node: 1")] 123 | fn duplicate_node() { 124 | let mut forest = Forest::new(); 125 | 126 | forest.add_root(0); 127 | forest.add_root(1); 128 | 129 | forest.add_edge(0, 1); 130 | } 131 | } 132 | 133 | #[cfg(test)] 134 | mod path { 135 | use super::*; 136 | 137 | #[test] 138 | fn outside() { 139 | let forest = Forest::new(); 140 | 141 | assert_eq!(forest.path(0), None) 142 | } 143 | 144 | #[test] 145 | fn root() { 146 | let mut forest = Forest::new(); 147 | 148 | forest.add_root(0); 149 | 150 | assert_eq!(forest.path(0), Some(vec![ 0 ])) 151 | } 152 | 153 | #[test] 154 | fn child() { 155 | let mut forest = Forest::new(); 156 | 157 | forest.add_root(0); 158 | forest.add_edge(0, 1); 159 | 160 | assert_eq!(forest.path(1), Some(vec![ 1, 0 ])) 161 | } 162 | 163 | #[test] 164 | fn grandchild() { 165 | let mut forest = Forest::new(); 166 | 167 | forest.add_root(0); 168 | forest.add_edge(0, 1); 169 | forest.add_edge(1, 2); 170 | 171 | assert_eq!(forest.path(2), Some(vec![ 2, 1, 0 ])) 172 | } 173 | 174 | #[test] 175 | fn grandchild_with_branching_before() { 176 | let mut forest = Forest::new(); 177 | 178 | forest.add_root(0); 179 | forest.add_edge(0, 1); 180 | forest.add_edge(0, 2); 181 | forest.add_edge(0, 3); 182 | forest.add_edge(1, 4); 183 | forest.add_edge(2, 5); 184 | forest.add_edge(3, 6); 185 | forest.add_edge(5, 7); 186 | 187 | assert_eq!(forest.path(7), Some(vec![ 7, 5, 2, 0 ])) 188 | } 189 | 190 | #[test] 191 | fn grandchild_and_other_path() { 192 | let mut forest = Forest::new(); 193 | 194 | forest.add_root(0); 195 | forest.add_edge(0, 1); 196 | forest.add_edge(1, 2); 197 | forest.add_root(3); 198 | forest.add_edge(3, 4); 199 | forest.add_edge(3, 5); 200 | forest.add_edge(5, 6); 201 | 202 | assert_eq!(forest.path(2), Some(vec![ 2, 1, 0 ])) 203 | } 204 | } 205 | 206 | #[cfg(test)] 207 | mod even_nodes { 208 | use std::collections::HashSet; 209 | use std::iter::FromIterator; 210 | use super::*; 211 | 212 | #[test] 213 | fn empty() { 214 | let forest = Forest::new(); 215 | 216 | assert_eq!( 217 | forest.even_nodes().collect::>(), 218 | HashSet::from_iter([ ].iter().cloned()) 219 | ) 220 | } 221 | 222 | #[test] 223 | fn two_root() { 224 | let mut forest = Forest::new(); 225 | 226 | forest.add_root(0); 227 | forest.add_root(1); 228 | 229 | assert_eq!( 230 | forest.even_nodes().collect::>(), 231 | HashSet::from_iter([ 0, 1 ].iter().cloned()) 232 | ) 233 | } 234 | 235 | #[test] 236 | fn complex_tree() { 237 | let mut forest = Forest::new(); 238 | 239 | forest.add_root(0); 240 | forest.add_edge(0, 1); 241 | forest.add_edge(1, 2); 242 | forest.add_root(3); 243 | forest.add_edge(3, 4); 244 | forest.add_edge(4, 5); 245 | forest.add_edge(4, 6); 246 | 247 | assert_eq!( 248 | forest.even_nodes().collect::>(), 249 | HashSet::from_iter([ 0, 3, 2, 5, 6 ].iter().cloned()) 250 | ) 251 | } 252 | } -------------------------------------------------------------------------------- /src/matching/greedy.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use crate::graph::Graph; 4 | use crate::selection::components; 5 | use crate::traversal::{ DepthFirst, Step }; 6 | use super::pairing::Pairing; 7 | 8 | /// Returns a greedy matching over all componenents of the Graph. Bipartate 9 | /// graphs may return a perfect Matching. Non-bipartate graphs yield either 10 | /// maximal or maximum Matchings. 11 | /// 12 | /// Because a greedy Matching can be used as a starting point to a more 13 | /// sophisticated matching procedure (e.g., Edmund's Blossom), it usually 14 | /// makes sense to try a greedy matching and only fall back to a more advanced 15 | /// procedure if the matching isn't perfect. 16 | /// 17 | /// For more on matching, see: *[The Maximum Matching Problem](https://depth-first.com/articles/2019/04/02/the-maximum-matching-problem/)*. 18 | /// 19 | /// ```rust 20 | /// use std::convert::TryFrom; 21 | /// use std::collections::BTreeSet; 22 | /// use gamma::graph::{ Graph, Error, DefaultGraph }; 23 | /// use gamma::matching::greedy; 24 | /// 25 | /// fn main() -> Result<(), Error> { 26 | /// let graph = DefaultGraph::try_from(vec![ 27 | /// vec![ 1 ], 28 | /// vec![ 0, 2 ], 29 | /// vec![ 1 ] 30 | /// ])?; 31 | /// let edges = greedy(&graph); 32 | /// 33 | // assert_eq!( 34 | // pairing.edges().collect::>(), 35 | // [ (0, 1) ].iter().cloned().collect::>() 36 | // ) 37 | /// 38 | /// Ok(()) 39 | /// } 40 | /// ``` 41 | pub fn greedy(graph: &G) -> Pairing { 42 | // let mut edges = Vec::new(); 43 | let mut pairing = Pairing::new(); 44 | let mut nodes = HashSet::new(); 45 | 46 | for graph in components(graph) { 47 | let root = graph.ids().next().expect("component root"); 48 | let traversal = DepthFirst::new(&graph, root).expect("traversal"); 49 | 50 | for Step { sid, tid, cut: _ } in traversal { 51 | if nodes.insert(sid) && nodes.insert(tid) { 52 | pairing.pair(sid, tid); 53 | } 54 | } 55 | } 56 | 57 | pairing 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use std::convert::TryFrom; 63 | use std::collections::BTreeSet; 64 | use super::*; 65 | use crate::graph::DefaultGraph; 66 | 67 | #[test] 68 | fn empty() { 69 | let graph = DefaultGraph::new(); 70 | let pairing = greedy(&graph); 71 | 72 | assert_eq!( 73 | pairing.edges().collect::>(), 74 | [ ].iter().cloned().collect::>() 75 | ) 76 | } 77 | 78 | #[test] 79 | fn p1() { 80 | let graph = DefaultGraph::try_from(vec![ 81 | vec![ ] 82 | ]).unwrap(); 83 | let pairing = greedy(&graph); 84 | 85 | assert_eq!( 86 | pairing.edges().collect::>(), 87 | [ ].iter().cloned().collect::>() 88 | ) 89 | } 90 | 91 | #[test] 92 | fn p2() { 93 | let graph = DefaultGraph::try_from(vec![ 94 | vec![ 1 ], 95 | vec![ 0 ] 96 | ]).unwrap(); 97 | let pairing = greedy(&graph); 98 | 99 | assert_eq!( 100 | pairing.edges().collect::>(), 101 | [ (0, 1) ].iter().cloned().collect::>() 102 | ) 103 | } 104 | 105 | #[test] 106 | fn p2_p2() { 107 | let graph = DefaultGraph::try_from(vec![ 108 | vec![ 1 ], 109 | vec![ 0 ], 110 | vec![ 3 ], 111 | vec![ 2 ] 112 | ]).unwrap(); 113 | let pairing = greedy(&graph); 114 | 115 | assert_eq!(pairing.edges().collect::>(), 116 | [ (0, 1), (2, 3) ].iter().cloned().collect::>() 117 | ) 118 | } 119 | 120 | #[test] 121 | fn p3() { 122 | let graph = DefaultGraph::try_from(vec![ 123 | vec![ 1 ], 124 | vec![ 0, 2 ], 125 | vec![ 1 ] 126 | ]).unwrap(); 127 | let pairing = greedy(&graph); 128 | 129 | assert_eq!(pairing.edges().collect::>(), 130 | [ (0, 1) ].iter().cloned().collect::>() 131 | ) 132 | } 133 | 134 | #[test] 135 | fn p4() { 136 | let graph = DefaultGraph::try_from(vec![ 137 | vec![ 1 ], 138 | vec![ 0, 2 ], 139 | vec![ 1, 3 ], 140 | vec![ 2 ] 141 | ]).unwrap(); 142 | let pairing = greedy(&graph); 143 | 144 | assert_eq!(pairing.edges().collect::>(), 145 | [ (0, 1), (2, 3) ].iter().cloned().collect::>() 146 | ) 147 | } 148 | 149 | #[test] 150 | fn s3() { 151 | let graph = DefaultGraph::try_from(vec![ 152 | vec![ 1 ], 153 | vec![ 0, 2, 3 ], 154 | vec![ 1 ], 155 | vec![ 1 ] 156 | ]).unwrap(); 157 | let pairing = greedy(&graph); 158 | 159 | assert_eq!(pairing.edges().collect::>(), 160 | [ (0, 1) ].iter().cloned().collect::>() 161 | ) 162 | } 163 | 164 | #[test] 165 | fn c6() { 166 | let graph = DefaultGraph::try_from(vec![ 167 | vec![ 1, 5 ], 168 | vec![ 0, 2 ], 169 | vec![ 1, 3 ], 170 | vec![ 2, 4 ], 171 | vec![ 3, 5 ], 172 | vec![ 4, 0 ] 173 | ]).unwrap(); 174 | let pairing = greedy(&graph); 175 | 176 | assert_eq!(pairing.edges().collect::>(), 177 | [ (0, 1), (2, 3), (4, 5) ].iter().cloned().collect::>() 178 | ) 179 | } 180 | } -------------------------------------------------------------------------------- /src/matching/marker.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ HashMap, HashSet }; 2 | use std::collections::hash_map::Entry::{ Occupied, Vacant }; 3 | 4 | pub struct Marker { 5 | nodes: HashSet, 6 | edges: HashMap> 7 | } 8 | 9 | impl Marker { 10 | pub fn new() -> Self { 11 | Self { 12 | nodes: HashSet::new(), 13 | edges: HashMap::new() 14 | } 15 | } 16 | 17 | pub fn mark_node(&mut self, id: usize) { 18 | if !self.nodes.insert(id) { 19 | panic!("node marked twice: {}", id) 20 | } 21 | } 22 | 23 | pub fn has_node(&self, id: usize) -> bool { 24 | self.nodes.contains(&id) 25 | } 26 | 27 | pub fn mark_edge(&mut self, sid: usize, tid: usize) { 28 | match self.edges.entry(sid) { 29 | Occupied(mut entry) => { 30 | if entry.get().contains(&tid) { 31 | panic!("edge marked twice: ({},{})", sid, tid) 32 | } else { 33 | entry.get_mut().push(tid) 34 | } 35 | }, 36 | Vacant(entry) => { 37 | entry.insert(vec![ tid ]); 38 | } 39 | } 40 | 41 | match self.edges.entry(tid) { 42 | Occupied(mut entry) => { 43 | entry.get_mut().push(sid) 44 | }, 45 | Vacant(entry) => { 46 | entry.insert(vec![ sid ]); 47 | } 48 | } 49 | } 50 | 51 | pub fn has_edge(&self, sid: usize, tid: usize) -> bool { 52 | match self.edges.get(&sid) { 53 | None => false, 54 | Some(neighbors) => neighbors.contains(&tid) 55 | } 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod mark_node { 61 | use super::*; 62 | 63 | #[test] 64 | #[should_panic(expected="node marked twice: 0")] 65 | fn duplicate() { 66 | let mut marker = Marker::new(); 67 | 68 | marker.mark_node(0); 69 | marker.mark_node(0); 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod mark_edge { 75 | use super::*; 76 | 77 | #[test] 78 | #[should_panic(expected="edge marked twice: (0,1)")] 79 | fn duplicate() { 80 | let mut marker = Marker::new(); 81 | 82 | marker.mark_edge(0, 1); 83 | marker.mark_edge(0, 1); 84 | } 85 | 86 | #[test] 87 | #[should_panic(expected="edge marked twice: (1,0)")] 88 | fn duplicate_reverse() { 89 | let mut marker = Marker::new(); 90 | 91 | marker.mark_edge(0, 1); 92 | marker.mark_edge(1, 0); 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod has_node { 98 | use super::*; 99 | 100 | #[test] 101 | fn outside() { 102 | let marker = Marker::new(); 103 | 104 | assert_eq!(marker.has_node(0), false) 105 | } 106 | 107 | #[test] 108 | fn inside() { 109 | let mut marker = Marker::new(); 110 | 111 | marker.mark_node(0); 112 | 113 | assert_eq!(marker.has_node(0), true) 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod has_edge { 119 | use super::*; 120 | 121 | #[test] 122 | fn outside() { 123 | let marker = Marker::new(); 124 | 125 | assert_eq!(marker.has_edge(0, 1), false); 126 | } 127 | 128 | #[test] 129 | fn inside() { 130 | let mut marker = Marker::new(); 131 | 132 | marker.mark_edge(0, 1); 133 | 134 | assert_eq!(marker.has_edge(0, 1), true); 135 | } 136 | 137 | #[test] 138 | fn inside_reverse() { 139 | let mut marker = Marker::new(); 140 | 141 | marker.mark_edge(0, 1); 142 | 143 | assert_eq!(marker.has_edge(1, 0), true); 144 | } 145 | } -------------------------------------------------------------------------------- /src/matching/maximum_matching.rs: -------------------------------------------------------------------------------- 1 | use crate::graph::{ Graph }; 2 | use super::pairing::Pairing; 3 | use super::forest::Forest; 4 | use super::marker::Marker; 5 | use super::blossom::Blossom; 6 | 7 | /// Performs a maximum matching over the Graph. 8 | /// 9 | /// A greedy matching can be used as a starting point to maximum matching, so 10 | /// it may be helpful to try a greedy matching, falling back to maximum 11 | /// matching if the matching isn't perfect. 12 | /// 13 | /// For more on matching, see: *[The Maximum Matching Problem](https://depth-first.com/articles/2019/04/02/the-maximum-matching-problem/)*. 14 | /// 15 | /// ```rust 16 | /// use std::convert::TryFrom; 17 | /// use std::collections::BTreeSet; 18 | /// use gamma::graph::{ Error, DefaultGraph }; 19 | /// use gamma::matching::{ maximum_matching, Pairing }; 20 | /// 21 | /// fn main() -> Result<(), Error> { 22 | /// let graph = DefaultGraph::try_from(vec![ 23 | /// (0, 1), (1, 2), (2, 3) 24 | /// ]).unwrap(); 25 | /// let mut pairing = Pairing::new(); 26 | /// 27 | /// maximum_matching(&graph, &mut pairing); 28 | /// 29 | /// assert_eq!( 30 | /// pairing.edges().collect::>(), 31 | /// [ (0, 1), (2, 3) ].iter().cloned().collect::>() 32 | /// ); 33 | /// 34 | /// Ok(()) 35 | /// } 36 | /// ``` 37 | pub fn maximum_matching<'a, G: Graph>( 38 | graph: &'a G, pairing: &'a mut Pairing 39 | ) { 40 | while let Some(path) = augmenting_path(graph, pairing) { 41 | pairing.augment(path); 42 | maximum_matching(graph, pairing); 43 | } 44 | } 45 | 46 | fn augmenting_path<'a, G: Graph>( 47 | graph: &'a G, pairing: &'a mut Pairing 48 | ) -> Option> { 49 | let mut forest = Forest::new(); 50 | let mut marker = Marker::new(); 51 | 52 | for (sid, tid) in pairing.edges() { 53 | marker.mark_edge(sid, tid); 54 | } 55 | 56 | for v in graph.ids() { 57 | if !pairing.has_node(v) { 58 | forest.add_root(v); 59 | } 60 | } 61 | 62 | loop { 63 | let v = match some_v(&forest, &marker) { 64 | Some(node) => node, 65 | None => break 66 | }; 67 | 68 | loop { 69 | let w = match some_w(v, graph, &marker) { 70 | Some(node) => node, 71 | None => break 72 | }; 73 | 74 | match forest.path(w) { 75 | Some(path_w) => { 76 | if path_w.len() % 2 == 1 { 77 | return even_path(v, path_w, graph, &forest, pairing) 78 | } 79 | }, 80 | None => { 81 | forest.add_edge(v, w); 82 | forest.add_edge(w, pairing.mate(w)); 83 | } 84 | } 85 | 86 | marker.mark_edge(v, w); 87 | } 88 | 89 | marker.mark_node(v); 90 | } 91 | 92 | None 93 | } 94 | 95 | fn some_v(forest: &Forest, marker: &Marker) -> Option { 96 | forest.even_nodes().find(|id| !marker.has_node(*id)) 97 | } 98 | 99 | fn some_w(v: usize, graph: &G, marker: &Marker) -> Option { 100 | graph.neighbors(v).expect("neighbors of v") 101 | .find(|&id| !marker.has_edge(v, id)) 102 | // graph.neighbors(v) 103 | // .expect("neighbors of v").cloned() 104 | // .find(|&id| !marker.has_edge(v, id)) 105 | } 106 | 107 | fn even_path( 108 | v: usize, 109 | mut path_w: Vec, 110 | graph: &G, 111 | forest: &Forest, 112 | pairing: &Pairing 113 | ) -> Option> { 114 | let mut path_v = forest.path(v).expect("v not in forest"); 115 | 116 | if path_v.last() == path_w.last() { 117 | process_blossom(path_v, path_w, graph, pairing) 118 | } else { 119 | path_v.reverse(); 120 | path_v.append(&mut path_w); 121 | 122 | Some(path_v) 123 | } 124 | } 125 | 126 | fn process_blossom( 127 | left: Vec, right: Vec, graph: &G, pairing: &Pairing 128 | ) -> Option> { 129 | let max_id = graph.ids().max().expect("no max id"); 130 | let blossom = Blossom::new(max_id + 1, left, right); 131 | let contracted_graph = blossom.contract_graph(graph).expect("bad graph"); 132 | let mut contracted_pairing = blossom.contract_pairing(&pairing); 133 | 134 | match augmenting_path(&contracted_graph, &mut contracted_pairing) { 135 | Some(path) => Some(blossom.lift(path, graph)), 136 | None => None 137 | } 138 | } 139 | 140 | #[cfg(test)] 141 | mod tests { 142 | use super::*; 143 | use std::collections::HashMap; 144 | use std::convert::TryFrom; 145 | use crate::graph::DefaultGraph; 146 | 147 | #[test] 148 | fn empty() { 149 | let graph = DefaultGraph::new(); 150 | let mut pairing = Pairing::new(); 151 | 152 | maximum_matching(&graph, &mut pairing); 153 | 154 | assert_eq!( 155 | pairing.edges().collect::>(), 156 | HashMap::new() 157 | ) 158 | } 159 | 160 | #[test] 161 | fn p2() { 162 | let graph = DefaultGraph::try_from(vec![ 163 | vec![ 1 ], 164 | vec![ 0 ] 165 | ]).unwrap(); 166 | let mut pairing = Pairing::new(); 167 | 168 | maximum_matching(&graph, &mut pairing); 169 | 170 | assert_eq!( 171 | pairing.edges().collect::>(), 172 | [ (0, 1) ].iter().cloned().collect::>() 173 | ) 174 | } 175 | 176 | #[test] 177 | fn p3() { 178 | let graph = DefaultGraph::try_from(vec![ 179 | vec![ 1 ], 180 | vec![ 0, 2 ], 181 | vec![ 1 ] 182 | ]).unwrap(); 183 | let mut pairing = Pairing::new(); 184 | 185 | maximum_matching(&graph, &mut pairing); 186 | 187 | assert_eq!( 188 | pairing.edges().collect::>(), 189 | [ (0, 1) ].iter().cloned().collect::>() 190 | ) 191 | } 192 | 193 | #[test] 194 | fn p4() { 195 | let graph = DefaultGraph::try_from(vec![ 196 | vec![ 1 ], 197 | vec![ 0, 2 ], 198 | vec![ 1, 3 ], 199 | vec![ 2 ] 200 | ]).unwrap(); 201 | let mut pairing = Pairing::new(); 202 | 203 | maximum_matching(&graph, &mut pairing); 204 | 205 | assert_eq!( 206 | pairing.edges().collect::>(), 207 | [ (0, 1), (2, 3) ].iter().cloned().collect::>() 208 | ) 209 | } 210 | 211 | #[test] 212 | fn p4_from_reversed_edges() { 213 | let graph = DefaultGraph::try_from(vec![ 214 | (0, 1), 215 | (0, 4), 216 | (3, 4) 217 | ]).unwrap(); 218 | let mut pairing = Pairing::new(); 219 | 220 | maximum_matching(&graph, &mut pairing); 221 | 222 | assert_eq!( 223 | pairing.edges().collect::>(), 224 | [ (0, 1), (3, 4) ].iter().cloned().collect::>() 225 | ) 226 | } 227 | 228 | #[test] 229 | fn c5() { 230 | let graph = DefaultGraph::try_from(vec![ 231 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 0) 232 | ]).unwrap(); 233 | let mut pairing = Pairing::new(); 234 | 235 | maximum_matching(&graph, &mut pairing); 236 | 237 | assert_eq!( 238 | pairing.edges().collect::>(), 239 | [ (0, 1), (2, 3) ].iter().cloned().collect::>() 240 | ) 241 | } 242 | 243 | #[test] 244 | fn c6() { 245 | let graph = DefaultGraph::try_from(vec![ 246 | vec![ 1, 5 ], 247 | vec![ 0, 2 ], 248 | vec![ 1, 3 ], 249 | vec![ 2, 4 ], 250 | vec![ 3, 5 ], 251 | vec![ 4, 0 ] 252 | ]).unwrap(); 253 | let mut pairing = Pairing::new(); 254 | 255 | maximum_matching(&graph, &mut pairing); 256 | 257 | assert_eq!( 258 | pairing.edges().collect::>(), 259 | [ (0, 1), (2, 3), (4, 5) ].iter().cloned().collect::>() 260 | ) 261 | } 262 | 263 | #[test] 264 | fn acenapthene() { 265 | let graph = DefaultGraph::try_from(vec![ 266 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), 267 | (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), 268 | (10, 0), (11, 5), (11, 1), (11, 9) 269 | ]).unwrap(); 270 | 271 | let mut pairing = Pairing::new(); 272 | 273 | maximum_matching(&graph, &mut pairing); 274 | 275 | assert_eq!( 276 | pairing.edges().collect::>(), 277 | [ (0, 10), (1, 11), (2, 3), (4, 5), (6, 7), (8, 9) ] 278 | .iter().cloned().collect::>() 279 | ) 280 | } 281 | 282 | #[test] 283 | fn p2_p2() { 284 | let graph = DefaultGraph::try_from(vec![ 285 | vec![ 1 ], 286 | vec![ 0 ], 287 | vec![ 3 ], 288 | vec![ 2 ] 289 | 290 | ]).unwrap(); 291 | let mut pairing = Pairing::new(); 292 | 293 | maximum_matching(&graph, &mut pairing); 294 | 295 | assert_eq!( 296 | pairing.edges().collect::>(), 297 | [ (0, 1), (2, 3) ].iter().cloned().collect::>() 298 | ) 299 | } 300 | 301 | #[test] 302 | fn path_through_5_blossom() { 303 | let graph = DefaultGraph::try_from(vec![ 304 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1), 305 | (4, 6), (6, 7) 306 | ]).unwrap(); 307 | let mut pairing = Pairing::new(); 308 | 309 | pairing.pair(2, 3); 310 | pairing.pair(1, 5); 311 | pairing.pair(4, 6); 312 | 313 | maximum_matching(&graph, &mut pairing); 314 | 315 | assert_eq!( 316 | pairing.edges().collect::>(), 317 | [ 318 | (0, 1), (2, 3), (4, 5), (6, 7) 319 | ].iter().cloned().collect::>() 320 | ) 321 | } 322 | 323 | #[test] 324 | fn c5_with_c3_stem_distant_unmatched() { 325 | let graph = DefaultGraph::try_from(vec![ 326 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (4, 5), (5, 6), (6, 7) 327 | ]).unwrap(); 328 | let mut pairing = Pairing::new(); 329 | 330 | pairing.pair(4, 0); 331 | pairing.pair(6, 5); 332 | pairing.pair(3, 2); 333 | 334 | maximum_matching(&graph, &mut pairing); 335 | 336 | assert_eq!( 337 | pairing.edges().collect::>(), 338 | [ 339 | (6, 7), (4, 5), (0, 1), (2, 3) 340 | ].iter().cloned().collect::>() 341 | ) 342 | } 343 | 344 | // https://doi.org/10.2991/icieac-14.2014.14 345 | #[test] 346 | fn yu_zhonge() { 347 | let graph = DefaultGraph::try_from(vec![ 348 | (1, 2), (2, 3), 349 | (3, 13), (13, 14), (14, 4), (4, 5), (5, 6), (6, 7), 350 | (7, 8), (8, 9), (9, 10), (10, 11), (11, 5), 351 | (10, 18), (18, 17), (17, 16), (16, 15), (15, 14), 352 | (18, 19), (19, 20), (20, 21), (21, 22), (22, 16), 353 | (4, 3), (13, 12) 354 | ]).unwrap(); 355 | let mut pairing = Pairing::new(); 356 | 357 | pairing.pair(1, 2); 358 | pairing.pair(3, 13); 359 | pairing.pair(4, 14); 360 | pairing.pair(5, 6); 361 | pairing.pair(7, 8); 362 | pairing.pair(9, 10); 363 | pairing.pair(15, 16); 364 | pairing.pair(17, 18); 365 | pairing.pair(19, 20); 366 | pairing.pair(21, 22); 367 | 368 | maximum_matching(&graph, &mut pairing); 369 | 370 | assert_eq!( 371 | pairing.edges().collect::>(), [ 372 | (1, 2), (3, 4), (6, 7), (8, 9), 373 | (5, 11), (10, 18), (19, 20), (21, 22), 374 | (16, 17), (14, 15), (12, 13) 375 | ].iter().cloned().collect::>() 376 | ) 377 | } 378 | 379 | #[test] 380 | fn coronene_6_5() { 381 | let graph = DefaultGraph::try_from(vec![ 382 | (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), 383 | (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), 384 | (16, 17), (17, 6), 385 | (6, 0), (8, 1), (10, 2), (12, 3), (14, 4), (16, 5), 386 | (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 8) 387 | ]).unwrap(); 388 | let mut pairing = Pairing::new(); 389 | 390 | maximum_matching(&graph, &mut pairing); 391 | 392 | assert_eq!( 393 | pairing.edges().collect::>(), [ 394 | (0, 1), (2, 3), (4, 5), (6, 7), (8, 9), 395 | (10, 11), (12, 13), (14, 15), (16, 17) 396 | ].iter().cloned().collect::>() 397 | ) 398 | } 399 | 400 | #[test] 401 | fn coronene_3_5() { 402 | let graph = DefaultGraph::try_from(vec![ 403 | (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), 404 | (10, 11), (11, 4), 405 | (0, 4), (1, 6), (2, 8), (3, 10), 406 | (0, 1), (1, 2), (2, 3), (3, 0) 407 | ]).unwrap(); 408 | let mut pairing = Pairing::new(); 409 | 410 | maximum_matching(&graph, &mut pairing); 411 | 412 | assert_eq!( 413 | pairing.edges().collect::>(), [ 414 | (4, 5), (0, 1), (8, 9), (10, 11), (2, 3), (6, 7) 415 | ].iter().cloned().collect::>() 416 | ) 417 | } 418 | 419 | #[test] 420 | fn c60() { 421 | let graph = DefaultGraph::try_from(vec![ 422 | (29, 30), (30, 43), (43, 44), (44, 55), (55, 29), 423 | (29, 28), (31, 30), (43, 42), (44, 45), (55, 54), 424 | (28, 57), (57, 56), (56, 31), (31, 32), (32, 33), 425 | (33, 42), (42, 41), (41, 40), (40, 45), (45, 46), 426 | (46, 47), (47, 54), (54, 26), (26, 27), (27, 28), 427 | (57, 7), (56, 4), (32, 3), (33, 34), (41, 36), 428 | (40, 39), (46, 51), (47, 48), (26, 25), (27, 8), 429 | (7, 6), (6, 5), (5, 4), (4, 3), (3, 2), 430 | (2, 35), (35, 34), (34, 36), (36, 37), (37, 38), 431 | (38, 39), (39, 51), (51, 50), (50, 49), (49, 48), 432 | (48, 25), (25, 24), (24, 9), (9, 8), (8, 7), 433 | (6, 11), (5, 0), (2, 1), (35, 16), (37, 17), 434 | (38, 53), (50, 52), (49, 22), (24, 23), (9, 10), 435 | (11, 12), (12, 0), (0, 1), (1, 15), (15, 16), 436 | (16, 17), (17, 18), (18, 53), (53, 52), (52, 21), 437 | (21, 22), (22, 23), (23, 58), (58, 10), (10, 11), 438 | (12, 13), (15, 14), (18, 19), (21, 20), (58, 59), 439 | (13, 14), (14, 19), (19, 20), (20, 59), (59, 13) 440 | ]).unwrap(); 441 | let mut pairing = Pairing::new(); 442 | 443 | maximum_matching(&graph, &mut pairing); 444 | 445 | assert_eq!( 446 | pairing.edges().collect::>(), [ 447 | (48, 49), (1, 2), (17, 37), (6, 7), (54, 55), (31, 56), 448 | (19, 20), (28, 57), (26, 27), (22, 23), (29, 30), (4, 5), 449 | (50, 51), (34, 35), (24, 25), (10, 11), (43, 44), (3, 32), 450 | (46, 47), (13, 14), (33, 42), (58, 59), (15, 16), (8, 9), 451 | (36, 41), (38, 39), (21, 52), (40, 45), (0, 12), (18, 53) 452 | ].iter().cloned().collect::>() 453 | ) 454 | } 455 | } -------------------------------------------------------------------------------- /src/matching/mod.rs: -------------------------------------------------------------------------------- 1 | mod forest; 2 | mod pairing; 3 | mod blossom; 4 | mod marker; 5 | mod maximum_matching; 6 | mod greedy; 7 | 8 | pub use pairing::Pairing; 9 | pub use maximum_matching::maximum_matching; 10 | pub use greedy::greedy; -------------------------------------------------------------------------------- /src/matching/pairing.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::collections::hash_map::Entry::{ Occupied, Vacant }; 3 | 4 | #[derive(Debug,PartialEq)] 5 | pub struct Pairing { 6 | pairs: HashMap 7 | } 8 | 9 | impl Pairing { 10 | pub fn new() -> Self { 11 | Self { 12 | pairs: HashMap::new() 13 | } 14 | } 15 | 16 | pub fn order(&self) -> usize { 17 | self.pairs.len() 18 | } 19 | 20 | pub fn has_node(&self, id: usize) -> bool { 21 | self.pairs.contains_key(&id) 22 | } 23 | 24 | pub fn pair(&mut self, sid: usize, tid: usize) { 25 | self.insert(sid, tid); 26 | self.insert(tid, sid) 27 | } 28 | 29 | pub fn edges(&self) -> impl Iterator + '_ { 30 | self.pairs.iter() 31 | .filter(|pair| pair.0 < pair.1) 32 | .map(|pair| (*pair.0, *pair.1)) 33 | } 34 | 35 | pub fn augment(&mut self, path: Vec) { 36 | if path.len() % 2 == 1 { 37 | panic!("even path augmentation"); 38 | } 39 | 40 | for i in 0..path.len() { 41 | if i % 2 == 0 { 42 | self.pair(path[i], path[i + 1]); 43 | } 44 | } 45 | } 46 | 47 | pub fn mate(&self, id: usize) -> usize { 48 | match self.pairs.get(&id) { 49 | Some(&mate) => mate, 50 | None => panic!("missing node: {}", id) 51 | } 52 | } 53 | 54 | fn insert(&mut self, sid: usize, tid: usize) { 55 | match self.pairs.entry(sid) { 56 | Occupied(mut entry) => { 57 | if *entry.get() == tid { 58 | // panic!("duplicate edge: ({},{})", sid, tid) 59 | } else { 60 | let old = entry.insert(tid); 61 | 62 | self.pairs.remove(&old); 63 | } 64 | }, 65 | Vacant(entry) => { 66 | entry.insert(tid); 67 | } 68 | } 69 | } 70 | } 71 | 72 | #[cfg(test)] 73 | mod order { 74 | use super::*; 75 | 76 | #[test] 77 | fn default() { 78 | let pairing = Pairing::new(); 79 | 80 | assert_eq!(pairing.order(), 0); 81 | } 82 | 83 | #[test] 84 | fn three_pairs() { 85 | let mut pairing = Pairing::new(); 86 | 87 | pairing.pair(0, 1); 88 | pairing.pair(2, 3); 89 | pairing.pair(4, 5); 90 | 91 | assert_eq!(pairing.order(), 6); 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod edges { 97 | use super::*; 98 | 99 | #[test] 100 | fn default() { 101 | let pairing = Pairing::new(); 102 | 103 | assert_eq!(pairing.pairs.is_empty(), true) 104 | } 105 | 106 | #[test] 107 | fn pair_unk_unk() { 108 | let mut pairing = Pairing::new(); 109 | 110 | pairing.pair(0, 1); 111 | pairing.pair(2, 3); 112 | 113 | assert_eq!( 114 | pairing.pairs, 115 | [ (0, 1), (2, 3), (1, 0), (3, 2) ] 116 | .iter().cloned().collect::>() 117 | ) 118 | } 119 | 120 | #[test] 121 | fn pair_unk_tid() { 122 | let mut pairing = Pairing::new(); 123 | 124 | pairing.pair(1, 2); 125 | pairing.pair(0, 1); 126 | 127 | assert_eq!( 128 | pairing.pairs, 129 | [ (0, 1), (1, 0) ].iter().cloned().collect::>() 130 | ) 131 | } 132 | 133 | #[test] 134 | fn pair_sid_unk() { 135 | let mut pairing = Pairing::new(); 136 | 137 | pairing.pair(1, 2); 138 | pairing.pair(2, 3); 139 | 140 | assert_eq!( 141 | pairing.pairs, 142 | [ (2, 3), (3, 2) ].iter().cloned().collect::>() 143 | ) 144 | } 145 | 146 | #[test] 147 | fn pair_sid_tid() { 148 | let mut pairing = Pairing::new(); 149 | 150 | pairing.pair(0, 1); 151 | pairing.pair(2, 3); 152 | pairing.pair(1, 2); 153 | 154 | assert_eq!( 155 | pairing.pairs, 156 | [ (1, 2), (2, 1) ].iter().cloned().collect::>() 157 | ) 158 | } 159 | 160 | #[test] 161 | fn augment_empty() { 162 | let mut pairing = Pairing::new(); 163 | let path = vec![ 0, 1, 2, 3 ]; 164 | 165 | pairing.augment(path); 166 | 167 | assert_eq!( 168 | pairing.pairs, 169 | [ (0, 1), (2, 3), (1, 0), (3, 2) ] 170 | .iter().cloned().collect::>() 171 | ) 172 | } 173 | 174 | #[test] 175 | fn augment_inner() { 176 | let mut pairing = Pairing::new(); 177 | let path = vec![ 0, 1, 2, 3 ]; 178 | 179 | pairing.pair(1, 2); 180 | pairing.augment(path); 181 | 182 | assert_eq!( 183 | pairing.pairs, 184 | [ (0, 1), (2, 3), (1, 0), (3, 2) ] 185 | .iter().cloned().collect::>() 186 | ) 187 | } 188 | } 189 | 190 | #[cfg(test)] 191 | mod augment { 192 | use super::*; 193 | 194 | #[test] 195 | #[should_panic(expected="even path augmentation")] 196 | fn odd_path() { 197 | let mut pairing = Pairing::new(); 198 | let path = vec![ 0, 1, 2, 3, 4 ]; 199 | 200 | pairing.augment(path) 201 | } 202 | } 203 | 204 | #[cfg(test)] 205 | mod has_node { 206 | use super::*; 207 | 208 | #[test] 209 | fn default() { 210 | let pairing = Pairing::new(); 211 | 212 | assert_eq!(pairing.has_node(0), false) 213 | } 214 | 215 | #[test] 216 | fn given_source() { 217 | let mut pairing = Pairing::new(); 218 | 219 | pairing.pair(0, 1); 220 | 221 | assert_eq!(pairing.has_node(0), true) 222 | } 223 | 224 | #[test] 225 | fn given_target() { 226 | let mut pairing = Pairing::new(); 227 | 228 | pairing.pair(0, 1); 229 | 230 | assert_eq!(pairing.has_node(1), true) 231 | } 232 | } 233 | 234 | #[cfg(test)] 235 | mod mate { 236 | use super::*; 237 | 238 | #[test] 239 | #[should_panic(expected="missing node: 0")] 240 | fn outside() { 241 | let pairing = Pairing::new(); 242 | 243 | pairing.mate(0); 244 | } 245 | 246 | #[test] 247 | fn sid() { 248 | let mut pairing = Pairing::new(); 249 | 250 | pairing.pair(0, 1); 251 | 252 | assert_eq!(pairing.mate(0), 1) 253 | } 254 | 255 | #[test] 256 | fn tid() { 257 | let mut pairing = Pairing::new(); 258 | 259 | pairing.pair(0, 1); 260 | 261 | assert_eq!(pairing.mate(1), 0) 262 | } 263 | } -------------------------------------------------------------------------------- /src/selection/components.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use std::collections::HashSet; 3 | 4 | use crate::graph::{ Graph, DefaultGraph }; 5 | use crate::traversal::{ DepthFirst }; 6 | 7 | /// Returns the [connected components](https://en.wikipedia.org/wiki/Component_(graph_theory)) 8 | /// of a Graph as an Adjacency. 9 | /// 10 | /// ```rust 11 | /// use std::convert::TryFrom; 12 | /// 13 | /// use gamma::graph::{ Graph, Error, DefaultGraph }; 14 | /// use gamma::selection::components; 15 | /// 16 | /// fn main() -> Result<(), Error> { 17 | /// let graph = DefaultGraph::try_from(vec![ 18 | /// vec![ 1 ], 19 | /// vec![ 0 ], 20 | /// vec![ ] 21 | /// ])?; 22 | /// let mut c1 = DefaultGraph::new(); 23 | /// let mut c2 = DefaultGraph::new(); 24 | /// 25 | /// c1.add_node(0)?; 26 | /// c1.add_node(1)?; 27 | /// c1.add_edge(0, 1)?; 28 | /// 29 | /// c2.add_node(2)?; 30 | /// 31 | /// assert_eq!(components(&graph).collect::>(), vec![ c1, c2 ]); 32 | /// 33 | /// Ok(()) 34 | /// } 35 | /// ``` 36 | pub fn components<'a, G: Graph>( 37 | graph: &'a G 38 | ) -> Components<'a, G> { 39 | Components { 40 | visited: HashSet::new(), 41 | iter: graph.ids(), 42 | graph: graph 43 | } 44 | } 45 | 46 | pub struct Components<'a, G: Graph> { 47 | visited: HashSet, 48 | // iter: std::slice::Iter<'a, usize>, 49 | iter: Box + 'a>, 50 | graph: &'a G 51 | } 52 | 53 | impl<'a, G: Graph> Iterator for Components<'a, G> { 54 | type Item = DefaultGraph; 55 | 56 | fn next(&mut self) -> Option { 57 | let root = loop { 58 | match self.iter.next() { 59 | Some(root) => { 60 | if !self.visited.contains(&root) { 61 | break root; 62 | } 63 | }, 64 | None => return None 65 | } 66 | }; 67 | 68 | self.visited.insert(root); 69 | 70 | let traversal = DepthFirst::new(self.graph, root).expect( 71 | "root not found" 72 | ); 73 | let mut component = DefaultGraph::try_from(traversal).expect( 74 | "traversal error" 75 | ); 76 | 77 | if component.is_empty() { 78 | component.add_node(root).expect("add root to empty graph"); 79 | } else { 80 | for id in component.ids() { 81 | self.visited.insert(id); 82 | } 83 | } 84 | 85 | Some(component) 86 | } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | 93 | #[test] 94 | fn p1() { 95 | let graph = DefaultGraph::try_from(vec![ 96 | vec![ ] 97 | ]).unwrap(); 98 | let components = components(&graph).collect::>(); 99 | 100 | assert_eq!(components, vec![ graph ]) 101 | } 102 | 103 | #[test] 104 | fn p1_p1() { 105 | let graph = DefaultGraph::try_from(vec![ 106 | vec![ ], 107 | vec![ ] 108 | ]).unwrap(); 109 | let components = components(&graph).collect::>(); 110 | let mut c1 = DefaultGraph::new(); 111 | let mut c2 = DefaultGraph::new(); 112 | 113 | assert_eq!(c1.add_node(0), Ok(())); 114 | assert_eq!(c2.add_node(1), Ok(())); 115 | 116 | assert_eq!(components, vec![ c1, c2 ]) 117 | } 118 | 119 | #[test] 120 | fn p2() { 121 | let graph = DefaultGraph::try_from(vec![ 122 | vec![ 1 ], 123 | vec![ 0 ] 124 | ]).unwrap(); 125 | let components = components(&graph).collect::>(); 126 | 127 | assert_eq!(components, vec![ graph ]) 128 | } 129 | 130 | #[test] 131 | fn p2_p1() { 132 | let graph = DefaultGraph::try_from(vec![ 133 | vec![ 1 ], 134 | vec![ 0 ], 135 | vec![ ], 136 | ]).unwrap(); 137 | let components = components(&graph).collect::>(); 138 | let mut c1 = DefaultGraph::new(); 139 | let mut c2 = DefaultGraph::new(); 140 | 141 | assert_eq!(c1.add_node(0), Ok(())); 142 | assert_eq!(c1.add_node(1), Ok(())); 143 | assert_eq!(c1.add_edge(0, 1), Ok(())); 144 | 145 | assert_eq!(c2.add_node(2), Ok(())); 146 | 147 | assert_eq!(components, vec![c1, c2 ]) 148 | } 149 | 150 | #[test] 151 | fn p2_p2_p2() { 152 | let graph = DefaultGraph::try_from(vec![ 153 | vec![ 1 ], 154 | vec![ 0 ], 155 | vec![ 3 ], 156 | vec![ 2 ], 157 | vec![ 5 ], 158 | vec![ 4 ] 159 | ]).unwrap(); 160 | let components = components(&graph).collect::>(); 161 | let mut c1 = DefaultGraph::new(); 162 | let mut c2 = DefaultGraph::new(); 163 | let mut c3 = DefaultGraph::new(); 164 | 165 | assert_eq!(c1.add_node(0), Ok(())); 166 | assert_eq!(c1.add_node(1), Ok(())); 167 | assert_eq!(c1.add_edge(0, 1), Ok(())); 168 | 169 | assert_eq!(c2.add_node(2), Ok(())); 170 | assert_eq!(c2.add_node(3), Ok(())); 171 | assert_eq!(c2.add_edge(2, 3), Ok(())); 172 | 173 | assert_eq!(c3.add_node(4), Ok(())); 174 | assert_eq!(c3.add_node(5), Ok(())); 175 | assert_eq!(c3.add_edge(4, 5), Ok(())); 176 | 177 | assert_eq!(components, vec![c1, c2, c3 ]); 178 | } 179 | 180 | #[test] 181 | fn c3_p2() { 182 | let graph = DefaultGraph::try_from(vec![ 183 | vec![ 1, 2 ], 184 | vec![ 0, 2 ], 185 | vec![ 0, 1 ], 186 | vec![ 4 ], 187 | vec![ 3 ] 188 | ]).unwrap(); 189 | let components = components(&graph).collect::>(); 190 | let mut c1 = DefaultGraph::new(); 191 | let mut c2 = DefaultGraph::new(); 192 | 193 | assert_eq!(c1.add_node(0), Ok(())); 194 | assert_eq!(c1.add_node(1), Ok(())); 195 | assert_eq!(c1.add_node(2), Ok(())); 196 | assert_eq!(c1.add_edge(0, 1), Ok(())); 197 | assert_eq!(c1.add_edge(1, 2), Ok(())); 198 | assert_eq!(c1.add_edge(2, 0), Ok(())); 199 | 200 | assert_eq!(c2.add_node(3), Ok(())); 201 | assert_eq!(c2.add_node(4), Ok(())); 202 | assert_eq!(c2.add_edge(3, 4), Ok(())); 203 | 204 | assert_eq!(components, vec![ c1, c2 ]) 205 | } 206 | } -------------------------------------------------------------------------------- /src/selection/mod.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | pub use components::components; -------------------------------------------------------------------------------- /src/traversal/breadth_first.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::collections::HashSet; 3 | 4 | use crate::graph::{ Graph, Error }; 5 | use super::Step; 6 | 7 | /// Implements a breadth-first traversal as a Step Iterator. 8 | /// 9 | /// ```rust 10 | /// use std::convert::TryFrom; 11 | /// use gamma::graph::{ Graph, Error, DefaultGraph }; 12 | /// use gamma::traversal::{ BreadthFirst, Step}; 13 | /// 14 | /// fn main() -> Result<(), Error> { 15 | /// let graph = DefaultGraph::try_from(vec![ 16 | /// vec![ 1, 3 ], 17 | /// vec![ 0, 2 ], 18 | /// vec![ 1, 3 ], 19 | /// vec![ 2, 0 ] 20 | /// ])?; 21 | /// let traversal = BreadthFirst::new(&graph, 0)?; 22 | /// 23 | /// assert_eq!(traversal.collect::>(), vec![ 24 | /// Step::new(0, 1, false), 25 | /// Step::new(0, 3, false), 26 | /// Step::new(1, 2, false), 27 | /// Step::new(3, 2, true) 28 | /// ]); 29 | /// 30 | /// Ok(()) 31 | /// } 32 | /// ``` 33 | // pub fn breadth_first<'a, G>( 34 | // graph: &'a G, root: usize 35 | // ) -> Result, Error> 36 | // where G: Graph { 37 | // let mut nodes = HashSet::new(); 38 | // let mut queue = VecDeque::new(); 39 | 40 | // for neighbor in graph.neighbors(root)? { 41 | // queue.push_front((root, *neighbor)); 42 | // } 43 | 44 | // nodes.insert(root); 45 | 46 | // Ok(BreadthFirst { nodes, queue, graph }) 47 | // } 48 | 49 | /// #[derive(Debug,PartialEq)] 50 | pub struct BreadthFirst<'a, G> { 51 | nodes: HashSet, 52 | queue: VecDeque<(usize, usize)>, 53 | graph: &'a G 54 | } 55 | 56 | impl<'a, G: Graph> BreadthFirst<'a, G> { 57 | pub fn new(graph: &'a G, root: usize) -> Result { 58 | let mut nodes = HashSet::new(); 59 | let mut queue = VecDeque::new(); 60 | 61 | for neighbor in graph.neighbors(root)? { 62 | queue.push_front((root, neighbor)); 63 | } 64 | 65 | nodes.insert(root); 66 | 67 | Ok(Self { nodes, queue, graph }) 68 | } 69 | } 70 | 71 | impl<'a, G> Iterator for BreadthFirst<'a, G> 72 | where G: Graph { 73 | type Item = Step; 74 | 75 | fn next(&mut self) -> Option { 76 | match self.queue.pop_back() { 77 | None => None, 78 | Some((parent, node)) => { 79 | if self.nodes.contains(&node) { 80 | Some(Step::new(parent, node, true)) 81 | } else { 82 | for neighbor in self.graph.neighbors(node).unwrap() { 83 | if neighbor == parent || self.nodes.contains(&neighbor) { 84 | continue; 85 | } 86 | 87 | self.queue.push_front((node, neighbor)); 88 | } 89 | 90 | self.nodes.insert(node); 91 | 92 | Some(Step::new(parent, node, false)) 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | use std::convert::TryFrom; 103 | use crate::graph::DefaultGraph; 104 | 105 | #[test] 106 | fn nonmember_root() { 107 | let graph = DefaultGraph::try_from(vec![ 108 | vec! [ ] 109 | ]).unwrap(); 110 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 111 | 112 | assert_eq!(traversal.collect::>(), vec![ ]); 113 | } 114 | 115 | #[test] 116 | fn p1() { 117 | let graph = DefaultGraph::try_from(vec![ 118 | vec![ ] 119 | ]).unwrap(); 120 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 121 | 122 | assert_eq!(traversal.collect::>(), vec![ ]); 123 | } 124 | 125 | #[test] 126 | fn p2() { 127 | let graph = DefaultGraph::try_from(vec![ 128 | vec![ 1 ], 129 | vec![ 0 ] 130 | ]).unwrap(); 131 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 132 | 133 | assert_eq!(traversal.collect::>(), vec![ 134 | Step::new(0, 1, false) 135 | ]); 136 | } 137 | 138 | #[test] 139 | fn p3_primary() { 140 | let graph = DefaultGraph::try_from(vec![ 141 | vec![ 1 ], 142 | vec![ 0, 2 ], 143 | vec![ 1 ] 144 | ]).unwrap(); 145 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 146 | 147 | assert_eq!(traversal.collect::>(), vec![ 148 | Step::new(0, 1, false), 149 | Step::new(1, 2, false) 150 | ]); 151 | } 152 | 153 | #[test] 154 | fn p3_secondary() { 155 | let graph = DefaultGraph::try_from(vec![ 156 | vec![ 1 ], 157 | vec![ 0, 2 ], 158 | vec![ 1 ] 159 | ]).unwrap(); 160 | let traversal = BreadthFirst::new(&graph, 1).unwrap(); 161 | 162 | assert_eq!(traversal.collect::>(), vec![ 163 | Step::new(1, 0, false), 164 | Step::new(1, 2, false) 165 | ]); 166 | } 167 | 168 | #[test] 169 | fn p4_primary() { 170 | let graph = DefaultGraph::try_from(vec![ 171 | vec![ 1 ], 172 | vec![ 0, 2 ], 173 | vec![ 1, 3 ], 174 | vec![ 2 ] 175 | ]).unwrap(); 176 | let traversal = BreadthFirst::new(&graph, 1).unwrap(); 177 | 178 | assert_eq!(traversal.collect::>(), vec![ 179 | Step::new(1, 0, false), 180 | Step::new(1, 2, false), 181 | Step::new(2, 3, false) 182 | ]); 183 | } 184 | 185 | #[test] 186 | fn s3_tertiary() { 187 | let graph = DefaultGraph::try_from(vec![ 188 | vec![ 1, 2, 3 ], 189 | vec![ 0 ], 190 | vec![ 0 ], 191 | vec![ 0 ] 192 | ]).unwrap(); 193 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 194 | 195 | assert_eq!(traversal.collect::>(), vec![ 196 | Step::new(0, 1, false), 197 | Step::new(0, 2, false), 198 | Step::new(0, 3, false) 199 | ]); 200 | } 201 | 202 | #[test] 203 | fn s3_primary() { 204 | let graph = DefaultGraph::try_from(vec![ 205 | vec![ 1, 2, 3 ], 206 | vec![ 0 ], 207 | vec![ 0 ], 208 | vec![ 0 ] 209 | ]).unwrap(); 210 | let traversal = BreadthFirst::new(&graph, 1).unwrap(); 211 | 212 | assert_eq!(traversal.collect::>(), vec![ 213 | Step::new(1, 0, false), 214 | Step::new(0, 2, false), 215 | Step::new(0, 3, false) 216 | ]); 217 | } 218 | 219 | #[test] 220 | fn c3() { 221 | let graph = DefaultGraph::try_from(vec![ 222 | vec![ 1, 2 ], 223 | vec![ 0, 2 ], 224 | vec![ 1, 0 ] 225 | ]).unwrap(); 226 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 227 | 228 | assert_eq!(traversal.collect::>(), vec![ 229 | Step::new(0, 1, false), 230 | Step::new(0, 2, false), 231 | Step::new(1, 2, true) 232 | ]); 233 | } 234 | 235 | #[test] 236 | fn c4() { 237 | let graph = DefaultGraph::try_from(vec![ 238 | vec![ 1, 3 ], 239 | vec![ 0, 2 ], 240 | vec![ 1, 3 ], 241 | vec![ 2, 0 ] 242 | ]).unwrap(); 243 | let traversal = BreadthFirst::new(&graph, 0).unwrap(); 244 | 245 | assert_eq!(traversal.collect::>(), vec![ 246 | Step::new(0, 1, false), 247 | Step::new(0, 3, false), 248 | Step::new(1, 2, false), 249 | Step::new(3, 2, true) 250 | ]); 251 | } 252 | 253 | #[test] 254 | fn diamond() { 255 | let graph = DefaultGraph::try_from(vec![ 256 | vec![ 1, 2, 3 ], 257 | vec![ 2, 0 ], 258 | vec![ 0, 1, 3 ], 259 | vec![ 0, 2 ] 260 | ]).unwrap(); 261 | let bfs = BreadthFirst::new(&graph, 0).unwrap(); 262 | 263 | assert_eq!(bfs.collect::>(), vec![ 264 | Step::new(0, 1, false), 265 | Step::new(0, 2, false), 266 | Step::new(0, 3, false), 267 | Step::new(1, 2, true), 268 | Step::new(2, 3, true) 269 | ]); 270 | } 271 | 272 | #[test] 273 | fn flower_from_stalk() { 274 | let graph = DefaultGraph::try_from(vec![ 275 | vec![ 1 ], 276 | vec![ 0, 2, 3 ], 277 | vec![ 1, 3 ], 278 | vec![ 2, 1 ] 279 | ]).unwrap(); 280 | let bfs = BreadthFirst::new(&graph, 0).unwrap(); 281 | 282 | assert_eq!(bfs.collect::>(), vec![ 283 | Step::new(0, 1, false), 284 | Step::new(1, 2, false), 285 | Step::new(1, 3, false), 286 | Step::new(2, 3, true) 287 | ]); 288 | } 289 | 290 | #[test] 291 | fn t2_primary() { 292 | let graph = DefaultGraph::try_from(vec![ 293 | vec![ 1, 4 ], 294 | vec![ 0, 2, 3 ], 295 | vec![ 1, 5 ], 296 | vec![ 1, 6 ], 297 | vec![ 0 ], 298 | vec![ 2 ], 299 | vec![ 3 ] 300 | ]).unwrap(); 301 | let bfs = BreadthFirst::new(&graph, 0).unwrap(); 302 | 303 | assert_eq!(bfs.collect::>(), vec![ 304 | Step::new(0, 1, false), 305 | Step::new(0, 4, false), 306 | Step::new(1, 2, false), 307 | Step::new(1, 3, false), 308 | Step::new(2, 5, false), 309 | Step::new(3, 6, false) 310 | ]); 311 | } 312 | 313 | #[test] 314 | fn t2_tertiary() { 315 | let graph = DefaultGraph::try_from(vec![ 316 | vec![ 1, 4 ], 317 | vec![ 0, 2, 3 ], 318 | vec![ 1, 5 ], 319 | vec![ 1, 6 ], 320 | vec![ 0 ], 321 | vec![ 2 ], 322 | vec![ 3 ] 323 | ]).unwrap(); 324 | let bfs = BreadthFirst::new(&graph, 1).unwrap(); 325 | 326 | assert_eq!(bfs.collect::>(), vec![ 327 | Step::new(1, 0, false), 328 | Step::new(1, 2, false), 329 | Step::new(1, 3, false), 330 | Step::new(0, 4, false), 331 | Step::new(2, 5, false), 332 | Step::new(3, 6, false) 333 | ]); 334 | } 335 | 336 | #[test] 337 | fn bicyclo_111() { 338 | let graph = DefaultGraph::try_from(vec![ 339 | vec![ 1, 2, 3 ], 340 | vec![ 0, 4 ], 341 | vec![ 0, 4 ], 342 | vec![ 0, 4 ], 343 | vec![ 1, 2, 3 ] 344 | ]).unwrap(); 345 | let bfs = BreadthFirst::new(&graph, 0).unwrap(); 346 | 347 | assert_eq!(bfs.collect::>(), vec![ 348 | Step::new(0, 1, false), 349 | Step::new(0, 2, false), 350 | Step::new(0, 3, false), 351 | Step::new(1, 4, false), 352 | Step::new(2, 4, true), 353 | Step::new(3, 4, true) 354 | ]); 355 | } 356 | 357 | #[test] 358 | fn bicyclo_221() { 359 | let graph = DefaultGraph::try_from(vec![ 360 | vec![ 1, 5 ], 361 | vec![ 0, 2, 6 ], 362 | vec![ 1, 3 ], 363 | vec![ 2, 4 ], 364 | vec![ 3, 5, 6 ], 365 | vec![ 4, 0 ], 366 | vec![ 4, 1 ] 367 | ]).unwrap(); 368 | let bfs = BreadthFirst::new(&graph, 0).unwrap(); 369 | 370 | assert_eq!(bfs.collect::>(), vec![ 371 | Step::new(0, 1, false), 372 | Step::new(0, 5, false), 373 | Step::new(1, 2, false), 374 | Step::new(1, 6, false), 375 | Step::new(5, 4, false), 376 | Step::new(2, 3, false), 377 | Step::new(6, 4, true), 378 | Step::new(4, 3, true) 379 | ]); 380 | } 381 | 382 | #[test] 383 | fn butterfly() { 384 | let graph = DefaultGraph::try_from(vec![ 385 | vec![ 1, 2 ], 386 | vec![ 0, 2 ], 387 | vec![ 0, 1, 3, 4 ], 388 | vec![ 2, 4 ], 389 | vec![ 2, 3 ] 390 | ]).unwrap(); 391 | let bfs = BreadthFirst::new(&graph, 0).unwrap(); 392 | 393 | assert_eq!(bfs.collect::>(), vec![ 394 | Step::new(0, 1, false), 395 | Step::new(0, 2, false), 396 | Step::new(1, 2, true), 397 | Step::new(2, 3, false), 398 | Step::new(2, 4, false), 399 | Step::new(3, 4, true) 400 | ]); 401 | } 402 | } -------------------------------------------------------------------------------- /src/traversal/depth_first.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use crate::graph::{ Graph, Error }; 4 | use super::Step; 5 | 6 | /// Implements a depth-first traversal as a Step Iterator. 7 | /// 8 | /// ```rust 9 | /// use std::convert::TryFrom; 10 | /// 11 | /// use gamma::graph::{ Graph, Error, DefaultGraph }; 12 | /// use gamma::traversal::{ DepthFirst, Step }; 13 | /// 14 | /// fn main() -> Result<(), Error> { 15 | /// let graph = DefaultGraph::try_from(vec![ 16 | /// vec![ 1, 3 ], 17 | /// vec![ 0, 2 ], 18 | /// vec![ 1, 3 ], 19 | /// vec![ 2, 0 ] 20 | /// ])?; 21 | /// let traversal = DepthFirst::new(&graph, 0)?; 22 | /// 23 | /// assert_eq!(traversal.collect::>(), vec![ 24 | /// Step::new(0, 1, false), 25 | /// Step::new(1, 2, false), 26 | /// Step::new(2, 3, false), 27 | /// Step::new(3, 0, true) 28 | /// ]); 29 | /// 30 | /// Ok(()) 31 | /// } 32 | /// ``` 33 | 34 | 35 | /// Iterates edges of graph in depth-first order. To perform a depth-first 36 | /// search, use the `depth_first` function instead. 37 | #[derive(Debug,PartialEq)] 38 | pub struct DepthFirst<'a, G> { 39 | nodes: HashSet, 40 | stack: Vec<(usize, usize)>, 41 | graph: &'a G 42 | } 43 | 44 | impl<'a, G: Graph> DepthFirst<'a, G> { 45 | pub fn new(graph: &'a G, root: usize) -> Result { 46 | let mut nodes = HashSet::new(); 47 | let mut stack = Vec::new(); 48 | 49 | for neighbor in graph.neighbors(root)? { 50 | stack.push((root, neighbor)); 51 | } 52 | 53 | nodes.insert(root); 54 | stack.reverse(); 55 | 56 | Ok(Self { nodes, stack, graph }) 57 | } 58 | 59 | pub fn into_table(self) -> (Vec, Vec<(usize, usize)>) { 60 | let mut nodes = Vec::new(); 61 | let mut edges = Vec::new(); 62 | 63 | for step in self { 64 | if nodes.is_empty() { 65 | nodes.push(step.sid); 66 | } 67 | 68 | if !step.cut { 69 | nodes.push(step.tid) 70 | } 71 | 72 | edges.push((step.sid, step.tid)); 73 | } 74 | 75 | (nodes, edges) 76 | } 77 | } 78 | 79 | impl<'a, G> Iterator for DepthFirst<'a, G> 80 | where G: Graph { 81 | type Item = Step; 82 | 83 | fn next(&mut self) -> Option { 84 | match self.stack.pop() { 85 | None => None, 86 | Some((parent, node)) => { 87 | if self.nodes.contains(&node) { 88 | Some(Step::new(parent, node, true)) 89 | } else { 90 | let neighbors = self.graph.neighbors(node).unwrap() 91 | .collect::>(); 92 | 93 | for neighbor in neighbors.into_iter().rev() { 94 | if neighbor == parent { 95 | continue; 96 | } 97 | 98 | if self.nodes.contains(&neighbor) { 99 | self.stack.retain( 100 | |edge| edge.0 != neighbor && edge.1 != node 101 | ); 102 | } 103 | 104 | self.stack.push((node, neighbor)); 105 | } 106 | 107 | self.nodes.insert(node); 108 | 109 | Some(Step::new(parent, node, false)) 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | #[cfg(test)] 117 | mod into_table { 118 | use super::*; 119 | use std::convert::TryFrom; 120 | use crate::graph::DefaultGraph; 121 | 122 | #[test] 123 | fn p3() { 124 | let graph = DefaultGraph::try_from(vec![ 125 | vec![ 1 ], 126 | vec![ 0, 2 ], 127 | vec![ 1 ] 128 | ]).unwrap(); 129 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 130 | 131 | assert_eq!(traversal.into_table(), (vec![ 0, 1, 2 ], vec![ 132 | (0, 1), 133 | (1, 2) 134 | ])) 135 | } 136 | 137 | #[test] 138 | fn c3() { 139 | let graph = DefaultGraph::try_from(vec![ 140 | vec![ 1, 2 ], 141 | vec![ 0, 2 ], 142 | vec![ 1, 0 ] 143 | ]).unwrap(); 144 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 145 | 146 | assert_eq!(traversal.into_table(), (vec![ 0, 1, 2 ], vec![ 147 | (0, 1), 148 | (1, 2), 149 | (2, 0) 150 | ])) 151 | } 152 | 153 | #[test] 154 | fn s3() { 155 | let graph = DefaultGraph::try_from(vec![ 156 | vec![ 1 ], 157 | vec![ 0, 2, 3 ], 158 | vec![ 1 ], 159 | vec![ 1 ] 160 | ]).unwrap(); 161 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 162 | 163 | assert_eq!(traversal.into_table(), (vec![ 0, 1, 2, 3 ], vec![ 164 | (0, 1), 165 | (1, 2), 166 | (1, 3) 167 | ])); 168 | } 169 | } 170 | 171 | #[cfg(test)] 172 | mod tests { 173 | use super::*; 174 | use std::convert::TryFrom; 175 | use crate::graph::DefaultGraph; 176 | 177 | #[test] 178 | fn unknown_root() { 179 | let graph = DefaultGraph::new(); 180 | let traversal = DepthFirst::new(&graph, 1); 181 | 182 | assert_eq!(traversal, Err(Error::UnknownId(1))); 183 | } 184 | 185 | #[test] 186 | fn p1() { 187 | let graph = DefaultGraph::try_from(vec![ 188 | vec![ ] 189 | ]).unwrap(); 190 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 191 | 192 | assert_eq!(traversal.collect::>(), vec![ ]); 193 | } 194 | 195 | #[test] 196 | fn p2() { 197 | let graph = DefaultGraph::try_from(vec![ 198 | vec![ 1 ], 199 | vec![ 0 ] 200 | ]).unwrap(); 201 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 202 | 203 | assert_eq!(traversal.collect::>(), vec![ 204 | Step::new(0, 1, false) 205 | ]); 206 | } 207 | 208 | #[test] 209 | fn p3() { 210 | let graph = DefaultGraph::try_from(vec![ 211 | vec![ 1 ], 212 | vec![ 0, 2 ], 213 | vec![ 1 ] 214 | ]).unwrap(); 215 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 216 | 217 | assert_eq!(traversal.collect::>(), vec![ 218 | Step::new(0, 1, false), 219 | Step::new(1, 2, false) 220 | ]); 221 | } 222 | 223 | #[test] 224 | fn p3_inside() { 225 | let graph = DefaultGraph::try_from(vec![ 226 | vec![ 1 ], 227 | vec![ 0, 2 ], 228 | vec![ 1 ] 229 | ]).unwrap(); 230 | let traversal = DepthFirst::new(&graph, 1).unwrap(); 231 | 232 | assert_eq!(traversal.collect::>(), vec![ 233 | Step::new(1, 0, false), 234 | Step::new(1, 2, false) 235 | ]); 236 | } 237 | 238 | #[test] 239 | fn p4() { 240 | let graph = DefaultGraph::try_from(vec![ 241 | vec![ 1 ], 242 | vec![ 0, 2 ], 243 | vec![ 1, 3 ], 244 | vec![ 2 ] 245 | ]).unwrap(); 246 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 247 | 248 | assert_eq!(traversal.collect::>(), vec![ 249 | Step::new(0, 1, false), 250 | Step::new(1, 2, false), 251 | Step::new(2, 3, false) 252 | ]); 253 | } 254 | 255 | #[test] 256 | fn c3() { 257 | let graph = DefaultGraph::try_from(vec![ 258 | vec![ 1, 2 ], 259 | vec![ 0, 2 ], 260 | vec![ 1, 0 ] 261 | ]).unwrap(); 262 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 263 | 264 | assert_eq!(traversal.collect::>(), vec![ 265 | Step::new(0, 1, false), 266 | Step::new(1, 2, false), 267 | Step::new(2, 0, true) 268 | ]); 269 | } 270 | 271 | #[test] 272 | fn s3() { 273 | let graph = DefaultGraph::try_from(vec![ 274 | vec![ 1 ], 275 | vec![ 0, 2, 3 ], 276 | vec![ 1 ], 277 | vec![ 1 ] 278 | ]).unwrap(); 279 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 280 | 281 | assert_eq!(traversal.collect::>(), vec![ 282 | Step::new(0, 1, false), 283 | Step::new(1, 2, false), 284 | Step::new(1, 3, false) 285 | ]); 286 | } 287 | 288 | #[test] 289 | fn s3_inside() { 290 | let graph = DefaultGraph::try_from(vec![ 291 | vec![ 1 ], 292 | vec![ 0, 2, 3 ], 293 | vec![ 1 ], 294 | vec![ 1 ] 295 | ]).unwrap(); 296 | let traversal = DepthFirst::new(&graph, 1).unwrap(); 297 | 298 | assert_eq!(traversal.collect::>(), vec![ 299 | Step::new(1, 0, false), 300 | Step::new(1, 2, false), 301 | Step::new(1, 3, false) 302 | ]); 303 | } 304 | 305 | #[test] 306 | fn flower_from_stalk() { 307 | let graph = DefaultGraph::try_from(vec![ 308 | vec![ 1 ], 309 | vec![ 0, 2, 3 ], 310 | vec![ 1, 3 ], 311 | vec![ 2, 1 ] 312 | ]).unwrap(); 313 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 314 | 315 | assert_eq!(traversal.collect::>(), vec![ 316 | Step::new(0, 1, false), 317 | Step::new(1, 2, false), 318 | Step::new(2, 3, false), 319 | Step::new(3, 1, true) 320 | ]); 321 | } 322 | 323 | #[test] 324 | fn flower_with_cut_in_branch() { 325 | let graph = DefaultGraph::try_from(vec![ 326 | vec![ 1, 2 ], 327 | vec![ 0, 2 ], 328 | vec![ 1, 0, 3 ], 329 | vec![ 2 ] 330 | ]).unwrap(); 331 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 332 | 333 | assert_eq!(traversal.collect::>(), vec![ 334 | Step::new(0, 1, false), 335 | Step::new(1, 2, false), 336 | Step::new(2, 0, true), 337 | Step::new(2, 3, false) 338 | ]); 339 | } 340 | 341 | #[test] 342 | fn blocked_branched_path() { 343 | let graph = DefaultGraph::try_from(vec![ 344 | vec![ 1 ], 345 | vec![ 0, 2, 3, 4 ], 346 | vec![ 1 ], 347 | vec![ 1, 4 ], 348 | vec![ 3, 1 ] 349 | ]).unwrap(); 350 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 351 | 352 | assert_eq!(traversal.collect::>(), vec![ 353 | Step::new(0, 1, false), 354 | Step::new(1, 2, false), 355 | Step::new(1, 3, false), 356 | Step::new(3, 4, false), 357 | Step::new(4, 1, true) 358 | ]); 359 | } 360 | 361 | #[test] 362 | fn bicyclo_220_with_cut_on_second_branching() { 363 | let graph = DefaultGraph::try_from(vec![ 364 | vec![ 1, 5 ], 365 | vec![ 0, 2 ], 366 | vec![ 1, 5, 3 ], 367 | vec![ 2, 4 ], 368 | vec![ 3, 5 ], 369 | vec![ 2, 4, 0 ] 370 | ]).unwrap(); 371 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 372 | 373 | assert_eq!(traversal.collect::>(), vec![ 374 | Step::new(0, 1, false), 375 | Step::new(1, 2, false), 376 | Step::new(2, 5, false), 377 | Step::new(5, 4, false), 378 | Step::new(4, 3, false), 379 | Step::new(3, 2, true), 380 | Step::new(5, 0, true) 381 | ]); 382 | } 383 | 384 | #[test] 385 | fn bicyclo_210() { 386 | let graph = DefaultGraph::try_from(vec![ 387 | vec![ 1, 2, 4 ], 388 | vec![ 0, 2 ], 389 | vec![ 1, 0, 3 ], 390 | vec![ 2, 4 ], 391 | vec![ 3, 0 ] 392 | ]).unwrap(); 393 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 394 | 395 | assert_eq!(traversal.collect::>(), vec![ 396 | Step::new(0, 1, false), 397 | Step::new(1, 2, false), 398 | Step::new(2, 0, true), 399 | Step::new(2, 3, false), 400 | Step::new(3, 4, false), 401 | Step::new(4, 0, true) 402 | ]); 403 | } 404 | 405 | #[test] 406 | fn cube() { 407 | let graph = DefaultGraph::try_from(vec![ 408 | vec![ 1, 3, 4 ], // 0 409 | vec![ 0, 2, 5 ], // 1 410 | vec![ 1, 3, 6 ], // 2 411 | vec![ 2, 0, 7 ], // 3 412 | vec![ 5, 7, 0 ], // 4 413 | vec![ 4, 6, 1 ], // 5 414 | vec![ 5, 7, 2 ], // 6 415 | vec![ 6, 4, 3 ] // 7 416 | ]).unwrap(); 417 | let traversal = DepthFirst::new(&graph, 0).unwrap(); 418 | 419 | assert_eq!(traversal.collect::>(), vec![ 420 | Step::new(0, 1, false), 421 | Step::new(1, 2, false), 422 | Step::new(2, 3, false), 423 | Step::new(3, 0, true), 424 | Step::new(3, 7, false), 425 | Step::new(7, 6, false), 426 | Step::new(6, 5, false), 427 | Step::new(5, 4, false), 428 | Step::new(4, 7, true), 429 | Step::new(4, 0, true), 430 | Step::new(5, 1, true), 431 | Step::new(6, 2, true) 432 | ]); 433 | } 434 | } -------------------------------------------------------------------------------- /src/traversal/mod.rs: -------------------------------------------------------------------------------- 1 | mod depth_first; 2 | mod breadth_first; 3 | mod step; 4 | 5 | pub use depth_first::DepthFirst; 6 | pub use breadth_first::BreadthFirst; 7 | pub use step::Step; -------------------------------------------------------------------------------- /src/traversal/step.rs: -------------------------------------------------------------------------------- 1 | /// A single traversal step comprised of source and target nodes, and a 2 | /// boolean flag indicating whether a cycle cut is present. 3 | #[derive(Eq,PartialEq,Hash,Debug)] 4 | pub struct Step { 5 | pub sid: usize, 6 | pub tid: usize, 7 | pub cut: bool 8 | } 9 | 10 | impl Step { 11 | pub fn new(sid: usize, tid: usize, cut: bool) -> Self { 12 | Step { sid, tid, cut } 13 | } 14 | } --------------------------------------------------------------------------------