blob: 637bc553d5e09f20bdb18db8c497089c22800e33 [file] [log] [blame]
use alloc::{vec, vec::Vec};
use petgraph_core::{
deprecated::visit::{
GetAdjacencyMatrix, GraphProp, IntoNeighborsDirected, NodeCompactIndexable,
},
edge::Direction,
};
#[derive(Debug)]
// TODO: make mapping generic over the index type of the other graph.
pub struct Vf2State<'a, G: GetAdjacencyMatrix> {
/// A reference to the graph this state was built from.
pub graph: &'a G,
/// The current mapping M(s) of nodes from G0 → G1 and G1 → G0,
/// `usize::MAX` for no mapping.
pub mapping: Vec<usize>,
/// out[i] is non-zero if i is in either M_0(s) or Tout_0(s)
/// These are all the next vertices that are not mapped yet, but
/// have an outgoing edge from the mapping.
out: Vec<usize>,
/// ins[i] is non-zero if i is in either M_0(s) or Tin_0(s)
/// These are all the incoming vertices, those not mapped yet, but
/// have an edge from them into the mapping.
/// Unused if graph is undirected -- it's identical with out in that case.
ins: Vec<usize>,
pub out_size: usize,
pub ins_size: usize,
pub adjacency_matrix: G::AdjMatrix,
generation: usize,
}
impl<'a, G> Vf2State<'a, G>
where
G: GetAdjacencyMatrix + GraphProp + NodeCompactIndexable + IntoNeighborsDirected,
{
pub fn new(g: &'a G) -> Self {
let c0 = g.node_count();
Vf2State {
graph: g,
mapping: vec![usize::MAX; c0],
out: vec![0; c0],
ins: vec![0; c0 * (g.is_directed() as usize)],
out_size: 0,
ins_size: 0,
adjacency_matrix: g.adjacency_matrix(),
generation: 0,
}
}
/// Return **true** if we have a complete mapping
pub fn is_complete(&self) -> bool {
self.generation == self.mapping.len()
}
/// Add mapping **from** <-> **to** to the state.
pub fn push_mapping(&mut self, from: G::NodeId, to: usize) {
self.generation += 1;
self.mapping[self.graph.to_index(from)] = to;
// update T0 & T1 ins/outs
// T0out: Node in G0 not in M0 but successor of a node in M0.
// st.out[0]: Node either in M0 or successor of M0
for ix in self.graph.neighbors_directed(from, Direction::Outgoing) {
if self.out[self.graph.to_index(ix)] == 0 {
self.out[self.graph.to_index(ix)] = self.generation;
self.out_size += 1;
}
}
if self.graph.is_directed() {
for ix in self.graph.neighbors_directed(from, Direction::Incoming) {
if self.ins[self.graph.to_index(ix)] == 0 {
self.ins[self.graph.to_index(ix)] = self.generation;
self.ins_size += 1;
}
}
}
}
/// Restore the state to before the last added mapping
pub fn pop_mapping(&mut self, from: G::NodeId) {
// undo (n, m) mapping
self.mapping[self.graph.to_index(from)] = usize::MAX;
// unmark in ins and outs
for ix in self.graph.neighbors_directed(from, Direction::Outgoing) {
if self.out[self.graph.to_index(ix)] == self.generation {
self.out[self.graph.to_index(ix)] = 0;
self.out_size -= 1;
}
}
if self.graph.is_directed() {
for ix in self.graph.neighbors_directed(from, Direction::Incoming) {
if self.ins[self.graph.to_index(ix)] == self.generation {
self.ins[self.graph.to_index(ix)] = 0;
self.ins_size -= 1;
}
}
}
self.generation -= 1;
}
/// Find the next (least) node in the Tout set.
pub fn next_out_index(&self, from_index: usize) -> Option<usize> {
self.out[from_index..]
.iter()
.enumerate()
.find(move |&(index, &elt)| elt > 0 && self.mapping[from_index + index] == usize::MAX)
.map(|(index, _)| index)
}
/// Find the next (least) node in the Tin set.
pub fn next_in_index(&self, from_index: usize) -> Option<usize> {
if !self.graph.is_directed() {
return None;
}
self.ins[from_index..]
.iter()
.enumerate()
.find(move |&(index, &elt)| elt > 0 && self.mapping[from_index + index] == usize::MAX)
.map(|(index, _)| index)
}
/// Find the next (least) node in the N - M set.
pub fn next_rest_index(&self, from_index: usize) -> Option<usize> {
self.mapping[from_index..]
.iter()
.enumerate()
.find(|&(_, &elt)| elt == usize::MAX)
.map(|(index, _)| index)
}
}