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:
sh
cargo new testme
cd testme/
Add quest-bind
to your project's dependencies:
sh
cargo add quest_bind --git https://github.com/marek-miller/quest-bind.git
Now write some code and put it in ./src/main.rs
:
```rust use quest_bind::*;
fn main() -> Result<(), QuestError> { // Initialize QuEST environment let env = &QuestEnv::new(); reportquestenv(env);
// Initialize 2-qubit register in |00> state
let qureg = &mut Qureg::try_new(2, env)?;
report_qureg_params(qureg);
init_zero_state(qureg);
println!("---\nPrepare Bell state: |00> + |11>");
hadamard(qureg, 0).and(controlled_not(qureg, 0, 1))?;
// Measure each qubit
let outcome0 = measure(qureg, 0)?;
let outcome1 = measure(qureg, 1)?;
println!("Qubit \"0\" measured in state: |{outcome0}>");
println!("Qubit \"1\" measured in state: |{outcome1}>");
assert_eq!(outcome0, outcome1);
println!("They match!");
// Both `env` and `qureg` are safely discarded here
Ok(())
} ```
You can read the available documentation locally or refer to QuEST headers for the full description of the C API:
sh
cargo doc --open
Lastly, compile and run the program:
sh
cargo run
You should be able to see something like:
```text 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.
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:
```toml [package] name = "testme" version = "0.1.0" edition = "2021"
[dependencies] quest_bind = { git = "https://github.com/marek-miller/quest-bind.git", features = ["mpi",] } ```
Now if you compile and run the above program again, the output should be:
text
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:
sh
git clone --recurse-submodules https://github.com/marek-miller/quest-bind.git
cd quest-bind
Then run:
sh
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:
sh
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.:
sh
cargo run --release --example grovers_search
To see the list of all available examples, try:
sh
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:
sh
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. The exception handler is locked during an API call. This means that calling QuEST functions is synchronous and should be thread-safe, but comes at the expense of being able to run only one QuEST API call at the time. Bear in mind, though, that each QuEST function retains access to all parallel computation resources available in the system.
Current implementation returns inside Result<_, QuestError>
only the first
exception caught. All subsequent messages reported by QuEST, together with that
first one, are nevertheless logged as errors. To be able to see them, add a
logger as a dependency to your crate, e.g.:
sh
cargo add env_logger
Then enable logging in your application:
rust
fn main() {
env_logger::init();
// (...)
}
and run:
sh
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:
rust
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:
sh
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:
sh
just lint
New features/improvements:
API breaking changes:
Change signature of the following functions:
mix_nontp_kraus_map()
mix_nontp_two_qubit_kraus_map()
mix_nontp_multi_qubit_kraus_map()
These functions now take the list of Kraus operators by reference.
apply_named_phase_func()
This function returns now Result<(), QuestError>
.
apply_pauli_sum()
apply_pauli_hamil()
These functions take argument in_qureg
as &mut
now (instead of a shared
reference).
Fix typo in the function name: apply_trotter_circuit()
Function: multi_controlled_multi_rotate_pauli()
also changes signature.
New features/improvements:
New features/improvements:
grovers_search.rs
Complex<f64>
type from num
crate (as QComplex
)"f32"
to set floating point precisionInitial release.