1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
use crate::fst_properties::mutable_properties::project_properties;
use crate::fst_properties::FstProperties;
use crate::fst_traits::MutableFst;
use crate::semirings::Semiring;
#[derive(Debug, Clone, PartialEq, PartialOrd, Copy)]
/// Different types of labels projection in a FST.
pub enum ProjectType {
/// Input projection : output labels are replaced with input ones.
ProjectInput,
/// Output projection : input labels are replaced with output ones.
ProjectOutput,
}
/// This operation projects an FST onto its domain or range by either copying
/// each transition input label to its output label or vice versa.
/// # Example 1
///
/// ## Project input
/// ```
/// # #[macro_use] extern crate rustfst;
/// # use anyhow::Result;
/// # use rustfst::utils::{acceptor, transducer};
/// # use rustfst::semirings::{Semiring, IntegerWeight};
/// # use rustfst::fst_impls::VectorFst;
/// # use rustfst::algorithms::{project, ProjectType};
/// # fn main() -> Result<()> {
/// let mut fst : VectorFst<IntegerWeight> = fst![2 => 3];
/// project(&mut fst, ProjectType::ProjectInput);
/// assert_eq!(fst, fst![2]);
/// # Ok(())
/// # }
/// ```
///
/// ## Project output
///
/// ```rust
/// # #[macro_use] extern crate rustfst;
/// # use anyhow::Result;
/// # use rustfst::utils::{acceptor, transducer};
/// # use rustfst::semirings::{Semiring, IntegerWeight};
/// # use rustfst::fst_impls::VectorFst;
/// # use rustfst::algorithms::{project, ProjectType};
/// # fn main() -> Result<()> {
/// let mut fst : VectorFst<IntegerWeight> = fst![2 => 3];
/// project(&mut fst, ProjectType::ProjectOutput);
/// assert_eq!(fst, fst![3]);
/// # Ok(())
/// # }
/// ```
///
/// # Example 2
///
/// ## Input
///
/// ![project_in](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/project_in.svg?sanitize=true)
///
/// ## Project input
///
/// ![project_out_project-input](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/project_out_project-input.svg?sanitize=true)
///
/// ## Project output
///
/// ![project_out_project-input](https://raw.githubusercontent.com/Garvys/rustfst-images-doc/master/images/project_out_project-output.svg?sanitize=true)
pub fn project<W: Semiring, F: MutableFst<W>>(fst: &mut F, project_type: ProjectType) {
let props = fst.properties();
match project_type {
ProjectType::ProjectInput => {
for state in fst.states_range() {
let mut it_trs = unsafe { fst.tr_iter_unchecked_mut(state) };
for idx_tr in 0..it_trs.len() {
let tr = unsafe { it_trs.get_unchecked(idx_tr) };
let ilabel = tr.ilabel;
unsafe { it_trs.set_olabel_unchecked(idx_tr, ilabel) }
}
}
}
ProjectType::ProjectOutput => {
for state in fst.states_range() {
let mut it_trs = unsafe { fst.tr_iter_unchecked_mut(state) };
for idx_tr in 0..it_trs.len() {
let tr = unsafe { it_trs.get_unchecked(idx_tr) };
let olabel = tr.olabel;
unsafe { it_trs.set_ilabel_unchecked(idx_tr, olabel) }
}
}
}
};
fst.set_properties_with_mask(
project_properties(props, project_type),
FstProperties::all_properties(),
);
}
#[cfg(test)]
mod tests {
use ::proptest::prelude::*;
use crate::fst_properties::FstProperties;
use crate::fst_traits::CoreFst;
use crate::prelude::*;
use super::*;
proptest! {
#[test]
fn test_project_input_proptest(mut fst in any::<VectorFst<TropicalWeight>>()) {
project(&mut fst, ProjectType::ProjectInput);
prop_assert!(fst.properties().intersects(FstProperties::ACCEPTOR));
}
}
proptest! {
#[test]
fn test_project_output_proptest(mut fst in any::<VectorFst<TropicalWeight>>()) {
project(&mut fst, ProjectType::ProjectOutput);
prop_assert!(fst.properties().intersects(FstProperties::ACCEPTOR));
}
}
}