qudit_core/
perm.rs

1use std::collections::HashSet;
2
3use faer::Mat;
4use faer::mat::AsMatMut;
5use faer::mat::AsMatRef;
6use faer::perm::Perm;
7use faer::perm::permute_cols;
8use faer::perm::permute_rows;
9use faer::reborrow::Reborrow;
10use faer::reborrow::ReborrowMut;
11use faer_traits::ComplexField;
12
13use crate::QuditSystem;
14use crate::Radices;
15use crate::Radix;
16
17// TODO: TEST with FRPR
18// let vec = Mat::from_fn(radices.get_dimension(), 1, |i, _| {
19//     c64::new(i as f64, 0.0)
20// });
21// let mut vec_buf = Mat::zeros(radices.get_dimension(), 1);
22// let mut extended_radices = radices.clone().to_vec();
23// let mut extended_perm = perm.clone().to_vec();
24// extended_radices.push(1);
25// extended_perm.push(perm.len());
26// fused_reshape_permute_reshape_into(
27//     vec.as_ref(),
28//     &extended_radices,
29//     &extended_perm,
30//     vec_buf.as_mut(),
31// );
32
33// let mut index_perm = vec![];
34// for i in 0..radices.get_dimension() {
35//     index_perm.push(vec_buf.read(i, 0).re as usize);
36// }
37
38/// Calculate the index permutation from a qudit permutation for a given radices.
39///
40/// # Arguments
41///
42/// * `radices` - The radices of the system being permuted.
43///
44/// * `perm` - A vector describing the permutation.
45///
46/// # Returns
47///
48/// A vector of indices that describes the permutation in the index space.
49///
50/// # Examples
51///
52/// ```
53/// use qudit_core::{Radices, calc_index_permutation};
54/// let radices = Radices::new([2, 2]);
55/// let perm = vec![1, 0];
56/// let index_perm = calc_index_permutation(&radices, &perm);
57/// assert_eq!(index_perm, vec![0, 2, 1, 3]);
58/// ```
59pub fn calc_index_permutation(radices: &Radices, perm: &[usize]) -> Vec<usize> {
60    let mut index_perm = Vec::with_capacity(radices.dimension());
61    let place_values = radices.place_values();
62    let perm_place_values = perm
63        .iter()
64        .map(|&x| place_values[x])
65        .collect::<Vec<usize>>();
66    let perm_radices = perm.iter().map(|&x| radices[x]).collect::<Radices>();
67
68    for i in 0..radices.dimension() {
69        let expansion = perm_radices.expand(i);
70
71        let mut acm_val = 0usize;
72        for i in 0..radices.len() {
73            acm_val += expansion[i] * perm_place_values[i];
74        }
75        index_perm.push(acm_val);
76    }
77
78    index_perm
79}
80
81/// A permutation of qudits.
82///
83/// Qudit systems are often shuffled around in compiler pipelines. This
84/// object captures a specific shuffling operation which can be represented
85/// in many ways.
86#[derive(Hash, PartialEq, Eq, Clone, Debug)]
87pub struct QuditPermutation {
88    /// The number of qudits in the permutation.
89    num_qudits: usize,
90
91    /// The radices of the qudit system being permuted.
92    radices: Radices,
93
94    /// The permutation vector in the qudit space.
95    perm: Vec<usize>,
96
97    /// The permutation vector in the index space.
98    index_perm: Vec<usize>,
99
100    /// The inverse permutation vector in the index space.
101    inverse_index_perm: Vec<usize>,
102}
103
104impl QuditPermutation {
105    /// Returns a permutation mapping qudit `i` to `perm[i]`.
106    ///
107    /// # Arguments
108    ///
109    /// * `radices` - The radices of the system being permuted
110    ///
111    /// * `perm` - A vector describing the permutation. The resulting operation
112    ///   will shift the i-th qudit to the `perm[i]`-th qudit.
113    ///
114    /// # Panics
115    ///
116    /// * If the supplied permutation is not a proper permutation. This can
117    ///   happen when there is a duplicate or invalid qudit index.
118    ///
119    /// * If the permutation and radices describe systems with different qudit
120    ///   counts, i.e. they have different lengths.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use qudit_core::{Radices, QuditPermutation};
126    ///
127    /// let three_qubits = Radices::new([2; 3]);
128    /// let qudit_shift = QuditPermutation::new(three_qubits, &vec![2, 0, 1]);
129    /// ```
130    ///
131    /// # See Also
132    ///
133    /// * [Self::from_qubit_location] - A convenience constructor for qubit permutations.
134    /// * [Self::from_qudit_location] - A convenience constructor for qudit permutations.
135    pub fn new<R: Into<Radices>>(radices: R, perm: &[usize]) -> QuditPermutation {
136        fn __new_impl(radices: Radices, perm: &[usize]) -> QuditPermutation {
137            if perm.len() != radices.len() {
138                panic!("Invalid qudit permutation: perm's qudit count doesn't match radices'.");
139            }
140
141            if !perm.iter().all(|x| x < &radices.len()) {
142                panic!("Invalid qudit permutation: permutation has invalid qudit index.");
143            }
144
145            let mut uniq = HashSet::new();
146            if !perm.iter().all(|x| uniq.insert(x)) {
147                panic!("Invalid qudit permutation: permutation has duplicate entries.");
148            }
149
150            let index_perm = calc_index_permutation(&radices, perm);
151
152            let mut inverse_index_perm = vec![0; radices.dimension()];
153            index_perm
154                .iter()
155                .enumerate()
156                .for_each(|(s, d)| inverse_index_perm[*d] = s);
157
158            QuditPermutation {
159                num_qudits: radices.len(),
160                radices,
161                perm: perm.to_vec(),
162                index_perm,
163                inverse_index_perm,
164            }
165        }
166        __new_impl(radices.into(), perm)
167    }
168
169    /// Returns a qubit permutation specifed by `perm`.
170    ///
171    /// # Arguments
172    ///
173    /// * `perm` - The qubit permutation.
174    ///
175    /// # Panics
176    ///
177    /// * If the supplied permutation is not a proper permutation. This can
178    ///   happen when there is a duplicate or invalid qudit index.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use qudit_core::{Radices, QuditPermutation};
184    ///
185    /// let qubit_shift = QuditPermutation::from_qubit_location(&vec![2, 0, 1]);
186    /// ```
187    ///
188    /// # See Also
189    ///
190    /// * [Self::new] - A general constructor for qudit permutations.
191    /// * [Self::from_qudit_location] - A convenience constructor for qudit permutations.
192    #[inline]
193    pub fn from_qubit_location(perm: &[usize]) -> QuditPermutation {
194        Self::from_qudit_location(2u8, perm)
195    }
196
197    /// Returns a qudit permutation specifed by `perm` with a uniform `radix`.
198    ///
199    /// # Arguments
200    ///
201    /// * `perm` - The qubit permutation.
202    ///
203    /// * `radix` - The radix of the qudits being permuted.
204    ///
205    /// # Panics
206    ///
207    /// * If the supplied permutation is not a proper permutation. This can
208    ///   happen when there is a duplicate or invalid qudit index.
209    ///
210    /// * If the radix is not a valid radix.
211    ///
212    /// # Examples
213    ///
214    /// ```
215    /// use qudit_core::{Radices, QuditPermutation};
216    ///
217    /// let qutrit_swap = QuditPermutation::from_qudit_location(3, &vec![1, 0]);
218    /// ```
219    ///
220    /// # See Also
221    ///
222    /// * [Self::new] - A general constructor for qudit permutations.
223    /// * [Self::from_qubit_location] - A convenience constructor for qubit permutations.
224    #[inline]
225    pub fn from_qudit_location<T: Into<Radix>>(radix: T, perm: &[usize]) -> QuditPermutation {
226        let qudit_iter = core::iter::repeat_n(radix.into(), perm.len());
227        let rdx = Radices::from_iter(qudit_iter);
228        QuditPermutation::new(rdx, perm)
229    }
230
231    /// Returns true if the permutation has physical meaning.
232    ///
233    /// A permutation cannot be physically implemented if it permutes
234    /// qudits of different radices.
235    ///
236    /// # Examples
237    /// ```
238    /// use qudit_core::{Radices, QuditPermutation};
239    ///
240    /// let three_qubits = Radices::new([2; 3]);
241    /// let qudit_shift = QuditPermutation::new(three_qubits, &vec![2, 0, 1]);
242    /// assert!(qudit_shift.is_physically_implementable());
243    ///
244    /// let three_qutrits = Radices::new([3; 3]);
245    /// let qudit_shift = QuditPermutation::new(three_qutrits, &vec![2, 0, 1]);
246    /// assert!(qudit_shift.is_physically_implementable());
247    ///
248    /// let three_qudits = Radices::new([2, 3, 4]);
249    /// let qudit_shift = QuditPermutation::new(three_qudits, &vec![2, 0, 1]);
250    /// assert!(!qudit_shift.is_physically_implementable());
251    /// ```
252    pub fn is_physically_implementable(&self) -> bool {
253        self.perm
254            .iter()
255            .enumerate()
256            .all(|x| self.radices[x.0] == self.radices[*x.1])
257    }
258
259    /// Returns a permutation that sorts the circuit location
260    ///
261    /// # Arguments
262    ///
263    /// * `loc` - The index list to be sorted.
264    ///
265    /// * `radices` - The radices of the system being permuted.
266    ///
267    /// # Panics
268    ///
269    /// * If the supplied radices are invalid.
270    ///
271    /// # Examples
272    ///
273    /// ```
274    /// use qudit_core::{Radices, QuditPermutation};
275    /// let radices = Radices::new([2, 3]);
276    /// let loc = vec![37, 24];
277    /// let hybrid_swap = QuditPermutation::locally_invert_location(radices.clone(), &loc);
278    /// assert_eq!(hybrid_swap, QuditPermutation::new(radices, &vec![1, 0]));
279    /// ```
280    pub fn locally_invert_location(radices: Radices, loc: &[usize]) -> QuditPermutation {
281        let mut perm: Vec<usize> = (0..loc.len()).collect();
282        perm.sort_by_key(|&i| &loc[i]);
283        QuditPermutation::new(radices, &perm)
284    }
285
286    /// Returns the permutation in the index space.
287    ///
288    /// The qudit permutation describes how the qudits of a system are permuted.
289    /// This ultimately results in the permutation of the index space of the system.
290    /// For example, two qubits have four basis states: |00>, |01>, |10>, and |11>.
291    /// If the qubits are permuted, the basis states are permuted as well.
292    /// The index permutation describes this permutation of the basis states.
293    ///
294    /// # Examples
295    /// ```
296    /// use qudit_core::{Radices, QuditPermutation};
297    ///
298    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
299    /// assert_eq!(qubit_swap.index_perm(), &vec![0, 2, 1, 3]);
300    /// ```
301    pub fn index_perm(&self) -> &[usize] {
302        &self.index_perm
303    }
304
305    /// Returns the radices of the system after being permuted.
306    ///
307    /// # Examples
308    /// ```
309    /// use qudit_core::{Radices, QuditPermutation};
310    /// let qudit_swap = QuditPermutation::new(Radices::new([2, 3]), &vec![1, 0]);
311    /// assert_eq!(qudit_swap.permuted_radices(), Radices::new([3, 2]));
312    ///
313    /// let qudit_swap = QuditPermutation::new(Radices::new([2, 3, 4]), &vec![1, 0, 2]);
314    /// assert_eq!(qudit_swap.permuted_radices(), Radices::new([3, 2, 4]));
315    /// ```
316    pub fn permuted_radices(&self) -> Radices {
317        self.perm.iter().map(|&i| self.radices[i]).collect()
318    }
319
320    /// Returns true is this permutation does not permute any qudit.
321    ///
322    /// # Examples
323    /// ```
324    /// use qudit_core::QuditPermutation;
325    /// let identity = QuditPermutation::from_qubit_location(&vec![0, 1]);
326    /// assert!(identity.is_identity());
327    /// ```
328    ///
329    /// ```
330    /// use qudit_core::QuditPermutation;
331    /// let identity = QuditPermutation::from_qubit_location(&vec![1, 0]);
332    /// assert!(!identity.is_identity());
333    /// ```
334    pub fn is_identity(&self) -> bool {
335        self.perm.iter().enumerate().all(|(s, d)| s == *d)
336    }
337
338    /// Returns a new permutation that composes `self` with `arg_perm`.
339    ///
340    /// # Panics
341    ///
342    /// If the permutations have incompatible radices.
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// use qudit_core::QuditPermutation;
348    /// let p1 = QuditPermutation::from_qubit_location(&vec![1, 0]);
349    /// let p2 = QuditPermutation::from_qubit_location(&vec![1, 0]);
350    /// assert!(p1.compose(&p2).is_identity());
351    ///
352    /// let p1 = QuditPermutation::from_qubit_location(&vec![0, 2, 1, 3]);
353    /// let p2 = QuditPermutation::from_qubit_location(&vec![1, 0, 3, 2]);
354    /// assert_eq!(p1.compose(&p2), QuditPermutation::from_qubit_location(&vec![2, 0, 3, 1]));
355    /// ```
356    pub fn compose(&self, arg_perm: &QuditPermutation) -> QuditPermutation {
357        if self.radices != arg_perm.radices {
358            panic!("Permutations being composed have incompatible radices.");
359        }
360
361        let mut composed_perm = vec![0; self.num_qudits()];
362        arg_perm
363            .iter()
364            .enumerate()
365            .for_each(|(s, d)| composed_perm[s] = self.perm[*d]);
366        QuditPermutation::new(self.radices.clone(), &composed_perm)
367    }
368
369    /// Returns a new permutation that inverts `self`.
370    ///
371    /// # Examples
372    ///
373    /// ```
374    /// use qudit_core::QuditPermutation;
375    /// let p1 = QuditPermutation::from_qubit_location(&vec![1, 0]);
376    /// assert_eq!(p1.invert(), p1);
377    /// ```
378    ///
379    /// ```
380    /// use qudit_core::QuditPermutation;
381    /// let p1 = QuditPermutation::from_qubit_location(&vec![2, 0, 3, 1]);
382    /// assert_eq!(p1.invert(), QuditPermutation::from_qubit_location(&vec![1, 3, 0, 2]));
383    /// ```
384    pub fn invert(&self) -> QuditPermutation {
385        let mut inverted_perm = vec![0; self.num_qudits()];
386        self.perm
387            .iter()
388            .enumerate()
389            .for_each(|(s, d)| inverted_perm[*d] = s);
390        QuditPermutation::new(self.radices.clone(), &inverted_perm)
391    }
392
393    /// Returns the permutation as a sequence of cycles.
394    ///
395    /// Any permutation can be represented as a product of disjoint cycles.
396    /// This function performs that decompositions.
397    ///
398    /// # Returns
399    ///
400    /// A vector of cycles, where each cycle is a vector of qudit indices.
401    ///
402    /// # Examples
403    /// ```
404    /// use qudit_core::{Radices, QuditPermutation};
405    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
406    /// assert_eq!(qubit_swap.cycles(), vec![vec![0, 1]]);
407    ///
408    /// let complex_perm = QuditPermutation::from_qudit_location(3, &vec![1, 2, 0]);
409    /// assert_eq!(complex_perm.cycles(), vec![vec![0, 1, 2]]);
410    ///
411    /// let complex_perm = QuditPermutation::from_qubit_location(&vec![1, 0, 3, 2]);
412    /// assert_eq!(complex_perm.cycles(), vec![vec![0, 1], vec![2, 3]]);
413    /// ```
414    ///
415    /// # Notes
416    ///
417    /// The cycles are not unique. For example, the permutation (0, 1, 2) can
418    /// be represented as either (0, 1, 2) or (2, 0, 1). This function returns
419    /// the former. Each new cycle starts with the smallest index.
420    ///
421    /// # See Also
422    ///
423    /// * [Self::transpositions] - Returns the permutation as a sequence of qudit swaps.
424    /// * [Self::index_cycles] - Returns the permutation as a sequence of index swaps.
425    /// * [Self::index_transpositions] - Returns the permutation as a sequence of index swaps.
426    pub fn cycles(&self) -> Vec<Vec<usize>> {
427        let mut cycles_vec = Vec::new();
428        let mut visited = vec![false; self.num_qudits];
429
430        for new_cycle_start_index in 0..self.num_qudits {
431            if visited[new_cycle_start_index] {
432                continue;
433            }
434
435            let mut cycle = Vec::new();
436            let mut cycle_iter_index = new_cycle_start_index;
437
438            while !visited[cycle_iter_index] {
439                visited[cycle_iter_index] = true;
440                cycle.push(cycle_iter_index);
441                cycle_iter_index = self.perm[cycle_iter_index];
442            }
443
444            if cycle.len() > 1 {
445                cycles_vec.push(cycle);
446            }
447        }
448
449        cycles_vec
450    }
451
452    /// Returns the index-space permutation as a sequence of cycles.
453    ///
454    /// # Returns
455    ///
456    /// A vector of cycles, where each cycle is a vector of qudit indices.
457    ///
458    /// # Examples
459    ///
460    /// ```
461    /// use qudit_core::{Radices, QuditPermutation};
462    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
463    /// assert_eq!(qubit_swap.index_cycles(), vec![vec![1, 2]]);
464    /// ```
465    ///
466    /// # See Also
467    ///
468    /// * [Self::cycles] - Returns the permutation as a sequence of cycles.
469    /// * [Self::transpositions] - Returns the permutation as a sequence of qudit swaps.
470    /// * [Self::index_transpositions] - Returns the permutation as a sequence of index swaps.
471    pub fn index_cycles(&self) -> Vec<Vec<usize>> {
472        let mut cycles_vec = Vec::new();
473        let mut visited = vec![false; self.dimension()];
474
475        for new_cycle_start_index in 0..self.dimension() {
476            if visited[new_cycle_start_index] {
477                continue;
478            }
479
480            let mut cycle = Vec::new();
481            let mut cycle_iter_index = new_cycle_start_index;
482
483            while !visited[cycle_iter_index] {
484                visited[cycle_iter_index] = true;
485                cycle.push(cycle_iter_index);
486                cycle_iter_index = self.index_perm[cycle_iter_index];
487            }
488
489            if cycle.len() > 1 {
490                cycles_vec.push(cycle);
491            }
492        }
493
494        cycles_vec
495    }
496
497    /// Return the permutation as a sequence of qudit swaps
498    ///
499    /// Any permutation can be represented as a product of transpositions
500    /// (2-cycles or swaps). This function decomposes the permutation as a
501    /// sequence of tranpositions.
502    ///
503    /// # Returns
504    ///
505    /// A vector of transpositions, where each swap is a qudit index tuple.
506    ///
507    /// # Examples
508    ///
509    /// ```
510    /// use qudit_core::{Radices, QuditPermutation};
511    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
512    /// assert_eq!(qubit_swap.transpositions(), vec![(0, 1)]);
513    ///
514    /// let complex_perm = QuditPermutation::from_qudit_location(3, &vec![1, 2, 0]);
515    /// assert_eq!(complex_perm.transpositions(), vec![(0, 1), (1, 2)]);
516    /// ```
517    ///
518    /// # See Also
519    ///
520    /// * [Self::cycles] - Returns the permutation as a sequence of cycles.
521    /// * [Self::index_cycles] - Returns the permutation as a sequence of index swaps.
522    /// * [Self::index_transpositions] - Returns the index permutation as a swap sequence.
523    pub fn transpositions(&self) -> Vec<(usize, usize)> {
524        let mut swaps_vec = Vec::new();
525        for cycle in self.cycles() {
526            for i in 0..cycle.len() - 1 {
527                swaps_vec.push((cycle[i], cycle[i + 1]));
528            }
529        }
530        swaps_vec
531    }
532
533    /// Return the index-space permutation as a sequence of swaps
534    ///
535    /// # Returns
536    ///
537    /// A vector of swaps, where each swap is a tuple of qudit indices.
538    ///
539    /// # Examples
540    ///
541    /// ```
542    /// use qudit_core::{Radices, QuditPermutation};
543    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
544    /// assert_eq!(qubit_swap.index_transpositions(), vec![(1, 2)]);
545    /// ```
546    ///
547    /// # See Also
548    ///
549    /// * [Self::cycles] - Returns the permutation as a sequence of cycles.
550    /// * [Self::transpositions] - Returns the permutation as a sequence of qudit swaps.
551    /// * [Self::index_cycles] - Returns the permutation as a sequence of index swaps.
552    pub fn index_transpositions(&self) -> Vec<(usize, usize)> {
553        let mut swaps_vec = Vec::new();
554        for cycle in self.index_cycles() {
555            for i in 0..cycle.len() - 1 {
556                swaps_vec.push((cycle[i], cycle[i + 1]));
557            }
558        }
559        swaps_vec
560    }
561
562    /// Swap the rows of a matrix according to the permutation.
563    ///
564    /// This function allocates and returns a new matrix. The permutation of
565    /// the rows is done in index space.
566    ///
567    /// # Arguments
568    ///
569    /// * `a` - The matrix to be permuted.
570    ///
571    /// # Returns
572    ///
573    /// A new matrix with the rows permuted.
574    ///
575    /// # Examples
576    ///
577    /// ```
578    /// use qudit_core::{Radices, QuditPermutation};
579    /// use faer::{mat, Mat};
580    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
581    /// let mat = mat![
582    ///     [1., 2., 3., 4.],
583    ///     [5., 6., 7., 8.],
584    ///     [9., 10., 11., 12.],
585    ///     [13., 14., 15., 16.]
586    /// ];
587    /// let permuted_mat = qubit_swap.swap_rows(&mat);
588    /// assert_eq!(permuted_mat, mat![
589    ///     [1., 2., 3., 4.],
590    ///     [9., 10., 11., 12.],
591    ///     [5., 6., 7., 8.],
592    ///     [13., 14., 15., 16.]
593    /// ]);
594    /// ```
595    ///
596    /// # See Also
597    ///
598    /// * [Self::swap_rows_to_buf] - Swaps the rows of a matrix witout allocations.
599    /// * [Self::swap_cols] - Swaps the columns of a matrix.
600    pub fn swap_rows<E: ComplexField>(
601        &self,
602        a: impl AsMatRef<T = E, Rows = usize, Cols = usize>,
603    ) -> Mat<E> {
604        let in_mat = a.as_mat_ref();
605        let p_mat = self.as_faer();
606        let mut out = Mat::zeros(in_mat.nrows(), in_mat.ncols());
607        permute_rows(out.as_mut(), in_mat, p_mat.as_ref());
608        out
609    }
610
611    /// Swap the rows of a matrix in place according to the permutation.
612    ///
613    /// # Arguments
614    ///
615    /// * `a` - The matrix to be permuted.
616    ///
617    /// # Panics
618    ///
619    /// * If the matrix dimensions do not match the dimension of the permutation.
620    ///
621    /// # Examples
622    ///
623    /// ```
624    /// use qudit_core::{Radices, QuditPermutation};
625    /// use faer::{mat, Mat};
626    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
627    /// let mut mat = mat![
628    ///     [1., 2., 3., 4.],
629    ///     [5., 6., 7., 8.],
630    ///     [9., 10., 11., 12.],
631    ///     [13., 14., 15., 16.]
632    /// ];
633    /// qubit_swap.swap_rows_in_place(&mut mat);
634    /// assert_eq!(mat, mat![
635    ///     [1., 2., 3., 4.],
636    ///     [9., 10., 11., 12.],
637    ///     [5., 6., 7., 8.],
638    ///     [13., 14., 15., 16.]
639    /// ]);
640    /// ```
641    ///
642    /// # See Also
643    ///
644    /// * [Self::swap_rows] - Swaps the rows of a matrix and returns a new matrix.
645    /// * [Self::swap_rows_to_buf] - Swaps the rows of a matrix without allocations.
646    /// * [Self::swap_cols_in_place] - Swaps the columns of a matrix in place.
647    /// * [Self::swap_cols_to_buf] - Swaps the columns of a matrix without allocations.
648    /// * [Self::apply] - Applies the permutation to a matrix and returns a new matrix.
649    ///
650    /// # Notes
651    ///
652    /// This is not entirely allocation free. The list of transpositions is calculated
653    /// resulting in a small allocation. To be entirely allocation free, one should
654    /// use the `swap_rows_to_buf` method instead.
655    pub fn swap_rows_in_place<E: ComplexField>(
656        &self,
657        mut a: impl AsMatMut<T = E, Rows = usize, Cols = usize>,
658    ) {
659        let mut in_mat = a.as_mat_mut();
660        let ncols = in_mat.ncols();
661        assert_eq!(in_mat.nrows(), self.dimension());
662        assert_eq!(in_mat.ncols(), self.dimension());
663        for cycle in self.index_transpositions() {
664            for col in 0..ncols {
665                // SAFETY: The matrix dimensions have been checked.
666                unsafe {
667                    let val0 = in_mat.rb().get_unchecked(cycle.0, col).clone();
668                    let val1 = in_mat.rb().get_unchecked(cycle.1, col).clone();
669                    *(in_mat.rb_mut().get_mut_unchecked(cycle.1, col)) = val0;
670                    *(in_mat.rb_mut().get_mut_unchecked(cycle.0, col)) = val1;
671                }
672            }
673        }
674    }
675
676    /// Swap the rows of a matrix from an input into an output buffer.
677    ///
678    /// This is an allocation free version of `swap_rows`.
679    ///
680    /// # Arguments
681    ///
682    /// * `a` - The matrix to be permuted.
683    ///
684    /// * `b` - The buffer to write the permuted matrix into.
685    ///
686    /// # Panics
687    ///
688    /// * If the matrix dimensions do not match the dimension of the permutation.
689    ///
690    /// # See Also
691    ///
692    /// * [Self::swap_rows] - Swaps the rows of a matrix and returns a new matrix.
693    /// * [Self::swap_rows_in_place] - Swaps the rows of a matrix in place.
694    /// * [Self::swap_cols_to_buf] - Swaps the columns of a matrix without allocations.
695    /// * [Self::apply_to_buf] - Applies the permutation to a matrix and writes the result into a buffer.
696    pub fn swap_rows_to_buf<E: ComplexField>(
697        &self,
698        a: impl AsMatRef<T = E, Rows = usize, Cols = usize>,
699        mut b: impl AsMatMut<T = E, Rows = usize, Cols = usize>,
700    ) {
701        let p_mat = self.as_faer();
702        permute_rows(b.as_mat_mut(), a.as_mat_ref(), p_mat.as_ref());
703    }
704
705    /// Swap the columns of a matrix according to the permutation.
706    ///
707    /// This function allocates and returns a new matrix. The permutation of
708    /// the columns is done in index space.
709    ///
710    /// # Arguments
711    ///
712    /// * `a` - The matrix to be permuted.
713    ///
714    /// # Returns
715    ///
716    /// A new matrix with the columns permuted.
717    ///
718    /// # Examples
719    ///
720    /// ```
721    /// use qudit_core::{Radices, QuditPermutation};
722    /// use faer::{mat, Mat};
723    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
724    /// let mat = mat![
725    ///     [1., 2., 3., 4.],
726    ///     [5., 6., 7., 8.],
727    ///     [9., 10., 11., 12.],
728    ///     [13., 14., 15., 16.]
729    /// ];
730    /// let permuted_mat = qubit_swap.swap_cols(&mat);
731    /// assert_eq!(permuted_mat, mat![
732    ///     [1., 3., 2., 4.],
733    ///     [5., 7., 6., 8.],
734    ///     [9., 11., 10., 12.],
735    ///     [13., 15., 14., 16.]
736    /// ]);
737    /// ```
738    ///
739    /// # See Also
740    ///
741    /// * [Self::swap_cols_to_buf] - Swaps the columns of a matrix without allocations.
742    /// * [Self::swap_rows] - Swaps the rows of a matrix.
743    /// * [Self::apply] - Applies the permutation to a matrix and returns a new matrix.
744    pub fn swap_cols<E: ComplexField>(
745        &self,
746        a: impl AsMatRef<T = E, Rows = usize, Cols = usize>,
747    ) -> Mat<E> {
748        let in_mat = a.as_mat_ref();
749        let p_mat = self.as_faer();
750        let mut out = Mat::zeros(in_mat.nrows(), in_mat.ncols());
751        permute_cols(out.as_mut(), in_mat, p_mat.as_ref());
752        out
753    }
754
755    /// Swap the columns of a matrix in place according to the permutation.
756    ///
757    /// # Arguments
758    ///
759    /// * `a` - The matrix to be permuted.
760    ///
761    /// # Panics
762    ///
763    /// * If the matrix dimensions do not match the dimension of the permutation.
764    ///
765    /// # Examples
766    ///
767    /// ```
768    /// use qudit_core::{Radices, QuditPermutation};
769    /// use faer::{mat, Mat};
770    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
771    /// let mut mat = mat![
772    ///     [1., 2., 3., 4.],
773    ///     [5., 6., 7., 8.],
774    ///     [9., 10., 11., 12.],
775    ///     [13., 14., 15., 16.]
776    /// ];
777    /// qubit_swap.swap_cols_in_place(mat.as_mut());
778    /// assert_eq!(mat, mat![
779    ///    [1., 3., 2., 4.],
780    ///    [5., 7., 6., 8.],
781    ///    [9., 11., 10., 12.],
782    ///    [13., 15., 14., 16.]
783    /// ]);
784    /// ```
785    ///
786    /// # See Also
787    ///
788    /// * [Self::swap_rows] - Swaps the rows of a matrix and returns a new matrix.
789    /// * [Self::swap_rows_to_buf] - Swaps the rows of a matrix without allocations.
790    /// * [Self::swap_rows_in_place] - Swaps the rows of a matrix in place.
791    /// * [Self::swap_cols] - Swaps the columns of a matrix with allocations.
792    /// * [Self::swap_cols_to_buf] - Swaps the columns of a matrix without allocations.
793    /// * [Self::apply] - Applies the permutation to a matrix and returns a new matrix.
794    ///
795    /// # Notes
796    ///
797    /// This is not entirely allocation free. The list of transpositions is calculated
798    /// resulting in a small allocation. To be entirely allocation free, one should
799    /// use the `swap_cols_to_buf` method instead.
800    pub fn swap_cols_in_place<E: ComplexField>(
801        &self,
802        mut a: impl AsMatMut<T = E, Rows = usize, Cols = usize>,
803    ) {
804        let mut in_mat = a.as_mat_mut();
805        assert_eq!(in_mat.nrows(), self.dimension());
806        assert_eq!(in_mat.ncols(), self.dimension());
807        for cycle in self.index_transpositions() {
808            for row in 0..in_mat.nrows() {
809                // SAFETY: The matrix dimensions have been checked.
810                unsafe {
811                    let val0 = in_mat.rb().get_unchecked(row, cycle.0).clone();
812                    let val1 = in_mat.rb().get_unchecked(row, cycle.1).clone();
813                    *(in_mat.rb_mut().get_mut_unchecked(row, cycle.1)) = val0;
814                    *(in_mat.rb_mut().get_mut_unchecked(row, cycle.0)) = val1;
815                }
816            }
817        }
818    }
819
820    /// Swap the columns of a matrix from an input into an output buffer.
821    ///
822    /// This is an allocation free version of `swap_cols`.
823    ///
824    /// # Arguments
825    ///
826    /// * `a` - The matrix to be permuted.
827    ///
828    /// * `b` - The buffer to write the permuted matrix into.
829    ///
830    /// # Panics
831    ///
832    /// * If the matrix dimensions do not match the dimension of the permutation.
833    ///
834    /// # See Also
835    ///
836    /// * [Self::swap_cols] - Swaps the columns of a matrix and returns a new matrix.
837    /// * [Self::swap_cols_in_place] - Swaps the columns of a matrix in place.
838    /// * [Self::swap_rows_to_buf] - Swaps the rows of a matrix without allocations.
839    /// * [Self::apply_to_buf] - Applies the permutation to a matrix and writes the result into a buffer.
840    /// * [Self::apply] - Applies the permutation to a matrix and returns a new matrix.
841    pub fn swap_cols_to_buf<E: ComplexField>(
842        &self,
843        a: impl AsMatRef<T = E, Rows = usize, Cols = usize>,
844        mut b: impl AsMatMut<T = E, Rows = usize, Cols = usize>,
845    ) {
846        let p_mat = self.as_faer();
847        permute_cols(b.as_mat_mut(), a.as_mat_ref(), p_mat.as_ref());
848    }
849
850    /// Apply the permutation to a matrix and return a new matrix.
851    ///
852    /// This is equivalent to calling `swap_rows` and then `swap_cols`.
853    ///
854    /// # Arguments
855    ///
856    /// * `a` - The matrix to be permuted.
857    ///
858    /// # Returns
859    ///
860    /// A new matrix with the rows and columns permuted.
861    ///
862    /// # Examples
863    ///
864    /// ```
865    /// use qudit_core::{Radices, QuditPermutation};
866    /// use faer::{mat, Mat};
867    /// let qubit_swap = QuditPermutation::from_qubit_location(&vec![1, 0]);
868    /// let mat = mat![
869    ///     [1., 2., 3., 4.],
870    ///     [5., 6., 7., 8.],
871    ///     [9., 10., 11., 12.],
872    ///     [13., 14., 15., 16.]
873    /// ];
874    /// let permuted_mat = qubit_swap.apply(&mat);
875    /// assert_eq!(permuted_mat, mat![
876    ///    [1., 3., 2., 4.],
877    ///    [9., 11., 10., 12.],
878    ///    [5., 7., 6., 8.],
879    ///    [13., 15., 14., 16.]
880    /// ]);
881    ///
882    /// // Applying qudit permutations can help convert between little
883    /// // and big endian gates.
884    /// let big_endian_cnot = mat![
885    ///     [1., 0., 0., 0.],
886    ///     [0., 1., 0., 0.],
887    ///     [0., 0., 0., 1.],
888    ///     [0., 0., 1., 0.]
889    /// ];
890    /// let little_endian_cnot = mat![
891    ///     [1., 0., 0., 0.],
892    ///     [0., 0., 0., 1.],
893    ///     [0., 0., 1., 0.],
894    ///     [0., 1., 0., 0.]
895    /// ];
896    /// assert_eq!(qubit_swap.apply(&big_endian_cnot), little_endian_cnot);
897    /// ```
898    ///
899    /// # See Also
900    ///
901    /// * [Self::swap_rows] - Swaps the rows of a matrix and returns a new matrix.
902    /// * [Self::swap_cols] - Swaps the columns of a matrix and returns a new matrix.
903    /// * [Self::apply_to_buf] - Applies the permutation to a matrix and writes the result into a buffer.
904    /// * [Self::apply_in_place] - Swaps the rows of a matrix in place.
905    pub fn apply<E: ComplexField>(
906        &self,
907        a: impl AsMatRef<T = E, Rows = usize, Cols = usize>,
908    ) -> Mat<E> {
909        self.swap_cols(self.swap_rows(a))
910    }
911
912    /// Apply the permutation to a matrix in place.
913    ///
914    /// # Arguments
915    ///
916    /// * `a` - The matrix to be permuted.
917    ///
918    /// # Returns
919    ///
920    /// A new matrix with the rows and columns permuted.
921    ///
922    /// # See Also
923    ///
924    /// * [Self::swap_rows_in_place] - Swaps the rows of a matrix in place.
925    /// * [Self::swap_cols_in_place] - Swaps the columns of a matrix in place.
926    /// * [Self::apply_to_buf] - Applies the permutation to a matrix and writes the result into a buffer.
927    /// * [Self::apply] - Applies the permutation to a matrix and returns a new matrix.
928    ///
929    /// # Notes
930    ///
931    /// This is not entirely allocation free. The list of transpositions is calculated
932    /// resulting in a small allocation. To be entirely allocation free, one should
933    /// use the `apply_to_buf` method instead.
934    pub fn apply_in_place<E: ComplexField>(
935        &self,
936        mut a: impl AsMatMut<T = E, Rows = usize, Cols = usize>,
937    ) {
938        self.swap_rows_in_place(a.as_mat_mut());
939        self.swap_cols_in_place(a.as_mat_mut());
940    }
941
942    /// Apply the permutation to a matrix and write the result into a buffer.
943    ///
944    /// This is an allocation free version of `apply`.
945    ///
946    /// # Arguments
947    ///
948    /// * `a` - The matrix to be permuted.
949    ///
950    /// * `b` - The buffer to write the permuted matrix into.
951    ///
952    /// # See Also
953    ///
954    /// * [Self::swap_rows_to_buf] - Swaps the rows of a matrix without allocations.
955    /// * [Self::swap_cols_to_buf] - Swaps the columns of a matrix without allocations.
956    /// * [Self::apply] - Applies the permutation to a matrix and returns a new matrix.
957    /// * [Self::apply_in_place] - Swaps the rows of a matrix in place.
958    pub fn apply_to_buf<E: ComplexField>(
959        &self,
960        a: impl AsMatRef<T = E, Rows = usize, Cols = usize>,
961        mut b: impl AsMatMut<T = E, Rows = usize, Cols = usize>,
962    ) {
963        let p_mat = self.as_faer();
964        permute_rows(b.as_mat_mut(), a.as_mat_ref(), p_mat.as_ref());
965        permute_cols(b.as_mat_mut(), a.as_mat_ref(), p_mat.as_ref());
966    }
967
968    /// Returns the permutation as an faer object.
969    fn as_faer(&self) -> Perm<usize> {
970        Perm::new_checked(
971            self.index_perm.clone().into(),
972            self.inverse_index_perm.clone().into(),
973            self.dimension(),
974        )
975    }
976
977    // Returns the permutation as a unitary matrix.
978    // // TODO: Update
979    // pub fn get_matrix<E: Entity>(&self) -> UnitaryMatrix<E> {
980    //     todo!()
981    // }
982}
983
984/// QuditPermutation can be dereferenced as a usize slice.
985impl core::ops::Deref for QuditPermutation {
986    type Target = [usize];
987
988    fn deref(&self) -> &Self::Target {
989        &self.perm
990    }
991}
992
993/// QuditPermutations permute a qudit system.
994impl QuditSystem for QuditPermutation {
995    /// Returns the radices of the system before being permuted.
996    fn radices(&self) -> Radices {
997        self.radices.clone()
998    }
999
1000    /// Returns the number of qudits being permuted.
1001    fn num_qudits(&self) -> usize {
1002        self.num_qudits
1003    }
1004
1005    /// Returns the dimension of the system being permuted.
1006    fn dimension(&self) -> usize {
1007        self.index_perm.len()
1008    }
1009}
1010
1011impl core::fmt::Display for QuditPermutation {
1012    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1013        write!(f, "[")?;
1014        for r in &self.perm {
1015            write!(f, "{}", r).unwrap();
1016        }
1017        write!(f, "]")?;
1018        Ok(())
1019    }
1020}
1021
1022#[cfg(test)]
1023pub mod strategies {
1024    // use super::*;
1025    // // use crate::radices::strategies::fixed_length_radices;
1026    // use crate::radices::Radices;
1027    // use proptest::prelude::*;
1028
1029    // pub fn perms(num_qudits: usize) -> impl Strategy<Value =
1030    // QuditPermutation> {     (
1031    //         fixed_length_radices(num_qudits, 5),
1032    //         Just(Vec::from_iter(0..num_qudits)).prop_shuffle(),
1033    //     )
1034    //         .prop_map(|(radices, perm)| QuditPermutation::new(radices, perm))
1035    // }
1036
1037    // pub fn perms_with_radices(radices: Radices) -> impl Strategy<Value =
1038    // QuditPermutation> {     Just(Vec::from_iter(0..radices.
1039    // get_num_qudits()))         .prop_shuffle()
1040    //         .prop_map(move |perm| QuditPermutation::new(radices.clone(),
1041    // perm)) }
1042
1043    // pub fn physical_perms(num_qudits: usize) -> impl Strategy<Value =
1044    // QuditPermutation> {     (
1045    //         fixed_length_radices(num_qudits, 5),
1046    //         Just(Vec::from_iter(0..num_qudits)).prop_shuffle(),
1047    //     )
1048    //         .prop_map(|(radices, perm)| QuditPermutation::new(radices, perm))
1049    //         .prop_filter("Permutation is not physical", |perm| {
1050    //             perm.has_physical_meaning()
1051    //         })
1052    // }
1053
1054    // pub fn qubit_perms(num_qudits: usize) -> impl Strategy<Value =
1055    // QuditPermutation> {     Just(Vec::from_iter(0..num_qudits))
1056    //         .prop_shuffle()
1057    //         .prop_map(move |perm| {
1058    //             QuditPermutation::new(Radices::new(vec![2; num_qudits]),
1059    // perm)         })
1060    // }
1061
1062    // pub fn qudit_perms(num_qudits: usize, radix: usize) -> impl
1063    // Strategy<Value = QuditPermutation> {     Just(Vec::from_iter(0..
1064    // num_qudits))         .prop_shuffle()
1065    //         .prop_map(move |perm| {
1066    //             QuditPermutation::new(Radices::new(vec![radix;
1067    // num_qudits]), perm)         })
1068    // }
1069
1070    // pub fn arbitrary_qudit_perms(num_qudits: usize) -> impl Strategy<Value =
1071    // QuditPermutation> {     (
1072    //         2..5usize,
1073    //         Just(Vec::from_iter(0..num_qudits)).prop_shuffle(),
1074    //     )
1075    //         .prop_map(move |(radix, perm)| {
1076    //             QuditPermutation::new(Radices::new(vec![radix;
1077    // num_qudits]), perm)         })
1078    // }
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083    // use super::QuditPermutation;
1084    // // use crate::math::hsd;
1085    // use crate::Gate;
1086    // use crate::Radices;
1087
1088    // #[test]
1089    // fn test_swap_as_perm() {
1090    //     for radix in 2..5 {
1091    //         let radices = Radices::new(vec![radix, radix]);
1092    //         let perm = QuditPermutation::new(radices, vec![1, 0]);
1093    //         let perm_mat = perm.get_matrix();
1094    //         assert_eq!(Gate::QuditSwap(radix).get_unitary(&[]), perm_mat);
1095    //     }
1096    // }
1097
1098    // #[test]
1099    // fn test_double_swap_as_perm() {
1100    //     for radix in 2..5 {
1101    //         let radices = Radices::new(vec![radix; 4]);
1102    //         let perm = QuditPermutation::new(radices, vec![1, 0, 3, 2]);
1103    //         let perm_mat = perm.get_matrix();
1104    //         let swap_utry = Gate::QuditSwap(radix).get_unitary(&[]);
1105    //         assert_eq!(kron(&swap_utry, &swap_utry), perm_mat);
1106    //     }
1107    // }
1108
1109    // #[test]
1110    // fn test_complicated_perm() {
1111    //     for radix in 2..3 {
1112    //         let radices = Radices::new(vec![radix; 4]);
1113    //         let perm = QuditPermutation::new(radices, vec![1, 3, 0, 2]);
1114    //         let perm_mat = perm.get_matrix();
1115    //         // for r in perm_mat.outer_iter() {
1116    //         //     let mut line_str = String::new();
1117    //         //     for c in r.iter() {
1118    //         //         line_str += &format!("{} + {}i, ", c.re, c.im);
1119    //         //     }
1120    //         //     println!("{}", line_str);
1121    //         // }
1122    //         let swap_utry = Gate::QuditSwap(radix).get_unitary(&[]);
1123    //         let id = Array::eye(radix);
1124    //         let c1 = kron(&id, &kron(&swap_utry, &id));
1125    //         let c2 = kron(&swap_utry, &swap_utry);
1126    //         println!("{}", hsd(c1.dot(&c2).view(), perm_mat.view()));
1127    //         assert!(hsd(c1.dot(&c2).view(), perm_mat.view()) < 1e-7);
1128    //     }
1129    // }
1130}