diff --git a/doc/development/deprecations.rst b/doc/development/deprecations.rst
index 05abd31e808..b7e58cb2279 100644
--- a/doc/development/deprecations.rst
+++ b/doc/development/deprecations.rst
@@ -45,6 +45,20 @@ Pending deprecations
- Deprecated in v0.39
- Will be removed in v0.40
+* The ``decomp_depth`` argument in ``qml.device`` is deprecated.
+
+ - Deprecated in v0.38
+ - Will be removed in v0.39
+
+* The ``simplify`` argument in ``qml.Hamiltonian`` and ``qml.ops.LinearCombination`` is deprecated.
+ Instead, ``qml.simplify()`` can be called on the constructed operator.
+
+ - Deprecated in v0.37
+ - Will be removed in v0.39
+
+* The :class:`~pennylane.BasisStatePreparation` template is deprecated.
+ Instead, use :class:`~pennylane.BasisState`.
+
* The ``QubitStateVector`` template is deprecated.
Instead, use ``StatePrep``.
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 7a4a716a1dd..95d4d17c4f2 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -44,6 +44,9 @@
`from pennylane.capture.primitives import *`.
[(#6129)](https://github.com/PennyLaneAI/pennylane/pull/6129)
+* `FermiWord` class now has a method to apply anti-commutator relations.
+ [(#6196)](https://github.com/PennyLaneAI/pennylane/pull/6196)
+
* `FermiWord` and `FermiSentence` classes now have methods to compute adjoints.
[(#6166)](https://github.com/PennyLaneAI/pennylane/pull/6166)
@@ -108,6 +111,10 @@
Deprecations 👋
+* The `qml.BasisStatePreparation` template is deprecated.
+ Instead, use `qml.BasisState`.
+ [(#6021)](https://github.com/PennyLaneAI/pennylane/pull/6021)
+
* The `'ancilla'` argument for `qml.iterative_qpe` has been deprecated. Instead, use the `'aux_wire'` argument.
[(#6277)](https://github.com/PennyLaneAI/pennylane/pull/6277)
diff --git a/pennylane/devices/tests/test_templates.py b/pennylane/devices/tests/test_templates.py
index 86e1101340a..c91304488ef 100644
--- a/pennylane/devices/tests/test_templates.py
+++ b/pennylane/devices/tests/test_templates.py
@@ -225,6 +225,9 @@ def circuit():
[math.fidelity_statevector(circuit(), exp_state)], [1.0], atol=tol(dev.shots)
)
+ @pytest.mark.filterwarnings(
+ "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning"
+ )
def test_BasisStatePreparation(self, device, tol):
"""Test the BasisStatePreparation template."""
dev = device(4)
diff --git a/pennylane/fermi/fermionic.py b/pennylane/fermi/fermionic.py
index 193cc0b4be8..b5f03020780 100644
--- a/pennylane/fermi/fermionic.py
+++ b/pennylane/fermi/fermionic.py
@@ -324,6 +324,110 @@ def to_mat(self, n_orbitals=None, format="dense", buffer_size=None):
wire_order=list(range(largest_order)), format=format, buffer_size=buffer_size
)
+ def shift_operator(self, initial_position, final_position):
+ r"""Shifts an operator in the FermiWord from ``initial_position`` to ``final_position`` by applying the fermionic anti-commutation relations.
+
+ There are three `anti-commutator relations `_:
+
+ .. math::
+ \left\{ a_i, a_j \right\} = 0, \quad \left\{ a^{\dagger}_i, a^{\dagger}_j \right\} = 0, \quad \left\{ a_i, a^{\dagger}_j \right\} = \delta_{ij},
+
+
+ where
+
+ .. math::
+ \left\{a_i, a_j \right\} = a_i a_j + a_j a_i,
+
+ and
+
+ .. math::
+ \delta_{ij} = \begin{cases} 1 & i = j \\ 0 & i \neq j \end{cases}.
+
+ Args:
+ initial_position (int): The position of the operator to be shifted.
+ final_position (int): The desired position of the operator.
+
+ Returns:
+ FermiSentence: The ``FermiSentence`` obtained after applying the anti-commutator relations.
+
+ Raises:
+ TypeError: if ``initial_position`` or ``final_position`` is not an integer
+ ValueError: if ``initial_position`` or ``final_position`` are outside the range ``[0, len(fermiword) - 1]``
+ where ``len(fermiword)`` is the number of operators in the FermiWord.
+
+
+ **Example**
+
+ >>> w = qml.fermi.FermiWord({(0, 0): '+', (1, 1): '-'})
+ >>> w.shift_operator(0, 1)
+ -1 * a(1) a⁺(0)
+ """
+
+ if not isinstance(initial_position, int) or not isinstance(final_position, int):
+ raise TypeError("Positions must be integers.")
+
+ if initial_position < 0 or final_position < 0:
+ raise ValueError("Positions must be positive integers.")
+
+ if initial_position > len(self.sorted_dic) - 1 or final_position > len(self.sorted_dic) - 1:
+ raise ValueError("Positions are out of range.")
+
+ if initial_position == final_position:
+ return FermiSentence({self: 1})
+
+ fw = self
+ fs = FermiSentence({fw: 1})
+ delta = 1 if initial_position < final_position else -1
+ current = initial_position
+
+ while current != final_position:
+ indices = list(fw.sorted_dic.keys())
+ next = current + delta
+ curr_idx, curr_val = indices[current], fw[indices[current]]
+ next_idx, next_val = indices[next], fw[indices[next]]
+
+ # commuting identical terms
+ if curr_idx[1] == next_idx[1] and curr_val == next_val:
+ current += delta
+ continue
+
+ coeff = fs.pop(fw)
+
+ fw = dict(fw)
+ fw[(current, next_idx[1])] = next_val
+ fw[(next, curr_idx[1])] = curr_val
+
+ if curr_idx[1] != next_idx[1]:
+ del fw[curr_idx], fw[next_idx]
+
+ fw = FermiWord(fw)
+
+ # anti-commutator is 0
+ if curr_val == next_val or curr_idx[1] != next_idx[1]:
+ current += delta
+ fs += -coeff * fw
+ continue
+
+ # anti-commutator is 1
+ _min = min(current, next)
+ _max = max(current, next)
+ items = list(fw.sorted_dic.items())
+
+ left = FermiWord({(i, key[1]): value for i, (key, value) in enumerate(items[:_min])})
+ middle = FermiWord(
+ {(i, key[1]): value for i, (key, value) in enumerate(items[_min : _max + 1])}
+ )
+ right = FermiWord(
+ {(i, key[1]): value for i, (key, value) in enumerate(items[_max + 1 :])}
+ )
+
+ terms = left * (1 - middle) * right
+ fs += coeff * terms
+
+ current += delta
+
+ return fs
+
# pylint: disable=useless-super-delegation
class FermiSentence(dict):
diff --git a/pennylane/templates/state_preparations/basis.py b/pennylane/templates/state_preparations/basis.py
index 48211a6d1e8..d63fa0f1b32 100644
--- a/pennylane/templates/state_preparations/basis.py
+++ b/pennylane/templates/state_preparations/basis.py
@@ -15,6 +15,8 @@
Contains the BasisStatePreparation template.
"""
+import warnings
+
import numpy as np
import pennylane as qml
@@ -30,6 +32,8 @@ class BasisStatePreparation(Operation):
``basis_state`` influences the circuit architecture and is therefore incompatible with
gradient computations.
+ ``BasisStatePreparation`` is deprecated and will be removed in version 0.40. Instead, please use ``BasisState``.
+
Args:
basis_state (array): Input array of shape ``(n,)``, where n is the number of wires
the state preparation acts on.
@@ -59,6 +63,13 @@ def circuit(basis_state):
ndim_params = (1,)
def __init__(self, basis_state, wires, id=None):
+
+ warnings.warn(
+ "BasisStatePreparation is deprecated and will be removed in version 0.40. "
+ "Instead, please use BasisState.",
+ qml.PennyLaneDeprecationWarning,
+ )
+
basis_state = qml.math.stack(basis_state)
# check if the `basis_state` param is batched
diff --git a/tests/capture/test_templates.py b/tests/capture/test_templates.py
index d04990b2971..619723a9b02 100644
--- a/tests/capture/test_templates.py
+++ b/tests/capture/test_templates.py
@@ -27,8 +27,12 @@
jax = pytest.importorskip("jax")
jnp = jax.numpy
-pytestmark = pytest.mark.jax
-
+pytestmark = [
+ pytest.mark.jax,
+ pytest.mark.filterwarnings(
+ "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning"
+ ),
+]
original_op_bind_code = qml.operation.Operator._primitive_bind_call.__code__
diff --git a/tests/fermi/test_fermionic.py b/tests/fermi/test_fermionic.py
index dc61295115a..4e028ad9875 100644
--- a/tests/fermi/test_fermionic.py
+++ b/tests/fermi/test_fermionic.py
@@ -53,6 +53,62 @@
fw7 = FermiWord({(0, 10): "-", (1, 30): "+", (2, 0): "-", (3, 400): "+"})
fw7_dag = FermiWord({(0, 400): "-", (1, 0): "+", (2, 30): "-", (3, 10): "+"})
+fw8 = FermiWord({(0, 0): "-", (1, 1): "+"})
+fw8c = FermiWord({(0, 1): "+", (1, 0): "-"})
+fw8cs = FermiSentence({fw8c: -1})
+
+fw9 = FermiWord({(0, 0): "-", (1, 1): "-"})
+fw9c = FermiWord({(0, 1): "-", (1, 0): "-"})
+fw9cs = FermiSentence({fw9c: -1})
+
+fw10 = FermiWord({(0, 0): "+", (1, 1): "+"})
+fw10c = FermiWord({(0, 1): "+", (1, 0): "+"})
+fw10cs = FermiSentence({fw10c: -1})
+
+fw11 = FermiWord({(0, 0): "-", (1, 0): "+"})
+fw11c = FermiWord({(0, 0): "+", (1, 0): "-"})
+fw11cs = 1 + FermiSentence({fw11c: -1})
+
+fw12 = FermiWord({(0, 0): "+", (1, 0): "+"})
+fw12c = FermiWord({(0, 0): "+", (1, 0): "+"})
+fw12cs = FermiSentence({fw12c: 1})
+
+fw13 = FermiWord({(0, 0): "-", (1, 0): "-"})
+fw13c = FermiWord({(0, 0): "-", (1, 0): "-"})
+fw13cs = FermiSentence({fw13c: 1})
+
+fw14 = FermiWord({(0, 0): "+", (1, 0): "-"})
+fw14c = FermiWord({(0, 0): "-", (1, 0): "+"})
+fw14cs = 1 + FermiSentence({fw14c: -1})
+
+fw15 = FermiWord({(0, 0): "-", (1, 1): "+", (2, 2): "+"})
+fw15c = FermiWord({(0, 1): "+", (1, 0): "-", (2, 2): "+"})
+fw15cs = FermiSentence({fw15c: -1})
+
+fw16 = FermiWord({(0, 0): "-", (1, 1): "+", (2, 2): "-"})
+fw16c = FermiWord({(0, 0): "-", (1, 2): "-", (2, 1): "+"})
+fw16cs = FermiSentence({fw16c: -1})
+
+fw17 = FermiWord({(0, 0): "-", (1, 0): "+", (2, 2): "-"})
+fw17c1 = FermiWord({(0, 2): "-"})
+fw17c2 = FermiWord({(0, 0): "+", (1, 0): "-", (2, 2): "-"})
+fw17cs = fw17c1 - fw17c2
+
+fw18 = FermiWord({(0, 0): "+", (1, 1): "+", (2, 2): "-", (3, 3): "-"})
+fw18c = FermiWord({(0, 0): "+", (1, 3): "-", (2, 1): "+", (3, 2): "-"})
+fw18cs = FermiSentence({fw18c: 1})
+
+fw19 = FermiWord({(0, 0): "+", (1, 1): "+", (2, 2): "-", (3, 2): "+"})
+fw19c1 = FermiWord({(0, 0): "+", (1, 1): "+"})
+fw19c2 = FermiWord({(0, 2): "+", (1, 0): "+", (2, 1): "+", (3, 2): "-"})
+fw19cs = FermiSentence({fw19c1: 1, fw19c2: -1})
+
+fw20 = FermiWord({(0, 0): "-", (1, 0): "+", (2, 1): "-", (3, 0): "-", (4, 0): "+"})
+fw20c1 = FermiWord({(0, 0): "-", (1, 0): "+", (2, 1): "-"})
+fw20c2 = FermiWord({(0, 0): "+", (1, 1): "-", (2, 0): "-"})
+fw20c3 = FermiWord({(0, 0): "+", (1, 0): "-", (2, 0): "+", (3, 1): "-", (4, 0): "-"})
+fw20cs = fw20c1 + fw20c2 - fw20c3
+
class TestFermiWord:
def test_missing(self):
@@ -167,6 +223,41 @@ def test_to_mat_error(self):
with pytest.raises(ValueError, match="n_orbitals cannot be smaller than 2"):
fw1.to_mat(n_orbitals=1)
+ tup_fw_shift = (
+ (fw8, 0, 1, fw8cs),
+ (fw9, 0, 1, fw9cs),
+ (fw10, 0, 1, fw10cs),
+ (fw11, 0, 1, fw11cs),
+ (fw12, 0, 1, fw12cs),
+ (fw13, 0, 1, fw13cs),
+ (fw14, 0, 1, fw14cs),
+ (fw15, 0, 1, fw15cs),
+ (fw16, 1, 2, fw16cs),
+ (fw17, 0, 1, fw17cs),
+ (fw8, 0, 0, FermiSentence({fw8: 1})),
+ (fw8, 1, 0, fw8cs),
+ (fw11, 1, 0, fw11cs),
+ (fw18, 3, 1, fw18cs),
+ (fw19, 3, 0, fw19cs),
+ (fw20, 4, 0, fw20cs),
+ )
+
+ @pytest.mark.parametrize("fw, i, j, fs", tup_fw_shift)
+ def test_shift_operator(self, fw, i, j, fs):
+ """Test that the shift_operator method correctly applies the anti-commutator relations."""
+ assert fw.shift_operator(i, j) == fs
+
+ def test_shift_operator_errors(self):
+ """Test that the shift_operator method correctly raises exceptions."""
+ with pytest.raises(TypeError, match="Positions must be integers."):
+ fw8.shift_operator(0.5, 1)
+
+ with pytest.raises(ValueError, match="Positions must be positive integers."):
+ fw8.shift_operator(-1, 0)
+
+ with pytest.raises(ValueError, match="Positions are out of range."):
+ fw8.shift_operator(1, 2)
+
tup_fw_dag = (
(fw1, fw1_dag),
(fw2, fw2_dag),
@@ -588,6 +679,15 @@ def test_array_must_not_exceed_length_1(self, method_name):
}
)
+fs8 = fw8 + fw9
+fs8c = fw8 + fw9cs
+
+fs9 = 1.3 * fw8 + (1.4 + 3.8j) * fw9
+fs9c = 1.3 * fw8 + (1.4 + 3.8j) * fw9cs
+
+fs10 = -1.3 * fw11 + 2.3 * fw9
+fs10c = -1.3 * fw11cs + 2.3 * fw9
+
class TestFermiSentence:
def test_missing(self):
diff --git a/tests/templates/test_state_preparations/test_basis_state_prep.py b/tests/templates/test_state_preparations/test_basis_state_prep.py
index 90b88463a05..441af8e007d 100644
--- a/tests/templates/test_state_preparations/test_basis_state_prep.py
+++ b/tests/templates/test_state_preparations/test_basis_state_prep.py
@@ -21,6 +21,10 @@
import pennylane as qml
+pytestmark = pytest.mark.filterwarnings(
+ "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning"
+)
+
def test_standard_validity():
"""Check the operation using the assert_valid function."""
@@ -33,6 +37,12 @@ def test_standard_validity():
qml.ops.functions.assert_valid(op)
+def test_BasisStatePreparation_is_deprecated():
+ """Test that BasisStatePreparation is deprecated."""
+ with pytest.warns(qml.PennyLaneDeprecationWarning, match="BasisStatePreparation is deprecated"):
+ _ = qml.BasisStatePreparation([1, 0], wires=[0, 1])
+
+
class TestDecomposition:
"""Tests that the template defines the correct decomposition."""
diff --git a/tests/test_qnode.py b/tests/test_qnode.py
index 1322ca62c16..19ce1b067a6 100644
--- a/tests/test_qnode.py
+++ b/tests/test_qnode.py
@@ -975,7 +975,7 @@ def test_sampling_with_mcm(self, basis_state, mocker):
@qml.qnode(dev)
def cry_qnode(x):
"""QNode where we apply a controlled Y-rotation."""
- qml.BasisStatePreparation(basis_state, wires=[0, 1])
+ qml.BasisState(basis_state, wires=[0, 1])
qml.CRY(x, wires=[0, 1])
return qml.sample(qml.PauliZ(1))
@@ -983,7 +983,7 @@ def cry_qnode(x):
def conditional_ry_qnode(x):
"""QNode where the defer measurements transform is applied by
default under the hood."""
- qml.BasisStatePreparation(basis_state, wires=[0, 1])
+ qml.BasisState(basis_state, wires=[0, 1])
m_0 = qml.measure(0)
qml.cond(m_0, qml.RY)(x, wires=1)
return qml.sample(qml.PauliZ(1))
diff --git a/tests/test_qnode_legacy.py b/tests/test_qnode_legacy.py
index 73eaf29b302..681be8161fe 100644
--- a/tests/test_qnode_legacy.py
+++ b/tests/test_qnode_legacy.py
@@ -890,7 +890,7 @@ def test_sampling_with_mcm(self, basis_state, mocker):
@qml.qnode(dev)
def cry_qnode(x):
"""QNode where we apply a controlled Y-rotation."""
- qml.BasisStatePreparation(basis_state, wires=[0, 1])
+ qml.BasisState(basis_state, wires=[0, 1])
qml.CRY(x, wires=[0, 1])
return qml.sample(qml.PauliZ(1))
@@ -898,7 +898,7 @@ def cry_qnode(x):
def conditional_ry_qnode(x):
"""QNode where the defer measurements transform is applied by
default under the hood."""
- qml.BasisStatePreparation(basis_state, wires=[0, 1])
+ qml.BasisState(basis_state, wires=[0, 1])
m_0 = qml.measure(0)
qml.cond(m_0, qml.RY)(x, wires=1)
return qml.sample(qml.PauliZ(1))
diff --git a/tests/transforms/test_batch_input.py b/tests/transforms/test_batch_input.py
index 3d97e981ed2..2499aafdcdd 100644
--- a/tests/transforms/test_batch_input.py
+++ b/tests/transforms/test_batch_input.py
@@ -200,6 +200,9 @@ def circuit2(data, weights):
assert np.allclose(res, indiv_res)
+@pytest.mark.filterwarnings(
+ "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning"
+)
def test_basis_state_preparation(mocker):
"""Test that batching works for BasisStatePreparation"""
dev = qml.device("default.qubit", wires=3)
diff --git a/tests/transforms/test_batch_params.py b/tests/transforms/test_batch_params.py
index ad4daddeb69..29b89ebda4a 100644
--- a/tests/transforms/test_batch_params.py
+++ b/tests/transforms/test_batch_params.py
@@ -174,6 +174,9 @@ def circuit2(data, weights):
assert np.allclose(res, indiv_res)
+@pytest.mark.filterwarnings(
+ "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning"
+)
def test_basis_state_preparation(mocker):
"""Test that batching works for BasisStatePreparation"""
dev = qml.device("default.qubit", wires=4)
diff --git a/tests/transforms/test_defer_measurements.py b/tests/transforms/test_defer_measurements.py
index 3d5e98791ea..52a65f924a5 100644
--- a/tests/transforms/test_defer_measurements.py
+++ b/tests/transforms/test_defer_measurements.py
@@ -1347,6 +1347,9 @@ def quantum_control_circuit(rads):
class TestTemplates:
"""Tests templates being conditioned on mid-circuit measurement outcomes."""
+ @pytest.mark.filterwarnings(
+ "ignore:BasisStatePreparation is deprecated:pennylane.PennyLaneDeprecationWarning"
+ )
def test_basis_state_prep(self):
"""Test the basis state prep template conditioned on mid-circuit
measurement outcomes."""