use crate::arc::Arc;
use crate::fst::{Fst, StateId};
use crate::semiring::Semiring;
use std::collections::VecDeque;
#[derive(Debug, Clone, PartialEq)]
pub struct FstPath<W: Semiring> {
pub states: Vec<StateId>,
pub arcs: Vec<Arc<W>>,
pub weight: W,
pub final_state: StateId,
}
impl<W: Semiring> FstPath<W> {
pub fn input_labels(&self) -> Vec<u32> {
self.arcs.iter().map(|arc| arc.ilabel).collect()
}
pub fn output_labels(&self) -> Vec<u32> {
self.arcs.iter().map(|arc| arc.olabel).collect()
}
pub fn io_pairs(&self) -> Vec<(u32, u32)> {
self.arcs
.iter()
.map(|arc| (arc.ilabel, arc.olabel))
.collect()
}
}
#[derive(Debug)]
pub struct PathsIterator<'a, W: Semiring, F: Fst<W>> {
fst: &'a F,
queue: VecDeque<(Vec<StateId>, Vec<Arc<W>>, W, StateId)>,
max_paths: Option<usize>,
paths_found: usize,
weight_threshold: Option<W>,
}
impl<'a, W: Semiring, F: Fst<W>> PathsIterator<'a, W, F> {
pub fn new(fst: &'a F) -> Self {
let mut queue = VecDeque::new();
if let Some(start) = fst.start() {
queue.push_back((vec![start], Vec::new(), W::one(), start));
}
Self {
fst,
queue,
max_paths: None,
paths_found: 0,
weight_threshold: None,
}
}
pub fn with_max_paths(mut self, max: usize) -> Self {
self.max_paths = Some(max);
self
}
pub fn with_weight_threshold(mut self, threshold: W) -> Self
where
W: crate::semiring::NaturallyOrderedSemiring,
{
self.weight_threshold = Some(threshold);
self
}
}
impl<'a, W: Semiring, F: Fst<W>> Iterator for PathsIterator<'a, W, F>
where
W: Clone,
{
type Item = FstPath<W>;
fn next(&mut self) -> Option<Self::Item> {
while let Some((states, arcs, weight, current_state)) = self.queue.pop_front() {
if let Some(max) = self.max_paths {
if self.paths_found >= max {
return None;
}
}
if let Some(ref threshold) = self.weight_threshold {
if weight > *threshold {
continue;
}
}
if let Some(final_weight) = self.fst.final_weight(current_state) {
let total_weight = weight.clone() * final_weight.clone();
if let Some(ref threshold) = self.weight_threshold {
if total_weight > *threshold {
} else {
self.paths_found += 1;
return Some(FstPath {
states: states.clone(),
arcs: arcs.clone(),
weight: total_weight,
final_state: current_state,
});
}
} else {
self.paths_found += 1;
return Some(FstPath {
states: states.clone(),
arcs: arcs.clone(),
weight: total_weight,
final_state: current_state,
});
}
}
for arc in self.fst.arcs(current_state) {
if states.contains(&arc.nextstate) {
continue;
}
let next_weight = weight.clone() * arc.weight.clone();
let mut next_states = states.clone();
next_states.push(arc.nextstate);
let mut next_arcs = arcs.clone();
next_arcs.push(arc.clone());
self.queue
.push_back((next_states, next_arcs, next_weight, arc.nextstate));
}
}
None
}
}
#[derive(Debug)]
pub struct StringPathsIterator<'a, W: Semiring, F: Fst<W>> {
path_iter: PathsIterator<'a, W, F>,
input_symbols: Option<&'a crate::utils::SymbolTable>,
output_symbols: Option<&'a crate::utils::SymbolTable>,
}
impl<'a, W: Semiring, F: Fst<W>> StringPathsIterator<'a, W, F> {
pub fn new(
fst: &'a F,
input_symbols: Option<&'a crate::utils::SymbolTable>,
output_symbols: Option<&'a crate::utils::SymbolTable>,
) -> Self {
Self {
path_iter: PathsIterator::new(fst),
input_symbols,
output_symbols,
}
}
pub fn with_max_paths(mut self, max: usize) -> Self {
self.path_iter = self.path_iter.with_max_paths(max);
self
}
}
#[derive(Debug, Clone)]
pub struct StringPath {
pub input: String,
pub output: String,
pub weight: String,
}
impl<'a, W: Semiring, F: Fst<W>> Iterator for StringPathsIterator<'a, W, F>
where
W: Clone + std::fmt::Display,
{
type Item = StringPath;
fn next(&mut self) -> Option<Self::Item> {
let path = self.path_iter.next()?;
let input = if let Some(symbols) = self.input_symbols {
let labels: Vec<&str> = path
.input_labels()
.iter()
.filter_map(|&label| symbols.find(label))
.collect();
labels.join(" ")
} else {
let labels: Vec<String> = path.input_labels().iter().map(|&l| l.to_string()).collect();
labels.join(" ")
};
let output = if let Some(symbols) = self.output_symbols {
let labels: Vec<&str> = path
.output_labels()
.iter()
.filter_map(|&label| symbols.find(label))
.collect();
labels.join(" ")
} else {
let labels: Vec<String> = path
.output_labels()
.iter()
.map(|&l| l.to_string())
.collect();
labels.join(" ")
};
Some(StringPath {
input,
output,
weight: path.weight.to_string(),
})
}
}
pub trait PathIterExt<W: Semiring>: crate::fst::Fst<W> {
fn paths_iter(&self) -> PathsIterator<'_, W, Self>
where
Self: Sized;
fn string_paths_iter<'a>(
&'a self,
input_symbols: Option<&'a crate::utils::SymbolTable>,
output_symbols: Option<&'a crate::utils::SymbolTable>,
) -> StringPathsIterator<'a, W, Self>
where
Self: Sized;
}
impl<W: Semiring, F: Fst<W>> PathIterExt<W> for F {
fn paths_iter(&self) -> PathsIterator<'_, W, Self> {
PathsIterator::new(self)
}
fn string_paths_iter<'a>(
&'a self,
input_symbols: Option<&'a crate::utils::SymbolTable>,
output_symbols: Option<&'a crate::utils::SymbolTable>,
) -> StringPathsIterator<'a, W, Self> {
StringPathsIterator::new(self, input_symbols, output_symbols)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn test_fst_path_input_labels() {
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, 10, TropicalWeight::one(), s1));
let path = fst.paths_iter().next().unwrap();
assert_eq!(path.input_labels(), vec![1]);
}
#[test]
fn test_fst_path_output_labels() {
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, 10, TropicalWeight::one(), s1));
let path = fst.paths_iter().next().unwrap();
assert_eq!(path.output_labels(), vec![10]);
}
#[test]
fn test_fst_path_io_pairs() {
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, 10, TropicalWeight::one(), s1));
let path = fst.paths_iter().next().unwrap();
assert_eq!(path.io_pairs(), vec![(1, 10)]);
}
#[test]
fn test_paths_iterator_empty() {
let fst = VectorFst::<TropicalWeight>::new();
let mut iter = fst.paths_iter();
assert!(iter.next().is_none());
}
#[test]
fn test_paths_iterator_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::one());
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::one(), s1));
let paths: Vec<_> = fst.paths_iter().collect();
assert_eq!(paths.len(), 1);
}
#[test]
fn test_string_paths_iterator() {
let mut fst = VectorFst::<TropicalWeight>::new();
let mut symbols = SymbolTable::new();
let hello_id = symbols.add_symbol("hello");
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");
}
}