1use std::marker::PhantomData;
4
5use itertools::Itertools;
6use num_complex::Complex64;
7use permutation::Permutation;
8
9use crate::tensornetwork::{
10 tensor::{EdgeIndex, Tensor},
11 tensordata::TensorData,
12};
13
14#[derive(Debug)]
18pub struct QuantumRegister<'a> {
19 base: usize,
20 size: usize,
21 phantom: PhantomData<&'a Circuit>,
22}
23
24impl QuantumRegister<'_> {
25 #[cfg(test)]
28 pub(crate) fn new(size: usize) -> Self {
29 QuantumRegister {
30 base: 0,
31 size,
32 phantom: PhantomData,
33 }
34 }
35
36 pub fn qubit(&self, index: usize) -> Qubit<'_> {
38 assert!(index < self.size);
39 Qubit {
40 index: self.base + index,
41 phantom: PhantomData,
42 }
43 }
44
45 pub fn qubits(&self) -> impl Iterator<Item = Qubit<'_>> {
47 (self.base..self.base + self.size).map(|i| Qubit {
48 index: i,
49 phantom: PhantomData,
50 })
51 }
52
53 #[inline]
55 pub fn len(&self) -> usize {
56 self.size
57 }
58
59 #[inline]
61 pub fn is_empty(&self) -> bool {
62 self.size == 0
63 }
64}
65
66pub struct Qubit<'a> {
68 index: usize,
69 phantom: PhantomData<&'a Circuit>,
70}
71
72#[derive(Debug, Clone)]
74pub struct Permutor {
75 target_leg_order: Vec<EdgeIndex>,
76}
77
78impl Permutor {
79 fn new(target_legs: Vec<EdgeIndex>) -> Self {
80 Self {
81 target_leg_order: target_legs,
82 }
83 }
84
85 pub fn apply(&self, tensor: Tensor) -> Tensor {
87 assert!(tensor.is_leaf());
88 if self.is_identity() {
89 return tensor;
90 }
91
92 let Tensor {
93 mut legs,
94 mut bond_dims,
95 tensordata,
96 ..
97 } = tensor;
98 let mut data = tensordata.into_data();
99
100 let mut perm = Self::permutation_between(&legs, &self.target_leg_order);
102
103 perm.apply_slice_in_place(&mut legs);
105 perm.apply_slice_in_place(&mut bond_dims);
106 data.transpose(&perm);
107
108 Tensor {
109 tensors: vec![],
110 legs,
111 bond_dims,
112 tensordata: TensorData::Matrix(data),
113 }
114 }
115
116 #[inline]
119 pub fn is_identity(&self) -> bool {
120 self.target_leg_order.is_empty()
121 }
122
123 fn permutation_between(given: &[usize], target: &[usize]) -> Permutation {
126 let given_to_sorted = permutation::sort_unstable(given);
127 let target_to_sorted = permutation::sort_unstable(target);
128 &target_to_sorted.inverse() * &given_to_sorted
129 }
130}
131
132#[derive(Debug, Default)]
135pub struct Circuit {
136 open_edges: Vec<EdgeIndex>,
138 next_edge: usize,
140 tensors: Vec<Tensor>,
142}
143
144impl Circuit {
145 fn ket0() -> TensorData {
147 TensorData::new_from_data(&[2], vec![Complex64::ONE, Complex64::ZERO], None)
148 }
149
150 fn ket1() -> TensorData {
152 TensorData::new_from_data(&[2], vec![Complex64::ZERO, Complex64::ONE], None)
153 }
154
155 fn z() -> TensorData {
157 TensorData::Gate((String::from("z"), vec![], false))
158 }
159
160 fn new_edge(&mut self) -> usize {
162 let edge = self.next_edge;
163 self.next_edge += 1;
164 edge
165 }
166
167 #[inline]
178 pub fn num_qubits(&self) -> usize {
179 self.open_edges.len()
180 }
181
182 pub fn allocate_register<'a>(&mut self, size: usize) -> QuantumRegister<'a> {
185 let previous_qubits = self.num_qubits();
186
187 self.open_edges.reserve(size);
188 self.tensors.reserve(size);
189 for _ in 0..size {
190 let edge = self.new_edge();
191 self.open_edges.push(edge);
192 let mut ket0 = Tensor::new_from_const(vec![edge], 2);
193 ket0.set_tensor_data(Self::ket0());
194 self.tensors.push(ket0);
195 }
196
197 QuantumRegister {
198 base: previous_qubits,
199 size,
200 phantom: PhantomData,
201 }
202 }
203
204 pub fn append_gate(&mut self, gate: TensorData, indices: &[Qubit]) {
206 assert!(
207 indices.iter().map(|q| q.index).all_unique(),
208 "Qubit arguments must be unique"
209 );
210
211 let old_edges = indices.iter().map(|q| self.open_edges[q.index]);
213 let new_edges = (0..indices.len()).map(|e| e + self.next_edge);
214 let edges = old_edges.chain(new_edges).collect_vec();
215 self.next_edge += indices.len();
216
217 for (q, next_edge) in indices.iter().zip(&edges[indices.len()..]) {
219 self.open_edges[q.index] = *next_edge;
220 }
221
222 let mut new_tensor = Tensor::new_from_const(edges, 2);
224 new_tensor.set_tensor_data(gate);
225
226 self.tensors.push(new_tensor);
228 }
229
230 pub fn into_amplitude_network(mut self, bitstring: &str) -> (Tensor, Permutor) {
244 assert_eq!(bitstring.len(), self.num_qubits());
245
246 self.tensors.reserve(bitstring.len());
248 let mut final_legs = Vec::new();
249 for (c, e) in bitstring.chars().zip(self.open_edges) {
250 let bra = match c {
251 '0' => Self::ket0(),
252 '1' => Self::ket1(),
253 '*' => {
254 final_legs.push(e);
255 continue; }
257 _ => panic!("Only 0, 1 and * are allowed in bitstring"),
258 };
259 let mut tensor = Tensor::new_from_const(vec![e], 2);
260 tensor.set_tensor_data(bra);
261 self.tensors.push(tensor);
262 }
263
264 let out = Tensor::new_composite(self.tensors);
269 let permutor = Permutor::new(final_legs);
270 (out, permutor)
271 }
272
273 #[inline]
279 pub fn into_statevector_network(self) -> (Tensor, Permutor) {
280 let qubits = self.num_qubits();
281 self.into_amplitude_network(&"*".repeat(qubits))
282 }
283
284 fn tensor_adjoint(tensor: &Tensor, leg_offset: usize) -> Tensor {
288 let half = tensor.legs().len() / 2;
290 let legs = tensor.legs()[half..]
291 .iter()
292 .chain(&tensor.legs()[..half])
293 .map(|l| l + leg_offset)
294 .collect();
295 let bond_dims = tensor.bond_dims()[half..]
296 .iter()
297 .chain(&tensor.bond_dims()[..half])
298 .copied()
299 .collect();
300
301 let data = tensor.tensor_data().clone();
303 let data = data.adjoint();
304
305 let mut adjoint = Tensor::new(legs, bond_dims);
306 adjoint.set_tensor_data(data);
307 adjoint
308 }
309
310 pub fn into_expectation_value_network(mut self) -> Tensor {
316 let offset = self.next_edge;
317 self.tensors.reserve(self.tensors.len() + self.num_qubits());
318
319 let mut adjoint_tensors = Vec::with_capacity(self.tensors.len());
321 for tensor in &self.tensors {
322 let adjoint = Self::tensor_adjoint(tensor, offset);
323 adjoint_tensors.push(adjoint);
324 }
325 self.tensors.append(&mut adjoint_tensors);
326
327 for e in self.open_edges {
329 let mut t = Tensor::new_from_const(vec![e, e + offset], 2);
330 t.set_tensor_data(Self::z());
331 self.tensors.push(t);
332 }
333
334 Tensor::new_composite(self.tensors)
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_3, FRAC_PI_4};
343
344 use float_cmp::assert_approx_eq;
345 use num_complex::Complex64;
346
347 use crate::{
348 contractionpath::paths::{
349 cotengrust::{Cotengrust, OptMethod},
350 FindPath,
351 },
352 tensornetwork::{
353 contraction::contract_tensor_network, tensor::Tensor, tensordata::TensorData,
354 },
355 };
356
357 fn test_permutation_between(given: &[usize], target: &[usize]) {
358 let perm = Permutor::permutation_between(given, target);
359 assert_eq!(perm.apply_slice(given), target);
360 }
361
362 #[test]
363 fn permutation_between() {
364 test_permutation_between(&[1, 2, 3, 4], &[1, 2, 3, 4]);
365 test_permutation_between(&[1, 2, 3, 4], &[4, 3, 2, 1]);
366 test_permutation_between(&[4, 3, 2, 1], &[1, 2, 3, 4]);
367 test_permutation_between(&[4, 1, 3, 2], &[2, 4, 3, 1]);
368 test_permutation_between(&[5, 1, 4, 3, 2, 6], &[1, 6, 3, 5, 2, 4]);
369 }
370
371 #[test]
372 fn hadamards_amplitude() {
373 let qubits = 5;
374 let mut circuit = Circuit::default();
375 let qr = circuit.allocate_register(qubits);
376 for q in qr.qubits() {
377 circuit.append_gate(TensorData::Gate((String::from("h"), vec![], false)), &[q]);
378 }
379 let (tensor_network, permutor) = circuit.into_amplitude_network("00000");
380 assert!(permutor.is_identity());
381
382 let mut opt = Cotengrust::new(&tensor_network, OptMethod::Greedy);
383 opt.find_path();
384 let path = opt.get_best_replace_path();
385
386 let result = contract_tensor_network(tensor_network, &path);
387
388 let mut tn_ref = Tensor::default();
389 tn_ref.set_tensor_data(TensorData::new_from_data(
390 &[],
391 vec![Complex64::new(FRAC_1_SQRT_2.powi(qubits as i32), 0.0)],
392 None,
393 ));
394
395 assert_approx_eq!(&Tensor, &result, &tn_ref);
396 }
397
398 #[test]
399 fn rx_expectation_value() {
400 let qubits = 2;
401 let mut circuit = Circuit::default();
402 let qr = circuit.allocate_register(qubits);
403 circuit.append_gate(
404 TensorData::Gate((String::from("rx"), vec![FRAC_PI_4], false)),
405 &[qr.qubit(0)],
406 );
407 circuit.append_gate(
408 TensorData::Gate((String::from("rx"), vec![FRAC_PI_3], false)),
409 &[qr.qubit(1)],
410 );
411 let tensor_network = circuit.into_expectation_value_network();
412
413 let mut opt = Cotengrust::new(&tensor_network, OptMethod::Greedy);
414 opt.find_path();
415 let path = opt.get_best_replace_path();
416
417 let result = contract_tensor_network(tensor_network, &path);
418
419 let mut tn_ref = Tensor::default();
420 tn_ref.set_tensor_data(TensorData::new_from_data(
421 &[],
422 vec![Complex64::new(FRAC_1_SQRT_2 * 0.5, 0.0)],
423 None,
424 ));
425
426 assert_approx_eq!(&Tensor, &result, &tn_ref);
427 }
428
429 #[test]
430 #[should_panic(expected = "Qubit arguments must be unique")]
431 fn duplicate_qubit_arg() {
432 let mut circuit = Circuit::default();
433 let qr = circuit.allocate_register(2);
434 circuit.append_gate(
435 TensorData::Gate((String::from("cx"), vec![], true)),
436 &[qr.qubit(1), qr.qubit(1)],
437 );
438 }
439}