Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4 Instruction API: Pragma, Reset, Fence, Delay #1551

Merged
219 changes: 149 additions & 70 deletions pyquil/quilbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"""
import abc
import collections
import json

from numbers import Complex
from typing import (
Expand All @@ -33,6 +32,7 @@
Sequence,
Set,
Tuple,
Type,
Union,
TYPE_CHECKING,
cast,
Expand Down Expand Up @@ -448,24 +448,58 @@ def out(self) -> str:
return str(self)


class ResetQubit(AbstractInstruction):
class Reset(quil_rs.Reset, AbstractInstruction):
"""
This is the pyQuil object for a Quil targeted reset instruction.
The RESET instruction.
"""

def __init__(self, qubit: Union[Qubit, QubitPlaceholder, FormalArgument]):
if not isinstance(qubit, (Qubit, QubitPlaceholder, FormalArgument)):
raise TypeError("qubit should be a Qubit")
self.qubit = qubit
def __new__(cls, qubit: Optional[Union[Qubit, QubitPlaceholder, FormalArgument]] = None):
rs_qubit: Optional[quil_rs.Qubit] = None
if qubit is not None:
rs_qubit = _convert_to_rs_qubit(qubit)
return super().__new__(cls, rs_qubit)

def out(self) -> str:
return "RESET {}".format(self.qubit.out())
return str(self)

def __str__(self) -> str:
return "RESET {}".format(_format_qubit_str(self.qubit))
@deprecated(
deprecated_in="4.0",
removed_in="5.0",
current_version=pyquil_version,
details="The indices flag will be removed, use get_qubit_indices() instead.",
)
def get_qubits(self, indices: bool = True) -> Optional[Set[QubitDesignator]]:
if super().qubit is None:
return None
if indices:
return self.get_qubit_indices() # type: ignore
return {_convert_to_py_qubit(super().qubit)} # type: ignore

def get_qubits(self, indices: bool = True) -> Set[QubitDesignator]:
return {_extract_qubit_index(self.qubit, indices)}
def get_qubit_indices(self) -> Optional[Set[int]]:
if super().qubit is None:
return None
return {super().qubit.to_fixed()} # type: ignore

@property
def qubit(self) -> Optional[QubitDesignator]:
if super().qubit:
return _convert_to_py_qubit(super().qubit) # type: ignore
return super().qubit

@qubit.setter
def qubit(self, qubit: Optional[QubitDesignator]):
rs_qubit: Optional[quil_rs.Qubit] = None
if qubit is not None:
rs_qubit = _convert_to_rs_qubit(qubit)
quil_rs.Reset.qubit.__set__(self, rs_qubit)


class ResetQubit(Reset):
"""
This is the pyQuil object for a Quil targeted reset instruction.
"""

...


class DefGate(AbstractInstruction):
Expand Down Expand Up @@ -734,14 +768,6 @@ class Wait(SimpleInstruction):
op = "WAIT"


class Reset(SimpleInstruction):
"""
The RESET instruction.
"""

op = "RESET"


class Nop(SimpleInstruction):
"""
The NOP instruction.
Expand Down Expand Up @@ -1069,7 +1095,7 @@ def out(self) -> str:
return "JUMP %s" % self.target


class Pragma(AbstractInstruction):
class Pragma(quil_rs.Pragma, AbstractInstruction):
"""
A PRAGMA instruction.

Expand All @@ -1079,39 +1105,63 @@ class Pragma(AbstractInstruction):

"""

def __init__(
self,
def __new__(
cls,
command: str,
args: Iterable[Union[QubitDesignator, str]] = (),
freeform_string: str = "",
):
if not isinstance(command, str):
raise TypeError(f"Pragma's require an identifier: {command}")

if not isinstance(args, collections.abc.Iterable):
raise TypeError(f"Pragma arguments must be an Iterable: {args}")
for a in args:
if not (
isinstance(a, str) or isinstance(a, int) or isinstance(a, QubitPlaceholder) or isinstance(a, Qubit)
):
raise TypeError(f"Pragma arguments must be strings or integers: {a}")
if not isinstance(freeform_string, str):
raise TypeError(f"The freeform string argument must be a string: {freeform_string}")

self.command = command
self.args = tuple(args)
self.freeform_string = freeform_string
) -> Type["Pragma"]:
data = freeform_string or None
return super().__new__(cls, command, Pragma._to_pragma_arguments(args), data)

@staticmethod
def _to_pragma_arguments(args: Iterable[Union[QubitDesignator, str]]) -> List[quil_rs.PragmaArgument]:
pragma_arguments = []
for arg in args:
if isinstance(arg, Qubit):
pragma_arguments.append(quil_rs.PragmaArgument.from_integer(arg.index))
elif isinstance(arg, (str, FormalArgument)):
pragma_arguments.append(quil_rs.PragmaArgument.from_identifier(str(arg)))
else:
raise TypeError("type(arg) isn't a valid QubitDesignator")
return pragma_arguments

@staticmethod
def _to_py_arguments(args: List[quil_rs.PragmaArgument]) -> List[QubitDesignator]:
arguments = []
for arg in args:
if arg.is_integer():
arguments.append(Qubit(arg.to_integer()))
else:
arguments.append(FormalArgument(arg.to_identifier()))
return arguments

def out(self) -> str:
ret = "PRAGMA {}".format(self.command)
if self.args:
ret += " {}".format(" ".join(str(a) for a in self.args))
if self.freeform_string:
ret += ' "{}"'.format(self.freeform_string)
return ret
return self.__str__()

def __repr__(self) -> str:
return "<PRAGMA {}>".format(self.command)
@property
def command(self) -> str:
return super().name

@command.setter
def command(self, command: str):
quil_rs.Pragma.name.__set__(self, command)

@property
def args(self) -> Tuple[QubitDesignator]:
return tuple(Pragma._to_py_arguments(super().arguments))

@args.setter
def args(self, args: str):
quil_rs.Pragma.arguments.__set__(self, Pragma._to_pragma_arguments(args))

@property
def freeform_string(self) -> str:
return super().data or ""

@freeform_string.setter
def freeform_string(self, freeform_string: str):
quil_rs.Pragma.data.__set__(self, freeform_string)


class Declare(quil_rs.Declaration, AbstractInstruction):
Expand Down Expand Up @@ -1379,33 +1429,62 @@ def get_qubits(self, indices: bool = True) -> Set[QubitDesignator]:
return _get_frame_qubits(self.frame, indices)


class DelayFrames(AbstractInstruction):
def __init__(self, frames: List[Frame], duration: float):
# all frames should be on the same qubits
if len(frames) == 0:
raise ValueError("DELAY expected nonempty list of frames.")
if len(set(tuple(f.qubits) for f in frames)) != 1:
raise ValueError("DELAY with explicit frames requires all frames are on the same qubits.")

self.frames = frames
self.duration = duration
class Delay(quil_rs.Delay, AbstractInstruction):
def __new__(cls, frames: List[Frame], qubits: List[Union[int, Qubit, FormalArgument]], duration: float) -> "Delay":
frame_names = [frame.name for frame in frames]
rs_qubits = _convert_to_rs_qubits(Delay._join_frame_qubits(frames, qubits))
expression = quil_rs_expr.Expression.from_number(complex(duration))
return super().__new__(cls, expression, frame_names, rs_qubits)

def out(self) -> str:
qubits = self.frames[0].qubits
ret = "DELAY " + _format_qubits_str(qubits)
for f in self.frames:
ret += f' "{f.name}"'
ret += f" {self.duration}"
return ret
return str(self)

@staticmethod
def _join_frame_qubits(
frames: List[Frame], qubits: List[Union[int, Qubit, FormalArgument]]
) -> List[Union[int, Qubit, FormalArgument]]:
merged_qubits = set(qubits)
for frame in frames:
merged_qubits.update(frame.qubits) # type: ignore
return list(qubits)

class DelayQubits(AbstractInstruction):
def __init__(self, qubits: List[Union[Qubit, FormalArgument]], duration: float):
self.qubits = qubits
self.duration = duration
@property
def qubits(self) -> List[QubitDesignator]:
return _convert_to_py_qubits(super().qubits)

def out(self) -> str:
return f"DELAY {_format_qubits_str(self.qubits)} {self.duration}"
@qubits.setter
def qubits(self, qubits: List[Union[int, Qubit, FormalArgument]]):
quil_rs.Delay.qubits.__set__(self, _convert_to_rs_qubits(qubits))

@property
def frames(self) -> List[Frame]:
return [Frame([], name) for name in super().frame_names]

@frames.setter
def frames(self, frames: List[Frame]):
new_qubits = Delay._join_frame_qubits(frames, self.qubits)
frame_names = [frame.name for frame in frames]
quil_rs.Delay.qubits.__set__(self, _convert_to_rs_qubits(new_qubits))
quil_rs.Delay.frame_names.__set__(self, frame_names)

@property
def duration(self) -> float:
return super().duration.to_real()

@duration.setter
def duration(self, duration: float):
expression = quil_rs_expr.Expression.from_number(complex(duration))
quil_rs.Delay.duration.__set__(self, expression)


class DelayFrames(Delay):
def __new__(cls, frames: List[Frame], duration: float):
return super().__new__(cls, frames, [], duration)


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


class FenceAll(SimpleInstruction):
Expand Down
63 changes: 63 additions & 0 deletions test/unit/__snapshots__/test_quilbase.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@

'''
# ---
# name: TestDelayFrames.test_out[frames0-5.0]
'DELAY "frame" 5'
# ---
# name: TestDelayQubits.test_out[FormalArgument]
'DELAY a 2.5'
# ---
# name: TestDelayQubits.test_out[Qubit]
'DELAY 0 5'
# ---
# name: TestFence.test_out[FormalArgument]
'FENCE a'
# ---
# name: TestFence.test_out[Qubit]
'FENCE 0'
# ---
# name: TestGate.test_controlled_modifier[CPHASE-Expression]
'CONTROLLED CPHASE(1.5707963267948966) 5 0 1'
# ---
Expand Down Expand Up @@ -207,3 +222,51 @@
# name: TestMeasurement.test_str[No-MemoryReference]
'MEASURE 0'
# ---
# name: TestPragma.test_out[Command-Only]
'PRAGMA NO-NOISE'
# ---
# name: TestPragma.test_out[With-Arg-And-String]
'PRAGMA READOUT-POVM 1 "(0.9 0.19999999999999996 0.09999999999999998 0.8)"'
# ---
# name: TestPragma.test_out[With-Arg]
'PRAGMA DOES-A-THING 0 b'
# ---
# name: TestPragma.test_out[With-String]
'PRAGMA INITIAL_REWIRING "GREEDY"'
# ---
# name: TestPragma.test_str[Command-Only]
'PRAGMA NO-NOISE'
# ---
# name: TestPragma.test_str[With-Arg-And-String]
'PRAGMA READOUT-POVM 1 "(0.9 0.19999999999999996 0.09999999999999998 0.8)"'
# ---
# name: TestPragma.test_str[With-Arg]
'PRAGMA DOES-A-THING 0 b'
# ---
# name: TestPragma.test_str[With-String]
'PRAGMA INITIAL_REWIRING "GREEDY"'
# ---
# name: TestReset.test_out[FormalArgument]
'RESET a'
# ---
# name: TestReset.test_out[None]
'RESET'
# ---
# name: TestReset.test_out[Qubit]
'RESET 0'
# ---
# name: TestReset.test_out[qubit0]
'RESET 0'
# ---
# name: TestReset.test_str[FormalArgument]
'RESET a'
# ---
# name: TestReset.test_str[None]
'RESET'
# ---
# name: TestReset.test_str[Qubit]
'RESET 0'
# ---
# name: TestReset.test_str[qubit0]
'RESET 0'
# ---
Loading