Skip to content

Commit a243b3e

Browse files
authored
V4 Instruction API: Pragma, Reset, Fence, Delay (#1551)
* setup Pragma tests * setup tests for Qubit * setup tests for Fence * back Pragma with quil_rs.Pragma * back Reset, ResetQubit, with quil-rs * Back Delay(Frames|Qubits) with quil-rs * update Delay implementation per feedback * back Fence, FenceAll, with quil-rs * remove unused snapshots, prints * update convers to rs/py instruction functions * better type for numpy numbers * cleanup, and assert ResetQubit qubit is not None
1 parent d6a8ef4 commit a243b3e

File tree

4 files changed

+432
-86
lines changed

4 files changed

+432
-86
lines changed

pyquil/quilatom.py

+2
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ def __hash__(self) -> int:
315315
def _convert_to_rs_expression(parameter: ParameterDesignator) -> quil_rs_expr.Expression:
316316
if isinstance(parameter, quil_rs_expr.Expression):
317317
return parameter
318+
elif isinstance(parameter, (int, float, complex, np.number)):
319+
return quil_rs_expr.Expression.from_number(complex(parameter))
318320
elif isinstance(parameter, Number):
319321
return quil_rs_expr.Expression.from_number(complex(parameter)) # type: ignore
320322
elif isinstance(parameter, (Expression, MemoryReference)):

pyquil/quilbase.py

+206-83
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"""
1919
import abc
2020
import collections
21-
import json
2221

2322
from numbers import Complex
2423
from typing import (
@@ -125,16 +124,26 @@ def __hash__(self) -> int:
125124
def _convert_to_rs_instruction(instr: AbstractInstruction) -> quil_rs.Instruction:
126125
if isinstance(instr, quil_rs.Instruction):
127126
return instr
128-
if isinstance(instr, AbstractInstruction):
129-
return quil_rs.Instruction(instr)
130127
if isinstance(instr, quil_rs.Calibration):
131128
return quil_rs.Instruction.from_calibration_definition(instr)
129+
if isinstance(instr, quil_rs.Declaration):
130+
return quil_rs.Instruction.from_declaration(instr)
131+
if isinstance(instr, quil_rs.Delay):
132+
return quil_rs.Instruction.from_delay(instr)
133+
if isinstance(instr, quil_rs.Fence):
134+
return quil_rs.Instruction.from_fence(instr)
132135
if isinstance(instr, quil_rs.Gate):
133136
return quil_rs.Instruction.from_gate(instr)
134137
if isinstance(instr, quil_rs.MeasureCalibrationDefinition):
135138
return quil_rs.Instruction.from_measure_calibration_definition(instr)
136139
if isinstance(instr, quil_rs.Measurement):
137140
return quil_rs.Instruction.from_measurement(instr)
141+
if isinstance(instr, quil_rs.Pragma):
142+
return quil_rs.Instruction.from_pragma(instr)
143+
if isinstance(instr, quil_rs.Reset):
144+
return quil_rs.Instruction.from_reset(instr)
145+
if isinstance(instr, AbstractInstruction):
146+
return quil_rs.Instruction(instr)
138147
else:
139148
raise ValueError(f"{type(instr)} is not an Instruction")
140149

@@ -147,16 +156,24 @@ def _convert_to_py_instruction(instr: quil_rs.Instruction) -> AbstractInstructio
147156
if isinstance(instr, quil_rs.Instruction):
148157
# TODOV4: Will have to handle unit variants since they don't have inner data
149158
instr = instr.inner()
150-
if isinstance(instr, quil_rs.Declaration):
151-
return Declare._from_rs_declaration(instr)
152159
if isinstance(instr, quil_rs.Calibration):
153160
return DefCalibration._from_rs_calibration(instr)
161+
if isinstance(instr, quil_rs.Declaration):
162+
return Declare._from_rs_declaration(instr)
163+
if isinstance(instr, quil_rs.Delay):
164+
return Delay._from_rs_delay(instr)
165+
if isinstance(instr, quil_rs.Fence):
166+
return Fence._from_rs_fence(instr)
154167
if isinstance(instr, quil_rs.Gate):
155168
return Gate._from_rs_gate(instr)
156169
if isinstance(instr, quil_rs.MeasureCalibrationDefinition):
157170
return DefMeasureCalibration._from_rs_measure_calibration_definition(instr)
158171
if isinstance(instr, quil_rs.Measurement):
159172
return Measurement._from_rs_measurement(instr)
173+
if isinstance(instr, quil_rs.Pragma):
174+
return Pragma._from_rs_pragma(instr)
175+
if isinstance(instr, quil_rs.Reset):
176+
return Reset._from_rs_reset(instr)
160177
if isinstance(instr, quil_rs.CircuitDefinition):
161178
return DefCircuit._from_rs_circuit_definition(instr)
162179
if isinstance(instr, quil_rs.GateDefinition):
@@ -424,24 +441,65 @@ def out(self) -> str:
424441
return str(self)
425442

426443

427-
class ResetQubit(AbstractInstruction):
444+
class Reset(quil_rs.Reset, AbstractInstruction):
428445
"""
429-
This is the pyQuil object for a Quil targeted reset instruction.
446+
The RESET instruction.
430447
"""
431448

432-
def __init__(self, qubit: Union[Qubit, QubitPlaceholder, FormalArgument]):
433-
if not isinstance(qubit, (Qubit, QubitPlaceholder, FormalArgument)):
434-
raise TypeError("qubit should be a Qubit")
435-
self.qubit = qubit
449+
def __new__(cls, qubit: Optional[Union[Qubit, QubitPlaceholder, FormalArgument]] = None):
450+
rs_qubit: Optional[quil_rs.Qubit] = None
451+
if qubit is not None:
452+
rs_qubit = _convert_to_rs_qubit(qubit)
453+
return super().__new__(cls, rs_qubit)
454+
455+
@classmethod
456+
def _from_rs_reset(cls, reset: quil_rs.Reset) -> "Reset":
457+
return super().__new__(cls, reset.qubit)
436458

437459
def out(self) -> str:
438-
return "RESET {}".format(self.qubit.out())
460+
return str(self)
439461

440-
def __str__(self) -> str:
441-
return "RESET {}".format(_format_qubit_str(self.qubit))
462+
@deprecated(
463+
deprecated_in="4.0",
464+
removed_in="5.0",
465+
current_version=pyquil_version,
466+
details="The indices flag will be removed, use get_qubit_indices() instead.",
467+
)
468+
def get_qubits(self, indices: bool = True) -> Optional[Set[QubitDesignator]]:
469+
if super().qubit is None:
470+
return None
471+
if indices:
472+
return self.get_qubit_indices() # type: ignore
473+
return {_convert_to_py_qubit(super().qubit)} # type: ignore
474+
475+
def get_qubit_indices(self) -> Optional[Set[int]]:
476+
if super().qubit is None:
477+
return None
478+
return {super().qubit.to_fixed()} # type: ignore
479+
480+
@property
481+
def qubit(self) -> Optional[QubitDesignator]:
482+
if super().qubit:
483+
return _convert_to_py_qubit(super().qubit) # type: ignore
484+
return super().qubit
485+
486+
@qubit.setter
487+
def qubit(self, qubit: Optional[QubitDesignator]):
488+
rs_qubit: Optional[quil_rs.Qubit] = None
489+
if qubit is not None:
490+
rs_qubit = _convert_to_rs_qubit(qubit)
491+
quil_rs.Reset.qubit.__set__(self, rs_qubit)
442492

443-
def get_qubits(self, indices: bool = True) -> Set[QubitDesignator]:
444-
return {_extract_qubit_index(self.qubit, indices)}
493+
494+
class ResetQubit(Reset):
495+
"""
496+
This is the pyQuil object for a Quil targeted reset instruction.
497+
"""
498+
499+
def __new__(cls, qubit: Union[Qubit, QubitPlaceholder, FormalArgument]):
500+
if qubit is None:
501+
raise TypeError("qubit should not be None")
502+
return super().__new__(cls, qubit)
445503

446504

447505
class DefGate(quil_rs.GateDefinition, AbstractInstruction):
@@ -671,14 +729,6 @@ class Wait(SimpleInstruction):
671729
op = "WAIT"
672730

673731

674-
class Reset(SimpleInstruction):
675-
"""
676-
The RESET instruction.
677-
"""
678-
679-
op = "RESET"
680-
681-
682732
class Nop(SimpleInstruction):
683733
"""
684734
The NOP instruction.
@@ -1006,7 +1056,7 @@ def out(self) -> str:
10061056
return "JUMP %s" % self.target
10071057

10081058

1009-
class Pragma(AbstractInstruction):
1059+
class Pragma(quil_rs.Pragma, AbstractInstruction):
10101060
"""
10111061
A PRAGMA instruction.
10121062
@@ -1016,39 +1066,67 @@ class Pragma(AbstractInstruction):
10161066
10171067
"""
10181068

1019-
def __init__(
1020-
self,
1069+
def __new__(
1070+
cls,
10211071
command: str,
10221072
args: Iterable[Union[QubitDesignator, str]] = (),
10231073
freeform_string: str = "",
1024-
):
1025-
if not isinstance(command, str):
1026-
raise TypeError(f"Pragma's require an identifier: {command}")
1027-
1028-
if not isinstance(args, collections.abc.Iterable):
1029-
raise TypeError(f"Pragma arguments must be an Iterable: {args}")
1030-
for a in args:
1031-
if not (
1032-
isinstance(a, str) or isinstance(a, int) or isinstance(a, QubitPlaceholder) or isinstance(a, Qubit)
1033-
):
1034-
raise TypeError(f"Pragma arguments must be strings or integers: {a}")
1035-
if not isinstance(freeform_string, str):
1036-
raise TypeError(f"The freeform string argument must be a string: {freeform_string}")
1037-
1038-
self.command = command
1039-
self.args = tuple(args)
1040-
self.freeform_string = freeform_string
1074+
) -> Type["Pragma"]:
1075+
data = freeform_string or None
1076+
return super().__new__(cls, command, Pragma._to_pragma_arguments(args), data)
1077+
1078+
@classmethod
1079+
def _from_rs_pragma(cls, pragma: quil_rs.Pragma) -> "Pragma":
1080+
return super().__new__(cls, pragma.name, pragma.arguments, pragma.data)
1081+
1082+
@staticmethod
1083+
def _to_pragma_arguments(args: Iterable[Union[QubitDesignator, str]]) -> List[quil_rs.PragmaArgument]:
1084+
pragma_arguments = []
1085+
for arg in args:
1086+
if isinstance(arg, Qubit):
1087+
pragma_arguments.append(quil_rs.PragmaArgument.from_integer(arg.index))
1088+
elif isinstance(arg, (str, FormalArgument)):
1089+
pragma_arguments.append(quil_rs.PragmaArgument.from_identifier(str(arg)))
1090+
else:
1091+
raise TypeError(f"{type(arg)} isn't a valid QubitDesignator")
1092+
return pragma_arguments
1093+
1094+
@staticmethod
1095+
def _to_py_arguments(args: List[quil_rs.PragmaArgument]) -> List[QubitDesignator]:
1096+
arguments = []
1097+
for arg in args:
1098+
if arg.is_integer():
1099+
arguments.append(Qubit(arg.to_integer()))
1100+
else:
1101+
arguments.append(FormalArgument(arg.to_identifier()))
1102+
return arguments
10411103

10421104
def out(self) -> str:
1043-
ret = "PRAGMA {}".format(self.command)
1044-
if self.args:
1045-
ret += " {}".format(" ".join(str(a) for a in self.args))
1046-
if self.freeform_string:
1047-
ret += ' "{}"'.format(self.freeform_string)
1048-
return ret
1105+
return str(self)
10491106

1050-
def __repr__(self) -> str:
1051-
return "<PRAGMA {}>".format(self.command)
1107+
@property
1108+
def command(self) -> str:
1109+
return super().name
1110+
1111+
@command.setter
1112+
def command(self, command: str):
1113+
quil_rs.Pragma.name.__set__(self, command)
1114+
1115+
@property
1116+
def args(self) -> Tuple[QubitDesignator]:
1117+
return tuple(Pragma._to_py_arguments(super().arguments))
1118+
1119+
@args.setter
1120+
def args(self, args: str):
1121+
quil_rs.Pragma.arguments.__set__(self, Pragma._to_pragma_arguments(args))
1122+
1123+
@property
1124+
def freeform_string(self) -> str:
1125+
return super().data or ""
1126+
1127+
@freeform_string.setter
1128+
def freeform_string(self, freeform_string: str):
1129+
quil_rs.Pragma.data.__set__(self, freeform_string)
10521130

10531131

10541132
class Declare(quil_rs.Declaration, AbstractInstruction):
@@ -1316,50 +1394,95 @@ def get_qubits(self, indices: bool = True) -> Set[QubitDesignator]:
13161394
return _get_frame_qubits(self.frame, indices)
13171395

13181396

1319-
class DelayFrames(AbstractInstruction):
1320-
def __init__(self, frames: List[Frame], duration: float):
1321-
# all frames should be on the same qubits
1322-
if len(frames) == 0:
1323-
raise ValueError("DELAY expected nonempty list of frames.")
1324-
if len(set(tuple(f.qubits) for f in frames)) != 1:
1325-
raise ValueError("DELAY with explicit frames requires all frames are on the same qubits.")
1397+
class Delay(quil_rs.Delay, AbstractInstruction):
1398+
def __new__(cls, frames: List[Frame], qubits: List[Union[int, Qubit, FormalArgument]], duration: float) -> "Delay":
1399+
frame_names = [frame.name for frame in frames]
1400+
rs_qubits = _convert_to_rs_qubits(Delay._join_frame_qubits(frames, qubits))
1401+
expression = quil_rs_expr.Expression.from_number(complex(duration))
1402+
return super().__new__(cls, expression, frame_names, rs_qubits)
13261403

1327-
self.frames = frames
1328-
self.duration = duration
1404+
@classmethod
1405+
def _from_rs_delay(cls, delay: quil_rs.Delay) -> "Delay":
1406+
return super().__new__(cls, delay.duration, delay.frame_names, delay.qubits)
1407+
1408+
@staticmethod
1409+
def _join_frame_qubits(
1410+
frames: List[Frame], qubits: List[Union[int, Qubit, FormalArgument]]
1411+
) -> List[Union[int, Qubit, FormalArgument]]:
1412+
merged_qubits = set(qubits)
1413+
for frame in frames:
1414+
merged_qubits.update(frame.qubits) # type: ignore
1415+
return list(merged_qubits)
13291416

13301417
def out(self) -> str:
1331-
qubits = self.frames[0].qubits
1332-
ret = "DELAY " + _format_qubits_str(qubits)
1333-
for f in self.frames:
1334-
ret += f' "{f.name}"'
1335-
ret += f" {self.duration}"
1336-
return ret
1418+
return str(self)
13371419

1420+
@property
1421+
def qubits(self) -> List[QubitDesignator]:
1422+
return _convert_to_py_qubits(super().qubits)
13381423

1339-
class DelayQubits(AbstractInstruction):
1340-
def __init__(self, qubits: List[Union[Qubit, FormalArgument]], duration: float):
1341-
self.qubits = qubits
1342-
self.duration = duration
1424+
@qubits.setter
1425+
def qubits(self, qubits: List[Union[int, Qubit, FormalArgument]]):
1426+
quil_rs.Delay.qubits.__set__(self, _convert_to_rs_qubits(qubits))
13431427

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

1432+
@frames.setter
1433+
def frames(self, frames: List[Frame]):
1434+
new_qubits = Delay._join_frame_qubits(frames, [])
1435+
frame_names = [frame.name for frame in frames]
1436+
quil_rs.Delay.qubits.__set__(self, _convert_to_rs_qubits(new_qubits))
1437+
quil_rs.Delay.frame_names.__set__(self, frame_names)
13471438

1348-
class FenceAll(SimpleInstruction):
1349-
"""
1350-
The FENCE instruction.
1351-
"""
1439+
@property
1440+
def duration(self) -> float:
1441+
return super().duration.to_real()
1442+
1443+
@duration.setter
1444+
def duration(self, duration: float):
1445+
expression = quil_rs_expr.Expression.from_number(complex(duration))
1446+
quil_rs.Delay.duration.__set__(self, expression)
13521447

1353-
op = "FENCE"
13541448

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

1356-
class Fence(AbstractInstruction):
1357-
def __init__(self, qubits: List[Union[Qubit, FormalArgument]]):
1358-
self.qubits = qubits
1453+
1454+
class DelayQubits(Delay):
1455+
def __new__(cls, qubits: List[Union[Qubit, FormalArgument]], duration: float):
1456+
return super().__new__(cls, [], qubits, duration)
1457+
1458+
1459+
class Fence(quil_rs.Fence, AbstractInstruction):
1460+
def __new__(cls, qubits: List[Union[Qubit, FormalArgument]]):
1461+
return super().__new__(cls, _convert_to_rs_qubits(qubits))
1462+
1463+
@classmethod
1464+
def _from_rs_fence(cls, fence: quil_rs.Fence) -> "Fence":
1465+
return super().__new__(cls, fence.qubits)
13591466

13601467
def out(self) -> str:
1361-
ret = "FENCE " + _format_qubits_str(self.qubits)
1362-
return ret
1468+
return str(self)
1469+
1470+
@property
1471+
def qubits(self) -> List[Union[Qubit, FormalArgument]]:
1472+
return _convert_to_py_qubits(super().qubits)
1473+
1474+
@qubits.setter
1475+
def qubits(self, qubits: List[Union[Qubit, FormalArgument]]):
1476+
quil_rs.Fence.qubits.__set__(self, _convert_to_rs_qubits(qubits))
1477+
1478+
1479+
class FenceAll(Fence):
1480+
"""
1481+
The FENCE instruction.
1482+
"""
1483+
1484+
def __new__(cls):
1485+
return super().__new__(cls, [])
13631486

13641487

13651488
class DefWaveform(quil_rs.WaveformDefinition, AbstractInstruction):

0 commit comments

Comments
 (0)