Skip to content

Commit

Permalink
feat: Add utility for filtering Programs and a method for removing Qu…
Browse files Browse the repository at this point in the history
…il-T instructions (#1718)

* feat: Add utility for filtering Programs and a method for removing Quil-T instructions

* style: line length

* fix doc issue

* fix doctest snippet

* update example to use correct method

* update quil to fix erroneously filtered WAIT
  • Loading branch information
MarquessV authored Jan 18, 2024
1 parent ea29bb2 commit 5d10fd6
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 49 deletions.
25 changes: 24 additions & 1 deletion docs/source/programs_and_gates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,31 @@ any other instruction:

...

.. _quil_t_qvm_warning:

.. warning::
`DELAY` and other Quil-T instructions aren't supported by the QVM.

``DELAY`` and other Quil-T instructions are not supported by the QVM or ``quilc``. If you want to test the validity
of a Quil-T containing program on a QVM you should remove all Quil-T instructions before running it. You can do this
dynamically by checking the ``qam`` property on your requested :py:class:`~pyquil.api.QuantumComputer`:

.. testcode:: remove-quil-t

from pyquil.quil import Program
from pyquil.gates import DELAY, H
from pyquil.api import QVM, get_qc

qc = get_qc("2q-qvm")
p = Program(H(0))
p += DELAY(0, 200e-9)

# If we're using a QVM, remove the Quil-T instructions
if isinstance(qc.qam, QVM):
p = p.remove_quil_t_instructions()
else: # Otherwise, compile to native Quil
p = qc.compiler.native_quil_to_executable(p)

qc.run(p)

.. warning::
In pyQuil v3 and below, it was common to specify a delay using ``PRAGMA DELAY``. This is no longer supported in v4 because it
Expand Down
6 changes: 6 additions & 0 deletions docs/source/quilt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ as determining `T1`. The hardware is almost at your fingertips.

For examples, see the adjacent notebooks. For more information, see
the `Quil project homepage <https://github.com/rigetti/quil>`_.

.. warning::

Quil-T instructions are not supported by `quilc` or the QVM. See
:ref:`this note <quil_t_qvm_warning>` for a pattern you can use to
dynamically remove them before testing your program against a QVM.
92 changes: 46 additions & 46 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ rpcq = "^3.10.0"
pydantic = "^1.10.7"
networkx = ">=2.5"
importlib-metadata = { version = ">=3.7.3,<5", python = "<3.8" }
qcs-sdk-python = "0.16.0"
qcs-sdk-python = "0.16.3"
tenacity = "^8.2.2"
types-python-dateutil = "^2.8.19"
types-retry = "^0.9.9"
Expand Down
25 changes: 25 additions & 0 deletions pyquil/quil.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
_convert_to_rs_instruction,
_convert_to_rs_instructions,
_convert_to_py_instructions,
_convert_to_py_instruction,
_convert_to_py_qubits,
)
from pyquil.quiltcalibrations import (
Expand Down Expand Up @@ -452,6 +453,30 @@ def _add_instruction(self, instruction: quil_rs.Instruction) -> None:
else:
self._program.add_instruction(instruction)

def filter_instructions(self, predicate: Callable[[AbstractInstruction], bool]) -> "Program":
"""
Return a new ``Program`` containing only the instructions for which ``predicate`` returns ``True``.
:param predicate: A function that takes an instruction and returns ``True`` if the instruction should not be
removed from the program, ``False`` otherwise.
:return: A new ``Program`` object with the filtered instructions.
"""

def rs_predicate(inst: quil_rs.Instruction) -> bool:
return predicate(_convert_to_py_instruction(inst))

filtered_program = Program(self._program.filter_instructions(rs_predicate))
filtered_program.num_shots = self.num_shots
return filtered_program

def remove_quil_t_instructions(self) -> "Program":
"""
Return a copy of the program with all Quil-T instructions removed.
"""
filtered_program = Program(self._program.filter_instructions(lambda inst: not inst.is_quil_t()))
filtered_program.num_shots = self.num_shots
return filtered_program

def gate(
self,
name: str,
Expand Down
57 changes: 56 additions & 1 deletion test/unit/test_program.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import numpy as np

from pyquil import Program
from pyquil.quil import AbstractInstruction, Declare, Measurement, MemoryReference
from pyquil.experiment._program import (
measure_qubits,
parameterized_single_qubit_measurement_basis,
parameterized_single_qubit_state_preparation,
parameterized_readout_symmetrization,
)
from pyquil.gates import MEASURE, RX, RZ
from pyquil.gates import MEASURE, RX, RZ, H


def test_measure_qubits():
Expand Down Expand Up @@ -84,3 +85,57 @@ def test_adding_does_not_mutate():
p_all = p1 + p2
assert str(p1) == str(original_p1)
assert p1.calibrations != p_all.calibrations


def test_filter_program():
program = Program(Declare("ro", "BIT", 1), MEASURE(0, MemoryReference("ro", 1)), H(0))

def predicate(instruction: AbstractInstruction) -> bool:
if isinstance(instruction, Declare):
return instruction.name != "ro"
elif isinstance(instruction, Measurement):
return instruction.classical_reg.name != "ro"
else:
return True

filtered_program = program.filter_instructions(predicate)
assert filtered_program == Program(H(0))


def test_filter_quil_t():
non_quil_t_program = Program(
"""DECLARE ro BIT[1]
H 0
CNOT 0 1
MEASURE 0 ro[0]
MEASURE 1 ro[0]
WAIT
"""
)

quil_t_program = Program(
"""
DEFCAL I q:
DELAY q 4e-08
DEFFRAME 0 "rf":
DIRECTION: "tx"
DEFCAL MEASURE 0 addr:
FENCE 0
DEFWAVEFORM q44_q45_cphase/sqrtCPHASE:
0.0, 0.0, 0.00027685415721916584
CAPTURE 10 "ro_rx" boxcar_kernel(duration: 1.6e-06, scale: 1.0, phase: 0.0, detuning: 0.0) q10_unclassified[0]
NONBLOCKING CAPTURE 10 "ro_rx" boxcar_kernel(duration: 1.6e-06, scale: 1.0, phase: 0.0, detuning: 0.0) q10_unclassified[0]
FENCE 0
PULSE 0 "rf_f12" gaussian(duration: 6.000000000000001e-08, fwhm: 1.5000000000000002e-08, t0: 3.0000000000000004e-08, scale: 0.16297407445283926, phase: 0.0, detuning: 0)
RAW-CAPTURE 0 "out" 200000000 iqs[0]
SET-FREQUENCY 0 "xy" 5400000000
SET-PHASE 0 "xy" pi
SET-SCALE 0 "xy" pi
SHIFT-FREQUENCY 0 "ro" 6100000000
SHIFT-PHASE 0 "xy" (-pi)
SHIFT-PHASE 0 "xy" (%theta*(2/pi))
SWAP-PHASES 2 3 "xy" 3 4 "xy";
"""
)
full_program = non_quil_t_program + quil_t_program
assert full_program.remove_quil_t_instructions() == non_quil_t_program

0 comments on commit 5d10fd6

Please sign in to comment.