use crate::fst::{Fst, StateId};
use crate::semiring::Semiring;
use crate::Result;
use std::collections::{HashMap, VecDeque};
pub fn isomorphic<W, F1, F2>(fst1: &F1, fst2: &F2) -> Result<bool>
where
W: Semiring + PartialEq,
F1: Fst<W>,
F2: Fst<W>,
{
if fst1.num_states() != fst2.num_states() {
return Ok(false);
}
if fst1.num_arcs_total() != fst2.num_arcs_total() {
return Ok(false);
}
let (start1, start2) = match (fst1.start(), fst2.start()) {
(Some(s1), Some(s2)) => (s1, s2),
(None, None) => {
return Ok(fst1.num_states() == 0 && fst2.num_states() == 0);
}
_ => {
return Ok(false);
}
};
if fst1.num_states() == 0 && fst2.num_states() == 0 {
return Ok(true);
}
let mut state_mapping: HashMap<StateId, StateId> = HashMap::new();
let mut queue: VecDeque<(StateId, StateId)> = VecDeque::new();
state_mapping.insert(start1, start2);
queue.push_back((start1, start2));
while let Some((s1, s2)) = queue.pop_front() {
let final1 = fst1.final_weight(s1);
let final2 = fst2.final_weight(s2);
match (final1, final2) {
(Some(w1), Some(w2)) => {
if w1 != w2 {
return Ok(false);
}
}
(None, None) => {
}
_ => {
return Ok(false);
}
}
if fst1.num_arcs(s1) != fst2.num_arcs(s2) {
return Ok(false);
}
let mut arcs1: Vec<_> = fst1.arcs(s1).collect();
let mut arcs2: Vec<_> = fst2.arcs(s2).collect();
arcs1.sort_by(|a, b| {
(a.ilabel, a.olabel, &a.weight, a.nextstate)
.partial_cmp(&(b.ilabel, b.olabel, &b.weight, b.nextstate))
.unwrap_or(std::cmp::Ordering::Equal)
});
arcs2.sort_by(|a, b| {
(a.ilabel, a.olabel, &a.weight, a.nextstate)
.partial_cmp(&(b.ilabel, b.olabel, &b.weight, b.nextstate))
.unwrap_or(std::cmp::Ordering::Equal)
});
for (arc1, arc2) in arcs1.iter().zip(arcs2.iter()) {
if arc1.ilabel != arc2.ilabel || arc1.olabel != arc2.olabel {
return Ok(false);
}
if arc1.weight != arc2.weight {
return Ok(false);
}
if let Some(&mapped_dest) = state_mapping.get(&arc1.nextstate) {
if mapped_dest != arc2.nextstate {
return Ok(false);
}
} else {
state_mapping.insert(arc1.nextstate, arc2.nextstate);
queue.push_back((arc1.nextstate, arc2.nextstate));
}
}
}
Ok(state_mapping.len() == fst1.num_states())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arc::Arc;
use crate::fst::{MutableFst, VectorFst};
use crate::semiring::{BooleanWeight, IntegerWeight, TropicalWeight};
use num_traits::One;
#[test]
fn test_isomorphic_identical_fsts() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::new(1.0));
fst1.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.5), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::new(1.0));
fst2.add_arc(t0, Arc::new(1, 2, TropicalWeight::new(0.5), t1));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_with_renumbered_states() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
let s2 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s2, TropicalWeight::new(2.0));
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.5), s1));
fst1.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(0.3), s2));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
let t2 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t2, TropicalWeight::new(2.0));
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.5), t1));
fst2.add_arc(t1, Arc::new(2, 2, TropicalWeight::new(0.3), t2));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_different_weights() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::new(1.0));
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.5), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::new(2.0)); fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.5), t1));
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_different_labels() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::new(1.0));
fst1.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.5), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::new(1.0));
fst2.add_arc(t0, Arc::new(1, 3, TropicalWeight::new(0.5), t1));
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_different_structure() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::new(1.0));
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.5), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
let t2 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t2, TropicalWeight::new(1.0));
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.5), t1));
fst2.add_arc(t1, Arc::new(2, 2, TropicalWeight::new(0.3), t2));
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_different_state_count() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s0, TropicalWeight::one());
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_different_arc_count() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::one(), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::one(), t1));
fst2.add_arc(t0, Arc::new(2, 2, TropicalWeight::one(), t1));
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_empty_fsts() {
let fst1 = VectorFst::<TropicalWeight>::new();
let fst2 = VectorFst::<TropicalWeight>::new();
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_single_state() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s0, TropicalWeight::new(1.5));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t0, TropicalWeight::new(1.5));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_linear_chain() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
let s2 = fst1.add_state();
let s3 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s3, TropicalWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.1), s1));
fst1.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(0.2), s2));
fst1.add_arc(s2, Arc::new(3, 3, TropicalWeight::new(0.3), s3));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
let t2 = fst2.add_state();
let t3 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t3, TropicalWeight::one());
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.1), t1));
fst2.add_arc(t1, Arc::new(2, 2, TropicalWeight::new(0.2), t2));
fst2.add_arc(t2, Arc::new(3, 3, TropicalWeight::new(0.3), t3));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_branching_structure() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
let s2 = fst1.add_state();
let s3 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.set_final(s2, TropicalWeight::new(2.0));
fst1.set_final(s3, TropicalWeight::new(3.0));
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.1), s1));
fst1.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(0.2), s2));
fst1.add_arc(s0, Arc::new(3, 3, TropicalWeight::new(0.3), s3));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
let t2 = fst2.add_state();
let t3 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
fst2.set_final(t2, TropicalWeight::new(2.0));
fst2.set_final(t3, TropicalWeight::new(3.0));
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.1), t1));
fst2.add_arc(t0, Arc::new(2, 2, TropicalWeight::new(0.2), t2));
fst2.add_arc(t0, Arc::new(3, 3, TropicalWeight::new(0.3), t3));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_with_cycles() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.5), s1));
fst1.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(0.3), s0));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.5), t1));
fst2.add_arc(t1, Arc::new(2, 2, TropicalWeight::new(0.3), t0));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_different_semirings() {
let mut fst1 = VectorFst::<BooleanWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, BooleanWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, BooleanWeight::one(), s1));
let mut fst2 = VectorFst::<BooleanWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, BooleanWeight::one());
fst2.add_arc(t0, Arc::new(1, 1, BooleanWeight::one(), t1));
assert!(isomorphic(&fst1, &fst2).unwrap());
let mut fst3 = VectorFst::<IntegerWeight>::new();
let s0 = fst3.add_state();
let s1 = fst3.add_state();
fst3.set_start(s0);
fst3.set_final(s1, IntegerWeight::new(5));
fst3.add_arc(s0, Arc::new(1, 1, IntegerWeight::new(3), s1));
let mut fst4 = VectorFst::<IntegerWeight>::new();
let t0 = fst4.add_state();
let t1 = fst4.add_state();
fst4.set_start(t0);
fst4.set_final(t1, IntegerWeight::new(5));
fst4.add_arc(t0, Arc::new(1, 1, IntegerWeight::new(3), t1));
assert!(isomorphic(&fst3, &fst4).unwrap());
}
#[test]
fn test_isomorphic_epsilon_arcs() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.add_arc(s0, Arc::epsilon(TropicalWeight::new(0.5), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
fst2.add_arc(t0, Arc::epsilon(TropicalWeight::new(0.5), t1));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_missing_start_state() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s0, TropicalWeight::one());
let mut fst2 = VectorFst::<TropicalWeight>::new();
let _t0 = fst2.add_state();
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_isomorphic_multiple_arcs_same_state() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.5), s1));
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(0.5), s1)); fst1.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(0.3), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.set_final(t1, TropicalWeight::one());
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.5), t1));
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::new(0.5), t1)); fst2.add_arc(t0, Arc::new(2, 2, TropicalWeight::new(0.3), t1));
assert!(isomorphic(&fst1, &fst2).unwrap());
}
#[test]
fn test_non_isomorphic_one_final_one_not() {
let mut fst1 = VectorFst::<TropicalWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, TropicalWeight::one());
fst1.add_arc(s0, Arc::new(1, 1, TropicalWeight::one(), s1));
let mut fst2 = VectorFst::<TropicalWeight>::new();
let t0 = fst2.add_state();
let t1 = fst2.add_state();
fst2.set_start(t0);
fst2.add_arc(t0, Arc::new(1, 1, TropicalWeight::one(), t1));
assert!(!isomorphic(&fst1, &fst2).unwrap());
}
}