use crate::arc::Arc;
use crate::fst::{Fst, MutableFst, StateId, VectorFst};
use crate::semiring::{DivisibleSemiring, Semiring};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReweightType {
ToInitial,
ToFinal,
}
pub fn reweight<W, F>(
fst: &F,
potentials: &[W],
reweight_type: ReweightType,
) -> Result<VectorFst<W>>
where
W: Semiring + DivisibleSemiring,
F: Fst<W>,
{
if potentials.len() != fst.num_states() {
return Err(Error::InvalidOperation(format!(
"Potentials length {} doesn't match FST states {}",
potentials.len(),
fst.num_states()
)));
}
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_idx in 0..fst.num_states() {
let state = state_idx as StateId;
if let Some(final_weight) = fst.final_weight(state) {
let new_final_weight = match reweight_type {
ReweightType::ToInitial => {
final_weight.divide(&potentials[state_idx]).ok_or_else(|| {
Error::InvalidOperation(format!(
"Cannot divide by potential at state {}",
state
))
})?
}
ReweightType::ToFinal => {
potentials[state_idx].times(final_weight)
}
};
result.set_final(state, new_final_weight);
}
for arc in fst.arcs(state) {
let new_weight = match reweight_type {
ReweightType::ToInitial => {
let temp = arc.weight.divide(&potentials[state_idx]).ok_or_else(|| {
Error::InvalidOperation(format!(
"Cannot divide by source potential at state {}",
state
))
})?;
temp.times(&potentials[arc.nextstate as usize])
}
ReweightType::ToFinal => {
let temp = arc
.weight
.divide(&potentials[arc.nextstate as usize])
.ok_or_else(|| {
Error::InvalidOperation(format!(
"Cannot divide by destination potential at state {}",
arc.nextstate
))
})?;
temp.times(&potentials[state_idx])
}
};
result.add_arc(
state,
Arc::new(arc.ilabel, arc.olabel, new_weight, arc.nextstate),
);
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[test]
fn test_reweight_identity_potentials() {
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, 1, TropicalWeight::new(2.0), s1));
let potentials = vec![TropicalWeight::one(); fst.num_states()];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 2);
assert_eq!(result.start(), Some(0));
assert_eq!(result.final_weight(1), Some(&TropicalWeight::new(1.0)));
let arcs: Vec<_> = result.arcs(0).collect();
assert_eq!(arcs.len(), 1);
assert_eq!(arcs[0].weight, TropicalWeight::new(2.0));
}
#[test]
fn test_reweight_to_initial_simple() {
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(5.0));
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(3.0), s1));
let potentials = vec![TropicalWeight::new(0.0), TropicalWeight::new(2.0)];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 2);
assert_eq!(result.start(), Some(0));
let arcs: Vec<_> = result.arcs(0).collect();
assert_eq!(arcs.len(), 1);
assert_eq!(arcs[0].ilabel, 1);
assert_eq!(arcs[0].olabel, 1);
assert_eq!(arcs[0].nextstate, 1);
}
#[test]
fn test_reweight_to_final_simple() {
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(5.0));
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(3.0), s1));
let potentials = vec![TropicalWeight::new(1.0), TropicalWeight::new(0.0)];
let result = reweight(&fst, &potentials, ReweightType::ToFinal).unwrap();
assert_eq!(result.num_states(), 2);
assert_eq!(result.start(), Some(0));
let arcs: Vec<_> = result.arcs(0).collect();
assert_eq!(arcs.len(), 1);
}
#[test]
fn test_reweight_empty_fst() {
let fst = VectorFst::<TropicalWeight>::new();
let potentials = vec![];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 0);
assert_eq!(result.start(), None);
}
#[test]
fn test_reweight_single_state() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
fst.set_start(s0);
fst.set_final(s0, TropicalWeight::new(3.0));
let potentials = vec![TropicalWeight::new(1.0)];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 1);
assert_eq!(result.start(), Some(0));
assert!(result.final_weight(0).is_some());
}
#[test]
fn test_reweight_multiple_arcs() {
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::new(1.0));
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(2.0), s1));
fst.add_arc(s0, Arc::new(2, 2, TropicalWeight::new(3.0), s2));
fst.add_arc(s1, Arc::new(3, 3, TropicalWeight::new(4.0), s2));
let potentials = vec![
TropicalWeight::new(0.0),
TropicalWeight::new(1.0),
TropicalWeight::new(2.0),
];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 3);
let arcs_s0: Vec<_> = result.arcs(0).collect();
assert_eq!(arcs_s0.len(), 2);
let arcs_s1: Vec<_> = result.arcs(1).collect();
assert_eq!(arcs_s1.len(), 1);
}
#[test]
fn test_reweight_preserves_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(5, 10, TropicalWeight::new(1.0), s1));
let potentials = vec![TropicalWeight::new(0.5), TropicalWeight::new(0.3)];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
let arcs: Vec<_> = result.arcs(0).collect();
assert_eq!(arcs[0].ilabel, 5);
assert_eq!(arcs[0].olabel, 10);
assert_eq!(arcs[0].nextstate, 1);
}
#[test]
fn test_reweight_invalid_potentials_length() {
let mut fst = VectorFst::<TropicalWeight>::new();
fst.add_state();
fst.add_state();
let potentials = vec![TropicalWeight::new(1.0)];
let result = reweight(&fst, &potentials, ReweightType::ToInitial);
assert!(result.is_err());
}
#[test]
fn test_reweight_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::new(1.0));
fst.add_arc(s0, Arc::new(1, 1, LogWeight::new(2.0), s1));
let potentials = vec![LogWeight::one(); fst.num_states()];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 2);
assert_eq!(result.start(), Some(0));
}
#[test]
fn test_reweight_preserves_structure() {
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(s3, TropicalWeight::one());
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
fst.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(2.0), s2));
fst.add_arc(s2, Arc::new(3, 3, TropicalWeight::new(3.0), s3));
let potentials = vec![
TropicalWeight::new(0.0),
TropicalWeight::new(0.5),
TropicalWeight::new(1.0),
TropicalWeight::new(1.5),
];
let result = reweight(&fst, &potentials, ReweightType::ToFinal).unwrap();
assert_eq!(result.num_states(), 4);
assert_eq!(result.arcs(0).count(), 1);
assert_eq!(result.arcs(1).count(), 1);
assert_eq!(result.arcs(2).count(), 1);
assert_eq!(result.arcs(3).count(), 0);
}
#[test]
fn test_reweight_no_final_states() {
let mut fst = VectorFst::<TropicalWeight>::new();
let s0 = fst.add_state();
let s1 = fst.add_state();
fst.set_start(s0);
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(2.0), s1));
let potentials = vec![TropicalWeight::one(); fst.num_states()];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 2);
assert!(result.final_weight(0).is_none());
assert!(result.final_weight(1).is_none());
}
#[test]
fn test_reweight_cyclic_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::one());
fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::new(1.0), s1));
fst.add_arc(s1, Arc::new(2, 2, TropicalWeight::new(2.0), s0));
let potentials = vec![TropicalWeight::new(0.5), TropicalWeight::new(1.5)];
let result = reweight(&fst, &potentials, ReweightType::ToInitial).unwrap();
assert_eq!(result.num_states(), 2);
assert_eq!(result.arcs(0).count(), 1);
assert_eq!(result.arcs(1).count(), 1);
}
}