Interacting with DQCsim

When you start a plugin with dqcs_plugin_run() or dqcs_plugin_start(), DQCsim will start calling the callbacks you provided. Each of these callbacks takes a dqcs_plugin_state_t handle, which can be used to interact with DQCsim and the downstream plugin(s) using the functions listed in this section.

Frontend to host communication

Within a frontend's run callback, the following two functions can be used to send and receive ArbData messages to and from the host.

dqcs_plugin_send()

Sends a message to the host.

dqcs_return_t dqcs_plugin_send(
    dqcs_plugin_state_t plugin,
    dqcs_handle_t arb
)

It is only legal to call this function from within the run() callback. Any other source will result in an error.

The cmd handle is consumed by this function if and only if it succeeds.

dqcs_plugin_recv()

Waits for a message from the host.

dqcs_handle_t dqcs_plugin_recv(dqcs_plugin_state_t plugin)

It is only legal to call this function from within the run() callback. Any other source will result in an error.

When successful, this function returns a new handle to the received ArbData object. 0 is used to indicate that an error occurred.

The send function always returns immediately, but the receive function may block to return control to the host if no messages were in the buffer. That means that the latter can call into a different callback, such as host_arb.

Upstream to downstream communication

The following functions can be used by upstream plugins (frontends and operators) to perform an operation on the downstream plugin. They correspond one-to-one with the downstream callbacks.

dqcs_plugin_allocate()

Allocate the given number of downstream qubits.

dqcs_handle_t dqcs_plugin_allocate(
    dqcs_plugin_state_t plugin,
    uintptr_t num_qubits,
    dqcs_handle_t cq
)

Backend plugins are not allowed to call this. Doing so will result in an error.

num_qubits specifies the number of qubits that are to be allocated.

commands must be 0 or a valid handle to an ArbCmd queue, containing a list of commands that may be used to modify the behavior of the qubit register; 0 is equivalent to zero commands. The queue is consumed by this function, i.e. the handle becomes invalid, if and only if it succeeds.

If the function is successful, a new handle to the set of qubit references representing the newly allocated register is returned. When the function fails, 0 is returned.

dqcs_plugin_free()

Free the given downstream qubits.

dqcs_return_t dqcs_plugin_free(
    dqcs_plugin_state_t plugin,
    dqcs_handle_t qbset
)

Backend plugins are not allowed to call this. Doing so will result in an error.

qubits must be a valid set of qubit references. The set is consumed by this function, i.e. the handle becomes invalid, if and only if it succeeds.

dqcs_plugin_gate()

Tells the downstream plugin to execute a gate.

dqcs_return_t dqcs_plugin_gate(
    dqcs_plugin_state_t plugin,
    dqcs_handle_t gate
)

Backend plugins are not allowed to call this. Doing so will result in an error.

gate must be a valid gate object. The object is consumed by this function, i.e. the handle becomes invalid, if and only if it succeeds.

dqcs_plugin_advance()

Tells the downstream plugin to run for the specified number of cycles.

dqcs_cycle_t dqcs_plugin_advance(
    dqcs_plugin_state_t plugin,
    dqcs_cycle_t cycles
)

Backend plugins are not allowed to call this. Doing so will result in an error.

The return value is the new cycle counter. This function uses -1 to signal an error.

dqcs_plugin_arb()

Sends an arbitrary command downstream.

dqcs_handle_t dqcs_plugin_arb(
    dqcs_plugin_state_t plugin,
    dqcs_handle_t cmd
)

Backend plugins are not allowed to call this. Doing so will result in an error.

This function returns a new handle to an ArbData object representing the return value of the ArbCmd when successful. Otherwise, it returns 0.

For performance reasons, all of the above functions except dqcs_plugin_arb() are asynchronous. They send the associated request immediately, but it is down to OS thread/process scheduling when the request is actually executed. This means the following:

  • The ordering of log messages sent by differing plugins depends on OS scheduling.
  • Errors caused by these asynchronous functions cannot be propagated upstream. Therefore, any error that does occur is necessarily fatal.

dqcs_plugin_arb() is exempt from this since it returns a value, so ArbCmd errors are not necessarily fatal.

Querying the state of the downstream plugin

Measurement results requested through measurement gates need to be explicitly fetched when they are needed through the following function. It always returns the result of the most recent measurement gate for a specific qubit.

dqcs_plugin_get_measurement()

Returns the latest measurement of the given downstream qubit.

dqcs_handle_t dqcs_plugin_get_measurement(
    dqcs_plugin_state_t plugin,
    dqcs_qubit_t qubit
)

Backend plugins are not allowed to call this. Doing so will result in an error.

If the function succeeds, it returns a new handle to a qubit measurement result object. Otherwise it returns 0.

DQCsim also records some timing information whenever a measurement is performed. This may be useful for calculating fidelity information within an algorithm running in the presence of errors.

dqcs_plugin_get_cycles_since_measure()

Returns the number of downstream cycles since the latest measurement of the given downstream qubit.

dqcs_cycle_t dqcs_plugin_get_cycles_since_measure(
    dqcs_plugin_state_t plugin,
    dqcs_qubit_t qubit
)

Backend plugins are not allowed to call this. Doing so will result in an error.

This function uses -1 to signal an error.

dqcs_plugin_get_cycles_between_measures()

Returns the number of downstream cycles between the last two measurements of the given downstream qubit.

dqcs_cycle_t dqcs_plugin_get_cycles_between_measures(
    dqcs_plugin_state_t plugin,
    dqcs_qubit_t qubit
)

Backend plugins are not allowed to call this. Doing so will result in an error.

This function uses -1 to signal an error.

Finally, a simulation cycle counter is maintained. This is just an accumulation of all the dqcs_plugin_advance() calls since the start of the simulation.

dqcs_plugin_get_cycle()

Returns the current value of the downstream cycle counter.

dqcs_cycle_t dqcs_plugin_get_cycle(dqcs_plugin_state_t plugin)

Backend plugins are not allowed to call this. Doing so will result in an error.

This function uses -1 to signal an error.

Random number generation

To ensure that a DQCsim simulation can be deterministically reproduced, it is strongly recommended to use the following random number generation functions.

dqcs_plugin_random_f64()

Generates a random floating point number using the simulator random seed.

double dqcs_plugin_random_f64(dqcs_plugin_state_t plugin)

The generated numbers are uniformly distributed in the range [0,1>.

This function only fails if the plugin handle is invalid, in which case it returns 0. Of course, 0 is also a valid (if rare) random return value.

dqcs_plugin_random_u64()

Generates a random unsigned 64-bit number using the simulator random seed.

dqcs_handle_t dqcs_plugin_random_u64(dqcs_plugin_state_t plugin)

This function only fails if the plugin handle is invalid, in which case it returns 0. Of course, 0 is also a valid (if rare) random return value.

Particularly, these generators use a separate PRNG stream depending on whether the callback they are executed from is synchronous to the upstream channel (modify_measurement) or the downstream channel (all other callbacks). This is important, because the ordering of upstream callbacks with respect to downstream callbacks is dependent on OS scheduling.

If you only use downstream callbacks, it's also fine to seed your own PRNG using the first number returned by dqcs_plugin_random_u64() in the init callback. However, using a randomly seeded PRNG is strongly discouraged, since it prevents a user from using a fixed random seed for reproduction.