cunqa.qjob
Contains objects that define and manage quantum emulation jobs. The core of this module is the
class QJob. These objects are created when a quantum job is sent to a
vQPU, as a return of the run function:
>>> run(circuit, qpu)
<cunqa.qjob.QJob object at XXXXXXXX>
In this method, after the QJob instance is created, the circuit is
submitted for simulation at the vQPU. QJob is the bridge between sending a
circuit with instructions and receiving the results.
Another functionality described in the submodule is the function gather,
which receives a list of QJob objects and returns their results as
Result objects.
>>> qjob_1 = run(circuit_1, qpu_1)
>>> qjob_2 = run(circuit_2, qpu_2)
>>> gather([qjob_1, qjob_2])
[<cunqa.result.Result object at XXXXXXXX>, <cunqa.result.Result object at XXXXXXXX>]
- class QJob(qclient, device, circuit_ir, **run_parameters)
Class to handle jobs sent to vQPUs. A
QJobobject is created as the output of therunmethod. The quantum job not only contains the circuit to be simulated, but also simulation instructions and information of the vQPU to which the job is sent. This class has a main attribute:QJob.resultwhich stores the information about the execution.- Parameters:
qclient (cunqa.qclient.QClient | QMIOClient)
device (dict)
circuit_ir (dict)
run_parameters (Any)
- result
Result of the job. If no error occured during simulation, a
Resultobject is retured.>>> qjob = qpu.run(circuit) >>> result = qjob.result >>> print(result.counts) {'00': 524, '11': 500}
Note
Since to obtain the result the simulation has to be finished, this method is a blocking call, which means that the program will be blocked until the
QClienthas recieved from the corresponding server the outcome of the job. The result is not sent from the server to theQClientuntil this method is called.Warning
Because of how the client-server comunication is built, the user must be careful and call for the results in the same order in which the jobs where submited. If the order is not respected, no errors would be raised but results will not correspond to the job - a mix up would happen. This is because the server follows the FIFO rule (First in first out): if we want to receive the second result, the first one has to be out.
But first, in order to be able to call the attribute
QJob.result, it is necessary to submit the job for execution. To do so, we will use the methodQJob.submit.- submit(param_values=None)
Asynchronous method to submit a job to the corresponding
QClient.>>> qjob = QJob(qclient, circuit_ir, **run_parameters) >>> qjob.submit() # Already has all the info of where and what to send
In case the circuit is parametric it needs to be called with the value of its free parameters set with the
param_values.Note
Opposite to
result, this is a non-blocking call. Once a job is submitted, the python program continues without waiting while the corresponding server receives and simulates the circuit.- param_values (dict | list): either a list of ordered parameters to assign to the
parametrized circuit or a dictionary with keys being the free parameters’ names and its values being its corresponding new values.
- Parameters:
param_values (dict[Symbol, float | int] | list[float | int])
- Return type:
None
But the objective of the
QJobclass is not only to retrieve the result. It also allows an easy updating of the quantum task sent without the need of resend the whole circuit. This is really useful, especially working with variational quantum algorithms (VQAs) [1], which need to change the parameters of the gates in a circuit as they are optimized in each iteration. This parameter update is done using theupgrade_parametersmethod.- upgrade_parameters(param_values)
Method to upgrade the parameters in a previously submitted job of parametric circuit. First it checks weather the prior simulation’s result was retrieved. If not, it is discarded, and the new set of parameters is sent to the server to be reassigned to the circuit for simulation. This method can be used on a loop, carefully saving the intermediate results. Also, this method is used by the class
QJobMapper, check out its documentation for a extensive description.There are two ways of passing new parameters. First, as a list with the corresponding values in the order of the gates in the circuit, in which case missing parameters will result in an error. On the other hand, as a dict where the keys are the free parameters names and the values the corresponding new value to that free parameter. Not all parameters need to be updated, but they must have been given a value at least once, because its last value would be used.
Warning
Before sending the circuit or upgrading its parameters, the result of the prior job must be called. It can be done manually, so that we can save it and obtain its information, or it can be done automatically as in the example above, but be aware that once the
upgrade_parametersmethod is called, this result is discarded.- Parameters:
param_values (dict | list) – either a list of ordered parameters to assign to the parametrized circuit or a dictionary with keys being the free parameters’ names and its values being its corresponding new values.
- Return type:
None
References:
- gather(qjobs)
Function to get the results of several
QJobobjects.Once the jobs are running:
>>> results = gather(qjobs)
This is a blocking call, results will be called sequentialy in . Since they are being run simultaneously, even if the first one on the list takes the longest, when it finishes the rest would have been done, so just the small overhead from calling them will be added.
Warning
Since this is mainly a for loop, the order must be respected when submiting jobs to the same vQPU.