rustiq-core 0.0.11

Quantum circuit synthesis library in rust
Documentation
use std::collections::HashSet;

use super::greedy_order_preserving::pauli_network_synthesis_no_permutation;
use super::greedy_pauli_network::pauli_network_synthesis;
use crate::structures::{
    CliffordCircuit, CliffordGate, IsometryTableau, Metric, PauliLike, PauliSet,
};
use crate::synthesis::clifford::isometry::isometry_synthesis;
use rand::thread_rng;
use rand::Rng;

fn permute_input(pset: &mut PauliSet) -> Vec<usize> {
    let mut rng = thread_rng();
    let mut permutation = (0..pset.n).collect::<Vec<usize>>();
    if pset.n <= 1 {
        return permutation;
    }
    for _ in 0..pset.n * pset.n {
        let i = rng.gen_range(0..pset.n - 1);
        let j = rng.gen_range(i + 1..pset.n);
        permutation.swap(i, j);
        pset.swap_qbits(i, j);
    }
    permutation
}

fn permute_circuit(circuit: &CliffordCircuit, permutation: &[usize]) -> CliffordCircuit {
    let mut output = CliffordCircuit::new(circuit.nqbits);
    for gate in circuit.gates.iter() {
        match gate {
            CliffordGate::H(i) => output.gates.push(CliffordGate::H(permutation[*i])),
            CliffordGate::S(i) => output.gates.push(CliffordGate::S(permutation[*i])),
            CliffordGate::Sd(i) => output.gates.push(CliffordGate::Sd(permutation[*i])),
            CliffordGate::SqrtX(i) => output.gates.push(CliffordGate::SqrtX(permutation[*i])),
            CliffordGate::SqrtXd(i) => output.gates.push(CliffordGate::SqrtXd(permutation[*i])),
            CliffordGate::CNOT(a, b) => output
                .gates
                .push(CliffordGate::CNOT(permutation[*a], permutation[*b])),
            CliffordGate::CZ(a, b) => output
                .gates
                .push(CliffordGate::CZ(permutation[*a], permutation[*b])),
        }
    }
    output
}

pub fn check_circuit(input: &[String], circuit: &CliffordCircuit) {
    let mut hit_map: HashSet<usize> = HashSet::new();
    let mut bucket = PauliSet::from_slice(input);
    for i in 0..bucket.len() {
        if bucket.support_size(i) == 0 || bucket.support_size(i) == 1 {
            hit_map.insert(i);
        }
    }
    for gate in circuit.gates.iter() {
        bucket.conjugate_with_gate(gate);

        for i in 0..bucket.len() {
            if bucket.support_size(i) == 1 {
                hit_map.insert(i);
            }
        }
    }
    assert!(
        hit_map.len() == input.len(),
        "Synthesized {} operators, expected {}",
        hit_map.len(),
        input.len()
    );
}

pub fn greedy_pauli_network(
    operator_sequence: &PauliSet,
    metric: &Metric,
    preserve_order: bool,
    nshuffles: usize,
    skip_sort: bool,
    fix_clifford: bool,
) -> CliffordCircuit {
    let synth = if preserve_order {
        pauli_network_synthesis_no_permutation
    } else {
        pauli_network_synthesis
    };
    let mut circuit = synth(&mut operator_sequence.clone(), metric, skip_sort);
    let mut cost = metric.on_circuit(&circuit);
    for _ in 0..nshuffles {
        let mut pset = operator_sequence.clone();
        let permutation = permute_input(&mut pset);
        let new_circuit = synth(&mut pset, metric, skip_sort);
        let new_cost = metric.on_circuit(&new_circuit);
        if new_cost < cost {
            cost = new_cost;
            circuit = permute_circuit(&new_circuit, &permutation);
        }
    }
    if fix_clifford {
        let mut tableau = IsometryTableau::new(circuit.nqbits, 0);
        tableau.conjugate_with_circuit(&circuit.dagger());
        let fix = isometry_synthesis(&tableau, metric, 100);
        circuit.extend_with(&fix);
    }
    circuit
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_inifinte_loop_identities() {
        let mut operator_sequence = PauliSet::new(4);
        operator_sequence.insert("XZYX", false);
        operator_sequence.insert("XXIY", false);
        operator_sequence.insert("IIII", false);
        operator_sequence.insert("ZZYI", false);
        operator_sequence.insert("XXZZ", false);
        operator_sequence.insert("ZYZY", false);

        let metric = Metric::DEPTH;
        let preserve_order = true;
        let nshuffles = 0;

        let _result = greedy_pauli_network(
            &operator_sequence,
            &metric,
            preserve_order,
            nshuffles,
            false,
            true,
        );
    }
    #[test]
    fn test_fix_clifford() {
        let mut operator_sequence = PauliSet::new(4);
        operator_sequence.insert("XZYX", false);
        operator_sequence.insert("XXIY", false);
        operator_sequence.insert("ZZYI", false);
        operator_sequence.insert("XXZZ", false);
        operator_sequence.insert("ZYZY", false);

        let metric = Metric::COUNT;
        let preserve_order = true;
        let nshuffles = 0;

        let result = greedy_pauli_network(
            &operator_sequence,
            &metric,
            preserve_order,
            nshuffles,
            false,
            true,
        );
        check_circuit(
            &[
                "XZYX".to_owned(),
                "XXIY".to_owned(),
                "ZZYI".to_owned(),
                "XXZZ".to_owned(),
                "ZYZY".to_owned(),
            ],
            &result,
        );
        let mut clifford = IsometryTableau::new(4, 0);
        clifford.conjugate_with_circuit(&result);
        for i in 0..clifford.n {
            let mut st = "".to_owned();
            for q in 0..clifford.n {
                if q != i {
                    st += "I";
                } else {
                    st += "X";
                }
            }
            let (_, t) = clifford.logicals.get(i);
            assert_eq!(t, st);
            let mut st = "".to_owned();
            for q in 0..clifford.n {
                if q != i {
                    st += "I";
                } else {
                    st += "Z";
                }
            }
            let (_, t) = clifford.logicals.get(i + clifford.n);
            assert_eq!(t, st);
        }
    }
    #[test]
    fn test_shuffle() {
        let mut operator_sequence = PauliSet::new(4);
        operator_sequence.insert("XZYX", false);
        operator_sequence.insert("XXIY", false);
        operator_sequence.insert("ZZYI", false);
        operator_sequence.insert("XZZZ", false);
        operator_sequence.insert("ZYZY", false);

        let metric = Metric::COUNT;
        let preserve_order = true;
        let nshuffles = 10;

        let result = greedy_pauli_network(
            &operator_sequence,
            &metric,
            preserve_order,
            nshuffles,
            false,
            false,
        );
        check_circuit(
            &[
                "XZYX".to_owned(),
                "XXIY".to_owned(),
                "ZZYI".to_owned(),
                "XZZZ".to_owned(),
                "ZYZY".to_owned(),
            ],
            &result,
        );
    }
}