use crate::algorithms::arc_map::MapSymbolsAction;
use crate::arc::{Arc, ArcStateId};
use crate::fst::{Fst, MutableFst};
use crate::properties::{
K_ARC_SORT_PROPERTIES, K_COPY_PROPERTIES, K_DELETE_ARCS_PROPERTIES, K_FST_PROPERTIES,
K_WEIGHT_INVARIANT_PROPERTIES,
};
use crate::weight::Weight;
pub trait StateMapper<From: Arc, To: Arc> {
fn start(&self) -> Option<To::StateId>;
fn final_weight(&self, state: From::StateId) -> To::Weight;
fn arcs(&mut self, state: From::StateId) -> Vec<To>;
fn input_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Copy
}
fn output_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Copy
}
fn properties(&self, props: u64) -> u64;
}
pub fn state_map<A, F, M>(fst: &mut F, mapper: &mut M)
where
A: Arc,
F: MutableFst<A>,
M: StateMapper<A, A>,
{
if mapper.input_symbols_action() == MapSymbolsAction::Clear {
fst.set_input_symbols(None);
}
if mapper.output_symbols_action() == MapSymbolsAction::Clear {
fst.set_output_symbols(None);
}
if fst.start().is_none() {
return;
}
let props = fst.properties(K_FST_PROPERTIES, false);
let states: Vec<A::StateId> = fst.states().collect();
let replacements: Vec<(Vec<A>, A::Weight)> = states
.iter()
.map(|&state| (mapper.arcs(state), mapper.final_weight(state)))
.collect();
if let Some(start) = mapper.start() {
fst.set_start(start);
}
for (&state, (arcs, weight)) in states.iter().zip(replacements) {
fst.delete_arcs(state);
for arc in arcs {
fst.add_arc(state, arc);
}
fst.set_final(state, weight);
}
fst.set_properties(mapper.properties(props), K_FST_PROPERTIES);
}
pub fn state_map_to<From, To, F1, F2, M>(ifst: &F1, ofst: &mut F2, mapper: &mut M)
where
From: Arc,
To: Arc<StateId = From::StateId>,
F1: Fst<From>,
F2: MutableFst<To>,
M: StateMapper<From, To>,
{
ofst.delete_all_states();
match mapper.input_symbols_action() {
MapSymbolsAction::Copy => ofst.set_input_symbols(ifst.input_symbols()),
MapSymbolsAction::Clear => ofst.set_input_symbols(None),
MapSymbolsAction::Noop => {}
}
match mapper.output_symbols_action() {
MapSymbolsAction::Copy => ofst.set_output_symbols(ifst.output_symbols()),
MapSymbolsAction::Clear => ofst.set_output_symbols(None),
MapSymbolsAction::Noop => {}
}
let iprops = ifst.properties(K_COPY_PROPERTIES, false);
if ifst.start().is_none() {
return;
}
if let Some(num_states) = ifst.num_states_if_known() {
ofst.reserve_states(num_states);
}
for _ in ifst.states() {
ofst.add_state();
}
if let Some(start) = mapper.start() {
ofst.set_start(start);
}
for state in ifst.states() {
for arc in mapper.arcs(state) {
ofst.add_arc(state, arc);
}
ofst.set_final(state, mapper.final_weight(state));
}
let oprops = ofst.properties(K_FST_PROPERTIES, false);
ofst.set_properties(mapper.properties(iprops) | oprops, K_FST_PROPERTIES);
}
fn arc_key<A: Arc>(arc: &A) -> (A::Label, A::Label, usize) {
(arc.ilabel(), arc.olabel(), arc.nextstate().as_usize())
}
pub struct ArcSumMapper<'a, A: Arc, F: Fst<A>> {
fst: &'a F,
_marker: std::marker::PhantomData<A>,
}
impl<'a, A: Arc, F: Fst<A>> ArcSumMapper<'a, A, F> {
pub fn new(fst: &'a F) -> Self {
Self {
fst,
_marker: std::marker::PhantomData,
}
}
}
impl<A: Arc, F: Fst<A>> StateMapper<A, A> for ArcSumMapper<'_, A, F> {
fn start(&self) -> Option<A::StateId> {
self.fst.start()
}
fn final_weight(&self, state: A::StateId) -> A::Weight {
self.fst.final_weight(state)
}
fn arcs(&mut self, state: A::StateId) -> Vec<A> {
let mut arcs: Vec<A> = self.fst.arcs(state).collect();
arcs.sort_by_key(arc_key::<A>);
let mut out: Vec<A> = Vec::with_capacity(arcs.len());
for arc in arcs {
match out.last_mut() {
Some(last) if arc_key::<A>(last) == arc_key::<A>(&arc) => {
*last = A::new(
last.ilabel(),
last.olabel(),
last.weight().plus(arc.weight()),
last.nextstate(),
);
}
_ => out.push(arc),
}
}
out
}
fn properties(&self, props: u64) -> u64 {
props & K_ARC_SORT_PROPERTIES & K_DELETE_ARCS_PROPERTIES & K_WEIGHT_INVARIANT_PROPERTIES
}
}
pub struct ArcUniqueMapper<'a, A: Arc, F: Fst<A>> {
fst: &'a F,
_marker: std::marker::PhantomData<A>,
}
impl<'a, A: Arc, F: Fst<A>> ArcUniqueMapper<'a, A, F> {
pub fn new(fst: &'a F) -> Self {
Self {
fst,
_marker: std::marker::PhantomData,
}
}
}
impl<A: Arc, F: Fst<A>> StateMapper<A, A> for ArcUniqueMapper<'_, A, F> {
fn start(&self) -> Option<A::StateId> {
self.fst.start()
}
fn final_weight(&self, state: A::StateId) -> A::Weight {
self.fst.final_weight(state)
}
fn arcs(&mut self, state: A::StateId) -> Vec<A> {
let mut arcs: Vec<A> = self.fst.arcs(state).collect();
arcs.sort_by_key(arc_key::<A>);
let mut out: Vec<A> = Vec::with_capacity(arcs.len());
let mut run_start = 0;
for arc in arcs {
let key = arc_key::<A>(&arc);
if out
.get(run_start)
.is_none_or(|first| arc_key::<A>(first) != key)
{
run_start = out.len();
out.push(arc);
continue;
}
if !out[run_start..]
.iter()
.any(|kept| kept.weight() == arc.weight())
{
out.push(arc);
}
}
out
}
fn properties(&self, props: u64) -> u64 {
props & K_ARC_SORT_PROPERTIES & K_DELETE_ARCS_PROPERTIES
}
}
pub struct IdentityStateMapper<'a, A: Arc, F: Fst<A>> {
fst: &'a F,
_marker: std::marker::PhantomData<A>,
}
impl<'a, A: Arc, F: Fst<A>> IdentityStateMapper<'a, A, F> {
pub fn new(fst: &'a F) -> Self {
Self {
fst,
_marker: std::marker::PhantomData,
}
}
}
impl<A: Arc, F: Fst<A>> StateMapper<A, A> for IdentityStateMapper<'_, A, F> {
fn start(&self) -> Option<A::StateId> {
self.fst.start()
}
fn final_weight(&self, state: A::StateId) -> A::Weight {
self.fst.final_weight(state)
}
fn arcs(&mut self, state: A::StateId) -> Vec<A> {
self.fst.arcs(state).collect()
}
fn properties(&self, props: u64) -> u64 {
props
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::algorithms::test_support::{string_weights, visible_paths};
use crate::arc::StdArc;
use crate::fst::ExpandedFst as _;
use crate::fsts::vector_fst::StdVectorFst;
use crate::weights::float_weight::TropicalWeight;
fn with_duplicates() -> StdVectorFst {
let mut fst = StdVectorFst::new();
for _ in 0..2 {
fst.add_state();
}
fst.set_start(0);
fst.set_final(1, TropicalWeight::one());
fst.add_arc(0, StdArc::new(1, 1, TropicalWeight(5.0), 1));
fst.add_arc(0, StdArc::new(1, 1, TropicalWeight(3.0), 1));
fst.add_arc(0, StdArc::new(1, 1, TropicalWeight(5.0), 1));
fst.add_arc(0, StdArc::new(2, 2, TropicalWeight(1.0), 1));
fst
}
fn arcs(fst: &StdVectorFst) -> Vec<(i32, i32, f32, i32)> {
(0..fst.num_states() as i32)
.flat_map(|s| {
fst.arcs(s)
.map(|a| (a.ilabel(), a.olabel(), a.weight().value(), a.nextstate()))
.collect::<Vec<_>>()
})
.collect()
}
#[test]
fn summing_combines_arcs_that_do_the_same_thing() {
let source = with_duplicates();
let mut fst = source.clone();
{
let mut mapper = ArcSumMapper::new(&source);
state_map(&mut fst, &mut mapper);
}
assert_eq!(arcs(&fst), vec![(1, 1, 3.0, 1), (2, 2, 1.0, 1)]);
}
#[test]
fn summing_preserves_what_the_fst_accepts() {
let source = with_duplicates();
let mut fst = source.clone();
let before = string_weights(visible_paths(&source, 6));
{
let mut mapper = ArcSumMapper::new(&source);
state_map(&mut fst, &mut mapper);
}
assert_eq!(string_weights(visible_paths(&fst, 6)), before);
}
#[test]
fn uniquing_keeps_arcs_that_differ_in_weight() {
let source = with_duplicates();
let mut fst = source.clone();
{
let mut mapper = ArcUniqueMapper::new(&source);
state_map(&mut fst, &mut mapper);
}
assert_eq!(
arcs(&fst),
vec![(1, 1, 5.0, 1), (1, 1, 3.0, 1), (2, 2, 1.0, 1)],
"the exact repeat of the 5.0 arc is gone; the 3.0 one stays"
);
}
#[test]
fn uniquing_gives_the_same_answer_however_the_arcs_arrive() {
let orderings = [
[(5.0, 0), (3.0, 1), (5.0, 2)],
[(3.0, 0), (5.0, 1), (5.0, 2)],
[(5.0, 0), (5.0, 1), (3.0, 2)],
];
let mut results = Vec::new();
for ordering in orderings {
let mut source = StdVectorFst::new();
for _ in 0..2 {
source.add_state();
}
source.set_start(0);
source.set_final(1, TropicalWeight::one());
for (weight, _) in ordering {
source.add_arc(0, StdArc::new(1, 1, TropicalWeight(weight), 1));
}
let mut fst = source.clone();
let mut mapper = ArcUniqueMapper::new(&source);
state_map(&mut fst, &mut mapper);
let mut weights: Vec<f32> = fst.arcs(0).map(|a| a.weight().value()).collect();
weights.sort_by(f32::total_cmp);
results.push(weights);
}
assert_eq!(results[0], vec![3.0, 5.0]);
assert!(
results.iter().all(|r| *r == results[0]),
"the answer moved with the input order: {results:?}"
);
}
#[test]
fn the_identity_mapper_reproduces_the_fst() {
let source = with_duplicates();
let mut ofst = StdVectorFst::new();
{
let mut mapper = IdentityStateMapper::new(&source);
state_map_to(&source, &mut ofst, &mut mapper);
}
assert_eq!(arcs(&ofst), arcs(&source));
assert_eq!(ofst.start(), source.start());
assert_eq!(ofst.final_weight(1), source.final_weight(1));
}
#[test]
fn mapping_into_another_fst_leaves_the_input_alone() {
let source = with_duplicates();
let mut ofst = StdVectorFst::new();
{
let mut mapper = ArcSumMapper::new(&source);
state_map_to(&source, &mut ofst, &mut mapper);
}
assert_eq!(arcs(&ofst), vec![(1, 1, 3.0, 1), (2, 2, 1.0, 1)]);
assert_eq!(arcs(&source).len(), 4, "the input still has its duplicates");
}
#[test]
fn an_fst_with_no_start_state_is_left_alone() {
let source = StdVectorFst::new();
let mut ofst = StdVectorFst::new();
ofst.add_state();
{
let mut mapper = ArcSumMapper::new(&source);
state_map_to(&source, &mut ofst, &mut mapper);
}
assert_eq!(ofst.num_states(), 0);
}
}