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.
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.var(qml.PauliZ(1))
Creating a QNodeCollection,
>>> qnodes = qml.QNodeCollection([qnode1, qnode2])
We can evaluate this QNode collection directly:
>>> qnodes(0.5643, -0.45) array([0.76084465, 1. ])
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:
>>> shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=4, n_wires=4) >>> params = np.random.random(shape) >>> 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)
Attributes
automatic differentiation interface used by the collection, if any
-
interface
¶ automatic differentiation interface used by the collection, if any
- Type
str, None
Methods
__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.
-
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
,jnp.stack
, andnp.stack
.- 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.