Skip to main content

fugue_evo/genome/
permutation.rs

1//! Permutation genome
2//!
3//! This module provides a permutation genome type for ordering problems
4//! (e.g., TSP, scheduling) with Fugue trace integration.
5
6use fugue::{addr, ChoiceValue, Trace};
7use rand::seq::SliceRandom;
8use rand::Rng;
9use serde::{Deserialize, Serialize};
10
11use crate::error::GenomeError;
12use crate::genome::bounds::MultiBounds;
13use crate::genome::traits::{EvolutionaryGenome, PermutationGenome};
14
15/// Permutation genome for ordering problems
16///
17/// Represents a permutation of indices 0..n, commonly used for:
18/// - Traveling Salesman Problem (TSP)
19/// - Job Shop Scheduling
20/// - Vehicle Routing Problems
21/// - Any problem where the solution is an ordering of elements
22#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
23pub struct Permutation {
24    /// The permutation (indices 0..n in some order)
25    perm: Vec<usize>,
26}
27
28impl Permutation {
29    /// Create a new permutation from a vector of indices
30    ///
31    /// # Panics
32    /// Panics if the input is not a valid permutation of 0..n
33    pub fn new(perm: Vec<usize>) -> Self {
34        let result = Self { perm };
35        assert!(
36            result.is_valid_permutation(),
37            "Input must be a valid permutation of 0..n"
38        );
39        result
40    }
41
42    /// Create a permutation from a vector without validation
43    ///
44    /// # Safety
45    /// The caller must ensure the input is a valid permutation of 0..n
46    pub fn new_unchecked(perm: Vec<usize>) -> Self {
47        Self { perm }
48    }
49
50    /// Try to create a permutation, returning an error if invalid
51    pub fn try_new(perm: Vec<usize>) -> Result<Self, GenomeError> {
52        let result = Self { perm };
53        if result.is_valid_permutation() {
54            Ok(result)
55        } else {
56            Err(GenomeError::InvalidStructure(
57                "Input is not a valid permutation of 0..n".to_string(),
58            ))
59        }
60    }
61
62    /// Create the identity permutation [0, 1, 2, ..., n-1]
63    pub fn identity(n: usize) -> Self {
64        Self {
65            perm: (0..n).collect(),
66        }
67    }
68
69    /// Create a random permutation of size n
70    pub fn random<R: Rng>(n: usize, rng: &mut R) -> Self {
71        let mut perm: Vec<usize> = (0..n).collect();
72        perm.shuffle(rng);
73        Self { perm }
74    }
75
76    /// Get the length of the permutation
77    pub fn len(&self) -> usize {
78        self.perm.len()
79    }
80
81    /// Check if the permutation is empty
82    pub fn is_empty(&self) -> bool {
83        self.perm.is_empty()
84    }
85
86    /// Get the element at index i
87    pub fn get(&self, i: usize) -> Option<usize> {
88        self.perm.get(i).copied()
89    }
90
91    /// Get the inverse permutation
92    ///
93    /// If `perm[i] = j`, then `inverse[j] = i`
94    pub fn inverse(&self) -> Self {
95        let n = self.perm.len();
96        let mut inv = vec![0; n];
97        for (i, &j) in self.perm.iter().enumerate() {
98            inv[j] = i;
99        }
100        Self { perm: inv }
101    }
102
103    /// Compose this permutation with another
104    ///
105    /// Returns a permutation where `result[i] = other[self[i]]`
106    pub fn compose(&self, other: &Self) -> Result<Self, GenomeError> {
107        if self.perm.len() != other.perm.len() {
108            return Err(GenomeError::DimensionMismatch {
109                expected: self.perm.len(),
110                actual: other.perm.len(),
111            });
112        }
113        let composed: Vec<usize> = self.perm.iter().map(|&i| other.perm[i]).collect();
114        Ok(Self { perm: composed })
115    }
116
117    /// Swap two elements at positions i and j
118    pub fn swap(&mut self, i: usize, j: usize) {
119        self.perm.swap(i, j);
120    }
121
122    /// Reverse a segment from start to end (inclusive)
123    pub fn reverse_segment(&mut self, start: usize, end: usize) {
124        if start < end && end < self.perm.len() {
125            self.perm[start..=end].reverse();
126        }
127    }
128
129    /// Insert element at position `from` to position `to`
130    pub fn insert(&mut self, from: usize, to: usize) {
131        if from == to || from >= self.perm.len() || to >= self.perm.len() {
132            return;
133        }
134        let elem = self.perm.remove(from);
135        self.perm.insert(to, elem);
136    }
137
138    /// Calculate the number of inversions (disorder measure)
139    ///
140    /// An inversion is a pair (i, j) where i < j but `perm[i] > perm[j]`.
141    /// Returns a value in `[0, n*(n-1)/2]` where 0 means sorted.
142    pub fn inversions(&self) -> usize {
143        let n = self.perm.len();
144        let mut count = 0;
145        for i in 0..n {
146            for j in (i + 1)..n {
147                if self.perm[i] > self.perm[j] {
148                    count += 1;
149                }
150            }
151        }
152        count
153    }
154
155    /// Calculate Kendall tau distance to another permutation
156    ///
157    /// Counts the number of pairwise disagreements (i.e., pairs that are
158    /// in different order in the two permutations).
159    pub fn kendall_tau_distance(&self, other: &Self) -> Result<usize, GenomeError> {
160        if self.perm.len() != other.perm.len() {
161            return Err(GenomeError::DimensionMismatch {
162                expected: self.perm.len(),
163                actual: other.perm.len(),
164            });
165        }
166
167        // Compose with inverse of other to get relative order
168        let other_inv = other.inverse();
169        let composed = self.compose(&other_inv)?;
170
171        // Count inversions in the composed permutation
172        Ok(composed.inversions())
173    }
174
175    /// Check if this is a cyclic permutation (single cycle)
176    pub fn is_cyclic(&self) -> bool {
177        if self.perm.is_empty() {
178            return true;
179        }
180
181        let n = self.perm.len();
182        let mut visited = vec![false; n];
183        let mut current = 0;
184        let mut cycle_len = 0;
185
186        while !visited[current] {
187            visited[current] = true;
188            current = self.perm[current];
189            cycle_len += 1;
190        }
191
192        cycle_len == n && current == 0
193    }
194
195    /// Get the underlying vector
196    pub fn into_inner(self) -> Vec<usize> {
197        self.perm
198    }
199
200    /// Get a reference to the underlying slice
201    pub fn as_slice(&self) -> &[usize] {
202        &self.perm
203    }
204}
205
206impl EvolutionaryGenome for Permutation {
207    type Allele = usize;
208    type Phenotype = Vec<usize>;
209
210    /// Convert Permutation to Fugue trace.
211    ///
212    /// Each position is stored at address "perm#i" where i is the index.
213    fn to_trace(&self) -> Trace {
214        let mut trace = Trace::default();
215        for (i, &val) in self.perm.iter().enumerate() {
216            trace.insert_choice(addr!("perm", i), ChoiceValue::Usize(val), 0.0);
217        }
218        trace
219    }
220
221    /// Reconstruct Permutation from Fugue trace.
222    ///
223    /// Reads values from addresses "perm#0", "perm#1", ... until no more are found.
224    fn from_trace(trace: &Trace) -> Result<Self, GenomeError> {
225        let mut perm = Vec::new();
226        let mut i = 0;
227        while let Some(val) = trace.get_usize(&addr!("perm", i)) {
228            perm.push(val);
229            i += 1;
230        }
231        if perm.is_empty() {
232            return Err(GenomeError::InvalidStructure(
233                "No permutation found in trace".to_string(),
234            ));
235        }
236        Self::try_new(perm)
237    }
238
239    fn decode(&self) -> Self::Phenotype {
240        self.perm.clone()
241    }
242
243    fn dimension(&self) -> usize {
244        self.perm.len()
245    }
246
247    fn generate<R: Rng>(rng: &mut R, bounds: &MultiBounds) -> Self {
248        let n = bounds.dimension();
249        Self::random(n, rng)
250    }
251
252    fn distance(&self, other: &Self) -> f64 {
253        self.kendall_tau_distance(other).unwrap_or(0) as f64
254    }
255
256    fn trace_prefix() -> &'static str {
257        "perm"
258    }
259}
260
261impl PermutationGenome for Permutation {
262    fn permutation(&self) -> &[usize] {
263        &self.perm
264    }
265
266    fn permutation_mut(&mut self) -> &mut [usize] {
267        &mut self.perm
268    }
269
270    fn from_permutation(perm: Vec<usize>) -> Result<Self, GenomeError> {
271        Self::try_new(perm)
272    }
273}
274
275impl std::ops::Index<usize> for Permutation {
276    type Output = usize;
277
278    fn index(&self, index: usize) -> &Self::Output {
279        &self.perm[index]
280    }
281}
282
283impl From<Vec<usize>> for Permutation {
284    fn from(perm: Vec<usize>) -> Self {
285        Self::new(perm)
286    }
287}
288
289impl From<Permutation> for Vec<usize> {
290    fn from(p: Permutation) -> Self {
291        p.perm
292    }
293}
294
295impl IntoIterator for Permutation {
296    type Item = usize;
297    type IntoIter = std::vec::IntoIter<usize>;
298
299    fn into_iter(self) -> Self::IntoIter {
300        self.perm.into_iter()
301    }
302}
303
304impl<'a> IntoIterator for &'a Permutation {
305    type Item = &'a usize;
306    type IntoIter = std::slice::Iter<'a, usize>;
307
308    fn into_iter(self) -> Self::IntoIter {
309        self.perm.iter()
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::genome::traits::PermutationGenome;
317
318    #[test]
319    fn test_permutation_new() {
320        let p = Permutation::new(vec![2, 0, 1, 3]);
321        assert_eq!(p.len(), 4);
322        assert_eq!(p[0], 2);
323        assert_eq!(p[1], 0);
324    }
325
326    #[test]
327    #[should_panic(expected = "valid permutation")]
328    fn test_permutation_new_invalid_duplicate() {
329        Permutation::new(vec![0, 1, 1, 3]);
330    }
331
332    #[test]
333    #[should_panic(expected = "valid permutation")]
334    fn test_permutation_new_invalid_out_of_range() {
335        Permutation::new(vec![0, 1, 5, 3]);
336    }
337
338    #[test]
339    fn test_permutation_try_new() {
340        assert!(Permutation::try_new(vec![2, 0, 1, 3]).is_ok());
341        assert!(Permutation::try_new(vec![0, 1, 1, 3]).is_err());
342    }
343
344    #[test]
345    fn test_permutation_identity() {
346        let p = Permutation::identity(5);
347        assert_eq!(p.as_slice(), &[0, 1, 2, 3, 4]);
348    }
349
350    #[test]
351    fn test_permutation_random() {
352        let mut rng = rand::thread_rng();
353        let p = Permutation::random(10, &mut rng);
354        assert!(p.is_valid_permutation());
355        assert_eq!(p.len(), 10);
356    }
357
358    #[test]
359    fn test_permutation_inverse() {
360        let p = Permutation::new(vec![2, 0, 3, 1]);
361        let inv = p.inverse();
362        // If p[i] = j, then inv[j] = i
363        // p[0]=2 -> inv[2]=0, p[1]=0 -> inv[0]=1, p[2]=3 -> inv[3]=2, p[3]=1 -> inv[1]=3
364        assert_eq!(inv.as_slice(), &[1, 3, 0, 2]);
365
366        // Composing with inverse should give identity
367        let composed = p.compose(&inv).unwrap();
368        assert_eq!(composed.as_slice(), &[0, 1, 2, 3]);
369    }
370
371    #[test]
372    fn test_permutation_compose() {
373        let p1 = Permutation::new(vec![1, 2, 0]);
374        let p2 = Permutation::new(vec![2, 0, 1]);
375        let composed = p1.compose(&p2).unwrap();
376        // composed[i] = p2[p1[i]]
377        // composed[0] = p2[1] = 0, composed[1] = p2[2] = 1, composed[2] = p2[0] = 2
378        assert_eq!(composed.as_slice(), &[0, 1, 2]);
379    }
380
381    #[test]
382    fn test_permutation_swap() {
383        let mut p = Permutation::new(vec![0, 1, 2, 3]);
384        p.swap(0, 3);
385        assert_eq!(p.as_slice(), &[3, 1, 2, 0]);
386    }
387
388    #[test]
389    fn test_permutation_reverse_segment() {
390        let mut p = Permutation::new(vec![0, 1, 2, 3, 4]);
391        p.reverse_segment(1, 3);
392        assert_eq!(p.as_slice(), &[0, 3, 2, 1, 4]);
393    }
394
395    #[test]
396    fn test_permutation_insert() {
397        let mut p = Permutation::new(vec![0, 1, 2, 3, 4]);
398        p.insert(1, 4);
399        assert_eq!(p.as_slice(), &[0, 2, 3, 4, 1]);
400    }
401
402    #[test]
403    fn test_permutation_inversions() {
404        // Sorted: 0 inversions
405        let p1 = Permutation::identity(5);
406        assert_eq!(p1.inversions(), 0);
407
408        // Reversed: n*(n-1)/2 inversions
409        let p2 = Permutation::new(vec![4, 3, 2, 1, 0]);
410        assert_eq!(p2.inversions(), 10); // 5*4/2 = 10
411
412        // Single swap: 1 inversion
413        let p3 = Permutation::new(vec![1, 0, 2, 3, 4]);
414        assert_eq!(p3.inversions(), 1);
415    }
416
417    #[test]
418    fn test_permutation_kendall_tau() {
419        let p1 = Permutation::new(vec![0, 1, 2, 3]);
420        let p2 = Permutation::new(vec![0, 1, 2, 3]);
421        assert_eq!(p1.kendall_tau_distance(&p2).unwrap(), 0);
422
423        let p3 = Permutation::new(vec![0, 1, 3, 2]);
424        assert_eq!(p1.kendall_tau_distance(&p3).unwrap(), 1);
425
426        let p4 = Permutation::new(vec![3, 2, 1, 0]);
427        assert_eq!(p1.kendall_tau_distance(&p4).unwrap(), 6);
428    }
429
430    #[test]
431    fn test_permutation_is_cyclic() {
432        // Single cycle (3 -> 1 -> 2 -> 0 -> 3)
433        let cyclic = Permutation::new(vec![3, 2, 0, 1]);
434        // Let's trace: 0 -> 3 -> 1 -> 2 -> 0, that's 4 elements in one cycle
435        assert!(cyclic.is_cyclic());
436
437        // Not a single cycle: identity has n fixed points (1-cycles)
438        let identity = Permutation::identity(4);
439        assert!(!identity.is_cyclic()); // Each element maps to itself
440
441        // Empty is trivially cyclic
442        let empty = Permutation::identity(0);
443        assert!(empty.is_cyclic());
444    }
445
446    #[test]
447    fn test_permutation_decode() {
448        let p = Permutation::new(vec![2, 0, 1]);
449        assert_eq!(p.decode(), vec![2, 0, 1]);
450    }
451
452    #[test]
453    fn test_permutation_dimension() {
454        let p = Permutation::new(vec![2, 0, 1, 3, 4]);
455        assert_eq!(p.dimension(), 5);
456    }
457
458    #[test]
459    fn test_permutation_generate() {
460        let mut rng = rand::thread_rng();
461        let bounds = MultiBounds::symmetric(1.0, 10);
462        let p = Permutation::generate(&mut rng, &bounds);
463        assert_eq!(p.dimension(), 10);
464        assert!(p.is_valid_permutation());
465    }
466
467    #[test]
468    fn test_permutation_distance() {
469        let p1 = Permutation::new(vec![0, 1, 2, 3]);
470        let p2 = Permutation::new(vec![3, 2, 1, 0]);
471        assert_eq!(p1.distance(&p2), 6.0);
472    }
473
474    #[test]
475    fn test_permutation_to_trace() {
476        let p = Permutation::new(vec![2, 0, 1]);
477        let trace = p.to_trace();
478
479        assert_eq!(trace.get_usize(&addr!("perm", 0)), Some(2));
480        assert_eq!(trace.get_usize(&addr!("perm", 1)), Some(0));
481        assert_eq!(trace.get_usize(&addr!("perm", 2)), Some(1));
482        assert_eq!(trace.get_usize(&addr!("perm", 3)), None);
483    }
484
485    #[test]
486    fn test_permutation_from_trace() {
487        let mut trace = Trace::default();
488        trace.insert_choice(addr!("perm", 0), ChoiceValue::Usize(1), 0.0);
489        trace.insert_choice(addr!("perm", 1), ChoiceValue::Usize(2), 0.0);
490        trace.insert_choice(addr!("perm", 2), ChoiceValue::Usize(0), 0.0);
491
492        let p = Permutation::from_trace(&trace).unwrap();
493        assert_eq!(p.as_slice(), &[1, 2, 0]);
494    }
495
496    #[test]
497    fn test_permutation_trace_roundtrip() {
498        let original = Permutation::new(vec![4, 2, 0, 3, 1]);
499        let trace = original.to_trace();
500        let recovered = Permutation::from_trace(&trace).unwrap();
501        assert_eq!(original, recovered);
502    }
503
504    #[test]
505    fn test_permutation_from_trace_invalid() {
506        let mut trace = Trace::default();
507        trace.insert_choice(addr!("perm", 0), ChoiceValue::Usize(0), 0.0);
508        trace.insert_choice(addr!("perm", 1), ChoiceValue::Usize(0), 0.0); // duplicate!
509
510        let result = Permutation::from_trace(&trace);
511        assert!(result.is_err());
512    }
513
514    #[test]
515    fn test_permutation_from_trace_empty() {
516        let trace = Trace::default();
517        let result = Permutation::from_trace(&trace);
518        assert!(result.is_err());
519    }
520
521    #[test]
522    fn test_permutation_serialization() {
523        let p = Permutation::new(vec![2, 0, 1, 3]);
524        let serialized = serde_json::to_string(&p).unwrap();
525        let deserialized: Permutation = serde_json::from_str(&serialized).unwrap();
526        assert_eq!(p, deserialized);
527    }
528
529    #[test]
530    fn test_permutation_iteration() {
531        let p = Permutation::new(vec![2, 0, 1]);
532        let collected: Vec<usize> = p.into_iter().collect();
533        assert_eq!(collected, vec![2, 0, 1]);
534    }
535
536    #[test]
537    fn test_permutation_ref_iteration() {
538        let p = Permutation::new(vec![2, 0, 1]);
539        let sum: usize = p.into_iter().sum();
540        assert_eq!(sum, 3);
541    }
542
543    #[test]
544    fn test_permutation_into_inner() {
545        let p = Permutation::new(vec![2, 0, 1]);
546        let v: Vec<usize> = p.into_inner();
547        assert_eq!(v, vec![2, 0, 1]);
548    }
549
550    #[test]
551    fn test_permutation_from_vec() {
552        let p: Permutation = vec![1, 0, 2].into();
553        assert_eq!(p.as_slice(), &[1, 0, 2]);
554    }
555}