Repository: Qiskit/qiskit

Issue:
qpy roundtrip for FourierChecking fails with TypeError: DiagonalGate.__init__() takes 2 positional arguments but 5 were given
### Environment

- **Qiskit Terra version**: 0.24.1
- **Python version**: 3.10
- **Operating system**: NixOS 23.11


### What is happening?

This is likely because the named gate (`DiagonalGate`) is not explicitly supported in the gates in the following code branch: https://github.com/Qiskit/qiskit-terra/blob/e9f8b7c50968501e019d0cb426676ac606eb5a10/qiskit/qpy/binary_io/circuits.py#L282-L289

### How can we reproduce the issue?

```python
import io
from qiskit import qpy
import qiskit.circuit.library as qcl

def encode(qc):
    with io.BytesIO() as f:
        qpy.dump(qc, f)
        # Rewind back to the beginning of the "file".
        f.seek(0)
        serialized = f.read()
    return serialized

def decode(data):
    with io.BytesIO(data) as f:
        circuits = qpy.load(f)
    return circuits[0]

# Works
decode(encode(qcl.QFT(3)))
# Doesn't work
decode(encode(qcl.FourierChecking([1, -1, -1, -1], [1, 1, -1, -1])))
```

### What should happen?

The error message should have said that the `DiagonalGate` is not supported when it is not covered by the if-else checks in the `_read_instruction`.

### Any suggestions?

_No response_


Discussion:
The traceback:
```
Traceback (most recent call last):
  File "/qiskit_bug_report.py", line 21, in <module>
    decode(encode(qcl.FourierChecking([1, -1, -1, -1], [1, 1, -1, -1])))
  File "/qiskit_bug_report.py", line 15, in decode
    circuits = qpy.load(f)
  File "/home/rht/code/venv/lib/python3.10/site-packages/qiskit/qpy/interface.py", line 269, in load
    loader(
  File "/home/rht/code/venv/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 932, in read_circuit
    custom_operations = _read_custom_operations(file_obj, version, vectors)
  File "/home/rht/code/venv/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 423, in _read_custom_operations
    definition_circuit = common.data_from_binary(
  File "/home/rht/code/venv/lib/python3.10/site-packages/qiskit/qpy/common.py", line 261, in data_from_binary
    obj = deserializer(container, **kwargs)
  File "/home/rht/code/venv/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 934, in read_circuit
    _read_instruction(file_obj, circ, out_registers, custom_operations, version, vectors)
  File "/home/rht/code/venv/lib/python3.10/site-packages/qiskit/qpy/binary_io/circuits.py", line 289, in _read_instruction
    gate = gate_class(*params)
TypeError: DiagonalGate.__init__() takes 2 positional arguments but 5 were given
```
Thanks for the report.  The fix should be a case of adding the string `"diagonal"` to the check of line 282 in your code link - `DiagonalGate` is an old gate that has its constructor and `params` fields behave in slightly different ways to most Qiskit gates.
Let me resolve it by adding `diagonal`

Resolving patch:
diff --git a/qiskit/qpy/binary_io/circuits.py b/qiskit/qpy/binary_io/circuits.py
--- a/qiskit/qpy/binary_io/circuits.py
+++ b/qiskit/qpy/binary_io/circuits.py
@@ -281,7 +281,14 @@ def _read_instruction(file_obj, circuit, registers, custom_operations, version,
             gate.ctrl_state = instruction.ctrl_state
         gate.condition = condition_tuple
     else:
-        if gate_name in {"Initialize", "StatePreparation", "UCRXGate", "UCRYGate", "UCRZGate"}:
+        if gate_name in {
+            "Initialize",
+            "StatePreparation",
+            "UCRXGate",
+            "UCRYGate",
+            "UCRZGate",
+            "DiagonalGate",
+        }:
             gate = gate_class(params)
         else:
             if gate_name == "Barrier":


Test patch:
diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py
--- a/test/python/circuit/test_circuit_load_from_qpy.py
+++ b/test/python/circuit/test_circuit_load_from_qpy.py
@@ -1408,6 +1408,21 @@ def test_incomplete_owned_bits(self):
         self.assertEqual(qc, new_circuit)
         self.assertDeprecatedBitProperties(qc, new_circuit)
 
+    def test_diagonal_gate(self):
+        """Test that a `DiagonalGate` successfully roundtrips."""
+        qc = QuantumCircuit(2)
+        qc.diagonal([1, -1, -1, 1], [0, 1])
+        with io.BytesIO() as fptr:
+            dump(qc, fptr)
+            fptr.seek(0)
+            new_circuit = load(fptr)[0]
+        # DiagonalGate (and a bunch of the qiskit.extensions gates) have non-deterministic
+        # definitions with regard to internal instruction names, so cannot be directly compared for
+        # equality.
+        self.assertIs(type(qc.data[0].operation), type(new_circuit.data[0].operation))
+        self.assertEqual(qc.data[0].operation.params, new_circuit.data[0].operation.params)
+        self.assertDeprecatedBitProperties(qc, new_circuit)
+
     def test_qpy_deprecation(self):
         """Test the old import path's deprecations fire."""
         with self.assertWarnsRegex(DeprecationWarning, "is deprecated"):