use fugue::{addr, ChoiceValue, Trace};
use rand::seq::SliceRandom;
use rand::Rng;
use serde::{Deserialize, Serialize};
use crate::error::GenomeError;
use crate::genome::bounds::MultiBounds;
use crate::genome::traits::{EvolutionaryGenome, PermutationGenome};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Permutation {
perm: Vec<usize>,
}
impl Permutation {
pub fn new(perm: Vec<usize>) -> Self {
let result = Self { perm };
assert!(
result.is_valid_permutation(),
"Input must be a valid permutation of 0..n"
);
result
}
pub fn from_vec_unchecked(perm: Vec<usize>) -> Self {
let result = Self { perm };
debug_assert!(
result.is_valid_permutation(),
"from_vec_unchecked called with a vector that is not a valid permutation of 0..n"
);
result
}
pub fn try_new(perm: Vec<usize>) -> Result<Self, GenomeError> {
let result = Self { perm };
if result.is_valid_permutation() {
Ok(result)
} else {
Err(GenomeError::InvalidStructure(
"Input is not a valid permutation of 0..n".to_string(),
))
}
}
pub fn identity(n: usize) -> Self {
Self {
perm: (0..n).collect(),
}
}
pub fn random<R: Rng>(n: usize, rng: &mut R) -> Self {
let mut perm: Vec<usize> = (0..n).collect();
perm.shuffle(rng);
Self { perm }
}
pub fn generate_with_len<R: Rng>(rng: &mut R, len: usize) -> Self {
Self::random(len, rng)
}
pub fn len(&self) -> usize {
self.perm.len()
}
pub fn is_empty(&self) -> bool {
self.perm.is_empty()
}
pub fn get(&self, i: usize) -> Option<usize> {
self.perm.get(i).copied()
}
pub fn inverse(&self) -> Self {
let n = self.perm.len();
let mut inv = vec![0; n];
for (i, &j) in self.perm.iter().enumerate() {
inv[j] = i;
}
Self { perm: inv }
}
pub fn compose(&self, other: &Self) -> Result<Self, GenomeError> {
if self.perm.len() != other.perm.len() {
return Err(GenomeError::DimensionMismatch {
expected: self.perm.len(),
actual: other.perm.len(),
});
}
let composed: Vec<usize> = self.perm.iter().map(|&i| other.perm[i]).collect();
Ok(Self { perm: composed })
}
pub fn swap(&mut self, i: usize, j: usize) {
self.perm.swap(i, j);
}
pub fn reverse_segment(&mut self, start: usize, end: usize) {
if start < end && end < self.perm.len() {
self.perm[start..=end].reverse();
}
}
pub fn insert(&mut self, from: usize, to: usize) {
if from == to || from >= self.perm.len() || to >= self.perm.len() {
return;
}
let elem = self.perm.remove(from);
self.perm.insert(to, elem);
}
pub fn inversions(&self) -> usize {
let n = self.perm.len();
let mut count = 0;
for i in 0..n {
for j in (i + 1)..n {
if self.perm[i] > self.perm[j] {
count += 1;
}
}
}
count
}
pub fn kendall_tau_distance(&self, other: &Self) -> Result<usize, GenomeError> {
if self.perm.len() != other.perm.len() {
return Err(GenomeError::DimensionMismatch {
expected: self.perm.len(),
actual: other.perm.len(),
});
}
let other_inv = other.inverse();
let composed = self.compose(&other_inv)?;
Ok(composed.inversions())
}
pub fn is_cyclic(&self) -> bool {
if self.perm.is_empty() {
return true;
}
let n = self.perm.len();
let mut visited = vec![false; n];
let mut current = 0;
let mut cycle_len = 0;
while !visited[current] {
visited[current] = true;
current = self.perm[current];
cycle_len += 1;
}
cycle_len == n && current == 0
}
pub fn into_inner(self) -> Vec<usize> {
self.perm
}
pub fn as_slice(&self) -> &[usize] {
&self.perm
}
}
impl EvolutionaryGenome for Permutation {
type Allele = usize;
type Phenotype = Vec<usize>;
fn to_trace(&self) -> Trace {
let mut trace = Trace::default();
for (i, &val) in self.perm.iter().enumerate() {
trace.insert_choice(addr!("perm", i), ChoiceValue::Usize(val), 0.0);
}
trace
}
fn from_trace(trace: &Trace) -> Result<Self, GenomeError> {
let mut perm = Vec::new();
let mut i = 0;
loop {
match trace.choices.get(&addr!("perm", i)) {
None => break,
Some(choice) => match choice.value.as_usize() {
Some(val) => {
perm.push(val);
i += 1;
}
None => {
return Err(GenomeError::TypeMismatch {
address: format!("perm#{i}"),
expected: "usize".to_string(),
actual: choice.value.type_name().to_string(),
});
}
},
}
}
if perm.is_empty() {
return Err(GenomeError::InvalidStructure(
"No permutation found in trace".to_string(),
));
}
Self::try_new(perm)
}
fn decode(&self) -> Self::Phenotype {
self.perm.clone()
}
fn dimension(&self) -> usize {
self.perm.len()
}
fn generate<R: Rng>(rng: &mut R, bounds: &MultiBounds) -> Self {
Self::generate_with_len(rng, bounds.dimension())
}
fn distance(&self, other: &Self) -> f64 {
self.try_distance(other).unwrap_or_else(|e| {
panic!("Permutation::distance: {e}; use try_distance for a fallible comparison")
})
}
fn try_distance(&self, other: &Self) -> Result<f64, GenomeError> {
self.kendall_tau_distance(other).map(|d| d as f64)
}
fn trace_prefix() -> &'static str {
"perm"
}
}
impl PermutationGenome for Permutation {
fn permutation(&self) -> &[usize] {
&self.perm
}
fn permutation_mut(&mut self) -> &mut [usize] {
&mut self.perm
}
fn from_permutation(perm: Vec<usize>) -> Result<Self, GenomeError> {
Self::try_new(perm)
}
}
impl std::ops::Index<usize> for Permutation {
type Output = usize;
fn index(&self, index: usize) -> &Self::Output {
&self.perm[index]
}
}
impl From<Vec<usize>> for Permutation {
fn from(perm: Vec<usize>) -> Self {
Self::new(perm)
}
}
impl From<Permutation> for Vec<usize> {
fn from(p: Permutation) -> Self {
p.perm
}
}
impl IntoIterator for Permutation {
type Item = usize;
type IntoIter = std::vec::IntoIter<usize>;
fn into_iter(self) -> Self::IntoIter {
self.perm.into_iter()
}
}
impl<'a> IntoIterator for &'a Permutation {
type Item = &'a usize;
type IntoIter = std::slice::Iter<'a, usize>;
fn into_iter(self) -> Self::IntoIter {
self.perm.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::genome::traits::PermutationGenome;
#[test]
fn test_permutation_new() {
let p = Permutation::new(vec![2, 0, 1, 3]);
assert_eq!(p.len(), 4);
assert_eq!(p[0], 2);
assert_eq!(p[1], 0);
}
#[test]
#[should_panic(expected = "valid permutation")]
fn test_permutation_new_invalid_duplicate() {
Permutation::new(vec![0, 1, 1, 3]);
}
#[test]
#[should_panic(expected = "valid permutation")]
fn test_permutation_new_invalid_out_of_range() {
Permutation::new(vec![0, 1, 5, 3]);
}
#[test]
fn test_permutation_try_new() {
assert!(Permutation::try_new(vec![2, 0, 1, 3]).is_ok());
assert!(Permutation::try_new(vec![0, 1, 1, 3]).is_err());
}
#[test]
fn test_permutation_identity() {
let p = Permutation::identity(5);
assert_eq!(p.as_slice(), &[0, 1, 2, 3, 4]);
}
#[test]
fn test_permutation_random() {
let mut rng = rand::thread_rng();
let p = Permutation::random(10, &mut rng);
assert!(p.is_valid_permutation());
assert_eq!(p.len(), 10);
}
#[test]
fn test_permutation_inverse() {
let p = Permutation::new(vec![2, 0, 3, 1]);
let inv = p.inverse();
assert_eq!(inv.as_slice(), &[1, 3, 0, 2]);
let composed = p.compose(&inv).unwrap();
assert_eq!(composed.as_slice(), &[0, 1, 2, 3]);
}
#[test]
fn test_permutation_compose() {
let p1 = Permutation::new(vec![1, 2, 0]);
let p2 = Permutation::new(vec![2, 0, 1]);
let composed = p1.compose(&p2).unwrap();
assert_eq!(composed.as_slice(), &[0, 1, 2]);
}
#[test]
fn test_permutation_swap() {
let mut p = Permutation::new(vec![0, 1, 2, 3]);
p.swap(0, 3);
assert_eq!(p.as_slice(), &[3, 1, 2, 0]);
}
#[test]
fn test_permutation_reverse_segment() {
let mut p = Permutation::new(vec![0, 1, 2, 3, 4]);
p.reverse_segment(1, 3);
assert_eq!(p.as_slice(), &[0, 3, 2, 1, 4]);
}
#[test]
fn test_permutation_insert() {
let mut p = Permutation::new(vec![0, 1, 2, 3, 4]);
p.insert(1, 4);
assert_eq!(p.as_slice(), &[0, 2, 3, 4, 1]);
}
#[test]
fn test_permutation_inversions() {
let p1 = Permutation::identity(5);
assert_eq!(p1.inversions(), 0);
let p2 = Permutation::new(vec![4, 3, 2, 1, 0]);
assert_eq!(p2.inversions(), 10);
let p3 = Permutation::new(vec![1, 0, 2, 3, 4]);
assert_eq!(p3.inversions(), 1);
}
#[test]
fn test_permutation_kendall_tau() {
let p1 = Permutation::new(vec![0, 1, 2, 3]);
let p2 = Permutation::new(vec![0, 1, 2, 3]);
assert_eq!(p1.kendall_tau_distance(&p2).unwrap(), 0);
let p3 = Permutation::new(vec![0, 1, 3, 2]);
assert_eq!(p1.kendall_tau_distance(&p3).unwrap(), 1);
let p4 = Permutation::new(vec![3, 2, 1, 0]);
assert_eq!(p1.kendall_tau_distance(&p4).unwrap(), 6);
}
#[test]
fn test_permutation_is_cyclic() {
let cyclic = Permutation::new(vec![3, 2, 0, 1]);
assert!(cyclic.is_cyclic());
let identity = Permutation::identity(4);
assert!(!identity.is_cyclic());
let empty = Permutation::identity(0);
assert!(empty.is_cyclic());
}
#[test]
fn test_permutation_decode() {
let p = Permutation::new(vec![2, 0, 1]);
assert_eq!(p.decode(), vec![2, 0, 1]);
}
#[test]
fn test_permutation_dimension() {
let p = Permutation::new(vec![2, 0, 1, 3, 4]);
assert_eq!(p.dimension(), 5);
}
#[test]
fn test_permutation_generate() {
let mut rng = rand::thread_rng();
let bounds = MultiBounds::symmetric(1.0, 10);
let p = Permutation::generate(&mut rng, &bounds);
assert_eq!(p.dimension(), 10);
assert!(p.is_valid_permutation());
}
#[test]
fn test_permutation_distance() {
let p1 = Permutation::new(vec![0, 1, 2, 3]);
let p2 = Permutation::new(vec![3, 2, 1, 0]);
assert_eq!(p1.distance(&p2), 6.0);
}
#[test]
fn test_permutation_to_trace() {
let p = Permutation::new(vec![2, 0, 1]);
let trace = p.to_trace();
assert_eq!(trace.get_usize(&addr!("perm", 0)), Some(2));
assert_eq!(trace.get_usize(&addr!("perm", 1)), Some(0));
assert_eq!(trace.get_usize(&addr!("perm", 2)), Some(1));
assert_eq!(trace.get_usize(&addr!("perm", 3)), None);
}
#[test]
fn test_permutation_from_trace() {
let mut trace = Trace::default();
trace.insert_choice(addr!("perm", 0), ChoiceValue::Usize(1), 0.0);
trace.insert_choice(addr!("perm", 1), ChoiceValue::Usize(2), 0.0);
trace.insert_choice(addr!("perm", 2), ChoiceValue::Usize(0), 0.0);
let p = Permutation::from_trace(&trace).unwrap();
assert_eq!(p.as_slice(), &[1, 2, 0]);
}
#[test]
fn test_permutation_trace_roundtrip() {
let original = Permutation::new(vec![4, 2, 0, 3, 1]);
let trace = original.to_trace();
let recovered = Permutation::from_trace(&trace).unwrap();
assert_eq!(original, recovered);
}
#[test]
fn test_permutation_from_trace_invalid() {
let mut trace = Trace::default();
trace.insert_choice(addr!("perm", 0), ChoiceValue::Usize(0), 0.0);
trace.insert_choice(addr!("perm", 1), ChoiceValue::Usize(0), 0.0);
let result = Permutation::from_trace(&trace);
assert!(result.is_err());
}
#[test]
fn test_permutation_from_trace_empty() {
let trace = Trace::default();
let result = Permutation::from_trace(&trace);
assert!(result.is_err());
}
#[test]
fn test_permutation_serialization() {
let p = Permutation::new(vec![2, 0, 1, 3]);
let serialized = serde_json::to_string(&p).unwrap();
let deserialized: Permutation = serde_json::from_str(&serialized).unwrap();
assert_eq!(p, deserialized);
}
#[test]
fn test_permutation_iteration() {
let p = Permutation::new(vec![2, 0, 1]);
let collected: Vec<usize> = p.into_iter().collect();
assert_eq!(collected, vec![2, 0, 1]);
}
#[test]
fn test_permutation_ref_iteration() {
let p = Permutation::new(vec![2, 0, 1]);
let sum: usize = p.into_iter().sum();
assert_eq!(sum, 3);
}
#[test]
fn test_permutation_into_inner() {
let p = Permutation::new(vec![2, 0, 1]);
let v: Vec<usize> = p.into_inner();
assert_eq!(v, vec![2, 0, 1]);
}
#[test]
fn test_permutation_from_vec() {
let p: Permutation = vec![1, 0, 2].into();
assert_eq!(p.as_slice(), &[1, 0, 2]);
}
#[test]
fn test_permutation_try_distance_length_mismatch() {
let p1 = Permutation::identity(3);
let p2 = Permutation::identity(5);
assert!(matches!(
p1.try_distance(&p2),
Err(GenomeError::DimensionMismatch {
expected: 3,
actual: 5
})
));
}
#[test]
#[should_panic(expected = "Dimension mismatch")]
fn test_permutation_distance_length_mismatch_panics() {
let p1 = Permutation::identity(3);
let p2 = Permutation::identity(5);
let _ = p1.distance(&p2);
}
#[test]
fn test_permutation_from_trace_type_mismatch() {
let mut trace = Trace::default();
trace.insert_choice(addr!("perm", 0), ChoiceValue::Usize(2), 0.0);
trace.insert_choice(addr!("perm", 1), ChoiceValue::Bool(true), 0.0); trace.insert_choice(addr!("perm", 2), ChoiceValue::Usize(0), 0.0);
match Permutation::from_trace(&trace) {
Err(GenomeError::TypeMismatch {
address,
expected,
actual,
}) => {
assert_eq!(address, "perm#1");
assert_eq!(expected, "usize");
assert_eq!(actual, "bool");
}
other => panic!("expected TypeMismatch, got {other:?}"),
}
}
#[test]
fn test_permutation_from_vec_unchecked_debug_asserts() {
let p = Permutation::from_vec_unchecked(vec![2, 0, 1]);
assert!(p.is_valid_permutation());
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "not a valid permutation")]
fn test_permutation_from_vec_unchecked_rejects_invalid_in_debug() {
let _ = Permutation::from_vec_unchecked(vec![0, 0, 2]);
}
#[test]
fn test_permutation_generate_with_len() {
let mut rng = rand::thread_rng();
let p = Permutation::generate_with_len(&mut rng, 8);
assert_eq!(p.len(), 8);
assert!(p.is_valid_permutation());
}
}