Repository: Qiskit/qiskit

Issue:
More incompatibilities of `TaperedPauliSumOp`
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: master @ 28c543ae
- **Python version**: 3.8.8
- **Operating system**: Linux, Fedora 33: `uname -r: 5.11.10-200.fc33.x86_64`

### What is the current behavior?

`TaperedPauliSumOp` cannot be multiplied with another `OperatorBase`.

### Steps to reproduce the problem

Try the following snippet:
```python
from qiskit.circuit import Parameter
from qiskit.opflow import PauliSumOp, TwoQubitReduction
from qiskit.opflow.evolutions import PauliTrotterEvolution

qubit_op = PauliSumOp.from_list([
    ("IIII", -0.8105479805373266),
    ("IIIZ", 0.17218393261915552),
    ("IIZZ", -0.22575349222402472),
    ("IZZI", 0.1721839326191556),
    ("ZZII", -0.22575349222402466),
    ("IIZI", 0.1209126326177663),
    ("IZZZ", 0.16892753870087912),
    ("IXZX", -0.045232799946057854),
    ("ZXIX", 0.045232799946057854),
    ("IXIX", 0.045232799946057854),
    ("ZXZX", -0.045232799946057854),
    ("ZZIZ", 0.16614543256382414),
    ("IZIZ", 0.16614543256382414),
    ("ZZZZ", 0.17464343068300453),
    ("ZIZI", 0.1209126326177663),
])

tapered_qubit_op = TwoQubitReduction(num_particles=2).convert(qubit_op)

param = Parameter('c')
param * tapered_qubit_op
```


### What is the expected behavior?

The multiplication should work just fine.

### Suggested solutions

Since this is very similar error to #6100 I suggest to find a more general solution which makes `TaperedPauliSumOp` generally compatible. I am quite positive that otherwise more such problems will arise.

More incompatibilities of `TaperedPauliSumOp`
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: master @ 28c543ae
- **Python version**: 3.8.8
- **Operating system**: Linux, Fedora 33: `uname -r: 5.11.10-200.fc33.x86_64`

### What is the current behavior?

`TaperedPauliSumOp` cannot be multiplied with another `OperatorBase`.

### Steps to reproduce the problem

Try the following snippet:
```python
from qiskit.circuit import Parameter
from qiskit.opflow import PauliSumOp, TwoQubitReduction
from qiskit.opflow.evolutions import PauliTrotterEvolution

qubit_op = PauliSumOp.from_list([
    ("IIII", -0.8105479805373266),
    ("IIIZ", 0.17218393261915552),
    ("IIZZ", -0.22575349222402472),
    ("IZZI", 0.1721839326191556),
    ("ZZII", -0.22575349222402466),
    ("IIZI", 0.1209126326177663),
    ("IZZZ", 0.16892753870087912),
    ("IXZX", -0.045232799946057854),
    ("ZXIX", 0.045232799946057854),
    ("IXIX", 0.045232799946057854),
    ("ZXZX", -0.045232799946057854),
    ("ZZIZ", 0.16614543256382414),
    ("IZIZ", 0.16614543256382414),
    ("ZZZZ", 0.17464343068300453),
    ("ZIZI", 0.1209126326177663),
])

tapered_qubit_op = TwoQubitReduction(num_particles=2).convert(qubit_op)

param = Parameter('c')
param * tapered_qubit_op
```


### What is the expected behavior?

The multiplication should work just fine.

### Suggested solutions

Since this is very similar error to #6100 I suggest to find a more general solution which makes `TaperedPauliSumOp` generally compatible. I am quite positive that otherwise more such problems will arise.



Discussion:
Thanks. 
I am sorry. I didn't think TaperedPauliSumOp with parameter exists.
I recognized `TaperedPauliSumOp.assign_parameters()` also doesn't work.

My question: Is Z2_symmetry invariant under parameter multiplication?
I would assume so :thinking: I found this bug while trying to combine a `TaperedPauliSumOp` with our new `EvolvedOperatorAnsatz` in Nature. There, an error occured on the following line: https://github.com/Qiskit/qiskit-nature/blob/master/qiskit_nature/circuit/library/ansatzes/evolved_operator_ansatz.py#L162

A more extensive example which should work once this issue is resolved is given in this test: https://github.com/Qiskit/qiskit-nature/blob/master/test/algorithms/ground_state_solvers/test_advanced_ucc_variants.py#L75

@pbark @woodsp-ibm Please correct me, if I am wrong about the invariance under parameter multiplication..
If invariant, `parm * tapered_qubit_op` can be TaperedPauliSumOp which has same z2_symmetries. If not, `parm * tapered_qubit_op` must be `PauliSumOp`.
Thanks. 
I am sorry. I didn't think TaperedPauliSumOp with parameter exists.
I recognized `TaperedPauliSumOp.assign_parameters()` also doesn't work.

My question: Is Z2_symmetry invariant under parameter multiplication?
I would assume so :thinking: I found this bug while trying to combine a `TaperedPauliSumOp` with our new `EvolvedOperatorAnsatz` in Nature. There, an error occured on the following line: https://github.com/Qiskit/qiskit-nature/blob/master/qiskit_nature/circuit/library/ansatzes/evolved_operator_ansatz.py#L162

A more extensive example which should work once this issue is resolved is given in this test: https://github.com/Qiskit/qiskit-nature/blob/master/test/algorithms/ground_state_solvers/test_advanced_ucc_variants.py#L75

@pbark @woodsp-ibm Please correct me, if I am wrong about the invariance under parameter multiplication..
If invariant, `parm * tapered_qubit_op` can be TaperedPauliSumOp which has same z2_symmetries. If not, `parm * tapered_qubit_op` must be `PauliSumOp`.

Resolving patch:
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
@@ -127,7 +127,7 @@ def mul(self, scalar: Union[complex, ParameterExpression]) -> OperatorBase:
         if isinstance(scalar, (int, float, complex)) and scalar != 0:
             return PauliSumOp(scalar * self.primitive, coeff=self.coeff)
 
-        return super().mul(scalar)
+        return PauliSumOp(self.primitive, coeff=self.coeff * scalar)
 
     def adjoint(self) -> "PauliSumOp":
         return PauliSumOp(self.primitive.adjoint(), coeff=self.coeff.conjugate())
diff --git a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py
--- a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py
+++ b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py
@@ -67,6 +67,10 @@ def z2_symmetries(self) -> "Z2Symmetries":
         """
         return self._z2_symmetries
 
+    def assign_parameters(self, param_dict: dict) -> OperatorBase:
+        pauli_sum = PauliSumOp(self.primitive, self.coeff)  # pylint: disable=no-member
+        return pauli_sum.assign_parameters(param_dict)
+
 
 class Z2Symmetries:
     """ Z2 Symmetries """


Test patch:
diff --git a/test/python/opflow/test_tapered_pauli.py b/test/python/opflow/test_tapered_pauli.py
new file mode 100644
--- /dev/null
+++ b/test/python/opflow/test_tapered_pauli.py
@@ -0,0 +1,57 @@
+# This code is part of Qiskit.
+#
+# (C) Copyright IBM 2021.
+#
+# This code is licensed under the Apache License, Version 2.0. You may
+# obtain a copy of this license in the LICENSE.txt file in the root directory
+# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
+#
+# Any modifications or derivative works of this code must retain this
+# copyright notice, and modified files need to carry a notice indicating
+# that they have been altered from the originals.
+
+""" Test TaperedPauliSumOp """
+
+import unittest
+from test.python.opflow import QiskitOpflowTestCase
+
+from qiskit.circuit import Parameter
+from qiskit.opflow import PauliSumOp, TaperedPauliSumOp, Z2Symmetries
+from qiskit.quantum_info import Pauli, SparsePauliOp
+
+
+class TestZ2Symmetries(QiskitOpflowTestCase):
+    """Z2Symmetries tests."""
+
+    def setUp(self):
+        super().setUp()
+        z2_symmetries = Z2Symmetries(
+            [Pauli("IIZI"), Pauli("ZIII")], [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1]
+        )
+        self.primitive = SparsePauliOp.from_list(
+            [
+                ("II", (-1.052373245772859)),
+                ("ZI", (-0.39793742484318007)),
+                ("IZ", (0.39793742484318007)),
+                ("ZZ", (-0.01128010425623538)),
+                ("XX", (0.18093119978423142)),
+            ]
+        )
+        self.tapered_qubit_op = TaperedPauliSumOp(self.primitive, z2_symmetries)
+
+    def test_multiply_parameter(self):
+        """ test for multiplication of parameter """
+        param = Parameter("c")
+        expected = PauliSumOp(self.primitive, coeff=param)
+        self.assertEqual(param * self.tapered_qubit_op, expected)
+
+    def test_assign_parameters(self):
+        """ test assign_parameters """
+        param = Parameter("c")
+        parameterized_op = param * self.tapered_qubit_op
+        expected = PauliSumOp(self.primitive, coeff=46)
+        self.assertEqual(parameterized_op.assign_parameters({param: 46}), expected)
+
+
+if __name__ == '__main__':
+    unittest.main()