qml.QNodeCollection

class QNodeCollection(qnodes=None)[source]

Bases: collections.abc.Sequence

Represents a sequence of independent QNodes that all share the same signature. When the collection is evaluated, all QNodes are simultaneously evaluated with the same parameters.

All QNodes within a QNodeCollection must use the same interface.

Note

the recommended method of creating a QNodeCollection is via map().

Parameters

qnodes (None or List[QNode]) – A list of QNodes sharing the same signature. If not provided, an empty QNode collection is instantiated.

See also

map(), apply(), sum(), dot()

Example:

A QNodeCollection can be created using a list of existing QNodes:

>>> qnode = qml.QNodeCollection([qnode1, qnode2])

Instantiating a QNode collection with no arguments creates an empty collection:

>>> qnodes = qml.QNodeCollection()
>>> len(qnodes)
0

QNodes can be appended:

>>> qnodes.append(qnode1)
>>> len(qnodes)
1

or extended:

>>> qnodes.extend([qnode2, qnode3])
>>> len(qnodes)
3

They can also be indexed:

>>> qnodes[0]
<QNode: device='default.qubit', func=circuit, wires=2, interface=torch>

or looped over:

>>> [i.num_wires for i in qnodes]
[2, 2, 2]

To evaluate a QNodeCollection, simply call the collection, passing the parameters as required by the constituent QNode. For example, consider the following two QNodes with the same signature:

dev1 = qml.device("default.qubit", wires=1)
dev2 = qml.device("default.qubit", wires=2)

@qml.qnode(dev1)
def qnode1(x, y):
    qml.RX(x, wires=0)
    qml.RY(y, wires=0)
    return qml.expval(qml.PauliZ(0))

@qml.qnode(dev2)
def qnode2(x, y):
    qml.Hadamard(wires=0)
    qml.RX(x, wires=0)
    qml.RY(y, wires=1)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(0)), qml.var(qml.PauliZ(1))

Creating a QNodeCollection,

>>> qnodes = qml.QNodeCollection([qnode1, qnode2])

We can evaluate this QNode collection directly:

>>> qnodes(0.5643, -0.45)
[ 7.60844651e-01 -5.55111512e-17  1.00000000e+00]

where the results from each QNode have been flattened and concatenated into a single one-dimensional list.

Asynchronous evaluation

Warning

You will find the best speedups when using asynchronous mode when QNodes are to be evaluated on external hardware devices or external simulators. It is not advised at this point to use asynchronous mode with default.qubit .

Warning

Asynchronous evaluation is experimental — please report all bugs and issues to our GitHub page. It currently works with all interfaces, however backpropagation and gradient computation is limited to Autograd and PyTorch. Quantum gradients using TensorFlow in asynchronous mode is currently not supported.

By default, the QNodes within the QNodeCollection are executed sequentially.

However, experimental asynchronous support is now available using the Dask parallelism library. This can be activated by passing the parallel=True keyword argument when evaluating the QNodeCollection.

For example, let’s create the following two QVM simulation devices:

>>> qpu1 = qml.device("forest.qvm", device="Aspen-4-4Q-D")
>>> qpu2 = qml.device("forest.qvm", device="Aspen-7-4Q-B")

We can create a collection of QNodes with different observables by mapping an ansatz over these devices using map():

>>> obs_list = [qml.PauliX(0), qml.PauliZ(0) @ qml.PauliZ(1)]
>>> qnodes = qml.map(qml.templates.StronglyEntanglingLayers, obs_list, [qpu1, qpu2])

We can now create some parameters and evaluate the collection:

>>> params = qml.init.strong_ent_layers_normal(n_layers=4, n_wires=4)
>>> qnodes(params)
array([0.046875  , 0.93164062])

The above collection was executed sequentially. Executing it in parallel:

>>> qnodes(params, parallel=True)
array([0.0234375 , 0.92578125])

We can time both approaches from within IPython or a Jupyter notebook:

>>> %timeit qnodes(params)
5.16 s ± 162 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
>>> %timeit qnodes(params, parallel=True)
2.99 s ± 40.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

interface

automatic differentiation interface used by the collection, if any

interface

automatic differentiation interface used by the collection, if any

Type

str, None

__call__(*args, **kwargs)

Call self as a function.

append(qnode)

Appends a QNode to the collection.

convert_results(results, interface)

Convert a list of results coming from multiple QNodes to the object required by each interface for auto-differentiation.

count(value)

evaluate(args, kwargs)

Evaluate all QNodes in the collection.

extend(qnodes)

Extends the collection by a list of QNodes.

index(value, [start, [stop]])

Raises ValueError if the value is not present.

__call__(*args, **kwargs)[source]

Call self as a function.

append(qnode)[source]

Appends a QNode to the collection. The appended QNode must have the same interface as the QNode collection.

static convert_results(results, interface)[source]

Convert a list of results coming from multiple QNodes to the object required by each interface for auto-differentiation.

Internally, this method makes use of tf.stack, torch.stack, and np.vstack.

Parameters
  • results (list) – list containing the results from multiple QNodes

  • interface (str) – the interfaces of the underlying QNodes

Returns

the converted and stacked results.

Return type

list or array or torch.Tensor or tf.Tensor

count(value) → integer -- return number of occurrences of value
evaluate(args, kwargs)[source]

Evaluate all QNodes in the collection.

Parameters
  • args (list) – list containing the arguments to pass to all internal QNodes

  • kwargs (dict) – dictionary containing the keyword arguments to pass to all internal QNodes

Returns

the results from each QNode

Return type

list

extend(qnodes)[source]

Extends the collection by a list of QNodes. The appended QNodes must have the same interface as the QNode collection.

index(value[, start[, stop]]) → integer -- return first index of value.

Raises ValueError if the value is not present.

Supporting start and stop arguments is optional, but recommended.

Contents

Using PennyLane

Development

API