Skip to content

Commit

Permalink
rewrite qss.serialize_circuits to support newest qiskit (#854)
Browse files Browse the repository at this point in the history
also adds workarounds for a number of known qpy bugs, and fixes some
bugs in our previous implementation (which didn't recurse into gate
.definitions, and therefore could break or silently modify gates if
circuits weren't flattened prior to submission)

fixes: #837

(notebook checks won't pass until after this is deployed)
  • Loading branch information
richrines1 authored Dec 14, 2023
1 parent 729c212 commit 1e8bf1e
Show file tree
Hide file tree
Showing 7 changed files with 461 additions and 215 deletions.
2 changes: 1 addition & 1 deletion checks-superstaq/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
black[jupyter]>=23.1.0
black[jupyter,colorama]>=23.1.0
flake8>=3.8.4
flake8-pyproject>=1.2.3
flake8-type-checking>=2.1.0
Expand Down
3 changes: 2 additions & 1 deletion qiskit-superstaq/qiskit_superstaq/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import compiler_output, serialization, validation
from . import compiler_output, custom_gates, serialization, validation
from ._version import __version__
from .compiler_output import active_qubit_indices, measured_clbit_indices, measured_qubit_indices
from .custom_gates import (
Expand All @@ -20,6 +20,7 @@
"AQTiCCXGate",
"AQTiToffoliGate",
"compiler_output",
"custom_gates",
"deserialize_circuits",
"measured_qubit_indices",
"measured_clbit_indices",
Expand Down
49 changes: 1 addition & 48 deletions qiskit-superstaq/qiskit_superstaq/custom_gates.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import functools
from typing import Callable, Dict, Optional, Tuple, Union
from typing import Optional, Tuple, Union

import numpy as np
import numpy.typing as npt
Expand Down Expand Up @@ -505,50 +505,3 @@ def __init__(self, label: Optional[str] = None) -> None:


AQTiToffoliGate = AQTiCCXGate


_custom_gate_resolvers: Dict[str, Callable[..., qiskit.circuit.Gate]] = {
"acecr": lambda rads: AceCR(rads=rads),
"acecr_rx": lambda *rads: AceCR(*rads),
"zzswap": ZZSwapGate,
"ix": iXGate,
"ixdg": iXdgGate,
"iccx": iCCXGate,
"iccx_o0": AQTiCCXGate,
"iccx_o1": lambda: iCCXGate(ctrl_state="01"),
"iccx_o2": lambda: iCCXGate(ctrl_state="10"),
"iccxdg": iCCXdgGate,
"iccxdg_o0": lambda: iCCXdgGate(ctrl_state="00"),
"iccxdg_o1": lambda: iCCXdgGate(ctrl_state="01"),
"iccxdg_o2": lambda: iCCXdgGate(ctrl_state="10"),
}


def custom_resolver(gate: qiskit.circuit.Instruction) -> Optional[qiskit.circuit.Gate]:
"""Recovers a custom gate type from a generic `qiskit.circuit.Gate`.
The resolution is done using `gate.definition.name` rather than `gate.name`, as the former
is set by all `qiskit-superstaq` custom gates and the latter may be modified by calls
such as `qiskit.QuantumCircuit.qasm()`.
Args:
gate: The input gate instruction from which to recover a custom gate type.
Returns:
A `qiskit.circuit.Gate` if the gate definition name is in the `_custom_gate_resolver`
dictionary (or the definition name is "parallel_gates").
"""

if gate.definition and gate.definition.name == "parallel_gates":
component_gates = [custom_resolver(inst) or inst for inst, _, _ in gate.definition]
return ParallelGates(*component_gates, label=gate.label)

if gate.definition and gate.definition.name in _custom_gate_resolvers:
new_gate = _custom_gate_resolvers[gate.definition.name](*gate.params)
elif gate.name in _custom_gate_resolvers:
new_gate = _custom_gate_resolvers[gate.name](*gate.params)
else:
return None

new_gate.label = gate.label
return new_gate
68 changes: 1 addition & 67 deletions qiskit-superstaq/qiskit_superstaq/custom_gates_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# pylint: disable=missing-function-docstring,missing-class-docstring
from typing import List, Set
from typing import Set

import numpy as np
import pytest
Expand Down Expand Up @@ -219,69 +219,3 @@ def test_aqticcx() -> None:
)

np.allclose(qiskit.quantum_info.Operator(qc), correct_unitary)


def test_custom_resolver() -> None:
custom_gates: List[qiskit.circuit.Gate] = [
qss.AceCR("+-"),
qss.AceCR("-+"),
qss.AceCR("+-", sandwich_rx_rads=1.23),
qss.AceCR(np.pi),
qss.AceCR(np.pi / 3, np.pi / 2),
qss.AceCR(0),
qss.ZZSwapGate(1.23),
qss.AQTiCCXGate(),
qss.custom_gates.iXGate(),
qss.custom_gates.iXdgGate(),
qss.custom_gates.iCCXGate(),
qss.custom_gates.iCCXGate(ctrl_state="01"),
qss.custom_gates.iCCXGate(ctrl_state="10"),
qss.custom_gates.iCCXdgGate(ctrl_state="00"),
qss.custom_gates.iCCXdgGate(),
qss.custom_gates.iCCXdgGate(ctrl_state="01"),
qss.custom_gates.iCCXdgGate(ctrl_state="10"),
]

generic_gates = []

for custom_gate in custom_gates:
generic_gate = qiskit.circuit.Gate(
custom_gate.name,
custom_gate.num_qubits,
custom_gate.params,
label=str(id(custom_gate)),
)
generic_gate.definition = custom_gate.definition
generic_gates.append(generic_gate)
assert generic_gate != custom_gate

resolved_gate = qss.custom_gates.custom_resolver(generic_gate)
assert resolved_gate == custom_gate
assert resolved_gate.label == str(id(custom_gate)) # (labels aren't included in __eq__)

parallel_gates = qss.ParallelGates(
qiskit.circuit.library.RXGate(4.56), qiskit.circuit.library.CXGate(), *custom_gates
)
parallel_generic_gates = qss.ParallelGates(
qiskit.circuit.library.RXGate(4.56),
qiskit.circuit.library.CXGate(),
*generic_gates,
label="label-1"
)
resolved_gate = qss.custom_gates.custom_resolver(parallel_generic_gates)
assert parallel_generic_gates != parallel_gates
assert resolved_gate == parallel_gates
assert resolved_gate.label == "label-1"

generic_parallel_gates = qiskit.circuit.Gate(
parallel_gates.name, parallel_gates.num_qubits, [], label="label-2"
)
generic_parallel_gates.definition = parallel_generic_gates.definition
resolved_gate = qss.custom_gates.custom_resolver(generic_parallel_gates)
assert generic_parallel_gates != parallel_gates
assert resolved_gate == parallel_gates
assert resolved_gate.label == "label-2"

assert qss.custom_gates.custom_resolver(qiskit.circuit.library.CXGate()) is None
assert qss.custom_gates.custom_resolver(qiskit.circuit.library.RXGate(2)) is None
assert qss.custom_gates.custom_resolver(qiskit.circuit.Gate("??", 1, [])) is None
Loading

0 comments on commit 1e8bf1e

Please sign in to comment.