Plugin anatomy
To define your own plugin, you can use the Plugin
class. The workflow is
as follows:
- Use
Plugin::Frontend()
,Plugin::Operator()
, orPlugin::Backend()
to start defining a plugin. - Assign callback functions at your leisure using the
with_*
functions. You can pass any combination of arguments supported bydqcsim::wrap::Callback()
to these builder functions, provided that the callback function signature is correct, of course. - Either:
- run the plugin in the current thread using
Plugin::run()
; - start the plugin in a DQCsim-managed worker thread using
Plugin::start()
; - or pass the plugin definition object to
PluginConfigurationBuilder::with_callbacks()
to directly add it to a simulation.
- run the plugin in the current thread using
Here's an example of a simple frontend plugin that just logs "Hello, World!"
:
#define DQCSIM_SHORT_LOGGING_MACROS
#include <dqcsim>
using namespace dqcsim::wrap;
ArbData run(RunningPluginState &state, ArbData &&arg) {
INFO("Hello, World!");
return ArbData();
}
int main(int argc, char *argv[]) {
return Plugin::Frontend("hello", "JvS", "v1.0")
.with_run(run)
.run(argc, argv);
}
The Plugin
class is equivalent to the
pdef
C API, but, as you can see, is much more
succinct. Note for instance that the example includes all error handling
implicitly thanks to exceptions, and that only a single statement is needed
thanks to the builder pattern.
The C++ API supports a few different styles for the callback functions. The one used here is the most basic one; even more basic than the one in the C API as it does not take a user-defined parameter to store state or initialization parameters in. You can add that quite simply:
#include <string>
#define DQCSIM_SHORT_LOGGING_MACROS
#include <dqcsim>
using namespace dqcsim::wrap;
ArbData run(std::string *message, RunningPluginState &state, ArbData &&arg) {
INFO("%s", message->c_str());
*message = "I was run!";
return ArbData();
}
int main(int argc, char *argv[]) {
std::string message = "Hello!";
int code = Plugin::Frontend("hello", "JvS", "v1.0")
.with_run(run, &message)
.run(argc, argv);
INFO("%s", message.c_str());
return code;
}
You can also abstract your plugin into a class. For instance, the following more complex example counts the number of gates passing through the operator.
#define DQCSIM_SHORT_LOGGING_MACROS
#include <dqcsim>
using namespace dqcsim::wrap;
class GateCounter {
public:
int counter = 0;
MeasurementSet gate(PluginState &state, Gate &&gate) {
counter++;
state.gate(std::move(gate));
return MeasurementSet();
}
void drop(PluginState &state) {
NOTE("%d gate(s) were transferred!", counter);
}
};
int main(int argc, char *argv[]) {
GateCounter gateCounter;
return Plugin::Operator("GateCounter", "JvS", "v1.0")
.with_gate(&gateCounter, &GateCounter::gate)
.with_drop(&gateCounter, &GateCounter::drop)
.run(argc, argv);
}
Note the std::move()
in the gate()
callback. Handles in DQCsim are
generally not efficiently copyable, thus move()
ing stuff around with rvalue
references becomes important at times. If you don't know what that means, you
should probably look it up at some point, but the C++ API provides
(inefficient) copy constructors and overloads with regular references for most
handle types, so usually things will just work without the std::move()
.