rustfst/utils/
labels_to_fst.rs

1use std::cmp;
2
3use crate::fst_traits::MutableFst;
4use crate::semirings::Semiring;
5use crate::tr::Tr;
6use crate::Label;
7
8/// Turns a list of input labels and output labels into a linear FST.
9/// The only accepted path in the FST has for input `labels_input` and for output `labels_output`.
10///
11/// # Example
12///
13/// ```
14/// # use rustfst::fst_traits::{CoreFst, MutableFst, ExpandedFst};
15/// # use rustfst::fst_impls::VectorFst;
16/// # use rustfst::semirings::{ProbabilityWeight, Semiring};
17/// # use rustfst::utils::transducer;
18/// # use rustfst::Tr;
19/// let labels_input = vec![32, 43, 21];
20/// let labels_output = vec![53, 18, 89];
21///
22/// let fst : VectorFst<ProbabilityWeight> = transducer(&labels_input, &labels_output, ProbabilityWeight::one());
23///
24/// assert_eq!(fst.num_states(), 4);
25///
26/// // The transducer function produces the same FST as the following code
27///
28/// let mut fst_ref = VectorFst::new();
29/// let s1 = fst_ref.add_state();
30/// let s2 = fst_ref.add_state();
31/// let s3 = fst_ref.add_state();
32/// let s4 = fst_ref.add_state();
33///
34/// fst_ref.set_start(s1).unwrap();
35/// fst_ref.set_final(s4, ProbabilityWeight::one()).unwrap();
36///
37/// fst_ref.add_tr(s1, Tr::new(labels_input[0], labels_output[0], ProbabilityWeight::one(), s2)).unwrap();
38/// fst_ref.add_tr(s2, Tr::new(labels_input[1], labels_output[1], ProbabilityWeight::one(), s3)).unwrap();
39/// fst_ref.add_tr(s3, Tr::new(labels_input[2], labels_output[2], ProbabilityWeight::one(), s4)).unwrap();
40///
41/// assert_eq!(fst, fst_ref);
42/// ```
43pub fn transducer<W: Semiring, F: MutableFst<W>>(
44    labels_input: &[Label],
45    labels_output: &[Label],
46    weight: W,
47) -> F {
48    let max_size = cmp::max(labels_input.len(), labels_output.len());
49
50    let mut fst = F::new();
51    let mut state_cour = fst.add_state();
52
53    // Can't fail as the state has just been added
54    fst.set_start(state_cour).unwrap();
55
56    for idx in 0..max_size {
57        let i = labels_input.get(idx).unwrap_or(&0);
58        let o = labels_output.get(idx).unwrap_or(&0);
59
60        let new_state = fst.add_state();
61
62        // Can't fail as the state has just been added
63        fst.add_tr(state_cour, Tr::new(*i, *o, W::one(), new_state))
64            .unwrap();
65
66        state_cour = new_state;
67    }
68
69    // Can't fail as the state has just been added
70    fst.set_final(state_cour, weight).unwrap();
71
72    fst
73}
74
75/// Turns a list of labels into a linear acceptor (FST with the same labels for both input and output).
76/// The only accepted path in the acceptor will be `labels`.
77///
78/// # Example
79///
80/// ```
81/// use rustfst::fst_traits::{CoreFst, MutableFst, ExpandedFst};
82/// use rustfst::fst_impls::VectorFst;
83/// use rustfst::semirings::{ProbabilityWeight, Semiring};
84/// use rustfst::utils::acceptor;
85/// use rustfst::Tr;
86///
87/// let labels = vec![32, 43, 21];
88///
89/// let fst : VectorFst<ProbabilityWeight> = acceptor(&labels, ProbabilityWeight::one());
90///
91/// assert_eq!(fst.num_states(), 4);
92///
93/// // The acceptor function produces the same FST as the following code
94///
95/// let mut fst_ref = VectorFst::new();
96/// let s1 = fst_ref.add_state();
97/// let s2 = fst_ref.add_state();
98/// let s3 = fst_ref.add_state();
99/// let s4 = fst_ref.add_state();
100///
101/// fst_ref.set_start(s1).unwrap();
102/// fst_ref.set_final(s4, ProbabilityWeight::one()).unwrap();
103///
104/// fst_ref.add_tr(s1, Tr::new(labels[0], labels[0], ProbabilityWeight::one(), s2)).unwrap();
105/// fst_ref.add_tr(s2, Tr::new(labels[1], labels[1], ProbabilityWeight::one(), s3)).unwrap();
106/// fst_ref.add_tr(s3, Tr::new(labels[2], labels[2], ProbabilityWeight::one(), s4)).unwrap();
107///
108/// assert_eq!(fst, fst_ref);
109///
110/// ```
111pub fn acceptor<W: Semiring, F: MutableFst<W>>(labels: &[Label], weight: W) -> F {
112    let mut fst = F::new();
113    let mut state_cour = fst.add_state();
114
115    // Can't fail as the state has just been added
116    fst.set_start(state_cour).unwrap();
117
118    for l in labels {
119        let new_state = fst.add_state();
120
121        // Can't fail as the state has just been added
122        fst.add_tr(state_cour, Tr::new(*l, *l, W::one(), new_state))
123            .unwrap();
124        state_cour = new_state;
125    }
126
127    // Can't fail as the state has just been added
128    fst.set_final(state_cour, weight).unwrap();
129
130    fst
131}
132
133/// Creates a linear Fst containing the arguments.
134///
135/// There are multiple forms to this macro :
136///
137/// - Create an unweighted linear acceptor :
138///
139/// This will return a linear FST with one transition for each label given
140/// (same input and output, weight one).
141///
142/// ```
143/// # #[macro_use] extern crate rustfst; fn main() {
144/// # use rustfst::utils;
145/// # use rustfst::fst_traits::{CoreFst, MutableFst, ExpandedFst, Fst};
146/// # use rustfst::fst_impls::VectorFst;
147/// # use rustfst::semirings::{ProbabilityWeight, Semiring};
148/// # use rustfst::utils::acceptor;
149/// # use rustfst::{Tr, FstPath};
150/// let fst : VectorFst<ProbabilityWeight> = fst![1,2,3];
151/// assert_eq!(fst.paths_iter().count(), 1);
152/// assert_eq!(fst.paths_iter().next().unwrap(), fst_path![1,2,3]);
153/// # }
154/// ```
155///
156/// - Create an unweighted linear transducer from two list of labels :
157///
158/// The only accepted path in the FST has for input the first
159/// list of labels and for output the second list of labels.
160///
161/// ```
162/// # #[macro_use] extern crate rustfst; fn main() {
163/// # use rustfst::utils;
164/// # use rustfst::fst_traits::{CoreFst, MutableFst, ExpandedFst, Fst};
165/// # use rustfst::fst_impls::VectorFst;
166/// # use rustfst::semirings::{ProbabilityWeight, Semiring};
167/// # use rustfst::utils::transducer;
168/// # use rustfst::{Tr, FstPath};
169/// let fst : VectorFst<ProbabilityWeight> = fst![1,2,3 => 1,2,4];
170/// assert_eq!(fst.paths_iter().count(), 1);
171/// assert_eq!(fst.paths_iter().next().unwrap(), fst_path![1,2,3 => 1,2,4]);
172/// # }
173/// ```
174///
175/// - Create a weighted linear acceptor :
176///
177/// This will return a linear FST with one transition for each label given
178/// (same input and output, weight one).
179///
180/// ```
181/// # #[macro_use] extern crate rustfst; fn main() {
182/// # use rustfst::utils;
183/// # use rustfst::fst_traits::{CoreFst, MutableFst, ExpandedFst, Fst};
184/// # use rustfst::fst_impls::VectorFst;
185/// # use rustfst::semirings::{ProbabilityWeight, Semiring};
186/// # use rustfst::utils::acceptor;
187/// # use rustfst::{Tr, FstPath};
188/// let fst : VectorFst<ProbabilityWeight> = fst![1,2,3; 0.2];
189/// assert_eq!(fst.paths_iter().count(), 1);
190/// assert_eq!(fst.paths_iter().next().unwrap(), fst_path![1,2,3; 0.2]);
191/// # }
192/// ```
193///
194/// - Create a weighted linear transducer from two list of labels and a weight :
195///
196/// The only accepted path in the FST has for input the first
197/// list of labels and for output the second list of labels.
198///
199/// ```
200/// # #[macro_use] extern crate rustfst; fn main() {
201/// # use rustfst::utils;
202/// # use rustfst::fst_traits::{CoreFst, MutableFst, ExpandedFst, Fst};
203/// # use rustfst::fst_impls::VectorFst;
204/// # use rustfst::semirings::{ProbabilityWeight, Semiring};
205/// # use rustfst::utils::transducer;
206/// # use rustfst::{Tr, FstPath};
207/// let fst : VectorFst<ProbabilityWeight> = fst![1,2,3 => 1,2,4; 0.2];
208/// assert_eq!(fst.paths_iter().count(), 1);
209/// assert_eq!(fst.paths_iter().next().unwrap(), fst_path![1,2,3 => 1,2,4; 0.2]);
210/// # }
211/// ```
212///
213#[macro_export]
214macro_rules! fst {
215    ( $( $x:expr ),* ) => {
216        {
217            fn semiring_one<W: Semiring>() -> W {
218                W::one()
219            }
220            acceptor(
221                &[$($x),*],
222                semiring_one()
223            )
224        }
225    };
226    ( $( $x:expr ),* => $( $y:expr ),* ) => {
227        {
228            fn semiring_one<W: Semiring>() -> W {
229                W::one()
230            }
231            transducer(
232                &[$($x),*],
233                &[$($y),*],
234                semiring_one()
235            )
236        }
237    };
238    ( $( $x:expr ),* ; $weight:expr ) => {
239        {
240            fn semiring_new<W: Semiring>(v: W::Type) -> W {
241                W::new(v)
242            }
243            acceptor(
244                &[$($x),*],
245                semiring_new($weight)
246            )
247        }
248    };
249    ( $( $x:expr ),* => $( $y:expr ),* ; $weight:expr ) => {
250        {
251            fn semiring_new<W: Semiring>(v: W::Type) -> W {
252                W::new(v)
253            }
254            transducer(
255                &[$($x),*],
256                &[$($y),*],
257                semiring_new($weight)
258            )
259        }
260    };
261}