1use std::marker::PhantomData;
4
5use itertools::Itertools;
6use num_complex::Complex64;
7use permutation::Permutation;
8
9use crate::{
10 tensornetwork::{
11 tensor::{CompositeTensor, EdgeIndex, LeafTensor},
12 tensordata::TensorData,
13 },
14 utils::traits::PermutationToVec,
15};
16
17#[derive(Debug)]
21pub struct QuantumRegister<'a> {
22 base: usize,
23 size: usize,
24 phantom: PhantomData<&'a Circuit>,
25}
26
27impl QuantumRegister<'_> {
28 #[cfg(test)]
31 pub(crate) fn new(size: usize) -> Self {
32 QuantumRegister {
33 base: 0,
34 size,
35 phantom: PhantomData,
36 }
37 }
38
39 pub fn qubit(&self, index: usize) -> Qubit<'_> {
41 assert!(index < self.size);
42 Qubit {
43 index: self.base + index,
44 phantom: PhantomData,
45 }
46 }
47
48 pub fn qubits(&self) -> impl Iterator<Item = Qubit<'_>> {
50 (self.base..self.base + self.size).map(|i| Qubit {
51 index: i,
52 phantom: PhantomData,
53 })
54 }
55
56 #[inline]
58 pub fn len(&self) -> usize {
59 self.size
60 }
61
62 #[inline]
64 pub fn is_empty(&self) -> bool {
65 self.size == 0
66 }
67}
68
69pub struct Qubit<'a> {
71 index: usize,
72 phantom: PhantomData<&'a Circuit>,
73}
74
75#[derive(Debug, Clone)]
77pub struct Permutor {
78 target_leg_order: Vec<EdgeIndex>,
79}
80
81impl Permutor {
82 fn new(target_legs: Vec<EdgeIndex>) -> Self {
83 Self {
84 target_leg_order: target_legs,
85 }
86 }
87
88 pub fn apply(&self, tensor: LeafTensor) -> LeafTensor {
90 if self.is_identity() {
91 return tensor;
92 }
93
94 let (mut legs, mut bond_dims, tensordata) = tensor.into_inner();
95 let mut data = tensordata.into_data();
96
97 let mut perm = Self::permutation_between(&legs, &self.target_leg_order);
99
100 perm.apply_slice_in_place(&mut legs);
102 perm.apply_slice_in_place(&mut bond_dims);
103 data = data.permuted_axes(perm.to_vec());
104
105 LeafTensor::new_with_data(legs, bond_dims, TensorData::Matrix(data))
106 }
107
108 #[inline]
111 pub fn is_identity(&self) -> bool {
112 self.target_leg_order.is_empty()
113 }
114
115 fn permutation_between(given: &[usize], target: &[usize]) -> Permutation {
118 let given_to_sorted = permutation::sort_unstable(given);
119 let target_to_sorted = permutation::sort_unstable(target);
120 &target_to_sorted.inverse() * &given_to_sorted
121 }
122}
123
124#[derive(Debug, Default)]
127pub struct Circuit {
128 open_edges: Vec<EdgeIndex>,
130 next_edge: usize,
132 tensor_network: CompositeTensor,
134}
135
136impl Circuit {
137 fn ket0() -> TensorData {
139 TensorData::new_from_data(&[2], vec![Complex64::ONE, Complex64::ZERO])
140 }
141
142 fn ket1() -> TensorData {
144 TensorData::new_from_data(&[2], vec![Complex64::ZERO, Complex64::ONE])
145 }
146
147 fn z() -> TensorData {
149 TensorData::Gate((String::from("z"), vec![], false))
150 }
151
152 fn new_edge(&mut self) -> usize {
154 let edge = self.next_edge;
155 self.next_edge += 1;
156 edge
157 }
158
159 #[inline]
170 pub fn num_qubits(&self) -> usize {
171 self.open_edges.len()
172 }
173
174 pub fn allocate_register<'a>(&mut self, size: usize) -> QuantumRegister<'a> {
177 let previous_qubits = self.num_qubits();
178
179 self.open_edges.reserve(size);
180 self.tensor_network.reserve(size);
181 for _ in 0..size {
182 let edge = self.new_edge();
183 self.open_edges.push(edge);
184 let mut ket0 = LeafTensor::new_from_const(vec![edge], 2);
185 ket0.set_tensor_data(Self::ket0());
186 self.tensor_network.push_tensor(ket0);
187 }
188
189 QuantumRegister {
190 base: previous_qubits,
191 size,
192 phantom: PhantomData,
193 }
194 }
195
196 pub fn append_gate(&mut self, gate: TensorData, indices: &[Qubit]) {
198 assert!(
199 indices.iter().map(|q| q.index).all_unique(),
200 "Qubit arguments must be unique"
201 );
202
203 let old_edges = indices.iter().map(|q| self.open_edges[q.index]);
205 let new_edges = (0..indices.len()).map(|e| e + self.next_edge);
206 let edges = new_edges.chain(old_edges).collect_vec();
207 self.next_edge += indices.len();
208
209 for (q, next_edge) in indices.iter().zip(&edges[..indices.len()]) {
211 self.open_edges[q.index] = *next_edge;
212 }
213
214 let mut new_tensor = LeafTensor::new_from_const(edges, 2);
216 new_tensor.set_tensor_data(gate);
217
218 self.tensor_network.push_tensor(new_tensor);
220 }
221
222 pub fn into_amplitude_network(mut self, bitstring: &str) -> (CompositeTensor, Permutor) {
236 assert_eq!(bitstring.len(), self.num_qubits());
237
238 self.tensor_network.reserve(bitstring.len());
240 let mut final_legs = Vec::new();
241 for (c, e) in bitstring.chars().zip(self.open_edges) {
242 let bra = match c {
243 '0' => Self::ket0(),
244 '1' => Self::ket1(),
245 '*' => {
246 final_legs.push(e);
247 continue; }
249 _ => panic!("Only 0, 1 and * are allowed in bitstring"),
250 };
251 let mut tensor = LeafTensor::new_from_const(vec![e], 2);
252 tensor.set_tensor_data(bra);
253 self.tensor_network.push_tensor(tensor);
254 }
255
256 let permutor = Permutor::new(final_legs);
261 (self.tensor_network, permutor)
262 }
263
264 #[inline]
270 pub fn into_statevector_network(self) -> (CompositeTensor, Permutor) {
271 let qubits = self.num_qubits();
272 self.into_amplitude_network(&"*".repeat(qubits))
273 }
274
275 fn tensor_adjoint(tensor: &LeafTensor, leg_offset: usize) -> LeafTensor {
279 let half = tensor.legs().len() / 2;
281 let legs = tensor.legs()[half..]
282 .iter()
283 .chain(&tensor.legs()[..half])
284 .map(|l| l + leg_offset)
285 .collect();
286 let bond_dims = tensor.bond_dims()[half..]
287 .iter()
288 .chain(&tensor.bond_dims()[..half])
289 .copied()
290 .collect();
291
292 let data = tensor.tensor_data().clone();
294 let data = data.adjoint();
295
296 LeafTensor::new_with_data(legs, bond_dims, data)
297 }
298
299 pub fn into_expectation_value_network(mut self) -> CompositeTensor {
305 let offset = self.next_edge;
306 self.tensor_network
307 .reserve(self.tensor_network.len() + self.num_qubits());
308
309 let mut adjoint_tensors = Vec::with_capacity(self.tensor_network.len());
311 for tensor in self.tensor_network.tensors() {
312 let tensor = tensor.as_leaf().unwrap();
313 let adjoint = Self::tensor_adjoint(tensor, offset);
314 adjoint_tensors.push(adjoint);
315 }
316 self.tensor_network.push_tensors(adjoint_tensors);
317
318 for e in self.open_edges {
320 let mut t = LeafTensor::new_from_const(vec![e, e + offset], 2);
321 t.set_tensor_data(Self::z());
322 self.tensor_network.push_tensor(t);
323 }
324
325 self.tensor_network
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332
333 use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_3, FRAC_PI_4};
334
335 use approx::assert_abs_diff_eq;
336 use num_complex::Complex64;
337
338 use crate::{
339 contractionpath::paths::{
340 cotengrust::{Cotengrust, OptMethod},
341 ContractionPathResult, Pathfinder,
342 },
343 path,
344 tensornetwork::{contraction::contract_tensor_network, tensordata::TensorData},
345 };
346
347 fn test_permutation_between(given: &[usize], target: &[usize]) {
348 let perm = Permutor::permutation_between(given, target);
349 assert_eq!(perm.apply_slice(given), target);
350 }
351
352 #[test]
353 fn permutation_between() {
354 test_permutation_between(&[1, 2, 3, 4], &[1, 2, 3, 4]);
355 test_permutation_between(&[1, 2, 3, 4], &[4, 3, 2, 1]);
356 test_permutation_between(&[4, 3, 2, 1], &[1, 2, 3, 4]);
357 test_permutation_between(&[4, 1, 3, 2], &[2, 4, 3, 1]);
358 test_permutation_between(&[5, 1, 4, 3, 2, 6], &[1, 6, 3, 5, 2, 4]);
359 }
360
361 #[test]
362 fn hadamards_amplitude() {
363 let qubits = 5;
364 let mut circuit = Circuit::default();
365 let qr = circuit.allocate_register(qubits);
366 for q in qr.qubits() {
367 circuit.append_gate(TensorData::Gate((String::from("h"), vec![], false)), &[q]);
368 }
369 let (tensor_network, permutor) = circuit.into_amplitude_network("00000");
370 assert!(permutor.is_identity());
371
372 let mut opt = Cotengrust::new(OptMethod::Greedy);
373 let result = opt.find_path(&tensor_network);
374 let path = result.replace_path();
375
376 let result = contract_tensor_network(tensor_network, &path);
377
378 let mut tn_ref = LeafTensor::default();
379 tn_ref.set_tensor_data(TensorData::new_from_data(
380 &[],
381 vec![Complex64::new(FRAC_1_SQRT_2.powi(qubits as i32), 0.0)],
382 ));
383
384 assert_abs_diff_eq!(&result, &tn_ref);
385 }
386
387 #[test]
388 fn rx_expectation_value() {
389 let qubits = 2;
390 let mut circuit = Circuit::default();
391 let qr = circuit.allocate_register(qubits);
392 circuit.append_gate(
393 TensorData::Gate((String::from("rx"), vec![FRAC_PI_4], false)),
394 &[qr.qubit(0)],
395 );
396 circuit.append_gate(
397 TensorData::Gate((String::from("rx"), vec![FRAC_PI_3], false)),
398 &[qr.qubit(1)],
399 );
400 let tensor_network = circuit.into_expectation_value_network();
401
402 let mut opt = Cotengrust::new(OptMethod::Greedy);
403 let result = opt.find_path(&tensor_network);
404 let path = result.replace_path();
405
406 let result = contract_tensor_network(tensor_network, &path);
407
408 let mut tn_ref = LeafTensor::default();
409 tn_ref.set_tensor_data(TensorData::new_from_data(
410 &[],
411 vec![Complex64::new(FRAC_1_SQRT_2 * 0.5, 0.0)],
412 ));
413
414 assert_abs_diff_eq!(&result, &tn_ref);
415 }
416
417 #[test]
418 #[should_panic(expected = "Qubit arguments must be unique")]
419 fn duplicate_qubit_arg() {
420 let mut circuit = Circuit::default();
421 let qr = circuit.allocate_register(2);
422 circuit.append_gate(
423 TensorData::Gate((String::from("cx"), vec![], true)),
424 &[qr.qubit(1), qr.qubit(1)],
425 );
426 }
427
428 #[test]
429 fn dimension_order() {
430 let mut circuit = Circuit::default();
431 let qr = circuit.allocate_register(1);
432 circuit.append_gate(
433 TensorData::new_from_data(
434 &[2, 2],
435 vec![
436 Complex64::new(1.0, 0.0),
437 Complex64::new(2.0, 0.0),
438 Complex64::new(3.0, 0.0),
439 Complex64::new(4.0, 0.0),
440 ],
441 ),
442 &[qr.qubit(0)],
443 );
444 let (tensor_network, permutor) = circuit.into_statevector_network();
445 let result = contract_tensor_network(tensor_network, &path![(0, 1)]);
446 let result = permutor.apply(result);
447 let mut tn_ref = LeafTensor::new_from_const(vec![1], 2);
448 tn_ref.set_tensor_data(TensorData::new_from_data(
449 &[2],
450 vec![Complex64::new(1.0, 0.0), Complex64::new(3.0, 0.0)],
451 ));
452 assert_abs_diff_eq!(&result, &tn_ref);
453 }
454
455 #[test]
456 fn permute() {
457 let mut tensor = LeafTensor::new_from_const(vec![0, 1, 2], 2);
458 let data = (0..8).map(|i| Complex64::new(i as f64, 0.0)).collect();
459 tensor.set_tensor_data(TensorData::new_from_data(&[2, 2, 2], data));
460
461 let permutor = Permutor::new(vec![2, 0, 1]);
462 let permuted = permutor.apply(tensor);
463
464 let ref_data = [0, 2, 4, 6, 1, 3, 5, 7]
465 .into_iter()
466 .map(|i| Complex64::new(i as f64, 0.0))
467 .collect();
468 let mut expected = LeafTensor::new_from_const(vec![2, 0, 1], 2);
469 expected.set_tensor_data(TensorData::new_from_data(&[2, 2, 2], ref_data));
470
471 assert_abs_diff_eq!(&permuted, &expected);
472 }
473}