Skip to content

Commit

Permalink
V4: Deprecate format parameter (#1566)
Browse files Browse the repository at this point in the history
* update mypy, fix various lints, deprecate format_parameter

* returns variable length tuple

* a few more lints

* more lints
  • Loading branch information
MarquessV authored Apr 20, 2023
1 parent 31d5454 commit 7b33cd3
Show file tree
Hide file tree
Showing 5 changed files with 491 additions and 365 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ black = "^22.8.0"
flake8 = "^3.8.1"
pytest = "^6.2.2"
pytest-cov = "^2.11.1"
mypy = "0.981"
mypy = "1.2.0"
pytest-xdist = "^2.2.1"
pytest-rerunfailures = "^9.1.1"
pytest-timeout = "^1.4.2"
Expand Down
106 changes: 58 additions & 48 deletions pyquil/quilatom.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
##############################################################################

from fractions import Fraction
from numbers import Complex, Number
from numbers import Number
from typing import (
Any,
Callable,
Expand All @@ -32,6 +32,7 @@
Union,
cast,
)
from typing_extensions import Self
from deprecation import deprecated

import numpy as np
Expand Down Expand Up @@ -165,7 +166,7 @@ def register(cls, n: int) -> List["QubitPlaceholder"]:
QubitDesignator = Union[Qubit, QubitPlaceholder, FormalArgument, int]


def _convert_to_rs_qubit(qubit: QubitDesignator) -> quil_rs.Qubit:
def _convert_to_rs_qubit(qubit: Union[QubitDesignator, quil_rs.Qubit]) -> quil_rs.Qubit:
if isinstance(qubit, quil_rs.Qubit):
return qubit
if isinstance(qubit, Qubit):
Expand Down Expand Up @@ -311,22 +312,24 @@ def __hash__(self) -> int:
return hash(id(self))


ParameterDesignator = Union["Expression", "MemoryReference", Number, complex]
ParameterDesignator = Union["Expression", "MemoryReference", int, float, complex, np.number]


def _convert_to_rs_expression(parameter: ParameterDesignator) -> quil_rs_expr.Expression:
def _convert_to_rs_expression(
parameter: Union[ParameterDesignator, quil_rs_expr.Expression]
) -> 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)):
return quil_rs_expr.Expression.parse(str(parameter))
raise ValueError(f"{type(parameter)} is not a valid ParameterDesignator")


def _convert_to_rs_expressions(parameters: Iterable[ParameterDesignator]) -> List[quil_rs_expr.Expression]:
def _convert_to_rs_expressions(
parameters: Sequence[Union[ParameterDesignator, quil_rs_expr.Expression]]
) -> List[quil_rs_expr.Expression]:
return [_convert_to_rs_expression(parameter) for parameter in parameters]


Expand All @@ -348,23 +351,31 @@ def _convert_to_py_parameter(
return np.pi
if parameter.is_variable():
return Parameter(parameter.to_variable())
elif isinstance(parameter, (Expression, MemoryReference, quil_rs.MemoryReference, Number)):
elif isinstance(parameter, (Expression, MemoryReference, Number, complex)):
return parameter
raise ValueError(f"{type(parameter)} is not a valid ParameterDesignator")


def _convert_to_py_parameters(parameters: Iterable[ParameterDesignator]) -> List[ParameterDesignator]:
def _convert_to_py_parameters(
parameters: Sequence[Union[ParameterDesignator, quil_rs.MemoryReference, quil_rs_expr.Expression]]
) -> List[ParameterDesignator]:
return [_convert_to_py_parameter(parameter) for parameter in parameters]


@deprecated(
deprecated_in="4.0",
removed_in="5.0",
current_version=pyquil_version,
details="The format_parameter function will be removed.",
)
def format_parameter(element: ParameterDesignator) -> str:
"""
Formats a particular parameter. Essentially the same as built-in formatting except using 'i'
instead of 'j' for the imaginary number.
:param element: The parameter to format for Quil output.
"""
if isinstance(element, int) or isinstance(element, np.int_):
if isinstance(element, (int, np.integer)):
return repr(element)
elif isinstance(element, float):
return _check_for_pi(element)
Expand Down Expand Up @@ -554,6 +565,19 @@ def __init__(
self.expression = expression
self.fn = fn

@classmethod
def _from_rs_function_call(cls, function_call: quil_rs_expr.FunctionCallExpression) -> "Function":
expression = _convert_to_py_expression(function_call.expression)
if function_call.function == quil_rs_expr.ExpressionFunction.Cis:
return quil_cis(expression)
if function_call.function == quil_rs_expr.ExpressionFunction.Cosine:
return quil_cos(expression)
if function_call.function == quil_rs_expr.ExpressionFunction.Exponent:
return quil_exp(expression)
if function_call.function == quil_rs_expr.ExpressionFunction.Sine:
return quil_sin(expression)
return quil_sqrt(expression)

def _substitute(self, d: ParameterSubstitutionsMapDesignator) -> Union["Function", ExpressionValueDesignator]:
sop = substitute(self.expression, d)
if isinstance(sop, Expression):
Expand Down Expand Up @@ -606,7 +630,7 @@ def __init__(self, op1: ExpressionDesignator, op2: ExpressionDesignator):
self.op2 = op2

@classmethod
def _from_rs_infix_expression(cls, infix_expression: quil_rs_expr.InfixExpression):
def _from_rs_infix_expression(cls, infix_expression: quil_rs_expr.InfixExpression) -> "BinaryExp":
left = _convert_to_py_expression(infix_expression.left)
right = _convert_to_py_expression(infix_expression.right)
if infix_expression.operator == quil_rs_expr.InfixOperator.Plus:
Expand Down Expand Up @@ -875,49 +899,40 @@ class Frame(quil_rs.FrameIdentifier):
Representation of a frame descriptor.
"""

@staticmethod
def __new__(cls, qubits: Sequence[Union[int, Qubit, FormalArgument]], name: str) -> "Frame":
def __new__(cls, qubits: Sequence[QubitDesignator], name: str) -> Self:
return super().__new__(cls, name, _convert_to_rs_qubits(qubits))

@classmethod
def _from_rs_frame_identifier(cls, frame: quil_rs.FrameIdentifier) -> "Frame":
return cls(frame.qubits, frame.name)
return super().__new__(cls, frame.name, frame.qubits)

@property
def qubits(self) -> Tuple[QubitDesignator]:
@property # type: ignore[override]
def qubits(self) -> Tuple[QubitDesignator, ...]:
return tuple(_convert_to_py_qubits(super().qubits))

@qubits.setter
def qubits(self, qubits: Tuple[Qubit, FormalArgument]):
return quil_rs.FrameIdentifier.qubits.__set__(self, _convert_to_rs_qubits(qubits))

@property
def name(self) -> str:
return super().name

@name.setter
def name(self, name: str):
return quil_rs.FrameIdentifier.name.__set__(self, name)
def qubits(self, qubits: Tuple[Qubit, FormalArgument]) -> None:
quil_rs.FrameIdentifier.qubits.__set__(self, _convert_to_rs_qubits(qubits)) # type: ignore[attr-defined]

def out(self) -> str:
return str(self)


class WaveformInvocation(quil_rs.WaveformInvocation, QuilAtom):
def __new__(cls, name: str, parameters: Optional[Dict[str, ParameterDesignator]] = None):
def __new__(cls, name: str, parameters: Optional[Dict[str, ParameterDesignator]] = None) -> Self:
if parameters is None:
parameters = {}
rs_parameters = {key: _convert_to_rs_expression(value) for key, value in parameters.items()}
return super().__new__(cls, name, rs_parameters)

@property
@property # type: ignore[override]
def parameters(self) -> Dict[str, ParameterDesignator]:
return {key: _convert_to_py_parameter(value) for key, value in super().parameters.items()}

@parameters.setter
def parameters(self, parameters: Dict[str, ParameterDesignator]):
def parameters(self, parameters: Dict[str, ParameterDesignator]) -> None:
rs_parameters = {key: _convert_to_rs_expression(value) for key, value in parameters.items()}
quil_rs.WaveformInvocation.parameters.__set__(self, rs_parameters)
quil_rs.WaveformInvocation.parameters.__set__(self, rs_parameters) # type: ignore[attr-defined]

def out(self) -> str:
return str(self)
Expand All @@ -934,24 +949,24 @@ class WaveformReference(WaveformInvocation):
Representation of a Waveform reference.
"""

def __new__(cls, name: str):
def __new__(cls, name: str) -> Self:
return super().__new__(cls, name, {})


def _template_waveform_property(name: str, doc: Optional[str] = None):
def _template_waveform_property(name: str, doc: Optional[str] = None) -> property:
"""
Helper method for initializing getters, setters, and deleters for
parameters on a ``TemplateWaveform``. Should only be used inside of
``TemplateWaveform`` or one its base classes.
"""

def fget(self: "TemplateWaveform"):
def fget(self: "TemplateWaveform") -> Optional[ParameterDesignator]:
return self.get_parameter(name)

def fset(self: "TemplateWaveform", value: ParameterDesignator):
def fset(self: "TemplateWaveform", value: ParameterDesignator) -> None:
self.set_parameter(name, value)

def fdel(self: "TemplateWaveform"):
def fdel(self: "TemplateWaveform") -> None:
self.set_parameter(name, None)

return property(fget, fset, fdel, doc)
Expand All @@ -964,7 +979,7 @@ class TemplateWaveform(quil_rs.WaveformInvocation, QuilAtom):
current_version=pyquil_version,
details="The TemplateWaveform class will be removed, consider using WaveformInvocation instead.",
)
def __new__(cls, name: str, *, duration: float, **kwargs):
def __new__(cls, name: str, *, duration: float, **kwargs: Union[ParameterDesignator, ExpressionDesignator]) -> Self:
rs_parameters = {key: _convert_to_rs_expression(value) for key, value in kwargs.items() if value is not None}
rs_parameters["duration"] = _convert_to_rs_expression(duration)
return super().__new__(cls, name, rs_parameters)
Expand All @@ -973,15 +988,18 @@ def out(self) -> str:
return str(self)

def get_parameter(self, name: str) -> Optional[ParameterDesignator]:
return _convert_to_py_parameter(super().parameters.get(name, None))
parameter = super().parameters.get(name, None)
if parameter is None:
return None
return _convert_to_py_parameter(parameter)

def set_parameter(self, name: str, value: Optional[ParameterDesignator]):
def set_parameter(self, name: str, value: Optional[ParameterDesignator]) -> None:
parameters = super().parameters
if value is None:
parameters.pop(name, None)
else:
parameters[name] = _convert_to_rs_expression(value)
quil_rs.WaveformInvocation.parameters.__set__(self, parameters)
quil_rs.WaveformInvocation.parameters.__set__(self, parameters) # type: ignore[attr-defined]

duration = _template_waveform_property("duration")

Expand Down Expand Up @@ -1012,7 +1030,7 @@ def samples(self, rate: float) -> np.ndarray:
raise NotImplementedError()

@classmethod
def _from_rs_waveform_invocation(cls, waveform: quil_rs.WaveformInvocation):
def _from_rs_waveform_invocation(cls, waveform: quil_rs.WaveformInvocation) -> "TemplateWaveform":
return super().__new__(cls, waveform.name, waveform.parameters)


Expand Down Expand Up @@ -1054,11 +1072,3 @@ def _convert_to_py_waveform(waveform: quil_rs.WaveformInvocation) -> Waveform:
return WaveformReference(waveform.name)

return TemplateWaveform._from_rs_waveform_invocation(waveform)


def _complex_str(iq: Any) -> str:
"""Convert a number to a string."""
if isinstance(iq, Complex):
return f"{iq.real}" if iq.imag == 0.0 else f"{iq.real} + ({iq.imag})*i"
else:
return str(iq)
Loading

0 comments on commit 7b33cd3

Please sign in to comment.