Skip to content

Commit

Permalink
fix: ResetQubit instructions will not be returned as Reset after bein…
Browse files Browse the repository at this point in the history
…g inserted into a Program (#1727)

* fix: ResetQubit instructions will not be returned as Reset after being inserted into a Program

* fix type hint

* handle types better

* do the mypy shuffle

* add DelayX to the tests

* fix typing
  • Loading branch information
MarquessV authored Feb 1, 2024
1 parent e0f82be commit cbf9ac7
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 6 deletions.
32 changes: 28 additions & 4 deletions pyquil/quilbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,13 @@ def _convert_to_py_instruction(instr: Any) -> AbstractInstruction:
if isinstance(instr, quil_rs.Declaration):
return Declare._from_rs_declaration(instr)
if isinstance(instr, quil_rs.Delay):
return Delay._from_rs_delay(instr)
print("len(instr.qubits):", len(instr.qubits))
print("len(instr.frame_names):", len(instr.frame_names))
if len(instr.qubits) > 0 and len(instr.frame_names) > 0:
return Delay._from_rs_delay(instr)
if len(instr.qubits) > 0:
return DelayQubits._from_rs_delay(instr)
return DelayFrames._from_rs_delay(instr)
if isinstance(instr, quil_rs.Fence):
if len(instr.qubits) == 0:
return FenceAll()
Expand All @@ -238,7 +244,10 @@ def _convert_to_py_instruction(instr: Any) -> AbstractInstruction:
if isinstance(instr, quil_rs.RawCapture):
return RawCapture._from_rs_raw_capture(instr)
if isinstance(instr, quil_rs.Reset):
return Reset._from_rs_reset(instr)
if instr.qubit is None:
return Reset._from_rs_reset(instr)
else:
return ResetQubit._from_rs_reset(instr)
if isinstance(instr, quil_rs.CircuitDefinition):
return DefCircuit._from_rs_circuit_definition(instr)
if isinstance(instr, quil_rs.GateDefinition):
Expand Down Expand Up @@ -555,7 +564,7 @@ class Reset(quil_rs.Reset, AbstractInstruction):
The RESET instruction.
"""

def __new__(cls, qubit: Optional[Union[Qubit, QubitPlaceholder, FormalArgument]] = None) -> Self:
def __new__(cls, qubit: Optional[Union[Qubit, QubitPlaceholder, FormalArgument, int]] = None) -> Self:
rs_qubit: Optional[quil_rs.Qubit] = None
if qubit is not None:
rs_qubit = _convert_to_rs_qubit(qubit)
Expand Down Expand Up @@ -614,11 +623,18 @@ class ResetQubit(Reset):
This is the pyQuil object for a Quil targeted reset instruction.
"""

def __new__(cls, qubit: Union[Qubit, QubitPlaceholder, FormalArgument]) -> Self:
def __new__(cls, qubit: Union[Qubit, QubitPlaceholder, FormalArgument, int]) -> Self:
if qubit is None:
raise TypeError("qubit should not be None")
return super().__new__(cls, qubit)

@classmethod
def _from_rs_reset(cls, reset: quil_rs.Reset) -> "ResetQubit":
if reset.qubit is not None:
qubit = _convert_to_py_qubit(reset.qubit)
return ResetQubit.__new__(cls, qubit)
raise ValueError("reset.qubit should not be None")


class DefGate(quil_rs.GateDefinition, AbstractInstruction):
"""
Expand Down Expand Up @@ -2396,11 +2412,19 @@ class DelayFrames(Delay):
def __new__(cls, frames: List[Frame], duration: float) -> Self:
return super().__new__(cls, frames, [], duration)

@classmethod
def _from_rs_delay(cls, delay: quil_rs.Delay) -> "DelayFrames":
return Delay._from_rs_delay.__func__(cls, delay) # type: ignore


class DelayQubits(Delay):
def __new__(cls, qubits: Sequence[Union[Qubit, FormalArgument]], duration: float) -> Self:
return super().__new__(cls, [], qubits, duration)

@classmethod
def _from_rs_delay(cls, delay: quil_rs.Delay) -> "DelayQubits":
return Delay._from_rs_delay.__func__(cls, delay) # type: ignore


class Fence(quil_rs.Fence, AbstractInstruction):
def __new__(cls, qubits: List[Union[Qubit, FormalArgument]]) -> Self:
Expand Down
26 changes: 24 additions & 2 deletions test/unit/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from syrupy.assertion import SnapshotAssertion

from pyquil import Program
from pyquil.quilatom import Label
from pyquil.quilbase import MemoryReference
from pyquil.quil import AbstractInstruction, Declare, Measurement
from pyquil.quilbase import Reset, ResetQubit, Delay, DelayFrames, DelayQubits, Frame
from pyquil.quilatom import Label, MemoryReference
from pyquil.experiment._program import (
measure_qubits,
parameterized_single_qubit_measurement_basis,
Expand Down Expand Up @@ -168,3 +168,25 @@ def test_filter_quil_t():
)
full_program = non_quil_t_program + quil_t_program
assert full_program.remove_quil_t_instructions() == non_quil_t_program


def test_compatibility_layer():
"""
Test that the compatibility layer that transforms pyQuil instructions to quil instructions works as intended.
"""
# Note: `quil` re-orders some instructions in a program (e.g. by shuffling DECLAREs to the top). This isn't a
# problem for the current set of instructions we're testing, but it's something to keep in mind if we add more.
instructions = [
ResetQubit(0),
Reset(),
Delay([Frame([0], "frame")], [0], 0.01),
DelayFrames([Frame([], "frame")], 0.01),
DelayQubits([0, 1], 0.01),
]
program = Program(instructions)
for (original, transformed) in zip(instructions, program):
assert isinstance(transformed, AbstractInstruction)
print("type(transformed):", type(transformed))
print("type(original):", type(original))
assert isinstance(transformed, type(original))
assert transformed == original

0 comments on commit cbf9ac7

Please sign in to comment.