quantrs2_sim/
utils.rs

1use scirs2_core::ndarray::{Array1, Array2};
2use scirs2_core::parallel_ops::*;
3use scirs2_core::Complex64;
4
5use quantrs2_core::qubit::QubitId;
6
7/// Calculate the kronecker product of two matrices
8pub fn kron(a: &Array2<Complex64>, b: &Array2<Complex64>) -> Array2<Complex64> {
9    let a_shape = a.shape();
10    let b_shape = b.shape();
11
12    let rows = a_shape[0] * b_shape[0];
13    let cols = a_shape[1] * b_shape[1];
14
15    let mut result = Array2::zeros((rows, cols));
16
17    for i in 0..a_shape[0] {
18        for j in 0..a_shape[1] {
19            let a_val = a[[i, j]];
20            let i_offset = i * b_shape[0];
21            let j_offset = j * b_shape[1];
22
23            for k in 0..b_shape[0] {
24                for l in 0..b_shape[1] {
25                    result[[i_offset + k, j_offset + l]] = a_val * b[[k, l]];
26                }
27            }
28        }
29    }
30
31    result
32}
33
34/// Calculate the tensor product of two state vectors
35pub fn tensor_product(a: &Array1<Complex64>, b: &Array1<Complex64>) -> Array1<Complex64> {
36    let a_len = a.len();
37    let b_len = b.len();
38    let result_len = a_len * b_len;
39
40    let mut result = Array1::zeros(result_len);
41
42    for i in 0..a_len {
43        let a_val = a[i];
44        let offset = i * b_len;
45
46        for j in 0..b_len {
47            result[offset + j] = a_val * b[j];
48        }
49    }
50
51    result
52}
53
54/// Calculate the bit representation of an integer
55pub fn int_to_bits(n: usize, num_bits: usize) -> Vec<u8> {
56    let mut bits = vec![0; num_bits];
57    for i in 0..num_bits {
58        bits[num_bits - 1 - i] = ((n >> i) & 1) as u8;
59    }
60    bits
61}
62
63/// Calculate the integer representation of a bit string
64pub fn bits_to_int(bits: &[u8]) -> usize {
65    bits.iter().fold(0, |acc, &bit| (acc << 1) | bit as usize)
66}
67
68/// Compute the index with a bit flipped at the specified position
69pub const fn flip_bit(index: usize, pos: usize) -> usize {
70    index ^ (1 << pos)
71}
72
73/// Compute the index with a controlled bit flip
74///
75/// If the control bit at ctrl_pos is 1, then the target bit at target_pos is flipped.
76pub const fn controlled_flip(index: usize, ctrl_pos: usize, target_pos: usize) -> usize {
77    if (index >> ctrl_pos) & 1 == 1 {
78        flip_bit(index, target_pos)
79    } else {
80        index
81    }
82}
83
84/// Compute the global index for a multi-qubit system
85///
86/// Given a list of qubit indices, compute the global index into the state vector.
87pub fn compute_index(qubit_indices: &[QubitId], state_bits: &[u8]) -> usize {
88    assert!(
89        (qubit_indices.len() == state_bits.len()),
90        "Mismatch between qubit indices and state bits"
91    );
92
93    let mut index = 0;
94    for (&qubit, &bit) in qubit_indices.iter().zip(state_bits.iter()) {
95        let q = qubit.id() as usize;
96        if bit != 0 {
97            index |= 1 << q;
98        }
99    }
100
101    index
102}
103
104/// Parallel map over state vector elements with index
105///
106/// Apply a function to each element of the state vector in parallel.
107/// The function is passed the index and current value of each element.
108pub fn par_indexed_map<F>(state: &mut [Complex64], f: F)
109where
110    F: Fn(usize, Complex64) -> Complex64 + Sync,
111{
112    state.par_iter_mut().enumerate().for_each(|(i, v)| {
113        *v = f(i, *v);
114    });
115}
116
117/// Convert a matrix representation of a gate (row-major) to a 2D ndarray
118pub fn gate_vec_to_array2(matrix: &[Complex64], dim: usize) -> Array2<Complex64> {
119    let mut result = Array2::zeros((dim, dim));
120
121    for i in 0..dim {
122        for j in 0..dim {
123            result[[i, j]] = matrix[i * dim + j];
124        }
125    }
126
127    result
128}