# Source code for pennylane.ops.qubit.arithmetic_ops

# Copyright 2018-2021 Xanadu Quantum Technologies Inc.

# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# Unless required by applicable law or agreed to in writing, software
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
"""
This submodule contains the discrete-variable quantum operations that perform
arithmetic operations on their input states.
"""
# pylint:disable=abstract-method,arguments-differ,protected-access
import numpy as np

import pennylane as qml
from pennylane.operation import Operation

[docs]class QubitCarry(Operation):
r"""QubitCarry(wires)
Apply the QubitCarry operation to four input wires.

This operation performs the transformation:

.. math::
|a\rangle |b\rangle |c\rangle |d\rangle \rightarrow |a\rangle |b\rangle |b\oplus c\rangle |bc \oplus d\oplus (b\oplus c)a\rangle

.. figure:: ../../_static/ops/QubitCarry.svg
:align: center
:width: 60%
:target: javascript:void(0);

See here <https://arxiv.org/abs/quant-ph/0008033v1>__ for more information.

.. note::
The first wire should be used to input a carry bit from previous operations. The final wire
holds the carry bit of this operation and the input state on this wire should be
:math:|0\rangle.

**Details:**

* Number of wires: 4
* Number of parameters: 0

Args:
wires (Sequence[int]): the wires the operation acts on

**Example**

The QubitCarry operation maps the state :math:|0110\rangle to :math:|0101\rangle, where
the last qubit denotes the carry value:

.. code-block::

input_bitstring = (0, 1, 1, 0)

@qml.qnode(dev)
def circuit(basis_state):
qml.BasisState(basis_state, wires=[0, 1, 2, 3])
qml.QubitCarry(wires=[0, 1, 2, 3])
return qml.probs(wires=[0, 1, 2, 3])

probs =  circuit(input_bitstring)
probs_indx = np.argwhere(probs == 1).flatten()[0]
bitstrings = list(itertools.product(range(2), repeat=4))
output_bitstring = bitstrings[probs_indx]

The output bitstring is

>>> output_bitstring
(0, 1, 0, 1)

The action of QubitCarry is to add wires 1 and 2. The modulo-two result is output
in wire 2 with a carry value output in wire 3. In this case, :math:1 \oplus 1 = 0 with
a carry, so we have:

>>> bc_sum = output_bitstring[2]
>>> bc_sum
0
>>> carry = output_bitstring[3]
>>> carry
1
"""
num_params = 0
num_wires = 4
par_domain = None
_mat = np.array(
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
]
)

@classmethod
def _matrix(cls, *params):
return QubitCarry._mat

[docs]    @staticmethod
def decomposition(wires):
decomp_ops = [
qml.Toffoli(wires=wires[1:]),
qml.CNOT(wires=[wires[1], wires[2]]),
qml.Toffoli(wires=[wires[0], wires[2], wires[3]]),
]
return decomp_ops

[docs]class QubitSum(Operation):
r"""QubitSum(wires)
Apply a QubitSum operation on three input wires.

This operation performs the transformation:

.. math::
|a\rangle |b\rangle |c\rangle \rightarrow |a\rangle |b\rangle |a\oplus b\oplus c\rangle

.. figure:: ../../_static/ops/QubitSum.svg
:align: center
:width: 40%
:target: javascript:void(0);

See here <https://arxiv.org/abs/quant-ph/0008033v1>__ for more information.

**Details:**

* Number of wires: 3
* Number of parameters: 0

Args:
wires (Sequence[int]): the wires the operation acts on

**Example**

The QubitSum operation maps the state :math:|010\rangle to :math:|011\rangle, with the
final wire holding the modulo-two sum of the first two wires:

.. code-block::

input_bitstring = (0, 1, 0)

@qml.qnode(dev)
def circuit(basis_state):
qml.BasisState(basis_state, wires = [0, 1, 2])
qml.QubitSum(wires=[0, 1, 2])
return qml.probs(wires=[0, 1, 2])

probs = circuit(input_bitstring)
probs_indx = np.argwhere(probs == 1).flatten()[0]
bitstrings = list(itertools.product(range(2), repeat=3))
output_bitstring = bitstrings[probs_indx]

The output bitstring is

>>> output_bitstring
(0, 1, 1)

The action of QubitSum is to add wires 0, 1, and 2. The modulo-two result is
output in wire 2. In this case, :math:0 \oplus 1 \oplus 0 = 1, so we have:

>>> abc_sum = output_bitstring[2]
>>> abc_sum
1
"""
num_params = 0
num_wires = 3
par_domain = None
_mat = np.array(
[
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 1],
]
)

[docs]    def label(self, decimals=None, base_label=None):
return super().label(decimals=decimals, base_label=base_label or "Σ")

@classmethod
def _matrix(cls, *params):
return QubitSum._mat

[docs]    @staticmethod
def decomposition(wires):
decomp_ops = [
qml.CNOT(wires=[wires[1], wires[2]]),
qml.CNOT(wires=[wires[0], wires[2]]),
]
return decomp_ops