-
Notifications
You must be signed in to change notification settings - Fork 349
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
Finish the rest of the type annotations for quil.py #1134
Conversation
62b6a1a
to
2daff9d
Compare
While annotating |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple quick things. Also, you'll probably notice that we've added some additional CI checks (see https://github.com/rigetti/pyquil/blob/master/CONTRIBUTING.md#style-guidelines). Finally, because the goal of this PR is to finish off the quil.py
file, it should be added to the list here in the Makefile
(https://github.com/rigetti/pyquil/blob/master/Makefile#L71) and thus verified as part of the CI.
CHANGELOG.md
Outdated
@@ -8,7 +8,7 @@ Changelog | |||
|
|||
### Improvements and Changes | |||
|
|||
- Added some type hints to the `Program` class (@rht, gh-1115). | |||
- Type hints have been added to the `Program` class (@rht, gh-1115, gh-1134) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Type hints have been added to the `Program` class (@rht, gh-1115, gh-1134) | |
- Type hints have been added to the `quil.py` file (@rht, gh-1115, gh-1134). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're doing the rest of the file right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the rest of the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But there are 52 mypy errors, some are caused by another files that makes it impossible to reduce in this PR. How should I reword the commit message and the changelog message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Down to 32 errors.
|
Rebase off of master now that I've merged #1133 to get the updated CI stuff. Check out the updated contributing guide as well to come up to speed if you're confused :) |
I have reformatted my commit with Black. |
04a51b1
to
1c37255
Compare
Line 372 in 2f61735
because |
d4742cb
to
dddb6e8
Compare
@karalekas I reworded the changelog and commit message by saying "some" so that this PR can be merged. The increased precision in |
I don't think we should change the return type because
|
Maybe |
Or maybe the expectation is that you've already called |
0872d7f
to
d145fed
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
github won't let me comment on lines without any changes, but the following diff around line 105 of quil.py removed some mypy errors for me:
@@ -105,12 +106,12 @@ class Program(object):
# labels so the program must first be have its labels instantiated.
# _synthesized_instructions is simply a cache on the result of the _synthesize()
# method. It is marked as None whenever new instructions are added.
- self._synthesized_instructions = None
+ self._synthesized_instructions: Optional[List[AbstractInstruction]] = None
self.inst(*instructions)
# Filled in with quil_to_native_quil
- self.native_quil_metadata = None # type: NativeQuilMetadata
+ self.native_quil_metadata : Optional[NativeQuilMetadata] = None
# default number of shots to loop through
self.num_shots = 1
9043c54
to
f30f99a
Compare
156b77b
to
fa69e56
Compare
In the 7th commit, I ignored the type checking for the function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple more small changes to resolve minor mypy errors.
There are still some errors left, but they require more thought / discussion on how (and whether) to resolve them.
@appleby there are 5 errors left as of the 8th commit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your patience @rht . Please apply the following diff, which gets the number of mypy errors down to zero and adds quil.py to the list of files checked by make check-types
. I will ping karalekas to see what he thinks about the changes in measure_all
.
diff --git a/Makefile b/Makefile
index 399e231a..77c3e260 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,8 @@ check-format:
# The dream is to one day run mypy on the whole tree. For now, limit checks to known-good files.
.PHONY: check-types
check-types:
- mypy pyquil/quilatom.py pyquil/quilbase.py pyquil/gates.py pyquil/noise.py
+ mypy pyquil/quilatom.py pyquil/quilbase.py pyquil/gates.py \
+ pyquil/noise.py pyquil/quil.py
.PHONY: check-style
check-style:
diff --git a/pyquil/quil.py b/pyquil/quil.py
index 8491f087..06d6f127 100644
--- a/pyquil/quil.py
+++ b/pyquil/quil.py
@@ -19,7 +19,7 @@ Module for creating and defining Quil programs.
import itertools
import types
import warnings
-from collections import OrderedDict, defaultdict
+from collections import defaultdict
from typing import (
Any,
Dict,
@@ -32,6 +32,7 @@ from typing import (
Set,
Tuple,
Union,
+ cast,
no_type_check,
)
@@ -128,7 +129,8 @@ class Program(object):
new_prog = Program()
new_prog._defined_gates = self._defined_gates.copy()
if self.native_quil_metadata is not None:
- new_prog.native_quil_metadata = self.native_quil_metadata.copy()
+ # TODO: remove this type: ignore once rpcq._base.Message gets type hints.
+ new_prog.native_quil_metadata = self.native_quil_metadata.copy() # type: ignore
new_prog.num_shots = self.num_shots
return new_prog
@@ -390,9 +392,18 @@ class Program(object):
MEASURE 2 [3]
"""
if qubit_reg_pairs == ():
- qubit_inds = self.get_qubits(indices=True)
- if len(qubit_inds) == 0:
+ qubits = self.get_qubits(indices=True)
+ if len(qubits) == 0:
return self
+ if any(isinstance(q, QubitPlaceholder) for q in qubits):
+ raise ValueError(
+ "Attempted to call measure_all on a Program that contains QubitPlaceholders. "
+ "You must either provide the qubit_reg_pairs argument to describe how to map "
+ "these QubitPlaceholders to memory registers, or else first call "
+ "pyquil.quil.address_qubits to instantiate the QubitPlaceholders."
+ )
+ # Help mypy determine that qubits does not contain any QubitPlaceholders.
+ qubit_inds = cast(List[int], qubits)
ro = self.declare("ro", "BIT", max(qubit_inds) + 1)
for qi in qubit_inds:
self.inst(MEASURE(qi, ro[qi]))
@@ -632,9 +643,11 @@ class Program(object):
if any(not isinstance(instr, Gate) for instr in self._instructions):
raise ValueError("Program to be daggered must contain only gate applications")
- # This is a bit hacky. Gate.dagger() mutates the gate object,
- # rather than returning a fresh (and daggered) copy.
- return Program([instr.dagger() for instr in reversed(Program(self.out())._instructions)])
+ # This is a bit hacky. Gate.dagger() mutates the gate object, rather than returning a fresh
+ # (and daggered) copy. Also, mypy doesn't understand that we already asserted that every
+ # instr in _instructions is a Gate, above, so help mypy out with a cast.
+ surely_gate_instructions = cast(List[Gate], Program(self.out())._instructions)
+ return Program([instr.dagger() for instr in reversed(surely_gate_instructions)])
def _synthesize(self) -> "Program":
"""
@@ -743,7 +756,7 @@ def _what_type_of_qubit_does_it_use(
# We probably want to index qubits in the order they are encountered in the program
# so an ordered set would be nice. Python doesn't *have* an ordered set. Use the keys
# of an ordered dictionary instead
- qubits = OrderedDict()
+ qubits = {}
for instr in program:
if isinstance(instr, Gate):
@@ -795,7 +808,9 @@ def get_default_qubit_mapping(program: Program) -> Dict[Union[Qubit, QubitPlaceh
warnings.warn(
"This program contains integer qubits, so getting a mapping doesn't make sense."
)
- return {q: q for q in qubits}
+ # _what_type_of_qubit_does_it_use ensures that if real_qubits is True, then qubits contains
+ # only real Qubits, not QubitPlaceholders. Help mypy figure this out with cast.
+ return {q: cast(Qubit, q) for q in qubits}
return {qp: Qubit(i) for i, qp in enumerate(qubits)}
@@ -771,7 +799,10 @@ def get_default_qubit_mapping(program): | |||
return {qp: Qubit(i) for i, qp in enumerate(qubits)} | |||
|
|||
|
|||
def address_qubits(program, qubit_mapping=None): | |||
@no_type_check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I think this is fine, but pining @karalekas to make sure he sees it.
I think it's possible to satisfy mypy here by splashing around a bunch of cast
s, but it's ugly and probably not worth it imo, so I'm fine with just ignoring this function.
I have applied the diff via 9th commit. |
@appleby I got 2 errors left even with the 9th commit:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments for fixes to the remaining two errors.
@appleby I still got this error locally:
|
Strange, if I remove the
Semaphore's |
@appleby more detail:
|
Interesting. This must be a difference between mypy 0.740 and 0.761. You'll notice we recently pinned the mypy version to 0.740 in |
Should the pinned version of mypy be updated to 0.761 from 0.740? |
No, there is a bug in mypy 0.750+, which is why we pinned to 0.740. According to mypy contributors, the bugfix won't be in a release until 0.770. See #1124. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks @rht!
qubits = self.get_qubits(indices=True) | ||
if len(qubits) == 0: | ||
return self | ||
if any(isinstance(q, QubitPlaceholder) for q in qubits): | ||
raise ValueError( | ||
"Attempted to call measure_all on a Program that contains QubitPlaceholders. " | ||
"You must either provide the qubit_reg_pairs argument to describe how to map " | ||
"these QubitPlaceholders to memory registers, or else first call " | ||
"pyquil.quil.address_qubits to instantiate the QubitPlaceholders." | ||
) | ||
# Help mypy determine that qubits does not contain any QubitPlaceholders. | ||
qubit_inds = cast(List[int], qubits) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@karalekas what do you think, does this diff seem reasonable? See also my comment here #1134 (comment)
Thanks for trudging through this stuff @rht, it is greatly appreciated!! |
Description
Continuation of the effort to annotate quil.py.
Checklist
auto-close keywords.
including author and PR number (@username, gh-xxx).