Expand description
§Using Experimental Simulators from C
This module exposes a C API for this crate, useful for embedding into simulation runtimes.
§Safety
As this is a foreign-function interface, many of the functions exposed here are unsafe, representing that the caller is required to ensure that safety conditions in the host language are upheld.
Please pay attention to any listed safety notes when calling into this C API.
§Generating and Using C API Headers
The qdk_sim crate has enabled the use of cbindgen, such that C-language header files are generated automatically as part of the build for this crate.
cargo install --force cbindgen
cbindgen --language C --output include/qdk_sim.h
cbindgen --language C++ --output include/qdk_sim.hppThis will generate include/qdk_sim.h and include/qdk_sim.hpp, which can then be used from C and C++ callers, respectively. For example, to call from C:
#include <stdio.h>
#include "qdk_sim.h"
int main() {
uintptr_t sim_id;
uintptr_t result0, result1;
init(2, "mixed", &sim_id);
h(sim_id, 0);
h(sim_id, 1);
m(sim_id, 0, &result0);
m(sim_id, 1, &result1);
printf("got %llu %llu", result0, result1);
destroy(sim_id);
}To build and run the above example using Clang on Windows:
$ clang example.c -Iinclude -Ltarget/debug -lqdk_sim -lws2_32 -lAdvapi32 -lUserenv
$ ./a.exe
got 1 1§Error Handling and Return Values
Most C API functions for this crate return an integer, with 0 indicating success and any other value indicating failure. In the case that a non-zero value is returned, API functions will also set the last error message, accessible by calling lasterr:
#include <stdio.h>
#include "qdk_sim.h"
int main() {
uintptr_t sim_id;
uintptr_t result0, result1;
if (init(2, "invalid", &sim_id) != 0) {
printf("Got an error message: %s", lasterr());
} else {
destroy(sim_id);
}
}C API functions that need to return data to the caller, such as m, do so by accepting pointers to memory where results should be stored.
⚠ WARNING: It is the caller’s responsibility to ensure that pointers used to hold results are valid (that is, point to memory that can be safely written into).
For example:
uintptr_t result;
if (m(sim_id, 0, &result) != 0) {
printf("Got an error message: %s", lasterr());
} else {
printf("Got a measurement result: %llu", result);
}§Initializing, Using, and Destroying Simulators
To create a new simulator from C, use the init function. This function accepts a pointer to an unsigned integer that will be set to an ID for the new simulator:
uintptr_t sim_id;
// Initialize a new simulator with two qubits and using a mixed-state
// representation.
if (init(2, "mixed", &sim_id) != 0) {
printf("Error initializing simulator: %s", lasterr());
}The ID for the newly created simulator can then be used to call into functions that apply different quantum operations, such as x, h, or cnot:
// Apply an 𝑋 operation to qubit #0.
if (x(sim_id, 0) != 0) {
printf("Error applying X: %s", lasterr());
}To free the memory associated with a given simulator, use the destroy function:
if (destroy(sim_id) != 0) {
printf("Error destroying simulator: %s", lasterr());
}§Getting and Setting Noise Models
Noise models for each simulator can be accessed or set by using get_noise_model, get_noise_model_by_name, set_noise_model, and set_noise_model_by_name. Each of these functions accepts either a name of a built-in noise model (see crate::NoiseModel::get_by_name for details).
Noise models in the C API are represented by strings containing JSON serializations of the crate::NoiseModel data model. For example:
#include <stdio.h>
#include "qdk_sim.h"
int main() {
const char *noise_model;
if (get_noise_model_by_name("ideal", &noise_model) != 0) {
printf("Error getting noise model: %s", lasterr());
}
printf("Noise model:\n%s", noise_model);
}
Running the above results in the JSON representation of the ideal noise model being written to the console:
Noise model:
{"initial_state":{"n_qubits":1,"data":{"Mixed":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}},"i":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0]]}}},"x":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.0,0.0],[1.0,0.0],[1.0,0.0],[0.0,0.0]]}}},"y":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.0,0.0],[0.0,1.0],[-0.0,-1.0],[0.0,0.0]]}}},"z":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[-1.0,-0.0]]}}},"h":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[0.7071067811865476,0.0],[0.7071067811865476,0.0],[0.7071067811865476,0.0],[-0.7071067811865476,-0.0]]}}},"s":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,1.0]]}}},"s_adj":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,-0.0],[0.0,-0.0],[0.0,-0.0],[0.0,-1.0]]}}},"t":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.7071067811865476,0.7071067811865476]]}}},"t_adj":{"n_qubits":1,"data":{"Unitary":{"v":1,"dim":[2,2],"data":[[1.0,-0.0],[0.0,-0.0],[0.0,-0.0],[0.7071067811865476,-0.7071067811865476]]}}},"cnot":{"n_qubits":2,"data":{"Unitary":{"v":1,"dim":[4,4],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0],[0.0,0.0]]}}},"z_meas":{"Effects":[{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]}}},{"n_qubits":1,"data":{"KrausDecomposition":{"v":1,"dim":[1,2,2],"data":[[0.0,0.0],[0.0,0.0],[0.0,0.0],[1.0,0.0]]}}}]}}See noise model serialization for more details.
Functions§
- cnot
- Applies the
CNOToperation acting on two given qubits to a given simulator, using the currently set noise model. - destroy
- Deallocates the simulator with the given id, releasing any resources owned by that simulator.
- get_
current_ state - Returns the state of a given simulator, serialized as a JSON object.
- get_
noise_ model - Returns the currently configured noise model for a given simulator, serialized as a string representing a JSON object.
- get_
noise_ model_ by_ name - Gets the noise model corresponding to a particular name, serialized as a string representing a JSON object.
- get_
simulator_ info - Returns information about how this simulator was built, serialized as a JSON object.
- h
- Applies the
Hoperation acting on a given qubit to a given simulator, using the currently set noise model. - init⚠
- Allocate a new simulator with a given capacity, measured in the number of qubits supported by that simulator. Returns an id that can be used to refer to the new simulator in future function calls.
- lasterr
- Returns the last error message raised by a call to a C function. If no error message has been raised, returns a null pointer.
- m⚠
- Measures a single qubit in the $Z$-basis, returning the result by setting the value at a given pointer.
- s
- Applies the
Soperation acting on a given qubit to a given simulator, using the currently set noise model. - s_adj
- Applies the
Adjoint Soperation acting on a given qubit to a given simulator, using the currently set noise model. - set_
noise_ ⚠model - Sets the noise model used by a given simulator instance, given a string containing a JSON serialization of that noise model.
- set_
noise_ ⚠model_ by_ name - Sets the noise model used by a given simulator instance, given a string containing the name of a built-in noise model.
- t
- Applies the
Toperation acting on a given qubit to a given simulator, using the currently set noise model. - t_adj
- Applies the
Adjoint Toperation acting on a given qubit to a given simulator, using the currently set noise model. - x
- Applies the
Xoperation acting on a given qubit to a given simulator, using the currently set noise model. - y
- Applies the
Yoperation acting on a given qubit to a given simulator, using the currently set noise model. - z
- Applies the
Zoperation acting on a given qubit to a given simulator, using the currently set noise model.