Skip to content

Commit

Permalink
Fix qpy support for Cliffords (#11495) (#11582)
Browse files Browse the repository at this point in the history
* clifford qpy support + test

* release notes

* cleanup: checking if instruction is of type Instruction

* review comment

* review comments: consistency + remove specialized handling

* adding test to test_qpy.py

* changing version string to 0.45.2

---------

Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com>
(cherry picked from commit 786c0e9)

Co-authored-by: Alexander Ivrii <alexi@il.ibm.com>
  • Loading branch information
mergify[bot] and alexanderivrii committed Jan 17, 2024
1 parent 92d589f commit 1029797
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
15 changes: 12 additions & 3 deletions qiskit/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from qiskit.extensions import quantum_initializer
from qiskit.qpy import common, formats, type_keys
from qiskit.qpy.binary_io import value, schedules
from qiskit.quantum_info.operators import SparsePauliOp
from qiskit.quantum_info.operators import SparsePauliOp, Clifford
from qiskit.synthesis import evolution as evo_synth
from qiskit.transpiler.layout import Layout, TranspileLayout

Expand Down Expand Up @@ -290,6 +290,8 @@ def _read_instruction(
gate_class = getattr(quantum_initializer, gate_name)
elif hasattr(controlflow, gate_name):
gate_class = getattr(controlflow, gate_name)
elif gate_name == "Clifford":
gate_class = Clifford
else:
raise AttributeError("Invalid instruction type: %s" % gate_name)

Expand Down Expand Up @@ -578,7 +580,11 @@ def _dumps_instruction_parameter(param, index_map, use_symengine):

# pylint: disable=too-many-boolean-expressions
def _write_instruction(file_obj, instruction, custom_operations, index_map, use_symengine):
gate_class_name = instruction.operation.base_class.__name__
if isinstance(instruction.operation, Instruction):
gate_class_name = instruction.operation.base_class.__name__
else:
gate_class_name = instruction.operation.__class__.__name__

custom_operations_list = []
if (
(
Expand All @@ -587,6 +593,7 @@ def _write_instruction(file_obj, instruction, custom_operations, index_map, use_
and not hasattr(extensions, gate_class_name)
and not hasattr(quantum_initializer, gate_class_name)
and not hasattr(controlflow, gate_class_name)
and gate_class_name != "Clifford"
)
or gate_class_name == "Gate"
or gate_class_name == "Instruction"
Expand Down Expand Up @@ -628,7 +635,7 @@ def _write_instruction(file_obj, instruction, custom_operations, index_map, use_
condition_value = int(instruction.operation.condition[1])

gate_class_name = gate_class_name.encode(common.ENCODE)
label = getattr(instruction.operation, "label")
label = getattr(instruction.operation, "label", None)
if label:
label_raw = label.encode(common.ENCODE)
else:
Expand All @@ -641,6 +648,8 @@ def _write_instruction(file_obj, instruction, custom_operations, index_map, use_
instruction.operation.target,
tuple(instruction.operation.cases_specifier()),
]
elif isinstance(instruction.operation, Clifford):
instruction_params = [instruction.operation.tableau]
else:
instruction_params = instruction.operation.params

Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/fix-clifford-qpy-2ffc8308c888e7e0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
QPY (using :func:`.qpy.dump` and :func:`.qpy.load`) will now correctly serialize
and deserialize quantum circuits with Clifford operators
(:class:`~qiskit.quantum_info.Clifford`).
29 changes: 28 additions & 1 deletion test/python/circuit/test_circuit_load_from_qpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from qiskit.synthesis import LieTrotter, SuzukiTrotter
from qiskit.test import QiskitTestCase
from qiskit.qpy import dump, load
from qiskit.quantum_info import Pauli, SparsePauliOp
from qiskit.quantum_info import Pauli, SparsePauliOp, Clifford
from qiskit.quantum_info.random import random_unitary
from qiskit.circuit.controlledgate import ControlledGate
from qiskit.utils import optionals
Expand Down Expand Up @@ -1697,6 +1697,33 @@ def test_valid_circuit_with_initialize_instruction(self, param):
self.assertEqual(qc, new_circuit)
self.assertDeprecatedBitProperties(qc, new_circuit)

def test_clifford(self):
"""Test that circuits with Clifford operations can be saved and retrieved correctly."""
cliff1 = Clifford.from_dict(
{
"stabilizer": ["-IZX", "+ZYZ", "+ZII"],
"destabilizer": ["+ZIZ", "+ZXZ", "-XIX"],
}
)
cliff2 = Clifford.from_dict(
{
"stabilizer": ["+YX", "+ZZ"],
"destabilizer": ["+IZ", "+YI"],
}
)

circuit = QuantumCircuit(6, 1)
circuit.cx(0, 1)
circuit.append(cliff1, [2, 4, 5])
circuit.h(4)
circuit.append(cliff2, [3, 0])

with io.BytesIO() as fptr:
dump(circuit, fptr)
fptr.seek(0)
new_circuit = load(fptr)[0]
self.assertEqual(circuit, new_circuit)


class TestSymengineLoadFromQPY(QiskitTestCase):
"""Test use of symengine in qpy set of methods."""
Expand Down
17 changes: 17 additions & 0 deletions test/qpy_compat/test_qpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,21 @@ def generate_layout_circuits():
return [qc]


def generate_clifford_circuits():
"""Test qpy circuits with Clifford operations."""
from qiskit.quantum_info import Clifford

cliff = Clifford.from_dict(
{
"stabilizer": ["-IZX", "+ZYZ", "+ZII"],
"destabilizer": ["+ZIZ", "+ZXZ", "-XIX"],
}
)
qc = QuantumCircuit(3)
qc.append(cliff, [0, 1, 2])
return [qc]


def generate_control_flow_expr():
"""`IfElseOp`, `WhileLoopOp` and `SwitchCaseOp` with `Expr` nodes in their discriminators."""
from qiskit.circuit.classical import expr, types
Expand Down Expand Up @@ -752,6 +767,8 @@ def generate_circuits(version_parts):
"acquire_inst_with_kernel_and_disc.qpy"
] = generate_acquire_instruction_with_kernel_and_discriminator()
output_circuits["control_flow_expr.qpy"] = generate_control_flow_expr()
if version_parts >= (0, 45, 2):
output_circuits["clifford.qpy"] = generate_clifford_circuits()
return output_circuits


Expand Down

0 comments on commit 1029797

Please sign in to comment.