Crates.io | quest_bind |
lib.rs | quest_bind |
version | 0.3.7 |
source | src |
created_at | 2023-07-01 23:05:48.583334 |
updated_at | 2023-09-08 09:36:49.008483 |
description | Wrapper around the Quantum Exact Simulation Toolkit (QuEST) |
homepage | https://github.com/marek-miller/quest_bind |
repository | https://github.com/marek-miller/quest_bind.git |
max_upload_size | |
id | 905768 |
size | 21,960,964 |
A wrapper around QuEST v3.5.0.
Quantum Exact Simulation Toolkit (QuEST) is a no-fluff, bent-on-speed quantum circuit simulator [1]. It is distributed under MIT License.
Initialize a new binary crate:
cargo new tryme
cd tryme/
Add quest_bind
to your project's dependencies:
cargo add quest_bind
Now write some code and put it in ./src/main.rs
:
use quest_bind::*;
fn main() -> Result<(), QuestError> {
// Initialize QuEST environment and report to screen
let env = &QuestEnv::new();
env.report_quest_env();
// Create a 2-qubit register and report its parameters
let mut qureg = Qureg::try_new(2, env).expect("cannot allocate new Qureg");
qureg.report_qureg_params();
// Initialize |00> state and print out the state to screen
qureg.init_zero_state();
qureg.report_state_to_screen(0);
// Prepare a Bell state `|00> + |11>`: apply Hadamard gate
// on qubit 0, then NOT on qubit 1, controlled by qubit 0.
println!("---\nPrepare Bell state: |00> + |11>");
qureg.hadamard(0).and(qureg.controlled_not(0, 1))?;
// Measure both qubits
let outcome0 = qureg.measure(0)?;
let outcome1 = qureg.measure(1)?;
println!("Qubit \"0\" measured in state: |{outcome0}>");
println!("Qubit \"1\" measured in state: |{outcome1}>");
// Because the state was entangled, the outcomes
// should always be the same
if outcome0 == outcome1 {
println!("They match!");
Ok(())
} else {
panic!("qubits in Bell state should be perfectly correlated");
}
// At this point both `qureg` and `env` are dropped and
// the allocated memory is freed.
}
The documentation is available online, as well as locally:
cargo doc --open
Lastly, compile and run the program:
cargo run
You should be able to see something like:
EXECUTION ENVIRONMENT:
Running locally on one node
Number of ranks is 1
OpenMP enabled
Number of threads available is 8
Precision: size of qreal is 8 bytes
QUBITS:
Number of qubits is 2.
Number of amps is 4.
Number of amps per rank is 4.
---
Prepare Bell state: |00> + |11>
Qubit "0" measured in state: |0>
Qubit "1" measured in state: |0>
They match!
QuEST support for MPI and GPU-accelerated computation ca be enabled in
quest_bind
by setting appropriate feature flags. To enable QuEST's MPI mode,
set the mpi
feature for quest_bind
. Simply edit Cargo.toml
of your binary
crate:
[package]
name = "tryme"
version = "0.1.0"
edition = "2021"
[dependencies]
quest_bind = { features = ["mpi"] }
Now if you compile and run the above program again, the output should be:
EXECUTION ENVIRONMENT:
Running distributed (MPI) version
Number of ranks is 1
...
The feature "gpu"
enables the GPU-accelerated mode. These two features are
mutually exclusive and in case both flags are set, the feature "mpi"
takes
precedence.
To run unit tests for this library, first clone the repository together with QuEST source code as submodule:
git clone --recurse-submodules https://github.com/marek-miller/quest_bind.git
cd quest_bind
Then run:
cargo test
Note that quest_bind
will not run QuEST
's test suite, nor will it check
QuEST
's correctness. The tests here are intended to check if the C API is
invoked correctly, and if Rust's types are passed safely back and forth across
the FFI boundary.
If you want to run the test suite in the single-precision floating point mode,
make sure the build script recompiles libQuEST.so
with the right type
definitions:
cargo clean
cargo test --features=f32
By defualt, quest_bind
uses Rust's double precision floating-point type:
f64
. See Numercal types section below.
You can also try the available examples by running, e.g.:
cargo run --release --example grovers_search
To see the list of all available examples, try:
cargo run --example
In the typical case when it's the numerical computation that dominates the CPU
usage, and not API calls, there should be no discernible difference in
performance between programs calling QuEST routines directly and analogous
applications using quest_bind
. Remember, however, to enable optimizations for
both quest_bind
and QuEST
by compiling your code using the "release"
profile:
cargo run --release
On failure, QuEST throws exceptions via user-configurable global
invalidQuESTInputError()
.
By default, this function prints an error message and aborts, which is
problematic in a large distributed setup.
We opt for catching all exceptions early by reimplementing
invalidQuESTInputError()
to unwind the stack using Rust's
panic
mechanism.
Additionally, all error messages reported by QuEST are logged as errors. To be able to see them, add a logger as a dependency to your crate, e.g.:
cargo add env_logger
Then enable logging in your application:
fn main() {
env_logger::init();
// (...)
}
and run:
RUST_LOG=info cargo run
See log
crate for more on logging in Rust.
The type QuestError
doesn't contain (possibly malformed) data returned by the
API call on failure. Only successful calls can reach the library user. This is
intentional, following guidelines from the QuEST documentation:
[Upon failure] Users must ensure that the triggered API call does not continue (e.g. the user exits or throws an exception), else QuEST will continue with the valid [sic!] input and likely trigger a seg-fault.
See Quest API for more information.
For now, numerical types used by quest_bind
match exactly the C types that
QuEST uses on x86_64
. This is a safe, but not very portable strategy. We pass
Rust types directly to QuEST without casting, assuming the following type
definitions:
pub type c_float = f32;
pub type c_double = f64;
pub type c_int = i32;
pub type c_longlong = i64;
pub type c_ulong = u64;
This should work for many different architectures. If your system uses slightly
different numerical types, quest_bind
simply won't compile and there is not
much you can do besides manually altering the source code.
To check what C types are defined by your Rust installation, see the local
documentation for the module std::ffi
in Rust's Standard Library:
rustup doc
Here's a few things to know, if you'd like to contribute to quest_bind
.
The Rust codebase is formatted according to the settings in ./rustfmt.toml
.
We enable some unstable features of rustfmt
. To format your patches
correctly, you will need the nightly version of the Rust compiler. Before
opening a pull request, remove lint from the code by running:
just lint