[][src]Crate qip

Quantum Computing library leveraging graph building to build efficient quantum circuit simulations. Rust is a great language for quantum computing with gate models because the borrow checker is very similar to the No-cloning theorem.

See all the examples in the examples directory of the Github repository.

Example (CSWAP)

Here's an example of a small circuit where two groups of Registers are swapped conditioned on a third. This circuit is very small, only three operations plus a measurement, so the boilerplate can look quite large in comparison, but that setup provides the ability to construct circuits easily and safely when they do get larger.

use qip::*;

// Make a new circuit builder.
let mut b = OpBuilder::new();

// Make three registers of sizes 1, 3, 3 (7 qubits total).
let q = b.qubit();  // Same as b.register(1)?;
let ra = b.register(3)?;
let rb = b.register(3)?;

// We will want to feed in some inputs later, hang on to the handles
// so we don't need to actually remember any indices.
let a_handle = ra.handle();
let b_handle = rb.handle();

// Define circuit
// First apply an H to q
let q = b.hadamard(q);
// Then swap ra and rb, conditioned on q.
let (q, _, _) = b.cswap(q, ra, rb)?;
// Finally apply H to q again.
let q = b.hadamard(q);

// Add a measurement to the first qubit, save a reference so we can get the result later.
let (q, m_handle) = b.measure(q);

// Now q is the end result of the above circuit, and we can run the circuit by referencing it.

// Make an initial state: |0,000,001> (default value for registers not mentioned is 0).
let initial_state = [a_handle.make_init_from_index(0b000)?,
                     b_handle.make_init_from_index(0b001)?];
// Run circuit with a given precision.
let (_, measured) = run_local_with_init::<f64>(&q, &initial_state)?;

// Lookup the result of the measurement we performed using the handle, and the probability
// of getting that measurement.
let (result, p) = measured.get_measurement(&m_handle).unwrap();

// Print the measured result
println!("Measured: {:?} (with chance {:?})", result, p);

The Program Macro

While the borrow checker included in rust is a wonderful tool for checking that our registers are behaving, it can be cumbersome. For that reason qip also includes a macro which provides an API similar to that which you would see in quantum computing textbooks

use qip::*;

let n = 3;
let mut b = OpBuilder::new();
let ra = b.register(n)?;
let rb = b.register(n)?;

fn gamma(b: &mut dyn UnitaryBuilder, mut rs: Vec<Register>) -> Result<Vec<Register>, CircuitError> {
    let rb = rs.pop().unwrap();
    let ra = rs.pop().unwrap();
    let (ra, rb) = b.cnot(ra, rb);
    let (rb, ra) = b.cnot(rb, ra);
    Ok(vec![ra, rb])
}

let (ra, rb) = program!(&mut b, ra, rb;
    // Applies gamma to |ra[0] ra[1]>|ra[2]>
    gamma ra[0..2], ra[2];
    // Applies gamma to |ra[0] rb[0]>|ra[2]>
    gamma |ra[0], rb[0],| ra[2];
    // Applies gamma to |ra[0]>|rb[0] ra[2]>
    gamma ra[0], |rb[0], ra[2],|;
    // Applies gamma to |ra[0] ra[1]>|ra[2]> if rb == |111>
    control gamma rb, ra[0..2], ra[2];
    // Applies gamma to |ra[0] ra[1]>|ra[2]> if rb == |110> (rb[0] == |0>, rb[1] == 1, ...)
    control(0b110) gamma rb, ra[0..2], ra[2];
)?;
let r = b.merge(vec![ra, rb])?;

To clean up gamma we can use the wrap_fn macro:

use qip::*;

let n = 3;
let mut b = OpBuilder::new();
let ra = b.register(n)?;
let rb = b.register(n)?;

fn gamma(b: &mut dyn UnitaryBuilder, ra: Register, rb: Register) -> (Register, Register) {
    let (ra, rb) = b.cnot(ra, rb);
    let (rb, ra) = b.cnot(rb, ra);
    (ra, rb)
}
// Make a function gamma_op from gamma which matches the spec required by program!(...).
// Here we tell wrap_fn! that gamma takes two registers, which we will internally call ra, rb.
wrap_fn!(gamma_op, gamma, ra, rb);
// if gamma returns a Result<(Register, Register), CircuitError>, write (gamma) instead.
// wrap_fn!(gamma_op, (gamma), ra, rb)

let (ra, rb) = program!(&mut b, ra, rb;
    gamma_op ra[0..2], ra[2];
)?;
let r = b.merge(vec![ra, rb])?;

And with these wrapped functions, automatically produce their conjugates / inverses:

use qip::*;

let n = 3;
let mut b = OpBuilder::new();
let ra = b.register(n)?;
let rb = b.register(n)?;

fn gamma(b: &mut dyn UnitaryBuilder, ra: Register, rb: Register) -> (Register, Register) {
    let (ra, rb) = b.cnot(ra, rb);
    let (rb, ra) = b.cnot(rb, ra);
    (ra, rb)
}
wrap_fn!(gamma_op, gamma, ra, rb);
invert_fn!(inv_gamma_op, gamma_op);

// This program is equivalent to the identity (U^-1 U = I).
let (ra, rb) = program!(&mut b, ra, rb;
    gamma_op ra, rb[2];
    inv_gamma_op ra, rb[2];
)?;

Re-exports

pub use self::builders::*;
pub use self::common_circuits::*;
pub use self::errors::*;
pub use self::macros::*;
pub use self::pipeline::run_local;
pub use self::pipeline::run_local_with_init;
pub use self::pipeline::run_with_state;
pub use self::pipeline::QuantumState;
pub use self::pipeline_debug::run_debug;
pub use self::qubits::Register;
pub use self::types::Precision;

Modules

boolean_circuits

Quantum analogues of boolean circuits

builders

Opbuilder and such

common_circuits

Common circuits for general usage.

errors

Error values for the library.

iterators

Efficient iterators for sparse kronprod matrices.

macros

Macros for general ease of use.

measurement_ops

Functions for measuring states.

pipeline

Code for building pipelines.

pipeline_debug

Tools for displaying pipelines.

qfft

Quantum fourier transform support.

qubits

Basic classes for defining circuits/pipelines.

sparse_state

Sparse quantum states

state_ops

Functions for running ops on states.

trace_state

Tracing state

types

Commonly used types.

unitary_decomposition

Break unitary matrices into circuits.

utils

Commonly used short functions.

Macros

invert_fn

Wrap a function to create a version compatible with program! as well as an inverse which is also compatible.

program

A helper macro for applying functions to specific qubits in registers.

register_exprDeprecated

A helper macro for applying functions to specific qubits in registers.

wrap_and_invert

Wrap a function to create a version compatible with program! as well as an inverse which is also compatible.

wrap_fn

Allows the wrapping of a function with signature: Fn(&mut dyn UnitaryBuilder, Register, Register, ...) -> (Register, ...) or Fn(&mut dyn UnitaryBuilder, Register, Register, ...) -> Result<(Register, ...), CircuitError> to make a new function with signature: Fn(&mut dyn UnitaryBuilder, Vec<Register>) -> Result<Vec<Register>, CircuitError> and is therefore compatible with program!.

Structs

Complex

A complex number in Cartesian form.