Skip to main content

quantrs2_sim/
utils.rs

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