use petgraph::graph::{EdgeIndex, Graph, NodeIndex};
use petgraph::visit::EdgeRef;
use petgraph::{Direction, EdgeType};
use petgraph::algo::subgraph_isomorphisms_iter;
#[must_use]
pub fn induced_matches<Np, Ep, Nh, Eh, Ty, NM, EM>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
mut node_match: NM,
mut edge_match: EM,
) -> Vec<Vec<usize>>
where
Ty: EdgeType,
NM: FnMut(&Np, &Nh) -> bool,
EM: FnMut(&Ep, &Eh) -> bool,
{
match subgraph_isomorphisms_iter(&pattern, &host, &mut node_match, &mut edge_match) {
Some(it) => it.collect(),
None => Vec::new(),
}
}
#[must_use]
pub fn induced_matches_unlabelled<Np, Ep, Nh, Eh, Ty>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
) -> Vec<Vec<usize>>
where
Ty: EdgeType,
{
induced_matches(pattern, host, |_, _| true, |_, _| true)
}
#[must_use]
pub fn count_induced_matches<Np, Ep, Nh, Eh, Ty>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
) -> usize
where
Ty: EdgeType,
{
match subgraph_isomorphisms_iter(&pattern, &host, &mut |_, _| true, &mut |_, _| true) {
Some(it) => it.count(),
None => 0,
}
}
enum Dir {
SelfLoop,
FromOther,
ToOther,
}
struct Cons {
other: usize,
dir: Dir,
edge: EdgeIndex,
}
struct Plan {
order: Vec<usize>,
cons: Vec<Vec<Cons>>,
pat_out: Vec<usize>,
pat_in: Vec<usize>,
}
fn plan<Np, Ep, Ty: EdgeType>(pattern: &Graph<Np, Ep, Ty>) -> Plan {
let pk = pattern.node_count();
let directed = Ty::is_directed();
let mut pnbr = vec![Vec::new(); pk];
let mut pat_out = vec![0usize; pk];
let mut pat_in = vec![0usize; pk];
for e in pattern.edge_references() {
let (si, ti) = (e.source().index(), e.target().index());
if si == ti {
continue; }
pnbr[si].push(ti);
pnbr[ti].push(si);
if directed {
pat_out[si] += 1;
pat_in[ti] += 1;
}
}
if !directed {
for v in 0..pk {
pat_out[v] = pnbr[v].len();
pat_in[v] = pnbr[v].len();
}
}
let mut order = Vec::with_capacity(pk);
let mut placed = vec![false; pk];
while order.len() < pk {
let mut best = None;
let mut best_score = (-1i64, -1i64);
for v in 0..pk {
if placed[v] {
continue;
}
let conn = pnbr[v].iter().filter(|&&u| placed[u]).count() as i64;
let deg = pnbr[v].len() as i64;
if (conn, deg) > best_score {
best_score = (conn, deg);
best = Some(v);
}
}
let v = best.expect("an unplaced vertex must exist while order is incomplete");
placed[v] = true;
order.push(v);
}
let mut pos_of = vec![0usize; pk];
for (p, &v) in order.iter().enumerate() {
pos_of[v] = p;
}
let mut cons: Vec<Vec<Cons>> = (0..pk).map(|_| Vec::new()).collect();
for e in pattern.edge_references() {
let (si, ti, ei) = (e.source().index(), e.target().index(), e.id());
if si == ti {
cons[pos_of[si]].push(Cons {
other: si,
dir: Dir::SelfLoop,
edge: ei,
});
continue;
}
let (pi, pj) = (pos_of[si], pos_of[ti]);
if pi < pj {
cons[pj].push(Cons {
other: si,
dir: Dir::FromOther,
edge: ei,
});
} else {
cons[pi].push(Cons {
other: ti,
dir: Dir::ToOther,
edge: ei,
});
}
}
Plan {
order,
cons,
pat_out,
pat_in,
}
}
struct Search<'a, Np, Ep, Nh, Eh, Ty, NM, EM> {
pattern: &'a Graph<Np, Ep, Ty>,
host: &'a Graph<Nh, Eh, Ty>,
plan: &'a Plan,
host_out: Vec<usize>,
host_in: Vec<usize>,
node_match: NM,
edge_match: EM,
assign: Vec<usize>,
used: Vec<bool>,
count_only: bool,
count: usize,
out: Vec<Vec<usize>>,
}
impl<Np, Ep, Nh, Eh, Ty, NM, EM> Search<'_, Np, Ep, Nh, Eh, Ty, NM, EM>
where
Ty: EdgeType,
NM: FnMut(&Np, &Nh) -> bool,
EM: FnMut(&Ep, &Eh) -> bool,
{
fn go(&mut self, pos: usize) {
let pk = self.plan.order.len();
if pos == pk {
self.count += 1;
if !self.count_only {
self.out.push(self.assign.clone());
}
return;
}
let pv = self.plan.order[pos];
let nh = self.host.node_count();
for hv in 0..nh {
if self.used[hv] {
continue;
}
if self.plan.pat_out[pv] > self.host_out[hv] || self.plan.pat_in[pv] > self.host_in[hv]
{
continue;
}
if !(self.node_match)(
&self.pattern[NodeIndex::new(pv)],
&self.host[NodeIndex::new(hv)],
) {
continue;
}
let mut ok = true;
for c in &self.plan.cons[pos] {
let ei = match c.dir {
Dir::SelfLoop => self.host.find_edge(NodeIndex::new(hv), NodeIndex::new(hv)),
Dir::FromOther => self
.host
.find_edge(NodeIndex::new(self.assign[c.other]), NodeIndex::new(hv)),
Dir::ToOther => self
.host
.find_edge(NodeIndex::new(hv), NodeIndex::new(self.assign[c.other])),
};
match ei {
None => {
ok = false;
break;
}
Some(hei) => {
if !(self.edge_match)(&self.pattern[c.edge], &self.host[hei]) {
ok = false;
break;
}
}
}
}
if !ok {
continue;
}
self.assign[pv] = hv;
self.used[hv] = true;
self.go(pos + 1);
self.used[hv] = false;
}
}
}
fn host_degrees<Nh, Eh, Ty: EdgeType>(host: &Graph<Nh, Eh, Ty>) -> (Vec<usize>, Vec<usize>) {
let nh = host.node_count();
let directed = Ty::is_directed();
let mut out = vec![0usize; nh];
let mut inc = vec![0usize; nh];
for h in 0..nh {
let v = NodeIndex::new(h);
if directed {
out[h] = host
.neighbors_directed(v, Direction::Outgoing)
.filter(|u| u.index() != h)
.count();
inc[h] = host
.neighbors_directed(v, Direction::Incoming)
.filter(|u| u.index() != h)
.count();
} else {
let d = host.neighbors(v).filter(|u| u.index() != h).count();
out[h] = d;
inc[h] = d;
}
}
(out, inc)
}
#[must_use]
pub fn monomorphisms<Np, Ep, Nh, Eh, Ty, NM, EM>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
node_match: NM,
edge_match: EM,
) -> Vec<Vec<usize>>
where
Ty: EdgeType,
NM: FnMut(&Np, &Nh) -> bool,
EM: FnMut(&Ep, &Eh) -> bool,
{
let plan = plan(pattern);
let (host_out, host_in) = host_degrees(host);
let pk = pattern.node_count();
let nh = host.node_count();
let mut search = Search {
pattern,
host,
plan: &plan,
host_out,
host_in,
node_match,
edge_match,
assign: vec![0usize; pk],
used: vec![false; nh],
count_only: false,
count: 0,
out: Vec::new(),
};
search.go(0);
search.out
}
#[must_use]
pub fn monomorphisms_unlabelled<Np, Ep, Nh, Eh, Ty>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
) -> Vec<Vec<usize>>
where
Ty: EdgeType,
{
monomorphisms(pattern, host, |_, _| true, |_, _| true)
}
#[must_use]
pub fn count_monomorphisms<Np, Ep, Nh, Eh, Ty, NM, EM>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
node_match: NM,
edge_match: EM,
) -> usize
where
Ty: EdgeType,
NM: FnMut(&Np, &Nh) -> bool,
EM: FnMut(&Ep, &Eh) -> bool,
{
let plan = plan(pattern);
let (host_out, host_in) = host_degrees(host);
let pk = pattern.node_count();
let nh = host.node_count();
let mut search = Search {
pattern,
host,
plan: &plan,
host_out,
host_in,
node_match,
edge_match,
assign: vec![0usize; pk],
used: vec![false; nh],
count_only: true,
count: 0,
out: Vec::new(),
};
search.go(0);
search.count
}
#[must_use]
pub fn count_monomorphisms_unlabelled<Np, Ep, Nh, Eh, Ty>(
pattern: &Graph<Np, Ep, Ty>,
host: &Graph<Nh, Eh, Ty>,
) -> usize
where
Ty: EdgeType,
{
count_monomorphisms(pattern, host, |_, _| true, |_, _| true)
}