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
106 changes: 106 additions & 0 deletions qip/examples/bell_inequalities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#[cfg(feature = "macros")]
use qip::prelude::*;
#[cfg(feature = "macros")]
use qip_macros::*;
#[cfg(feature = "macros")]
use std::num::NonZeroUsize;

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

let q = b.qubit();
let ra = b.register(n);
let r = program!(&mut b, r;
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.stochastic_measure(r);

//run and get probabilities
let (_, mut measured) = run_local::<f64>(&r).unwrap();
measured.pop_stochastic_measurements(m_handle).unwrap()
}

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

let r = b.register(n);
let r = program!( &mut b, r;
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.stochastic_measure(r);

// run and get probabilities
let (_, mut measured) = run_local::<f64>(&r).unwrap();
measured.pop_stochastic_measurements(m_handle).unwrap()
}
#[cfg(feature = "macros")]
fn circuit3() -> Vec<f64> {
let mut b = LocalBuilder::<f64>::default();
let n = NonZeroUsize::new(2).unwrap();
let r = b.register(n);

let r = program!(&mut b, r;
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.stochastic_measure(r);

// run and get probabilities
let (_, mut measured) = run_local::<f64>(&r).unwrap();
measured.pop_stochastic_measurements(m_handle).unwrap()
}

#[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.


/// Bell inequality:
/// |P(a, b) - P(a, c)| - P(b, c) <= 1
/// To get P(a, b), P(a, c) and P(b, c) we use the 3 circuits presented in:
/// https://arxiv.org/pdf/1712.05642.pdf - II.IMPLEMENTED EXPERIMENTS -> 3. Bell's inequality
///
/// A violation of the inequality is expected in any quantum computer or simulator.
#[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();
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 = num::abs(p_of_a_b - p_of_a_c) - 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
);
}
}