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

Instruction API: Calibrations #155

Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ Cargo.lock

# JetBrains Editors
.idea/

# Python artifacts
*.so
7 changes: 7 additions & 0 deletions quil-py/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
max-line-length = 120
# E203, E302 ignored for black compatibility
extend-ignore = E203, E302

per-file-ignores =
**/__init__.pyi:F401,F403 # Disable "unused" warning for top-level exports
5 changes: 5 additions & 0 deletions quil-py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ sdist-include = ["README.md"]
[build-system]
requires = ["maturin>=0.13,<0.14"]
build-backend = "maturin"

[tool.black]
line-length = 120
target-version = ['py37', 'py38', 'py39', 'py310']
include = '\.pyi?$'
3 changes: 3 additions & 0 deletions quil-py/quil/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import instructions as instructions
import program as program
import validation as validation
64 changes: 64 additions & 0 deletions quil-py/quil/instructions/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from typing import Optional

from _calibration import Calibration, MeasureCalibrationDefinition
from _gate import (
Gate as Gate,
GateSpecification as GateSpecification,
GateModifier as GateModifier,
GateDefinition as GateDefinition,
)
from _expression import Expression as Expression
from _qubit import Qubit as Qubit

class Instruction:
"""
A Quil instruction. Each variant corresponds to a possible type of Quil instruction.

Variants:
``arithmetic``: An arithmetic expression
``calibration_definition``: Corresponds to a `DEFCAL` instruction (not `DEFCAL MEASURE`)
``declaration``: Corresponds to a `DECLARE` statement
``gate``: A Quil quantum gate instruction
``halt``: Corresponds to the `HALT` instruction.
``measure_calibration_definition``: Corresponds to a `DEFCAL MEASURE` instruction.
``nop``: Corresponds to the `NOP` instruction.

Methods (for each variant):
``is_*``: Returns ``True`` if the instruction is that variant, ``False`` otherwise.

If the variant has inner data:
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
``as_*``: Returns the inner data if it is the given variant, ``None`` otherwise.
``to_*``: Returns the inner data if it is the given variant, raises ``ValueError`` otherwise.
``from_*``: Creates a new ``Instruction`` of the given variant from an instance of the inner type.

If the variant doesn't have inner data (ie. ``halt``)
``new_*``: Creates a new ``Instruction`` for the variant
"""

def is_arithmetic(self) -> bool: ...
def is_calibration_definition(self) -> bool: ...
def is_declaration(self) -> bool: ...
def is_gate(self) -> bool: ...
def is_halt(self) -> bool: ...
def is_measure_calibration_definition(self) -> bool: ...
def is_nop(self) -> bool: ...
@staticmethod
def new_halt() -> "Instruction": ...
@staticmethod
def new_nop() -> "Instruction": ...
@staticmethod
def from_calibration_definition(calibration: Calibration) -> "Instruction": ...
@staticmethod
def from_gate(gate: Gate) -> "Instruction": ...
@staticmethod
def from_measure_calibration_definition(
measure_calibration_definition: MeasureCalibrationDefinition,
) -> "Instruction": ...
def as_calibration_definition(self) -> Optional[Calibration]: ...
def to_calibration_definition(self) -> Calibration: ...
def as_gate(self) -> Optional[Gate]: ...
def to_gate(self) -> Gate: ...
def as_measure_calibration_definition(
self,
) -> Optional[MeasureCalibrationDefinition]: ...
def to_measure_calibration_definition(self) -> MeasureCalibrationDefinition: ...
39 changes: 39 additions & 0 deletions quil-py/quil/instructions/_calibration.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from typing import List, Optional

from . import Instruction, Expression, Qubit, GateModifier

class Calibration:
@property
def name(self) -> str: ...
@property
def parameters(self) -> List[Expression]: ...
@property
def qubits(self) -> List[Qubit]: ...
@property
def instructions(self) -> List[Instruction]: ...
@property
def modifiers(self) -> List[GateModifier]: ...
@classmethod
def __new__(
cls,
name: str,
parameters: List[Expression],
qubits: List[Qubit],
instructions: List[Instruction],
modifiers: List[GateModifier],
) -> "Calibration": ...

class MeasureCalibrationDefinition:
@property
def qubit(self) -> Optional[Qubit]: ...
@property
def parameter(self) -> str: ...
@property
def instructions(self) -> List[Instruction]: ...
@classmethod
def __new__(
cls,
qubit: Optional[Qubit],
parameter: str,
instructions: List[Instruction],
) -> "MeasureCalibrationDefinition": ...
40 changes: 40 additions & 0 deletions quil-py/quil/instructions/_expression.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Optional

class Expression:
"""
A Quil expression.

Variants:
``address``: A memory reference
``function_call``: A function call
``infix``: An infix expression
``number``: A number
``pi``: The constant `pi`
``prefix``: A prefix expression
``variable``: A variable

Methods (for each variant):
``is_*``: Returns ``True`` if the expression is that variant, ``False`` otherwise.

If the variant has inner data:
``as_*``: returns the inner data if it is the given variant, ``None`` otherwise.
``to_*``: returns the inner data if it is the given variant, raises ``ValueError`` otherwise.
``from_*``: Creates a new ``Expression`` of the given variant from an instance of the inner type.

If the variant doesn't have inner data (ie. ``pi``)
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
``new_*``: Creates a new ``Expression`` for the variant
"""

def is_address(self) -> bool: ...
def is_function_call(self) -> bool: ...
def is_infix(self) -> bool: ...
def is_number(self) -> bool: ...
def is_pi(self) -> bool: ...
def is_prefix(self) -> bool: ...
def is_variable(self) -> bool: ...
def as_number(self) -> Optional[complex]: ...
def to_number(self) -> complex: ...
def as_variable(self) -> Optional[str]: ...
def to_variable(self) -> str: ...

# TODO: Define the rest of the methods as part of building out the API for each variant
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
85 changes: 85 additions & 0 deletions quil-py/quil/instructions/_gate.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from enum import Enum
from typing import List, Optional

from . import Expression, Qubit

class GateModifier(Enum):
Controlled = "CONTROLLED"
Dagger = "DAGGER"
Forked = "FORKED"

class Gate:
@classmethod
def __new__(
cls,
name: str,
parameters: List[Expression],
qubits: List[Qubit],
modifiers: List[GateModifier],
) -> "Gate": ...
@property
def name(self) -> str: ...
@property
def parameters(self) -> List[Expression]: ...
@property
def qubits(self) -> List[Qubit]: ...
@property
def modifiers(self) -> List[GateModifier]: ...
def dagger(self) -> "Gate":
"""
Returns a copy of the gate with the ``DAGGER`` modififer added to it.
"""
...
def controlled(self, control_qubit: Qubit) -> "Gate":
"""
Returns a copy of the gate with the ``CONTROLLED`` modififer added to it.
"""
def forked(self, fork_qubit: Qubit, alt_params: List[Expression]) -> "Gate":
"""
Returns a copy of the gate with the ``FORKED`` modifier added to it.

Raises a ``GateError`` if the number of provided alternate parameters don't
equal the number of existing parameters.
"""
...

class GateSpecification:
"""
A specification for a gate definition.

Variants:
``matrix``: A gate specificied by a matrix of ``Expression``s representing a unitary operation.
``permutation``: A gate specified by a vector of integers that defines a permutation.

Methods (for each variant):
- is_*: Returns ``True`` if the inner type is of that variant.
- as_*: Returns the inner data if it is the given variant, ``None`` otherwise.
- to_*: Returns the inner data if it is the given variant, raises ``ValueError`` otherwise.
- from_*: Creates a new ``GateSpecification`` using an instance of the inner type for the variant.
"""

def is_matrix(self) -> bool: ...
def is_permutation(self) -> bool: ...
def as_matrix(self) -> Optional[List[List[Expression]]]: ...
def to_matrix(self) -> List[List[Expression]]: ...
def as_permutation(self) -> Optional[List[int]]: ...
def to_permutation(self) -> List[int]: ...
@staticmethod
def from_matrix(matrix: List[List[Expression]]) -> "GateSpecification": ...
@staticmethod
def from_permutation(permutation: List[int]) -> "GateSpecification": ...

class GateDefinition:
@classmethod
def __new__(
cls,
name: str,
parameters: List[str],
specification: GateSpecification,
) -> "GateDefinition": ...
@property
def name(self) -> str: ...
@property
def parameters(self) -> List[str]: ...
@property
def specification(self) -> GateSpecification: ...
27 changes: 27 additions & 0 deletions quil-py/quil/instructions/_qubit.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import Optional

class Qubit:
"""
A Qubit

Variants:
``fixed``: A qubit represented as a fixed integer index.
``variable``: A qubit represented by a name.

Methods (for each variant):
- is_*: Returns ``True`` if the inner type is of that variant.
- as_*: Returns the inner data if it is the given variant, ``None`` otherwise.
- to_*: Returns the inner data if it is the given variant, raises ``ValueError`` otherwise.
- from_*: Creates a new ``Qubit`` using an instance of the inner type for the variant.
"""

def is_fixed(self) -> bool: ...
def is_variable(self) -> bool: ...
def as_fixed(self) -> Optional[int]: ...
def to_fixed(self) -> int: ...
def as_variable(self) -> Optional[str]: ...
def to_variable(self) -> str: ...
@staticmethod
def from_fixed(index: int) -> "Qubit": ...
@staticmethod
def from_variable(name: str) -> "Qubit": ...
Empty file added quil-py/quil/py.typed
Empty file.
1 change: 1 addition & 0 deletions quil-py/quil/validation/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import identifier as identifier
16 changes: 16 additions & 0 deletions quil-py/quil/validation/identifier.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class IdentifierValidationError(ValueError):
"""Errors that may occur when validating a Quil identifier."""

...

def validate_identifier(ident: str):
"""
Raises an ``IdentifierValidationError` if ident isn't a valid Quil identifier.
"""
...

def validate_user_identifier(ident: str):
"""
Raises an ``IdentifierValidationError` if ident is reserved keyword or isn't a valid Quil identifier.
"""
...
50 changes: 48 additions & 2 deletions quil-py/src/instruction/calibration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ use quil_rs::{

use rigetti_pyo3::{
impl_repr, impl_str, py_wrap_data_struct,
pyo3::{types::PyString, Py},
pyo3::{pymethods, types::PyString, Py, PyResult, Python},
PyTryFrom, ToPythonError,
};

use crate::instruction::{PyExpression, PyGateModifier, PyInstruction, PyQubit};
use crate::{
instruction::{PyExpression, PyGateModifier, PyInstruction, PyQubit},
validation::identifier::IdentifierValidationError,
};

py_wrap_data_struct! {
PyCalibration(Calibration) as "Calibration" {
Expand All @@ -22,6 +26,31 @@ py_wrap_data_struct! {
impl_repr!(PyCalibration);
impl_str!(PyCalibration);

#[pymethods]
impl PyCalibration {
#[new]
pub fn new(
py: Python<'_>,
name: &str,
parameters: Vec<PyExpression>,
qubits: Vec<PyQubit>,
instructions: Vec<PyInstruction>,
modifiers: Vec<PyGateModifier>,
) -> PyResult<Self> {
Ok(Self(
Calibration::new(
name,
Vec::<Expression>::py_try_from(py, &parameters)?,
Vec::<Qubit>::py_try_from(py, &qubits)?,
Vec::<Instruction>::py_try_from(py, &instructions)?,
Vec::<GateModifier>::py_try_from(py, &modifiers)?,
)
.map_err(IdentifierValidationError::from)
.map_err(IdentifierValidationError::to_py_err)?,
))
}
}

py_wrap_data_struct! {
PyMeasureCalibrationDefinition(MeasureCalibrationDefinition) as "MeasureCalibrationDefinition" {
qubit: Option<Qubit> => Option<PyQubit>,
Expand All @@ -31,3 +60,20 @@ py_wrap_data_struct! {
}
impl_repr!(PyMeasureCalibrationDefinition);
impl_str!(PyMeasureCalibrationDefinition);

#[pymethods]
impl PyMeasureCalibrationDefinition {
#[new]
pub fn new(
py: Python<'_>,
qubit: Option<PyQubit>,
parameter: String,
instructions: Vec<PyInstruction>,
) -> PyResult<Self> {
Ok(Self(MeasureCalibrationDefinition::new(
Option::<Qubit>::py_try_from(py, &qubit)?,
parameter,
Vec::<Instruction>::py_try_from(py, &instructions)?,
)))
}
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 5 additions & 3 deletions quil-py/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ mod waveform;
py_wrap_union_enum! {
#[derive(Debug)]
PyInstruction(Instruction) as "Instruction" {
halt: Halt,
nop: Nop,
arithmetic: Arithmetic => PyArithmetic,
calibration_definition: CalibrationDefinition => PyCalibration,
declaration: Declaration => PyDeclaration,
gate: Gate => PyGate,
declaration: Declaration => PyDeclaration
halt: Halt,
measure_calibration_definition: MeasureCalibrationDefinition => PyMeasureCalibrationDefinition,
nop: Nop
}
}
impl_repr!(PyInstruction);
Expand Down
Loading