Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
16 changes: 16 additions & 0 deletions tangelo/linq/tests/data/H2_JW_occfirst.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
QubitOperator:
(-0.10973055606700678+0j) [] +
(-0.0454428841443262+0j) [X0 X1 Y2 Y3] +
(0.0454428841443262+0j) [X0 Y1 Y2 X3] +
(0.0454428841443262+0j) [Y0 X1 X2 Y3] +
(-0.0454428841443262+0j) [Y0 Y1 X2 X3] +
(0.16988452027940382+0j) [Z0] +
(0.16821198673715723+0j) [Z0 Z1] +
(0.12005143072546026+0j) [Z0 Z2] +
(0.16549431486978647+0j) [Z0 Z3] +
(0.16988452027940382+0j) [Z1] +
(0.16549431486978647+0j) [Z1 Z2] +
(0.12005143072546026+0j) [Z1 Z3] +
(-0.21886306781219628+0j) [Z2] +
(0.17395378776494128+0j) [Z2 Z3] +
(-0.21886306781219633+0j) [Z3]
90 changes: 90 additions & 0 deletions tangelo/linq/tests/test_translator_qubitop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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.linq.translator.translate_qubitop import translate_operator
from tangelo.toolboxes.operators import QubitOperator

# 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()
62 changes: 60 additions & 2 deletions tangelo/linq/translator/translate_qiskit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Functions helping with quantum circuit format conversion between abstract
format and qiskit format.
"""Functions helping with quantum circuit and operator format conversion between
Tangelo format and qiskit format.

In order to produce an equivalent circuit for the target backend, it is
necessary to account for:
Expand All @@ -22,6 +22,9 @@
may also differ.
"""

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


def get_qiskit_gates():
"""Map gate name of the abstract format to the equivalent add_gate method of
Expand Down Expand Up @@ -98,3 +101,58 @@ def translate_qiskit(source_circuit):
else:
raise ValueError(f"Gate '{gate.name}' not supported on backend qiskit")
return target_circuit


def quop_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 qubit operator.
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 because of qiskit convention.
term_list += [(term_string[::-1], coeff)]

return PauliSumOp.from_list(term_list)


def quop_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.
"""

# Create 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
58 changes: 58 additions & 0 deletions tangelo/linq/translator/translate_qubitop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# 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.linq.translator.translate_qiskit import quop_qiskit_to_tangelo, quop_tangelo_to_qiskit


FROM_TANGELO = {
"qiskit": quop_tangelo_to_qiskit
}

TO_TANGELO = {
"qiskit": quop_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 translation 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 != "tangelo":
if source not in TO_TANGELO:
raise NotImplementedError(f"Source {source} is not supported.")
qubit_operator = TO_TANGELO[source](qubit_operator)
if target != "tangelo":
if target not in FROM_TANGELO:
raise NotImplementedError(f"Target {target} is 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)

return qubit_operator
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
Loading