tnc/builders/
random_circuit.rs

1use itertools::Itertools;
2use rand::distr::Bernoulli;
3use rand::seq::IndexedRandom;
4use rand::Rng;
5use rustc_hash::FxHashMap;
6
7use crate::builders::circuit_builder::Circuit;
8use crate::builders::connectivity::{Connectivity, ConnectivityLayout};
9use crate::builders::tensorgeneration::random_sparse_tensor_data_with_rng;
10use crate::tensornetwork::tensor::Tensor;
11use crate::tensornetwork::tensordata::TensorData;
12use crate::utils::traits::WithCapacity;
13
14macro_rules! fsim {
15    ($a:expr, $b:expr, $c:expr) => {
16        $crate::tensornetwork::tensordata::TensorData::Gate((
17            String::from("fsim"),
18            vec![$a, $b],
19            $c,
20        ))
21    };
22}
23
24/// Creates a random circuit.
25///
26/// The circuit has `rounds` many rounds of single and two qubit gate layers. Gates
27/// are placed with the given probabilities and only on qubit pairs specified by the
28/// `connectivity`.
29pub fn random_circuit<R>(
30    qubits: usize,
31    rounds: usize,
32    single_qubit_probability: f64,
33    two_qubit_probability: f64,
34    rng: &mut R,
35    connectivity: ConnectivityLayout,
36) -> Tensor
37where
38    R: Rng,
39{
40    let single_qubit_gates = [
41        TensorData::Gate((String::from("sx"), Vec::new(), false)),
42        TensorData::Gate((String::from("sy"), Vec::new(), false)),
43        TensorData::Gate((String::from("sz"), Vec::new(), false)),
44    ];
45
46    let single_qubit_die = Bernoulli::new(single_qubit_probability).unwrap();
47    let two_qubit_die = Bernoulli::new(two_qubit_probability).unwrap();
48
49    // Get connectivity for given size
50    let connectivity_graph = Connectivity::new(connectivity);
51    let filtered_connectivity = connectivity_graph
52        .connectivity
53        .iter()
54        .filter(|&&(u, v)| u < qubits && v < qubits)
55        .collect_vec();
56
57    // Initialize circuit with random qubit states
58    let mut circuit = Circuit::default();
59    let qr = circuit.allocate_register(qubits);
60
61    for _ in 1..rounds {
62        for i in 0..qubits {
63            // Placing of random single qubit gate
64            if rng.sample(single_qubit_die) {
65                let gate = single_qubit_gates.choose(rng).unwrap().clone();
66                circuit.append_gate(gate, &[qr.qubit(i)]);
67            }
68        }
69        for (i, j) in &filtered_connectivity {
70            // Placing of random two qubit gate
71            if rng.sample(two_qubit_die) {
72                let gate = fsim!(0.3, 0.2, false);
73                circuit.append_gate(gate, &[qr.qubit(*i), qr.qubit(*j)]);
74            }
75        }
76    }
77
78    // Set up final state
79    circuit.into_amplitude_network(&"0".repeat(qubits)).0
80}
81
82/// Creates a random circuit.
83///
84/// The circuit has `rounds` many rounds of single and two qubit gate layers, then a
85/// layer of random observables followed by the same single and two qubit gate layers
86/// mirrored. Gates are placed with the given probabilities and only on qubit pairs
87/// specified by the `connectivity`.
88pub fn random_circuit_with_observable<R>(
89    qubits: usize,
90    round: usize,
91    single_qubit_probability: f64,
92    two_qubit_probability: f64,
93    observable_probability: f64,
94    rng: &mut R,
95    connectivity: ConnectivityLayout,
96) -> Tensor
97where
98    R: Rng,
99{
100    let observable_locations = (0..qubits)
101        .filter(|_| rng.random_bool(observable_probability))
102        .collect_vec();
103
104    random_circuit_with_set_observable(
105        qubits,
106        round,
107        single_qubit_probability,
108        two_qubit_probability,
109        &observable_locations,
110        rng,
111        connectivity,
112    )
113}
114
115/// Creates a random circuit with `rounds` many rounds of single and two qubit gate
116/// layers, then a layer of random observables followed by the same single and two
117/// qubit gate layers mirrored. The observables are placed on the given qubits. The
118/// gates are placed with the given probabilities and only on qubit pairs specified
119/// by the `connectivity`.
120pub fn random_circuit_with_set_observable<R>(
121    qubits: usize,
122    round: usize,
123    single_qubit_probability: f64,
124    two_qubit_probability: f64,
125    observable_location: &[usize],
126    rng: &mut R,
127    connectivity: ConnectivityLayout,
128) -> Tensor
129where
130    R: Rng,
131{
132    let single_qubit_gates = [
133        (
134            TensorData::Gate((String::from("sx"), Vec::new(), false)),
135            TensorData::Gate((String::from("sx"), Vec::new(), true)),
136        ),
137        (
138            TensorData::Gate((String::from("sy"), Vec::new(), false)),
139            TensorData::Gate((String::from("sx"), Vec::new(), true)),
140        ),
141        (
142            TensorData::Gate((String::from("sz"), Vec::new(), false)),
143            TensorData::Gate((String::from("sx"), Vec::new(), true)),
144        ),
145    ];
146
147    let observables = [
148        TensorData::Gate((String::from("x"), Vec::new(), false)),
149        TensorData::Gate((String::from("y"), Vec::new(), false)),
150        TensorData::Gate((String::from("z"), Vec::new(), false)),
151    ];
152
153    let single_qubit_die = Bernoulli::new(single_qubit_probability).unwrap();
154    let two_qubit_die = Bernoulli::new(two_qubit_probability).unwrap();
155
156    // Initialize tensornetwork of size `usize`
157    let mut random_tn = Tensor::default();
158
159    let mut open_edges = FxHashMap::with_capacity(qubits);
160
161    let mut next_edge = 0;
162
163    let mut final_state = Vec::with_capacity(observable_location.len());
164    for i in 0..qubits {
165        // Placing of random observable
166        if observable_location.contains(&i) {
167            open_edges.insert(i, (next_edge, next_edge + 1));
168            next_edge += 2;
169
170            let new_observable = observables.choose(rng).unwrap().clone();
171            let mut new_tensor =
172                Tensor::new_from_const(vec![open_edges[&i].0, open_edges[&i].1], 2);
173            new_tensor.set_tensor_data(new_observable);
174            final_state.push(new_tensor);
175        } else {
176            // set empty positions
177            open_edges.insert(i, (0, 0));
178        }
179    }
180    random_tn.push_tensors(final_state);
181
182    // Get connectivity for given size
183    let connectivity_graph = Connectivity::new(connectivity);
184    let filtered_connectivity = connectivity_graph
185        .connectivity
186        .iter()
187        .filter(|&&(u, v)| u < qubits && v < qubits)
188        .collect_vec();
189
190    let mut intermediate_gates = Vec::new();
191    for _ in 1..round {
192        // Placing of random two qubit gate if affects outcome of observable
193        for (i, j) in &filtered_connectivity {
194            // Placing of random two qubit gate
195            if rng.sample(two_qubit_die)
196                && (open_edges[i].0 != open_edges[i].1 || open_edges[j].0 != open_edges[j].1)
197            {
198                let (left_i_index, right_i_index) = if open_edges[i].0 != open_edges[i].1 {
199                    (open_edges[i].0, open_edges[i].1)
200                } else {
201                    next_edge += 1;
202                    (next_edge - 1, next_edge - 1)
203                };
204
205                let (left_j_index, right_j_index) = if open_edges[j].0 != open_edges[j].1 {
206                    (open_edges[j].0, open_edges[j].1)
207                } else {
208                    next_edge += 1;
209                    (next_edge - 1, next_edge - 1)
210                };
211
212                let mut left_new_tensor = Tensor::new_from_const(
213                    vec![next_edge, next_edge + 1, left_i_index, left_j_index],
214                    2,
215                );
216                left_new_tensor.set_tensor_data(fsim!(0.3, 0.2, false));
217                intermediate_gates.push(left_new_tensor);
218
219                let mut right_new_tensor = Tensor::new_from_const(
220                    vec![right_i_index, right_j_index, next_edge + 2, next_edge + 3],
221                    2,
222                );
223                right_new_tensor.set_tensor_data(fsim!(0.3, 0.2, true));
224                intermediate_gates.push(right_new_tensor);
225
226                open_edges.insert(*i, (next_edge, next_edge + 2));
227                open_edges.insert(*j, (next_edge + 1, next_edge + 3));
228
229                next_edge += 4;
230            }
231        }
232
233        for i in 0..qubits {
234            // Placing of random single qubit gate if affects outcome of observable
235            let (left_index, right_index) = open_edges[&i];
236            if rng.sample(single_qubit_die) && left_index != right_index {
237                let (left_new_gate, right_new_gate) =
238                    single_qubit_gates.choose(rng).unwrap().clone();
239
240                let mut left_new_tensor = Tensor::new_from_const(vec![next_edge, left_index], 2);
241                left_new_tensor.set_tensor_data(left_new_gate);
242                intermediate_gates.push(left_new_tensor);
243
244                let mut right_new_tensor =
245                    Tensor::new_from_const(vec![right_index, next_edge + 1], 2);
246                right_new_tensor.set_tensor_data(right_new_gate);
247                intermediate_gates.push(right_new_tensor);
248
249                open_edges.insert(i, (next_edge, next_edge + 1));
250                next_edge += 2;
251            }
252        }
253    }
254    random_tn.push_tensors(intermediate_gates);
255
256    // set up random initial state
257    let mut initial_state = Vec::with_capacity(qubits);
258    for i in 0..qubits {
259        let (left_index, right_index) = open_edges[&i];
260        if left_index != right_index {
261            let random_sparse_tensor = random_sparse_tensor_data_with_rng(&[2], Some(1f32), rng);
262
263            let mut left_new_state = Tensor::new_from_const(vec![left_index], 2);
264            left_new_state.set_tensor_data(random_sparse_tensor.clone());
265            initial_state.push(left_new_state);
266
267            let mut right_new_state = Tensor::new_from_const(vec![right_index], 2);
268            right_new_state.set_tensor_data(random_sparse_tensor);
269            initial_state.push(right_new_state);
270        }
271    }
272    random_tn.push_tensors(initial_state);
273
274    random_tn
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280
281    use std::iter::zip;
282
283    use rand::rng;
284
285    use crate::builders::connectivity::ConnectivityLayout;
286    use crate::tensornetwork::tensor::Tensor;
287
288    #[test]
289    fn test_random_circuit_with_observable() {
290        let size = 4;
291        let rounds = 3;
292        let single_qubit_probability = 1f64;
293        let two_qubit_probability = 1f64;
294        let observable_probability = 1f64;
295        // results should be independent of rng used.
296        let mut rng = rng();
297        let connectivity = ConnectivityLayout::Line(size);
298        let circuit = random_circuit_with_observable(
299            size,
300            rounds,
301            single_qubit_probability,
302            two_qubit_probability,
303            observable_probability,
304            &mut rng,
305            connectivity,
306        );
307        let ref_legs = [
308            Tensor::new_from_const(vec![0, 1], 2),
309            Tensor::new_from_const(vec![2, 3], 2),
310            Tensor::new_from_const(vec![4, 5], 2),
311            Tensor::new_from_const(vec![6, 7], 2),
312            Tensor::new_from_const(vec![8, 9, 0, 2], 2),
313            Tensor::new_from_const(vec![1, 3, 10, 11], 2),
314            Tensor::new_from_const(vec![12, 13, 9, 4], 2),
315            Tensor::new_from_const(vec![11, 5, 14, 15], 2),
316            Tensor::new_from_const(vec![16, 17, 13, 6], 2),
317            Tensor::new_from_const(vec![15, 7, 18, 19], 2),
318            Tensor::new_from_const(vec![20, 8], 2),
319            Tensor::new_from_const(vec![10, 21], 2),
320            Tensor::new_from_const(vec![22, 12], 2),
321            Tensor::new_from_const(vec![14, 23], 2),
322            Tensor::new_from_const(vec![24, 16], 2),
323            Tensor::new_from_const(vec![18, 25], 2),
324            Tensor::new_from_const(vec![26, 17], 2),
325            Tensor::new_from_const(vec![19, 27], 2),
326            Tensor::new_from_const(vec![28, 29, 20, 22], 2),
327            Tensor::new_from_const(vec![21, 23, 30, 31], 2),
328            Tensor::new_from_const(vec![32, 33, 29, 24], 2),
329            Tensor::new_from_const(vec![31, 25, 34, 35], 2),
330            Tensor::new_from_const(vec![36, 37, 33, 26], 2),
331            Tensor::new_from_const(vec![35, 27, 38, 39], 2),
332            Tensor::new_from_const(vec![40, 28], 2),
333            Tensor::new_from_const(vec![30, 41], 2),
334            Tensor::new_from_const(vec![42, 32], 2),
335            Tensor::new_from_const(vec![34, 43], 2),
336            Tensor::new_from_const(vec![44, 36], 2),
337            Tensor::new_from_const(vec![38, 45], 2),
338            Tensor::new_from_const(vec![46, 37], 2),
339            Tensor::new_from_const(vec![39, 47], 2),
340            Tensor::new_from_const(vec![40], 2),
341            Tensor::new_from_const(vec![41], 2),
342            Tensor::new_from_const(vec![42], 2),
343            Tensor::new_from_const(vec![43], 2),
344            Tensor::new_from_const(vec![44], 2),
345            Tensor::new_from_const(vec![45], 2),
346            Tensor::new_from_const(vec![46], 2),
347            Tensor::new_from_const(vec![47], 2),
348        ];
349
350        assert_eq!(circuit.tensors().len(), 40);
351        for (tensor, ref_tensor) in zip(circuit.tensors(), ref_legs) {
352            assert_eq!(tensor.legs(), ref_tensor.legs());
353            assert_eq!(tensor.bond_dims(), ref_tensor.bond_dims());
354        }
355    }
356
357    #[test]
358    fn test_random_circuit_with_set_observable() {
359        let size = 4;
360        let rounds = 3;
361        let single_qubit_probability = 1f64;
362        let two_qubit_probability = 1f64;
363        let observable_location = &[2];
364        // results should be independent of rng used.
365        let mut rng = rng();
366        let connectivity = ConnectivityLayout::Line(size);
367        let circuit = random_circuit_with_set_observable(
368            size,
369            rounds,
370            single_qubit_probability,
371            two_qubit_probability,
372            observable_location,
373            &mut rng,
374            connectivity,
375        );
376        let ref_legs = [
377            Tensor::new_from_const(vec![0, 1], 2),
378            Tensor::new_from_const(vec![3, 4, 2, 0], 2),
379            Tensor::new_from_const(vec![2, 1, 5, 6], 2),
380            Tensor::new_from_const(vec![8, 9, 4, 7], 2),
381            Tensor::new_from_const(vec![6, 7, 10, 11], 2),
382            Tensor::new_from_const(vec![12, 3], 2),
383            Tensor::new_from_const(vec![5, 13], 2),
384            Tensor::new_from_const(vec![14, 8], 2),
385            Tensor::new_from_const(vec![10, 15], 2),
386            Tensor::new_from_const(vec![16, 9], 2),
387            Tensor::new_from_const(vec![11, 17], 2),
388            Tensor::new_from_const(vec![19, 20, 18, 12], 2),
389            Tensor::new_from_const(vec![18, 13, 21, 22], 2),
390            Tensor::new_from_const(vec![23, 24, 20, 14], 2),
391            Tensor::new_from_const(vec![22, 15, 25, 26], 2),
392            Tensor::new_from_const(vec![27, 28, 24, 16], 2),
393            Tensor::new_from_const(vec![26, 17, 29, 30], 2),
394            Tensor::new_from_const(vec![31, 19], 2),
395            Tensor::new_from_const(vec![21, 32], 2),
396            Tensor::new_from_const(vec![33, 23], 2),
397            Tensor::new_from_const(vec![25, 34], 2),
398            Tensor::new_from_const(vec![35, 27], 2),
399            Tensor::new_from_const(vec![29, 36], 2),
400            Tensor::new_from_const(vec![37, 28], 2),
401            Tensor::new_from_const(vec![30, 38], 2),
402            Tensor::new_from_const(vec![31], 2),
403            Tensor::new_from_const(vec![32], 2),
404            Tensor::new_from_const(vec![33], 2),
405            Tensor::new_from_const(vec![34], 2),
406            Tensor::new_from_const(vec![35], 2),
407            Tensor::new_from_const(vec![36], 2),
408            Tensor::new_from_const(vec![37], 2),
409            Tensor::new_from_const(vec![38], 2),
410        ];
411
412        assert_eq!(circuit.tensors().len(), 33);
413        for (tensor, ref_tensor) in zip(circuit.tensors(), ref_legs) {
414            assert_eq!(tensor.legs(), ref_tensor.legs());
415            assert_eq!(tensor.bond_dims(), ref_tensor.bond_dims());
416        }
417    }
418}