cunqa.mappers

Contains map-like callables to distribute circuits among vQPUS.

Variational Quantum Algorithms [1] require numerous executions of parametric cirucits, where in each step of the optimization process new parameters are assigned to them. This implies that, after parameters are updated, a new circuit must be created, transpiled and then sent to the quantum processor or simulator. For simplifying this process, we have QJobMapper and QPUCircuitMapper classes. Both classes are conceived to be used with Scipy optimizers [2] as the workers argument in global optimizers.

  • QJobMapper takes a list of existing QJob objects, then, the class can be called passing a set of parameters and a cost function. This callable updates each existing QJob object with such parameters through the upgrade_parameters method. Then, it gathers the results of the executions and returns the value of the cost function for each.

  • QPUCircuitMapper is instanciated with a circuit and instructions for its execution, together with a list of the QPU objects. The difference with QJobMapper is that here the method execute is mapped to each QPU, passing it the circuit with the given parameters assigned so that for this case several QJob objects are created.

Examples utilizing both classes can be found in the Examples gallery. These examples focus on optimization of VQAs, using a global optimizer called Differential Evolution [3].

References:

class QJobMapper(qjobs)

Class to map the method upgrade_parameters to a set of jobs sent to virtual QPUs.

The core of the class is on its __call__ method, to which parameters that the method upgrade_parameters takes are passed together with a cost function, so that a the value for this cost for each initial QJob is returned.

An example is shown below, once we have a list of QJob objects as qjobs:

>>> mapper = QJobMapper(qjobs)
>>>
>>> # defining the parameters set accordingly to the number of parameters
>>> # of the circuit and the number of QJobs in the list.
>>> new_parameters = [...]
>>>
>>> # defining the cost function passed to the result of each QJob
>>> def cost_function(result):
>>>     counts = result.counts
>>>     ...
>>>     return cost_value
>>>
>>> cost_values = mapper(cost_function, new_parameters)

We intuitively see how convenient this class can be for optimization algorithms: one has a parametric circuit to which updated sets of parameters can be sent, getting back the value of the cost function. Examples applied to optimizations are shown at the Examples gallery.

Parameters:

qjobs (list[QJob])

qjobs

list of objects to be mapped.

Type:

QJob

__call__(func, population)

Callable method to map the function func to the results of assigning population to the given jobs. Regarding the population, each set of parameters will be assigned to each QJob object, so the list must have size (N,p), being N the lenght of qjobs and p the number of parameters in the circuit. Mainly, this is thought for the function to take a Result object and to return a value. For example, the function can evaluate the expected value of an observable from the output of the circuit.

Parameters:
  • func (callable) – function to be passed to the results of the jobs.

  • population (list[list[int | float] | np.array[int | float]]) – list of numpy vectors to

  • jobs. (be mapped to the)

Returns:

List of outputs of the function applied to the results of each job for the given population.

class QPUCircuitMapper(qpus, circuit, **run_parameters)

Class to map the method execute to a list of QPUs.

The class is initialized with a list of QPU objects associated to the virtual QPUs that the optimization will require, together with the circuit and the simulation instructions needed for its execution.

Then, its __call__ method takes a set of parameters as population to assign to the circuit. Each assembled circuit is sent to each virtual QPU with the instructions provided on the instatiation of the mapper. The method returns the value for the provided function func for the result of each simulation.

Its use is pretty similar to QJobMapper, though creating QJob objects ahead is not needed.

>>> qpus = get_QPUs(...)
>>>
>>> # Creating the mapper with the pre-defined parametric circuit and other simulation instructions.
>>> mapper = QPUCircuitMapper(qpus, circuit, shots = 1000, ...)
>>>
>>> # Defining the parameters set according to the number of parameters
>>> # of the circuit and the number of QJobs in the list.
>>> new_parameters = [...]
>>>
>>> # Defining the cost function passed to the result of each QJob
>>> def cost_function(result):
>>>     counts = result.counts
>>>     ...
>>>     return cost_value
>>>
>>> cost_values = mapper(cost_function, new_parameters)

For each call of the mapper, circuits are assembled, jobs are sent, results are gathered and cost values are calculated. Its implementation for optimization problems is shown at the Examples gallery.

Parameters:
  • qpus (list[QPU])

  • circuit (QuantumCircuit)

  • run_parameters (Any | None)

qpus

Objects linked to the virtual QPUs to wich the circuit is mapped.

Type:

list[QPU]

circuit

Circuit to which parameters are assigned at the QPUCircuitMapper.__call__ method.

Type:

QuantumCircuit

run_parameters

Any other run instructions needed for the simulation.

Type:

Optional[Any]

__call__(func, population)

Callable method to map the function func to the results of the circuits sent to the given QPUs after assigning them population. Regarding the population, each set of parameters will be assigned to each circuit, so the list must have size (N,p), being N the lenght of qpus and p the number of parameters in the circuit. Mainly, this is thought for the function to take a Result object and to return a value. For example, the function can evaluate the expected value of an observable from the output of the circuit.

Parameters:
  • func (func) – function to be mapped to the QPUs. It must take as argument a Result instance.

  • params (list[list[float | int]]) – population of vectors to be mapped to the circuits sent to the QPUs.

Returns:

List of the results of the function applied to the output of the circuits sent to the QPUs.