nmr_schedule/modifiers/
tm_filter.rs

1use core::fmt::Display;
2
3use crate::{
4    generators::{Generator, Trace},
5    modifier, Schedule,
6};
7
8pub(crate) mod generators {
9    /// The generator after applying [`super::TMFilter`]
10    #[derive(Debug)]
11    pub struct TMFilterGenerator<T> {
12        pub(crate) generator: T,
13    }
14}
15
16use alloc::vec::Vec;
17use generators::*;
18use ndarray::Ix1;
19
20use super::{Filter, Modifier};
21
22/// The trace for `TMFilter`
23#[derive(Debug)]
24pub struct TMFilterTrace {
25    /// Gives the indices of each swap performed in the schedule
26    pub swaps_done: Vec<usize>,
27}
28
29impl Display for TMFilterTrace {
30    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31        write!(f, "Performed {} swaps", self.swaps_done.len())
32    }
33}
34
35impl<T: Generator<Ix1>> Generator<Ix1> for TMFilterGenerator<T> {
36    fn _generate(&self, count: usize, dims: Ix1, iteration: u64) -> Trace<Ix1> {
37        let trace = self
38            .generator
39            .generate_with_iter_and_trace(count, dims, iteration);
40
41        TMFilter::filter_from_iter_and_trace(&TMFilter::new(), trace, iteration)
42    }
43}
44
45modifier!(
46    TMFilter<Ix1>,
47    TMFilterBuilder,
48    r#"Filter the schedule to reduce patterns by swapping samples to make the schedule "look like" the Thue-Morse sequence.
49    
50The Thue-Morse sequence is known to avoid consecutive patterns and this effectively transfers that property to the schedule.
51    
52The iteration parameter alters the slice of the Thue-Morse sequence that is used for filtering."#,
53    tm_filter,
54);
55
56impl Modifier<Ix1> for TMFilter {
57    type Output<T: Generator<Ix1>> = TMFilterGenerator<T>;
58
59    fn modify<T: Generator<Ix1>>(self, generator: T) -> Self::Output<T> {
60        TMFilterGenerator { generator }
61    }
62}
63
64impl Filter<Ix1> for TMFilter {
65    fn filter_with_iter_and_trace(&self, sched: Schedule<Ix1>, iteration: u64) -> Trace<Ix1> {
66        if sched.len() < 2 {
67            return Trace::new(
68                sched,
69                TMFilterTrace {
70                    swaps_done: Vec::new(),
71                },
72            );
73        }
74
75        let mut sched = sched.into_inner();
76
77        const fn thue_morse(i: u64) -> bool {
78            i.count_ones() % 2 == 0
79        }
80
81        let mut swaps_done = Vec::new();
82
83        for i in 1..sched.len() - 2 {
84            let v64 = i as u64;
85            if sched[i] == thue_morse(v64 + iteration + 1)
86                && sched[i + 1] == thue_morse(v64 + iteration)
87                && sched[i] != sched[i + 1]
88            {
89                swaps_done.push(i);
90                sched[i] = thue_morse(v64 + iteration);
91                sched[i + 1] = thue_morse(v64 + iteration + 1);
92            }
93        }
94
95        Trace::new(Schedule::new(sched), TMFilterTrace { swaps_done })
96    }
97}