use crate::algorithms::dfs_visit::{DfsVisitor, dfs_visit_any};
use crate::algorithms::state_sort::state_sort;
use crate::arc::{Arc, ArcStateId};
use crate::error::OpenFstError;
use crate::fst::{Fst, MutableFst};
use crate::properties::{K_ACYCLIC, K_CYCLIC, K_INITIAL_ACYCLIC, K_NOT_TOP_SORTED, K_TOP_SORTED};
pub struct TopOrderVisitor<A: Arc> {
finish: Vec<A::StateId>,
acyclic: bool,
}
impl<A: Arc> Default for TopOrderVisitor<A> {
fn default() -> Self {
Self::new()
}
}
impl<A: Arc> TopOrderVisitor<A> {
pub fn new() -> Self {
Self {
finish: Vec::new(),
acyclic: true,
}
}
pub fn acyclic(&self) -> bool {
self.acyclic
}
pub fn order(&self) -> Option<Vec<A::StateId>> {
if !self.acyclic {
return None;
}
let mut order = vec![A::StateId::no_state(); self.finish.len()];
for (position, state) in self.finish.iter().rev().enumerate() {
order[state.as_usize()] = A::StateId::from_usize(position);
}
Some(order)
}
}
impl<A: Arc> DfsVisitor<A> for TopOrderVisitor<A> {
fn init_visit<F: Fst<A>>(&mut self, _fst: &F) {
self.finish.clear();
self.acyclic = true;
}
#[inline]
fn init_state(&mut self, _s: A::StateId, _root: A::StateId) -> bool {
true
}
#[inline]
fn tree_arc(&mut self, _s: A::StateId, _arc: &A) -> bool {
true
}
fn back_arc(&mut self, _s: A::StateId, _arc: &A) -> bool {
self.acyclic = false;
false
}
#[inline]
fn forward_or_cross_arc(&mut self, _s: A::StateId, _arc: &A) -> bool {
true
}
#[inline]
fn finish_state(&mut self, s: A::StateId, _parent: Option<A::StateId>, _arc: Option<&A>) {
self.finish.push(s);
}
fn finish_visit(&mut self) {}
}
pub fn top_sort<A: Arc, F: MutableFst<A>>(fst: &mut F) -> Result<bool, OpenFstError> {
let mut visitor = TopOrderVisitor::<A>::new();
dfs_visit_any(&*fst, &mut visitor);
match visitor.order() {
Some(order) if order.len() != fst.num_states() => Ok(true),
Some(order) => {
state_sort(fst, &order)?;
fst.set_properties(
K_ACYCLIC | K_INITIAL_ACYCLIC | K_TOP_SORTED,
K_ACYCLIC | K_INITIAL_ACYCLIC | K_TOP_SORTED,
);
Ok(true)
}
None => {
fst.set_properties(K_CYCLIC | K_NOT_TOP_SORTED, K_CYCLIC | K_NOT_TOP_SORTED);
Ok(false)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algorithms::test_support::{Rng, random_acyclic_fst};
use crate::arc::StdArc;
use crate::fst::ExpandedFst as _;
use crate::fsts::vector_fst::StdVectorFst;
use crate::properties::{K_ERROR, K_FST_PROPERTIES};
use crate::weight::Weight;
use crate::weights::float_weight::TropicalWeight;
fn build(nstates: usize, edges: &[(i32, i32)], start: i32) -> StdVectorFst {
let mut fst = StdVectorFst::new();
for _ in 0..nstates {
fst.add_state();
}
fst.set_start(start);
for &(from, to) in edges {
fst.add_arc(from, StdArc::new(1, 1, TropicalWeight::one(), to));
}
fst
}
#[test]
fn every_arc_goes_forwards_afterwards() {
let mut rng = Rng::new(0x7095_0077);
for round in 0..200 {
let mut fst = random_acyclic_fst(&mut rng, 6);
let nstates = fst.num_states();
let mut order: Vec<i32> = (0..nstates as i32).collect();
for i in (1..nstates).rev() {
order.swap(i, rng.below(i + 1));
}
crate::algorithms::state_sort::state_sort(&mut fst, &order).unwrap();
assert!(top_sort(&mut fst).unwrap(), "round {round}");
for s in 0..fst.num_states() as i32 {
for arc in fst.arcs(s) {
assert!(
arc.nextstate() > s,
"round {round}: {s} -> {}",
arc.nextstate()
);
}
}
}
}
#[test]
fn a_cyclic_fst_is_left_alone() {
let mut fst = build(3, &[(0, 1), (1, 2), (2, 1)], 0);
fst.set_final(2, TropicalWeight::one());
let before: Vec<Vec<StdArc>> = (0..3).map(|s| fst.arcs(s).collect()).collect();
assert!(!top_sort(&mut fst).unwrap());
for s in 0..3 {
assert_eq!(fst.arcs(s).collect::<Vec<_>>(), before[s as usize]);
}
let props = fst.properties(K_FST_PROPERTIES, false);
assert_ne!(props & K_CYCLIC, 0);
assert_ne!(props & K_NOT_TOP_SORTED, 0);
assert_eq!(props & K_TOP_SORTED, 0);
}
#[test]
fn a_self_loop_is_a_cycle() {
let mut fst = build(2, &[(0, 1), (1, 1)], 0);
fst.set_final(1, TropicalWeight::one());
assert!(!top_sort(&mut fst).unwrap());
}
#[test]
fn sorting_preserves_the_states_and_their_arcs() {
let mut fst = build(4, &[(0, 2), (2, 1), (1, 3), (0, 3)], 0);
fst.set_final(3, TropicalWeight(2.5));
assert!(top_sort(&mut fst).unwrap());
assert_eq!(fst.num_states(), 4);
assert_eq!(fst.start(), Some(0));
assert_eq!(fst.final_weight(3), TropicalWeight(2.5));
assert_eq!(fst.count_arcs(), 4);
let props = fst.properties(K_FST_PROPERTIES, false);
assert_ne!(props & K_TOP_SORTED, 0);
assert_ne!(props & K_ACYCLIC, 0);
}
#[test]
fn an_fst_with_no_start_state_is_left_alone_and_nothing_is_claimed() {
let mut fst = StdVectorFst::new();
fst.add_state();
fst.add_state();
fst.add_arc(0, StdArc::new(1, 1, TropicalWeight::one(), 1));
fst.add_arc(1, StdArc::new(1, 1, TropicalWeight::one(), 0));
let before = fst.properties(K_FST_PROPERTIES, false);
assert!(top_sort(&mut fst).unwrap());
assert_eq!(fst.num_states(), 2);
assert_eq!(fst.count_arcs(), 2);
assert_eq!(
fst.properties(K_FST_PROPERTIES, false),
before,
"no property was touched"
);
assert_eq!(fst.properties(K_ERROR, false) & K_ERROR, 0);
assert_eq!(fst.properties(K_TOP_SORTED, false) & K_TOP_SORTED, 0);
assert_eq!(fst.properties(K_ACYCLIC, false) & K_ACYCLIC, 0);
}
#[test]
fn no_order_is_reported_for_a_cyclic_fst() {
let fst = build(2, &[(0, 1), (1, 0)], 0);
let mut visitor = TopOrderVisitor::<StdArc>::new();
dfs_visit_any(&fst, &mut visitor);
assert!(!visitor.acyclic());
assert!(visitor.order().is_none());
}
}