Repository: Qiskit/qiskit

Issue:
`TaperedPauliSumOp` is incompatible with `VQE`
<!-- ⚠️ 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` @ 0c8bb3dbf8d688590431ca79a83ba8aede84ed20
- **Python version**: 3.8.8
- **Operating system**: Linux (Fedora 33): `uname -r: 5.10.23-200.fc33.x86_64`

### What is the current behavior?

Using a tapered `PauliSumOp` as the input to a `VQE` results in the following runtime error:
```python-traceback
Traceback (most recent call last):
  File "terra-bug-minimal.py", line 31, in <module>
    result = vqe.compute_minimum_eigenvalue(tapered_qubit_op)
  File "/home/oss/Files/Qiskit/src/qiskit-terra/qiskit/algorithms/minimum_eigen_solvers/vqe.py", line 412, in compute_minimum_eigenvalue
    self._expect_op = self.construct_expectation(self._var_form_params, operator)
  File "/home/oss/Files/Qiskit/src/qiskit-terra/qiskit/algorithms/minimum_eigen_solvers/vqe.py", line 300, in construct_expectation
    observable_meas = self.expectation.convert(StateFn(operator, is_measurement=True))
  File "/home/oss/Files/Qiskit/src/qiskit-terra/qiskit/opflow/expectations/matrix_expectation.py", line 38, in convert
    return operator.to_matrix_op()
  File "/home/oss/Files/Qiskit/src/qiskit-terra/qiskit/opflow/state_fns/operator_state_fn.py", line 119, in to_matrix_op
    return OperatorStateFn(self.primitive.to_matrix_op(massive=massive) * self.coeff,
  File "/home/oss/Files/Qiskit/src/qiskit-terra/qiskit/opflow/primitive_ops/primitive_op.py", line 244, in to_matrix_op
    prim_mat = self.__class__(self.primitive).to_matrix(massive=massive)
TypeError: __init__() missing 1 required positional argument: 'z2_symmetries'
```

### Steps to reproduce the problem

Below is a minimal code snippet to reproduce the issue:
```python
from qiskit import BasicAer
from qiskit.algorithms import VQE
from qiskit.circuit.library import TwoLocal
from qiskit.opflow import PauliSumOp, TwoQubitReduction

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)

backend = BasicAer.get_backend('statevector_simulator')
ryrz = TwoLocal(rotation_blocks=['ry', 'rz'], entanglement_blocks='cz')

vqe = VQE(ryrz, quantum_instance=backend)

result = vqe.compute_minimum_eigenvalue(tapered_qubit_op)
```

### What is the expected behavior?

I would expect that the `VQE` is able to compute the minimum eigenvalue just like for any other `opflow` operator type.

### Suggested solutions

A simple solution would be to make the `z2_symmetries` argument of `TaperedPauliSumOp.__init__` optional. However, this may lead to other unwanted side-effects.
Another possible solution would be to make the `TaperedPauliSumOp` look more like a `PauliSumOp` (at least in this context). But I am unsure how to achieve this given the `__init__` call which is raising the problem is obtained via `self.__class__`. And I don't think we should hack the behavior of this internal object. 


Discussion:
@ikkoham I am tagging you because (if I recall correctly) you mainly worked on the two-qubit-reduction and tapering code.

Resolving patch:
diff --git a/qiskit/opflow/operator_base.py b/qiskit/opflow/operator_base.py
--- a/qiskit/opflow/operator_base.py
+++ b/qiskit/opflow/operator_base.py
@@ -14,6 +14,7 @@
 
 import itertools
 from abc import ABC, abstractmethod
+from copy import deepcopy
 from typing import Dict, List, Optional, Set, Tuple, Union, cast
 
 import numpy as np
@@ -557,6 +558,10 @@ def _expand_shorter_operator_and_permute(self, other: 'OperatorBase',
                 new_self = self._expand_dim(other.num_qubits - self.num_qubits)
         return new_self, other
 
+    def copy(self) -> "OperatorBase":
+        """Return a deep copy of the Operator."""
+        return deepcopy(self)
+
     # Composition
 
     def __matmul__(self, other: 'OperatorBase') -> 'OperatorBase':
diff --git a/qiskit/opflow/primitive_ops/primitive_op.py b/qiskit/opflow/primitive_ops/primitive_op.py
--- a/qiskit/opflow/primitive_ops/primitive_op.py
+++ b/qiskit/opflow/primitive_ops/primitive_op.py
@@ -241,9 +241,12 @@ def to_matrix(self, massive: bool = False) -> np.ndarray:
 
     def to_matrix_op(self, massive: bool = False) -> OperatorBase:
         """ Returns a ``MatrixOp`` equivalent to this Operator. """
-        prim_mat = self.__class__(self.primitive).to_matrix(massive=massive)
+        coeff = self.coeff
+        op = self.copy()
+        op._coeff = 1
+        prim_mat = op.to_matrix(massive=massive)
         from .matrix_op import MatrixOp
-        return MatrixOp(prim_mat, coeff=self.coeff)
+        return MatrixOp(prim_mat, coeff=coeff)
 
     def to_instruction(self) -> Instruction:
         """ Returns an ``Instruction`` equivalent to this Operator. """


Test patch:
diff --git a/test/python/algorithms/test_vqe.py b/test/python/algorithms/test_vqe.py
--- a/test/python/algorithms/test_vqe.py
+++ b/test/python/algorithms/test_vqe.py
@@ -23,9 +23,9 @@
 from qiskit.circuit.library import TwoLocal, EfficientSU2
 from qiskit.utils import QuantumInstance, algorithm_globals
 from qiskit.exceptions import MissingOptionalLibraryError
-from qiskit.opflow import (PrimitiveOp, X, Z, I,
+from qiskit.opflow import (PrimitiveOp, X, Z, I, PauliSumOp,
                            AerPauliExpectation, PauliExpectation,
-                           MatrixExpectation, ExpectationBase)
+                           MatrixExpectation, ExpectationBase, TwoQubitReduction)
 from qiskit.algorithms.optimizers import L_BFGS_B, COBYLA, SPSA, SLSQP
 from qiskit.algorithms import VQE, AlgorithmError
 
@@ -233,6 +233,39 @@ def test_with_aer_qasm_snapshot_mode(self):
         result = vqe.compute_minimum_eigenvalue(operator=self.h2_op)
         self.assertAlmostEqual(result.eigenvalue.real, self.h2_energy, places=6)
 
+    def test_with_two_qubit_reduction(self):
+        """Test the VQE using TwoQubitReduction."""
+        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)
+        for simulator in [self.qasm_simulator, self.statevector_simulator]:
+            with self.subTest(f"Test for {simulator}."):
+                vqe = VQE(
+                    self.ry_wavefunction,
+                    SPSA(maxiter=300, last_avg=5),
+                    quantum_instance=simulator,
+                )
+                result = vqe.compute_minimum_eigenvalue(tapered_qubit_op)
+                energy = -1.868 if simulator == self.qasm_simulator else self.h2_energy
+                self.assertAlmostEqual(result.eigenvalue.real, energy, places=2)
+
     def test_callback(self):
         """Test the callback on VQE."""
         history = {'eval_count': [], 'parameters': [], 'mean': [], 'std': []}