Repository: Qiskit/qiskit

Issue:
Error converting EvolvedOp to instruction
### Environment

- **Qiskit Terra version**: cffbb849344a501be624a75e64dcf757a41a6921
- **Python version**: 3.9.6
- **Operating system**: RHEL 8.5


### What is happening?

Attempting to convert an EvolvedOp to an instruction raises `ExtensionError: 'Input matrix is not unitary.'`

### How can we reproduce the issue?

```python
from qiskit.opflow import EvolvedOp, X

op = EvolvedOp(0.5 * X)
op.to_instruction()
```
```
---------------------------------------------------------------------------
ExtensionError                            Traceback (most recent call last)
<ipython-input-1-59021ff152db> in <module>
      2 
      3 op = EvolvedOp(0.5 * X)
----> 4 op.to_instruction()

~/projects/qiskit-terra/qiskit/opflow/evolutions/evolved_op.py in to_instruction(self, massive)
    173         if not isinstance(mat_op, MatrixOp):
    174             raise OpflowError("to_instruction is not allowed for ListOp.")
--> 175         return mat_op.to_instruction()

~/projects/qiskit-terra/qiskit/opflow/primitive_ops/matrix_op.py in to_instruction(self)
    229 
    230     def to_instruction(self) -> Instruction:
--> 231         return (self.coeff * self.primitive).to_instruction()

~/projects/qiskit-terra/qiskit/quantum_info/operators/operator.py in to_instruction(self)
    263         from qiskit.extensions.unitary import UnitaryGate
    264 
--> 265         return UnitaryGate(self.data)
    266 
    267     def conjugate(self):

~/projects/qiskit-terra/qiskit/extensions/unitary.py in __init__(self, data, label)
     83         # Check input is unitary
     84         if not is_unitary_matrix(data):
---> 85             raise ExtensionError("Input matrix is not unitary.")
     86         # Check input is N-qubit matrix
     87         input_dim, output_dim = data.shape

ExtensionError: 'Input matrix is not unitary.'
```

### What should happen?

It should work.

### Any suggestions?

Apparently `to_instruction` is trying to convert `op.primitive` instead of `op` and failing because `op.primitive` is not unitary. For EvolvedOp, `op` and `op.primitive` represent different things (`op.primitive` is the logarithm of `op`) so this logic is incorrect.


Discussion:
The `EvolvedOp` is only representing an object which still needs to be evolved. So you'll have to convert it using an `EvolutionBase` before you can ask for the circuit/convert it to an instruction. For example
```python
from qiskit.opflow import EvolvedOp, X, PauliTrotterEvolution

to_evolve = EvolvedOp(0.5 * X)
evolved = PauliTrotterEvolution().convert(to_evolve)
evolved.to_instruction()
```
So this behavior is actually expected, but we definitely should improve documentation if it differs from the behavior you expected 🙂 
The following code works, and returns a matrix representation of the exponentiated operator:
```python
from qiskit.opflow import EvolvedOp, X

op = EvolvedOp(0.5 * X)
op.to_matrix_op()
```
According to [the documentation](https://qiskit.org/documentation/stubs/qiskit.opflow.evolutions.EvolvedOp.html), `to_matrix_op` and `to_instruction` are both supposed to return an object "equivalent to this Operator":
![image](https://user-images.githubusercontent.com/4296166/167194382-4fe4a7a9-1856-49bf-a3b7-a53fb249eb7a.png)
This documentation is inherited from the PrimitiveOp base class. So the behavior of `to_matrix_op` and `to_instruction` should be consistent with each other (or else the documentation is incorrect and should be fixed).

Resolving patch:
diff --git a/qiskit/opflow/evolutions/evolved_op.py b/qiskit/opflow/evolutions/evolved_op.py
--- a/qiskit/opflow/evolutions/evolved_op.py
+++ b/qiskit/opflow/evolutions/evolved_op.py
@@ -169,7 +169,7 @@ def log_i(self, massive: bool = False) -> OperatorBase:
 
     # pylint: disable=arguments-differ
     def to_instruction(self, massive: bool = False) -> Instruction:
-        mat_op = self.primitive.to_matrix_op(massive=massive)
+        mat_op = self.to_matrix_op(massive=massive)
         if not isinstance(mat_op, MatrixOp):
             raise OpflowError("to_instruction is not allowed for ListOp.")
         return mat_op.to_instruction()


Test patch:
diff --git a/test/python/opflow/test_evolution.py b/test/python/opflow/test_evolution.py
--- a/test/python/opflow/test_evolution.py
+++ b/test/python/opflow/test_evolution.py
@@ -19,6 +19,7 @@
 
 import qiskit
 from qiskit.circuit import Parameter, ParameterVector
+from qiskit.extensions import UnitaryGate
 from qiskit.opflow import (
     CX,
     CircuitOp,
@@ -364,6 +365,19 @@ def test_suzuki_directly(self):
         )
         np.testing.assert_array_almost_equal(evolution.to_matrix(), matrix)
 
+    def test_evolved_op_to_instruction(self):
+        """Test calling `to_instruction` on a plain EvolvedOp.
+
+        Regression test of Qiskit/qiskit-terra#8025.
+        """
+        op = EvolvedOp(0.5 * X)
+        circuit = op.to_instruction()
+
+        unitary = scipy.linalg.expm(-0.5j * X.to_matrix())
+        expected = UnitaryGate(unitary)
+
+        self.assertEqual(circuit, expected)
+
 
 if __name__ == "__main__":
     unittest.main()