use crate::arc::Arc;
use crate::fst::{Fst, Label, MutableFst, VectorFst};
use crate::semiring::{Semiring, TropicalWeight};
use crate::Result;
use num_traits::One;
use std::collections::HashMap;
use std::hash::Hash;
#[derive(Debug, Clone)]
pub struct EncodeTable<W: Semiring> {
encode_map: HashMap<(Label, Label, W), Label>,
decode_vec: Vec<(Label, Label, W)>,
next_label: Label,
}
impl<W: Semiring + Hash + Eq + Clone> EncodeTable<W> {
pub fn new() -> Self {
Self {
encode_map: HashMap::new(),
decode_vec: Vec::new(),
next_label: 1, }
}
pub fn encode(&mut self, ilabel: Label, olabel: Label, weight: W) -> Label {
let key = (ilabel, olabel, weight.clone());
if let Some(&encoded_label) = self.encode_map.get(&key) {
encoded_label
} else {
let encoded_label = self.next_label;
self.encode_map.insert(key.clone(), encoded_label);
self.decode_vec.push(key);
self.next_label += 1;
encoded_label
}
}
pub fn decode(&self, label: Label) -> Option<(Label, Label, W)> {
if label == 0 {
return Some((0, 0, W::one()));
}
let index = (label - 1) as usize;
self.decode_vec.get(index).cloned()
}
pub fn size(&self) -> usize {
self.decode_vec.len()
}
}
impl<W: Semiring + Hash + Eq + Clone> Default for EncodeTable<W> {
fn default() -> Self {
Self::new()
}
}
pub fn encode<W, F>(fst: &F) -> Result<(VectorFst<TropicalWeight>, EncodeTable<W>)>
where
W: Semiring + Hash + Eq + Clone,
F: Fst<W>,
{
let mut table = EncodeTable::new();
let mut result = VectorFst::<TropicalWeight>::new();
for _ in 0..fst.num_states() {
result.add_state();
}
if let Some(start) = fst.start() {
result.set_start(start);
}
for state in fst.states() {
if fst.is_final(state) {
result.set_final(state, TropicalWeight::one());
}
}
for state in fst.states() {
for arc in fst.arcs(state) {
let encoded_label = table.encode(arc.ilabel, arc.olabel, arc.weight);
result.add_arc(
state,
Arc::new(
encoded_label,
encoded_label,
TropicalWeight::one(),
arc.nextstate,
),
);
}
}
Ok((result, table))
}
pub fn decode<W>(fst: &VectorFst<TropicalWeight>, table: &EncodeTable<W>) -> Result<VectorFst<W>>
where
W: Semiring + Clone + Hash + Eq,
{
let mut result = VectorFst::<W>::new();
for _ in 0..fst.num_states() {
result.add_state();
}
if let Some(start) = fst.start() {
result.set_start(start);
}
for state in fst.states() {
if fst.is_final(state) {
result.set_final(state, W::one());
}
}
for state in fst.states() {
for arc in fst.arcs(state) {
if let Some((ilabel, olabel, weight)) = table.decode(arc.ilabel) {
result.add_arc(state, Arc::new(ilabel, olabel, weight, arc.nextstate));
} else {
return Err(crate::Error::InvalidOperation(format!(
"Failed to decode label {} at state {}",
arc.ilabel, state
)));
}
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fst::MutableFst;
use crate::semiring::{BooleanWeight, IntegerWeight, LogWeight};
#[test]
fn test_encode_table_basic() {
let mut table = EncodeTable::<TropicalWeight>::new();
let label1 = table.encode(1, 2, TropicalWeight::new(3.0));
let label2 = table.encode(4, 5, TropicalWeight::new(6.0));
assert_ne!(label1, label2);
assert_eq!(table.size(), 2);
}
#[test]
fn test_encode_table_duplicates() {
let mut table = EncodeTable::<TropicalWeight>::new();
let label1 = table.encode(1, 2, TropicalWeight::new(3.0));
let label2 = table.encode(1, 2, TropicalWeight::new(3.0));
assert_eq!(label1, label2);
assert_eq!(table.size(), 1);
}
#[test]
fn test_encode_table_decode() {
let mut table = EncodeTable::<TropicalWeight>::new();
let label = table.encode(1, 2, TropicalWeight::new(3.0));
let decoded = table.decode(label).unwrap();
assert_eq!(decoded, (1, 2, TropicalWeight::new(3.0)));
}
#[test]
fn test_encode_simple_fst() {
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(1.0));
fst.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.5), s1));
let (encoded, table) = encode(&fst).unwrap();
assert_eq!(encoded.num_states(), fst.num_states());
assert_eq!(encoded.num_arcs_total(), fst.num_arcs_total());
assert_eq!(table.size(), 1);
}
#[test]
fn test_encode_creates_unit_weights() {
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(1.0));
fst.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.5), s1));
let (encoded, _) = encode(&fst).unwrap();
for state in encoded.states() {
for arc in encoded.arcs(state) {
assert_eq!(arc.weight, TropicalWeight::one());
}
}
}
#[test]
fn test_encode_decode_roundtrip() {
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(1.0));
fst.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.5), s1));
let (encoded, table) = encode(&fst).unwrap();
let decoded = decode(&encoded, &table).unwrap();
assert_eq!(decoded.num_states(), fst.num_states());
assert_eq!(decoded.num_arcs_total(), fst.num_arcs_total());
assert_eq!(decoded.start(), fst.start());
for state in fst.states() {
let orig_arcs: Vec<_> = fst.arcs(state).collect();
let dec_arcs: Vec<_> = decoded.arcs(state).collect();
assert_eq!(orig_arcs.len(), dec_arcs.len());
for (orig, dec) in orig_arcs.iter().zip(dec_arcs.iter()) {
assert_eq!(orig.ilabel, dec.ilabel);
assert_eq!(orig.olabel, dec.olabel);
assert_eq!(orig.weight, dec.weight);
assert_eq!(orig.nextstate, dec.nextstate);
}
}
}
#[test]
fn test_encode_with_duplicate_arcs() {
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::new(0.5), s1));
fst.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.5), s1));
let (encoded, table) = encode(&fst).unwrap();
assert_eq!(table.size(), 1);
let arcs: Vec<_> = encoded.arcs(s0).collect();
assert_eq!(arcs.len(), 2);
assert_eq!(arcs[0].ilabel, arcs[1].ilabel); }
#[test]
fn test_encode_empty_fst() {
let fst = VectorFst::<TropicalWeight>::new();
let (encoded, table) = encode(&fst).unwrap();
assert_eq!(encoded.num_states(), 0);
assert_eq!(table.size(), 0);
}
#[test]
fn test_encode_single_state() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
fst.set_start(s0);
fst.set_final(s0, TropicalWeight::one());
let (encoded, table) = encode(&fst).unwrap();
assert_eq!(encoded.num_states(), 1);
assert_eq!(table.size(), 0); }
#[test]
fn test_encode_multiple_semirings() {
let mut fst1 = VectorFst::<BooleanWeight>::new();
let s0 = fst1.add_state();
let s1 = fst1.add_state();
fst1.set_start(s0);
fst1.set_final(s1, BooleanWeight::one());
fst1.add_arc(s0, Arc::new(1, 2, BooleanWeight::one(), s1));
let (encoded1, table1) = encode(&fst1).unwrap();
assert_eq!(table1.size(), 1);
let decoded1 = decode(&encoded1, &table1).unwrap();
assert_eq!(decoded1.num_states(), fst1.num_states());
let mut fst2 = VectorFst::<IntegerWeight>::new();
let s0 = fst2.add_state();
let s1 = fst2.add_state();
fst2.set_start(s0);
fst2.set_final(s1, IntegerWeight::one());
fst2.add_arc(s0, Arc::new(1, 2, IntegerWeight::new(5), s1));
let (encoded2, table2) = encode(&fst2).unwrap();
assert_eq!(table2.size(), 1);
let decoded2 = decode(&encoded2, &table2).unwrap();
assert_eq!(decoded2.num_states(), fst2.num_states());
}
#[test]
fn test_encode_epsilon_arcs() {
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::epsilon(TropicalWeight::new(0.5), s1));
let (encoded, table) = encode(&fst).unwrap();
let decoded = decode(&encoded, &table).unwrap();
let orig_arcs: Vec<_> = fst.arcs(s0).collect();
let dec_arcs: Vec<_> = decoded.arcs(s0).collect();
assert_eq!(orig_arcs.len(), dec_arcs.len());
assert_eq!(orig_arcs[0].ilabel, dec_arcs[0].ilabel);
assert_eq!(orig_arcs[0].olabel, dec_arcs[0].olabel);
}
#[test]
fn test_encode_complex_fst() {
let mut fst = VectorFst::<TropicalWeight>::new();
let states: Vec<_> = (0..5).map(|_| fst.add_state()).collect();
fst.set_start(states[0]);
fst.set_final(states[4], TropicalWeight::new(2.0));
fst.add_arc(
states[0],
Arc::new(1, 2, TropicalWeight::new(0.1), states[1]),
);
fst.add_arc(
states[0],
Arc::new(3, 4, TropicalWeight::new(0.2), states[2]),
);
fst.add_arc(
states[1],
Arc::new(5, 6, TropicalWeight::new(0.3), states[3]),
);
fst.add_arc(
states[2],
Arc::new(7, 8, TropicalWeight::new(0.4), states[3]),
);
fst.add_arc(
states[3],
Arc::new(9, 10, TropicalWeight::new(0.5), states[4]),
);
let (encoded, table) = encode(&fst).unwrap();
let decoded = decode(&encoded, &table).unwrap();
assert_eq!(decoded.num_states(), fst.num_states());
assert_eq!(decoded.num_arcs_total(), fst.num_arcs_total());
assert_eq!(table.size(), 5); }
#[test]
fn test_decode_invalid_label() {
let table = EncodeTable::<TropicalWeight>::new();
let mut encoded = VectorFst::<TropicalWeight>::new();
let s0 = encoded.add_state();
let s1 = encoded.add_state();
encoded.set_start(s0);
encoded.add_arc(
s0,
Arc::new(999, 999, TropicalWeight::one(), s1), );
let result = decode(&encoded, &table);
assert!(result.is_err());
}
#[test]
fn test_encode_table_default() {
let table: EncodeTable<TropicalWeight> = EncodeTable::default();
assert_eq!(table.size(), 0);
}
#[test]
fn test_encode_decode_with_log_weight() {
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::one());
fst.add_arc(s0, Arc::new(1, 2, LogWeight::new(0.5), s1));
let (encoded, table) = encode(&fst).unwrap();
let decoded = decode(&encoded, &table).unwrap();
assert_eq!(decoded.num_states(), fst.num_states());
assert_eq!(decoded.num_arcs_total(), fst.num_arcs_total());
}
#[test]
fn test_encode_multiple_different_arcs() {
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::new(0.5), s1));
fst.add_arc(s0, Arc::new(1, 3, TropicalWeight::new(0.5), s1)); fst.add_arc(s0, Arc::new(1, 2, TropicalWeight::new(0.6), s1));
let (encoded, table) = encode(&fst).unwrap();
assert_eq!(table.size(), 3);
let arcs: Vec<_> = encoded.arcs(s0).collect();
assert_eq!(arcs.len(), 3);
assert_ne!(arcs[0].ilabel, arcs[1].ilabel);
assert_ne!(arcs[0].ilabel, arcs[2].ilabel);
assert_ne!(arcs[1].ilabel, arcs[2].ilabel);
}
#[test]
fn test_encode_table_epsilon_decode() {
let table = EncodeTable::<TropicalWeight>::new();
let decoded = table.decode(0).unwrap();
assert_eq!(decoded, (0, 0, TropicalWeight::one()));
}
}