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 someHardwareSpecs
and return a newBatch
.post_process for the way out (i.e from the QPU to the user). This method will process a
BatchResult
and return either aBatchResult
or a newBatch
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:

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 QPUs and plugins. You will find below all the details to define and run your own QPUs and plugins.
Writing your own QPU
The example below shows how to construct your own QPU. The QPU below is very rudimentary: it merely displays the gates contained in the circuit and then returns random uniform states as an output.
The QPU merely has to inherit from the QPUHandler
class and implement a submit_job
method that takes a Job
as an input and returns a Result
:
import numpy as np
from qat.core.qpu import QPUHandler
from qat.core import Result
from qat.core.wrappers.result import aggregate_data
class MyQPU(QPUHandler):
def submit_job(self, job):
circuit = job.circuit
# printing the circuit gates
for gate_name, params, qubits in circuit.iterate_simple():
print("%s gate on %s %s"%(gate_name, qubits, "(%s)"%params if len(params)>0 else ""))
# now outputting random states (that do not have anything to do with the circuit!!)
nqbits = job.circuit.nbqbits
result = Result()
for _ in range(job.nbshots):
state = np.random.randint(0, 2**nqbits)
result.add_sample(state=state)
if job.aggregate_data:
result = aggregate_data(result)
return result
Let us execute a quantum circuit on this QPU:
from qat.lang.AQASM import Program, H, CNOT, RX
prog = Program()
reg = prog.qalloc(3)
H(reg[0])
CNOT(reg[0], reg[1])
RX(np.pi/2)(reg[0])
CNOT(reg[1], reg[2])
circ = prog.to_circ()
job = circ.to_job(nbshots=5, aggregate_data=False)
qpu = MyQPU()
res = qpu.submit(job)
for sample in res:
print(sample.state)
H gate on [0]
CNOT gate on [0, 1]
RX gate on [0] ([1.5707963267948966])
CNOT gate on [1, 2]
|000>
|001>
|011>
|111>
|010>
We can also “aggregate” the samples, i.e compute the histograms of the returned states:
job = circ.to_job(nbshots=500, aggregate_data=True)
res = qpu.submit(job)
for sample in res:
print(sample.state, sample.probability)
H gate on [0]
CNOT gate on [0, 1]
RX gate on [0] ([1.5707963267948966])
CNOT gate on [1, 2]
|011>
|100>
|111>
|110>
|000>
H gate on [0]
CNOT gate on [0, 1]
RX gate on [0] ([1.5707963267948966])
CNOT gate on [1, 2]
|111> 0.13
|001> 0.11
|100> 0.144
|110> 0.136
|000> 0.11
|011> 0.128
|101> 0.13
|010> 0.112
You can check that the observed frequency of each state should close in on \(1/n_\mathrm{qbits}\) as the number of shots increases.
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 method 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):
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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 0x150526cf3cd0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150526cfd100>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd220>, <qat.lang.AQASM.bits.Qbit object at 0x150526cfd340>])])
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):
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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])]), 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 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x150431c8de20>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c930d0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c931f0>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93310>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None, _parameter_map=None, _values=None, values_data=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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])]), 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 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x150431c93af0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c93d60>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93e80>, <qat.lang.AQASM.bits.Qbit object at 0x150431c93fa0>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None, _parameter_map=None, _values=None, values_data=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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])]), 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 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x150431c967c0>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x150431c96a30>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96b50>, <qat.lang.AQASM.bits.Qbit object at 0x150431c96c70>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None, _parameter_map=None, _values=None, values_data=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):
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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])]), 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 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x147dded0bf70>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10220>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10340>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10460>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None, _parameter_map=None, _values=None, values_data=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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])]), 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 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x147dded10c40>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded10eb0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded10fd0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0f130>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None, _parameter_map=None, _values=None, values_data=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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])]), 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 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])])], qregs=[QRegister(scope=<qat.lang.AQASM.program.Program object at 0x147dded0f910>, length=3, start=0, msb=None, _subtype_metadata=None, qbits=[<qat.lang.AQASM.bits.Qbit object at 0x147dded0fb80>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fca0>, <qat.lang.AQASM.bits.Qbit object at 0x147dded0fdc0>])], error=None, value_data=None, error_data=None, meta_data={}, in_memory=None, _parameter_map=None, _values=None, values_data=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)
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.