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

feat: add source mapping for calibration expansion #370

Merged
merged 41 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1a1e8a6
feat: add source mapping for calibration expansion
kalzoo May 4, 2024
3a319f1
chore: improve docs
kalzoo May 7, 2024
e13abf7
fix bug in target range mapping in calibration expansion
kalzoo May 7, 2024
917ba23
WIP: header-aware program calibration expansion
kalzoo May 7, 2024
bf246b3
fix bug in program calibration expansion into header instructions
kalzoo May 9, 2024
277105d
remove unnecessary SourceMapRange trait
kalzoo May 9, 2024
b35f8fe
add source map python bindings
kalzoo May 9, 2024
1bec330
feat!: refactor identifiers out of Calibration and MeasureCalibration…
kalzoo May 9, 2024
ec90415
add and fix py stubs for CalibrationIdentifier and MeasureCalibration…
kalzoo May 9, 2024
2ecf658
reorder bindings file in lex order
kalzoo May 9, 2024
8446b61
add source-map py stubs
kalzoo May 9, 2024
feaa61c
Add py stub for Program.expand_calibrations_with_source_map
kalzoo May 9, 2024
00119a6
chore: docs
kalzoo May 9, 2024
1965440
chore: fmt
kalzoo May 9, 2024
8906a76
refactor instruction calibration expansion so that source maps are on…
kalzoo May 9, 2024
645e1a2
update snapshot
kalzoo May 9, 2024
cafe117
chore: lint
kalzoo May 10, 2024
3216ef3
chore: cleanup
kalzoo Jul 1, 2024
ac63749
add query (lookup) operations for SourceMap
kalzoo Jul 22, 2024
4b61fc8
fix bug in calibration expansion
kalzoo Jul 22, 2024
07ae1d3
chore: lint
kalzoo Jul 22, 2024
78df471
fix range return type
kalzoo Jul 22, 2024
25bd576
add test
kalzoo Jul 22, 2024
f64aa8a
chore: cleanup
kalzoo Jul 22, 2024
d3abce6
chore: fix docs
kalzoo Jul 22, 2024
c97dcca
chore: fix test
kalzoo Jul 22, 2024
8ffb15a
chore: cleanup
kalzoo Jul 22, 2024
2ca8eea
chore: cleanup
kalzoo Jul 22, 2024
8f91186
chore: fix comment
kalzoo Jul 29, 2024
56476ca
fix test
kalzoo Jul 29, 2024
5d9f759
make test more readable
kalzoo Jul 29, 2024
59daee1
chore: fix readme
kalzoo Jul 29, 2024
7a5972c
chore: lint
kalzoo Jul 29, 2024
5d31908
add useful example to Python docs
kalzoo Jul 29, 2024
de94d05
fix doc example
kalzoo Jul 29, 2024
5c05993
fix doc example
kalzoo Jul 29, 2024
a879779
Merge branch 'main' into 366-source-mapping
kalzoo Jul 29, 2024
a298437
chore: lint
kalzoo Jul 29, 2024
9a3b453
Merge branch 'main' into 366-source-mapping
kalzoo Oct 11, 2024
022900a
make InstructionIndex a newtype struct for source mapping
kalzoo Oct 12, 2024
f72ae40
add missing from_ methods to new python bindings
kalzoo Oct 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions Cargo.lock

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

96 changes: 90 additions & 6 deletions quil-py/quil/instructions/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -826,11 +826,8 @@ class UnaryLogic:
class Calibration:
def __new__(
cls,
name: str,
parameters: Sequence[Expression],
qubits: Sequence[Qubit],
identifier: CalibrationIdentifier,
instructions: Sequence[Instruction],
modifiers: Sequence[GateModifier],
) -> Self: ...
@property
def name(self) -> str: ...
Expand All @@ -845,6 +842,10 @@ class Calibration:
@qubits.setter
def qubits(self, qubits: Sequence[Qubit]) -> None: ...
@property
def identifier(self) -> CalibrationIdentifier: ...
@identifier.setter
def identifier(self, identifier: CalibrationIdentifier) -> None: ...
@property
def instructions(self) -> List[Instruction]: ...
@instructions.setter
def instructions(self, instructions: Sequence[Instruction]) -> None: ...
Expand Down Expand Up @@ -873,11 +874,55 @@ class Calibration:
def __copy__(self) -> Self:
"""Returns a shallow copy of the class."""

class CalibrationIdentifier:
def __new__(
cls,
name: str,
parameters: Sequence[Expression],
qubits: Sequence[Qubit],
modifiers: Sequence[GateModifier],
) -> Self: ...
@property
def name(self) -> str: ...
@name.setter
def name(self, name: str) -> None: ...
@property
def parameters(self) -> List[Expression]: ...
@parameters.setter
def parameters(self, parameters: Sequence[Expression]) -> None: ...
@property
def qubits(self) -> List[Qubit]: ...
@qubits.setter
def qubits(self, qubits: Sequence[Qubit]) -> None: ...
@property
def modifiers(self) -> List[GateModifier]: ...
@modifiers.setter
def modifiers(self, modifiers: Sequence[GateModifier]) -> None: ...
def to_quil(self) -> str:
"""Attempt to convert the instruction to a valid Quil string.

Raises an exception if the instruction can't be converted to valid Quil.
"""
...
def to_quil_or_debug(self) -> str:
"""Convert the instruction to a Quil string.

If any part of the instruction can't be converted to valid Quil, it will be printed in a human-readable debug format.
"""
def __deepcopy__(self, _: Dict) -> Self:
"""Creates and returns a deep copy of the class.

If the instruction contains any ``QubitPlaceholder`` or ``TargetPlaceholder``, then they will be replaced with
new placeholders so resolving them in the copy will not resolve them in the original.
Should be used by passing an instance of the class to ``copy.deepcopy``
"""
def __copy__(self) -> Self:
"""Returns a shallow copy of the class."""

class MeasureCalibrationDefinition:
def __new__(
cls,
qubit: Optional[Qubit],
parameter: str,
identifier: MeasureCalibrationIdentifier,
instructions: Sequence[Instruction],
) -> Self: ...
@property
Expand All @@ -889,6 +934,10 @@ class MeasureCalibrationDefinition:
@parameter.setter
def parameter(self, parameter: str) -> None: ...
@property
def identifier(self) -> MeasureCalibrationIdentifier: ...
@identifier.setter
def identifier(self, identifier: MeasureCalibrationIdentifier) -> None: ...
@property
def instructions(self) -> List[Instruction]: ...
@instructions.setter
def instructions(self, instructions: Sequence[Instruction]) -> None: ...
Expand All @@ -913,6 +962,41 @@ class MeasureCalibrationDefinition:
def __copy__(self) -> Self:
"""Returns a shallow copy of the class."""

class MeasureCalibrationIdentifier:
def __new__(
cls,
qubit: Optional[Qubit],
parameter: str,
) -> Self: ...
@property
def qubit(self) -> Optional[Qubit]: ...
@qubit.setter
def qubit(self, qubit: Optional[Qubit]) -> None: ...
@property
def parameter(self) -> str: ...
@parameter.setter
def parameter(self, parameter: str) -> None: ...
def to_quil(self) -> str:
"""Attempt to convert the instruction to a valid Quil string.

Raises an exception if the instruction can't be converted to valid Quil.
"""
...
def to_quil_or_debug(self) -> str:
"""Convert the instruction to a Quil string.

If any part of the instruction can't be converted to valid Quil, it will be printed in a human-readable debug format.
"""
def __deepcopy__(self, _: Dict) -> Self:
"""Creates and returns a deep copy of the class.

If the instruction contains any ``QubitPlaceholder`` or ``TargetPlaceholder``, then they will be replaced with
new placeholders so resolving them in the copy will not resolve them in the original.
Should be used by passing an instance of the class to ``copy.deepcopy``
"""
def __copy__(self) -> Self:
"""Returns a shallow copy of the class."""

class CircuitDefinition:
def __new__(
cls,
Expand Down
125 changes: 124 additions & 1 deletion quil-py/quil/program/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable, Dict, FrozenSet, List, Optional, Sequence, Set, final
from typing import Callable, Dict, FrozenSet, List, Optional, Sequence, Set, Tuple, Union, final

import numpy as np
from numpy.typing import NDArray
Expand All @@ -7,12 +7,14 @@ from typing_extensions import Self
from quil.instructions import (
AttributeValue,
Calibration,
CalibrationIdentifier,
Declaration,
FrameIdentifier,
Gate,
GateDefinition,
Instruction,
MeasureCalibrationDefinition,
MeasureCalibrationIdentifier,
Measurement,
MemoryReference,
Qubit,
Expand Down Expand Up @@ -65,6 +67,12 @@ class Program:
expands directly or indirectly into itself)
"""
...
def expand_calibrations_with_source_map(self) -> ProgramCalibrationExpansion:
"""Expand any instructions in the program which have a matching calibration, leaving the others unchanged.

Return the expanded copy of the program and a source mapping describing the expansions made.
"""
...
def into_simplified(self) -> "Program":
"""Simplify this program into a new `Program` which contains only instructions and definitions which are executed; effectively, perform dead code removal.

Expand Down Expand Up @@ -258,6 +266,78 @@ class BasicBlock:
If this is ``None``, the implicit behavior is to "continue" to the subsequent block.
"""

@final
class CalibrationExpansion:
def calibration_used(self) -> CalibrationSource: ...
"""The calibration which was used to expand the instruction."""
def range(self) -> Tuple[int, int]: ...
"""The range of instructions in the expanded list which were generated by this expansion."""
def expansions(self) -> CalibrationExpansionSourceMap: ...
"""The source map describing the nested expansions made."""

@final
class CalibrationExpansionSourceMap:
def entries(self) -> List[CalibrationExpansionSourceMapEntry]: ...

@final
class CalibrationExpansionSourceMapEntry:
"""
A description of the expansion of one instruction into other instructions.

If present, the instruction located at `source_location` was expanded using calibrations
into the instructions located at `target_location`.

Note that both `source_location` and `target_location` are relative to the scope of expansion.
In the case of a nested expansion, both describe the location relative only to that
level of expansion and *not* the original program.

Consider the following example:

```
DEFCAL A:
NOP
B
HALT


DEFCAL B:
NOP
WAIT

NOP
NOP
NOP
A
```

In this program, `A` will expand into `NOP`, `B`, and `HALT`. Then, `B` will expand into `NOP` and `WAIT`.
Each level of this expansion will have its own `CalibrationExpansionSourceMap` describing the expansion.
In the map of `B` to `NOP` and `WAIT`, the `source_location` will be `1` because `B` is the second instruction
in `DEFCAL A`, even though `A` is the 4th instruction (index = 3) in the original program.
"""
def source_location(self) -> int: ...
"""The instruction index within the source program's body instructions."""
def target_location(self) -> CalibrationExpansion: ...
"""The location of the expanded instruction within the target program's body instructions."""

@final
class CalibrationSource:
"""
The source of a calibration expansion, which can be either a calibration (`DEFCAL`)
or a measure calibration (`DEFCAL MEASURE`).
"""
def as_calibration(self) -> CalibrationIdentifier: ...
def as_measure_calibration(self) -> MeasureCalibrationIdentifier: ...
def is_calibration(self) -> bool: ...
def is_measure_calibration(self) -> bool: ...
def to_calibration(self) -> CalibrationIdentifier: ...
def to_measure_calibration(self) -> MeasureCalibrationIdentifier: ...
@staticmethod
def from_calibration(inner: CalibrationIdentifier): ...
@staticmethod
def from_measure_calibration(inner: MeasureCalibrationIdentifier): ...
def inner(self) -> Union[CalibrationIdentifier, MeasureCalibrationIdentifier]: ...

@final
class CalibrationSet:
@staticmethod
Expand Down Expand Up @@ -318,6 +398,27 @@ class CalibrationSet:
"""Return the Quil instructions which describe the contained calibrations."""
...

@final
class MaybeCalibrationExpansion:
"""
The result of having expanded a certain instruction within a program. Has two variants:

- `expanded`: The instruction was expanded into other instructions, described by a `CalibrationExpansion`.
- `int`: The instruction was not expanded and is described by an integer, the index of the instruction
within the resulting program's body instructions.
"""
def as_expanded(self) -> CalibrationExpansion: ...
def as_unexpanded(self) -> int: ...
@staticmethod
def from_expanded(inner: CalibrationExpansion): ...
@staticmethod
def from_unexpanded(inner: int): ...
def inner(self) -> Union[CalibrationExpansion, int]: ...
def is_expanded(self) -> bool: ...
def is_unexpanded(self) -> bool: ...
def to_expanded(self) -> CalibrationExpansion: ...
def to_unexpanded(self) -> int: ...

class ScheduleSecondsItem:
"""A single item within a fixed schedule, representing a single instruction within a basic block."""

Expand Down Expand Up @@ -412,3 +513,25 @@ class MemoryRegion:
def sharing(self) -> Optional[Sharing]: ...
@sharing.setter
def sharing(self, sharing: Optional[Sharing]): ...

@final
class ProgramCalibrationExpansion:
def program(self) -> Program: ...
"""The program containing the expanded instructions"""
def source_map(self) -> ProgramCalibrationExpansionSourceMap: ...
"""The source mapping describing the expansions made"""

@final
class ProgramCalibrationExpansionSourceMap:
def entries(self) -> List[ProgramCalibrationExpansionSourceMapEntry]: ...

@final
class ProgramCalibrationExpansionSourceMapEntry:
"""
A description of the possible expansion of one instruction into other instructions
within the scope of a program's calibrations.
"""
def source_location(self) -> int: ...
"""The instruction index within the source program's body instructions."""
def target_location(self) -> MaybeCalibrationExpansion: ...
"""The location of the possibly-expanded instruction within the target program's body instructions."""
Loading
Loading