rustiq_core/structures/
clifford_circuit.rs

1use rand::Rng;
2#[derive(Debug, Clone, Copy, PartialEq)]
3pub enum CliffordGate {
4    CNOT(usize, usize),
5    CZ(usize, usize),
6    H(usize),
7    S(usize),
8    Sd(usize),
9    SqrtX(usize),
10    SqrtXd(usize),
11}
12impl CliffordGate {
13    pub fn dagger(&self) -> Self {
14        match self {
15            Self::S(i) => Self::Sd(*i),
16            Self::SqrtX(i) => Self::SqrtXd(*i),
17            Self::Sd(i) => Self::S(*i),
18            Self::SqrtXd(i) => Self::SqrtX(*i),
19            _ => *self,
20        }
21    }
22    pub fn to_vec(&self) -> (String, Vec<usize>) {
23        match self {
24            CliffordGate::CNOT(i, j) => ("CNOT".to_owned(), vec![*i, *j]),
25            CliffordGate::CZ(i, j) => ("CZ".to_owned(), vec![*i, *j]),
26            CliffordGate::H(i) => ("H".to_owned(), vec![*i]),
27            CliffordGate::S(i) => ("S".to_owned(), vec![*i]),
28            CliffordGate::Sd(i) => ("Sd".to_owned(), vec![*i]),
29            CliffordGate::SqrtX(i) => ("SqrtX".to_owned(), vec![*i]),
30            CliffordGate::SqrtXd(i) => ("SqrtXd".to_owned(), vec![*i]),
31        }
32    }
33    pub fn from_vec(gate: &str, qbits: &[usize]) -> Self {
34        match gate {
35            "H" => Self::H(qbits[0]),
36            "S" => Self::S(qbits[0]),
37            "Sd" => Self::Sd(qbits[0]),
38            "SqrtX" => Self::SqrtX(qbits[0]),
39            "SqrtXd" => Self::SqrtXd(qbits[0]),
40            "CX" => Self::CNOT(qbits[0], qbits[1]),
41            "CNOT" => Self::CNOT(qbits[0], qbits[1]),
42            "CZ" => Self::CZ(qbits[0], qbits[1]),
43            _ => panic!("Unknown gate {}", gate),
44        }
45    }
46    pub fn arity(&self) -> usize {
47        match self {
48            CliffordGate::CNOT(_, _) => 2,
49            CliffordGate::CZ(_, _) => 2,
50            _ => 1,
51        }
52    }
53}
54#[derive(Debug, Clone)]
55pub struct CliffordCircuit {
56    pub nqbits: usize,
57    pub gates: Vec<CliffordGate>,
58}
59
60impl CliffordCircuit {
61    pub fn new(nqbits: usize) -> Self {
62        Self {
63            nqbits,
64            gates: Vec::new(),
65        }
66    }
67    pub fn from_vec(gates: Vec<(String, Vec<usize>)>) -> Self {
68        let mut nqbits = 0;
69        for (_, qbits) in gates.iter() {
70            for qbit in qbits {
71                if qbit + 1 > nqbits {
72                    nqbits = qbit + 1;
73                }
74            }
75        }
76        Self {
77            nqbits,
78            gates: gates
79                .iter()
80                .map(|(gate, qbits)| CliffordGate::from_vec(gate, qbits))
81                .collect(),
82        }
83    }
84
85    pub fn random(nqubits: usize, ngates: usize) -> Self {
86        let mut rng = rand::thread_rng();
87        let mut circuit = Self::new(nqubits);
88        for _ in 0..ngates {
89            if rng.gen_bool(0.5) {
90                // CNOT
91                let i = rng.gen_range(0..nqubits);
92                let mut j = rng.gen_range(0..nqubits);
93                while j == i {
94                    j = rng.gen_range(0..nqubits);
95                }
96                circuit.gates.push(CliffordGate::CNOT(i, j));
97                continue;
98            }
99            if rng.gen_bool(0.5) {
100                // H
101                let i = rng.gen_range(0..nqubits);
102                circuit.gates.push(CliffordGate::H(i));
103                continue;
104            }
105            let i = rng.gen_range(0..nqubits);
106            circuit.gates.push(CliffordGate::S(i));
107        }
108        circuit
109    }
110
111    pub fn extend_with(&mut self, other: &CliffordCircuit) {
112        self.gates.extend_from_slice(&other.gates);
113    }
114    /// Counts the number of CNOT gates
115    pub fn cnot_count(&self) -> usize {
116        self.gates
117            .iter()
118            .filter(|gate| matches!(gate, CliffordGate::CNOT(_, _)))
119            .count()
120    }
121    /// Counts the number of CNOT gates
122    pub fn entangling_count(&self) -> usize {
123        self.gates
124            .iter()
125            .filter(|gate| matches!(gate, CliffordGate::CNOT(_, _) | CliffordGate::CZ(_, _)))
126            .count()
127    }
128    /// Computes the CNOT depth of the circuit
129    pub fn cnot_depth(&self) -> usize {
130        let mut depths: Vec<usize> = vec![0; self.nqbits];
131        for gate in self.gates.iter() {
132            if let CliffordGate::CNOT(i, j) = gate {
133                let gate_depth = std::cmp::max(depths[*i], depths[*j]) + 1;
134                depths[*i] = gate_depth;
135                depths[*j] = gate_depth;
136            }
137        }
138        *depths.iter().max().unwrap()
139    }
140    /// Computes the CNOT depth of the circuit
141    pub fn entangling_depth(&self) -> usize {
142        let mut depths: Vec<usize> = vec![0; self.nqbits];
143        for gate in self.gates.iter() {
144            match gate {
145                CliffordGate::CNOT(i, j) => {
146                    let gate_depth = std::cmp::max(depths[*i], depths[*j]) + 1;
147                    depths[*i] = gate_depth;
148                    depths[*j] = gate_depth;
149                }
150                CliffordGate::CZ(i, j) => {
151                    let gate_depth = std::cmp::max(depths[*i], depths[*j]) + 1;
152                    depths[*i] = gate_depth;
153                    depths[*j] = gate_depth;
154                }
155                _ => {}
156            }
157        }
158        *depths.iter().max().unwrap()
159    }
160    /// Returns the inverse of the circuit
161    pub fn dagger(&self) -> Self {
162        let new_gates = self.gates.iter().rev().map(|gate| gate.dagger()).collect();
163        Self {
164            nqbits: self.nqbits,
165            gates: new_gates,
166        }
167    }
168}