[docs]
class NetworkNode:
"""A quantum network node.
:param num_in: The number of discrete classical inputs that the node accepts.
:type num_in: int
:param num_out: The number of classical outputs for the node.
:type num_out: int
:param wires: The wires on which the node operates.
:type wires: list[int]
:param cc_wires_in: The classical communication wires input to the node.
:type cc_wires_in: list[int]
:param cc_wires_out: The classical communication wires to output measurement results on.
:type cc_wires_out: list[int]
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`_
called either as ``circuit(settings, wires)``, or as ``circuit(settings, wires, cc_wires)`` where
``settings`` is an *array[float]* of length ``num_settings`` and ``cc_wires`` contains the measurement results
received from upstream nodes.
:type ansatz_fn: function
:param num_settings: The number of settings parameterizing the ``ansatz_fn`` circuit.
:type num_settings: int
:returns: An instance of the ``NetworkNode`` class.
**Calling a Network Node's Ansatz Function:**
Given an instantiated network node, ``node = NetworkNode(*args)``, its ansatz quantum circuit functtion
can be called as ``node(settings, cc_wires)`` or, if ``node.cc_wires_in==[]``, the node can be called
as ``node(settings)`` provided that ``cc_wires=[]``.
**Attributes:**
All inputs are stored as class attributes under the same name, *e.g.*, the ``num_in`` argument
is stored as ``node.num_in``.
"""
def __init__(
self,
num_in=1,
num_out=1,
wires=[],
cc_wires_in=[],
cc_wires_out=[],
ansatz_fn=None,
num_settings=0,
):
self.num_in = num_in
self.num_out = num_out
self.wires = wires
self.cc_wires_in = cc_wires_in
self.cc_wires_out = cc_wires_out
self.ansatz_fn = ansatz_fn if ansatz_fn else self.default_ansatz_fn
self.num_settings = num_settings
def __call__(self, settings=[], cc_wires=[]):
args = [settings, self.wires]
if cc_wires:
args += [cc_wires]
return self.ansatz_fn(*args)
def default_ansatz_fn(self, settings, wires, cc_wires=[]):
pass
[docs]
class NoiseNode(NetworkNode):
"""A network node that applies noise to its local qubit wires.
:param wires: The wires on which the node operates.
:type wires: list[int
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`
that takes the following form:
.. code-block:: python
def noise_ansatz(settings, wires):
# apply noise operation
qml.Depolarizing(0.5, wires=wires[0])
qml.AmplitudeDamping(0.5, wires=wires[1])
where ``settings=[]`` is unused because noise is considered to be static.
:type ansatz_fn: function
We model noise to be independent from classical inputs and upstream measurement results.
Therefore, only the ``wires`` and ``ansatz_fn`` attributes can be set.
All attributes are inherited from the :class:`qnetvo.NetworkNode` class.
:returns: An instantiated ``NoiseNode`` class.
"""
def __init__(self, wires=[], ansatz_fn=None):
super().__init__(wires=wires, ansatz_fn=ansatz_fn)
[docs]
class ProcessingNode(NetworkNode):
"""A network node that operates upon its local qubit wires where the operation can
be conditioned upon a classical input or upstream measurement results.
:param num_in: The number of discrete classical inputs that the node accepts.
:type num_in: int
:param wires: The wires on which the node operates.
:type wires: list[int]
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`_
that takes the following form:
.. code-block:: python
def processing_ansatz(settings, wires):
# apply processing operation
qml.ArbitraryUnitary(settings[0:16], wires=wires[0:2])
where ``settings`` is an *array[float]* of length ``num_settings``.
:type ansatz_fn: function
:param num_settings: The number of settings parameterizing the ``ansatz_fn`` circuit.
:type num_settings: int
All attributes are inherited from the :class:`qnetvo.NetworkNode` class.
:returns: An instantiated ``ProcessingNode`` class.
"""
def __init__(self, num_in=1, wires=[], ansatz_fn=None, num_settings=0):
super().__init__(
num_in=num_in,
wires=wires,
ansatz_fn=ansatz_fn,
num_settings=num_settings,
)
[docs]
class PrepareNode(ProcessingNode):
"""A network node that initializes a state on its local qubit wires where the
preparation can be conditioned on a classical input or upstream measurement results.
:param num_in: The number of discrete classical inputs that the node accepts.
:type num_in: int
:param wires: The wires on which the node operates.
:type wires: list[int]
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`_
that takes the following form:
.. code-block:: python
def prepare_ansatz(settings, wires):
# initalize quantum state from |0...0>
qml.ArbitraryStatePreparation(settings[0:6], wires=wires[0:2])
where ``settings`` is an *array[float]* of length ``num_settings``.
:type ansatz_fn: function
:param num_settings: The number of settings parameterizing the ``ansatz_fn`` circuit.
:type num_settings: int
All attributes are inherited from the :class:`qnetvo.NetworkNode` class.
:returns: An instantiated ``PrepareNode`` class.
"""
pass
[docs]
class MeasureNode(NetworkNode):
"""A network node that measures its local qubit wires where the measurement can be
conditioned on a classical input or upstream measurement results.
:param num_in: The number of discrete classical inputs that the node accepts.
:type num_in: int
:param num_out: The number of classical outputs for the node.
:type num_out: int
:param wires: The wires on which the node operates.
:type wires: list[int]
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`_
that takes the following form:
.. code-block:: python
def measure_ansatz(settings, wires):
# rotate measurement basis
qml.Rot(*settings[0:3], wires=wires[0])
where ``settings`` is an *array[float]* of length ``num_settings``.
Note that the measurement ansatz does not apply a measurement operation.
Measurement operations are specified later when :class:`qnetvo.NetworkAnsatz`
class is used to construct qnodes and cost functions.
:type ansatz_fn: function
:param num_settings: The number of settings parameterizing the ``ansatz_fn`` circuit.
:type num_settings: int
All attributes are inherited from the :class:`qnetvo.NetworkNode` class.
In addition, the number of classical outputs are specified.
:returns: An instantiated ``MeasureNode`` class.
"""
def __init__(self, num_in=1, num_out=1, wires=[], ansatz_fn=None, num_settings=0):
super().__init__(
num_in=num_in,
num_out=num_out,
wires=wires,
ansatz_fn=ansatz_fn,
num_settings=num_settings,
)
[docs]
class CCSenderNode(NetworkNode):
"""A network node that measures one or more of its local qubits and sends the measurement
result(s) to one or more downstream :class:`qnetvo.CCReceiverNode` instances.
:param num_in: The number of discrete classical inputs that the node accepts.
:type num_in: int
:param wires: The wires on which the node operates.
:type wires: list[int]
:param cc_wires_out: The classical communication wires to output measurement results on.
:type cc_wires_out: list[int]
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`_
that takes the following form:
.. code-block:: python
def cc_sender_ansatz(settings, wires):
# apply quantum circuit operations
qml.Rot(*settings[0:3], wires=wires[0])
# measure qubit to obtain classical communication bit
cc_bit_out = qml.measure(wires[0])
# output list of measurement results
return [cc_bit_out]
where ``settings`` is an *array[float]* of length ``num_settings``.
Note that for each wire specified in ``cc_wires_out``, there should be a corresponding
``cc_bit_out`` result obtained using `qml.measure`_.
:type ansatz_fn: function
:param num_settings: The number of settings parameterizing the ``ansatz_fn`` circuit.
:type num_settings: int
All attributes are inherited from the :class:`qnetvo.NetworkNode` class.
.. _qml.measure: https://docs.pennylane.ai/en/stable/code/api/pennylane.measure.html
:returns: An instantiated ``CCSenderNode`` class.
"""
def __init__(self, num_in=1, wires=[], cc_wires_out=[], ansatz_fn=None, num_settings=0):
super().__init__(
num_in=num_in,
wires=wires,
ansatz_fn=ansatz_fn,
num_settings=num_settings,
cc_wires_out=cc_wires_out,
)
[docs]
class CCReceiverNode(NetworkNode):
"""A network node that receives classical communication from an upstream :class:`qnetvo.CCSenderNode`.
:param num_in: The number of discrete classical inputs that the node accepts.
:type num_in: int
:param wires: The wires on which the node operates.
:type wires: list[int]
:param cc_wires_in: The classical communication wires input to the node.
:type cc_wires_in: list[int]
:param ansatz_fn: A `PennyLane quantum circuit function <https://docs.pennylane.ai/en/stable/introduction/circuits.html>`_
that takes the following form:
.. code-block:: python
def cc_receive_ansatz(settings, wires, cc_wires):
# apply quantum operations conditioned on classical communication
qml.cond(cc_wires[0], qml.Rot)(*settings[0:3], wires=wires[0])
qml.cond(cc_wires[1], qml.Rot)(*settings[3:6], wires=wires[0])
where ``settings`` is an *array[float]* of length ``num_settings`` and ``cc_wires`` contains
the measurement results received from upstream nodes.
:type ansatz_fn: function
:param num_settings: The number of settings parameterizing the ``ansatz_fn`` circuit.
:type num_settings: int
All attributes are inherited from the :class:`qnetvo.NetworkNode` class.
Note that the classical inputs specified by ``num_in`` are distinct from the classical
communication inputs passed through ``cc_wires``.
That is, the classical inputs ``num_in`` are known before the network simulation is run
whereas the classical communication in ``cc_wires`` are determined during the simulation.
:returns: An instantiated ``CCReceiverNode`` class.
"""
def __init__(
self,
num_in=1,
wires=[],
cc_wires_in=[],
ansatz_fn=None,
num_settings=0,
):
super().__init__(
num_in=num_in,
wires=wires,
ansatz_fn=ansatz_fn,
num_settings=num_settings,
cc_wires_in=cc_wires_in,
)