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

[WIP] Introduce PEP484 type hints to paulis.py #147

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
56 changes: 47 additions & 9 deletions pyquil/paulis.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
from six import integer_types
from six.moves import range

from typing import Dict, Iterator, List, Tuple, Union # noqa: F401

PAULI_OPS = ["X", "Y", "Z", "I"]
PAULI_PROD = {'ZZ': 'I', 'YY': 'I', 'XX': 'I', 'II': 'I',
'XY': 'Z', 'XZ': 'Y', 'YX': 'Z', 'YZ': 'X', 'ZX': 'Y',
Expand All @@ -53,6 +55,7 @@ class PauliTerm(object):
"""

def __init__(self, op, index, coefficient=1.0):
# type: (str, int, float) -> None
""" Create a new Pauli Term with a Pauli operator at a particular index and a leading
coefficient.

Expand All @@ -63,23 +66,24 @@ def __init__(self, op, index, coefficient=1.0):
assert op in PAULI_OPS
assert isinstance(index, integer_types) and index >= 0

self._ops = {}
self._ops = {} # type: Dict[int,str]
if op != "I":
self._ops[index] = op
if not isinstance(coefficient, Number):
raise ValueError("coefficient of PauliTerm must be a Number.")
self.coefficient = complex(coefficient)
self._id = None
self.coefficient = complex(coefficient) # type: complex
self._id = "" # type: str

def id(self):
# type: () -> str
"""
Returns the unique identifier string for the PauliTerm (ignoring the coefficient).
Used in the simplify method of PauliSum.

:return: The unique identifier for this term.
:rtype: string
"""
if self._id is not None:
if self._id is not "":
return self._id
else:
s = ""
Expand All @@ -89,6 +93,7 @@ def id(self):
return s

def __eq__(self, other):
# type: (PauliTerm) -> bool
if not isinstance(other, (PauliTerm, PauliSum)):
raise TypeError("Can't compare PauliTerm with object of type {}.".format(type(other)))
elif isinstance(other, PauliSum):
Expand All @@ -97,19 +102,22 @@ def __eq__(self, other):
return self.id() == other.id() and np.isclose(self.coefficient, other.coefficient)

def __ne__(self, other):
# type: (PauliTerm) -> bool
# x!=y and x<>y call __ne__() instead of negating __eq__
# This is only a weirdness in python2 as in python 3 __ne__ defaults to the inversion of
# __eq__
return not self.__eq__(other)

def __len__(self):
# type: () -> int
"""
The length of the PauliTerm is the number of Pauli operators in the term. A term that
consists of only a scalar has a length of zero.
"""
return len(self._ops)

def copy(self):
# type: () -> PauliTerm
"""
Properly creates a new PauliTerm, with a completely new dictionary
of operators
Expand All @@ -126,19 +134,23 @@ def copy(self):
return new_term

def get_qubits(self):
# type () -> List[int]
"""Gets all the qubits that this PauliTerm operates on.
"""
# sort the keys to get a deterministic iteration order over qubits
return sorted(self._ops.keys())

def __getitem__(self, i):
# type: (int) -> str
return self._ops.get(i, "I")

def __iter__(self):
# type: () -> Iterator[Tuple[int, str]]
for i in self.get_qubits():
yield i, self[i]

def _multiply_factor(self, factor, index):
# type: (str, int) -> PauliTerm
new_term = PauliTerm("I", 0)
new_coeff = self.coefficient
new_ops = self._ops.copy()
Expand All @@ -157,6 +169,7 @@ def _multiply_factor(self, factor, index):
return new_term

def __mul__(self, term):
# type: (Union[PauliTerm, PauliSum, Number]) -> PauliTerm
"""Multiplies this Pauli Term with another PauliTerm, PauliSum, or number according to the
Pauli algebra rules.

Expand All @@ -178,6 +191,7 @@ def __mul__(self, term):
return term_with_coeff(new_term, new_term.coefficient * new_coeff)

def __rmul__(self, other):
# type: (Number) -> PauliTerm
"""Multiplies this PauliTerm with another object, probably a number.

:param other: A number or PauliTerm to multiply by
Expand All @@ -188,6 +202,7 @@ def __rmul__(self, other):
return self * other

def __pow__(self, power):
# type: (int) -> PauliTerm
"""Raises this PauliTerm to power.

:param int power: The power to raise this PauliTerm to.
Expand All @@ -209,21 +224,23 @@ def __pow__(self, power):
return result

def __add__(self, other):
# type: (Union[PauliTerm, Number]) -> PauliSum
"""Adds this PauliTerm with another one.

:param other: A PauliTerm object or a Number
:returns: A PauliSum object representing the sum of this PauliTerm and other
:rtype: PauliSum
"""
if isinstance(other, Number):
return self + PauliTerm("I", 0, other)
return self + PauliTerm("I", 0, float(other))
elif isinstance(other, PauliSum):
return other + self
else:
new_sum = PauliSum([self, other])
return new_sum.simplify()

def __radd__(self, other):
# type: (Number) -> PauliSum
"""Adds this PauliTerm with a Number.

:param other: A PauliTerm object or a Number
Expand All @@ -234,6 +251,7 @@ def __radd__(self, other):
return self + other

def __sub__(self, other):
# type: (Union[PauliTerm, Number]) -> PauliSum
"""Subtracts a PauliTerm from this one.

:param other: A PauliTerm object or a Number
Expand All @@ -243,6 +261,7 @@ def __sub__(self, other):
return self + -1. * other

def __rsub__(self, other):
# type: (Union[PauliTerm, Number]) -> PauliSum
"""Subtracts this PauliTerm from a Number or PauliTerm.

:param other: A PauliTerm object or a Number
Expand All @@ -252,6 +271,7 @@ def __rsub__(self, other):
return other + -1. * self

def __str__(self):
# type: () -> str
term_strs = []
for index in sorted(self._ops.keys()):
term_strs.append("%s%s" % (self[index], index))
Expand Down Expand Up @@ -347,6 +367,7 @@ def sZ(q):


def term_with_coeff(term, coeff):
# type: (PauliTerm, Number) -> PauliTerm
"""
Change the coefficient of a PauliTerm.

Expand All @@ -368,6 +389,7 @@ class PauliSum(object):
"""

def __init__(self, terms):
# type: (List[PauliTerm]) -> None
"""
:param Sequence terms: A Sequence of PauliTerms.
"""
Expand All @@ -380,9 +402,10 @@ def __init__(self, terms):
self.terms = terms

def __eq__(self, other):
# type: (Union[PauliTerm, PauliSum]) -> bool
"""Equality testing to see if two PauliSum's are equivalent.

:param PauliSum other: The PauliSum to compare this PauliSum with.
:param PauliTerm or PauliSum other: The PauliSum to compare this PauliSum with.
:return: True if other is equivalent to this PauliSum, False otherwise.
:rtype: bool
"""
Expand All @@ -396,24 +419,28 @@ def __eq__(self, other):
return all([term == other.terms[i] for i, term in enumerate(self.terms)])

def __ne__(self, other):
# type: (Union[PauliTerm, PauliSum]) -> bool
"""Inequality testing to see if two PauliSum's are not equivalent.

:param PauliSum other: The PauliSum to compare this PauliSum with.
:param PauliTerm or PauliSum other: The PauliSum to compare this PauliSum with.
:return: False if other is equivalent to this PauliSum, True otherwise.
:rtype: bool
"""
return not self == other

def __str__(self):
# type: () -> str
return " + ".join([str(term) for term in self.terms])

def __len__(self):
# type () -> int
"""
The length of the PauliSum is the number of PauliTerms in the sum.
"""
return len(self.terms)

def __getitem__(self, item):
# type (int) -> PauliTerm
"""
:param int item: The index of the term in the sum to return
:return: The PauliTerm at the index-th position in the PauliSum
Expand All @@ -422,9 +449,11 @@ def __getitem__(self, item):
return self.terms[item]

def __iter__(self):
# type () -> Iterator[PauliTerm]
return self.terms.__iter__()

def __mul__(self, other):
# type: (Union[PauliSum, PauliTerm, Number]) -> PauliSum
"""
Multiplies together this PauliSum with PauliSum, PauliTerm or Number objects. The new term
is then simplified according to the Pauli Algebra rules.
Expand All @@ -445,6 +474,7 @@ def __mul__(self, other):
return new_sum.simplify()

def __rmul__(self, other):
# type: (Union[PauliSum, PauliTerm, Number]) -> PauliSum
"""
Multiples together this PauliSum with PauliSum, PauliTerm or Number objects. The new term
is then simplified according to the Pauli Algebra rules.
Expand All @@ -460,6 +490,7 @@ def __rmul__(self, other):
return PauliSum(new_terms).simplify()

def __pow__(self, power):
# type: (int) -> PauliSum
"""Raises this PauliSum to power.

:param int power: The power to raise this PauliSum to.
Expand All @@ -485,6 +516,7 @@ def __pow__(self, power):
return result

def __add__(self, other):
# type: (Union[PauliSum, PauliTerm, Number]) -> PauliSum
"""
Adds together this PauliSum with PauliSum, PauliTerm or Number objects. The new term
is then simplified according to the Pauli Algebra rules.
Expand All @@ -503,6 +535,7 @@ def __add__(self, other):
return new_sum.simplify()

def __radd__(self, other):
# type: (Union[PauliSum, PauliTerm, Number]) -> PauliSum
"""
Adds together this PauliSum with PauliSum, PauliTerm or Number objects. The new term
is then simplified according to the Pauli Algebra rules.
Expand All @@ -515,6 +548,7 @@ def __radd__(self, other):
return self + other

def __sub__(self, other):
# type: (Union[PauliSum, PauliTerm, Number]) -> PauliSum
"""
Finds the difference of this PauliSum with PauliSum, PauliTerm or Number objects. The new
term is then simplified according to the Pauli Algebra rules.
Expand All @@ -526,6 +560,7 @@ def __sub__(self, other):
return self + -1. * other

def __rsub__(self, other):
# type: (Union[PauliSum, PauliTerm, Number]) -> PauliSum
"""
Finds the different of this PauliSum with PauliSum, PauliTerm or Number objects. The new
term is then simplified according to the Pauli Algebra rules.
Expand All @@ -537,6 +572,8 @@ def __rsub__(self, other):
return other + -1. * self

def get_qubits(self):
# type () -> List[Any]
# TODO: precisify
"""
The support of all the operators in the PauliSum object.

Expand All @@ -546,6 +583,7 @@ def get_qubits(self):
return list(set().union(*[term.get_qubits() for term in self.terms]))

def simplify(self):
# type: () -> PauliSum
"""
Simplifies the sum of Pauli operators according to Pauli algebra rules.
"""
Expand All @@ -554,15 +592,15 @@ def coalesce(d):
for k in sorted(d):
term_list = d[k]
if (len(term_list) == 1 and not
np.isclose(term_list[0].coefficient, 0.0)):
np.isclose(term_list[0].coefficient, 0.0)):
terms.append(term_list[0])
else:
coeff = sum(t.coefficient for t in term_list)
if not np.isclose(coeff, 0.0):
terms.append(term_with_coeff(term_list[0], coeff))
return PauliSum(terms)

like_terms = {}
like_terms = {} # type: Dict[str, List[PauliTerm]]
for term in self.terms:
id = term.id()
if id not in like_terms:
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ tox==2.9.1
typing==3.6.2
urllib3==1.22
virtualenv==15.1.0
typing==3.6.2
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
tests_require=[
'pytest >= 3.0.0',
'mock',
'typing == 3.6.2',
],
test_suite='pyquil.tests',
entry_points={
Expand Down