# Source code for pennylane.qchem.observable_hf

# Copyright 2018-2022 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 module contains the functions needed for creating fermionic and qubit observables.
"""
# pylint: disable= too-many-branches, too-many-return-statements
import pennylane as qml
from pennylane import numpy as np
from pennylane.fermi import FermiSentence, FermiWord
from pennylane.operation import active_new_opmath
from pennylane.pauli.utils import simplify

[docs]def fermionic_observable(constant, one=None, two=None, cutoff=1.0e-12):
r"""Create a fermionic observable from molecular orbital integrals.

Args:
constant (array[float]): the contribution of the core orbitals and nuclei
one (array[float]): the one-particle molecular orbital integrals
two (array[float]): the two-particle molecular orbital integrals
cutoff (float): cutoff value for discarding the negligible integrals

Returns:
FermiSentence: fermionic observable

**Example**

>>> constant = np.array([1.0])
>>> integral = np.array([[0.5, -0.8270995], [-0.8270995, 0.5]])
>>> fermionic_observable(constant, integral)
1.0 * I
+ 0.5 * a⁺(0) a(0)
+ -0.8270995 * a⁺(0) a(2)
+ 0.5 * a⁺(1) a(1)
+ -0.8270995 * a⁺(1) a(3)
+ -0.8270995 * a⁺(2) a(0)
+ 0.5 * a⁺(2) a(2)
+ -0.8270995 * a⁺(3) a(1)
+ 0.5 * a⁺(3) a(3)
"""
coeffs = qml.math.array([])

if not qml.math.allclose(constant, 0.0):
coeffs = qml.math.concatenate((coeffs, constant))
operators = [[]]
else:
operators = []

if one is not None:
indices_one = qml.math.argwhere(abs(one) >= cutoff)
# up-up + down-down terms
operators_one = (indices_one * 2).tolist() + (indices_one * 2 + 1).tolist()
coeffs_one = qml.math.tile(one[abs(one) >= cutoff], 2)
coeffs = qml.math.convert_like(coeffs, one)
coeffs = qml.math.concatenate((coeffs, coeffs_one))
operators = operators + operators_one

if two is not None:
indices_two = np.array(qml.math.argwhere(abs(two) >= cutoff))
n = len(indices_two)
operators_two = (
[(indices_two[i] * 2).tolist() for i in range(n)]  # up-up-up-up
+ [(indices_two[i] * 2 + [0, 1, 1, 0]).tolist() for i in range(n)]  # up-down-down-up
+ [(indices_two[i] * 2 + [1, 0, 0, 1]).tolist() for i in range(n)]  # down-up-up-down
+ [(indices_two[i] * 2 + 1).tolist() for i in range(n)]  # down-down-down-down
)
coeffs_two = qml.math.tile(two[abs(two) >= cutoff], 4) / 2

coeffs = qml.math.concatenate((coeffs, coeffs_two))
operators = operators + operators_two

indices_sort = [operators.index(i) for i in sorted(operators)]
if indices_sort:
indices_sort = qml.math.array(indices_sort)

sentence = FermiSentence({FermiWord({}): constant})
for c, o in zip(coeffs[indices_sort], sorted(operators)):
if len(o) == 2:
sentence.update({FermiWord({(0, o): "+", (1, o): "-"}): c})
if len(o) == 4:
sentence.update(
{FermiWord({(0, o): "+", (1, o): "+", (2, o): "-", (3, o): "-"}): c}
)
sentence.simplify()

return sentence

[docs]def qubit_observable(o_ferm, cutoff=1.0e-12):
r"""Convert a fermionic observable to a PennyLane qubit observable.

Args:
o_ferm (Union[FermiWord, FermiSentence]): fermionic operator
cutoff (float): cutoff value for discarding the negligible terms

Returns:
Operator: Simplified PennyLane Hamiltonian

**Example**

>>> w1 = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
>>> w2 = qml.fermi.FermiWord({(0, 1) : '+', (1, 2) : '-'})
>>> s = qml.fermi.FermiSentence({w1 : 1.2, w2: 3.1})
>>> print(qubit_observable(s))
(-0.3j) [Y0 X1]
+ (0.3j) [X0 Y1]
+ (-0.775j) [Y1 X2]
+ (0.775j) [X1 Y2]
+ ((0.3+0j)) [Y0 Y1]
+ ((0.3+0j)) [X0 X1]
+ ((0.775+0j)) [Y1 Y2]
+ ((0.775+0j)) [X1 X2]

If the new op-math is active, an arithmetic operator is returned.

>>> qml.operation.enable_new_opmath()
>>> w1 = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
>>> w2 = qml.fermi.FermiWord({(0, 0) : '+', (1, 1) : '-'})
>>> s = qml.fermi.FermiSentence({w1 : 1.2, w2: 3.1})
>>> print(qubit_observable(s))
(-0.775j*(PauliY(wires=) @ PauliX(wires=)))
+ ((0.775+0j)*(PauliY(wires=) @ PauliY(wires=)))
+ ((0.775+0j)*(PauliX(wires=) @ PauliX(wires=)))
+ (0.775j*(PauliX(wires=) @ PauliY(wires=)))
"""
h = qml.jordan_wigner(o_ferm, ps=True, tol=cutoff)
h.simplify(tol=cutoff)

if active_new_opmath():
if not h.wires:
return h.operation(wire_order=)
return h.operation()

if not h.wires:
h = h.hamiltonian(wire_order=)
return qml.Hamiltonian(
h.coeffs, [qml.Identity(0) if o.name == "Identity" else o for o in h.ops]
)

h = h.hamiltonian()

return simplify(
qml.Hamiltonian(h.coeffs, [qml.Identity(0) if o.name == "Identity" else o for o in h.ops])
)


Using PennyLane

Development

API

Internals