From da31cc47834408f2657f7ff6fe5ef5468ccc2a2e Mon Sep 17 00:00:00 2001 From: woodsp Date: Fri, 12 Nov 2021 12:12:10 -0500 Subject: [PATCH 1/2] Fix tapering of zero/empty operators --- qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py | 12 ++++++------ .../taper_empty_operator_fix-53ce20e5d2b68fd6.yaml | 11 +++++++++++ test/python/opflow/test_z2_symmetries.py | 11 +++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/taper_empty_operator_fix-53ce20e5d2b68fd6.yaml diff --git a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py index f1ebef3fb3c2..3ec4ab2943a0 100644 --- a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py @@ -360,12 +360,12 @@ def taper(self, operator: PauliSumOp) -> OperatorBase: "Z2 symmetries, single qubit pauli and single qubit list cannot be empty." ) - if operator.is_zero(): - logger.warning("The operator is empty, return the empty operator directly.") - return operator - - for clifford in self.cliffords: - operator = cast(PauliSumOp, clifford @ operator @ clifford) + # If the operator is zero then we can skip the following. We still need to taper the + # operator to reduce its size i.e. the number of qubits so for example 0*"IIII" could + # taper to 0*"II" when symmetries remove two qubits. + if not operator.is_zero(): + for clifford in self.cliffords: + operator = cast(PauliSumOp, clifford @ operator @ clifford) if self._tapering_values is None: tapered_ops_list = [ diff --git a/releasenotes/notes/taper_empty_operator_fix-53ce20e5d2b68fd6.yaml b/releasenotes/notes/taper_empty_operator_fix-53ce20e5d2b68fd6.yaml new file mode 100644 index 000000000000..16e49452d3b2 --- /dev/null +++ b/releasenotes/notes/taper_empty_operator_fix-53ce20e5d2b68fd6.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + When tapering an empty/zero operator, the code, on detecting it was zero, logged a + warning and returned the original operator. Such operators are commonly found in + the auxiliary operators, when using Qiskit Nature, and the above behavior caused VQE + to throw an exception as tapered non-zero operators were a different number of qubits + from the tapered zero operators (since taper has returned the input operator unchanged). + The code will now correctly taper a zero operator such that the number of qubits is + reduced as expected and matches to tapered non-zero operators e.g 0*"IIII" when we are + tapering by 3 qubits will become 0*"I". diff --git a/test/python/opflow/test_z2_symmetries.py b/test/python/opflow/test_z2_symmetries.py index fb6b654e534b..0a5f63b9f99f 100644 --- a/test/python/opflow/test_z2_symmetries.py +++ b/test/python/opflow/test_z2_symmetries.py @@ -55,3 +55,14 @@ def test_find_Z2_symmetries(self): ) expected_op = TaperedPauliSumOp(primitive, z2_symmetries) self.assertEqual(tapered_op, expected_op) + + def test_taper_empty_operator(self): + """Test tapering of empty operator""" + z2_symmetries = Z2Symmetries(symmetries=[Pauli('IIZI'), Pauli('IZIZ'), Pauli('ZIII')], + sq_paulis=[Pauli('IIXI'), Pauli('IIIX'), Pauli('XIII')], + sq_list=[1, 0, 3], + tapering_values=[1, -1, -1]) + empty_op = PauliSumOp.from_list([("IIII", 0.0)]) + tapered_op = z2_symmetries.taper(empty_op) + expected_op = PauliSumOp.from_list([("I", 0.0)]) + self.assertEqual(tapered_op, expected_op) From 73666df4b3e6e63930733050ad28264427972241 Mon Sep 17 00:00:00 2001 From: woodsp Date: Fri, 12 Nov 2021 12:30:20 -0500 Subject: [PATCH 2/2] Forgot to run black! --- test/python/opflow/test_z2_symmetries.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/python/opflow/test_z2_symmetries.py b/test/python/opflow/test_z2_symmetries.py index 0a5f63b9f99f..d46f4b6b0e8a 100644 --- a/test/python/opflow/test_z2_symmetries.py +++ b/test/python/opflow/test_z2_symmetries.py @@ -58,10 +58,12 @@ def test_find_Z2_symmetries(self): def test_taper_empty_operator(self): """Test tapering of empty operator""" - z2_symmetries = Z2Symmetries(symmetries=[Pauli('IIZI'), Pauli('IZIZ'), Pauli('ZIII')], - sq_paulis=[Pauli('IIXI'), Pauli('IIIX'), Pauli('XIII')], - sq_list=[1, 0, 3], - tapering_values=[1, -1, -1]) + z2_symmetries = Z2Symmetries( + symmetries=[Pauli("IIZI"), Pauli("IZIZ"), Pauli("ZIII")], + sq_paulis=[Pauli("IIXI"), Pauli("IIIX"), Pauli("XIII")], + sq_list=[1, 0, 3], + tapering_values=[1, -1, -1], + ) empty_op = PauliSumOp.from_list([("IIII", 0.0)]) tapered_op = z2_symmetries.taper(empty_op) expected_op = PauliSumOp.from_list([("I", 0.0)])