Skip to content

Commit

Permalink
Merge branch 'master' into vn-entanglement-mp
Browse files Browse the repository at this point in the history
  • Loading branch information
mudit2812 committed Sep 18, 2024
2 parents 0f5395c + 09ae791 commit 0d34b3e
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 6 deletions.
14 changes: 14 additions & 0 deletions doc/development/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``.

Expand Down
7 changes: 7 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -108,6 +111,10 @@

<h3>Deprecations 👋</h3>

* 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)

Expand Down
3 changes: 3 additions & 0 deletions pennylane/devices/tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
104 changes: 104 additions & 0 deletions pennylane/fermi/fermionic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://en.wikipedia.org/wiki/Creation_and_annihilation_operators#Creation_and_annihilation_operators_in_quantum_field_theories>`_:
.. 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):
Expand Down
11 changes: 11 additions & 0 deletions pennylane/templates/state_preparations/basis.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
Contains the BasisStatePreparation template.
"""

import warnings

import numpy as np

import pennylane as qml
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions tests/capture/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__


Expand Down
100 changes: 100 additions & 0 deletions tests/fermi/test_fermionic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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):
Expand Down
10 changes: 10 additions & 0 deletions tests/templates/test_state_preparations/test_basis_state_prep.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -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."""

Expand Down
4 changes: 2 additions & 2 deletions tests/test_qnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,15 +975,15 @@ 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))

@qml.qnode(dev)
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))
Expand Down
4 changes: 2 additions & 2 deletions tests/test_qnode_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,15 +890,15 @@ 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))

@qml.qnode(dev)
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))
Expand Down
Loading

0 comments on commit 0d34b3e

Please sign in to comment.