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 QJob object is created as the output of the run method. 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.result which 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 Result object 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 QClient has recieved from the corresponding server the outcome of the job. The result is not sent from the server to the QClient until 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 method QJob.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 QJob class 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 the upgrade_parameters method.

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_parameters method 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 QJob objects.

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.

Parameters:

qjobs (list[QJob]) – list of objects to get the result from.

Returns:

List of Result objects.

Return type:

list[Result]