arcweight 0.3.0

A high-performance, modular library for weighted finite state transducers with comprehensive examples and benchmarks
Documentation
//! Tests for path iteration utilities

use arcweight::prelude::*;
use arcweight::utils::PathIterExt;

#[test]
fn test_paths_iter_empty_fst() {
    let fst = VectorFst::<TropicalWeight>::new();
    let mut iter = fst.paths_iter();
    assert!(iter.next().is_none());
}

#[test]
fn test_paths_iter_single_path() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::new(0.5));
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 1);
    assert_eq!(paths[0].states, vec![0, 1]);
    assert_eq!(paths[0].arcs.len(), 1);
    assert_eq!(paths[0].input_labels(), vec![1]);
    assert_eq!(paths[0].output_labels(), vec![1]);
}

#[test]
fn test_paths_iter_multiple_paths() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    let s2 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::new(0.5));
    fst.set_final(s2, TropicalWeight::new(0.3));
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
    fst.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(2.0), s2));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 2);
}

#[test]
fn test_paths_iter_with_cycles() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::new(0.5));
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
    fst.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(0.5), s1)); // cycle

    let paths: Vec<_> = fst.paths_iter().with_max_paths(10).collect();
    assert!(!paths.is_empty());
    assert!(paths.len() <= 10);
}

#[test]
fn test_paths_iter_weight_threshold() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    let s2 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::new(0.5));
    fst.set_final(s2, TropicalWeight::new(10.0)); // High weight
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
    fst.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(1.0), s2));

    let threshold = TropicalWeight::new(5.0);
    let paths: Vec<_> = fst.paths_iter().with_weight_threshold(threshold).collect();
    // Should only include path to s1 (weight 1.5) not s2 (weight 11.0)
    assert_eq!(paths.len(), 1);
    assert_eq!(paths[0].final_state, s1);
}

#[test]
fn test_string_paths_iter() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let mut symbols = SymbolTable::new();
    let hello_id = symbols.add_symbol("hello");
    let _world_id = symbols.add_symbol("world");

    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::one());
    fst.add_arc(s0, Arc::new(hello_id, hello_id, TropicalWeight::one(), s1));

    let paths: Vec<_> = fst
        .string_paths_iter(Some(&symbols), Some(&symbols))
        .collect();
    assert_eq!(paths.len(), 1);
    assert_eq!(paths[0].input, "hello");
    assert_eq!(paths[0].output, "hello");
}

#[test]
fn test_string_paths_iter_no_symbols() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::one());
    fst.add_arc(s0, Arc::new(1, 2, TropicalWeight::one(), s1));

    let paths: Vec<_> = fst.string_paths_iter(None, None).collect();
    assert_eq!(paths.len(), 1);
    assert_eq!(paths[0].input, "1");
    assert_eq!(paths[0].output, "2");
}

#[test]
fn test_fst_path_io_pairs() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    let s2 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s2, TropicalWeight::one());
    fst.add_arc(s0, Arc::new(1, 10, TropicalWeight::one(), s1));
    fst.add_arc(s1, Arc::new(2, 20, TropicalWeight::one(), s2));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 1);
    let pairs = paths[0].io_pairs();
    assert_eq!(pairs, vec![(1, 10), (2, 20)]);
}

#[test]
fn test_paths_iter_complex_fst() {
    // Create a more complex FST with multiple paths
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    let s2 = fst.add_state();
    let s3 = fst.add_state();

    fst.set_start(s0);
    fst.set_final(s2, TropicalWeight::new(0.5));
    fst.set_final(s3, TropicalWeight::new(0.3));

    // Path 1: s0 -> s1 -> s2
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
    fst.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(1.0), s2));

    // Path 2: s0 -> s3
    fst.add_arc(s0, Arc::new(3, 3, TropicalWeight::new(2.0), s3));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 2);

    // Verify path structures
    let path1 = paths.iter().find(|p| p.final_state == s2).unwrap();
    assert_eq!(path1.states.len(), 3);
    assert_eq!(path1.arcs.len(), 2);

    let path2 = paths.iter().find(|p| p.final_state == s3).unwrap();
    assert_eq!(path2.states.len(), 2);
    assert_eq!(path2.arcs.len(), 1);
}

#[test]
fn test_paths_iter_epsilon_transitions() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    let s2 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s2, TropicalWeight::one());
    fst.add_arc(s0, Arc::new(0, 0, TropicalWeight::one(), s1)); // epsilon
    fst.add_arc(s1, Arc::new(1, 1, TropicalWeight::one(), s2));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 1);
    assert_eq!(paths[0].input_labels(), vec![0, 1]);
}

#[test]
fn test_paths_iter_max_paths_limit() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    fst.set_start(s0);

    // Create multiple final states
    for i in 0..20 {
        let s = fst.add_state();
        fst.set_final(s, TropicalWeight::one());
        fst.add_arc(s0, Arc::new(i, i, TropicalWeight::one(), s));
    }

    let paths: Vec<_> = fst.paths_iter().with_max_paths(5).collect();
    assert_eq!(paths.len(), 5);
}

#[test]
fn test_paths_iter_no_start_state() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    fst.set_final(s0, TropicalWeight::one());
    // No start state set

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 0);
}

#[test]
fn test_paths_iter_no_final_states() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    // No final states
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::one(), s1));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 0);
}

#[test]
fn test_fst_path_clone() {
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, TropicalWeight::one());
    fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::one(), s1));

    let path = fst.paths_iter().next().unwrap();
    let cloned = path.clone();
    assert_eq!(path.states, cloned.states);
    assert_eq!(path.arcs, cloned.arcs);
    assert_eq!(path.weight, cloned.weight);
}

#[test]
fn test_paths_iter_different_semirings() {
    // Test with LogWeight
    let mut fst = VectorFst::<LogWeight>::new();
    let s0 = fst.add_state();
    let s1 = fst.add_state();
    fst.set_start(s0);
    fst.set_final(s1, LogWeight::new(-0.693)); // log(0.5)
    fst.add_arc(s0, Arc::new(1, 1, LogWeight::new(-0.693), s1));

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 1);
}

#[test]
fn test_paths_iter_large_fst() {
    // Create a larger FST to test performance
    let mut fst = VectorFst::<TropicalWeight>::new();
    let s0 = fst.add_state();
    fst.set_start(s0);

    // Create a tree structure
    for i in 0..100 {
        let s = fst.add_state();
        fst.set_final(s, TropicalWeight::one());
        fst.add_arc(s0, Arc::new(i, i, TropicalWeight::one(), s));
    }

    let paths: Vec<_> = fst.paths_iter().collect();
    assert_eq!(paths.len(), 100);
}