diff --git a/doc/_static/templates/state_preparations/cosine_window.png b/doc/_static/templates/state_preparations/cosine_window.png
new file mode 100644
index 00000000000..e127e8002eb
Binary files /dev/null and b/doc/_static/templates/state_preparations/cosine_window.png differ
diff --git a/doc/_static/templates/state_preparations/thumbnail_cosine_window.png b/doc/_static/templates/state_preparations/thumbnail_cosine_window.png
new file mode 100644
index 00000000000..159bd18160e
Binary files /dev/null and b/doc/_static/templates/state_preparations/thumbnail_cosine_window.png differ
diff --git a/doc/introduction/templates.rst b/doc/introduction/templates.rst
index f5519e9fd22..179b83c0ac0 100644
--- a/doc/introduction/templates.rst
+++ b/doc/introduction/templates.rst
@@ -114,6 +114,10 @@ state preparation is typically used as the first operation.
:description: :doc:`ArbitraryStatePreparation <../code/api/pennylane.ArbitraryStatePreparation>`
:figure: _static/templates/subroutines/arbitrarystateprep.png
+.. gallery-item::
+ :description: :doc:`CosineWindow <../code/api/pennylane.CosineWindow>`
+ :figure: _static/templates/state_preparations/thumbnail_cosine_window.png
+
.. raw:: html
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 172bac2cb71..919806fb84a 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -148,6 +148,54 @@
[(#4620)](https://github.com/PennyLaneAI/pennylane/pull/4620)
[(#4632)](https://github.com/PennyLaneAI/pennylane/pull/4632)
+ * The `CosineWindow` template has been added to prepare an initial state based on a cosine wave function.
+ [(#4683)](https://github.com/PennyLaneAI/pennylane/pull/4683)
+
+ ```python
+ import pennylane as qml
+ import matplotlib.pyplot as plt
+
+ dev = qml.device('default.qubit', wires=4)
+
+ @qml.qnode(dev)
+ def example_circuit():
+ qml.CosineWindow(wires=range(4))
+ return qml.state()
+ output = example_circuit()
+
+ # Graph showing state amplitudes
+ plt.bar(range(len(output)), output)
+ plt.show()
+ ```
+
+ We can show how this operator is built:
+
+ ```python
+ import pennylane as qml
+
+ dev = qml.device("default.qubit", wires=5)
+
+ op = qml.CosineWindow(wires=range(5))
+
+ @qml.qnode(dev)
+ def circuit():
+ op.decomposition()
+ return qml.state()
+
+ print(qml.draw(circuit)())
+
+ ```
+
+ ```pycon
+
+ 0: ──────────────╭QFT†──Rϕ(1.57)─┤ State
+ 1: ──────────────├QFT†──Rϕ(0.79)─┤ State
+ 2: ──────────────├QFT†──Rϕ(0.39)─┤ State
+ 3: ──────────────├QFT†──Rϕ(0.20)─┤ State
+ 4: ──H──RZ(3.14)─╰QFT†──Rϕ(0.10)─┤ State
+
+ ```
+
* `qml.transforms.decomposition.sk_decomposition` method implements the Solovay-Kitaev algorithm for
approximately decomposing any single-qubit operation to Clifford+T basis.
[(#4687)](https://github.com/PennyLaneAI/pennylane/pull/4687)
@@ -495,6 +543,7 @@
This release contains contributions from (in alphabetical order):
+Guillermo Alonso,
Utkarsh Azad,
Jack Brown,
Stepan Fomichev,
diff --git a/pennylane/templates/state_preparations/__init__.py b/pennylane/templates/state_preparations/__init__.py
index dc00ab1c905..16d3e6b64f2 100644
--- a/pennylane/templates/state_preparations/__init__.py
+++ b/pennylane/templates/state_preparations/__init__.py
@@ -20,3 +20,4 @@
from .basis import BasisStatePreparation
from .arbitrary_state_preparation import ArbitraryStatePreparation
from .basis_qutrit import QutritBasisStatePreparation
+from .cosine_window import CosineWindow
diff --git a/pennylane/templates/state_preparations/cosine_window.py b/pennylane/templates/state_preparations/cosine_window.py
new file mode 100644
index 00000000000..179b6ece2fa
--- /dev/null
+++ b/pennylane/templates/state_preparations/cosine_window.py
@@ -0,0 +1,129 @@
+# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+r"""
+Contains the CosineWindow template.
+"""
+import numpy as np
+import pennylane as qml
+from pennylane.operation import StatePrepBase
+from pennylane import math
+from pennylane.wires import Wires, WireError
+
+
+class CosineWindow(StatePrepBase):
+ r"""CosineWindow(wires)
+ Prepares an initial state with a cosine wave function.
+
+ The wave function is defined below where :math:`m` is the number of wires.
+
+ .. math::
+
+ |\psi\rangle = \sqrt{2^{1-m}} \sum_{k=0}^{2^m-1} \cos(\frac{\pi k}{2^m} - \frac{\pi}{2}) |k\rangle,
+
+ .. figure:: ../../_static/templates/state_preparations/cosine_window.png
+ :align: center
+ :width: 65%
+ :target: javascript:void(0);
+
+ .. note::
+
+ The wave function is shifted by :math:`\frac{\pi}{2}` units so that the window is centered.
+
+ For more details see `Phys. Rev. D 106 (2022) `_.
+
+ .. seealso:: :class:`~.QuantumPhaseEstimation` and :class:`~.QFT`.
+
+ Args:
+ wires (Sequence[int] or int): the wire(s) the operation acts on
+
+ **Example**
+
+ >>> dev = qml.device('default.qubit', wires=2)
+ >>> @qml.qnode(dev)
+ ... def example_circuit():
+ ... qml.CosineWindow(wires=range(2))
+ ... return qml.probs()
+ >>> print(example_circuit())
+ [1.87469973e-33 2.50000000e-01 5.00000000e-01 2.50000000e-01]
+ """
+
+ @staticmethod
+ def compute_decomposition(wires): # pylint: disable=arguments-differ,unused-argument
+ r"""Representation of the operator as a product of other operators (static method).
+ It is efficiently decomposed from one QFT over all qubits and one-qubit rotation gates.
+
+ Args:
+ wires (Iterable, Wires): the wire(s) the operation acts on
+
+ Returns:
+ list[Operator]: decomposition into lower level operations
+ """
+
+ decomp_ops = []
+
+ decomp_ops.append(qml.Hadamard(wires=wires[-1]))
+ decomp_ops.append(qml.RZ(np.pi, wires=wires[-1]))
+ decomp_ops.append(qml.adjoint(qml.QFT)(wires=wires))
+
+ for ind, wire in enumerate(wires):
+ decomp_ops.append(qml.PhaseShift(np.pi * 2 ** (-ind - 1), wires=wire))
+
+ return decomp_ops
+
+ def label(self, decimals=None, base_label=None, cache=None):
+ return "CosineWindow"
+
+ def state_vector(self, wire_order=None): # pylint: disable=arguments-differ,unused-argument
+ r"""Calculation of the state vector generated by the cosine window.
+
+ Args:
+ wire_order (Iterable, Wires): Custom order of wires for the returned state vector.
+
+ Raises:
+ WireError: Custom wire_order must contain all wires.
+
+ Returns:
+ TensorLike[complex]: output state
+ """
+
+ num_op_wires = len(self.wires)
+ op_vector_shape = (2,) * num_op_wires
+ vector = np.array(
+ [
+ np.sqrt(2 ** (1 - num_op_wires))
+ * np.cos(-np.pi / 2 + np.pi * x / 2**num_op_wires)
+ for x in range(2**num_op_wires)
+ ]
+ )
+ op_vector = math.reshape(vector, op_vector_shape)
+
+ if wire_order is None or Wires(wire_order) == self.wires:
+ return op_vector
+
+ wire_order = Wires(wire_order)
+ if not wire_order.contains_wires(self.wires):
+ raise WireError(f"Custom wire_order must contain all {self.name} wires")
+
+ indices = tuple([Ellipsis] + [slice(None)] * num_op_wires)
+
+ ket_shape = [2] * num_op_wires
+ ket = np.zeros(ket_shape, dtype=np.complex128)
+ ket[indices] = op_vector
+
+ if self.wires != wire_order[:num_op_wires]:
+ current_order = self.wires + list(Wires.unique_wires([wire_order, self.wires]))
+ desired_order = [current_order.index(w) for w in wire_order]
+ ket = ket.transpose(desired_order)
+
+ return math.convert_like(ket, op_vector)
diff --git a/tests/templates/test_state_preparations/test_cosine_window.py b/tests/templates/test_state_preparations/test_cosine_window.py
new file mode 100644
index 00000000000..40858853fa6
--- /dev/null
+++ b/tests/templates/test_state_preparations/test_cosine_window.py
@@ -0,0 +1,119 @@
+# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Unit tests for the CosineWindow template.
+"""
+# pylint: disable=too-few-public-methods
+import pytest
+import numpy as np
+import pennylane as qml
+from pennylane.wires import WireError
+
+
+class TestDecomposition:
+ """Tests that the template defines the correct decomposition."""
+
+ def test_correct_gates_single_wire(self):
+ """Test that the correct gates are applied."""
+
+ op = qml.CosineWindow(wires=[0])
+ queue = op.expand().operations
+
+ assert queue[0].name == "Hadamard"
+ assert queue[1].name == "RZ"
+ assert queue[2].name == "Adjoint(QFT)"
+ assert queue[3].name == "PhaseShift"
+
+ assert np.isclose(queue[3].data[0], np.pi / 2)
+
+ def test_correct_gates_many_wires(self):
+ """Test that the correct gates are applied on two wires."""
+
+ op = qml.CosineWindow(wires=[0, 1, 2, 3, 4])
+ queue = op.decomposition()
+
+ assert queue[0].name == "Hadamard"
+ assert queue[1].name == "RZ"
+ assert queue[2].name == "Adjoint(QFT)"
+
+ for ind, q in enumerate(queue[3:]):
+ assert q.name == "PhaseShift"
+ assert np.isclose(q.data[0], np.pi / 2 ** (ind + 1))
+
+ def test_custom_wire_labels(self):
+ """Test that template can deal with non-numeric, nonconsecutive wire labels."""
+
+ dev = qml.device("default.qubit", wires=3)
+ dev2 = qml.device("default.qubit", wires=["z", "a", "k"])
+
+ @qml.qnode(dev)
+ def circuit():
+ qml.CosineWindow(wires=range(3))
+ return qml.expval(qml.Identity(0)), qml.state()
+
+ @qml.qnode(dev2)
+ def circuit2():
+ qml.CosineWindow(wires=["z", "a", "k"])
+ return qml.expval(qml.Identity("z")), qml.state()
+
+ res1, state1 = circuit()
+ res2, state2 = circuit2()
+
+ assert np.allclose(res1, res2)
+ assert np.allclose(state1, state2)
+
+
+class TestRepresentation:
+ """Test id and label."""
+
+ def test_id(self):
+ """Tests that the id attribute can be set."""
+ wires = [0, 1, 2]
+ template = qml.CosineWindow(wires=wires, id="a")
+ assert template.id == "a"
+ assert template.wires == qml.wires.Wires(wires)
+
+ def test_label(self):
+ """Test label method returns CosineWindow"""
+ op = qml.CosineWindow(wires=[0, 1])
+ assert op.label() == "CosineWindow"
+
+
+class TestStateVector:
+ """Test the state_vector() method of various CosineWindow operations."""
+
+ def test_CosineWindow_state_vector(self):
+ """Tests that the state vector is correct for a single wire."""
+ op = qml.CosineWindow(wires=[0])
+ res = op.state_vector()
+ expected = np.array([0.0, 1.0])
+ assert np.allclose(res, expected)
+
+ op = qml.CosineWindow(wires=[0, 1])
+ res = np.reshape(op.state_vector() ** 2, (-1,))
+ expected = np.array([0.0, 0.25, 0.5, 0.25])
+ assert np.allclose(res, expected)
+
+ def test_CosineWindow_state_vector_bad_wire_order(self):
+ """Tests that the provided wire_order must contain the wires in the operation."""
+ qsv_op = qml.CosineWindow(wires=[0, 1])
+ with pytest.raises(WireError, match="wire_order must contain all CosineWindow wires"):
+ qsv_op.state_vector(wire_order=[1, 2])
+
+ def test_CosineWindow_state_vector_wire_order(self):
+ """Tests that the state vector works with a different order of wires."""
+ op = qml.CosineWindow(wires=[0, 1])
+ res = np.reshape(op.state_vector(wire_order=[1, 0]) ** 2, (-1,))
+ expected = np.array([0.0, 0.5, 0.25, 0.25])
+ assert np.allclose(res, expected)