use std::collections::VecDeque;
use fixedbitset::FixedBitSet;
use hashbrown::{HashMap, HashSet};
use crate::prelude::*;
pub struct Matching<'a, G: GraphBasics<'a>> {
graph: &'a G,
mate: Vec<Option<G::EdgeIndex>>,
}
impl<'a, G> Matching<'a, G>
where
G: GraphProperties<'a>,
{
pub fn mate(
&self,
node: G::NodeIndex,
) -> Option<HashSet<G::NodeIndex>> {
self.mate.get(node.into()).map(|&id| {
let mut u = self.graph.contained_nodes(id.unwrap()).unwrap().clone();
u.retain(|&x| x != node);
u
})
}
pub fn matched_edges(&'a self) -> impl Iterator<Item = (G::EdgeIndex, G::EdgeRef)> + 'a {
self.mate
.iter()
.filter_map(|&mate| mate)
.map(|e| (e, self.graph.edge(e).unwrap()))
}
pub fn nodes(&self) -> MatchedNodes<'_, G> {
MatchedNodes {
graph: &self.graph,
mate: self.mate.as_slice(),
current: 0,
}
}
pub fn contains_edge(&self, a: G::EdgeIndex) -> bool {
self.mate.contains(Some(a))
}
pub fn contains_node(&self, node: G::NodeIndex) -> bool {
self.mate(node).is_some()
}
pub fn len(&self) -> usize {
self.mate
.iter()
.filter(|&&mate| mate.is_some())
.collect::<HashSet<_>>().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a, G> Matching<'a, G>
where
G: GraphProperties<'a>
{
pub fn is_perfect(&self) -> bool {
!self.mate.contains(None)
}
}
pub struct MatchedNodes<'a, G: GraphBasics<'a>> {
graph: &'a G,
mate: &'a [Option<G::EdgeIndex>],
current: usize,
}
impl<'a, G> Iterator for MatchedNodes<'a, G>
where
G: GraphBasics<'a>,
{
type Item = (G::NodeIndex, G::NodeRef);
fn next(&mut self) -> Option<Self::Item> {
while self.current != self.mate.len() {
let current = self.current;
self.current += 1;
if self.mate[current].is_some() {
return Some((current, self.graph.node(current)));
}
}
None
}
}
pub struct MatchedEdges<'a, G: GraphBasics<'a>> {
graph: &'a G,
mate: HashMap<usize, G::EdgeIndex>,
}
impl<'a, G> Iterator for MatchedEdges<'a, G>
where
G: GraphBasics<'a>,
{
type Item = (G::EdgeIndex, G::EdgeRef);
fn next(&mut self) -> Option<Self::Item> {
while self.current != self.mate.len() {
let current = self.current;
self.current += 1;
if self.mate[current].is_some() {
return Some((current, self.graph.node(current)));
}
}
None
}
}
pub fn greedy_matching<'a, G>(graph: &G) -> Matching<'a, G>
where
G: GraphProperties<'a>,
{
let (mates, n_edges) = greedy_matching_inner(graph);
Matching { graph, mate: mates }
}
#[inline]
fn greedy_matching_inner<G>(graph: &G) -> (Vec<Option<<G as GraphBasics<'_>>::NodeIndex>>, usize)
where
G: GraphProperties,
{
let mut mate = vec![None; graph.node_bound()];
let mut n_edges = 0;
let visited = &mut graph.visit_map();
for start in graph.node_identifiers() {
let mut last = Some(start);
non_backtracking_dfs(graph, start, visited, |next| {
if let Some(pred) = last.take() {
mate[graph.to_index(pred)] = Some(next);
mate[graph.to_index(next)] = Some(pred);
n_edges += 1;
} else {
last = Some(next);
}
});
}
(mate, n_edges)
}
fn non_backtracking_dfs<G, F>(graph: &G, source: <G as GraphBasics<'_>>::NodeIndex, visited: &mut FixedBitSet, mut visitor: F)
where
G: GraphProperties,
F: FnMut(<G as GraphBasics<'_>>::NodeIndex),
{
if visited.visit(source) {
for target in graph.neighbors(source) {
if !visited.is_visited(&target) {
visitor(target);
non_backtracking_dfs(graph, target, visited, visitor);
break;
}
}
}
}
#[derive(Clone, Copy)]
enum Label<'a, G: GraphBasics<'a>> {
None,
Start,
Vertex(G::NodeIndex),
Edge(G::EdgeIndex, [G::NodeIndex; 2]),
Flag(G::EdgeIndex),
}
impl<'a, G: GraphBasics<'a>> Label<'a, G> {
fn is_outer(&self) -> bool {
self != &Label::None
&& !match self {
Label::Flag(_) => true,
_ => false,
}
}
fn is_inner(&self) -> bool {
!self.is_outer()
}
fn to_vertex(&self) -> Option<G::NodeIndex> {
match *self {
Label::Vertex(v) => Some(v),
_ => None,
}
}
fn is_flagged(&self, edge: G::EdgeIndex) -> bool {
match self {
Label::Flag(flag) if flag == &edge => true,
_ => false,
}
}
}
impl<'a, G: GraphBasics<'a>> Default for Label<'a, G> {
fn default() -> Self {
Label::None
}
}
impl<'a, G: GraphBasics<'a>> PartialEq for Label<'a, G> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Label::None, Label::None) => true,
(Label::Start, Label::Start) => true,
(Label::Vertex(v1), Label::Vertex(v2)) => v1 == v2,
(Label::Edge(e1, _), Label::Edge(e2, _)) => e1 == e2,
(Label::Flag(e1), Label::Flag(e2)) => e1 == e2,
_ => false,
}
}
}
pub fn maximum_matching<'a, G>(graph: &G) -> Matching<'a, G>
where
G: GraphProperties
{
assert_ne!(
graph.node_bound(),
std::usize::MAX,
"The input graph capacity should be strictly less than std::usize::MAX."
);
let (mut mate, mut n_edges) = greedy_matching_inner(graph);
mate.push(None);
let len = graph.node_bound() + 1;
debug_assert_eq!(mate.len(), len);
let mut label: Vec<Label<G>> = vec![Label::None; len];
let mut first_inner = vec![std::usize::MAX; len];
let visited = &mut graph.visit_map();
for start in 0..graph.node_bound() {
if mate[start].is_some() {
continue;
}
label[start] = Label::Start;
first_inner[start] = graph.dummy_idx();
graph.reset_map(visited);
let start = graph.from_index(start);
let mut queue = VecDeque::new();
queue.push_back(start);
visited.visit(start);
'search: while let Some(outer_vertex) = queue.pop_front() {
for edge in graph.incident_edges(outer_vertex) {
if edge.source() == edge.target() {
continue;
}
let other_vertex = edge.target();
let other_idx = graph.to_index(other_vertex);
if mate[other_idx].is_none() && other_vertex != start {
mate[other_idx] = Some(outer_vertex);
augment_path(graph, outer_vertex, other_vertex, &mut mate, &label);
n_edges += 1;
break 'search;
} else if label[other_idx].is_outer() {
find_join(
graph,
edge,
&mate,
&mut label,
&mut first_inner,
|labeled| {
if visited.visit(labeled) {
queue.push_back(labeled);
}
},
);
} else {
let mate_vertex = mate[other_idx];
let mate_idx = mate_vertex.map_or(graph.dummy_idx(), |id| graph.to_index(id));
if label[mate_idx].is_inner() {
label[mate_idx] = Label::Vertex(outer_vertex);
first_inner[mate_idx] = other_idx;
}
if let Some(mate_vertex) = mate_vertex {
if visited.visit(mate_vertex) {
queue.push_back(mate_vertex);
}
}
}
}
}
for lbl in label.iter_mut() {
*lbl = Label::None;
}
}
mate.pop();
Matching {
graph,
mate,
}
}
fn find_join<G, F>(
graph: &G,
edge: <G as GraphBasics<'_>>::EdgeRef,
mate: &[Option<<G as GraphBasics<'_>>::NodeIndex>],
label: &mut [Label<G>],
first_inner: &mut [usize],
mut visitor: F,
) where
G: GraphProperties,
F: FnMut(<G as GraphBasics<'_>>::NodeIndex),
{
let source = graph.to_index(edge.source());
let target = graph.to_index(edge.target());
let mut left = first_inner[source];
let mut right = first_inner[target];
if left == right {
return;
}
let flag = Label::Flag(edge.id());
label[left] = flag;
label[right] = flag;
let join = loop {
if right != graph.dummy_idx() {
std::mem::swap(&mut left, &mut right);
}
let left_mate = graph.to_index(mate[left].unwrap());
let next_inner = label[left_mate].to_vertex().unwrap();
left = first_inner[graph.to_index(next_inner)];
if !label[left].is_flagged(edge.id()) {
label[left] = flag;
} else {
break left;
}
};
for endpoint in [source, target].iter().copied() {
let mut inner = first_inner[endpoint];
while inner != join {
if let Some(ix) = graph.try_from_index(inner) {
visitor(ix);
}
label[inner] = Label::Edge(edge.id(), [edge.source(), edge.target()]);
first_inner[inner] = join;
let inner_mate = graph.to_index(mate[inner].unwrap());
let next_inner = label[inner_mate].to_vertex().unwrap();
inner = first_inner[graph.to_index(next_inner)];
}
}
for (vertex_idx, vertex_label) in label.iter().enumerate() {
if vertex_idx != graph.dummy_idx()
&& vertex_label.is_outer()
&& label[first_inner[vertex_idx]].is_outer()
{
first_inner[vertex_idx] = join;
}
}
}
fn augment_path<'a, G>(
graph: &'a G,
outer: G::NodeIndex,
other: G::NodeIndex,
mate: &mut [Option<G::NodeIndex>],
label: &[Label<'a, G>],
) where
G: GraphProperties<'a>,
{
let outer_idx = graph.to_index(outer);
let temp = mate[outer_idx];
let temp_idx = temp.map_or(graph.dummy_idx(), |id| graph.to_index(id));
mate[outer_idx] = Some(other);
if mate[temp_idx] != Some(outer) {
} else if let Label::Vertex(vertex) = label[outer_idx] {
mate[temp_idx] = Some(vertex);
if let Some(temp) = temp {
augment_path(graph, vertex, temp, mate, label);
}
} else if let Label::Edge(_, [source, target]) = label[outer_idx] {
augment_path(graph, source, target, mate, label);
augment_path(graph, target, source, mate, label);
} else {
panic!("Unexpected label when augmenting path");
}
}