use std::marker::PhantomData;
use crate::arc::{Arc, ArcLabel, ArcStateId, GallicArc};
use crate::error::OpenFstError;
use crate::fst::{Fst, MutableFst};
use crate::properties::{
K_ADD_SUPER_FINAL_PROPERTIES, K_COPY_PROPERTIES, K_ERROR, K_FST_PROPERTIES,
K_O_LABEL_INVARIANT_PROPERTIES, K_WEIGHT_INVARIANT_PROPERTIES, project_properties,
};
use crate::weight::{Divide, DivideType, Weight};
use crate::weights::string_weight::{
GallicTypeMarker, GallicWeight, StringWeight, StringWeightValue,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MapFinalAction {
NoSuperfinal,
AllowSuperfinal,
RequireSuperfinal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MapSymbolsAction {
Clear,
Copy,
Noop,
}
pub trait ArcMapper<From: Arc, To: Arc> {
fn map(&mut self, arc: &From) -> To;
fn final_action(&self) -> MapFinalAction;
fn input_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Copy
}
fn output_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Copy
}
fn properties(&self, props: u64) -> u64;
}
fn final_as_arc<From: Arc>(weight: From::Weight) -> From {
From::new(
From::Label::epsilon(),
From::Label::epsilon(),
weight,
From::StateId::no_state(),
)
}
fn became_an_arc<To: Arc>(arc: &To) -> bool {
arc.ilabel() != To::Label::epsilon() || arc.olabel() != To::Label::epsilon()
}
pub fn arc_map<A, F, M>(fst: &mut F, mapper: &mut M) -> Result<(), OpenFstError>
where
A: Arc,
F: MutableFst<A>,
M: ArcMapper<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 Ok(());
}
let props = fst.properties(K_FST_PROPERTIES, false);
let final_action = mapper.final_action();
let zero = A::Weight::zero();
let mut superfinal = None;
if final_action == MapFinalAction::RequireSuperfinal {
let s = fst.add_state();
fst.set_final(s, A::Weight::one());
superfinal = Some(s);
}
let states: Vec<A::StateId> = fst.states().collect();
for state in states {
let mut label_error = false;
fst.mutate_arcs(state, |arc| *arc = mapper.map(arc));
if Some(state) == superfinal {
continue;
}
let mapped = mapper.map(&final_as_arc::<A>(fst.final_weight(state)));
match final_action {
MapFinalAction::NoSuperfinal => {
if became_an_arc(&mapped) {
label_error = true;
} else {
fst.set_final(state, mapped.weight().clone());
}
}
MapFinalAction::AllowSuperfinal => {
if became_an_arc(&mapped) {
let target = match superfinal {
Some(s) => s,
None => {
let s = fst.add_state();
fst.set_final(s, A::Weight::one());
superfinal = Some(s);
s
}
};
fst.add_arc(
state,
A::new(
mapped.ilabel(),
mapped.olabel(),
mapped.weight().clone(),
target,
),
);
fst.set_final(state, zero.clone());
} else {
fst.set_final(state, mapped.weight().clone());
}
}
MapFinalAction::RequireSuperfinal => {
let target = superfinal.expect("a superfinal state was added above");
if became_an_arc(&mapped) || *mapped.weight() != zero {
fst.add_arc(
state,
A::new(
mapped.ilabel(),
mapped.olabel(),
mapped.weight().clone(),
target,
),
);
}
fst.set_final(state, zero.clone());
}
}
if label_error {
return Err(OpenFstError::InvalidOperation(
"ArcMap: the mapper produced a labelled arc from a final weight, but said it \
would not need a superfinal state"
.to_string(),
));
}
}
fst.set_properties(mapper.properties(props), K_FST_PROPERTIES);
Ok(())
}
pub fn arc_map_to<From, To, F1, F2, M>(
ifst: &F1,
ofst: &mut F2,
mapper: &mut M,
) -> Result<(), OpenFstError>
where
From: Arc,
To: Arc<StateId = From::StateId>,
F1: Fst<From>,
F2: MutableFst<To>,
M: ArcMapper<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);
let Some(start) = ifst.start() else {
return Ok(());
};
let final_action = mapper.final_action();
let zero = To::Weight::zero();
if let Some(num_states) = ifst.num_states_if_known() {
ofst.reserve_states(num_states + usize::from(final_action != MapFinalAction::NoSuperfinal));
}
for _ in ifst.states() {
ofst.add_state();
}
let mut superfinal = None;
if final_action == MapFinalAction::RequireSuperfinal {
let s = ofst.add_state();
ofst.set_final(s, To::Weight::one());
superfinal = Some(s);
}
for state in ifst.states() {
if state == start {
ofst.set_start(state);
}
ofst.reserve_arcs(
state,
ifst.num_arcs(state) + usize::from(final_action != MapFinalAction::NoSuperfinal),
);
for arc in ifst.arcs(state) {
let mapped = mapper.map(&arc);
ofst.add_arc(state, mapped);
}
let mapped = mapper.map(&final_as_arc::<From>(ifst.final_weight(state)));
match final_action {
MapFinalAction::NoSuperfinal => {
if became_an_arc(&mapped) {
return Err(OpenFstError::InvalidOperation(
"ArcMap: the mapper produced a labelled arc from a final weight, but \
said it would not need a superfinal state"
.to_string(),
));
}
ofst.set_final(state, mapped.weight().clone());
}
MapFinalAction::AllowSuperfinal => {
if became_an_arc(&mapped) {
let target = match superfinal {
Some(s) => s,
None => {
let s = ofst.add_state();
ofst.set_final(s, To::Weight::one());
superfinal = Some(s);
s
}
};
ofst.add_arc(
state,
To::new(
mapped.ilabel(),
mapped.olabel(),
mapped.weight().clone(),
target,
),
);
ofst.set_final(state, zero.clone());
} else {
ofst.set_final(state, mapped.weight().clone());
}
}
MapFinalAction::RequireSuperfinal => {
let target = superfinal.expect("a superfinal state was added above");
if became_an_arc(&mapped) || *mapped.weight() != zero {
ofst.add_arc(
state,
To::new(
mapped.ilabel(),
mapped.olabel(),
mapped.weight().clone(),
target,
),
);
}
ofst.set_final(state, zero.clone());
}
}
}
let oprops = ofst.properties(K_FST_PROPERTIES, false);
ofst.set_properties(mapper.properties(iprops) | oprops, K_FST_PROPERTIES);
Ok(())
}
#[derive(Debug, Clone, Copy, Default)]
pub struct IdentityArcMapper;
impl<A: Arc> ArcMapper<A, A> for IdentityArcMapper {
#[inline]
fn map(&mut self, arc: &A) -> A {
arc.clone()
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct InputEpsilonMapper;
impl<A: Arc> ArcMapper<A, A> for InputEpsilonMapper {
#[inline]
fn map(&mut self, arc: &A) -> A {
A::new(
A::Label::epsilon(),
arc.olabel(),
arc.weight().clone(),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn input_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Clear
}
fn properties(&self, props: u64) -> u64 {
crate::properties::project_properties(props, false)
& crate::properties::K_SET_ARC_PROPERTIES
| crate::properties::K_I_EPSILONS
| crate::properties::K_I_LABEL_SORTED
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct OutputEpsilonMapper;
impl<A: Arc> ArcMapper<A, A> for OutputEpsilonMapper {
#[inline]
fn map(&mut self, arc: &A) -> A {
A::new(
arc.ilabel(),
A::Label::epsilon(),
arc.weight().clone(),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn output_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Clear
}
fn properties(&self, props: u64) -> u64 {
crate::properties::project_properties(props, true) & crate::properties::K_SET_ARC_PROPERTIES
| crate::properties::K_O_EPSILONS
| crate::properties::K_O_LABEL_SORTED
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SuperFinalMapper;
impl<A: Arc> ArcMapper<A, A> for SuperFinalMapper {
#[inline]
fn map(&mut self, arc: &A) -> A {
arc.clone()
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::RequireSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props & crate::properties::K_ADD_SUPER_FINAL_PROPERTIES
}
}
#[derive(Debug, Clone)]
pub struct TimesMapper<W> {
weight: W,
}
impl<W: Weight> TimesMapper<W> {
pub fn new(weight: W) -> Self {
Self { weight }
}
}
impl<A: Arc> ArcMapper<A, A> for TimesMapper<A::Weight> {
#[inline]
fn map(&mut self, arc: &A) -> A {
A::new(
arc.ilabel(),
arc.olabel(),
self.weight.times(arc.weight()),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props & crate::properties::K_WEIGHT_INVARIANT_PROPERTIES
}
}
#[derive(Debug, Clone)]
pub struct PlusMapper<W> {
weight: W,
}
impl<W: Weight> PlusMapper<W> {
pub fn new(weight: W) -> Self {
Self { weight }
}
}
impl<A: Arc> ArcMapper<A, A> for PlusMapper<A::Weight> {
#[inline]
fn map(&mut self, arc: &A) -> A {
A::new(
arc.ilabel(),
arc.olabel(),
self.weight.plus(arc.weight()),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props & crate::properties::K_WEIGHT_INVARIANT_PROPERTIES
}
}
#[derive(Debug, Clone, Copy)]
pub struct PowerMapper {
power: f64,
}
impl PowerMapper {
pub fn new(power: f64) -> Self {
Self { power }
}
}
impl<A: Arc> ArcMapper<A, A> for PowerMapper
where
A::Weight: Weight,
{
fn map(&mut self, arc: &A) -> A {
A::new(
arc.ilabel(),
arc.olabel(),
power(arc.weight(), self.power),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props & crate::properties::K_WEIGHT_INVARIANT_PROPERTIES
}
}
fn power<W: Weight>(weight: &W, n: f64) -> W {
let n = n.max(0.0).round() as u64;
let mut result = W::one();
for _ in 0..n {
result = result.times(weight);
}
result
}
#[derive(Debug, Clone, Copy, Default)]
pub struct InvertWeightMapper;
impl<A: Arc> ArcMapper<A, A> for InvertWeightMapper
where
A::Weight: Divide,
{
#[inline]
fn map(&mut self, arc: &A) -> A {
A::new(
arc.ilabel(),
arc.olabel(),
A::Weight::one().divide(arc.weight(), DivideType::Any),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props & crate::properties::K_WEIGHT_INVARIANT_PROPERTIES
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RmWeightMapper;
impl<A: Arc> ArcMapper<A, A> for RmWeightMapper {
#[inline]
fn map(&mut self, arc: &A) -> A {
let weight = if *arc.weight() == A::Weight::zero() {
A::Weight::zero()
} else {
A::Weight::one()
};
A::new(arc.ilabel(), arc.olabel(), weight, arc.nextstate())
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
(props & crate::properties::K_WEIGHT_INVARIANT_PROPERTIES) | crate::properties::K_UNWEIGHTED
}
}
#[derive(Debug, Clone, Copy)]
pub struct QuantizeMapper {
delta: f32,
}
impl Default for QuantizeMapper {
fn default() -> Self {
Self {
delta: crate::weight::DELTA,
}
}
}
impl QuantizeMapper {
pub fn new(delta: f32) -> Self {
Self { delta }
}
}
impl<A: Arc> ArcMapper<A, A> for QuantizeMapper {
#[inline]
fn map(&mut self, arc: &A) -> A {
A::new(
arc.ilabel(),
arc.olabel(),
arc.weight().quantize(self.delta),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props & crate::properties::K_WEIGHT_INVARIANT_PROPERTIES
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ReverseWeightMapper;
impl<From, To> ArcMapper<From, To> for ReverseWeightMapper
where
From: Arc,
To: Arc<
Label = From::Label,
StateId = From::StateId,
Weight = <From::Weight as Weight>::ReverseWeight,
>,
{
#[inline]
fn map(&mut self, arc: &From) -> To {
To::new(
arc.ilabel(),
arc.olabel(),
arc.weight().reverse(),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct WeightConvertMapper<To> {
_marker: std::marker::PhantomData<To>,
}
impl<To> WeightConvertMapper<To> {
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
impl<A1, A2> ArcMapper<A1, A2> for WeightConvertMapper<A2>
where
A1: Arc,
A2: Arc<Label = A1::Label, StateId = A1::StateId>,
A2::Weight: From<A1::Weight>,
{
#[inline]
fn map(&mut self, arc: &A1) -> A2 {
A2::new(
arc.ilabel(),
arc.olabel(),
A2::Weight::from(arc.weight().clone()),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn properties(&self, props: u64) -> u64 {
props
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ToGallicMapper<G> {
_marker: PhantomData<G>,
}
impl<G> ToGallicMapper<G> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<A, G> ArcMapper<A, GallicArc<A, G>> for ToGallicMapper<G>
where
A: Arc,
G: GallicTypeMarker,
{
fn map(&mut self, arc: &A) -> GallicArc<A, G> {
let epsilon = A::Label::epsilon();
let no_state = A::StateId::no_state();
let gallic = |labels: StringWeight<A::Label, G::StringType>, weight: A::Weight| {
GallicWeight::<A::Label, A::Weight, G>::from_parts(labels, weight)
};
if arc.nextstate() == no_state {
return if *arc.weight() == A::Weight::zero() {
GallicArc::new(epsilon, epsilon, GallicWeight::zero(), no_state)
} else {
GallicArc::new(
epsilon,
epsilon,
gallic(StringWeight::one(), arc.weight().clone()),
no_state,
)
};
}
let labels = if arc.olabel() == epsilon {
StringWeight::one()
} else {
StringWeight::new(vec![arc.olabel()])
};
GallicArc::new(
arc.ilabel(),
arc.ilabel(),
gallic(labels, arc.weight().clone()),
arc.nextstate(),
)
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::NoSuperfinal
}
fn input_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Copy
}
fn output_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Clear
}
fn properties(&self, props: u64) -> u64 {
project_properties(props, true) & K_WEIGHT_INVARIANT_PROPERTIES
}
}
#[derive(Debug, Clone)]
pub struct FromGallicMapper<L, G> {
superfinal_label: L,
error: bool,
_marker: PhantomData<G>,
}
impl<L: ArcLabel, G> FromGallicMapper<L, G> {
pub fn new() -> Self {
Self::with_superfinal_label(L::epsilon())
}
pub fn with_superfinal_label(superfinal_label: L) -> Self {
Self {
superfinal_label,
error: false,
_marker: PhantomData,
}
}
pub fn error(&self) -> bool {
self.error
}
}
impl<L: ArcLabel, G> Default for FromGallicMapper<L, G> {
fn default() -> Self {
Self::new()
}
}
impl<A, G> ArcMapper<GallicArc<A, G>, A> for FromGallicMapper<A::Label, G>
where
A: Arc,
G: GallicTypeMarker,
{
fn map(&mut self, arc: &GallicArc<A, G>) -> A {
let epsilon = A::Label::epsilon();
let no_state = A::StateId::no_state();
if arc.nextstate() == no_state && *arc.weight() == GallicWeight::zero() {
return A::new(arc.ilabel(), epsilon, A::Weight::zero(), no_state);
}
let (label, weight) = match &arc.weight().labels().value {
StringWeightValue::Labels(labels) if labels.len() <= 1 => (
labels.first().copied().unwrap_or(epsilon),
arc.weight().weight().clone(),
),
_ => {
self.error = true;
(epsilon, A::Weight::zero())
}
};
if arc.ilabel() != arc.olabel() {
self.error = true;
}
if arc.ilabel() == epsilon && label != epsilon && arc.nextstate() == no_state {
A::new(self.superfinal_label, label, weight, arc.nextstate())
} else {
A::new(arc.ilabel(), label, weight, arc.nextstate())
}
}
fn final_action(&self) -> MapFinalAction {
MapFinalAction::AllowSuperfinal
}
fn input_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Copy
}
fn output_symbols_action(&self) -> MapSymbolsAction {
MapSymbolsAction::Clear
}
fn properties(&self, props: u64) -> u64 {
let out = props
& K_O_LABEL_INVARIANT_PROPERTIES
& K_WEIGHT_INVARIANT_PROPERTIES
& K_ADD_SUPER_FINAL_PROPERTIES;
if self.error { out | K_ERROR } else { out }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::AtomicRc;
use crate::arc::StdArc;
use crate::fst::ExpandedFst as _;
use crate::fsts::vector_fst::StdVectorFst;
use crate::properties::{K_ACCEPTOR, K_NOT_ACCEPTOR, K_UNWEIGHTED};
use crate::symbol_table::SymbolTable;
use crate::weights::float_weight::TropicalWeight;
fn fst() -> StdVectorFst {
let mut fst = StdVectorFst::new();
for _ in 0..3 {
fst.add_state();
}
fst.set_start(0);
fst.add_arc(0, StdArc::new(1, 2, TropicalWeight(1.0), 1));
fst.add_arc(1, StdArc::new(3, 4, TropicalWeight(2.0), 2));
fst.set_final(2, TropicalWeight(3.0));
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 the_identity_mapper_changes_nothing() {
let mut fst = fst();
let before = arcs(&fst);
arc_map(&mut fst, &mut IdentityArcMapper).unwrap();
assert_eq!(arcs(&fst), before);
assert_eq!(fst.final_weight(2), TropicalWeight(3.0));
}
#[test]
fn the_epsilon_mappers_blank_one_side_and_drop_its_table() {
let mut table = SymbolTable::new("input".to_string());
table.add_symbol("<eps>", 0);
let mut with_tables = fst();
with_tables.set_input_symbols(Some(AtomicRc::new(table.clone())));
with_tables.set_output_symbols(Some(AtomicRc::new(table)));
arc_map(&mut with_tables, &mut InputEpsilonMapper).unwrap();
assert_eq!(
arcs(&with_tables),
vec![(0, 2, 1.0, 1), (0, 4, 2.0, 2)],
"the input side is blanked"
);
assert!(
with_tables.input_symbols().is_none(),
"its table went with it"
);
assert!(with_tables.output_symbols().is_some());
let mut other = fst();
arc_map(&mut other, &mut OutputEpsilonMapper).unwrap();
assert_eq!(arcs(&other), vec![(1, 0, 1.0, 1), (3, 0, 2.0, 2)]);
}
#[test]
fn removing_weights_leaves_an_unweighted_fst() {
let mut fst = fst();
arc_map(&mut fst, &mut RmWeightMapper).unwrap();
assert_eq!(arcs(&fst), vec![(1, 2, 0.0, 1), (3, 4, 0.0, 2)]);
assert_eq!(fst.final_weight(2), TropicalWeight::one());
assert_ne!(fst.properties(K_UNWEIGHTED, false) & K_UNWEIGHTED, 0);
}
#[test]
fn multiplying_reaches_the_final_weights_too() {
let mut fst = fst();
arc_map(&mut fst, &mut TimesMapper::new(TropicalWeight(10.0))).unwrap();
assert_eq!(arcs(&fst), vec![(1, 2, 11.0, 1), (3, 4, 12.0, 2)]);
assert_eq!(fst.final_weight(2), TropicalWeight(13.0));
assert_eq!(fst.final_weight(0), TropicalWeight::zero());
}
#[test]
fn adding_caps_every_weight() {
let mut fst = fst();
arc_map(&mut fst, &mut PlusMapper::new(TropicalWeight(1.5))).unwrap();
assert_eq!(arcs(&fst), vec![(1, 2, 1.0, 1), (3, 4, 1.5, 2)]);
assert_eq!(fst.final_weight(2), TropicalWeight(1.5));
}
#[test]
fn quantizing_rounds_every_weight() {
let mut fst = StdVectorFst::new();
fst.add_state();
fst.set_start(0);
fst.add_arc(0, StdArc::new(1, 1, TropicalWeight(1.234_567), 0));
fst.set_final(0, TropicalWeight(2.765_432));
arc_map(&mut fst, &mut QuantizeMapper::new(0.5)).unwrap();
assert_eq!(fst.arcs(0).next().unwrap().weight().value(), 1.0);
assert_eq!(fst.final_weight(0).value(), 3.0);
}
#[test]
fn the_superfinal_mapper_leaves_exactly_one_final_state() {
let mut fst = fst();
fst.set_final(1, TropicalWeight(5.0));
arc_map(&mut fst, &mut SuperFinalMapper).unwrap();
let finals: Vec<i32> = (0..fst.num_states() as i32)
.filter(|&s| fst.final_weight(s) != TropicalWeight::zero())
.collect();
assert_eq!(finals.len(), 1);
let superfinal = finals[0];
assert_eq!(fst.final_weight(superfinal), TropicalWeight::one());
let to_superfinal: Vec<f32> = (0..fst.num_states() as i32)
.flat_map(|s| {
fst.arcs(s)
.filter(|a| a.nextstate() == superfinal)
.map(|a| a.weight().value())
.collect::<Vec<_>>()
})
.collect();
assert_eq!(to_superfinal.len(), 2);
assert!(to_superfinal.contains(&5.0));
assert!(to_superfinal.contains(&3.0));
}
#[test]
fn mapping_into_another_fst_leaves_the_input_alone() {
let ifst = fst();
let mut ofst = StdVectorFst::new();
ofst.add_state();
arc_map_to(&ifst, &mut ofst, &mut RmWeightMapper).unwrap();
assert_eq!(ofst.num_states(), 3, "the output starts from nothing");
assert_eq!(ofst.start(), Some(0));
assert_eq!(arcs(&ofst), vec![(1, 2, 0.0, 1), (3, 4, 0.0, 2)]);
assert_eq!(ofst.final_weight(2), TropicalWeight::one());
assert_eq!(arcs(&ifst), vec![(1, 2, 1.0, 1), (3, 4, 2.0, 2)]);
}
#[test]
fn reversing_weights_maps_between_arc_types() {
let ifst = fst();
let mut ofst = StdVectorFst::new();
arc_map_to(&ifst, &mut ofst, &mut ReverseWeightMapper).unwrap();
assert_eq!(arcs(&ofst), arcs(&ifst));
assert_eq!(ofst.final_weight(2), TropicalWeight(3.0));
}
#[test]
fn an_fst_with_no_start_state_is_left_alone() {
let mut fst = StdVectorFst::new();
fst.add_state();
arc_map(&mut fst, &mut RmWeightMapper).unwrap();
assert_eq!(fst.num_states(), 1);
let ifst = StdVectorFst::new();
let mut ofst = StdVectorFst::new();
ofst.add_state();
arc_map_to(&ifst, &mut ofst, &mut IdentityArcMapper).unwrap();
assert_eq!(ofst.num_states(), 0);
}
#[test]
fn label_properties_are_given_up_when_labels_change() {
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(5, 5, TropicalWeight::one(), 1));
assert_ne!(fst.properties(K_ACCEPTOR, true) & K_ACCEPTOR, 0);
arc_map(&mut fst, &mut InputEpsilonMapper).unwrap();
assert_eq!(
fst.properties(K_ACCEPTOR, false) & K_ACCEPTOR,
0,
"it is no longer an acceptor and must not say it is"
);
assert_ne!(fst.properties(K_NOT_ACCEPTOR, true) & K_NOT_ACCEPTOR, 0);
}
}