Repository: Qiskit/qiskit

Issue:
`GlobalPhaseGate` generates an error when trying to generate `Statevector`
### Environment

- **Qiskit Terra version**: 0.24.0rc1
- **Python version**:  3.10
- **Operating system**: MacOS Monterrey


### What is happening?

`Statevector` class generates a `tuple index out of range` error when a quantum circuit includes an instance of the recently-added `GlobalPhaseGate`.

### How can we reproduce the issue?

```python
from math import pi
from qiskit import QuantumCircuit
from qiskit.circuit.library import GlobalPhaseGate
from qiskit.quantum_info import Statevector

qc = QuantumCircuit(1)
qc.append(GlobalPhaseGate(pi/2))
print(Statevector(qc))
```

### What should happen?

The issue seems to be related to how the operator used to evolve the initial state is being generated.  This operator uses the instruction `.to_matrix()` method, which for the `GlobalPhaseGate` results in a matrix  of shape (1,1), which is basically an array containing the global phase being added. For example:

```python
>>> from qiskit.circuit.library import GlobalPhaseGate
>>> from math import pi
>>> GlobalPhaseGate(pi/3).to_matrix()
array([[0.5+0.8660254j]])
```
For this to work correctly, the operator needs to be a diagonal matrix with the global phase value of shape (2^n_qubits, 2^n_qubits) instead of just the value itself.

### Any suggestions?

I would volunteer to help with this, but if I am not mistaken, the `to_matrix()` method is inherited from the `Gate` class, so I can't think of an easy solution other than treating `GlobalPhaseGate` as a special case.


Discussion:
Thanks for this report - there were bound to be some more issues with 0q operations that #8272 missed.

The matrix returned shouldn't be full shape (and can't - we don't pass that information to `to_matrix`).  I think the return value as things stand is acceptable, since it's returning a 2d array of shape `(2**n_qubits, 2**n_qubits)`.  It feels more like a problem with the handling of scalar values within `quantum_info`, I think; the code that explodes here is because there's an operator that's of shape `(1,)`, whereas it might want to be `(1, 1)`.  I haven't fully looked through the code / thought about things though.

*edit*: having looked a little further, `OpShape.shape` deliberately returns a one-element tuple when there's no qubits on the right-hand side so that it looks like a statevector.  I suspect then that the _proper_ fix is for it to detect the `num_qargs_l = num_qargs_r = 0` case, and reduce that to a returned scalar shape of `()`.  That will probably have some knock-on effects throughout the code-base, but it's probably the most correct thing for us to try.
To add on to my edit: the code in `Statevector._evolve_operator` would still need updating in that situation to handle the possibility of a scalar operator.
Hi @jakelishman. Sorry, I just realized that saying that "`GlobalPhaseGate` might need to be treated as a special case" maybe caused some confusion. I didn't mean to say that the `to_matrix()` method should take as an input the size of the system, or that it shouldn't return something different than what it already does; sorry about that. 

What I meant is that in `Statevector._evolve_instruction` the operator is generated using `Operator._instruction_to_matrix()`, which generates the matrix using the `to_matrix()` method of the instruction of interest. Since for the `GlobalPhaseGate` this is a (1, 1) matrix, the evolution is not working correctly because the operator should instead be of size (2^n, 2^n). So, somewhere along this chain, this needs to be fixed, perhaps exclusively for the `GlobalPhaseGate`.

One thing that it's worth pointing out, is that the correct matrix is generated when using the `Operator` class. For example:
```python
>>> from math import pi
>>> from qiskit import QuantumCircuit
>>> from qiskit.quantum_info import Operator
>>> from qiskit.circuit.library import GlobalPhaseGate
>>> qc = QuantumCircuit(2)
>>> qc.append(GlobalPhaseGate(pi/3))
>>> print(Operator(qc))
Operator([[0.5+0.8660254j, 0. +0.j       , 0. +0.j       , 0. +0.j       ],
          [0. +0.j       , 0.5+0.8660254j, 0. +0.j       , 0. +0.j       ],
          [0. +0.j       , 0. +0.j       , 0.5+0.8660254j, 0. +0.j       ],
          [0. +0.j       , 0. +0.j       , 0. +0.j       , 0.5+0.8660254j]],
         input_dims=(2, 2), output_dims=(2, 2))

```
What I noticed is that when creating an `Operator` object, a matrix of the size of the system is first created, and then the overall operator is constructed using `Operator.compose`, which checks that the dimensions are compatible, generating the right output. So, something similar would have to be done when trying to generate a statevector.


My point is that the global phase gate _is_ of the correct size already - it's a 0q operator, so it matches your definition, and `Operator` working correctly shows that. `Statevector` has slightly different handling of the `OpShape`, and that's what made me notice that the 0q case is being handled as "vector-like" instead of "operator-like". This coincidentally doesn't cause bugs with the construction of an `Operator`, but it is still part of the underlying issue.

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
@@ -123,6 +123,9 @@ def num_qargs(self):
     @property
     def shape(self):
         """Return a tuple of the matrix shape"""
+        if self._num_qargs_l == self._num_qargs_r == 0:
+            # Scalar shape is op-like
+            return (1, 1)
         if not self._num_qargs_r:
             # Vector shape
             return (self._dim_l,)


Test patch:
diff --git a/test/python/quantum_info/states/test_statevector.py b/test/python/quantum_info/states/test_statevector.py
--- a/test/python/quantum_info/states/test_statevector.py
+++ b/test/python/quantum_info/states/test_statevector.py
@@ -24,7 +24,7 @@
 from qiskit import QiskitError
 from qiskit import QuantumRegister, QuantumCircuit
 from qiskit import transpile
-from qiskit.circuit.library import HGate, QFT
+from qiskit.circuit.library import HGate, QFT, GlobalPhaseGate
 from qiskit.providers.basicaer import QasmSimulatorPy
 
 from qiskit.quantum_info.random import random_unitary, random_statevector, random_pauli
@@ -186,6 +186,13 @@ def test_from_circuit(self):
         psi = Statevector.from_instruction(circuit)
         self.assertEqual(psi, target)
 
+        # Test 0q instruction
+        target = Statevector([1j, 0])
+        circuit = QuantumCircuit(1)
+        circuit.append(GlobalPhaseGate(np.pi / 2), [], [])
+        psi = Statevector.from_instruction(circuit)
+        self.assertEqual(psi, target)
+
     def test_from_instruction(self):
         """Test initialization from an instruction."""
         target = np.dot(HGate().to_matrix(), [1, 0])