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
2 changes: 2 additions & 0 deletions pyquil/quilatom.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ def __hash__(self) -> int:
def _convert_to_rs_expression(parameter: ParameterDesignator) -> quil_rs_expr.Expression:
if isinstance(parameter, quil_rs_expr.Expression):
return parameter
elif isinstance(parameter, (int, float, complex, np.number)):
return quil_rs_expr.Expression.from_number(complex(parameter))
elif isinstance(parameter, Number):
return quil_rs_expr.Expression.from_number(complex(parameter)) # type: ignore
elif isinstance(parameter, (Expression, MemoryReference)):
Expand Down
289 changes: 206 additions & 83 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 Down Expand Up @@ -125,16 +124,26 @@ def __hash__(self) -> int:
def _convert_to_rs_instruction(instr: AbstractInstruction) -> quil_rs.Instruction:
if isinstance(instr, quil_rs.Instruction):
return instr
if isinstance(instr, AbstractInstruction):
return quil_rs.Instruction(instr)
if isinstance(instr, quil_rs.Calibration):
return quil_rs.Instruction.from_calibration_definition(instr)
if isinstance(instr, quil_rs.Declaration):
return quil_rs.Instruction.from_declaration(instr)
if isinstance(instr, quil_rs.Delay):
return quil_rs.Instruction.from_delay(instr)
if isinstance(instr, quil_rs.Fence):
return quil_rs.Instruction.from_fence(instr)
if isinstance(instr, quil_rs.Gate):
return quil_rs.Instruction.from_gate(instr)
if isinstance(instr, quil_rs.MeasureCalibrationDefinition):
return quil_rs.Instruction.from_measure_calibration_definition(instr)
if isinstance(instr, quil_rs.Measurement):
return quil_rs.Instruction.from_measurement(instr)
if isinstance(instr, quil_rs.Pragma):
return quil_rs.Instruction.from_pragma(instr)
if isinstance(instr, quil_rs.Reset):
return quil_rs.Instruction.from_reset(instr)
if isinstance(instr, AbstractInstruction):
return quil_rs.Instruction(instr)
else:
raise ValueError(f"{type(instr)} is not an Instruction")

Expand All @@ -147,16 +156,24 @@ def _convert_to_py_instruction(instr: quil_rs.Instruction) -> AbstractInstructio
if isinstance(instr, quil_rs.Instruction):
# TODOV4: Will have to handle unit variants since they don't have inner data
instr = instr.inner()
if isinstance(instr, quil_rs.Declaration):
return Declare._from_rs_declaration(instr)
if isinstance(instr, quil_rs.Calibration):
return DefCalibration._from_rs_calibration(instr)
if isinstance(instr, quil_rs.Declaration):
return Declare._from_rs_declaration(instr)
if isinstance(instr, quil_rs.Delay):
return Delay._from_rs_delay(instr)
if isinstance(instr, quil_rs.Fence):
return Fence._from_rs_fence(instr)
if isinstance(instr, quil_rs.Gate):
return Gate._from_rs_gate(instr)
if isinstance(instr, quil_rs.MeasureCalibrationDefinition):
return DefMeasureCalibration._from_rs_measure_calibration_definition(instr)
if isinstance(instr, quil_rs.Measurement):
return Measurement._from_rs_measurement(instr)
if isinstance(instr, quil_rs.Pragma):
return Pragma._from_rs_pragma(instr)
if isinstance(instr, quil_rs.Reset):
return Reset._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 @@ -424,24 +441,65 @@ 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)

@classmethod
def _from_rs_reset(cls, reset: quil_rs.Reset) -> "Reset":
return super().__new__(cls, reset.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_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)

def get_qubits(self, indices: bool = True) -> Set[QubitDesignator]:
return {_extract_qubit_index(self.qubit, indices)}

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

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


class DefGate(quil_rs.GateDefinition, AbstractInstruction):
Expand Down Expand Up @@ -671,14 +729,6 @@ class Wait(SimpleInstruction):
op = "WAIT"


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

op = "RESET"


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


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

Expand All @@ -1016,39 +1066,67 @@ 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)

@classmethod
def _from_rs_pragma(cls, pragma: quil_rs.Pragma) -> "Pragma":
return super().__new__(cls, pragma.name, pragma.arguments, pragma.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(f"{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 str(self)

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 @@ -1316,50 +1394,95 @@ 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.")
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)

self.frames = frames
self.duration = duration
@classmethod
def _from_rs_delay(cls, delay: quil_rs.Delay) -> "Delay":
return super().__new__(cls, delay.duration, delay.frame_names, delay.qubits)

@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(merged_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)

@property
def qubits(self) -> List[QubitDesignator]:
return _convert_to_py_qubits(super().qubits)

class DelayQubits(AbstractInstruction):
def __init__(self, qubits: List[Union[Qubit, FormalArgument]], duration: float):
self.qubits = qubits
self.duration = duration
@qubits.setter
def qubits(self, qubits: List[Union[int, Qubit, FormalArgument]]):
quil_rs.Delay.qubits.__set__(self, _convert_to_rs_qubits(qubits))

def out(self) -> str:
return f"DELAY {_format_qubits_str(self.qubits)} {self.duration}"
@property
def frames(self) -> List[Frame]:
return [Frame(self.qubits, name) for name in super().frame_names]

@frames.setter
def frames(self, frames: List[Frame]):
new_qubits = Delay._join_frame_qubits(frames, [])
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)

class FenceAll(SimpleInstruction):
"""
The FENCE instruction.
"""
@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)

op = "FENCE"

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

class Fence(AbstractInstruction):
def __init__(self, qubits: List[Union[Qubit, FormalArgument]]):
self.qubits = qubits

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


class Fence(quil_rs.Fence, AbstractInstruction):
def __new__(cls, qubits: List[Union[Qubit, FormalArgument]]):
return super().__new__(cls, _convert_to_rs_qubits(qubits))

@classmethod
def _from_rs_fence(cls, fence: quil_rs.Fence) -> "Fence":
return super().__new__(cls, fence.qubits)

def out(self) -> str:
ret = "FENCE " + _format_qubits_str(self.qubits)
return ret
return str(self)

@property
def qubits(self) -> List[Union[Qubit, FormalArgument]]:
return _convert_to_py_qubits(super().qubits)

@qubits.setter
def qubits(self, qubits: List[Union[Qubit, FormalArgument]]):
quil_rs.Fence.qubits.__set__(self, _convert_to_rs_qubits(qubits))


class FenceAll(Fence):
"""
The FENCE instruction.
"""

def __new__(cls):
return super().__new__(cls, [])


class DefWaveform(quil_rs.WaveformDefinition, AbstractInstruction):
Expand Down
Loading