microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
billti/num2-sim

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/noisy_simulator/src/lib.rs

203lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This crate contains the noisy simulator backend for the Q# language.
5//!
6//! It includes two simulators:
7//! - A density matrix simulator.
8//! - A state vector simulator.
9//!
10//! # Density matrix simulator
11//! The density matrix simulator is faster, since it evolves the entire probability space
12//! of the system as a whole, so you only need to run the simulation once, and take all
13//! the samples you need from the final density matrix. However, it is more memory intensive.
14//!
15//! A density matrix has 2 ^ (2 * `number_of_qubits`) complex numbers entries.
16//! If each complex number is represented as two 64-bits floating point numbers,
17//! the density matrix will be 2 ^ (2 * `number_of_qubits`) * 16 bytes. E.g., a density
18//! matrix representing a 20 qubits system will be 17592186044416 bytes, or 16.4 TB.
19//!
20//! # State vector simulator
21//! The state vector simulator allocates less memory, however if you want 1,000,000 shots
22//! of the circuit, you need to run the simulation 1,000,000 times.
23//! A state vector has 2 ^ (`number_of_qubits`) complex entries. So, a state vector of a 20
24//! qubits system will be 16777216 bytes, or 16 MB.
25//!
26//! # Which one should I use?
27//! If you are interested in running many shots of the circuit it is better to use the
28//! density matrix simulator, as long as you have enough memory in your system (13 qubits or less).
29//!
30//! However if you are interested in a single or very few shots, you should use the state
31//! vector simulator.
32
33#![deny(missing_docs)]
34
35pub(crate) mod density_matrix_simulator;
36pub(crate) mod instrument;
37pub(crate) mod kernel;
38pub(crate) mod operation;
39pub(crate) mod state_vector_simulator;
40#[cfg(test)]
41pub(crate) mod tests;
42
43use nalgebra::{DMatrix, DVector};
44use num_complex::Complex;
45use thiserror::Error;
46
47// Re-exports.
48pub use {
49 density_matrix_simulator::{DensityMatrix, DensityMatrixSimulator},
50 instrument::Instrument,
51 operation::Operation,
52 state_vector_simulator::{StateVector, StateVectorSimulator},
53};
54
55/// A square matrix of `Complex<f64>`.
56pub type SquareMatrix = DMatrix<Complex<f64>>;
57/// A complex vector.
58pub type ComplexVector = DVector<Complex<f64>>;
59/// Error tolerance used in the simulators.
60pub(crate) const TOLERANCE: f64 = 1e-12;
61
62/// A trait representing a noisy quantum circuit simulator.
63pub trait NoisySimulator {
64 /// State of the noisy simulator. Depending on the simulation method the state will be
65 /// a `DensityMatrix` or a `StateVector`.
66 type State;
67
68 /// Creates a new `NoisySimulator`.
69 fn new(number_of_qubits: usize) -> Self;
70
71 /// Creates a new `NoisySimulator` with a given seed for its random number generator.
72 fn new_with_seed(number_of_qubits: usize, seed: u64) -> Self;
73
74 /// Apply an operation to the given qubit ids.
75 fn apply_operation(&mut self, operation: &Operation, qubits: &[usize]) -> Result<(), Error>;
76
77 /// Apply non selective evolution to the given qubit ids.
78 fn apply_instrument(&mut self, instrument: &Instrument, qubits: &[usize]) -> Result<(), Error>;
79
80 /// Performs selective evolution under the given instrument.
81 /// Returns the index of the observed outcome.
82 ///
83 /// Use this method to perform measurements on the quantum system.
84 fn sample_instrument(
85 &mut self,
86 instrument: &Instrument,
87 qubits: &[usize],
88 ) -> Result<usize, Error>;
89
90 /// Performs selective evolution under the given instrument using a custom random distribution.
91 /// Returns the index of the observed outcome.
92 ///
93 /// This method is used for testing and debugging purposes.
94 fn sample_instrument_with_distribution(
95 &mut self,
96 instrument: &Instrument,
97 qubits: &[usize],
98 random_sample: f64,
99 ) -> Result<usize, Error>;
100
101 /// Returns the `State` if the simulator is in a valid state.
102 fn state(&self) -> Result<&Self::State, &Error>;
103
104 /// Set state of the quantum system.
105 fn set_state(&mut self, new_state: Self::State) -> Result<(), Error>;
106
107 /// Return theoretical change in trace due to operations that have been applied so far
108 /// In reality, the density matrix is always renormalized after instruments/operations
109 /// have been applied.
110 fn trace_change(&self) -> Result<f64, Error>;
111
112 /// Set the trace of the quantum system.
113 fn set_trace(&mut self, trace: f64) -> Result<(), Error>;
114}
115
116/// A noisy simulation error.
117#[derive(Clone, Debug, Error, PartialEq)]
118pub enum Error {
119 /// Failure when building a `DensityMatrix` from raw data.
120 #[error("error when building `DensityMatrix` from raw_data: {0}")]
121 DensityMatrixTryFromError(String),
122 /// Failure when buidling an instrument.
123 #[error("error when building instrument: {0}")]
124 FailedToConstructInstrument(String),
125 /// Failure when building an operation.
126 #[error("error when building operation: {0}")]
127 FailedToConstructOperation(String),
128 /// Failure when sampling instrument outcome.
129 #[error("numerical error: no outcome found when sampling instrument")]
130 FailedToSampleInstrumentOutcome,
131 /// Failure when sampling Kraus operators.
132 #[error("numerical error: no outcome found when sampling Kraus operators")]
133 FailedToSampleKrausOperators,
134 /// Provided an invalid state when creating or setting the state of the simulator.
135 #[error("provided an invalid state when creating or setting the state of the simulator: {0}")]
136 InvalidState(String),
137 /// `Matrix` ⋅ `Vector` multiplication mismatch.
138 #[error(
139 "matrix ⋅ vector multiplication mismatch; matrix is of dimension ({nrows}, {ncols}) but vector has {vec_dim} entries"
140 )]
141 MatrixVecDimensionMismatch {
142 /// Number of rows in the matrix.
143 nrows: usize,
144 /// Number of columns in the matrix.
145 ncols: usize,
146 /// Number of elements in the vector.
147 vec_dim: usize,
148 },
149 /// State is not normalized.
150 #[error("numerical error: trace should be between 0 and 1, but it is {0}")]
151 NotNormalized(f64),
152 /// A numerical error, such as a probability-0 event.
153 #[error("numerical error: probability-0 event")]
154 ProbabilityZeroEvent,
155 /// A qubit-id is greater than the number of qubits the simulation supports.
156 #[error("qubit id out of bounds: {0}")]
157 QubitIdOutOfBounds(usize),
158 /// Failure when building a `StateVector` from raw data.
159 #[error("error when building `StateVector` from raw_data: {0}")]
160 StateVectorTryFromError(String),
161 /// Trace is not real
162 #[error(
163 "state trace should be real since it represents a probability, but its imaginary part is: {0}"
164 )]
165 TraceIsNotReal(f64),
166}
167
168impl Error {
169 const fn is_unrecoverable(&self) -> bool {
170 matches!(
171 self,
172 Error::ProbabilityZeroEvent
173 | Error::FailedToSampleInstrumentOutcome
174 | Error::FailedToSampleKrausOperators
175 )
176 }
177}
178
179impl From<&Error> for Error {
180 fn from(value: &Error) -> Self {
181 value.clone()
182 }
183}
184
185impl From<&mut Error> for Error {
186 fn from(value: &mut Error) -> Self {
187 value.clone()
188 }
189}
190
191/// Handles errors in the simulator.
192/// If an error is unrecoverable, it will set the state of the simulator to that error,
193/// invalidating any further evolution of the quantum system.
194macro_rules! handle_error {
195 ($self:expr, $err:expr) => {
196 if $err.is_unrecoverable() {
197 $self.state = Err($err.clone());
198 }
199 return Err($err)
200 };
201}
202
203pub(crate) use handle_error;
204