Running a simulation

When you've finished building a simulation configuration object, you can turn it into a real simulation as described in this section.

Constructing a simulation

To run the simulation, all you have to do is pass the simulation configuration object to dqcs_sim_new(). This function will return when all the plugins have finished initializing as configured in the configuration object, and return a handle to the simulation.

dqcs_sim_new()

Constructs a DQCsim simulation.

dqcs_handle_t dqcs_sim_new(dqcs_handle_t scfg)

The provided handle is consumed if it is a simulation configuration, regardless of whether simulation construction succeeds.

Note that it is currently not possible to have more than one simulation handle within a single thread at the same time. This has to do with DQCsim's log system, which uses thread-local storage to determine where log messages should go. If you want to run multiple simulations in parallel, you'll have to run them from different threads.

Interacting with a simulation

After constructing the simulation, you have to explicitly tell the frontend plugin to start executing a quantum algorithm. This is done using dqcs_sim_start(). This function is asynchronous: the simulation request is only sent to the frontend when a blocking function is called. To get the result/return value of a previously started quantum algorithm, you can use dqcs_sim_stop(). In fact, you have to do this for every call to dqcs_sim_start(), and you can't have more than one quantum algorithm running at a time within the context of a single simulation.

dqcs_sim_start()

Starts a program on the simulated accelerator.

dqcs_return_t dqcs_sim_start(
    dqcs_handle_t sim,
    dqcs_handle_t data
)

This is an asynchronous call: nothing happens until yield(), recv(), or wait() is called.

The ArbData handle is optional; if 0 is passed, an empty data object is used. If a handle is passed, it is consumed if and only if the API call succeeds.

dqcs_sim_wait()

Waits for the simulated accelerator to finish its current program.

dqcs_handle_t dqcs_sim_wait(dqcs_handle_t sim)

When this succeeds, the return value of the accelerator's run() function is returned in the form of a new handle. When it fails, 0 is returned.

Deadlocks are detected and prevented by returning an error.

While a quantum algorithm is running, you can interact with it using ArbData message queues. You can send and receive data to and from these queues using the following functions. The send function is asynchronous, while the receive function will block if no messages are available.

dqcs_sim_send()

Sends a message to the simulated accelerator.

dqcs_return_t dqcs_sim_send(
    dqcs_handle_t sim,
    dqcs_handle_t data
)

This is an asynchronous call: nothing happens until yield(), recv(), or wait() is called.

The ArbData handle is optional; if 0 is passed, an empty data object is used. If a handle is passed, it is consumed if and only if the API call succeeds.

dqcs_sim_recv()

Waits for the simulated accelerator to send a message to us.

dqcs_handle_t dqcs_sim_recv(dqcs_handle_t sim)

When this succeeds, the received data is returned in the form of a new handle. When it fails, 0 is returned.

Deadlocks are detected and prevented by returning an error.

At any time, you can force DQCsim to pass control to the frontend plugin using the following function. This is primarily useful for debugging, when you for instance want to see the results of a single sent message in the log message stream without calling a blocking function that actually does something.

dqcs_sim_yield()

Yields to the simulator.

dqcs_return_t dqcs_sim_yield(dqcs_handle_t sim)

The simulation runs until it blocks again. This is useful if you want an immediate response to an otherwise asynchronous call through the logging system or some communication channel outside of DQCsim's control.

This function silently returns immediately if no asynchronous data was pending or if the simulator is waiting for something that has not been sent yet.

You can also send ArbCmds to plugins at any time. This corresponds to calling the host_arb callback within a plugin. This is always synchronous; any requests queued through dqcs_sim_start() and dqcs_sim_send() are processed before the ArbCmd, and the function waits for the ArbCmd to finish executing in order for it to return its result.

dqcs_sim_arb()

Sends an ArbCmd message to one of the plugins, referenced by name.

dqcs_handle_t dqcs_sim_arb(
    dqcs_handle_t sim,
    const char *name,
    dqcs_handle_t cmd
)

ArbCmds are executed immediately after yielding to the simulator, so all pending asynchronous calls are flushed and executed before the ArbCmd.

When this succeeds, the received data is returned in the form of a new handle. When it fails, 0 is returned.

The ArbCmd handle is consumed if and only if the API call succeeds.

dqcs_sim_arb_idx()

Sends an ArbCmd message to one of the plugins, referenced by index.

dqcs_handle_t dqcs_sim_arb_idx(
    dqcs_handle_t sim,
    ssize_t index,
    dqcs_handle_t cmd
)

The frontend always has index 0. 1 through N are used for the operators in front to back order (where N is the number of operators). The backend is at index N+1.

Python-style negative indices are supported. That is, -1 can be used to refer to the backend, -2 to the last operator, and so on.

ArbCmds are executed immediately after yielding to the simulator, so all pending asynchronous calls are flushed and executed before the ArbCmd.

When this succeeds, the received data is returned in the form of a new handle. When it fails, 0 is returned.

The ArbCmd handle is consumed if and only if the API call succeeds.

Querying plugin information

You can query the metadata associated with the plugins that make up a simulation using the following functions.

dqcs_sim_get_name()

Queries the implementation name of a plugin, referenced by instance name.

char *dqcs_sim_get_name(
    dqcs_handle_t sim,
    const char *name
)

On success, this returns a newly allocated string containing the name. Free it with free() when you're done with it to avoid memory leaks. On failure (i.e., the handle is invalid) this returns NULL.

dqcs_sim_get_name_idx()

Queries the implementation name of a plugin, referenced by index.

char *dqcs_sim_get_name_idx(
    dqcs_handle_t sim,
    ssize_t index
)

On success, this returns a newly allocated string containing the name. Free it with free() when you're done with it to avoid memory leaks. On failure (i.e., the handle is invalid) this returns NULL.

dqcs_sim_get_author()

Queries the author of a plugin, referenced by instance name.

char *dqcs_sim_get_author(
    dqcs_handle_t sim,
    const char *name
)

On success, this returns a newly allocated string containing the author. Free it with free() when you're done with it to avoid memory leaks. On failure (i.e., the handle is invalid) this returns NULL.

dqcs_sim_get_author_idx()

Queries the author of a plugin, referenced by index.

char *dqcs_sim_get_author_idx(
    dqcs_handle_t sim,
    ssize_t index
)

On success, this returns a newly allocated string containing the author. Free it with free() when you're done with it to avoid memory leaks. On failure (i.e., the handle is invalid) this returns NULL.

dqcs_sim_get_version()

Queries the version of a plugin, referenced by instance name.

char *dqcs_sim_get_version(
    dqcs_handle_t sim,
    const char *name
)

On success, this returns a newly allocated string containing the version. Free it with free() when you're done with it to avoid memory leaks. On failure (i.e., the handle is invalid) this returns NULL.

dqcs_sim_get_version_idx()

Queries the version of a plugin, referenced by index.

char *dqcs_sim_get_version_idx(
    dqcs_handle_t sim,
    ssize_t index
)

On success, this returns a newly allocated string containing the version. Free it with free() when you're done with it to avoid memory leaks. On failure (i.e., the handle is invalid) this returns NULL.

Shutting a simulation down

When you're done with a simulation, you can just use dqcs_handle_delete() to shut it down. Before doing that, though, it is strongly recommended to output a reproduction file. This file lets you reproduce the simulation exactly without needing the host executable (or needing it to be deterministic) with just DQCsim's command-line interface. You never know when you might need this for debugging!

dqcs_sim_write_reproduction_file()

Writes a reproduction file for the simulation so far.

dqcs_return_t dqcs_sim_write_reproduction_file(
    dqcs_handle_t sim,
    const char *filename
)