Repository: Qiskit/qiskit

Issue:
`RecursionError` when calling `SummedOp.to_pauli_op()`
This
```python
from qiskit.opflow import I, X, Z
op = ((Z + I) ^ Z) + (Z ^ X)
op.to_pauli_op()
```
fails with `RecursionError: maximum recursion depth exceeded while calling a Python object`

The error occurs in this method
https://github.com/Qiskit/qiskit-terra/blob/0c8bb3dbf8d688590431ca79a83ba8aede84ed20/qiskit/opflow/list_ops/summed_op.py#L174-L187

In this case, one can see that `pauli_sum` is `==` to `self`, so the last line calls the method again with the same (implicit) argument.

See also #5733

* Terra version 0c8bb3db
`RecursionError` when calling `SummedOp.to_pauli_op()`
This
```python
from qiskit.opflow import I, X, Z
op = ((Z + I) ^ Z) + (Z ^ X)
op.to_pauli_op()
```
fails with `RecursionError: maximum recursion depth exceeded while calling a Python object`

The error occurs in this method
https://github.com/Qiskit/qiskit-terra/blob/0c8bb3dbf8d688590431ca79a83ba8aede84ed20/qiskit/opflow/list_ops/summed_op.py#L174-L187

In this case, one can see that `pauli_sum` is `==` to `self`, so the last line calls the method again with the same (implicit) argument.

See also #5733

* Terra version 0c8bb3db


Discussion:
I just found this issue (sorry for late).

This bug can be splited into two parts. 
1. `((Z + I) ^ Z) + (Z ^ X)` should be PauliSumOp. We need to enhance Pauli.tensor() and PauliSumOp.tensor().
2. Fix the error of the `to_pauli_op()`.
I just found this issue (sorry for late).

This bug can be splited into two parts. 
1. `((Z + I) ^ Z) + (Z ^ X)` should be PauliSumOp. We need to enhance Pauli.tensor() and PauliSumOp.tensor().
2. Fix the error of the `to_pauli_op()`.

Resolving patch:
diff --git a/qiskit/opflow/list_ops/summed_op.py b/qiskit/opflow/list_ops/summed_op.py
--- a/qiskit/opflow/list_ops/summed_op.py
+++ b/qiskit/opflow/list_ops/summed_op.py
@@ -184,6 +184,8 @@ def to_pauli_op(self, massive: bool = False) -> "SummedOp":
             coeff=self.coeff,
             abelian=self.abelian,
         ).reduce()
+        if isinstance(pauli_sum, SummedOp):
+            return pauli_sum
         return pauli_sum.to_pauli_op()  # type: ignore
 
     def equals(self, other: OperatorBase) -> bool:
diff --git a/qiskit/opflow/primitive_ops/pauli_op.py b/qiskit/opflow/primitive_ops/pauli_op.py
--- a/qiskit/opflow/primitive_ops/pauli_op.py
+++ b/qiskit/opflow/primitive_ops/pauli_op.py
@@ -99,11 +99,14 @@ def _expand_dim(self, num_qubits: int) -> 'PauliOp':
     def tensor(self, other: OperatorBase) -> OperatorBase:
         # Both Paulis
         if isinstance(other, PauliOp):
-            # Copying here because Terra's Pauli kron is in-place.
-            op_copy = Pauli((other.primitive.z, other.primitive.x))
-            return PauliOp(self.primitive.tensor(op_copy), coeff=self.coeff * other.coeff)
+            return PauliOp(self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff)
 
         # pylint: disable=cyclic-import
+        from .pauli_sum_op import PauliSumOp
+        if isinstance(other, PauliSumOp):
+            new_primitive = SparsePauliOp(self.primitive).tensor(other.primitive)
+            return PauliSumOp(new_primitive, coeff=self.coeff * other.coeff)
+
         from .circuit_op import CircuitOp
         if isinstance(other, CircuitOp):
             return self.to_circuit_op().tensor(other)
diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py
--- a/qiskit/opflow/primitive_ops/pauli_sum_op.py
+++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py
@@ -162,6 +162,11 @@ def tensor(self, other: OperatorBase) -> Union["PauliSumOp", TensoredOp]:
                 self.primitive.tensor(other.primitive),
                 coeff=self.coeff * other.coeff,
             )
+        if isinstance(other, PauliOp):
+            return PauliSumOp(
+                self.primitive.tensor(other.primitive),
+                coeff=self.coeff * other.coeff,
+            )
 
         return TensoredOp([self, other])
 


Test patch:
diff --git a/test/python/opflow/test_op_construction.py b/test/python/opflow/test_op_construction.py
--- a/test/python/opflow/test_op_construction.py
+++ b/test/python/opflow/test_op_construction.py
@@ -397,6 +397,11 @@ def test_summed_op_reduce(self):
         with self.subTest('SummedOp test 9'):
             self.assertEqual(sum_op.reduce(), sum_op)
 
+        sum_op = ((Z + I) ^ Z) + (Z ^ X)
+        with self.subTest('SummedOp test 10'):
+            expected = SummedOp([PauliOp(Pauli('ZZ')), PauliOp(Pauli('IZ')), PauliOp(Pauli('ZX'))])
+            self.assertEqual(sum_op.to_pauli_op(), expected)
+
     def test_compose_op_of_different_dim(self):
         """
         Test if smaller operator expands to correct dim when composed with bigger operator.
diff --git a/test/python/opflow/test_pauli_sum_op.py b/test/python/opflow/test_pauli_sum_op.py
--- a/test/python/opflow/test_pauli_sum_op.py
+++ b/test/python/opflow/test_pauli_sum_op.py
@@ -127,18 +127,29 @@ def test_equals(self):
 
     def test_tensor(self):
         """ Test for tensor operation """
-        pauli_sum = ((I - Z) ^ (I - Z)) + ((X - Y) ^ (X + Y))
-        expected = (
-            (I ^ I)
-            - (I ^ Z)
-            - (Z ^ I)
-            + (Z ^ Z)
-            + (X ^ X)
-            + (X ^ Y)
-            - (Y ^ X)
-            - (Y ^ Y)
-        )
-        self.assertEqual(pauli_sum, expected)
+        with self.subTest("Test 1"):
+            pauli_sum = ((I - Z) ^ (I - Z)) + ((X - Y) ^ (X + Y))
+            expected = (
+                (I ^ I)
+                - (I ^ Z)
+                - (Z ^ I)
+                + (Z ^ Z)
+                + (X ^ X)
+                + (X ^ Y)
+                - (Y ^ X)
+                - (Y ^ Y)
+            )
+            self.assertEqual(pauli_sum, expected)
+
+        with self.subTest("Test 2"):
+            pauli_sum = ((Z + I) ^ Z)
+            expected = (Z ^ Z) + (I ^ Z)
+            self.assertEqual(pauli_sum, expected)
+
+        with self.subTest("Test 3"):
+            pauli_sum = (Z ^ (Z + I))
+            expected = (Z ^ Z) + (Z ^ I)
+            self.assertEqual(pauli_sum, expected)
 
     def test_permute(self):
         """ permute test """