Repository: Qiskit/qiskit

Issue:
Partial trace operation throws up error once it is broken 
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Informations

- **Qiskit Aer version**: 0.10.2
- **Python version**: 3.6.9
- **Operating system**: Mac OS 

### What is the current behavior?

See minimal code example below 



### Steps to reproduce the problem
```

# Import Qiskit
from qiskit import QuantumCircuit
import qiskit.quantum_info as qi
from qiskit import Aer, execute

simulator = Aer.get_backend('statevector_simulator', seed = 100)


qc_AB = QuantumCircuit(2)
qc_AB.h(0)
qc_AB.cx(0,1)

sv = execute(qc_AB, simulator).result().get_statevector()

rho_B = qi.partial_trace(sv,[0]) #trace out qubit 0
```

Now If I provide an empty list to trace out then it throws up an error which is expected 
```
rho_B = qi.partial_trace(sv, [])  

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-26-a145dac87eb0> in <module>
----> 1 rho_B = qi.partial_trace(sv,[])

~/PQKs/pqks/lib/python3.6/site-packages/qiskit/quantum_info/states/utils.py in partial_trace(state, qargs)
     56     if isinstance(state, Statevector):
     57         trace_systems = len(state._op_shape.dims_l()) - 1 - np.array(qargs)
---> 58         arr = state._data.reshape(state._op_shape.tensor_shape)
     59         rho = np.tensordot(arr, arr.conj(), axes=(trace_systems, trace_systems))
     60         rho = np.reshape(rho, traced_shape.shape)

ValueError: cannot reshape array of size 4 into shape (2,2,2,2)

```

After the error, if I now recall a previous instruction that successfully ran then the error reappears.

```
rho_B = qi.partial_trace(sv,[0]) #trace out qubit 0

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-27-8cbae0b90ecc> in <module>
----> 1 rho_B = qi.partial_trace(sv,[0])

~/PQKs/pqks/lib/python3.6/site-packages/qiskit/quantum_info/states/utils.py in partial_trace(state, qargs)
     56     if isinstance(state, Statevector):
     57         trace_systems = len(state._op_shape.dims_l()) - 1 - np.array(qargs)
---> 58         arr = state._data.reshape(state._op_shape.tensor_shape)
     59         rho = np.tensordot(arr, arr.conj(), axes=(trace_systems, trace_systems))
     60         rho = np.reshape(rho, traced_shape.shape)

ValueError: cannot reshape array of size 4 into shape (2,2,2,2)

```

It seems as if the `qi.partial_trace` operation is in some state that once it is broken, it cant function again?


If I use `.get_statevector().data` the above issue disappears. 


### What is the expected behavior?

Operation should not be state-bound and work regardless?


### Suggested solutions





Discussion:
I've transferred this to Terra, because the issue isn't related to Aer, it's just about `qiskit.quantum_info`, which is in Terra:
```python
In [1]: from qiskit.quantum_info import Statevector, partial_trace
   ...: sv = Statevector([0, np.sqrt(0.5), np.sqrt(0.5), 0])
   ...: try:
   ...:     partial_trace(sv, [])
   ...: except ValueError:
   ...:     pass
   ...: partial_trace(sv, [0])
```
gives
```python
ValueError                                Traceback (most recent call last)
Input In [1], in <module>
      5 except ValueError:
      6     pass
----> 7 partial_trace(sv, [0])

File ~/code/qiskit/terra/qiskit/quantum_info/states/utils.py:57, in partial_trace(state, qargs)
     55 if isinstance(state, Statevector):
     56     trace_systems = len(state._op_shape.dims_l()) - 1 - np.array(qargs)
---> 57     arr = state._data.reshape(state._op_shape.tensor_shape)
     58     rho = np.tensordot(arr, arr.conj(), axes=(trace_systems, trace_systems))
     59     rho = np.reshape(rho, traced_shape.shape)

ValueError: cannot reshape array of size 4 into shape (2,2,2,2)
```

I'm guessing that somewhere along the line, `Statevector._op_shape` gets mutated in place, and never fixed because of the exception.
Also, I'm not super convinced that passing an empty list should be an error - I'd have thought we should just return the density matrix of the state.

Resolving patch:
diff --git a/qiskit/quantum_info/operators/op_shape.py b/qiskit/quantum_info/operators/op_shape.py
--- a/qiskit/quantum_info/operators/op_shape.py
+++ b/qiskit/quantum_info/operators/op_shape.py
@@ -332,7 +332,7 @@ def subset(self, qargs=None, qargs_l=None, qargs_r=None):
         )
 
     def remove(self, qargs=None, qargs_l=None, qargs_r=None):
-        """Return the reduced OpShape with specified qargs removed"""
+        """Return a new :class:`OpShape` with the specified qargs removed"""
         if qargs:
             # Convert qargs to left and right qargs
             if qargs_l or qargs_r:
@@ -342,7 +342,7 @@ def remove(self, qargs=None, qargs_l=None, qargs_r=None):
             if self._num_qargs_r:
                 qargs_r = qargs
         if qargs_l is None and qargs_r is None:
-            return self
+            return self.copy()
 
         # Format integer qargs
         if isinstance(qargs_l, Integral):
diff --git a/qiskit/quantum_info/states/utils.py b/qiskit/quantum_info/states/utils.py
--- a/qiskit/quantum_info/states/utils.py
+++ b/qiskit/quantum_info/states/utils.py
@@ -60,6 +60,9 @@ def partial_trace(state, qargs):
         return DensityMatrix(rho, dims=traced_shape._dims_l)
 
     # Density matrix case
+    # Empty partial trace case.
+    if not qargs:
+        return state.copy()
     # Trace first subsystem to avoid coping whole density matrix
     dims = state.dims(qargs)
     tr_op = SuperOp(np.eye(dims[0]).reshape(1, dims[0] ** 2), input_dims=[dims[0]], output_dims=[1])


Test patch:
diff --git a/test/python/quantum_info/states/test_utils.py b/test/python/quantum_info/states/test_utils.py
--- a/test/python/quantum_info/states/test_utils.py
+++ b/test/python/quantum_info/states/test_utils.py
@@ -32,6 +32,7 @@ def test_statevector_partial_trace(self):
         self.assertEqual(partial_trace(psi, [0]), DensityMatrix.from_label("10"))
         self.assertEqual(partial_trace(psi, [1]), DensityMatrix.from_label("1+"))
         self.assertEqual(partial_trace(psi, [2]), DensityMatrix.from_label("0+"))
+        self.assertEqual(partial_trace(psi, []), DensityMatrix(psi))
 
     def test_density_matrix_partial_trace(self):
         """Test partial_trace function on density matrices"""
@@ -42,6 +43,7 @@ def test_density_matrix_partial_trace(self):
         self.assertEqual(partial_trace(rho, [0]), DensityMatrix.from_label("10"))
         self.assertEqual(partial_trace(rho, [1]), DensityMatrix.from_label("1+"))
         self.assertEqual(partial_trace(rho, [2]), DensityMatrix.from_label("0+"))
+        self.assertEqual(partial_trace(rho, []), rho)
 
     def test_shannon_entropy(self):
         """Test shannon_entropy function"""