Building custom execution stacks

Plugins & information flow

When dealing with low-level objects such as quantum circuits or execution results (e.g samples), it becomes quickly necessary to have a fine control over how the information is processed and transformed.

In order to simplify the design of applications, QLM comes with an additional API, called Plugin. Plugins are objects that can process a flow of quantum circuits (or jobs) on their way to a QPU, and/or process a flow of information (samples or values) on their way back from a QPU.

Their API is composed of two methods:

  • compile for the way in (i.e from the user to the QPU). This method will take a Batch together with some HardwareSpecs and return a new Batch.

  • post_process for the way out (i.e from the QPU to the user). This method will process a BatchResult and return either a BatchResult or a new Batch that should go back to the QPU.

This simple semantics allow to compose Plugin to form expressive compilation stacks and extend the features provided by a QPU.

Creating a stack using plugins is quite straightforward:

my_stack = plugin1 | plugin2 | .. | my_qpu

In this code, when a fresh batch or job is submitted, the batch will run through the compile of plugin1, the resulting batch will run through the compile of plugin2, etc. When reaching my_qpu the execution will start, and the results will be processed in reversed order on their way back.

Overall the information flow can be summed up as follows:

images/plugin_qpu.png

Plugins can be used to perform a wide range of transformations. Some of them are detailed in this section.

See Plugins for a list of all available plugins.

But more importantly, it is quite simple to write your own QLM compatible plugin. You will find below all the details to define and run your own plugin.

Writing your own plugin

The basic API of the Plugin service is described by the abstract class AbstractPlugin.

In order to inherit from all the features of the Plugin class, you should start by inheriting from AbstractPlugin.

from qat.core.plugins import AbstractPlugin

class MyPlugin(AbstractPlugin):
    pass

MyPlugin()
Traceback (most recent call last):
  File "<stdin>", line 6, in <module>
TypeError: Can't instantiate abstract class MyPlugin with abstract methods compile

Of course, we need to implement the basic Plugin API via the two methods compile (way in) and post_process (way out, optional). There is also a third method required: do_post_processing. We will discuss this method later.

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

MyPlugin()

As you can see, our plugin is now “valid” and can be instantiated. For now, it is not a very useful plugin. Since our plugin inherits from the AbstractPlugin class, we can compose it with a QPU to build a 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

MyPlugin()

from qat.qpus import PyLinalg

my_stack = MyPlugin() | PyLinalg()

from qat.lang.AQASM import Program, H

prog = Program()
for qb in prog.qalloc(3):
    prog.apply(H, qb)

for sample in my_stack.submit(prog.to_circ().to_job()):
    print(sample)
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])
Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x1510f810c710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x1510f810c828>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810ca58>, <qat.lang.AQASM.bits.Qbit object at 0x1510f810cb38>])])

Let us change our code so that our plugin prints any incoming circuit and the size of each result returned by the QPU:

from qat.core.plugins import AbstractPlugin

class MyPlugin(AbstractPlugin):
    def compile(self, batch, hardware_specs):
        for i, job in enumerate(batch.jobs):
            print(">> Job #{}".format(i))
            for op in job.circuit.iterate_simple():
                print(op)
        return batch

    def post_process(self, batch_result):
        # do something with the execution results
        for result in batch_result.results:
            print('Result of size', len(result.raw_data))
        return batch_result

MyPlugin()

from qat.qpus import PyLinalg

my_stack = MyPlugin() | PyLinalg()

from qat.lang.AQASM import Program, H

prog = Program()
for qb in prog.qalloc(3):
    prog.apply(H, qb)

job = prog.to_circ().to_job()
# Let's submit 3 times our job in a single go
for sample in my_stack.submit([job] * 3):
    print(sample)
>> Job #0
('H', [], [0])
('H', [], [1])
('H', [], [2])
>> Job #1
('H', [], [0])
('H', [], [1])
('H', [], [2])
>> Job #2
('H', [], [0])
('H', [], [1])
('H', [], [2])
Result of size 8
Result of size 8
Result of size 8
Result(need_flip=False, lsb_first=False, nbqbits=None, has_statevector=False, statevector=None, data=None, _value=None, raw_data=[Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None)
Result(need_flip=False, lsb_first=False, nbqbits=None, has_statevector=False, statevector=None, data=None, _value=None, raw_data=[Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None)
Result(need_flip=False, lsb_first=False, nbqbits=None, has_statevector=False, statevector=None, data=None, _value=None, raw_data=[Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x14c9f2824710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x14c9f2824828>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824a58>, <qat.lang.AQASM.bits.Qbit object at 0x14c9f2824b38>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None)

As you can see, the plugin saw the 3 jobs on their way in and the three results on their way out!

Finally, let’s change the implementation of the do_post_processing method:

from qat.core.plugins import AbstractPlugin

class MyPlugin(AbstractPlugin):
    def compile(self, batch, hardware_specs):
        for i, job in enumerate(batch.jobs):
            print(">> Job #{}".format(i))
            for op in job.circuit.iterate_simple():
                print(op)
        return batch

    def post_process(self, batch_result):
        # do something with the execution results
        for result in batch_result.results:
            print('Result of size', len(result.raw_data))
        return batch_result

    def do_post_processing(self):
        return False

MyPlugin()

from qat.qpus import PyLinalg

my_stack = MyPlugin() | PyLinalg()

from qat.lang.AQASM import Program, H

prog = Program()
for qb in prog.qalloc(3):
    prog.apply(H, qb)

job = prog.to_circ().to_job()
# Let's submit 3 times our job in a single go
for sample in my_stack.submit([job] * 3):
    print(sample)
>> Job #0
('H', [], [0])
('H', [], [1])
('H', [], [2])
>> Job #1
('H', [], [0])
('H', [], [1])
('H', [], [2])
>> Job #2
('H', [], [0])
('H', [], [1])
('H', [], [2])
Result(need_flip=False, lsb_first=False, nbqbits=None, has_statevector=False, statevector=None, data=None, _value=None, raw_data=[Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None)
Result(need_flip=False, lsb_first=False, nbqbits=None, has_statevector=False, statevector=None, data=None, _value=None, raw_data=[Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None)
Result(need_flip=False, lsb_first=False, nbqbits=None, has_statevector=False, statevector=None, data=None, _value=None, raw_data=[Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=0, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=1, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=2, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=3, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=4, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=5, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=6, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])]), Sample(_amplitude=ComplexNumber(re=0.3535533905932737, im=0.0), probability=0.12499999999999994, _state=7, err=None, intermediate_measurements=[], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x149adcfeb710>, length=3, start=0, msb=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x149adcfeb828>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfeba58>, <qat.lang.AQASM.bits.Qbit object at 0x149adcfebb38>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None)

Since the method now returns False, the stack will skip the plugin post_process method. This is useful when a Plugin does not need to post process the outgoing results. In the case where the Plugin is delocalized (i.e is a distant service), this will save a lot of time.

The default implementation of do_post_processing checks if the plugin has a method post_process. If a post_process method exists, the method do_post_processing will return True, otherwise False.

Please note that the post_process can return as well BatchResult objects as Batch objects. If the output of this function is a Batch object, the Batch will be sent back to the QPU (and compiled by the plugins present between the current plugin and the QPU).

Building expressive stacks using Junctions

If Plugins can be seen as two-way pipes that transform quantum programs on the way in and execution result on the way back, Junction can be seen as, well, junctions, in this piping system. More precisely, they provide a simple interface to embed repeated, adaptive, classical computations in the middle on the execution stack.

The simpler, and most widely used example would the one of a variational optimizer dealing with a variational eigensolving procedure. In this setting, the incoming Job is an “abstract” job with open angles or variables. The optimizer would like to start and iteratively give these angles some value, evaluate the energy for this set of values and iterate until satfisfied. Of couse, it is completely possible to deal with this type of routines outside of the QLM stack, but this might prevent some optimized workflow where the job is first compiled and optimized for a given architecture and only then enters the variational solver.

With junctions, it is rather trivial to embed any such adaptive treatment after the compilation and optimization stage of the stack.

Lets see how this works!

In the following example, we will construct a quite naive junction that will process a quantum circuit with a single parameter and iteratively try all the values for this parameter with some step width. After having explored the search space, it will return the best (i.e least) ecountered value.

import numpy as np
from qat.plugins import Junction
from qat.qpus import get_default_qpu
from qat.core import Result

class IterativeExploration(Junction):
    def __init__(self, nsteps=23):
        super(IterativeExploration, self).__init__()
        self.nsteps = nsteps

    def run(self, initial_job, meta_data):
        job = initial_job
        variable = job.get_variables().pop()
        angles = np.linspace(0, 2 * np.pi, self.nsteps)
        all_values = []
        for val in angles:
            current_job = job(**{variable: val})
            result = self.execute(current_job)
            all_values.append(result.value)
        min_val = min(all_values)
        best_index = all_values.index(min_val)
        best_param = angles[best_index]
        return Result(value=min_val, meta_data={"best_param": best_param})


# Building a simple stack
qpu = get_default_qpu()
stack = IterativeExploration(50) | qpu


# and a simple job
from qat.core import Observable
from qat.lang.AQASM import Program, RY

prog = Program()
qbits = prog.qalloc(1)
prog.apply(RY(prog.new_var(float, r"\beta")), qbits)
job = prog.to_circ().to_job(observable=Observable.sigma_z(0, 1))

result = stack.submit(job)
print("Best value:", result.value, "for beta =", result.meta_data["best_param"])
Best value: -0.9979453927503363 for beta = 3.077478517802246

So how does it work? The run method is the entry point of our repeated procedure. This method will be called by the junction upon reception of a new abstract job from the higher part of the stack. It receives an incoming job and the associated meta data (in case you would like to offer some additional control to the user submitting the job). You can write anything you want inside this method. In addition, the junction interface gives you acces to another method: the execute method. This method can be seen as a submit method. It takes a qlm job and transmit it down to the rest of the stack and get back the result.

Here, on our example, we simply iteratively bind the value of the parameter (using the overloaded __call__ operator of the Job object), execute this job and store the result in a list.

Notice also that we need to return a proper QLM result object. This is so that the result can be, in turn, post processed by the upper part of the stack.

If you don’t want to bother with the (quite low) administrative burden of binding the variables and extracting the value attribute, the qat.plugins.Optimizer class provides a slightly simpler API that particularizes the junction API to fit to the one required by most variational optimizers (see the source code documentation for more precisions).

Connecting to a remote Plugin

Any Plugin can be started in server mode. Instead of compiling a batch locally, a client could connect to the remote plugin to compile the batch. This section explains the creation of a server and how to a client could connect to this server

Server mode

Any plugin has a method serve() used to start the plugin in server mode

from qat.plugins import ObservableSplitter

# Define a PORT and an IP
PORT = 1234
IP = "*"

# Create a plugin and start it in server mode
plugin = ObservableSplitter()
plugin.serve(PORT, IP)

Any plugin could be started in server mode.

Client mode

If a Plugin has been started in server mode, the class RemotePlugin can be used to connect to this remote plugin

Assuming a server is listening to the port 1234 and the IP of this server is 127.0.0.1, the following code can be used to connect to the server:

from qat.core.plugins import RemotePlugin

# Define PORT and IP
PORT = 1234
IP = "127.0.0.1"

# Connect to the server
plugin = RemotePlugin(PORT, IP)

The plugin object is a plugin, this object could be piped to extra plugins and QPUs. The connection is synchronous, therefore, if the client is disconnected during the compilation or during the post processing step, results of the computation are lost.

Connecting to a remote QPU

myQLM can be used as client of a connection. Instead of executing a circuit on a computer, myQLM can send circuits to a remote QPU. Moreover, myQLM can be used to define a custom QPU that can be launched in server mode. This section explains the creation of a server and also how to connect to a remote QPU.

Server mode

Any QPU has a method serve() to start this QPU in server mode. This method takes the PORT and the IP as arguments. For instance:

from qat.pylinalg import PyLinalg

# Define a PORT and a IP
PORT = 1234
IP = "*"

# Define a QPU
qpu = PyLinalg()
qpu.serve(PORT, IP)

This code should work for any QPU.

Client mode

If a distant QPU is started in server mode, myQLM can be used as client of a connection.

Assuming the server is listening to the port 1234 and the ip of the server is 127.0.0.1, the following code can be used to connect to the server:

from qat.core.qpu import RemoteQPU

# Define PORT and IP
PORT = 1234
IP = "127.0.0.1"

# Define a client
qpu = RemoteQPU(PORT, IP)

The qpu object works like a QPU so plugins could be linked to it. Each batch passed to this client are sent to the server, the results are then retrieved. The connection is synchronous, therefore, if the client is disconnected during the execution of a Batch/Job, results of the execution are lost.