From f4c22ab064ff8c1daa7c9b6d2cbf491e5196ec64 Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Fri, 22 Nov 2024 17:59:58 +0100 Subject: [PATCH 1/3] implement permutation via slices --- nemo-physical/src/util/mapping/permutation.rs | 202 ++++++++---------- nemo-physical/src/util/mapping/traits.rs | 1 + 2 files changed, 87 insertions(+), 116 deletions(-) diff --git a/nemo-physical/src/util/mapping/permutation.rs b/nemo-physical/src/util/mapping/permutation.rs index c9d87f24c..2785b119b 100644 --- a/nemo-physical/src/util/mapping/permutation.rs +++ b/nemo-physical/src/util/mapping/permutation.rs @@ -1,7 +1,7 @@ //! Module for representing a permutation on natural numbers use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, fmt::Display, hash::{Hash, Hasher}, }; @@ -11,122 +11,102 @@ use super::traits::NatMapping; /// Represents a permutation on the set of natural numbers. #[derive(Debug, Default, Eq, Clone)] pub struct Permutation { - // The function represented by this object will map `i` to `map.get(i)`. - // All inputs that are not keys in this map are implicitly mapped to themselves. - // This allows us to have a canonical representation such that we can easily check for equality - map: HashMap, + // permutation(i) = j <=> indeces[i] = j + indeces: Box<[usize]>, } impl Permutation { /// Return an instance of the function from a vector representation where the input `vec[i]` is mapped to `i`. pub fn from_vector(vec: Vec) -> Self { - let mut map = HashMap::::new(); + (Self { + indeces: vec.into(), + }) + .invert() + } - for (value, input) in vec.into_iter().enumerate() { - if input == value { - // Values that map to themselves will be represented implicitly - continue; + /// Returns a permutation where i is mapped to vec[i] + fn trimmed(mut vec: Vec) -> Self { + while let Some(&last) = vec.last() { + if last == vec.len() - 1 { + vec.pop().unwrap(); + } else { + break; } - - map.insert(input, value); } - let result = Self { map }; - debug_assert!(result.is_valid()); - - result + Self { + indeces: vec.into(), + } } /// Return an instance of the function from a hash map representation where the input `i` is mapped to `map.get(i)`. - pub fn from_map(mut map: HashMap) -> Self { - // Values that map to themselves will not be stored explicitly - map.retain(|i, v| *i != *v); + pub fn from_map(map: HashMap) -> Self { + let mut vec = Vec::with_capacity(map.len()); - let result = Self { map }; - debug_assert!(result.is_valid()); + for (k, v) in map { + for i in vec.len()..=k { + vec.push(i); + } - result + vec[k] = v; + } + + Self::trimmed(vec) } /// Return a [Permutation] that when applied to the given slice of values would put them in ascending order. pub fn from_unsorted(values: &[T]) -> Self { - let mut result: Vec = (0..values.len()).collect(); - result.sort_by(|&a, &b| values[a].cmp(&values[b])); + let mut indeces: Vec = (0..values.len()).collect(); + indeces.sort_by(|&a, &b| values[a].cmp(&values[b])); - Self::from_vector(result) + Self::from_vector(indeces) } /// Return the largest input value that is not mapped to itself. pub(crate) fn last_mapped(&self) -> Option { - self.map.keys().max().copied() - } - - /// In order for the internal representation to be a valid permutation we need to check - /// that no two inputs map to the same value. - fn is_valid(&self) -> bool { - let mut values = HashSet::::new(); - - for value in self.map.values() { - // Value has been explicitly mapped twice - if !values.insert(*value) { - return false; - } - - // Value has been implicitly mapped twice - if !self.map.contains_key(value) { - return false; + for (i, &v) in self.indeces.iter().enumerate().rev() { + if i != v { + return Some(i); } } - true + None } /// Return a list of all cycles of length greater than 1. /// A cycle is a list of inputs such that the (i+1)th results from applying the permutation to the ith element. /// and the first can be obtained by applying it to the last. fn find_cycles(&self) -> Vec> { - let mut result = Vec::>::new(); - - let mut input_set: HashSet = self.map.keys().cloned().collect(); - while let Some(start) = input_set.iter().next().cloned() { - let mut current_cycle = Vec::::new(); - current_cycle.push(start); - - input_set.remove(&start); - - let mut current_value = start; - loop { - let next_value = self.get(current_value); - input_set.remove(&next_value); - - current_value = next_value; - - if current_value == start { - break; - } - - current_cycle.push(current_value); + let mut tmp: Vec> = self.indeces.iter().cloned().map(Some).collect(); + let mut cycles = Vec::new(); + + for i in 0..tmp.len() { + let mut curr = match tmp[i] { + None => continue, + Some(c) if c == i => continue, + Some(c) => c, + }; + + let mut cycle = Vec::new(); + cycle.push(i); + + while curr != i { + cycle.push(curr); + curr = tmp[curr] + .take() + .expect("should be populated if this is a permutation"); } - result.push(current_cycle); + cycles.push(cycle) } - result + cycles } /// Return a vector represenation of the permutation. + /// where everything is opposite of what you expect. fn vector_minimal(&self) -> Vec { - let mut result = Vec::::new(); - - for (&input, &value) in &self.map { - for new_index in result.len()..=value { - result.push(new_index); - } - - result[value] = input; - } - - result + self.invert().indeces.into() } /// Derive a [Permutation] that would transform a vector of elements into another. @@ -152,6 +132,7 @@ impl Permutation { /// Compute a [Permutation] that when chained to this [Permutation] /// would result in the given target [Permutation]. #[allow(dead_code)] + #[cfg(unused)] pub(crate) fn permute_into(&self, target: &Self) -> Self { let max_key_source = self.map.keys().max().cloned().unwrap_or(0); let max_key_target = target.map.keys().max().cloned().unwrap_or(0); @@ -171,20 +152,25 @@ impl Permutation { /// Return a new [Permutation] that is the inverse of this. pub(crate) fn invert(&self) -> Self { - let mut map = HashMap::::new(); + let mut vec = Vec::with_capacity(self.indeces.len()); - for (input, value) in &self.map { - map.insert(*value, *input); + for (k, &v) in self.indeces.iter().enumerate() { + for i in vec.len()..=v { + vec.push(i); + } + + vec[v] = k; } - Self { map } + Self::trimmed(vec) } /// Apply this permutation to a slice of things. pub(crate) fn permute(&self, vec: &[T]) -> Vec { let mut result: Vec = vec.to_vec(); - for (input, value) in self.map.iter() { - result[*value] = vec[*input].clone(); + + for (input, &value) in self.indeces.iter().enumerate() { + result[value] = vec[input].clone(); } result @@ -192,41 +178,25 @@ impl Permutation { /// Return the value of the function for a given input. pub(crate) fn get(&self, input: usize) -> usize { - self.get_partial(input) - .expect("Permutations map each value") + *self.indeces.get(input).unwrap_or(&input) } } impl NatMapping for Permutation { fn get_partial(&self, input: usize) -> Option { - Some(*self.map.get(&input).unwrap_or(&input)) + Some(self.get(input)) } fn chain_permutation(&self, permutation: &Permutation) -> Self { - let mut result_map = HashMap::::new(); + let indeces = (0..std::cmp::max(self.indeces.len(), permutation.indeces.len())) + .map(|i| permutation.get(self.get(i))) + .collect(); - for (input, value) in &self.map { - let chained_value = permutation.get(*value); - if chained_value == *input { - continue; - } - - result_map.insert(*input, chained_value); - } - - for (input, value) in &permutation.map { - if self.map.contains_key(input) { - continue; - } - - result_map.insert(*input, *value); - } - - Self { map: result_map } + Self::trimmed(indeces) } fn is_identity(&self) -> bool { - self.map.is_empty() + self.indeces.iter().enumerate().all(|(i, &v)| i == v) } } @@ -363,16 +333,16 @@ mod test { assert_eq!(Permutation::from_map(map).last_mapped(), None); } - #[test] - fn test_is_valid() { - let map = HashMap::::from([(7, 8), (8, 7), (6, 8)]); - let perm = Permutation { map }; - assert!(!perm.is_valid()); - - let map = HashMap::::from([(7, 8)]); - let perm = Permutation { map }; - assert!(!perm.is_valid()); - } + // #[test] + // fn test_is_valid() { + // let map = HashMap::::from([(7, 8), (8, 7), (6, 8)]); + // let perm = Permutation { map }; + // assert!(!perm.is_valid()); + + // let map = HashMap::::from([(7, 8)]); + // let perm = Permutation { map }; + // assert!(!perm.is_valid()); + // } fn sort_cycles(mut cycles: Vec>) -> Vec> { for cycle in &mut cycles { diff --git a/nemo-physical/src/util/mapping/traits.rs b/nemo-physical/src/util/mapping/traits.rs index cf35ffa6c..e0362b6b0 100644 --- a/nemo-physical/src/util/mapping/traits.rs +++ b/nemo-physical/src/util/mapping/traits.rs @@ -8,6 +8,7 @@ use super::permutation::Permutation; pub trait NatMapping: Debug + Display + PartialEq + Eq { /// Return the value of the function for a given input. /// Returns `None` if the input is not in the function's domain. + #[allow(dead_code)] fn get_partial(&self, input: usize) -> Option; /// Return the function which results from chaining `self` with a given [Permutation]. From 16f256fba61ebe9e4e6719999c65c790cae3cafb Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Wed, 4 Dec 2024 01:16:37 +0100 Subject: [PATCH 2/3] save both directions for permutations --- nemo-physical/src/util/mapping/permutation.rs | 97 +++++++++---------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/nemo-physical/src/util/mapping/permutation.rs b/nemo-physical/src/util/mapping/permutation.rs index 2785b119b..cae3fa172 100644 --- a/nemo-physical/src/util/mapping/permutation.rs +++ b/nemo-physical/src/util/mapping/permutation.rs @@ -4,6 +4,7 @@ use std::{ collections::HashMap, fmt::Display, hash::{Hash, Hasher}, + iter::repeat, }; use super::traits::NatMapping; @@ -11,31 +12,49 @@ use super::traits::NatMapping; /// Represents a permutation on the set of natural numbers. #[derive(Debug, Default, Eq, Clone)] pub struct Permutation { - // permutation(i) = j <=> indeces[i] = j - indeces: Box<[usize]>, + idx_to_val: Box<[usize]>, + val_to_idx: Box<[usize]>, +} + +fn invert(input: &[usize]) -> Box<[usize]> { + let mut result: Box<[usize]> = repeat(0).take(input.len()).collect(); + + for (k, &v) in input.iter().enumerate() { + result[v] = k; + } + + result } impl Permutation { /// Return an instance of the function from a vector representation where the input `vec[i]` is mapped to `i`. - pub fn from_vector(vec: Vec) -> Self { - (Self { - indeces: vec.into(), - }) - .invert() + pub fn from_vector(mut vec: Vec) -> Self { + while let Some(last) = vec.last() { + if *last == vec.len() - 1 { + _ = vec.pop(); + } else { + break; + } + } + + Self { + idx_to_val: invert(&vec), + val_to_idx: vec.into(), + } } - /// Returns a permutation where i is mapped to vec[i] - fn trimmed(mut vec: Vec) -> Self { - while let Some(&last) = vec.last() { - if last == vec.len() - 1 { - vec.pop().unwrap(); + fn from_idx_to_val(mut input: Vec) -> Self { + while let Some(last) = input.last() { + if *last == input.len() - 1 { + _ = input.pop(); } else { break; } } Self { - indeces: vec.into(), + val_to_idx: invert(&input), + idx_to_val: input.into(), } } @@ -51,7 +70,7 @@ impl Permutation { vec[k] = v; } - Self::trimmed(vec) + Self::from_idx_to_val(vec) } /// Return a [Permutation] that when applied to the given slice of values would put them in ascending order. @@ -64,7 +83,7 @@ impl Permutation { /// Return the largest input value that is not mapped to itself. pub(crate) fn last_mapped(&self) -> Option { - for (i, &v) in self.indeces.iter().enumerate().rev() { + for (i, &v) in self.idx_to_val.iter().enumerate().rev() { if i != v { return Some(i); } @@ -77,7 +96,7 @@ impl Permutation { /// A cycle is a list of inputs such that the (i+1)th results from applying the permutation to the ith element. /// and the first can be obtained by applying it to the last. fn find_cycles(&self) -> Vec> { - let mut tmp: Vec> = self.indeces.iter().cloned().map(Some).collect(); + let mut tmp: Vec> = self.idx_to_val.iter().cloned().map(Some).collect(); let mut cycles = Vec::new(); for i in 0..tmp.len() { @@ -106,7 +125,7 @@ impl Permutation { /// Return a vector represenation of the permutation. /// where everything is opposite of what you expect. fn vector_minimal(&self) -> Vec { - self.invert().indeces.into() + self.val_to_idx.to_vec() } /// Derive a [Permutation] that would transform a vector of elements into another. @@ -129,47 +148,19 @@ impl Permutation { Self::from_map(map) } - /// Compute a [Permutation] that when chained to this [Permutation] - /// would result in the given target [Permutation]. - #[allow(dead_code)] - #[cfg(unused)] - pub(crate) fn permute_into(&self, target: &Self) -> Self { - let max_key_source = self.map.keys().max().cloned().unwrap_or(0); - let max_key_target = target.map.keys().max().cloned().unwrap_or(0); - let max_key = max_key_source.max(max_key_target); - - let mut result_map = HashMap::new(); - - for input in 0..max_key { - let source_output = self.get(input); - let target_output = target.get(input); - - result_map.insert(source_output, target_output); - } - - Self::from_map(result_map) - } - /// Return a new [Permutation] that is the inverse of this. pub(crate) fn invert(&self) -> Self { - let mut vec = Vec::with_capacity(self.indeces.len()); - - for (k, &v) in self.indeces.iter().enumerate() { - for i in vec.len()..=v { - vec.push(i); - } - - vec[v] = k; + Self { + val_to_idx: self.idx_to_val.clone(), + idx_to_val: self.val_to_idx.clone(), } - - Self::trimmed(vec) } /// Apply this permutation to a slice of things. pub(crate) fn permute(&self, vec: &[T]) -> Vec { let mut result: Vec = vec.to_vec(); - for (input, &value) in self.indeces.iter().enumerate() { + for (input, &value) in self.idx_to_val.iter().enumerate() { result[value] = vec[input].clone(); } @@ -178,7 +169,7 @@ impl Permutation { /// Return the value of the function for a given input. pub(crate) fn get(&self, input: usize) -> usize { - *self.indeces.get(input).unwrap_or(&input) + *self.idx_to_val.get(input).unwrap_or(&input) } } @@ -188,15 +179,15 @@ impl NatMapping for Permutation { } fn chain_permutation(&self, permutation: &Permutation) -> Self { - let indeces = (0..std::cmp::max(self.indeces.len(), permutation.indeces.len())) + let indeces = (0..std::cmp::max(self.idx_to_val.len(), permutation.idx_to_val.len())) .map(|i| permutation.get(self.get(i))) .collect(); - Self::trimmed(indeces) + Self::from_idx_to_val(indeces) } fn is_identity(&self) -> bool { - self.indeces.iter().enumerate().all(|(i, &v)| i == v) + self.idx_to_val.iter().enumerate().all(|(i, &v)| i == v) } } From b92bcae326bb6b0e1ce2cfc2e1ee415906a469d8 Mon Sep 17 00:00:00 2001 From: Matthias Meissner Date: Wed, 4 Dec 2024 08:17:33 +0100 Subject: [PATCH 3/3] remove obsolete test --- nemo-physical/src/util/mapping/permutation.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/nemo-physical/src/util/mapping/permutation.rs b/nemo-physical/src/util/mapping/permutation.rs index cae3fa172..99c2377d1 100644 --- a/nemo-physical/src/util/mapping/permutation.rs +++ b/nemo-physical/src/util/mapping/permutation.rs @@ -324,17 +324,6 @@ mod test { assert_eq!(Permutation::from_map(map).last_mapped(), None); } - // #[test] - // fn test_is_valid() { - // let map = HashMap::::from([(7, 8), (8, 7), (6, 8)]); - // let perm = Permutation { map }; - // assert!(!perm.is_valid()); - - // let map = HashMap::::from([(7, 8)]); - // let perm = Permutation { map }; - // assert!(!perm.is_valid()); - // } - fn sort_cycles(mut cycles: Vec>) -> Vec> { for cycle in &mut cycles { cycle.sort();