From 1cc054af161c398765293b2b7fb469fab6f88795 Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 20 Dec 2022 22:49:12 -0500 Subject: [PATCH 01/13] Modified the custom_jw_transform.py so that it works without specifying qubits and qubit map inputs. Plus, the case where the user put duplicate indices for qubits list is handled --- .../hamiltonian/custom_jw_transform.py | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 src/tequila/hamiltonian/custom_jw_transform.py diff --git a/src/tequila/hamiltonian/custom_jw_transform.py b/src/tequila/hamiltonian/custom_jw_transform.py new file mode 100644 index 00000000..bc863983 --- /dev/null +++ b/src/tequila/hamiltonian/custom_jw_transform.py @@ -0,0 +1,237 @@ +import typing +import openfermion +from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian + + +def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: + """ + This method maps fermion to qubits. + + Precondition: + The last index of qubits should be greater than + the site value of fermion. + For instance fermion: "3", qubits: [1, 2, 3, 4] is allowed, but + fermion: "3", qubits: [1, 2, 3] is not allowed + + Post condition: + QubitHamiltonian of the qubits representation is returned + + === Parameter === + fermion: string representation of fermion operator to be mapped + qubits: list of qubits on which Paulis are applied + e.g. cjw.custom_transform("3^", [0, 1, 3, 5]) + + === Return === + QubitHamiltonian + """ + if not _is_input_compatible(fermion, qubits): + raise Exception("Incorrect Input Format") + + qubit_hamiltonian = QubitHamiltonian() + operator = fermion[0] + site = qubits[int(operator[0])] + pauli_x = 'X' + str(site) + pauli_y = 'Y' + str(site) + if fermion[-1] == '^': + pauli_z = '' + for qubit in range(0, int(operator)): + pauli_z += 'Z' + str(qubits[qubit]) + ' ' + + qubit_hamiltonian += QubitHamiltonian( + qubit_operator=openfermion.QubitOperator( + term=pauli_z + pauli_x, coefficient=0.5) + ) + + qubit_hamiltonian += QubitHamiltonian( + qubit_operator=openfermion.QubitOperator( + term=pauli_z + pauli_y, coefficient=-0.5j) + ) + + else: + pauli_z = '' + for qubit in range(0, int(operator)): + pauli_z += 'Z' + str(qubits[qubit]) + ' ' + qubit_hamiltonian += QubitHamiltonian( + qubit_operator=openfermion.QubitOperator( + term=pauli_z + pauli_x, coefficient=0.5) + ) + qubit_hamiltonian += QubitHamiltonian( + qubit_operator=openfermion.QubitOperator( + term=pauli_z + pauli_y, coefficient=0.5j) + ) + + return qubit_hamiltonian + + +def _custom_transform_one_fermion( + fermion: str, qubits: list, + qubit_map: typing.Optional[dict] = None) -> QubitHamiltonian: + """ + This method maps one fermion operator to qubit with specification of + the mapping allowed + + Precondition: + The last index of qubits should be greater than + the site value of fermion. (Example in custom_transform method) + The qubit specified by qubit_map should be contained in qubits and + should not be duplicated in qubits. + + Post condition: + Return QubitHamiltonian based on the custom transformation the user + specified with qubits and qubit_map + + === Parameter === + fermion: str e.g. '3' '4^' + qubits: list[int] + qubit_map: dict[str, int], default=None + + === Return === + QubitHamiltonian + + """ + + qubit_hamiltonian = QubitHamiltonian() + revised_qubits = qubits + mapped_qubits = False + if qubit_map is None: + qubit_hamiltonian += _custom_transform(fermion, qubits) + return qubit_hamiltonian + for key in qubit_map: + if key == fermion and qubits.count(qubit_map[key]) > 1: + raise Exception("Duplicated qubits. Either change " + "the mapped qubit or qubits list") + if key == fermion and qubit_map[key] not in qubits: + raise Exception("The mapped qubit should be contained in " + "qubits list") + if key == fermion: + revised_qubits.remove(qubit_map[key]) + revised_qubits.insert(int(key[0]), qubit_map[key]) + qubit_hamiltonian += _custom_transform( + fermion, revised_qubits) + mapped_qubits = True + if not mapped_qubits: + qubit_hamiltonian += _custom_transform(fermion, qubits) + return qubit_hamiltonian + + +def custom_jw_transform( + fermions: typing.Union[str, list], qubits: typing.Optional[list] = None, + qubit_map: typing.Optional[dict] = None) -> QubitHamiltonian: + """ + This method maps multiple fermion operators to specified qubits + Precondition: + Same as custom_transform_one_fermion + + Post condition: + Return QubitHamiltonian based on the custom transformation the user + specified with qubits and qubit_map + + === Parameter === + fermion: str e.g. '3' '3 4^ 6' + qubits: list[list[int]] e.g. [[1, 2, 4, 5], [2, 3, 4, 6], [3, 5, 2, 3]] + qubit_map: dict[str, int], default=None e.g. {'3': 4, '5^': 2} + + === Return === + QubitHamiltonian + """ + + if qubits is None: + if qubit_map is None: + largest_index = _find_largest_index(fermions) + new_qubit_list = [i for i in range(largest_index + 1)] + if _num_of_fermions(fermions) == 1: + return custom_jw_transform(fermions, new_qubit_list, qubit_map) + else: + qubits_list = \ + [new_qubit_list for _ in range(_num_of_fermions(fermions))] + return custom_jw_transform(fermions, qubits_list, qubit_map) + + if not _is_input_compatible(fermions, qubits): + raise Exception("Incorrect input given (fermions or qubits)." + " Follow the convention for fermions and qubits input") + + if isinstance(fermions, str): + if len(fermions) <= 2: + return _custom_transform_one_fermion(fermions, qubits, qubit_map) + fermion_ops = _split_fermions(fermions) + qubit_hamiltonian = _custom_transform_one_fermion( + fermion_ops[0], qubits[0], qubit_map) + for i in range(1, len(fermion_ops)): + qubit_hamiltonian *= _custom_transform_one_fermion( + fermion_ops[i], qubits[i], qubit_map) + + return qubit_hamiltonian + if isinstance(fermions, list): + qubit_hamiltonian = _custom_transform_one_fermion( + fermions[0], qubits[0], qubit_map) + for i in range(1, len(fermions)): + qubit_hamiltonian *= _custom_transform_one_fermion( + fermions[i], qubits[i], qubit_map) + + return qubit_hamiltonian + + +def _num_of_fermions(fermions: typing.Union[str, list]) -> int: + if isinstance(fermions, str): + if len(fermions) <= 2: + return 1 + return len(_split_fermions(fermions)) + elif isinstance(fermions, list): + return len(fermions) + + +def _find_largest_index(fermions: typing.Union[str, list]) -> int: + if isinstance(fermions, str): + if len(fermions) <= 2: + return int(fermions[0]) + fermion_ops = _split_fermions(fermions) + max_ind = 0 + for index in fermion_ops: + max_ind = max(max_ind, int(index[0])) + return max_ind + elif isinstance(fermions, list): + max_ind = 0 + for index in fermions: + max_ind = max(max_ind, int(index[0])) + return max_ind + + +def _split_fermions(fermions: str) -> list: + if ", " in fermions: + fermion_ops = fermions.split(", ") + elif " " in fermions: + fermion_ops = fermions.split(" ") + elif "," in fermions: + fermion_ops = fermions.split(",") + elif ":" in fermions: + fermion_ops = fermions.split(":") + else: + raise Exception("Incorrect input format") + return fermion_ops + + +def _is_input_compatible( + fermion: typing.Union[str, list], qubits: list) -> bool: + """ + Helper method for CustomJordanWigner class + Checks if the inputs, namely the fermion operators are compatible with + qubits in the following ways; the size, ... + """ + if isinstance(fermion, str) and len(fermion) < 3: + if int(fermion[0]) >= len(qubits): + return False + if len(qubits) != len(set(qubits)): + return False + return True + elif isinstance(fermion, str) and len(fermion) >= 3: + for maps in qubits: + if not isinstance(maps, list): + return False + fermion_ops = _split_fermions(fermion) + if len(fermion_ops) > len(qubits): + return False + for i in range(len(fermion_ops)): + if not _is_input_compatible(fermion_ops, qubits[i]): + return False + return True + return True From 55d6fa3577d7550eaf13b5d0d16470211e2a5800 Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 20 Dec 2022 22:51:37 -0500 Subject: [PATCH 02/13] test cases using pytest to test custom_jw_transform are created in test_custom_jw_transform.py file --- tests/test_custom_jw_transform.py | 382 ++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 tests/test_custom_jw_transform.py diff --git a/tests/test_custom_jw_transform.py b/tests/test_custom_jw_transform.py new file mode 100644 index 00000000..d6ebd4dc --- /dev/null +++ b/tests/test_custom_jw_transform.py @@ -0,0 +1,382 @@ +import pytest +import numpy as np +import openfermion +from tequila.hamiltonian.custom_jw_transform import custom_jw_transform +from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian +from numpy.linalg import eig + + +@pytest.mark.parametrize("fermions, qubits, qubit_op1, qubit_op2", + [("3", [1, 2, 3, 4], "Z1 Z2 Z3 X4", "Z1 Z2 Z3 Y4"), + ("1", [0, 3], "Z0 X3", "Z0 Y3"), + ("4", [0, 1, 2, 3, 4], + "Z0 Z1 Z2 Z3 X4", "Z0 Z1 Z2 Z3 Y4")]) +def test_custom_jw(fermions, qubits, qubit_op1, qubit_op2): + actual1 = custom_jw_transform(fermions, qubits) + expected1 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + assert actual1 == expected1 + + +@pytest.mark.parametrize("fermions, qubits, qubit_op1, qubit_op2", + [("3^", [1, 2, 3, 4], "Z1 Z2 Z3 X4", "Z1 Z2 Z3 Y4"), + ("2^", [0, 3, 5], "Z0 Z3 X5", "Z0 Z3 Y5"), + ("5^", [0, 2, 3, 4, 6, 7], "Z0 Z2 Z3 Z4 Z6 X7", + "Z0 Z2 Z3 Z4 Z6 Y7")]) +def test_custom_jw_dg(fermions, qubits, qubit_op1, qubit_op2): + actual1 = custom_jw_transform(fermions, qubits) + expected1 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=-0.5j)) + assert actual1 == expected1 + + +@pytest.mark.parametrize("fermions, qubits", + [("3", [1, 2, 3]), + ("1", [2]), + ("4^", [0, 4])]) +def test_custom_jw_short_qubits_list(fermions, qubits): + with pytest.raises(Exception) as e_info: + custom_jw_transform(fermions, qubits) + + +@pytest.mark.parametrize("fermions, qubits, qubit_op1, qubit_op2", + [("3", [1, 2, 3, 4, 5], "Z1 Z2 Z3 X4", "Z1 Z2 Z3 Y4"), + ("1", [1, 2, 5], "Z1 X2", "Z1 Y2"), + ("4", [1, 2, 3, 4, 5, 6, 7], + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5")]) +def test_custom_jw_long_qubits_list(fermions, qubits, qubit_op1, qubit_op2): + actual1 = custom_jw_transform(fermions, qubits) + expected1 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + assert actual1 == expected1 + + +@pytest.mark.parametrize("fermions, qubits, qubit_op1, qubit_op2", + [("3^", [1, 2, 3, 4, 5], "Z1 Z2 Z3 X4", "Z1 Z2 Z3 Y4"), + ("1^", [1, 2, 5], "Z1 X2", "Z1 Y2"), + ("4^", [1, 2, 3, 4, 5, 6, 7], + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5")]) +def test_custom_jw_long_qubits_list_dg(fermions, qubits, qubit_op1, qubit_op2): + actual1 = custom_jw_transform(fermions, qubits) + expected1 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=-0.5j)) + assert actual1 == expected1 + + +@pytest.mark.parametrize("fermions, qubits, qubit_map, qubit_op1, qubit_op2", + [("3", [1, 2, 3, 4], {"3": 3}, + "Z1 Z2 Z4 X3", "Z1 Z2 Z4 Y3"), + ("1", [1, 2, 3, 4], {"1": 4}, "Z1 X4", "Z1 Y4"), + ("4", [1, 2, 3, 4, 6], {"4": 2}, + "Z1 Z3 Z4 Z6 X2", "Z1 Z3 Z4 Z6 Y2")]) +def test_custom_jw_qubit_map( + fermions, qubits, qubit_map, qubit_op1, qubit_op2): + actual = custom_jw_transform(fermions, qubits, qubit_map) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubits, qubit_map, qubit_op1, qubit_op2", + [("3^", [1, 2, 3, 4], {"3^": 3}, + "Z1 Z2 Z4 X3", "Z1 Z2 Z4 Y3"), + ("1^", [1, 2, 3, 4], {"1^": 4}, "Z1 X4", "Z1 Y4"), + ("4^", [1, 2, 3, 4, 6], {"4^": 2}, + "Z1 Z3 Z4 Z6 X2", "Z1 Z3 Z4 Z6 Y2")]) +def test_custom_jw_qubit_map_dg( + fermions, qubits, qubit_map, qubit_op1, qubit_op2): + actual = custom_jw_transform(fermions, qubits, qubit_map) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=-0.5j)) + + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubits, qubit_map, qubits_op1, qubit_op2", + [("3", [1, 2, 3, 4], {"4": 3}, + "Z1 Z2 Z3 X4", "Z1 Z2 Z3 Y4"), + ("1", [1, 2, 3, 4], {"2": 4}, "Z1 X2", "Z1 Y2"), + ("4", [1, 2, 3, 4, 6], {"4^": 2}, + "Z1 Z2 Z3 Z4 X6", "Z1 Z2 Z3 Z4 Y6")]) +def test_custom_jw_no_key(fermions, qubits, qubit_map, qubits_op1, qubit_op2): + actual = custom_jw_transform(fermions, qubits, qubit_map) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubits_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubits, qubit_map, qubits_op1, qubit_op2", + [("3^", [1, 2, 3, 4], {"4^": 3}, + "Z1 Z2 Z3 X4", "Z1 Z2 Z3 Y4"), + ("1^", [1, 2, 3, 4], {"1": 4}, "Z1 X2", "Z1 Y2"), + ("4^", [1, 2, 3, 4, 6], {"5^": 2}, + "Z1 Z2 Z3 Z4 X6", "Z1 Z2 Z3 Z4 Y6")]) +def test_custom_jw_no_key_dg(fermions, qubits, qubit_map, qubits_op1, qubit_op2): + actual = custom_jw_transform(fermions, qubits, qubit_map) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubits_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=-0.5j)) + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubits, qubit_map", + [("3", [1, 2, 3, 4], {"3": 5}), + ("1", [0, 1], {"1": 3}), + ("4", [0, 1, 2, 3, 5], {"4": 6})]) +def test_custom_jw_no_qubit(fermions, qubits, qubit_map): + with pytest.raises(Exception) as e_info: + custom_jw_transform(fermions, qubits, qubit_map) + + +@pytest.mark.parametrize("fermions, qubits, qubit_map", + [("3", [2, 2, 2, 2], {"3": 2}), + ("1", [1, 1], {"1": 1}), + ("4^", [0, 1, 0, 2, 1], {"4^": 2})]) +def test_custom_jw_duplicate_qubits(fermions, qubits, qubit_map): + with pytest.raises(Exception) as e_info: + custom_jw_transform(fermions, qubits, qubit_map) + + +@pytest.mark.parametrize("fermions, qubits, qubit_op1, qubit_op2," + " qubit_op3, qubit_op4", + [("3 4^", [[2, 3, 4, 1], [1, 2, 3, 4, 5]], + "Z2 Z3 Z4 X1", "Z2 Z3 Z4 Y1", + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5"), + ("1 1^", [[2, 3, 1], [1, 2, 4, 5]], + "Z2 X3", "Z2 Y3", + "Z1 X2", "Z1 Y2"), + ("2 4^", [[2, 3, 1], [1, 2, 3, 4, 5]], + "Z2 Z3 X1", "Z2 Z3 Y1", + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5")]) +def test_custom_jw_multiple_no_map(fermions, qubits, qubit_op1, + qubit_op2, qubit_op3, qubit_op4): + actual = custom_jw_transform(fermions, qubits) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + expected *= (QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op3, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op4, coefficient=-0.5j))) + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubits, qubit_map, qubit_op1, qubit_op2," + " qubit_op3, qubit_op4, qubit_op5, qubit_op6", + [("3 4^ 1", [[2, 3, 4, 1], [1, 2, 3, 4, 5], [0, 1]], + {"3": 2}, "Z3 Z4 Z1 X2", "Z3 Z4 Z1 Y2", + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5", + "Z0 X1", "Z0 Y1"), + ("1 1^ 2", [[2, 3, 1], [1, 2, 4, 5], [1, 2, 3]], + {"1": 2, "1^": 4}, "Z3 X2", "Z3 Y2", + "Z1 X4", "Z1 Y4", + "Z1 Z2 X3", "Z1 Z2 Y3"), + ("2 4^ 1", [[2, 3, 1], [1, 2, 3, 4, 5], [2, 3]], + {"2": 3, "4^": 3}, "Z2 Z1 X3", "Z2 Z1 Y3", + "Z1 Z2 Z4 Z5 X3", "Z1 Z2 Z4 Z5 Y3", + "Z2 X3", "Z2 Y3")]) +def test_custom_jw_multiple_with_map( + fermions, qubits, qubit_map, qubit_op1, qubit_op2, + qubit_op3, qubit_op4, qubit_op5, qubit_op6): + actual = custom_jw_transform(fermions, qubits, qubit_map) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + expected *= (QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op3, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op4, coefficient=-0.5j))) + expected *= ((QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op5, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op6, coefficient=-0.5j)))) + + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubits, qubit_map", + [("3 4^", [[2, 3, 4, 1]], {"3": 2}), + ("1 1^", [[2, 3, 1]], {"1": 2, "1^": 4}), + ("2 4^", [[2, 3, 1]], + {"2": 3, "4^": 3})]) +def test_custom_jw_multiple_short_qubits_list(fermions, qubits, qubit_map): + with pytest.raises(Exception) as e_info: + custom_jw_transform(fermions, qubits, qubit_map) + + +@pytest.mark.parametrize( + "fermions1, fermions2, qubits1, qubits2, qubit_map1, qubit_map2," + " qubit_op1, qubit_op2, qubit_op3, qubit_op4, " + "qubit_op5, qubit_op6, qubit_op7, qubit_op8", [ + ("3 4", "2 1", [[2, 3, 4, 1], [1, 2, 3, 4, 5]], [[2, 3, 1], [2, 3]], + {"3": 2}, {"2": 3, "4^": 3}, "Z3 Z4 Z1 X2", "Z3 Z4 Z1 Y2", + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5", "Z2 Z1 X3", "Z2 Z1 Y3", + "Z2 X3", "Z2 Y3"), + ("1 2", "5 1", [[2, 3, 4], [1, 2, 3, 4, 5]], + [[2, 3, 1, 0, 4, 6], [2, 3]], {"2": 2}, {"5": 3, "1": 3}, + "Z3 X2", "Z3 Y2", "Z1 Z3 X2", "Z1 Z3 Y2", + "Z2 Z3 Z1 Z0 Z4 Z6 X3", "Z2 Z3 Z1 Z0 Z4 Z6 Y3", + "Z2 X3", "Z2 Y3"), + ]) +def test_custom_jw_multiple_addition( + fermions1, fermions2, qubits1, qubits2, qubit_map1, qubit_map2, + qubit_op1, qubit_op2, qubit_op3, qubit_op4, + qubit_op5, qubit_op6, qubit_op7, qubit_op8): + result1 = custom_jw_transform(fermions1, qubits1, qubit_map1) + result2 = custom_jw_transform(fermions2, qubits2, qubit_map2) + actual = result1 + result2 + expected1 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + expected1 *= QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op3, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op4, coefficient=0.5j)) + expected2 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op5, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op6, coefficient=0.5j)) + expected2 *= QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op7, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op8, coefficient=0.5j)) + expected = expected1 + expected2 + assert actual == expected + + +@pytest.mark.parametrize( + "fermions1, fermions2, qubits1, qubits2, qubit_map1, qubit_map2," + " qubit_op1, qubit_op2, qubit_op3, qubit_op4, " + "qubit_op5, qubit_op6, qubit_op7, qubit_op8", [ + ("3 4^", "2 1^", [[2, 3, 4, 1], [1, 2, 3, 4, 5]], [[2, 3, 1], [2, 3]], + {"3": 2}, {"2": 3, "4^": 3}, "Z3 Z4 Z1 X2", "Z3 Z4 Z1 Y2", + "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5", "Z2 Z1 X3", "Z2 Z1 Y3", + "Z2 X3", "Z2 Y3"), + ("1 2^", "5 1^", [[2, 3, 4], [1, 2, 3, 4, 5]], + [[2, 3, 1, 0, 4, 6], [2, 3]], {"2": 2}, {"5": 3, "1": 3}, + "Z3 X2", "Z3 Y2", "Z1 Z3 X2", "Z1 Z3 Y2", + "Z2 Z3 Z1 Z0 Z4 Z6 X3", "Z2 Z3 Z1 Z0 Z4 Z6 Y3", + "Z2 X3", "Z2 Y3"), + ]) +def test_custom_jw_multiple_addition_dg( + fermions1, fermions2, qubits1, qubits2, qubit_map1, qubit_map2, + qubit_op1, qubit_op2, qubit_op3, qubit_op4, + qubit_op5, qubit_op6, qubit_op7, qubit_op8): + result1 = custom_jw_transform(fermions1, qubits1, qubit_map1) + result2 = custom_jw_transform(fermions2, qubits2, qubit_map2) + actual = result1 + result2 + expected1 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + expected1 *= QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op3, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op4, coefficient=-0.5j)) + expected2 = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op5, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op6, coefficient=0.5j)) + expected2 *= QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op7, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op8, coefficient=-0.5j)) + expected = expected1 + expected2 + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubit_op1, qubit_op2", + [("1", "Z0 X1", "Z0 Y1"), + ("2", "Z0 Z1 X2", "Z0 Z1 Y2"), + ("5", "Z0 Z1 Z2 Z3 Z4 X5", "Z0 Z1 Z2 Z3 Z4 Y5")]) +def test_custom_jw_no_qubits(fermions, qubit_op1, qubit_op2): + actual = custom_jw_transform(fermions) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubit_op1, qubit_op2", + [("1^", "Z0 X1", "Z0 Y1"), + ("2^", "Z0 Z1 X2", "Z0 Z1 Y2"), + ("5^", "Z0 Z1 Z2 Z3 Z4 X5", "Z0 Z1 Z2 Z3 Z4 Y5")]) +def test_custom_jw_no_qubits_dg(fermions, qubit_op1, qubit_op2): + actual = custom_jw_transform(fermions) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=-0.5j)) + assert actual == expected + + +@pytest.mark.parametrize("fermions, qubit_op1, qubit_op2, qubit_op3, qubit_op4", + [("1 2", "Z0 X1", "Z0 Y1", "Z0 Z1 X2", "Z0 Z1 Y2"), + ("2 4", "Z0 Z1 X2", "Z0 Z1 Y2", "Z0 Z1 Z2 Z3 X4", + "Z0 Z1 Z2 Z3 Y4"), + ("5 1", "Z0 Z1 Z2 Z3 Z4 X5", "Z0 Z1 Z2 Z3 Z4 Y5", + "Z0 X1", "Z0 Y1")]) +def test_custom_jw_no_qubits_multiple(fermions, qubit_op1, + qubit_op2, qubit_op3, qubit_op4): + actual = custom_jw_transform(fermions) + expected = QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op1, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op2, coefficient=0.5j)) + expected *= QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op3, coefficient=0.5) + ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( + term=qubit_op4, coefficient=0.5j)) + assert actual == expected + + + + +@pytest.mark.parametrize("fermions, qubits", + [("1 1^", [[0, 1], [2, 3]]), + ("2^ 2", [[0, 1, 2], [2, 3, 4]]), + ("3 3^", [[0, 2, 1, 4], [2, 3, 4, 5]])]) +def test_custom_jw_hermitian_eigen_value(fermions, qubits): + actual = custom_jw_transform(fermions, qubits).to_matrix() + + a = np.array(actual) + w, v = eig(a) + for k in w: + if round(k) != -1 and round(k) != 0 and round(k) != 1: + assert False + assert True + + +@pytest.mark.parametrize("fermions, qubits", + [("1 2^", [[0, 1], [2, 3, 4]]), + ("2 3", [[0, 1, 2], [2, 3, 4, 2]]), + ("1 3^", [[0, 2, 1], [2, 3, 4, 5]])]) +def test_custom_jw_zero_eigen_value(fermions, qubits): + actual = custom_jw_transform(fermions, qubits).to_matrix() + + a = np.array(actual) + w, v = eig(a) + for k in w: + if round(k) != -1 and round(k) != 0 and round(k) != 1: + assert False + assert True From efece141849fe9f684153f79a4065baffca28f75 Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Wed, 21 Dec 2022 12:36:13 -0500 Subject: [PATCH 03/13] Modified some of the failed test cases --- tests/test_custom_jw_transform.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/test_custom_jw_transform.py b/tests/test_custom_jw_transform.py index d6ebd4dc..37cf8bfe 100644 --- a/tests/test_custom_jw_transform.py +++ b/tests/test_custom_jw_transform.py @@ -207,7 +207,7 @@ def test_custom_jw_multiple_with_map( expected *= ((QubitHamiltonian(qubit_operator=openfermion.QubitOperator( term=qubit_op5, coefficient=0.5) ) + QubitHamiltonian(qubit_operator=openfermion.QubitOperator( - term=qubit_op6, coefficient=-0.5j)))) + term=qubit_op6, coefficient=0.5j)))) assert actual == expected @@ -232,8 +232,8 @@ def test_custom_jw_multiple_short_qubits_list(fermions, qubits, qubit_map): "Z2 X3", "Z2 Y3"), ("1 2", "5 1", [[2, 3, 4], [1, 2, 3, 4, 5]], [[2, 3, 1, 0, 4, 6], [2, 3]], {"2": 2}, {"5": 3, "1": 3}, - "Z3 X2", "Z3 Y2", "Z1 Z3 X2", "Z1 Z3 Y2", - "Z2 Z3 Z1 Z0 Z4 Z6 X3", "Z2 Z3 Z1 Z0 Z4 Z6 Y3", + "Z2 X3", "Z2 Y3", "Z1 Z3 X2", "Z1 Z3 Y2", + "Z2 Z1 Z0 Z4 Z6 X3", "Z2 Z1 Z0 Z4 Z6 Y3", "Z2 X3", "Z2 Y3"), ]) def test_custom_jw_multiple_addition( @@ -272,9 +272,9 @@ def test_custom_jw_multiple_addition( "Z1 Z2 Z3 Z4 X5", "Z1 Z2 Z3 Z4 Y5", "Z2 Z1 X3", "Z2 Z1 Y3", "Z2 X3", "Z2 Y3"), ("1 2^", "5 1^", [[2, 3, 4], [1, 2, 3, 4, 5]], - [[2, 3, 1, 0, 4, 6], [2, 3]], {"2": 2}, {"5": 3, "1": 3}, - "Z3 X2", "Z3 Y2", "Z1 Z3 X2", "Z1 Z3 Y2", - "Z2 Z3 Z1 Z0 Z4 Z6 X3", "Z2 Z3 Z1 Z0 Z4 Z6 Y3", + [[2, 3, 1, 0, 4, 6], [2, 3]], {"2^": 2}, {"5": 3, "1^": 3}, + "Z2 X3", "Z2 Y3", "Z1 Z3 X2", "Z1 Z3 Y2", + "Z2 Z1 Z0 Z4 Z6 X3", "Z2 Z1 Z0 Z4 Z6 Y3", "Z2 X3", "Z2 Y3"), ]) def test_custom_jw_multiple_addition_dg( @@ -350,8 +350,6 @@ def test_custom_jw_no_qubits_multiple(fermions, qubit_op1, assert actual == expected - - @pytest.mark.parametrize("fermions, qubits", [("1 1^", [[0, 1], [2, 3]]), ("2^ 2", [[0, 1, 2], [2, 3, 4]]), @@ -369,7 +367,7 @@ def test_custom_jw_hermitian_eigen_value(fermions, qubits): @pytest.mark.parametrize("fermions, qubits", [("1 2^", [[0, 1], [2, 3, 4]]), - ("2 3", [[0, 1, 2], [2, 3, 4, 2]]), + ("2 3", [[0, 1, 2], [2, 3, 4, 1]]), ("1 3^", [[0, 2, 1], [2, 3, 4, 5]])]) def test_custom_jw_zero_eigen_value(fermions, qubits): actual = custom_jw_transform(fermions, qubits).to_matrix() From d6de495b672fa4664b49f0818f57bdcdce8e820f Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 28 Feb 2023 09:48:40 -0500 Subject: [PATCH 04/13] Visualization for circuit. --- src/tequila/circuit/visualize.py | 156 +++++++++++++++++++++++++++++++ tests/test_visualize.py | 63 +++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 src/tequila/circuit/visualize.py create mode 100644 tests/test_visualize.py diff --git a/src/tequila/circuit/visualize.py b/src/tequila/circuit/visualize.py new file mode 100644 index 00000000..2587d61a --- /dev/null +++ b/src/tequila/circuit/visualize.py @@ -0,0 +1,156 @@ +import tequila +from typing import Optional, List +import matplotlib.pyplot as plt +import matplotlib.figure as figure +import networkx + + +def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, + circuit: Optional[tequila.QCircuit] = None, + connectivity: Optional[List[tuple]] = None, + file_path: Optional[str] = None) -> figure.Figure: + """ + Precondition: + The maximum number of qubits is 10 at the moment (Feb24, 2023) + + Post condition: + The graph of the qubit_hamiltonian is displayed + or is stored in file_path + + One thing to note is that if you are using command-line interface, + the plot might not be successfully shown + + , so it's better idea to save it as an image. + === parameters === + qubit_hamiltonian: A QubitHamiltonian representation of pauli operators + circuit: A QCircuit that corresponds to the + connectivity: networkx.Graph.edges that show the connectivity of qubits + A nested list should be provided for this argument. + file_name: str format of file name + to which the plotted graph will be exported. The file will be a png format + + === return === + None + + === sample usages === + >>> visualize( + >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1))) + *** A graph with nodes 0 and 5 having colour red + and node 3 having colour green *** + >>> visualize( + >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), + >>> circuit=gates.X(0) + gates.CNOT(0, 5) + gates.Y(3)) + *** A graph with nodes 0 and 5 having color red and + node 3 having colour green with edge 0 and 5 exists *** + >>> visualize( + >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), + >>> connectivity=[(0, 0), (0, 5), (3, 3)]) + *** A graph with nodes 0 and 5 having color red and + node 3 having colour green with edge 0 and 5 exists *** + >>> visualize( + >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), + >>> connectivity=[(0, 0), (0, 5), (3, 3)], + >>> file_name="test_system") + *** Exported an image of a graph with nodes 0 and 5 having color red and + node 3 having colour green with edge 0 and 5 exists to test_system.png *** + """ + if(len(qubit_hamiltonian.qubit_operator.terms)) != 1: + raise Exception("The input qubit_operator should have length 1") + + qh = next(iter(qubit_hamiltonian.qubit_operator.terms)) + graph = networkx.Graph() + graph, pos = _draw_basics(graph) + if circuit is None and connectivity is None: + graph, pos = _visualize_helper(qh, graph, pos) + + elif connectivity is None: + lConn = _circuit_to_connectivity(circuit) + graph.add_edges_from(lConn) + graph, pos = _visualize_helper(qh, graph, pos, lConn) + + elif circuit is None: + graph, pos = _visualize_helper(qh, graph, pos, connectivity) + + if file_path is None: + return plt.figure() + if file_path is not None: + plt.savefig(file_path+".png", format="PNG") + return plt.figure() + + +def _circuit_to_connectivity(circuit): + """ + A helper function for visualize() function. + This function returns the list of connections found in circuit + """ + Gdict = {s: [] for s in circuit.qubits} + for gate in circuit.gates: + if gate.control: + for s in gate.control: + for t in gate.target: + tstr = '' + tstr += str(t) + target = int(tstr) + Gdict[s].append(target) # add target to key of correlated controls + for p in gate.target: + for r in gate.control: + cstr = '' + cstr += str(r) + control = int(cstr) + Gdict[p].append(control) # add control to key of correlated targets + else: + for s in gate.target: + for t in gate.target: + tstr2 = '' + tstr2 += str(t) + target2 = int(tstr2) + Gdict[s].append(target2) + lConn = [] # List of connections between qubits + for a, b in Gdict.items(): + for q in b: + lConn.append((a, q)) + return lConn + + +def _draw_basics(graph): + """ + A helper function for visualize() function. + This function sets up the basic graph to be used. + """ + length = 10 + for site in range(length): + graph.add_node(site) + pos = networkx.spring_layout(graph, seed=3113794652) + networkx.draw_networkx_nodes( + graph, pos, node_color="#FFFFFF", edgecolors="#000000") + return graph, pos + + +def _visualize_helper(qh_list: List[tequila.QubitHamiltonian], + graph, pos, connection: Optional[list] = None): + """ + A helper function for visualize() function. + This function visualize the graph based on the QubitHamiltonian and connection + """ + for pair in qh_list: + if pair[1] == 'X': + networkx.draw_networkx_nodes( + graph, pos, + nodelist=[pair[0]], node_color="tab:red") + elif pair[1] == 'Y': + networkx.draw_networkx_nodes( + graph, pos, + nodelist=[pair[0]], node_color="tab:green") + elif pair[1] == 'Z': + networkx.draw_networkx_nodes( + graph, pos, + nodelist=[pair[0]], node_color="tab:blue") + if connection is not None: + for edge in connection: + if edge[0] != edge[1]: + networkx.draw_networkx_edges( + graph, pos, edgelist=[edge], width=5) + networkx.draw_networkx_labels( + graph, pos, + labels={0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}) + return graph, pos diff --git a/tests/test_visualize.py b/tests/test_visualize.py new file mode 100644 index 00000000..35a6d135 --- /dev/null +++ b/tests/test_visualize.py @@ -0,0 +1,63 @@ +import pytest +from tequila.circuit.visualize import visualize +import sys +import tequila +import openfermion +import matplotlib.figure as figure +import matplotlib.pyplot as plt + +if 'networkx' in sys.modules: + NETWORKX_EXIST = True +else: + NETWORKX_EXIST = False + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize("qh", tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1))) +def test_visualize_with_qubit_hamiltonian(qh): + fig = visualize(qh) + assert plt.fignum_exists(fig.number) + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize( + "qh", tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)) + + tequila.QubitHamiltonian(openfermion.QubitOperator("X0 Y3", 1))) +def test_visualize_with_invalid_qubit_hamiltonian(qh): + with pytest.raises(Exception) as e_info: + visualize(qh) + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize("qh, circuit", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3)]) +def test_visualize_with_circui(qh, circuit): + fig = visualize(qh, circuit=circuit) + assert plt.fignum_exists(fig.number) + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize("qh, connectivity", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)]]) +def test_visualize_with_connectivity(qh, connectivity): + fig = visualize(qh, connectivity=connectivity) + assert plt.fignum_exists(fig.number) + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize("qh, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), "test_file1"]) +def test_visualize_with_file(qh, file): + fig = visualize(qh, file_path=file) + assert plt.fignum_exists(fig.number) + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize("qh, circuit, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2"]) +def test_visualize_with_circuit_and_file(qh, circuit, file): + fig = visualize(qh, circuit=circuit, file_path=file) + assert plt.fignum_exists(fig) + + +@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.parametrize("qh, connectivity, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)], "test_file3"]) +def test_visualize_with_connectivity(qh, connectivity, file): + fig = visualize(qh, connectivity=connectivity, file_path=file) + assert plt.fignum_exists(fig.number) From bb6162968c3c2ff94930f96f774ac481435ec7ea Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Wed, 1 Mar 2023 09:53:03 -0500 Subject: [PATCH 05/13] Fixed the reimporing openfermion probelm --- .../hamiltonian/custom_jw_transform.py | 41 +++++------ src/tequila/hamiltonian/edge_vertex_ops.py | 71 +++++++++++++++++++ tests/test_array.py | 2 + 3 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 src/tequila/hamiltonian/edge_vertex_ops.py diff --git a/src/tequila/hamiltonian/custom_jw_transform.py b/src/tequila/hamiltonian/custom_jw_transform.py index bc863983..f559583c 100644 --- a/src/tequila/hamiltonian/custom_jw_transform.py +++ b/src/tequila/hamiltonian/custom_jw_transform.py @@ -1,6 +1,6 @@ import typing -import openfermion from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian +import tequila.hamiltonian.paulis as paulis def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: @@ -30,35 +30,23 @@ def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: qubit_hamiltonian = QubitHamiltonian() operator = fermion[0] site = qubits[int(operator[0])] - pauli_x = 'X' + str(site) - pauli_y = 'Y' + str(site) + pauli_x = paulis.X(site) + pauli_y = paulis.Y(site) if fermion[-1] == '^': - pauli_z = '' + pauli_z = QubitHamiltonian() for qubit in range(0, int(operator)): - pauli_z += 'Z' + str(qubits[qubit]) + ' ' + pauli_z *= paulis.Z(qubits[qubit]) - qubit_hamiltonian += QubitHamiltonian( - qubit_operator=openfermion.QubitOperator( - term=pauli_z + pauli_x, coefficient=0.5) - ) - - qubit_hamiltonian += QubitHamiltonian( - qubit_operator=openfermion.QubitOperator( - term=pauli_z + pauli_y, coefficient=-0.5j) - ) + qubit_hamiltonian += 0.5 * (pauli_z + pauli_x) + qubit_hamiltonian += -0.5j * (pauli_z + pauli_y) else: - pauli_z = '' + pauli_z = QubitHamiltonian() for qubit in range(0, int(operator)): - pauli_z += 'Z' + str(qubits[qubit]) + ' ' - qubit_hamiltonian += QubitHamiltonian( - qubit_operator=openfermion.QubitOperator( - term=pauli_z + pauli_x, coefficient=0.5) - ) - qubit_hamiltonian += QubitHamiltonian( - qubit_operator=openfermion.QubitOperator( - term=pauli_z + pauli_y, coefficient=0.5j) - ) + pauli_z *= paulis.Z(qubits[qubit]) + + qubit_hamiltonian += 0.5 * (pauli_z + pauli_x) + qubit_hamiltonian += 0.5j * (pauli_z + pauli_y) return qubit_hamiltonian @@ -172,6 +160,9 @@ def custom_jw_transform( def _num_of_fermions(fermions: typing.Union[str, list]) -> int: + """ + + """ if isinstance(fermions, str): if len(fermions) <= 2: return 1 @@ -213,7 +204,7 @@ def _split_fermions(fermions: str) -> list: def _is_input_compatible( fermion: typing.Union[str, list], qubits: list) -> bool: """ - Helper method for CustomJordanWigner class + Helper function for CustomJordanWigner class Checks if the inputs, namely the fermion operators are compatible with qubits in the following ways; the size, ... """ diff --git a/src/tequila/hamiltonian/edge_vertex_ops.py b/src/tequila/hamiltonian/edge_vertex_ops.py new file mode 100644 index 00000000..ae51cf54 --- /dev/null +++ b/src/tequila/hamiltonian/edge_vertex_ops.py @@ -0,0 +1,71 @@ +import openfermion + +import tequila + + +def fermion_to_majorana(ops: openfermion.FermionOperator): + """ + We use openfermion to get Majorana operators based on fermion operators + """ + return openfermion.get_majorana_operator(ops) + + +def majo_to_graph(majorana: openfermion.MajoranaOperator): + """ + + """ + one_dim_chain = "" + indices_pairs = [] + for term in majorana.terms: + if majorana.terms[term] != 0 and term != (): + indices_pairs.append(term) + + for pair in indices_pairs: + if pair[0] % 2 == 0 and pair[1] == pair[0] + 1: + one_dim_chain += "B{site} ".format(site=pair[0] // 2) + elif pair[0] % 2 == 0 and pair[1] % 2 == 0: + one_dim_chain += "A{site1}{site2} ".format(site1=pair[0] // 2, + site2=pair[1] // 2) + elif pair[0] % 2 == 0 and pair[1] % 2 == 1: + one_dim_chain += "A{site1}{site2}*B{site2} ".format( + site1=pair[0] // 2, site2=(pair[1] - 1) // 2) + elif pair[0] % 2 == 1 and pair[1] % 2 == 0: + one_dim_chain += "B{site1}*A{site1}{site2} ".format( + site1=(pair[0] - 1) // 2, site2=pair[1] // 2) + return one_dim_chain + + +def op_to_pauli(op: str): + """ + op is an Edge or Vertex operator + examples are 'A12', 'B2' + """ + if op[0] == "A": + if op[1] < op[2]: + return tequila.QubitHamiltonian(openfermion.QubitOperator( + "Y{site1} X{site2}".format(site1=op[1], site2=op[2]))) + else: + return tequila.QubitHamiltonian(openfermion.QubitOperator( + "Y{site1} X{site2}".format( + site1=op[1], site2=op[2]), coefficient=-1)) + elif op[0] == "B": + return tequila.QubitHamiltonian(openfermion.QubitOperator( + "X{site} Y{site}".format(site=op[1]), coefficient=1j)) + + +def graph_to_pauli(graph: str): + """ + graph is a string representation of all the edge and vertex operators + Example is graph = 'B1*A12 A12*B2' + """ + op_list = graph.split() + paulis = tequila.QubitHamiltonian() + for graph_op in op_list: + smaller_list = graph_op.split("*") + tempo_pauli = tequila.QubitHamiltonian() + for op in smaller_list: + tempo_pauli *= op_to_pauli(op) + + paulis += tempo_pauli + return paulis + diff --git a/tests/test_array.py b/tests/test_array.py index 872c65d5..037333c7 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -14,6 +14,7 @@ def test_array_computation(backend): result = tq.simulate(E, backend=backend) assert all(result == numpy.asarray([1.0, 0.0, 0.0, -1.0])) + @pytest.mark.parametrize("backend", backends) @pytest.mark.parametrize("shape", [(4,),(2,2)]) def test_array_shape(backend, shape): @@ -24,6 +25,7 @@ def test_array_shape(backend, shape): result = tq.simulate(E, backend=backend) assert (result == expected).all() + @pytest.mark.parametrize("backend", backends) @pytest.mark.parametrize("shape", [(2,2)]) def test_array_contraction(backend, shape): From 48474b7f92d42847bb7a94f140d8223deb6b38ca Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Fri, 10 Mar 2023 16:13:19 -0500 Subject: [PATCH 06/13] Resolved issues commented in the pull requests --- src/tequila/circuit/visualize.py | 48 ++++--------------- .../hamiltonian/custom_jw_transform.py | 10 ++-- tests/test_visualize.py | 25 +++++++++- 3 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/tequila/circuit/visualize.py b/src/tequila/circuit/visualize.py index 2587d61a..0682a50c 100644 --- a/src/tequila/circuit/visualize.py +++ b/src/tequila/circuit/visualize.py @@ -1,3 +1,5 @@ +import importlib.util + import tequila from typing import Optional, List import matplotlib.pyplot as plt @@ -54,8 +56,14 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, *** Exported an image of a graph with nodes 0 and 5 having color red and node 3 having colour green with edge 0 and 5 exists to test_system.png *** """ + if importlib.util.find_spec("networkx") is None: + raise tequila.TequilaException("networkx module is not in the system") + if importlib.util.find_spec("matplotlib") is None: + raise tequila.TequilaException("matplotlib module is not in the system") + if(len(qubit_hamiltonian.qubit_operator.terms)) != 1: - raise Exception("The input qubit_operator should have length 1") + raise tequila.TequilaException("The input qubit_operator" + " should have length 1") qh = next(iter(qubit_hamiltonian.qubit_operator.terms)) graph = networkx.Graph() @@ -64,9 +72,7 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, graph, pos = _visualize_helper(qh, graph, pos) elif connectivity is None: - lConn = _circuit_to_connectivity(circuit) - graph.add_edges_from(lConn) - graph, pos = _visualize_helper(qh, graph, pos, lConn) + graph, pos = _visualize_helper(qh, graph, pos, list(circuit.to_networkx().edges)) elif circuit is None: graph, pos = _visualize_helper(qh, graph, pos, connectivity) @@ -78,40 +84,6 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, return plt.figure() -def _circuit_to_connectivity(circuit): - """ - A helper function for visualize() function. - This function returns the list of connections found in circuit - """ - Gdict = {s: [] for s in circuit.qubits} - for gate in circuit.gates: - if gate.control: - for s in gate.control: - for t in gate.target: - tstr = '' - tstr += str(t) - target = int(tstr) - Gdict[s].append(target) # add target to key of correlated controls - for p in gate.target: - for r in gate.control: - cstr = '' - cstr += str(r) - control = int(cstr) - Gdict[p].append(control) # add control to key of correlated targets - else: - for s in gate.target: - for t in gate.target: - tstr2 = '' - tstr2 += str(t) - target2 = int(tstr2) - Gdict[s].append(target2) - lConn = [] # List of connections between qubits - for a, b in Gdict.items(): - for q in b: - lConn.append((a, q)) - return lConn - - def _draw_basics(graph): """ A helper function for visualize() function. diff --git a/src/tequila/hamiltonian/custom_jw_transform.py b/src/tequila/hamiltonian/custom_jw_transform.py index f559583c..7a5ac5a9 100644 --- a/src/tequila/hamiltonian/custom_jw_transform.py +++ b/src/tequila/hamiltonian/custom_jw_transform.py @@ -30,23 +30,21 @@ def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: qubit_hamiltonian = QubitHamiltonian() operator = fermion[0] site = qubits[int(operator[0])] - pauli_x = paulis.X(site) - pauli_y = paulis.Y(site) if fermion[-1] == '^': pauli_z = QubitHamiltonian() for qubit in range(0, int(operator)): pauli_z *= paulis.Z(qubits[qubit]) - qubit_hamiltonian += 0.5 * (pauli_z + pauli_x) - qubit_hamiltonian += -0.5j * (pauli_z + pauli_y) + qubit_hamiltonian += 0.5 * (pauli_z + paulis.X(site)) + qubit_hamiltonian += -0.5j * (pauli_z + paulis.Y(site)) else: pauli_z = QubitHamiltonian() for qubit in range(0, int(operator)): pauli_z *= paulis.Z(qubits[qubit]) - qubit_hamiltonian += 0.5 * (pauli_z + pauli_x) - qubit_hamiltonian += 0.5j * (pauli_z + pauli_y) + qubit_hamiltonian += 0.5 * (pauli_z + paulis.X(site)) + qubit_hamiltonian += 0.5j * (pauli_z + paulis.Y(site)) return qubit_hamiltonian diff --git a/tests/test_visualize.py b/tests/test_visualize.py index 35a6d135..dbf31e7e 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualize.py @@ -3,7 +3,6 @@ import sys import tequila import openfermion -import matplotlib.figure as figure import matplotlib.pyplot as plt if 'networkx' in sys.modules: @@ -11,8 +10,20 @@ else: NETWORKX_EXIST = False +if 'matplotlib' in sys.modules: + MATPLOTLIB_EXIST = True +else: + MATPLOTLIB_EXIST = False + +if 'openfermion' in sys.modules: + OPENFERMION_EXIST = True +else: + OPENFERMION_EXIST = False + @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh", tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1))) def test_visualize_with_qubit_hamiltonian(qh): fig = visualize(qh) @@ -20,6 +31,8 @@ def test_visualize_with_qubit_hamiltonian(qh): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize( "qh", tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)) + tequila.QubitHamiltonian(openfermion.QubitOperator("X0 Y3", 1))) @@ -29,6 +42,8 @@ def test_visualize_with_invalid_qubit_hamiltonian(qh): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh, circuit", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3)]) def test_visualize_with_circui(qh, circuit): fig = visualize(qh, circuit=circuit) @@ -36,6 +51,8 @@ def test_visualize_with_circui(qh, circuit): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh, connectivity", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)]]) def test_visualize_with_connectivity(qh, connectivity): fig = visualize(qh, connectivity=connectivity) @@ -43,6 +60,8 @@ def test_visualize_with_connectivity(qh, connectivity): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), "test_file1"]) def test_visualize_with_file(qh, file): fig = visualize(qh, file_path=file) @@ -50,6 +69,8 @@ def test_visualize_with_file(qh, file): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh, circuit, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2"]) def test_visualize_with_circuit_and_file(qh, circuit, file): fig = visualize(qh, circuit=circuit, file_path=file) @@ -57,6 +78,8 @@ def test_visualize_with_circuit_and_file(qh, circuit, file): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") +@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") +@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh, connectivity, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)], "test_file3"]) def test_visualize_with_connectivity(qh, connectivity, file): fig = visualize(qh, connectivity=connectivity, file_path=file) From 10092f731f6a644f74948aa4344fb3b691e88ad5 Mon Sep 17 00:00:00 2001 From: Rick0317 <89174344+Rick0317@users.noreply.github.com> Date: Fri, 10 Mar 2023 16:13:25 -0500 Subject: [PATCH 07/13] Delete edge_vertex_ops.py --- src/tequila/hamiltonian/edge_vertex_ops.py | 71 ---------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/tequila/hamiltonian/edge_vertex_ops.py diff --git a/src/tequila/hamiltonian/edge_vertex_ops.py b/src/tequila/hamiltonian/edge_vertex_ops.py deleted file mode 100644 index ae51cf54..00000000 --- a/src/tequila/hamiltonian/edge_vertex_ops.py +++ /dev/null @@ -1,71 +0,0 @@ -import openfermion - -import tequila - - -def fermion_to_majorana(ops: openfermion.FermionOperator): - """ - We use openfermion to get Majorana operators based on fermion operators - """ - return openfermion.get_majorana_operator(ops) - - -def majo_to_graph(majorana: openfermion.MajoranaOperator): - """ - - """ - one_dim_chain = "" - indices_pairs = [] - for term in majorana.terms: - if majorana.terms[term] != 0 and term != (): - indices_pairs.append(term) - - for pair in indices_pairs: - if pair[0] % 2 == 0 and pair[1] == pair[0] + 1: - one_dim_chain += "B{site} ".format(site=pair[0] // 2) - elif pair[0] % 2 == 0 and pair[1] % 2 == 0: - one_dim_chain += "A{site1}{site2} ".format(site1=pair[0] // 2, - site2=pair[1] // 2) - elif pair[0] % 2 == 0 and pair[1] % 2 == 1: - one_dim_chain += "A{site1}{site2}*B{site2} ".format( - site1=pair[0] // 2, site2=(pair[1] - 1) // 2) - elif pair[0] % 2 == 1 and pair[1] % 2 == 0: - one_dim_chain += "B{site1}*A{site1}{site2} ".format( - site1=(pair[0] - 1) // 2, site2=pair[1] // 2) - return one_dim_chain - - -def op_to_pauli(op: str): - """ - op is an Edge or Vertex operator - examples are 'A12', 'B2' - """ - if op[0] == "A": - if op[1] < op[2]: - return tequila.QubitHamiltonian(openfermion.QubitOperator( - "Y{site1} X{site2}".format(site1=op[1], site2=op[2]))) - else: - return tequila.QubitHamiltonian(openfermion.QubitOperator( - "Y{site1} X{site2}".format( - site1=op[1], site2=op[2]), coefficient=-1)) - elif op[0] == "B": - return tequila.QubitHamiltonian(openfermion.QubitOperator( - "X{site} Y{site}".format(site=op[1]), coefficient=1j)) - - -def graph_to_pauli(graph: str): - """ - graph is a string representation of all the edge and vertex operators - Example is graph = 'B1*A12 A12*B2' - """ - op_list = graph.split() - paulis = tequila.QubitHamiltonian() - for graph_op in op_list: - smaller_list = graph_op.split("*") - tempo_pauli = tequila.QubitHamiltonian() - for op in smaller_list: - tempo_pauli *= op_to_pauli(op) - - paulis += tempo_pauli - return paulis - From 76a97f0e0eae64a4f0ca9ad9b6fe78f36b86602b Mon Sep 17 00:00:00 2001 From: Rick0317 <89174344+Rick0317@users.noreply.github.com> Date: Fri, 10 Mar 2023 16:14:00 -0500 Subject: [PATCH 08/13] Update test_array.py --- tests/test_array.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_array.py b/tests/test_array.py index 037333c7..872c65d5 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -14,7 +14,6 @@ def test_array_computation(backend): result = tq.simulate(E, backend=backend) assert all(result == numpy.asarray([1.0, 0.0, 0.0, -1.0])) - @pytest.mark.parametrize("backend", backends) @pytest.mark.parametrize("shape", [(4,),(2,2)]) def test_array_shape(backend, shape): @@ -25,7 +24,6 @@ def test_array_shape(backend, shape): result = tq.simulate(E, backend=backend) assert (result == expected).all() - @pytest.mark.parametrize("backend", backends) @pytest.mark.parametrize("shape", [(2,2)]) def test_array_contraction(backend, shape): From e9f302a822b6715292738374cbc7eda64dd1c804 Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Mon, 27 Mar 2023 15:57:08 -0400 Subject: [PATCH 09/13] Resolved some issues --- src/tequila/circuit/visualize.py | 12 +++++----- .../hamiltonian/custom_jw_transform.py | 24 ++++++++++++------- tests/test_visualize.py | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/tequila/circuit/visualize.py b/src/tequila/circuit/visualize.py index 0682a50c..61c8fdce 100644 --- a/src/tequila/circuit/visualize.py +++ b/src/tequila/circuit/visualize.py @@ -22,7 +22,7 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, One thing to note is that if you are using command-line interface, the plot might not be successfully shown - , so it's better idea to save it as an image. + so it's better idea to save it as an image. === parameters === qubit_hamiltonian: A QubitHamiltonian representation of pauli operators circuit: A QCircuit that corresponds to the @@ -36,21 +36,21 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, === sample usages === >>> visualize( - >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1))) + >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"))) *** A graph with nodes 0 and 5 having colour red and node 3 having colour green *** >>> visualize( - >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), - >>> circuit=gates.X(0) + gates.CNOT(0, 5) + gates.Y(3)) + >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"), + >>> circuit=tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3)) *** A graph with nodes 0 and 5 having color red and node 3 having colour green with edge 0 and 5 exists *** >>> visualize( - >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), + >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"), >>> connectivity=[(0, 0), (0, 5), (3, 3)]) *** A graph with nodes 0 and 5 having color red and node 3 having colour green with edge 0 and 5 exists *** >>> visualize( - >>> tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), + >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"), >>> connectivity=[(0, 0), (0, 5), (3, 3)], >>> file_name="test_system") *** Exported an image of a graph with nodes 0 and 5 having color red and diff --git a/src/tequila/hamiltonian/custom_jw_transform.py b/src/tequila/hamiltonian/custom_jw_transform.py index 7a5ac5a9..40d7aaaa 100644 --- a/src/tequila/hamiltonian/custom_jw_transform.py +++ b/src/tequila/hamiltonian/custom_jw_transform.py @@ -1,6 +1,7 @@ import typing from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian import tequila.hamiltonian.paulis as paulis +import openfermion def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: @@ -30,21 +31,28 @@ def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: qubit_hamiltonian = QubitHamiltonian() operator = fermion[0] site = qubits[int(operator[0])] + x_op = 'X(' + str(site) + ')' + pauli_x = QubitHamiltonian(x_op) + y_op = 'Y(' + str(site) + ')' + pauli_y = QubitHamiltonian(y_op) if fermion[-1] == '^': - pauli_z = QubitHamiltonian() + pauli_z = QubitHamiltonian("1") for qubit in range(0, int(operator)): - pauli_z *= paulis.Z(qubits[qubit]) + z_op = 'Z(' + str(qubits[qubit]) + ')' + pauli_z *= QubitHamiltonian(z_op) - qubit_hamiltonian += 0.5 * (pauli_z + paulis.X(site)) - qubit_hamiltonian += -0.5j * (pauli_z + paulis.Y(site)) + qubit_hamiltonian += pauli_z * pauli_x * 0.5 + + qubit_hamiltonian += pauli_z * pauli_y * -0.5j else: - pauli_z = QubitHamiltonian() + pauli_z = QubitHamiltonian("1") for qubit in range(0, int(operator)): - pauli_z *= paulis.Z(qubits[qubit]) + z_op = 'Z(' + str(qubits[qubit]) + ')' + pauli_z *= QubitHamiltonian(z_op) - qubit_hamiltonian += 0.5 * (pauli_z + paulis.X(site)) - qubit_hamiltonian += 0.5j * (pauli_z + paulis.Y(site)) + qubit_hamiltonian += pauli_z * pauli_x * 0.5 + qubit_hamiltonian += pauli_z * pauli_y * 0.5j return qubit_hamiltonian diff --git a/tests/test_visualize.py b/tests/test_visualize.py index dbf31e7e..141937b9 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualize.py @@ -24,7 +24,7 @@ @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh", tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1))) +@pytest.mark.parametrize("qh", [tequila.QubitHamiltonian("X(0)X(5)Y(3)")]) def test_visualize_with_qubit_hamiltonian(qh): fig = visualize(qh) assert plt.fignum_exists(fig.number) From 4a4545d7f0fccb778b5917bf84f2ad97ca65053e Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 28 Mar 2023 08:49:47 -0400 Subject: [PATCH 10/13] Resolved some issues --- tests/test_visualize.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_visualize.py b/tests/test_visualize.py index 141937b9..91557485 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualize.py @@ -5,6 +5,7 @@ import openfermion import matplotlib.pyplot as plt + if 'networkx' in sys.modules: NETWORKX_EXIST = True else: @@ -24,7 +25,7 @@ @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh", [tequila.QubitHamiltonian("X(0)X(5)Y(3)")]) +@pytest.mark.parametrize("qh", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"))]) def test_visualize_with_qubit_hamiltonian(qh): fig = visualize(qh) assert plt.fignum_exists(fig.number) From 60f811ef1567505702ca7da1bfab8667b5ac3b48 Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 28 Mar 2023 10:02:01 -0400 Subject: [PATCH 11/13] Test for visualization --- ...est_visualize.py => test_visualization.py} | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) rename tests/{test_visualize.py => test_visualization.py} (64%) diff --git a/tests/test_visualize.py b/tests/test_visualization.py similarity index 64% rename from tests/test_visualize.py rename to tests/test_visualization.py index 91557485..83787345 100644 --- a/tests/test_visualize.py +++ b/tests/test_visualization.py @@ -1,10 +1,11 @@ import pytest +import pytest from tequila.circuit.visualize import visualize import sys import tequila import openfermion import matplotlib.pyplot as plt - +import os if 'networkx' in sys.modules: NETWORKX_EXIST = True @@ -25,7 +26,7 @@ @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"))]) +@pytest.mark.parametrize("qh", [tequila.QubitHamiltonian("X(0)X(5)Y(3)")]) def test_visualize_with_qubit_hamiltonian(qh): fig = visualize(qh) assert plt.fignum_exists(fig.number) @@ -35,18 +36,17 @@ def test_visualize_with_qubit_hamiltonian(qh): @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize( - "qh", tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)) - + tequila.QubitHamiltonian(openfermion.QubitOperator("X0 Y3", 1))) + "qh", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)) + + tequila.QubitHamiltonian(openfermion.QubitOperator("X0 Y3", 1))]) def test_visualize_with_invalid_qubit_hamiltonian(qh): with pytest.raises(Exception) as e_info: visualize(qh) - @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, circuit", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3)]) -def test_visualize_with_circui(qh, circuit): +@pytest.mark.parametrize("qh, circuit", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3))]) +def test_visualize_with_circuit(qh, circuit): fig = visualize(qh, circuit=circuit) assert plt.fignum_exists(fig.number) @@ -54,34 +54,31 @@ def test_visualize_with_circui(qh, circuit): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, connectivity", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)]]) -def test_visualize_with_connectivity(qh, connectivity): - fig = visualize(qh, connectivity=connectivity) - assert plt.fignum_exists(fig.number) - - -@pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") -@pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), "test_file1"]) -def test_visualize_with_file(qh, file): +@pytest.mark.parametrize("qh, file", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), "test_file1")]) +def test_visualize_with_filepath(qh, file): fig = visualize(qh, file_path=file) assert plt.fignum_exists(fig.number) + if os.path.isfile(file+".png"): + os.remove(file+".png") @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, circuit, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2"]) +@pytest.mark.parametrize("qh, circuit, file", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2")]) def test_visualize_with_circuit_and_file(qh, circuit, file): fig = visualize(qh, circuit=circuit, file_path=file) - assert plt.fignum_exists(fig) + assert os.path.isfile(file+".png") and not plt.fignum_exists(fig) + if os.path.isfile(file+".png"): + os.remove(file+".png") @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, connectivity, file", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)], "test_file3"]) +@pytest.mark.parametrize("qh, connectivity, file", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)], "test_file3")]) def test_visualize_with_connectivity(qh, connectivity, file): fig = visualize(qh, connectivity=connectivity, file_path=file) assert plt.fignum_exists(fig.number) + if os.path.isfile(file+".png"): + os.remove(file+".png") From 1b1b401f234815a8d7bea1c1e4c292c9a5f727f6 Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 28 Mar 2023 10:22:44 -0400 Subject: [PATCH 12/13] Revised code accordingly to comments on pull request --- src/tequila/circuit/visualize.py | 51 +++++++++---------- .../hamiltonian/custom_jw_transform.py | 2 - tests/test_visualization.py | 28 +++------- 3 files changed, 32 insertions(+), 49 deletions(-) diff --git a/src/tequila/circuit/visualize.py b/src/tequila/circuit/visualize.py index 61c8fdce..ed11f821 100644 --- a/src/tequila/circuit/visualize.py +++ b/src/tequila/circuit/visualize.py @@ -1,23 +1,25 @@ import importlib.util - import tequila from typing import Optional, List -import matplotlib.pyplot as plt -import matplotlib.figure as figure -import networkx +if importlib.util.find_spec("networkx") is not None: + import networkx + +if importlib.util.find_spec("matplotlib") is not None: + import matplotlib.pyplot as plt + import matplotlib.figure as figure def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, circuit: Optional[tequila.QCircuit] = None, connectivity: Optional[List[tuple]] = None, - file_path: Optional[str] = None) -> figure.Figure: + filename: Optional[str] = None) -> figure.Figure: """ Precondition: The maximum number of qubits is 10 at the moment (Feb24, 2023) Post condition: The graph of the qubit_hamiltonian is displayed - or is stored in file_path + or is stored in filename One thing to note is that if you are using command-line interface, the plot might not be successfully shown @@ -28,38 +30,33 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, circuit: A QCircuit that corresponds to the connectivity: networkx.Graph.edges that show the connectivity of qubits A nested list should be provided for this argument. - file_name: str format of file name + filename: str format of file name to which the plotted graph will be exported. The file will be a png format === return === - None + matplotlib.figure === sample usages === - >>> visualize( - >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"))) + visualize(tequila.QubitHamiltonian("X(0)X(5)Y(3)"))) *** A graph with nodes 0 and 5 having colour red and node 3 having colour green *** - >>> visualize( - >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"), - >>> circuit=tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3)) + + visualize(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), + circuit=tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3)) *** A graph with nodes 0 and 5 having color red and node 3 having colour green with edge 0 and 5 exists *** - >>> visualize( - >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"), - >>> connectivity=[(0, 0), (0, 5), (3, 3)]) + + visualize(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), + connectivity=[(0, 0), (0, 5), (3, 3)]) *** A graph with nodes 0 and 5 having color red and node 3 having colour green with edge 0 and 5 exists *** - >>> visualize( - >>> tequila.QubitHamiltonian("X(0)X(5)Y(3)"), - >>> connectivity=[(0, 0), (0, 5), (3, 3)], - >>> file_name="test_system") + + visualize(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), + connectivity=[(0, 0), (0, 5), (3, 3)], + filename="test_system") *** Exported an image of a graph with nodes 0 and 5 having color red and node 3 having colour green with edge 0 and 5 exists to test_system.png *** """ - if importlib.util.find_spec("networkx") is None: - raise tequila.TequilaException("networkx module is not in the system") - if importlib.util.find_spec("matplotlib") is None: - raise tequila.TequilaException("matplotlib module is not in the system") if(len(qubit_hamiltonian.qubit_operator.terms)) != 1: raise tequila.TequilaException("The input qubit_operator" @@ -77,10 +74,10 @@ def visualize(qubit_hamiltonian: tequila.QubitHamiltonian, elif circuit is None: graph, pos = _visualize_helper(qh, graph, pos, connectivity) - if file_path is None: + if filename is None: return plt.figure() - if file_path is not None: - plt.savefig(file_path+".png", format="PNG") + if filename is not None: + plt.savefig(filename+".png", format="PNG") return plt.figure() diff --git a/src/tequila/hamiltonian/custom_jw_transform.py b/src/tequila/hamiltonian/custom_jw_transform.py index 40d7aaaa..616dc3d0 100644 --- a/src/tequila/hamiltonian/custom_jw_transform.py +++ b/src/tequila/hamiltonian/custom_jw_transform.py @@ -1,7 +1,5 @@ import typing from tequila.hamiltonian.qubit_hamiltonian import QubitHamiltonian -import tequila.hamiltonian.paulis as paulis -import openfermion def _custom_transform(fermion: str, qubits: list) -> QubitHamiltonian: diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 83787345..c79fb65d 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -1,10 +1,7 @@ import pytest -import pytest from tequila.circuit.visualize import visualize import sys import tequila -import openfermion -import matplotlib.pyplot as plt import os if 'networkx' in sys.modules: @@ -14,18 +11,14 @@ if 'matplotlib' in sys.modules: MATPLOTLIB_EXIST = True + import matplotlib.pyplot as plt else: MATPLOTLIB_EXIST = False - -if 'openfermion' in sys.modules: - OPENFERMION_EXIST = True -else: - OPENFERMION_EXIST = False + import matplotlib.pyplot as plt @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize("qh", [tequila.QubitHamiltonian("X(0)X(5)Y(3)")]) def test_visualize_with_qubit_hamiltonian(qh): fig = visualize(qh) @@ -34,18 +27,16 @@ def test_visualize_with_qubit_hamiltonian(qh): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") @pytest.mark.parametrize( - "qh", [tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)) - + tequila.QubitHamiltonian(openfermion.QubitOperator("X0 Y3", 1))]) + "qh", [tequila.QubitHamiltonian("X(0)X(5)Y(3)") + + tequila.QubitHamiltonian("X(0)Y(3)")]) def test_visualize_with_invalid_qubit_hamiltonian(qh): with pytest.raises(Exception) as e_info: visualize(qh) @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, circuit", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3))]) +@pytest.mark.parametrize("qh, circuit", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3))]) def test_visualize_with_circuit(qh, circuit): fig = visualize(qh, circuit=circuit) assert plt.fignum_exists(fig.number) @@ -53,8 +44,7 @@ def test_visualize_with_circuit(qh, circuit): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, file", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), "test_file1")]) +@pytest.mark.parametrize("qh, file", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), "test_file1")]) def test_visualize_with_filepath(qh, file): fig = visualize(qh, file_path=file) assert plt.fignum_exists(fig.number) @@ -64,8 +54,7 @@ def test_visualize_with_filepath(qh, file): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, circuit, file", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2")]) +@pytest.mark.parametrize("qh, circuit, file", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2")]) def test_visualize_with_circuit_and_file(qh, circuit, file): fig = visualize(qh, circuit=circuit, file_path=file) assert os.path.isfile(file+".png") and not plt.fignum_exists(fig) @@ -75,8 +64,7 @@ def test_visualize_with_circuit_and_file(qh, circuit, file): @pytest.mark.skipif(condition=not NETWORKX_EXIST, reason="You don't have networkx") @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") -@pytest.mark.skipif(condition=not OPENFERMION_EXIST, reason="You don't have openfermion") -@pytest.mark.parametrize("qh, connectivity, file", [(tequila.QubitHamiltonian(openfermion.QubitOperator("X0 X5 Y3", 1)), [(0, 0), (0, 5), (3, 3)], "test_file3")]) +@pytest.mark.parametrize("qh, connectivity, file", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), [(0, 0), (0, 5), (3, 3)], "test_file3")]) def test_visualize_with_connectivity(qh, connectivity, file): fig = visualize(qh, connectivity=connectivity, file_path=file) assert plt.fignum_exists(fig.number) From fbcd68142318129fb4796b62afa1dfb59491199f Mon Sep 17 00:00:00 2001 From: Rick0317 Date: Tue, 28 Mar 2023 10:24:43 -0400 Subject: [PATCH 13/13] parameter name changed --- tests/test_visualization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index c79fb65d..22e580a3 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -46,7 +46,7 @@ def test_visualize_with_circuit(qh, circuit): @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.parametrize("qh, file", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), "test_file1")]) def test_visualize_with_filepath(qh, file): - fig = visualize(qh, file_path=file) + fig = visualize(qh, filename=file) assert plt.fignum_exists(fig.number) if os.path.isfile(file+".png"): os.remove(file+".png") @@ -56,7 +56,7 @@ def test_visualize_with_filepath(qh, file): @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.parametrize("qh, circuit, file", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), tequila.gates.X(0) + tequila.gates.CNOT(0, 5) + tequila.gates.Y(3), "test_file2")]) def test_visualize_with_circuit_and_file(qh, circuit, file): - fig = visualize(qh, circuit=circuit, file_path=file) + fig = visualize(qh, circuit=circuit, filename=file) assert os.path.isfile(file+".png") and not plt.fignum_exists(fig) if os.path.isfile(file+".png"): os.remove(file+".png") @@ -66,7 +66,7 @@ def test_visualize_with_circuit_and_file(qh, circuit, file): @pytest.mark.skipif(condition=not MATPLOTLIB_EXIST, reason="You don't have matplotlib") @pytest.mark.parametrize("qh, connectivity, file", [(tequila.QubitHamiltonian("X(0)X(5)Y(3)"), [(0, 0), (0, 5), (3, 3)], "test_file3")]) def test_visualize_with_connectivity(qh, connectivity, file): - fig = visualize(qh, connectivity=connectivity, file_path=file) + fig = visualize(qh, connectivity=connectivity, filename=file) assert plt.fignum_exists(fig.number) if os.path.isfile(file+".png"): os.remove(file+".png")