From 0658e22cc38c1ca82790b54756099e57bb2ab52e Mon Sep 17 00:00:00 2001 From: Mike Appleby <86076+appleby@users.noreply.github.com> Date: Sun, 15 Sep 2019 19:44:59 -0500 Subject: [PATCH 01/25] Add type annotations to the gates module Resolves #988 --- pyquil/gates.py | 155 ++++++++++++++++++++++++++++----------------- pyquil/quilatom.py | 61 ++++++++++-------- 2 files changed, 131 insertions(+), 85 deletions(-) diff --git a/pyquil/gates.py b/pyquil/gates.py index fad123a48..84c39d49a 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -14,19 +14,28 @@ # limitations under the License. ############################################################################## from warnings import warn -from typing import Callable, Dict -from pyquil.quilatom import unpack_qubit, unpack_classical_reg, MemoryReference, Addr, Qubit -from pyquil.quilbase import (Measurement, Gate, Wait, Reset, Halt, Nop, +from typing import Any, Callable, Dict, Optional, Tuple, Union + +from pyquil.quilatom import (Addr, MemoryReference, MemoryReferenceDesignator, Parameter, Qubit, + QubitDesignator, QubitPlaceholder, unpack_classical_reg, unpack_qubit) +from pyquil.quilbase import (AbstractInstruction, Gate, Halt, Reset, ResetQubit, Measurement, Nop, + Wait, ClassicalNeg, ClassicalNot, ClassicalAnd, ClassicalInclusiveOr, ClassicalExclusiveOr, ClassicalEqual, ClassicalGreaterEqual, ClassicalGreaterThan, ClassicalLessEqual, ClassicalLessThan, ClassicalMove, ClassicalExchange, ClassicalConvert, ClassicalLoad, ClassicalStore, - ClassicalAdd, ClassicalSub, ClassicalMul, ClassicalDiv, ResetQubit) + ClassicalAdd, ClassicalSub, ClassicalMul, ClassicalDiv) + +MemoryReferenceOrImmediateValue = Union[MemoryReferenceDesignator, int, float] +MemoryReferenceOrImmediateInt = Union[MemoryReferenceDesignator, int] +GateParameter = Union[Parameter, MemoryReference, int, float, complex] -def unpack_reg_val_pair(classical_reg1, classical_reg2): +def unpack_reg_val_pair(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceOrImmediateValue) \ + -> Tuple[MemoryReference, MemoryReferenceOrImmediateValue]: """ Helper function for typechecking / type-coercing arguments to constructors for binary classical operators. @@ -36,14 +45,15 @@ def unpack_reg_val_pair(classical_reg1, classical_reg2): """ left = unpack_classical_reg(classical_reg1) if isinstance(classical_reg2, int) or isinstance(classical_reg2, float): - right = classical_reg2 - else: - right = unpack_classical_reg(classical_reg2) - - return left, right + return left, classical_reg2 + return left, unpack_classical_reg(classical_reg2) -def prepare_ternary_operands(classical_reg1, classical_reg2, classical_reg3): +def prepare_ternary_operands(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceDesignator, + # TODO:(appleby) why doesn't prepare_ternary_operands allow floats for classical_reg3 + classical_reg3: MemoryReferenceOrImmediateInt) \ + -> Tuple[MemoryReference, MemoryReference, MemoryReferenceOrImmediateInt]: """ Helper function for typechecking / type-coercing arguments to constructors for ternary classical operators. @@ -64,7 +74,7 @@ def prepare_ternary_operands(classical_reg1, classical_reg2, classical_reg3): return classical_reg1, classical_reg2, classical_reg3 -def I(qubit): +def I(qubit: QubitDesignator) -> Gate: """Produces the I identity gate:: I = [1, 0] @@ -81,7 +91,7 @@ def I(qubit): return Gate(name="I", params=[], qubits=[unpack_qubit(qubit)]) -def X(qubit): +def X(qubit: QubitDesignator) -> Gate: """Produces the X ("NOT") gate:: X = [[0, 1], @@ -95,7 +105,7 @@ def X(qubit): return Gate(name="X", params=[], qubits=[unpack_qubit(qubit)]) -def Y(qubit): +def Y(qubit: QubitDesignator) -> Gate: """Produces the Y gate:: Y = [[0, 0 - 1j], @@ -109,7 +119,7 @@ def Y(qubit): return Gate(name="Y", params=[], qubits=[unpack_qubit(qubit)]) -def Z(qubit): +def Z(qubit: QubitDesignator) -> Gate: """Produces the Z gate:: Z = [[1, 0], @@ -123,7 +133,7 @@ def Z(qubit): return Gate(name="Z", params=[], qubits=[unpack_qubit(qubit)]) -def H(qubit): +def H(qubit: QubitDesignator) -> Gate: """Produces the Hadamard gate:: H = (1 / sqrt(2)) * [[1, 1], @@ -137,7 +147,7 @@ def H(qubit): return Gate(name="H", params=[], qubits=[unpack_qubit(qubit)]) -def S(qubit): +def S(qubit: QubitDesignator) -> Gate: """Produces the S gate:: S = [[1, 0], @@ -151,7 +161,7 @@ def S(qubit): return Gate(name="S", params=[], qubits=[unpack_qubit(qubit)]) -def T(qubit): +def T(qubit: QubitDesignator) -> Gate: """Produces the T gate:: T = [[1, 0], @@ -165,7 +175,7 @@ def T(qubit): return Gate(name="T", params=[], qubits=[unpack_qubit(qubit)]) -def RX(angle, qubit): +def RX(angle: GateParameter, qubit: QubitDesignator) -> Gate: """Produces the RX gate:: RX(phi) = [[cos(phi / 2), -1j * sin(phi / 2)], @@ -180,7 +190,7 @@ def RX(angle, qubit): return Gate(name="RX", params=[angle], qubits=[unpack_qubit(qubit)]) -def RY(angle, qubit): +def RY(angle: GateParameter, qubit: QubitDesignator) -> Gate: """Produces the RY gate:: RY(phi) = [[cos(phi / 2), -sin(phi / 2)], @@ -195,7 +205,7 @@ def RY(angle, qubit): return Gate(name="RY", params=[angle], qubits=[unpack_qubit(qubit)]) -def RZ(angle, qubit): +def RZ(angle: GateParameter, qubit: QubitDesignator) -> Gate: """Produces the RZ gate:: RZ(phi) = [[cos(phi / 2) - 1j * sin(phi / 2), 0] @@ -210,7 +220,7 @@ def RZ(angle, qubit): return Gate(name="RZ", params=[angle], qubits=[unpack_qubit(qubit)]) -def PHASE(angle, qubit): +def PHASE(angle: GateParameter, qubit: QubitDesignator) -> Gate: """Produces the PHASE gate:: PHASE(phi) = [[1, 0], @@ -225,7 +235,7 @@ def PHASE(angle, qubit): return Gate(name="PHASE", params=[angle], qubits=[unpack_qubit(qubit)]) -def CZ(control, target): +def CZ(control: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a controlled-Z gate:: CZ = [[1, 0, 0, 0], @@ -244,7 +254,7 @@ def CZ(control, target): return Gate(name="CZ", params=[], qubits=[unpack_qubit(q) for q in (control, target)]) -def CNOT(control, target): +def CNOT(control: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a controlled-NOT (controlled-X) gate:: CNOT = [[1, 0, 0, 0], @@ -262,7 +272,7 @@ def CNOT(control, target): return Gate(name="CNOT", params=[], qubits=[unpack_qubit(q) for q in (control, target)]) -def CCNOT(control1, control2, target): +def CCNOT(control1: QubitDesignator, control2: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a doubly-controlled NOT gate:: CCNOT = [[1, 0, 0, 0, 0, 0, 0, 0], @@ -287,7 +297,7 @@ def CCNOT(control1, control2, target): return Gate(name="CCNOT", params=[], qubits=qubits) -def CPHASE00(angle, control, target): +def CPHASE00(angle: GateParameter, control: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a controlled-phase gate that phases the ``|00>`` state:: CPHASE00(phi) = diag([exp(1j * phi), 1, 1, 1]) @@ -304,7 +314,7 @@ def CPHASE00(angle, control, target): return Gate(name="CPHASE00", params=[angle], qubits=qubits) -def CPHASE01(angle, control, target): +def CPHASE01(angle: GateParameter, control: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a controlled-phase gate that phases the ``|01>`` state:: CPHASE01(phi) = diag([1.0, exp(1j * phi), 1.0, 1.0]) @@ -322,7 +332,7 @@ def CPHASE01(angle, control, target): return Gate(name="CPHASE01", params=[angle], qubits=qubits) -def CPHASE10(angle, control, target): +def CPHASE10(angle: GateParameter, control: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a controlled-phase gate that phases the ``|10>`` state:: CPHASE10(phi) = diag([1, 1, exp(1j * phi), 1]) @@ -340,7 +350,7 @@ def CPHASE10(angle, control, target): return Gate(name="CPHASE10", params=[angle], qubits=qubits) -def CPHASE(angle, control, target): +def CPHASE(angle: GateParameter, control: QubitDesignator, target: QubitDesignator) -> Gate: """Produces a controlled-phase instruction:: CPHASE(phi) = diag([1, 1, 1, exp(1j * phi)]) @@ -360,7 +370,7 @@ def CPHASE(angle, control, target): return Gate(name="CPHASE", params=[angle], qubits=qubits) -def SWAP(q1, q2): +def SWAP(q1: QubitDesignator, q2: QubitDesignator) -> Gate: """Produces a SWAP gate which swaps the state of two qubits:: SWAP = [[1, 0, 0, 0], @@ -376,7 +386,7 @@ def SWAP(q1, q2): return Gate(name="SWAP", params=[], qubits=[unpack_qubit(q) for q in (q1, q2)]) -def CSWAP(control, target_1, target_2): +def CSWAP(control: QubitDesignator, target_1: QubitDesignator, target_2: QubitDesignator) -> Gate: """Produces a controlled-SWAP gate. This gate conditionally swaps the state of two qubits:: CSWAP = [[1, 0, 0, 0, 0, 0, 0, 0], @@ -398,7 +408,7 @@ def CSWAP(control, target_1, target_2): return Gate(name="CSWAP", params=[], qubits=qubits) -def ISWAP(q1, q2): +def ISWAP(q1: QubitDesignator, q2: QubitDesignator) -> Gate: """Produces an ISWAP gate:: ISWAP = [[1, 0, 0, 0], @@ -416,7 +426,7 @@ def ISWAP(q1, q2): return Gate(name="ISWAP", params=[], qubits=[unpack_qubit(q) for q in (q1, q2)]) -def PSWAP(angle, q1, q2): +def PSWAP(angle: GateParameter, q1: QubitDesignator, q2: QubitDesignator) -> Gate: """Produces a parameterized SWAP gate:: PSWAP(phi) = [[1, 0, 0, 0], @@ -443,11 +453,11 @@ def PSWAP(angle, q1, q2): """ -def RESET(qubit_index=None): +def RESET(qubit_index: Optional[QubitDesignator] = None) -> Union[Reset, ResetQubit]: """ Reset all qubits or just one specific qubit. - :param Optional[Union[integer_types, Qubit, QubitPlaceholder]] qubit_index: The qubit to reset. + :param Optional[QubitDesignator] qubit_index: The qubit to reset. This can be a qubit's index, a Qubit, or a QubitPlaceholder. If None, reset all qubits. :returns: A Reset or ResetQubit Quil AST expression corresponding to a global or targeted @@ -475,7 +485,7 @@ def RESET(qubit_index=None): """ -def MEASURE(qubit, classical_reg): +def MEASURE(qubit: QubitDesignator, classical_reg: Optional[MemoryReferenceDesignator]) -> Measurement: """ Produce a MEASURE instruction. @@ -495,7 +505,7 @@ def MEASURE(qubit, classical_reg): return Measurement(qubit, address) -def TRUE(classical_reg): +def TRUE(classical_reg: Union[MemoryReference, int]) -> ClassicalMove: """ Produce a TRUE instruction. @@ -508,7 +518,7 @@ def TRUE(classical_reg): return MOVE(classical_reg, 1) -def FALSE(classical_reg): +def FALSE(classical_reg: Union[MemoryReference, int]) -> ClassicalMove: """ Produce a FALSE instruction. @@ -521,7 +531,7 @@ def FALSE(classical_reg): return MOVE(classical_reg, 0) -def NEG(classical_reg): +def NEG(classical_reg: MemoryReferenceDesignator) -> ClassicalNeg: """ Produce a NEG instruction. @@ -531,7 +541,7 @@ def NEG(classical_reg): return ClassicalNeg(unpack_classical_reg(classical_reg)) -def NOT(classical_reg): +def NOT(classical_reg: MemoryReferenceDesignator) -> ClassicalNot: """ Produce a NOT instruction. @@ -541,7 +551,8 @@ def NOT(classical_reg): return ClassicalNot(unpack_classical_reg(classical_reg)) -def AND(classical_reg1, classical_reg2): +def AND(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceOrImmediateValue) \ + -> ClassicalAnd: """ Produce an AND instruction. @@ -555,7 +566,8 @@ def AND(classical_reg1, classical_reg2): return ClassicalAnd(left, right) -def OR(classical_reg1, classical_reg2): +def OR(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator) \ + -> ClassicalInclusiveOr: """ Produce an OR instruction. @@ -569,7 +581,8 @@ def OR(classical_reg1, classical_reg2): return IOR(classical_reg2, classical_reg1) -def IOR(classical_reg1, classical_reg2): +def IOR(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceOrImmediateValue) \ + -> ClassicalInclusiveOr: """ Produce an inclusive OR instruction. @@ -581,7 +594,8 @@ def IOR(classical_reg1, classical_reg2): return ClassicalInclusiveOr(left, right) -def XOR(classical_reg1, classical_reg2): +def XOR(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceOrImmediateValue) \ + -> ClassicalExclusiveOr: """ Produce an exclusive OR instruction. @@ -593,7 +607,8 @@ def XOR(classical_reg1, classical_reg2): return ClassicalExclusiveOr(left, right) -def MOVE(classical_reg1, classical_reg2): +def MOVE(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceOrImmediateValue) \ + -> ClassicalMove: """ Produce a MOVE instruction. @@ -605,7 +620,8 @@ def MOVE(classical_reg1, classical_reg2): return ClassicalMove(left, right) -def EXCHANGE(classical_reg1, classical_reg2): +def EXCHANGE(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceDesignator) \ + -> ClassicalExchange: """ Produce an EXCHANGE instruction. @@ -618,7 +634,9 @@ def EXCHANGE(classical_reg1, classical_reg2): return ClassicalExchange(left, right) -def LOAD(target_reg, region_name, offset_reg): +def LOAD(target_reg: MemoryReferenceDesignator, + region_name: str, + offset_reg: MemoryReferenceDesignator) -> ClassicalLoad: """ Produce a LOAD instruction. @@ -630,7 +648,11 @@ def LOAD(target_reg, region_name, offset_reg): return ClassicalLoad(unpack_classical_reg(target_reg), region_name, unpack_classical_reg(offset_reg)) -def STORE(region_name, offset_reg, source): +# TODO:(appleby) Docstring wrong here? ClassicalStore's __init__ will throw an exception if either +# of the last two args are not MemoryReferences. Perhaps the comment should say that offset_reg can +# be either MemoryReference or constant (i.e. MemoryReferenceDesignator)? +def STORE(region_name: str, offset_reg: MemoryReferenceDesignator, source: MemoryReference) \ + -> ClassicalStore: """ Produce a STORE instruction. @@ -644,7 +666,8 @@ def STORE(region_name, offset_reg, source): return ClassicalStore(region_name, unpack_classical_reg(offset_reg), source) -def CONVERT(classical_reg1, classical_reg2): +# TODO:(appleby) should this take 2 MemoryReferenceDesignators and call unpack_classical_reg on them? +def CONVERT(classical_reg1: MemoryReference, classical_reg2: MemoryReference) -> ClassicalConvert: """ Produce a CONVERT instruction. @@ -656,7 +679,8 @@ def CONVERT(classical_reg1, classical_reg2): unpack_classical_reg(classical_reg2)) -def ADD(classical_reg, right): +def ADD(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceOrImmediateValue) \ + -> ClassicalAdd: """ Produce an ADD instruction. @@ -668,7 +692,8 @@ def ADD(classical_reg, right): return ClassicalAdd(left, right) -def SUB(classical_reg, right): +def SUB(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceOrImmediateValue) \ + -> ClassicalSub: """ Produce a SUB instruction. @@ -680,7 +705,8 @@ def SUB(classical_reg, right): return ClassicalSub(left, right) -def MUL(classical_reg, right): +def MUL(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceOrImmediateValue) \ + -> ClassicalMul: """ Produce a MUL instruction. @@ -692,7 +718,8 @@ def MUL(classical_reg, right): return ClassicalMul(left, right) -def DIV(classical_reg, right): +def DIV(classical_reg: MemoryReferenceDesignator, right: MemoryReferenceOrImmediateValue) \ + -> ClassicalDiv: """ Produce an DIV instruction. @@ -704,7 +731,9 @@ def DIV(classical_reg, right): return ClassicalDiv(left, right) -def EQ(classical_reg1, classical_reg2, classical_reg3): +def EQ(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceDesignator, + classical_reg3: MemoryReferenceOrImmediateInt) -> ClassicalEqual: """ Produce an EQ instruction. @@ -720,7 +749,9 @@ def EQ(classical_reg1, classical_reg2, classical_reg3): return ClassicalEqual(classical_reg1, classical_reg2, classical_reg3) -def LT(classical_reg1, classical_reg2, classical_reg3): +def LT(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceDesignator, + classical_reg3: MemoryReferenceOrImmediateInt) -> ClassicalLessThan: """ Produce an LT instruction. @@ -735,7 +766,9 @@ def LT(classical_reg1, classical_reg2, classical_reg3): return ClassicalLessThan(classical_reg1, classical_reg2, classical_reg3) -def LE(classical_reg1, classical_reg2, classical_reg3): +def LE(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceDesignator, + classical_reg3: MemoryReferenceOrImmediateInt) -> ClassicalLessEqual: """ Produce an LE instruction. @@ -750,7 +783,9 @@ def LE(classical_reg1, classical_reg2, classical_reg3): return ClassicalLessEqual(classical_reg1, classical_reg2, classical_reg3) -def GT(classical_reg1, classical_reg2, classical_reg3): +def GT(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceDesignator, + classical_reg3: MemoryReferenceOrImmediateInt) -> ClassicalGreaterThan: """ Produce an GT instruction. @@ -765,7 +800,9 @@ def GT(classical_reg1, classical_reg2, classical_reg3): return ClassicalGreaterThan(classical_reg1, classical_reg2, classical_reg3) -def GE(classical_reg1, classical_reg2, classical_reg3): +def GE(classical_reg1: MemoryReferenceDesignator, + classical_reg2: MemoryReferenceDesignator, + classical_reg3: MemoryReferenceOrImmediateInt) -> ClassicalGreaterEqual: """ Produce an GE instruction. @@ -812,7 +849,7 @@ def GE(classical_reg1, classical_reg2, classical_reg3): Alias for the above dictionary of quantum gates. """ -STANDARD_INSTRUCTIONS = { +STANDARD_INSTRUCTIONS: Dict[str, Union[AbstractInstruction, Callable[..., AbstractInstruction]]] = { 'WAIT': WAIT, 'RESET': RESET, 'NOP': NOP, diff --git a/pyquil/quilatom.py b/pyquil/quilatom.py index db019157b..094e6d76e 100644 --- a/pyquil/quilatom.py +++ b/pyquil/quilatom.py @@ -16,6 +16,7 @@ import numpy as np from warnings import warn from fractions import Fraction +from typing import List, Tuple, Union class QuilAtom(object): @@ -103,7 +104,10 @@ def register(cls, n): return [cls() for _ in range(n)] -def unpack_qubit(qubit): +QubitDesignator = Union[Qubit, QubitPlaceholder, int] + + +def unpack_qubit(qubit: QubitDesignator) -> Union[Qubit, QubitPlaceholder]: """ Get a qubit from an object. @@ -120,31 +124,6 @@ def unpack_qubit(qubit): raise TypeError("qubit should be an int or Qubit instance") -def unpack_classical_reg(c): - """ - Get the address for a classical register. - - :param c: A list of length 2, a pair, a string (to be interpreted as name[0]), or a MemoryReference. - :return: The address as a MemoryReference. - """ - if isinstance(c, list) or isinstance(c, tuple): - if len(c) > 2 or len(c) == 0: - raise ValueError("if c is a list/tuple, it should be of length <= 2") - if len(c) == 1: - c = (c[0], 0) - if not isinstance(c[0], str): - raise ValueError("if c is a list/tuple, its first member should be a string") - if not isinstance(c[1], int): - raise ValueError("if c is a list/tuple, its second member should be an int") - return MemoryReference(c[0], c[1]) - if isinstance(c, MemoryReference): - return c - elif isinstance(c, str): - return MemoryReference(c, 0) - else: - raise TypeError("c should be a list of length 2, a pair, a string, or a MemoryReference") - - class Label(QuilAtom): """ Representation of a label. @@ -634,3 +613,33 @@ def __init__(self, value): if not isinstance(value, int) or value < 0: raise TypeError("Addr value must be a non-negative int") super(Addr, self).__init__("ro", offset=value, declared_size=None) + + +# Like the Tuple, the List must be length 2, where the first item is a string and the second is an +# int. +MemoryReferenceDesignator = Union[MemoryReference, str, Tuple[str, int], List[Union[str, int]]] + + +def unpack_classical_reg(c: MemoryReferenceDesignator) -> MemoryReference: + """ + Get the address for a classical register. + + :param c: A list of length 2, a pair, a string (to be interpreted as name[0]), or a MemoryReference. + :return: The address as a MemoryReference. + """ + if isinstance(c, list) or isinstance(c, tuple): + if len(c) > 2 or len(c) == 0: + raise ValueError("if c is a list/tuple, it should be of length <= 2") + if len(c) == 1: + c = (c[0], 0) + if not isinstance(c[0], str): + raise ValueError("if c is a list/tuple, its first member should be a string") + if not isinstance(c[1], int): + raise ValueError("if c is a list/tuple, its second member should be an int") + return MemoryReference(c[0], c[1]) + if isinstance(c, MemoryReference): + return c + elif isinstance(c, str): + return MemoryReference(c, 0) + else: + raise TypeError("c should be a list of length 2, a pair, a string, or a MemoryReference") From 0d7de29df1444c6dccc2df43cfd5567c997259f9 Mon Sep 17 00:00:00 2001 From: Mike Appleby <86076+appleby@users.noreply.github.com> Date: Mon, 16 Sep 2019 10:09:26 -0500 Subject: [PATCH 02/25] Add type annotations to the quilatom module --- pyquil/gates.py | 2 + pyquil/quilatom.py | 251 +++++++++++++++++++++++---------------------- 2 files changed, 132 insertions(+), 121 deletions(-) diff --git a/pyquil/gates.py b/pyquil/gates.py index 84c39d49a..0a55dc3e7 100644 --- a/pyquil/gates.py +++ b/pyquil/gates.py @@ -31,8 +31,10 @@ MemoryReferenceOrImmediateValue = Union[MemoryReferenceDesignator, int, float] MemoryReferenceOrImmediateInt = Union[MemoryReferenceDesignator, int] +# TODO:(appleby) Should this type just be quilatom.ParameterDesignator? GateParameter = Union[Parameter, MemoryReference, int, float, complex] + def unpack_reg_val_pair(classical_reg1: MemoryReferenceDesignator, classical_reg2: MemoryReferenceOrImmediateValue) \ -> Tuple[MemoryReference, MemoryReferenceOrImmediateValue]: diff --git a/pyquil/quilatom.py b/pyquil/quilatom.py index 094e6d76e..f46635db8 100644 --- a/pyquil/quilatom.py +++ b/pyquil/quilatom.py @@ -16,7 +16,7 @@ import numpy as np from warnings import warn from fractions import Fraction -from typing import List, Tuple, Union +from typing import Any, Callable, ClassVar, Dict, List, Optional, Set, Tuple, Union class QuilAtom(object): @@ -33,7 +33,7 @@ def __str__(self): def __eq__(self, other): raise NotImplementedError() - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not self.__eq__(other) def __hash__(self): @@ -47,24 +47,24 @@ class Qubit(QuilAtom): :param int index: Index of the qubit. """ - def __init__(self, index): + def __init__(self, index: int) -> None: if not (isinstance(index, int) and index >= 0): raise TypeError("Addr index must be a non-negative int") self.index = index - def out(self): + def out(self) -> str: return str(self.index) - def __str__(self): + def __str__(self) -> str: return str(self.index) - def __repr__(self): + def __repr__(self) -> str: return "".format(self.index) - def __hash__(self): + def __hash__(self) -> int: return hash(self.index) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return isinstance(other, Qubit) and other.index == self.index @@ -72,20 +72,20 @@ class QubitPlaceholder(QuilAtom): def out(self): raise RuntimeError("Qubit {} has not been assigned an index".format(self)) - def __str__(self): + def __str__(self) -> str: return "q{}".format(id(self)) - def __repr__(self): + def __repr__(self) -> str: return "".format(id(self)) - def __hash__(self): + def __hash__(self) -> int: return hash(id(self)) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: return isinstance(other, QubitPlaceholder) and id(other) == id(self) @classmethod - def register(cls, n): + def register(cls, n: int) -> List['QubitPlaceholder']: """Return a 'register' of ``n`` QubitPlaceholders. >>> qs = QubitPlaceholder.register(8) # a qubyte @@ -124,6 +124,35 @@ def unpack_qubit(qubit: QubitDesignator) -> Union[Qubit, QubitPlaceholder]: raise TypeError("qubit should be an int or Qubit instance") +# Like the Tuple, the List must be length 2, where the first item is a string and the second an int. +MemoryReferenceDesignator = Union['MemoryReference', Tuple[str, int], List, str] + + +def unpack_classical_reg(c: MemoryReferenceDesignator) -> 'MemoryReference': + """ + Get the address for a classical register. + + :param c: A list of length 2, a pair, a string (to be interpreted as name[0]), or a MemoryReference. + :return: The address as a MemoryReference. + """ + if isinstance(c, list) or isinstance(c, tuple): + if len(c) > 2 or len(c) == 0: + raise ValueError("if c is a list/tuple, it should be of length <= 2") + if len(c) == 1: + c = (c[0], 0) + if not isinstance(c[0], str): + raise ValueError("if c is a list/tuple, its first member should be a string") + if not isinstance(c[1], int): + raise ValueError("if c is a list/tuple, its second member should be an int") + return MemoryReference(c[0], c[1]) + if isinstance(c, MemoryReference): + return c + elif isinstance(c, str): + return MemoryReference(c, 0) + else: + raise TypeError("c should be a list of length 2, a pair, a string, or a MemoryReference") + + class Label(QuilAtom): """ Representation of a label. @@ -131,51 +160,54 @@ class Label(QuilAtom): :param string label_name: The label name. """ - def __init__(self, label_name): + def __init__(self, label_name: str) -> None: self.name = label_name - def out(self): + def out(self) -> str: return "@{name}".format(name=self.name) - def __str__(self): + def __str__(self) -> str: return "@{name}".format(name=self.name) - def __repr__(self): + def __repr__(self) -> str: return "