tnc/builders/
sycamore_circuit.rs

1//! Generating circuits similar to the Sycamore circuits.
2
3use std::f64::consts::{FRAC_PI_2, FRAC_PI_6};
4
5use rand::{seq::IndexedRandom, Rng};
6
7use crate::{
8    builders::{
9        circuit_builder::Circuit,
10        connectivity::{sycamore_a, sycamore_b, sycamore_c, sycamore_d},
11    },
12    tensornetwork::tensordata::TensorData,
13};
14
15/// Creates a circuit based on the Sycamore circuit scheme.
16///
17/// The `depth` is the number of rounds, where one round consists of a layer of
18/// single-qubit gates followed by a layer of two-qubit gates. The circuit is
19/// initialized in the |0> state.
20///
21/// For more details on the circuit, see <https://arxiv.org/abs/1910.11333>.
22pub fn sycamore_circuit<R>(qubits: usize, depth: usize, rng: &mut R) -> Circuit
23where
24    R: Rng,
25{
26    // TODO: if we generalize the connection patterns, we can allow arbitrary sizes here
27    assert!(
28        qubits <= 49,
29        "Currently only supports circuits of size equal to the original Sycamore experiment"
30    );
31    let mut rounds = [
32        sycamore_a, sycamore_b, sycamore_c, sycamore_d, sycamore_c, sycamore_d, sycamore_a,
33        sycamore_b,
34    ]
35    .iter()
36    .cycle();
37    let single_qubit_gates = [
38        TensorData::Gate((String::from("sx"), Vec::new(), false)),
39        TensorData::Gate((String::from("sy"), Vec::new(), false)),
40        TensorData::Gate((String::from("sz"), Vec::new(), false)),
41    ];
42    let two_qubit_gate =
43        TensorData::Gate((String::from("fsim"), vec![FRAC_PI_2, FRAC_PI_6], false));
44
45    // Initialize circuit
46    let mut circuit = Circuit::default();
47    let qreg = circuit.allocate_register(qubits);
48
49    // Add interleaved layers of random single-qubit gates and two-qubit gates
50    for round in 0..=depth {
51        // Add single-qubit gate layer
52        for i in 0..qubits {
53            let gate = single_qubit_gates.choose(rng).unwrap().clone();
54            circuit.append_gate(gate, &[qreg.qubit(i)]);
55        }
56
57        // In the last round, we only have a final single-qubit layer.
58        if round < depth {
59            // Add two-qubit gates with round-specific connectivity.
60            let layer = rounds.next().unwrap()();
61            for (i, j) in layer {
62                if i > qubits || j > qubits {
63                    continue;
64                }
65                let i = i - 1;
66                let j = j - 1;
67                circuit.append_gate(two_qubit_gate.clone(), &[qreg.qubit(i), qreg.qubit(j)]);
68            }
69        }
70    }
71    circuit
72}
73
74#[cfg(test)]
75mod tests {
76    use itertools::Itertools;
77    use rand::{rngs::StdRng, SeedableRng};
78
79    use super::*;
80
81    #[test]
82    fn small_sycamore() {
83        let mut rng = StdRng::seed_from_u64(42);
84        let circuit = sycamore_circuit(3, 3, &mut rng);
85        let (tn, _) = circuit.into_amplitude_network(&"0".repeat(3));
86
87        let rank_counts = tn.tensors().iter().counts_by(|t| t.legs().len());
88        assert_eq!(rank_counts.len(), 3);
89        // 3 initial state, 3 final state
90        assert_eq!(rank_counts[&1], 6);
91        // 4 * 3 single qubit gates
92        assert_eq!(rank_counts[&2], 12);
93        // 1 two-qubit gate (in round C)
94        assert_eq!(rank_counts[&4], 1);
95    }
96}