microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/noisy_simulator/src/kernel.rs

119lines · modeblame

312af694orpuente-MS2 years ago1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
3a78aa85orpuente-MS2 years ago4//! This module contains the `apply_kernel` function used by the `DensityMatrixSimualtor`
312af694orpuente-MS2 years ago5//! and the `TrajectorySimulator`.
6
7use crate::{ComplexVector, Error, SquareMatrix};
8use nalgebra::Complex;
9
10/// This function extracts the relevant entries from the `state_vector` into its own vector.
11/// Then it applies the `operation_matrix` to this extracted entries.
12/// Finally it stores the results back into the state vector.
13///
84a8c59aorpuente-MS2 years ago14/// Performance note: Because `nalgebra` stores its matrices in column major form, we apply
15/// `gemv_tr` to avoid incurring cache misses. That is why we transpose all Kraus operators
16/// when they enter the simulator:
17/// `gemv(1, matrix, vec, 0)` is equivalent to `gemv_tr(1, matrix_tr, vec, 0)`,
18/// but the later has much better performance.
19///
312af694orpuente-MS2 years ago20/// Errors: If the `operation_matrix` doesn't have the right dimension for the number of target `qubits`,
21/// this function will return `Error::MatrixVecDimensionMismatch`.
22pub fn apply_kernel(
23state: &mut ComplexVector,
24operation_matrix: &SquareMatrix,
25qubits: &[usize],
26) -> Result<(), Error> {
27// Construct a mask that has 1s at locations given by the target `qubits` ids.
28let mask = make_mask(state, qubits);
29
30// Number of elements in small matrix-vector multiplications (dimension of gate matrix).
31let num_elements: usize = 1 << qubits.len();
32let (nrows, ncols) = operation_matrix.shape();
33
34if num_elements != ncols {
35return Err(Error::MatrixVecDimensionMismatch {
36nrows,
37ncols,
38vec_dim: num_elements,
39});
40}
41
42// Compute all index offsets of the entries to load.
43// E.g., for a 2-qubit gate acting on qubits [k1,k2],
44// index offsets are [0, 2^k1, 2^k2, 2^(k1+k2)]
45let mut index_offsets: Vec<usize> = std::vec::Vec::with_capacity(num_elements);
46for k in 0..num_elements {
47let mut j = 0;
48let mut k_ = k;
49let mut idx = 0;
50while k_ != 0 {
51idx |= (k_ & 1) << qubits[j];
52k_ >>= 1;
53j += 1;
54}
55index_offsets.push(idx);
56}
57
58// Main loop.
59let mut extracted_entries = ComplexVector::zeros(num_elements);
60let mut new_entries = ComplexVector::zeros(num_elements);
61for s in 0..state.len() {
62if (s & mask) == 0 {
63// Extract relevant entries into a vector to make the gate application easier.
84a8c59aorpuente-MS2 years ago64(0..index_offsets.len()).for_each(|k| {
65// SAFETY: extracted_entries has size index_offset.len(), so that get_unchecked is safe.
66// state.get_unchecked(idx) is safe because:
67// 1. s has total_qubits_in_system bits
68// 2. index_offset has total_qubits_in_system bits
69// 3. Therefore, idx = s | index_offset also has total_qubits_in_system bits.
70// 4. idx < (1 << total_qubits_in_system) = state.len() because
71// it has (total_qubits_in_system + 1) bits.
72// 5. Therefore state.get_unchecked(idx) is safe.
312af694orpuente-MS2 years ago73let idx = s | index_offsets[k];
84a8c59aorpuente-MS2 years ago74unsafe {
75*extracted_entries.get_unchecked_mut(k) = *state.get_unchecked(idx);
76}
77});
312af694orpuente-MS2 years ago78
79// Apply the gate.
84a8c59aorpuente-MS2 years ago80new_entries.gemv_tr(
81Complex::ONE,
82operation_matrix,
83&extracted_entries,
84Complex::ZERO,
85);
312af694orpuente-MS2 years ago86
87// Store accumulated result back into the state vector.
84a8c59aorpuente-MS2 years ago88(0..index_offsets.len()).for_each(|k| {
89// SAFETY: new_entries has size index_offset.len(), so that get_unchecked is safe.
90// state.get_unchecked(idx) is safe because:
91// 1. s has total_qubits_in_system bits
92// 2. index_offset has total_qubits_in_system bits
93// 3. Therefore, idx = s | index_offset also has total_qubits_in_system bits.
94// 4. idx < (1 << total_qubits_in_system) = state.len() because
95// it has (total_qubits_in_system + 1) bits.
96// 5. Therefore state.get_unchecked(idx) is safe.
312af694orpuente-MS2 years ago97let idx = s | index_offsets[k];
84a8c59aorpuente-MS2 years ago98unsafe {
99*state.get_unchecked_mut(idx) = *new_entries.get_unchecked(k);
100}
101});
312af694orpuente-MS2 years ago102}
103}
104
105Ok(())
106}
107
108/// Construct a mask that has 1s at locations given by the target `qubits` ids.
109fn make_mask(state: &ComplexVector, qubits: &[usize]) -> usize {
110// Number of elements in the density matrix.
111let num_elements = state.len();
112let mut mask: usize = 0;
113for id in qubits {
114let id_mask: usize = 1 << id;
115assert!(id_mask < num_elements, "invalid qubit id: {id}");
116mask |= id_mask;
117}
118mask
119}