Skip to content

Commit

Permalink
[Code Together] Adding SISWAP operation (#1563)
Browse files Browse the repository at this point in the history
* Initial work for SISWAP

* bug fixes

* some more bug fixes

* bug fixing

* bug fixes

* bug_fixes

* buug fixes

* added tests

* completed tests

* fixing tests

* format fix for tests/devices and fix in default_qubit.py

* fixing tests

* fixing gate_data.py matrix for SISWAP

* fixing styling in tests/gate_data.py

* Update pennylane/ops/__init__.py

Co-authored-by: antalszava <antalszava@gmail.com>

* added tests for sqisw to test_default_qubit.py and test_qubit_ops.py

* fixing tests for sisqw

* adding SQISW to doc/introduction/operations.rst

* Update pennylane/ops/qubit/non_parametric_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update pennylane/ops/qubit/non_parametric_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update tests/ops/test_qubit_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update pennylane/ops/qubit/non_parametric_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update tests/ops/test_qubit_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update tests/ops/test_qubit_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update tests/ops/test_qubit_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update tests/ops/test_qubit_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* Update tests/ops/test_qubit_ops.py

Co-authored-by: antalszava <antalszava@gmail.com>

* updating test_default_qubit.py

* styling fix for tests/ops/test_qunit_ops.py

* Update pennylane/ops/qubit/non_parametric_ops.py

* Update .github/CHANGELOG.md

Co-authored-by: antalszava <antalszava@gmail.com>
  • Loading branch information
charmerDark and antalszava committed Aug 23, 2021
1 parent 566e104 commit 068439f
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 3 deletions.
5 changes: 4 additions & 1 deletion .github/CHANGELOG.md
Expand Up @@ -2,6 +2,9 @@

<h3>New features since last release</h3>

* Added a new `SISWAP` operation and a `SQISW` alias with support to the `default_qubit` device.
[#1563](https://github.com/PennyLaneAI/pennylane/pull/1563)

* The `RotosolveOptimizer` now can tackle general parametrized circuits, and is no longer
restricted to single-qubit Pauli rotations.
[(#1489)](https://github.com/PennyLaneAI/pennylane/pull/1489)
Expand Down Expand Up @@ -262,7 +265,7 @@ and requirements-ci.txt (unpinned). This latter would be used by the CI.
This release contains contributions from (in alphabetical order):


Akash Narayanan B, Thomas Bromley, Tanya Garg, Josh Izaac, Prateek Jain, Johannes Jakob Meyer, Maria Schuld,
Vishnu Ajith, Akash Narayanan B, Thomas Bromley, Tanya Garg, Josh Izaac, Prateek Jain, Johannes Jakob Meyer, Maria Schuld,
Ingrid Strandberg, David Wierichs, Vincent Wong.


Expand Down
2 changes: 2 additions & 0 deletions doc/introduction/operations.rst
Expand Up @@ -73,6 +73,8 @@ Qubit gates
~pennylane.CY
~pennylane.SWAP
~pennylane.ISWAP
~pennylane.SISWAP
~pennylane.SQISW
~pennylane.IsingXX
~pennylane.IsingYY
~pennylane.IsingZZ
Expand Down
2 changes: 2 additions & 0 deletions pennylane/devices/default_qubit.py
Expand Up @@ -108,6 +108,8 @@ class DefaultQubit(QubitDevice):
"CNOT",
"SWAP",
"ISWAP",
"SISWAP",
"SQISW",
"CSWAP",
"Toffoli",
"CY",
Expand Down
2 changes: 2 additions & 0 deletions pennylane/ops/qubit/__init__.py
Expand Up @@ -52,6 +52,8 @@
"CY",
"SWAP",
"ISWAP",
"SISWAP",
"SQISW",
"CSWAP",
"Toffoli",
"RX",
Expand Down
63 changes: 63 additions & 0 deletions pennylane/ops/qubit/non_parametric_ops.py
Expand Up @@ -646,6 +646,69 @@ def adjoint(self):
return ISWAP(wires=self.wires).inv()


class SISWAP(Operation):
r"""SISWAP(wires)
The square root of i-swap operator. Can also be accessed as ``qml.SQISW``
.. math:: SISWAP = \begin{bmatrix}
1 & 0 & 0 & 0 \\
0 & 1/ \sqrt{2} & i/\sqrt{2} & 0\\
0 & i/ \sqrt{2} & 1/ \sqrt{2} & 0\\
0 & 0 & 0 & 1
\end{bmatrix}.
**Details:**
* Number of wires: 2
* Number of parameters: 0
Args:
wires (Sequence[int]): the wires the operation acts on
"""
num_params = 0
num_wires = 2
par_domain = None

@classmethod
def _matrix(cls, *params):
return np.array(
[
[1, 0, 0, 0],
[0, INV_SQRT2, INV_SQRT2 * 1j, 0],
[0, INV_SQRT2 * 1j, INV_SQRT2, 0],
[0, 0, 0, 1],
]
)

@classmethod
def _eigvals(cls, *params):
return np.array([INV_SQRT2 * (1 + 1j), INV_SQRT2 * (1 - 1j), 1, 1])

@staticmethod
def decomposition(wires):
decomp_ops = [
SX(wires=wires[0]),
qml.RZ(np.pi / 2, wires=wires[0]),
CNOT(wires=[wires[0], wires[1]]),
SX(wires=wires[0]),
qml.RZ(7 * np.pi / 4, wires=wires[0]),
SX(wires=wires[0]),
qml.RZ(np.pi / 2, wires=wires[0]),
SX(wires=wires[1]),
qml.RZ(7 * np.pi / 4, wires=wires[1]),
CNOT(wires=[wires[0], wires[1]]),
SX(wires=wires[0]),
SX(wires=wires[1]),
]
return decomp_ops

def adjoint(self):
return SISWAP(wires=self.wires).inv()


SQISW = SISWAP


class CSWAP(Operation):
r"""CSWAP(wires)
The controlled-swap operator
Expand Down
59 changes: 57 additions & 2 deletions tests/devices/test_default_qubit.py
Expand Up @@ -224,7 +224,57 @@ def test_apply_operation_single_wire_no_parameters_inverse(
),
]

all_two_wires_no_parameters = test_data_two_wires_no_parameters + test_data_iswap
test_data_siswap = [
(qml.SISWAP, [1, 0, 0, 0], [1, 0, 0, 0]),
(qml.SISWAP, [0, 1, 0, 0], [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0]),
(
qml.SISWAP,
[1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0],
[1 / math.sqrt(2), 0.5, 0.5 * 1j, 0],
),
]

test_data_sqisw = [
(qml.SQISW, [1, 0, 0, 0], [1, 0, 0, 0]),
(qml.SQISW, [0, 1, 0, 0], [0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0]),
(
qml.SQISW,
[1 / math.sqrt(2), 1 / math.sqrt(2), 0, 0],
[1 / math.sqrt(2), 0.5, 0.5 * 1j, 0],
),
]

test_data_siswap_inv = [
(
qml.SISWAP,
[1 / math.sqrt(2), 0, 1 / math.sqrt(2), 0],
[1 / math.sqrt(2), -0.5 * 1j, 0.5, 0],
),
(qml.SISWAP, [0, 0, 1, 0], [0, -1 / math.sqrt(2) * 1j, 1 / math.sqrt(2), 0]),
(
qml.SISWAP,
[1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0],
[1 / math.sqrt(2), 0.5 * 1j, -0.5, 0],
),
]

test_data_sqisw_inv = [
(
qml.SQISW,
[1 / math.sqrt(2), 0, 1 / math.sqrt(2), 0],
[1 / math.sqrt(2), -0.5 * 1j, 0.5, 0],
),
(qml.SQISW, [0, 0, 1, 0], [0, -1 / math.sqrt(2) * 1j, 1 / math.sqrt(2), 0]),
(
qml.SQISW,
[1 / math.sqrt(2), 0, -1 / math.sqrt(2), 0],
[1 / math.sqrt(2), 0.5 * 1j, -0.5, 0],
),
]

all_two_wires_no_parameters = (
test_data_two_wires_no_parameters + test_data_iswap + test_data_siswap + test_data_sqisw
)

@pytest.mark.parametrize("operation,input,expected_output", all_two_wires_no_parameters)
def test_apply_operation_two_wires_no_parameters(
Expand All @@ -240,7 +290,12 @@ def test_apply_operation_two_wires_no_parameters(
qubit_device_2_wires._state.flatten(), np.array(expected_output), atol=tol, rtol=0
)

all_two_wires_no_parameters_inv = test_data_two_wires_no_parameters + test_data_iswap_inv
all_two_wires_no_parameters_inv = (
test_data_two_wires_no_parameters
+ test_data_iswap_inv
+ test_data_siswap_inv
+ test_data_sqisw_inv
)

@pytest.mark.parametrize("operation,input,expected_output", all_two_wires_no_parameters_inv)
def test_apply_operation_two_wires_no_parameters_inverse(
Expand Down
8 changes: 8 additions & 0 deletions tests/gate_data.py
Expand Up @@ -23,6 +23,14 @@
CNOT = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) #: CNOT gate
SWAP = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) #: SWAP gate
ISWAP = np.array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]) #: ISWAP gate
SISWAP = np.array(
[
[1, 0, 0, 0],
[0, 1 / math.sqrt(2), 1 / math.sqrt(2) * 1j, 0],
[0, 1 / math.sqrt(2) * 1j, 1 / math.sqrt(2), 0],
[0, 0, 0, 1],
]
)
CZ = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]]) #: CZ gate
S = np.array([[1, 0], [0, 1j]]) #: Phase Gate
T = np.array([[1, 0], [0, cmath.exp(1j * np.pi / 4)]]) #: T Gate
Expand Down
61 changes: 61 additions & 0 deletions tests/ops/test_qubit_ops.py
Expand Up @@ -39,6 +39,7 @@
CNOT,
SWAP,
ISWAP,
SISWAP,
CZ,
S,
T,
Expand Down Expand Up @@ -400,6 +401,7 @@ def circuit(basis_state):
(qml.CNOT, CNOT),
(qml.SWAP, SWAP),
(qml.ISWAP, ISWAP),
(qml.SISWAP, SISWAP),
(qml.CZ, CZ),
(qml.S, S),
(qml.T, T),
Expand All @@ -419,6 +421,8 @@ def circuit(basis_state):
qml.CY(wires=[0, 1]),
qml.SWAP(wires=[0, 1]),
qml.ISWAP(wires=[0, 1]),
qml.SISWAP(wires=[0, 1]),
qml.SQISW(wires=[0, 1]),
qml.CSWAP(wires=[0, 1, 2]),
qml.PauliRot(0.123, "Y", wires=0),
qml.IsingXX(0.123, wires=[0, 1]),
Expand Down Expand Up @@ -772,6 +776,55 @@ def test_ISWAP_decomposition(self, tol):

assert np.allclose(decomposed_matrix, op.matrix, atol=tol, rtol=0)

@pytest.mark.parametrize("siswap_op", [qml.SISWAP, qml.SQISW])
def test_SISWAP_decomposition(self, siswap_op, tol):
"""Tests that the decomposition of the SISWAP gate and its SQISW alias gate is correct"""
op = siswap_op(wires=[0, 1])
res = op.decomposition(op.wires)

assert len(res) == 12

assert res[0].wires == Wires([0])
assert res[1].wires == Wires([0])
assert res[2].wires == Wires([0, 1])
assert res[3].wires == Wires([0])
assert res[4].wires == Wires([0])
assert res[5].wires == Wires([0])
assert res[6].wires == Wires([0])
assert res[7].wires == Wires([1])
assert res[8].wires == Wires([1])
assert res[9].wires == Wires([0, 1])
assert res[10].wires == Wires([0])
assert res[11].wires == Wires([1])

assert res[0].name == "SX"
assert res[1].name == "RZ"
assert res[2].name == "CNOT"
assert res[3].name == "SX"
assert res[4].name == "RZ"
assert res[5].name == "SX"
assert res[6].name == "RZ"
assert res[7].name == "SX"
assert res[8].name == "RZ"
assert res[9].name == "CNOT"
assert res[10].name == "SX"
assert res[11].name == "SX"

mats = []
for i in reversed(res):
if i.wires == Wires([1]):
mats.append(np.kron(np.eye(2), i.matrix))
elif i.wires == Wires([0]):
mats.append(np.kron(i.matrix, np.eye(2)))
elif i.wires == Wires([1, 0]) and i.name == "CNOT":
mats.append(np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]))
else:
mats.append(i.matrix)

decomposed_matrix = np.linalg.multi_dot(mats)

assert np.allclose(decomposed_matrix, op.matrix, atol=tol, rtol=0)

def test_isingxx_decomposition(self, tol):
"""Tests that the decomposition of the IsingXX gate is correct"""
param = 0.1234
Expand Down Expand Up @@ -1536,6 +1589,14 @@ def test_iswap_eigenval(self):
res = op.eigvals
assert np.allclose(res, exp)

@pytest.mark.parametrize("siswap_op", [qml.SISWAP, qml.SQISW])
def test_siswap_eigenval(self, siswap_op):
"""Tests that the ISWAP eigenvalue matches the numpy eigenvalues of the ISWAP matrix"""
op = siswap_op(wires=[0, 1])
exp = np.linalg.eigvals(op.matrix)
res = op.eigvals
assert np.allclose(res, exp)

def test_swap_decomposition(self):
"""Tests the swap operator produces the correct output"""
opr = qml.SWAP(wires=[0, 1])
Expand Down

0 comments on commit 068439f

Please sign in to comment.