Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions source/noisy_simulator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,7 @@ macro_rules! handle_error {
}

pub(crate) use handle_error;

pub(crate) fn eq_with_tolerance(left: f64, right: f64, tolerance: f64) -> bool {
(left - right).abs() <= tolerance
}
55 changes: 54 additions & 1 deletion source/noisy_simulator/src/state_vector_simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ mod tests;
use rand::{Rng, SeedableRng, rngs::StdRng};

use crate::{
ComplexVector, Error, NoisySimulator, SquareMatrix, TOLERANCE, handle_error,
ComplexVector, Error, NoisySimulator, SquareMatrix, TOLERANCE, eq_with_tolerance, handle_error,
instrument::Instrument, kernel::apply_kernel, operation::Operation,
};

/// A vector representing the state of a quantum system.
#[derive(Debug)]
pub struct StateVector {
/// Dimension of the vector.
dimension: usize,
Expand All @@ -26,6 +27,58 @@ pub struct StateVector {
data: ComplexVector,
}

impl PartialEq for StateVector {
/// Compares two state vectors for equality up to a global phase.
///
/// Two state vectors are considered equal if one can be obtained from the other
/// by multiplying by a complex number of unit magnitude (a global phase factor).
fn eq(&self, other: &Self) -> bool {
use num_complex::Complex;

if self.dimension != other.dimension || self.number_of_qubits != other.number_of_qubits {
return false;
}

if !eq_with_tolerance(self.trace_change, other.trace_change, TOLERANCE) {
return false;
}

// Find the first non-zero element in self to determine the global phase
let phase = self
.data
.iter()
.zip(other.data.iter())
.find(|(a, _)| a.norm() > TOLERANCE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider finding the largest by norm. May improve precision.

.map(|(a, b)| {
if b.norm() > TOLERANCE {
// phase = b / a, so self * phase ≈ other
b / a
} else {
// a is non-zero but b is zero - not equal
Complex::new(f64::NAN, 0.0)
}
});

match phase {
Some(phase) if phase.re.is_nan() => false,
Some(phase) => {
// Check that the phase has unit magnitude
if !eq_with_tolerance(phase.norm(), 1.0, TOLERANCE) {
return false;
}
// Check that all elements match after applying the phase
self.data
.iter()
.zip(other.data.iter())
.all(|(a, b)| eq_with_tolerance((a * phase - b).norm(), 0.0, TOLERANCE))
}
// The first vector is the zero vector.
// We return `true` iff the second vector is also the zero vector.
None => other.data.iter().all(|b| b.norm() <= TOLERANCE),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you normalize? If so, is this even possible to have all values so small?

}
}
}

impl StateVector {
fn new(number_of_qubits: usize) -> Self {
let dimension = 1 << number_of_qubits;
Expand Down
4 changes: 2 additions & 2 deletions source/noisy_simulator/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
pub mod noiseless_tests;
pub mod noisy_tests;

use crate::TOLERANCE;
use crate::{TOLERANCE, eq_with_tolerance};

/// Assert that two f64 are equal up to a `TOLERANCE`.
pub fn assert_approx_eq(left: f64, right: f64) {
Expand All @@ -13,7 +13,7 @@ pub fn assert_approx_eq(left: f64, right: f64) {

pub fn assert_approx_eq_with_tolerance(left: f64, right: f64, tolerance: f64) {
assert!(
(left - right).abs() <= tolerance,
eq_with_tolerance(left, right, tolerance),
"aprox_equal failed, left = {left}, right = {right}"
);
}
3 changes: 3 additions & 0 deletions source/pip/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ serde_json = { workspace = true }
rayon = { workspace = true }
rand = { workspace = true }

[dev-dependencies]
expect-test = { workspace = true }

[lints]
workspace = true

Expand Down
11 changes: 8 additions & 3 deletions source/pip/src/qir_simulation/cpu_simulators.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#[cfg(test)]
mod tests;

use crate::qir_simulation::{NoiseConfig, QirInstruction, QirInstructionId, unbind_noise_config};
use pyo3::{IntoPyObjectExt, exceptions::PyValueError, prelude::*, types::PyList};
use pyo3::{PyResult, pyfunction};
Expand Down Expand Up @@ -186,7 +189,8 @@ where
.par_iter()
.map(|shot_seed| {
let simulator = make_simulator(num_qubits, num_results, *shot_seed, noise.clone());
run_shot(instructions, simulator)
let mut simulator = run_shot(instructions, simulator);
simulator.take_measurements()
})
.collect::<Vec<_>>();

Expand All @@ -206,10 +210,11 @@ where
values
}

fn run_shot(instructions: &[QirInstruction], mut sim: impl Simulator) -> Vec<MeasurementResult> {
fn run_shot<S: Simulator>(instructions: &[QirInstruction], mut sim: S) -> S {
for qir_inst in instructions {
match qir_inst {
QirInstruction::OneQubitGate(id, qubit) => match id {
QirInstructionId::I => {} // Identity gate is a no-op
QirInstructionId::H => sim.h(*qubit as usize),
QirInstructionId::X => sim.x(*qubit as usize),
QirInstructionId::Y => sim.y(*qubit as usize),
Expand Down Expand Up @@ -261,5 +266,5 @@ fn run_shot(instructions: &[QirInstruction], mut sim: impl Simulator) -> Vec<Mea
}
}

sim.take_measurements()
sim
}
11 changes: 11 additions & 0 deletions source/pip/src/qir_simulation/cpu_simulators/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

mod clifford_noiseless;
mod clifford_noisy;
mod full_state_noiseless;
mod full_state_noisy;
mod test_utils;

/// Seed used for reproducible randomness in tests.
const SEED: u32 = 42;
Loading
Loading