Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tangelo/toolboxes/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
from .operators import FermionOperator, QubitOperator, QubitHamiltonian
from .operators import count_qubits, normal_ordered, squared_normal_ordered, list_to_fermionoperator, qubitop_to_qubitham
from .multiformoperator import MultiformOperator
from.translation import translate_operator
73 changes: 73 additions & 0 deletions tangelo/toolboxes/operators/_translation_qiskit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2021 Good Chemistry Company.
#
# 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.

"""Module to convert Qiskit qubit operators."""

from tangelo.toolboxes.operators import QubitOperator
from tangelo.linq.helpers import pauli_of_to_string, pauli_string_to_of


def tangelo_to_qiskit(qubit_operator, n_qubits):
"""Helper function to translate a Tangelo QubitOperator to a qiskit
PauliSumOp. Qiskit must be installed for the function to work.

Args:
qubit_operator (tangelo.toolboxes.operators.QubitOperator): Self-explanatory.
n_qubits (int): Number of qubits relevant to the operator.

Returns:
(qiskit.opflow.primitive_ops.PauliSumOp): Qiskit qubit operator.
"""

# Import qiskit functions.
from qiskit.opflow.primitive_ops import PauliSumOp

# Convert each term sequencially.
term_list = list()
for term_tuple, coeff in qubit_operator.terms.items():
term_string = pauli_of_to_string(term_tuple, n_qubits)

# Reverse the string becasue of qiskit convention.
term_list += [(term_string[::-1], coeff)]

return PauliSumOp.from_list(term_list)


def qiskit_to_tangelo(qubit_operator):
"""Helper function to translate a a qiskit PauliSumOp to a Tangelo
QubitOperator.

Args:
qubit_operator (qiskit.opflow.primitive_ops.PauliSumOp): Self-explanatory.

Returns:
(tangelo.toolboxes.operators.QubitOperator): Tangelo qubit operator.
"""

# Creation of a dictionary to append all terms at once.
terms_dict = dict()
for pauli_word in qubit_operator:
# Inversion of the string because of qiskit ordering.
term_string = pauli_word.to_pauli_op().primitive.to_label()[::-1]
term_tuple = pauli_string_to_of(term_string)
terms_dict[tuple(term_tuple)] = pauli_word.coeff

# Create and copy the information into a new QubitOperator.
tangelo_op = QubitOperator()
tangelo_op.terms = terms_dict

# Clean the QubitOperator.
tangelo_op.compress()

return tangelo_op
89 changes: 89 additions & 0 deletions tangelo/toolboxes/operators/tests/test_translation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright 2021 Good Chemistry Company.
#
# 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.

import os
import unittest
import numpy as np
from openfermion.utils import load_operator
from openfermion.linalg import eigenspectrum

from tangelo.helpers.utils import installed_backends
from tangelo.toolboxes.operators import QubitOperator
from tangelo.toolboxes.operators import translate_operator

# For openfermion.load_operator function.
pwd_this_test = os.path.dirname(os.path.abspath(__file__))

tangelo_op = QubitOperator("X0 Y1 Z2", 1.)


class TranslateOperatorTest(unittest.TestCase):

def test_unsupported_source(self):
"""Test error with an unsuported source."""

with self.assertRaises(NotImplementedError):
translate_operator(tangelo_op, source="sourcenotsupported", target="tangelo")

def test_unsupported_target(self):
"""Test error with an unsuported target."""

with self.assertRaises(NotImplementedError):
translate_operator(tangelo_op, source="tangelo", target="targetnotsupported")

def test_tangelo_not_involved(self):
"""Test error if tangelo is not the source nor the target."""

with self.assertRaises(NotImplementedError):
translate_operator(tangelo_op, source="nottangelo", target="nottangeloeither")

@unittest.skipIf("qiskit" not in installed_backends, "Test Skipped: Qiskit not available \n")
def test_qiskit_to_tangelo(self):
"""Test translation from a qiskit to a tangelo operator."""

from qiskit.opflow.primitive_ops import PauliSumOp
qiskit_op = PauliSumOp.from_list([("ZYX", 1.)])

test_op = translate_operator(qiskit_op, source="qiskit", target="tangelo")
self.assertEqual(test_op, tangelo_op)

@unittest.skipIf("qiskit" not in installed_backends, "Test Skipped: Qiskit not available \n")
def test_tangelo_to_qiskit(self):
"""Test translation from a tangelo to a qiskit operator."""

from qiskit.opflow.primitive_ops import PauliSumOp
qiskit_op = PauliSumOp.from_list([("ZYX", 1.)])

test_op = translate_operator(tangelo_op, source="tangelo", target="qiskit")
self.assertEqual(qiskit_op, test_op)

@unittest.skipIf("qiskit" not in installed_backends, "Test Skipped: Qiskit not available \n")
def test_tangelo_to_qiskit_H2_eigenvalues(self):
"""Test eigenvalues resulting from a tangelo to qiskit translation."""

from qiskit.algorithms import NumPyEigensolver

qu_op = load_operator("H2_JW_occfirst.data", data_directory=pwd_this_test+"/data", plain_text=True)
test_op = translate_operator(qu_op, source="tangelo", target="qiskit")

eigenvalues_tangelo = eigenspectrum(qu_op)

qiskit_solver = NumPyEigensolver(2**4)
eigenvalues_qiskit = qiskit_solver.compute_eigenvalues(test_op)

np.testing.assert_array_almost_equal(eigenvalues_tangelo, eigenvalues_qiskit.eigenvalues)


if __name__ == "__main__":
unittest.main()
63 changes: 63 additions & 0 deletions tangelo/toolboxes/operators/translation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright 2021 Good Chemistry Company.
#
# 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.

"""Module to convert qubit operators to different formats."""

from tangelo.toolboxes.operators import count_qubits
from tangelo.toolboxes.operators._translation_qiskit import tangelo_to_qiskit, qiskit_to_tangelo


FROM_TANGELO = {
"qiskit": tangelo_to_qiskit
}

TO_TANGELO = {
"qiskit": qiskit_to_tangelo
}


def translate_operator(qubit_operator, source, target, n_qubits=None):
"""Function to convert a qubit operator defined within the "source" format
to another format. Only the trnaslation from and to tangelo are currently
supported.

Args:
qubit_operator (source format): Self-explanatory.
source (string): Identifier for the source format.
target (string): Identifier for the target format.
n_qubits (int): Number of qubits relevant to the operator.

Returns:
(target format): Translated qubit operator.
"""

source = source.lower()
target = target.lower()

if source == target:
return qubit_operator

if "tangelo" not in {source, target}:
raise NotImplementedError(f"Only translation from and to tangelo are supported.")
elif source == "tangelo":
if target not in FROM_TANGELO:
raise NotImplementedError(f"Target {target} not supported.")
n_qubits = count_qubits(qubit_operator) if n_qubits is None else n_qubits
qubit_operator = FROM_TANGELO[target](qubit_operator, n_qubits)
elif target == "tangelo":
if source not in TO_TANGELO:
raise NotImplementedError(f"Source {source} not supported.")
qubit_operator = TO_TANGELO[source](qubit_operator)

return qubit_operator