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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
//! State minimization for weighted finite-state transducers.
//!
//! This module implements FST minimization using Brzozowski's double-reversal algorithm,
//! producing the unique canonical minimal automaton for a given weighted language.
//!
//! # Overview
//!
//! Minimization finds the smallest FST that recognizes the same weighted language:
//!
//! ```text
//! minimize(A) = A_min where |Q_min| is minimal and L(A_min) = L(A)
//! ```
//!
//! # Complexity
//!
//! | Case | Time | Space |
//! |------|------|-------|
//! | Worst | $`O(2^V)`$ | $`O(2^V)`$ |
//! | Typical | $`O(V + E)`$ | $`O(V + E)`$ |
//!
//! The exponential case occurs when reversal creates extensive nondeterminism,
//! which is rare for FSTs from practical applications.
//!
//! # Algorithm
//!
//! Brzozowski's minimization (1962):
//!
//! ```text
//! minimize(A) = det(rev(det(rev(A))))
//! ```
//!
//! 1. **Reverse:** Swap initial/final states, reverse all arcs
//! 2. **Determinize:** Merge states with identical suffix languages
//! 3. **Reverse:** Swap initial/final states again
//! 4. **Determinize:** Merge states with identical prefix languages
//! 5. **Connect:** Remove unreachable and non-coaccessible states
//!
//! The key insight is that determinization of a reversed automaton merges
//! states that are equivalent with respect to suffix languages, and the
//! double application achieves canonical minimization.
//!
//! # Alternative: Hopcroft's Algorithm
//!
//! For guaranteed $`O(n \log n)`$ performance, see [`minimize_hopcroft`].
//! Brzozowski's algorithm is often faster in practice but has worse worst-case
//! complexity.
//!
//! # Applications
//!
//! - **Memory optimization:** Reduce FST size before deployment
//! - **Compilation:** Final optimization step in FST construction
//! - **Composition:** Smaller FSTs compose more efficiently
//! - **Comparison:** Minimal FSTs enable canonical comparison
//!
//! # Example
//!
//! ```rust
//! use arcweight::prelude::*;
//!
//! 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::one());
//! fst.add_arc(s0, Arc::new(1, 1, TropicalWeight::one(), s1));
//! fst.add_arc(s1, Arc::new(2, 2, TropicalWeight::one(), s2));
//!
//! let minimized: VectorFst<TropicalWeight> = minimize(&fst)?;
//! println!("Reduced from {} to {} states", fst.num_states(), minimized.num_states());
//! # Ok::<(), arcweight::Error>(())
//! ```
//!
//! # References
//!
//! \[1\] Brzozowski, J. A. 1962. Canonical regular expressions and minimal state
//! graphs for definite events. In *Proceedings of the Symposium on Mathematical
//! Theory of Automata*, Vol. 12. Polytechnic Press, Brooklyn, NY, 529-561.
//!
//! \[2\] Mohri, M. 2009. Weighted automata algorithms. In *Handbook of Weighted
//! Automata*, M. Droste, W. Kuich, and H. Vogler, Eds. Springer, 213-254.
//! <https://doi.org/10.1007/978-3-642-01492-5_6>
//!
//! \[3\] Watson, B. W. 1995. *Taxonomies and Toolkits of Regular Language Algorithms*.
//! Ph.D. thesis, Eindhoven University of Technology.
//!
//! [`minimize_hopcroft`]: crate::algorithms::minimize_hopcroft()
use crate;
use crate;
use crateDivisibleSemiring;
use crateResult;
use Hash;
/// Minimize a deterministic FST to canonical minimal form
///
/// Reduces the FST to the minimum number of states while preserving the accepted
/// weighted language. Uses Brzozowski's algorithm which is guaranteed to produce
/// the unique canonical minimal FST for any regular language.
///
/// Requires [`DivisibleSemiring`] for weight normalization during internal determinization.
/// Works on both deterministic and nondeterministic FSTs (non-deterministic inputs are
/// determinized as part of the algorithm).
///
/// # Complexity
///
/// - **Time:** O(2^V) worst case, O(V + E) typical case
/// - V = number of states in input FST
/// - E = number of arcs in input FST
/// - Dominated by four determinization steps
/// - Worst case: exponential subset construction (rare in practice)
/// - Typical case: near-linear with sparse nondeterminism
/// - **Space:** O(2^V) for subset storage during determinization
/// - Temporary FSTs created at each step
/// - Peak memory: largest intermediate determinized FST
///
/// # Algorithm
///
/// Brzozowski's minimization (1962):
/// 1. **Reverse:** Swap initial and final states, reverse all arcs
/// 2. **Determinize:** Merge states with identical suffixes
/// 3. **Reverse:** Swap initial and final states again
/// 4. **Determinize:** Merge states with identical prefixes
/// 5. **Connect:** Remove unreachable and non-coaccessible states
///
/// **Key insight:** Double reversal + determinization merges all equivalent states,
/// producing the unique minimal automaton.
///
/// # Performance Notes
///
/// - **Deterministic input:** Much faster when input is already deterministic
/// - **Size reduction:** Effectiveness depends on redundancy in original FST
/// - **Memory usage:** Creates four intermediate FSTs (reverse, det, reverse, det)
/// - **Alternative algorithms:** Direct minimization (Hopcroft, Moore) may be faster for special cases
/// - **Preprocessing:** Consider [`connect`] before minimization to remove dead states
/// - **Best for:** FSTs with significant redundancy (post-union, post-concatenation)
///
/// # Examples
///
/// ## Basic Minimization
///
/// ```rust
/// use arcweight::prelude::*;
///
/// // Create an FST with redundant states that accept "ab"
/// 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(); // Redundant state
/// let s4 = fst.add_state(); // Redundant state
///
/// fst.set_start(s0);
/// fst.set_final(s2, TropicalWeight::one());
/// fst.set_final(s4, TropicalWeight::one()); // Same language as s2
///
/// // Create redundant paths
/// fst.add_arc(s0, Arc::new('a' as u32, 'a' as u32, TropicalWeight::one(), s1));
/// fst.add_arc(s1, Arc::new('b' as u32, 'b' as u32, TropicalWeight::one(), s2));
///
/// // Redundant path with same language
/// fst.add_arc(s0, Arc::new('a' as u32, 'a' as u32, TropicalWeight::one(), s3));
/// fst.add_arc(s3, Arc::new('b' as u32, 'b' as u32, TropicalWeight::one(), s4));
///
/// // Minimize merges equivalent states
/// let minimized: VectorFst<TropicalWeight> = minimize(&fst)?;
///
/// println!("Original: {} states, Minimized: {} states",
/// fst.num_states(), minimized.num_states());
/// assert!(minimized.num_states() <= fst.num_states());
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// ## Optimization Pipeline
///
/// ```rust
/// use arcweight::prelude::*;
///
/// // Demonstrates a typical FST optimization pipeline with separate functions
/// // Note: No single semiring in ArcWeight implements all required traits simultaneously
///
/// // For DivisibleSemiring operations (determinization, minimization)
/// fn optimize_divisible_fst<W: DivisibleSemiring + std::hash::Hash + Eq + Ord>(
/// fst: &VectorFst<W>
/// ) -> Result<VectorFst<W>> {
/// let connected: VectorFst<W> = connect(fst)?;
/// // Note: remove_epsilons requires StarSemiring, skipped for DivisibleSemiring-only
/// let deterministic: VectorFst<W> = determinize(&connected)?;
/// minimize(&deterministic)
/// }
///
/// // For StarSemiring operations (closure, epsilon handling)
/// fn optimize_star_fst<W: StarSemiring + std::hash::Hash + Eq + Ord>(
/// fst: &VectorFst<W>
/// ) -> Result<VectorFst<W>> {
/// let connected: VectorFst<W> = connect(fst)?;
/// let no_eps: VectorFst<W> = remove_epsilons(&connected)?;
/// // Note: Cannot minimize star semirings without divisibility
/// Ok(no_eps)
/// }
///
/// // Example with TropicalWeight (DivisibleSemiring + Hash + Eq + Ord)
/// let mut tropical_fst = VectorFst::<TropicalWeight>::new();
/// let s0 = tropical_fst.add_state();
/// let s1 = tropical_fst.add_state();
/// tropical_fst.set_start(s0);
/// tropical_fst.set_final(s1, TropicalWeight::one());
/// tropical_fst.add_arc(s0, Arc::new('a' as u32, 'a' as u32, TropicalWeight::one(), s1));
///
/// let optimized_tropical = optimize_divisible_fst(&tropical_fst).unwrap();
/// assert!(optimized_tropical.num_states() > 0);
///
/// // Example with BooleanWeight (StarSemiring + Hash + Eq + Ord)
/// let mut boolean_fst = VectorFst::<BooleanWeight>::new();
/// let s0 = boolean_fst.add_state();
/// let s1 = boolean_fst.add_state();
/// boolean_fst.set_start(s0);
/// boolean_fst.set_final(s1, BooleanWeight::one());
/// boolean_fst.add_arc(s0, Arc::new('a' as u32, 'a' as u32, BooleanWeight::one(), s1));
///
/// let optimized_boolean = optimize_star_fst(&boolean_fst).unwrap();
/// assert!(optimized_boolean.num_states() > 0);
/// ```
///
/// ## Dictionary Optimization
///
/// ```rust
/// use arcweight::prelude::*;
///
/// // Simple dictionary FST minimization
/// let mut dict = VectorFst::<TropicalWeight>::new();
/// let s0 = dict.add_state();
/// let s1 = dict.add_state();
/// dict.set_start(s0);
/// dict.set_final(s1, TropicalWeight::one());
/// dict.add_arc(s0, Arc::new('a' as u32, 'a' as u32, TropicalWeight::one(), s1));
///
/// // Minimize the dictionary FST
/// let minimized: VectorFst<TropicalWeight> = minimize(&dict).unwrap();
/// println!("Original: {} states, Minimized: {} states",
/// dict.num_states(), minimized.num_states());
/// ```
///
/// # Errors
///
/// Returns [`Error::Algorithm`](crate::Error::Algorithm) if:
/// - The input FST is invalid, corrupted, or has no start state
/// - Memory allocation fails during any intermediate step
/// - The semiring doesn't support required division operations
/// - Reversal, determinization, or connection operations fail
/// - Intermediate FSTs become too large to process
///
/// # References
///
/// \[1\] Brzozowski, J. A. 1962. Canonical regular expressions and minimal state
/// graphs for definite events. *Mathematical Theory of Automata* 12, 529-561.
///
/// \[2\] Mohri, M. 2009. Weighted automata algorithms. In *Handbook of Weighted
/// Automata*, Springer, 213-254. <https://doi.org/10.1007/978-3-642-01492-5_6>
///
/// # See Also
///
/// - [`minimize_hopcroft`] - $`O(n \log n)`$ alternative using partition refinement
/// - [`determinize`] - Core operation used internally (applied twice)
/// - [`reverse`] - Reversal operation used internally (applied twice)
/// - [`connect`] - Final cleanup step to remove unreachable states
/// - [`DivisibleSemiring`] - Required trait for weight normalization
/// - [`TropicalWeight`] - Compatible semiring for shortest-path problems
/// - [`LogWeight`] - Compatible semiring for probabilistic computations
/// - [`compose`] - Often benefits from minimization preprocessing
///
/// [`minimize_hopcroft`]: crate::algorithms::minimize_hopcroft()
/// [`determinize`]: crate::algorithms::determinize::determinize
/// [`reverse`]: crate::algorithms::reverse::reverse
/// [`connect`]: crate::algorithms::connect::connect
/// [`compose`]: crate::algorithms::compose::compose
/// [`TropicalWeight`]: crate::semiring::TropicalWeight
/// [`LogWeight`]: crate::semiring::LogWeight