Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples PR 1: dense_coding, bell_inequalities, cswap #41

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
108 changes: 108 additions & 0 deletions qip/examples/bell_inequalities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use qip::builder::StochasticMeasurementHandle;
#[cfg(feature = "macros")]
use qip::prelude::*;
#[cfg(feature = "macros")]
use qip::prelude::{CircuitBuilder, CircuitError};
#[cfg(feature = "macros")]
use qip_macros::*;
#[cfg(feature = "macros")]
use qip::macros::program_ops::*;
use std::num::NonZeroUsize;

#[cfg(feature = "macros")]
fn circuit1() -> &'static [f64]{
let mut b = LocalBuilder::<f64>::default();
let n = NonZeroUsize::new(2).unwrap();

let q = b.qubit();
let r1 = b.register(n);
let r = program!(&mut b; r1;
not r;
h r[0];
control not r[0], r[1];
rz(std::f64::consts::FRAC_PI_3) r[1];
h r;
).unwrap();
let (r, m_handle) = b.measure_stochastic(r);

//run and get probabilities
let (r, m_handle) = b.measure_stochastic(r); //returns (Self::Register, Self::StochasticMeasurementHandle)
let (_, measurements) = b.calculate_state();
let stochastic_measurement_probability = measurements.get_stochastic_measurement(m_handle);
return stochastic_measurement_probability;
}

#[cfg(feature = "macros")]
fn circuit2() -> &'static [f64]{
let mut b = LocalBuilder::<f64>::default();
let n = NonZeroUsize::new(2).unwrap();

let r2 = b.register(n);
let r = program!( &mut b; r2;
not r;
h r[0];
control not r[0], r[1];
rz(2. * std::f64::consts::FRAC_PI_3) r[1];
h r;
).unwrap();
let (r, m_handle) = b.measure_stochastic(r);

// run and get probabilities
let (r, m_handle) = b.measure_stochastic(r); //returns (Self::Register, Self::StochasticMeasurementHandle)
let (_, measurements) = b.calculate_state();
let stochastic_measurement_probability = measurements.get_stochastic_measurement(m_handle);
return stochastic_measurement_probability;
}
#[cfg(feature = "macros")]
fn circuit3() -> &'static [f64] {
let mut b = LocalBuilder::<f64>::default();
let n = NonZeroUsize::new(2).unwrap();
let r3 = b.register(n);

let r = program!(&mut b; r3;
not r;
h r[0];
control not r[0], r[1];
rz(std::f64::consts::FRAC_PI_3) r[0];
rz(2. * std::f64::consts::FRAC_PI_3) r[1];
h r;
).unwrap();
let (r, m_handle) = b.measure_stochastic(r); //returns (Self::Register, Self::StochasticMeasurementHandle)
let (_, measurements) = b.calculate_state();
let stochastic_measurement_probability = measurements.get_stochastic_measurement(m_handle);
return stochastic_measurement_probability;

}

#[cfg(not(feature = "macros"))]
fn main() -> () {}
Copy link
Owner

Choose a reason for hiding this comment

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

We should put an error message here telling the user to run with the macro feature or --all-features.




#[cfg(feature = "macros")]
fn main() -> Result<(), CircuitError> {
println!("Bell inequality: |P(a, b) - P(a, c)| - P(b, c) <= 1");

let a_b = circuit1();
let p_of_a_b = (a_b[0] + a_b[3]) - (a_b[1] + a_b[2]);
println!("P(a, b) = {:.2}", p_of_a_b);

let a_c = circuit2();
let p_of_a_c = (a_c[0] + a_c[3]) - (a_c[1] + a_c[2]);
println!("P(a, c) = {:.2}", p_of_a_c);

let b_c = circuit3();
println!("{:?}", b_c);
let p_of_b_c = (b_c[0] + b_c[3]) - (b_c[1] + b_c[2]);
println!("P(b, c) = {:.2}", p_of_b_c);

let left_side = (p_of_a_b - p_of_a_c).abs() - p_of_b_c;
println!(
"|{:.2} - {:.2}| - ({:.2}) = {:.2} IS NOT <= 1",
p_of_a_b, p_of_a_c, p_of_b_c, left_side
);

assert!(left_side > 1.0);

Ok(())
}
27 changes: 27 additions & 0 deletions qip/examples/cswap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use qip::prelude::*;
use std::num::NonZeroUsize;

fn main() -> Result<(), CircuitError> {
let mut b = LocalBuilder::<f64>::default();
let n = NonZeroUsize::new(3).unwrap();

let q = b.qubit();
let ra = b.register(n);
let rb = b.register(n);

let q = b.h(q);

let mut cb = b.condition_with(q);
let (ra, rb) = cb.swap(ra, rb).unwrap();
let q = cb.dissolve();

let q = b.h(q);

let (q, m_handle) = b.measure(q);

let (_, measured) = b.calculate_state_with_init([(&ra, 0b000), (&rb, 0b001)]);
let (result, p) = measured.get_measurement(m_handle);
println!("Measured: {:?} (with chance {:?})", result, p);

Ok(())
}
70 changes: 70 additions & 0 deletions qip/examples/dense_coding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use qip::builder::Qudit;
use qip::prelude::*;
use std::num::NonZeroUsize;

/// Encode the two classical bits Alice wants to communicate to Bob.
///
/// Depending on the classical bits combination a different gate is applied:
/// 00: Do nothing (or apply Identity gate)
/// 01: Apply Pauli-X gate
/// 10: Apply Pauli-Z gate
/// 11: Apply Pauli-Y gate (or apply Pauli-Z gate followed by a Pauli-X gate)
///
/// Returns Alice qubit with applied gate.
///
/// https://en.wikipedia.org/wiki/Superdense_coding#Encoding
fn run_alice<P: Precision, CB: CliffordTBuilder<P>>(
b: &mut CB,
epr_alice: CB::Register,
bit_a: bool,
bit_b: bool,
) -> CB::Register {
match (bit_a, bit_b) {
(false, false) => epr_alice,
(false, true) => b.x(epr_alice),
(true, false) => b.z(epr_alice),
(true, true) => b.y(epr_alice),
}
}

/// Decode the message Alice transmitted to Bob.
///
/// Bob applies the restoration operation on his qubit and the one transmitted
/// by Alice to decode the original message. After restoration:
/// |00>: 00
/// |10>: 10
/// |01>: 01
/// |11>: 11
///
/// Returns a pair of classical bits.
///
/// https://en.wikipedia.org/wiki/Superdense_coding#Decoding
fn run_bob<P: Precision>(b: &mut LocalBuilder<P>, r_alice: Qudit, epr_bob: Qudit) -> (bool, bool) {
let (r_alice, r_bob) = b.cnot(r_alice, epr_bob).unwrap();
let r_alice = b.h(r_alice);
let r = b.merge_two_registers(r_bob, r_alice);
let (r, m) = b.measure(r);
let (_, measurements) = b.calculate_state();
let (m, _) = measurements.get_measurement(m);
((m & 2) == 2, (m & 1) == 1)
}

fn main() {
let n = NonZeroUsize::new(1).unwrap();
let bits_a = vec![true, false, true, false];
let bits_b = vec![true, true, false, false];

for (bit_a, bit_b) in bits_a.into_iter().zip(bits_b.into_iter()) {
let mut b = LocalBuilder::<f64>::default();
let epr_alice = b.register(n);
let epr_bob = b.register(n);
Copy link
Owner

Choose a reason for hiding this comment

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

You made a change here which broke the example:
What you implemented is two independent qubits, whereas superdense coding requires two entabled qubits (EPR pair):
See

let (epr_alice, epr_bob) = epr_pair(&mut b, 1);

I dont think the library currently has an EPR function, but I can add one later. You should look up how to make an EPR pair yourself and try implementing that as a helper function for this example.


let r_alice = run_alice(&mut b, epr_alice, bit_a, bit_b);
let (bob_a, bob_b) = run_bob(&mut b, r_alice, epr_bob);

println!(
"Alice: ({:?},{:?}) \tBob: ({:?}, {:?})",
bit_a, bit_b, bob_a, bob_b
);
}
}