microsoft/qdk

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/pipeline-issue-debugging

Branches

Tags

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

Clone

HTTPS

Download ZIP

source/noisy_simulator/src/lib.rs

200lines · 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 /// Performs selective evolution under the given instrument.
78 /// Returns the index of the observed outcome.
79 ///
80 /// Use this method to perform measurements on the quantum system.
81 fn sample_instrument(
82 &mut self,
83 instrument: &Instrument,
84 qubits: &[usize],
85 ) -> Result<usize, Error>;
86
87 /// Performs selective evolution under the given instrument using a custom random distribution.
88 /// Returns the index of the observed outcome.
89 ///
90 /// This method is used for testing and debugging purposes.
91 fn sample_instrument_with_distribution(
92 &mut self,
93 instrument: &Instrument,
94 qubits: &[usize],
95 random_sample: f64,
96 ) -> Result<usize, Error>;
97
98 /// Returns the `State` if the simulator is in a valid state.
99 fn state(&self) -> Result<&Self::State, &Error>;
100
101 /// Set state of the quantum system.
102 fn set_state(&mut self, new_state: Self::State) -> Result<(), Error>;
103
104 /// Return theoretical change in trace due to operations that have been applied so far
105 /// In reality, the density matrix is always renormalized after instruments/operations
106 /// have been applied.
107 fn trace_change(&self) -> Result<f64, Error>;
108
109 /// Set the trace of the quantum system.
110 fn set_trace(&mut self, trace: f64) -> Result<(), Error>;
111}
112
113/// A noisy simulation error.
114#[derive(Clone, Debug, Error, PartialEq)]
115pub enum Error {
116 /// Failure when building a `DensityMatrix` from raw data.
117 #[error("error when building `DensityMatrix` from raw_data: {0}")]
118 DensityMatrixTryFromError(String),
119 /// Failure when building an instrument.
120 #[error("error when building instrument: {0}")]
121 FailedToConstructInstrument(String),
122 /// Failure when building an operation.
123 #[error("error when building operation: {0}")]
124 FailedToConstructOperation(String),
125 /// Failure when sampling instrument outcome.
126 #[error("numerical error: no outcome found when sampling instrument")]
127 FailedToSampleInstrumentOutcome,
128 /// Failure when sampling Kraus operators.
129 #[error("numerical error: no outcome found when sampling Kraus operators")]
130 FailedToSampleKrausOperators,
131 /// Provided an invalid state when creating or setting the state of the simulator.
132 #[error("provided an invalid state when creating or setting the state of the simulator: {0}")]
133 InvalidState(String),
134 /// `Matrix` ⋅ `Vector` multiplication mismatch.
135 #[error(
136 "matrix ⋅ vector multiplication mismatch; matrix is of dimension ({nrows}, {ncols}) but vector has {vec_dim} entries"
137 )]
138 MatrixVecDimensionMismatch {
139 /// Number of rows in the matrix.
140 nrows: usize,
141 /// Number of columns in the matrix.
142 ncols: usize,
143 /// Number of elements in the vector.
144 vec_dim: usize,
145 },
146 /// State is not normalized.
147 #[error("numerical error: trace should be between 0 and 1, but it is {0}")]
148 NotNormalized(f64),
149 /// A numerical error, such as a probability-0 event.
150 #[error("numerical error: probability-0 event")]
151 ProbabilityZeroEvent,
152 /// A qubit-id is greater than the number of qubits the simulation supports.
153 #[error("qubit id out of bounds: {0}")]
154 QubitIdOutOfBounds(usize),
155 /// Failure when building a `StateVector` from raw data.
156 #[error("error when building `StateVector` from raw_data: {0}")]
157 StateVectorTryFromError(String),
158 /// Trace is not real
159 #[error(
160 "state trace should be real since it represents a probability, but its imaginary part is: {0}"
161 )]
162 TraceIsNotReal(f64),
163}
164
165impl Error {
166 const fn is_unrecoverable(&self) -> bool {
167 matches!(
168 self,
169 Error::ProbabilityZeroEvent
170 | Error::FailedToSampleInstrumentOutcome
171 | Error::FailedToSampleKrausOperators
172 )
173 }
174}
175
176impl From<&Error> for Error {
177 fn from(value: &Error) -> Self {
178 value.clone()
179 }
180}
181
182impl From<&mut Error> for Error {
183 fn from(value: &mut Error) -> Self {
184 value.clone()
185 }
186}
187
188/// Handles errors in the simulator.
189/// If an error is unrecoverable, it will set the state of the simulator to that error,
190/// invalidating any further evolution of the quantum system.
191macro_rules! handle_error {
192 ($self:expr, $err:expr) => {{
193 if $err.is_unrecoverable() {
194 $self.state = Err($err.clone());
195 }
196 return Err($err);
197 }};
198}
199
200pub(crate) use handle_error;
201