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}