Make your own plugin
In our framework, a Plugin class should inherit from qat.plugins.AbstractPlugin
which defined the following
methods:
Method
compile()
required, this method takes aBatch
and aHardwareSpecs
and returns aBatch
. This compile method (way in) will pre-process any batch before executing it on the QPUMethod
post_process()
optional, this method takes aBatchResult
and returns either aBatchResult
or aBatch
. This post_process method (way out) will post-process the result returned by the QPU. If this method returns aBatch
, this new batch will be resubmitted to the QPU, otherwise the new result is returned to the user
Raising exception in the code
A Plugin can raise a PluginException
. A plugin can be accessed remotely (using
Qaptiva Access or by starting the Plugin in server mode). This exception can be serialized and re-raised on
the client side
A PluginException
can be raised using assert_plugin()
from qat.core.assertion import assert_plugin
# If my_condition() returns False: raises a PluginException
# If my_condition() returns True: do nothing
assert_plugin(my_condition(), "Error message")
Method compile
Method compile()
is the only required method. This function takes
two parameters:
An argument of type
Batch
defining what should be executed by the QPUAn argument of type
HardwareSpecs
giving the capabilities of the QPU. This argument is given by theget_specs()
method of the QPU. This object contains some information on the QPU and can be used to adapt the compilation stage
This function should return a Batch
that will be executed by the rest of the stack
from qat.core.plugins import AbstractPlugin
class MyPlugin(AbstractPlugin):
def compile(self, batch, hardware_specs):
# Do something with the batch of jobs `batch`, and, optionally, the specs
return batch
Method post_process
Method post_process()
is an optional method. This function takes a BatchResult
(i.e. a list of Result
) and returns either a:
A
BatchResult
: the result is then returned to the user (and post-processed by plugins placed before in the stack, if any)A
Batch
: the result is resubmitted to the QPU (and pre-processed by plugins placed between the current plugin and the QPU)
This function is designed to repair the result if needed. For instance, the compile()
method can
return an equivalent batch (which is not equal), the result can then be repaired using post_process()
to ensure that the result returned to the user correspond to expected result of the initial not compiled batch
from qat.core.plugins import AbstractPlugin
class MyPlugin(AbstractPlugin):
def compile(self, batch, hardware_specs):
# Do something with the batch of jobs `batch`, and, optionally, the specs
return batch
def post_process(self, batch_result):
# Do something with the execution result
return batch_result
Note
Any plugin has a do_post_processing()
method. This method is used to check if the plugin
must repair the result before returning it to the user (this function does not take any parameter and returns a boolean).
If a delocalized plugin (i.e. distant plugin) is used in a stack, this function will be used to check if the remote plugin needs to do post-processing. If the remote plugin does not need to do post-processing, the result is not send to this plugin, saving a lot of time
A concrete example
This section provides an example of plugin displaying debug information to the screen. This plugin will:
print all circuits submitted to the QPU. This function will loop of over the jobs composing the batch and print the circuit for each job
print the result of the computation. This function will loop over the results composing the batch-result and print the samples for each result
The plugin will look like:
from qat.core.plugins import AbstractPlugin
class MyPlugin(AbstractPlugin):
def compile(self, batch, hardware_specs):
# For each job
for index, job in enumerate(batch):
print(f">> Job #{index}")
# Print the circuit (i.e. printing each gate)
for op in job.circuit.iterate_simple():
print(op)
# Return compiled batch
return batch
def post_process(self, batch_result):
# For each result
for index, result in enumerate(batch_result):
print(f">> Result #{index} - result of size {len(result)}")
# Print the result (i.e. print each sample)
for sample in result:
print(sample.state, sample.probability)
return batch_result
This plugin can be used with any QPU, by using the pipe operator. The following example creates a QPU using
get_default_qpu()
as execution engine but pre-processing and post-processing jobs using our plugin
from qat.qpus import get_default_qpu
my_qpu = MyPlugin() | get_default_qpu()
This QPU can be used to execute a GHZ circuit
from qat.lang import qrout, H, CNOT
@qrout
def ghz():
H(0)
CNOT(0, 1)
CNOT(1, 2)
all_results = my_qpu.submit([ghz.to_job()] * 3)
print("\n===== Final result =====")
for result in all_results:
print(result)
>> Job #0
('H', [], [0])
('CNOT', [], [0, 1])
('CNOT', [], [1, 2])
>> Job #1
('H', [], [0])
('CNOT', [], [0, 1])
('CNOT', [], [1, 2])
>> Job #2
('H', [], [0])
('CNOT', [], [0, 1])
('CNOT', [], [1, 2])
>> Result #0 - result of size 2
|000> 0.4999999999999999
|111> 0.4999999999999999
>> Result #1 - result of size 2
|000> 0.4999999999999999
|111> 0.4999999999999999
>> Result #2 - result of size 2
|000> 0.4999999999999999
|111> 0.4999999999999999
===== Final result =====
Result(raw_data=None, _value=None, error=None, value_data=None, error_data=None, meta_data={'simulation_time': '0.000321'}, in_memory=True, data=ResData(mem_ptr=94876961580992, data_type=1, data_size=8, _serialized=None, qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1499e4395c70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1499e43ea7c0>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea790>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea5e0>])]), qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1499e4395c70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1499e43ea7c0>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea790>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea5e0>])], _parameter_map=None, _values=None, values_data=None, need_flip=False, nbqbits=None, lsb_first=False, has_statevector=True, statevector=array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0. +0.j,
0. +0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]))
Result(raw_data=None, _value=None, error=None, value_data=None, error_data=None, meta_data={'simulation_time': '0.000120'}, in_memory=True, data=ResData(mem_ptr=94876953801328, data_type=1, data_size=8, _serialized=None, qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1499e4395c70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1499e43ea7c0>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea790>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea5e0>])]), qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1499e4395c70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1499e43ea7c0>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea790>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea5e0>])], _parameter_map=None, _values=None, values_data=None, need_flip=False, nbqbits=None, lsb_first=False, has_statevector=True, statevector=array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0. +0.j,
0. +0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]))
Result(raw_data=None, _value=None, error=None, value_data=None, error_data=None, meta_data={'simulation_time': '0.000285'}, in_memory=True, data=ResData(mem_ptr=94876966430976, data_type=1, data_size=8, _serialized=None, qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1499e4395c70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1499e43ea7c0>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea790>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea5e0>])]), qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1499e4395c70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1499e43ea7c0>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea790>, <qat.lang.AQASM.bits.Qbit object at 0x1499e43ea5e0>])], _parameter_map=None, _values=None, values_data=None, need_flip=False, nbqbits=None, lsb_first=False, has_statevector=True, statevector=array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0. +0.j,
0. +0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]))