1use crate::builders::circuit_builder::Circuit;
2use crate::io::qasm::{
3 ast::Visitor, circuit_creator::CircuitCreator, expression_folder::ExpressionFolder,
4 gate_inliner::GateInliner, include_resolver::expand_includes, parser::parse,
5};
6
7pub fn import_qasm<S>(code: S) -> Circuit
13where
14 S: Into<String>,
15{
16 let mut full_code = code.into();
18 expand_includes(&mut full_code);
19
20 let mut program = parse(&full_code);
22
23 let mut expression_folder = ExpressionFolder;
25 expression_folder.visit_program(&mut program);
26
27 let mut inliner = GateInliner::default();
29 inliner.inline_program(&mut program);
30
31 let mut expression_folder = ExpressionFolder;
33 expression_folder.visit_program(&mut program);
34
35 let circuit_creator = CircuitCreator;
37 circuit_creator.create_circuit(&program)
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 use std::f64::consts::FRAC_1_SQRT_2;
45
46 use approx::assert_abs_diff_eq;
47 use num_complex::Complex64;
48
49 use crate::{
50 builders::circuit_builder::Permutor,
51 contractionpath::ContractionPath,
52 tensornetwork::{
53 contraction::contract_tensor_network,
54 tensor::{CompositeTensor, EdgeIndex, LeafTensor, TensorIndex},
55 tensordata::TensorData,
56 },
57 };
58
59 fn edge_connects(
61 edge_id: EdgeIndex,
62 t1_id: TensorIndex,
63 t2_id: TensorIndex,
64 tn: &CompositeTensor,
65 ) -> bool {
66 let t1 = tn.tensor(t1_id).as_leaf().unwrap();
67 let t2 = tn.tensor(t2_id).as_leaf().unwrap();
68 let overlap = t1 & t2;
69 overlap.legs().contains(&edge_id)
70 }
71
72 fn is_open_edge_of(edge_id: EdgeIndex, t1_id: TensorIndex, tn: &CompositeTensor) -> bool {
74 let t1 = tn.tensor(t1_id).as_leaf().unwrap();
76 if !t1.legs().contains(&edge_id) {
77 return false;
78 }
79
80 for (tensor_id, tensor) in tn.tensors().iter().enumerate() {
82 let tensor = tensor.as_leaf().unwrap();
83 if tensor_id != t1_id && tensor.legs().contains(&edge_id) {
84 return false;
85 }
86 }
87 true
88 }
89
90 struct IdTensor<'a> {
91 id: usize,
92 tensor: &'a LeafTensor,
93 }
94
95 fn get_quantum_tensors(
96 tn: &CompositeTensor,
97 ) -> (Vec<IdTensor<'_>>, Vec<IdTensor<'_>>, Vec<IdTensor<'_>>) {
98 let mut kets = Vec::new();
99 let mut single_qubit_gates = Vec::new();
100 let mut two_qubit_gates = Vec::new();
101 for (tid, tensor) in tn.tensors().iter().enumerate() {
102 let leaf = tensor.as_leaf().unwrap();
103 let id: usize = tid;
104 let legs = leaf.legs().len();
105 match legs {
106 1 => kets.push(IdTensor { id, tensor: leaf }),
107 2 => single_qubit_gates.push(IdTensor { id, tensor: leaf }),
108 4 => two_qubit_gates.push(IdTensor { id, tensor: leaf }),
109 _ => panic!("Tensor with unexpected leg count {legs} in quantum tensor network"),
110 }
111 }
112 (kets, single_qubit_gates, two_qubit_gates)
113 }
114
115 #[test]
116 fn bell_tensornetwork_construction() {
117 let code = "OPENQASM 2.0;
118 include \"qelib1.inc\";
119 qreg q[2];
120 h q[0];
121 cx q[0], q[1];
122 ";
123 let circuit = import_qasm(code);
124 let (tn, _) = circuit.into_statevector_network();
125
126 let (kets, single_qubit_gates, two_qubit_gates) = get_quantum_tensors(&tn);
127 let [k0, k1] = kets.as_slice() else { panic!() };
128 let [h] = single_qubit_gates.as_slice() else {
129 panic!()
130 };
131 let [cx] = two_qubit_gates.as_slice() else {
132 panic!()
133 };
134
135 let first_qubit_id = h.tensor.legs()[1];
138 let (first_qubit, second_qubit) = if first_qubit_id == k0.id {
139 (k0, k1)
140 } else if first_qubit_id == k1.id {
141 (k1, k0)
142 } else {
143 panic!("H gate tensor not connected to any ket tensor");
144 };
145
146 let fq_to_h_id = first_qubit.tensor.legs()[0];
148 assert_eq!(h.tensor.legs()[1], fq_to_h_id);
149 assert!(edge_connects(fq_to_h_id, first_qubit_id, h.id, &tn));
150
151 let sq_to_cx_t_id = second_qubit.tensor.legs()[0];
152 assert_eq!(cx.tensor.legs()[3], sq_to_cx_t_id);
153 assert!(edge_connects(sq_to_cx_t_id, second_qubit.id, cx.id, &tn));
154
155 let h_to_cx_c_id = h.tensor.legs()[0];
156 assert_eq!(cx.tensor.legs()[2], h_to_cx_c_id);
157 assert!(edge_connects(h_to_cx_c_id, h.id, cx.id, &tn));
158
159 let cx_c_to_open_id = cx.tensor.legs()[0];
160 assert!(is_open_edge_of(cx_c_to_open_id, cx.id, &tn));
161
162 let cx_t_to_open_id = cx.tensor.legs()[1];
163 assert!(is_open_edge_of(cx_t_to_open_id, cx.id, &tn));
164 }
165
166 fn contract_tn(tn: CompositeTensor, perm: &Permutor) -> TensorData {
169 let opt_path =
170 ContractionPath::simple((1..tn.tensors().len()).map(|tid| (0, tid)).collect());
171 let leaf = contract_tensor_network(tn, &opt_path);
172 let leaf = perm.apply(leaf);
173 leaf.into_data()
174 }
175
176 #[test]
177 fn bell_contract() {
178 let code = "OPENQASM 2.0;
179 include \"qelib1.inc\";
180 qreg q[2];
181 h q[0];
182 cx q[0], q[1];
183 ";
184 let circuit = import_qasm(code);
185 let (tn, perm) = circuit.into_statevector_network();
186 let resulting_state = contract_tn(tn, &perm);
187
188 let expected = TensorData::new_from_data(
189 &[2, 2],
190 vec![
191 Complex64::new(FRAC_1_SQRT_2, 0.),
192 Complex64::ZERO,
193 Complex64::ZERO,
194 Complex64::new(FRAC_1_SQRT_2, 0.),
195 ],
196 );
197 assert_abs_diff_eq!(&resulting_state, &expected);
198 }
199
200 #[test]
201 fn custom_swap() {
202 let code = "OPENQASM 2.0;
203 include \"qelib1.inc\";
204 qreg q[2];
205 gate myswap a, b {
206 cx a, b;
207 cx b, a;
208 cx a, b;
209 }
210 x q[0];
211 myswap q[1], q[0];
212 ";
213 let circuit = import_qasm(code);
214 let (tn, perm) = circuit.into_statevector_network();
215 let resulting_state = contract_tn(tn, &perm);
216
217 let expected = TensorData::new_from_data(
218 &[2, 2],
219 vec![
220 Complex64::ZERO,
221 Complex64::ONE,
222 Complex64::ZERO,
223 Complex64::ZERO,
224 ],
225 );
226 assert_abs_diff_eq!(&resulting_state, &expected);
227 }
228
229 fn odd_test_circuit() -> Circuit {
230 let code = "OPENQASM 2.0;
232 include \"qelib1.inc\";
233 qreg q[3];
234 rx(0.5) q[0];
235 rx(0.2) q[1];
236 rx(0.3) q[2];
237 cx q[0], q[1];
238 cx q[1], q[2];";
239 import_qasm(code)
240 }
241
242 #[test]
243 fn statevector_order() {
244 let circuit = odd_test_circuit();
245 let (tn, perm) = circuit.into_statevector_network();
246 let resulting_state = contract_tn(tn, &perm);
247
248 let expected = TensorData::new_from_data(
249 &[2, 2, 2],
250 vec![
251 Complex64::new(0.953246407214305, 0.0),
252 Complex64::new(0.0, -0.14406910361762032),
253 Complex64::new(-0.014455126269118733, 0.0),
254 Complex64::new(0.0, -0.09564366568448116),
255 Complex64::new(-0.024421837348497916, 0.0),
256 Complex64::new(0.0, 0.0036909997130494475),
257 Complex64::new(-0.03678688170631573, 0.0),
258 Complex64::new(0.0, -0.24340376901515096),
259 ],
260 );
261 assert_abs_diff_eq!(&resulting_state, &expected);
262 }
263
264 #[test]
265 fn statevector_order_two_fixed_qubits() {
266 let circuit = odd_test_circuit();
267 let (tn, perm) = circuit.into_amplitude_network("1*0");
269 let resulting_state = contract_tn(tn, &perm);
270
271 let expected = TensorData::new_from_data(
272 &[2],
273 vec![
274 Complex64::new(-0.024421837348497916, 0.0),
275 Complex64::new(-0.03678688170631573, 0.0),
276 ],
277 );
278 assert_abs_diff_eq!(&resulting_state, &expected);
279 }
280
281 #[test]
282 fn statevector_order_one_fixed_qubit() {
283 let circuit = odd_test_circuit();
284 let (tn, perm) = circuit.into_amplitude_network("*1*");
286 let resulting_state = contract_tn(tn, &perm);
287
288 let expected = TensorData::new_from_data(
289 &[2, 2],
290 vec![
291 Complex64::new(-0.014455126269118733, 0.0),
292 Complex64::new(0.0, -0.09564366568448116),
293 Complex64::new(-0.03678688170631573, 0.0),
294 Complex64::new(0.0, -0.24340376901515096),
295 ],
296 );
297 assert_abs_diff_eq!(&resulting_state, &expected);
298 }
299
300 #[test]
301 fn gate_order() {
302 let code = "OPENQASM 2.0;
304 include \"qelib1.inc\";
305 qreg q[2];
306 creg c[1];
307 u2(0,0) q[0];
308 u2(-pi,-pi) q[1];
309 cx q[0],q[1];
310 u2(-pi,-pi) q[0];";
311
312 let circuit = import_qasm(code);
313 let (tn, perm) = circuit.into_statevector_network();
314 let resulting_state = contract_tn(tn, &perm);
315
316 let expected = TensorData::new_from_data(
317 &[2, 2],
318 vec![
319 Complex64::ZERO,
320 Complex64::ZERO,
321 Complex64::new(-FRAC_1_SQRT_2, 0.0),
322 Complex64::new(FRAC_1_SQRT_2, 0.0),
323 ],
324 );
325 assert_abs_diff_eq!(&resulting_state, &expected);
326 }
327}