-
-
Notifications
You must be signed in to change notification settings - Fork 145
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
Clean up types #1825
Clean up types #1825
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1825 +/- ##
==========================================
- Coverage 98.47% 98.45% -0.03%
==========================================
Files 79 79
Lines 3681 3682 +1
==========================================
Hits 3625 3625
- Misses 56 57 +1
|
mitiq/cdr/clifford_training_data.py
Outdated
if random_state is None: | ||
random_state = np.random # type: ignore |
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.
The reason we used random_state = np.random
instead of a new instance random_state = np.RandomState()
is that the user can set a global seed np.random.seed(123)
with a single instruction e.g. at the beginning of a notebook and it should propagate to all Mitiq code.
Cirq uses a similar approach.
Actually I am not sure if this is good or bad practice. Just wanted to explain the motivation.
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 adding some background here (and the link to Cirq), it's super helpful! I agree we should definitely tread lightly around changing RandomState related things, but I'm not sure that np.random.seed
works in that way. From my understanding, this function only effects the next call of a numpy random function, but not subsequent ones. So if a user sets a seed at the top of a file running Mitiq, I would not expect completely reproducible outputs.
>>> import numpy as np
>>> np.random.seed(42)
>>> np.random.randint(500)
102
>>> np.random.seed(42)
>>> np.random.randint(500)
102
>>> np.random.randint(500)
435
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.
The seed actually affects subsequent functions (the internal random states evolves in a reproducible way):
>>> import numpy as np
>>> np.random.seed(42)
>>> np.random.randint(500)
102
>>> np.random.randint(500)
435
>>> np.random.seed(42)
>>> np.random.randint(500)
102
>>> np.random.randint(500)
435
So, everything which depends on np.random
becomes deterministically reproducible.
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.
Ahhh I see, thank you for clarifying. In that case, yeah I think we should avoid setting np.random.RandomState(None)
. I'll make the necessary changes.
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.
I tried random_state: Union[np.random.RandomState, np.random.Generator] = np.random.default_rng(),
and all tests pass. Can remove the None checks and most Optionals too, eg:
def _select(
non_clifford_ops: Sequence[cirq.ops.Operation],
fraction_non_clifford: float,
method: str = "uniform",
sigma: Optional[float] = 1.0,
random_state: Union[np.random.RandomState, np.random.Generator] = np.random.default_rng(),
) -> List[int]:
...
num_non_cliff = len(non_clifford_ops)
num_to_replace = int(round(fraction_non_clifford * num_non_cliff))
Happy to push if we like it and you're low on bandwidth @natestemen.
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.
@natestemen, as discussed made a branch to test out these changes. They're here when you get a chance to take a look!
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.
From my testing the np.random.default_rng
function does not respect if the user has set a seed using np.random.seed
. So I'll revert my changes here back to what we had previously.
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.
Apart from the np.random.RandomState()
vs np.random
problem discussed in the previous comments, all the rest LGTM
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.
Overall great work @natestemen. Just a couple nit items (aside from the np.random convo) and otherwise LGTM
mitiq/interface/conversions.py
Outdated
circuit = cast(Program, circuit) | ||
|
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.
I'm getting circuits typed as any after this. I think it's good practice to do what we do in the qiskit
pass above: circuit = circuit.copy()
?
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.
I added the cast
call back in 3af0b0f
, but I think since we don't need to modify the program for a pyquil program, we do not need to copy the circuit as we do with qiskit.
when these were removed, no mypy errors were encountered
we know this is complex since we require it in the __init__
what was `noise_model` is not a `cirq.NOISE_MODEL_LIKE`, but a function that returned a `cirq.NOISE_MODEL_LIKE`.
when these were removed, no mypy errors were encountered
_random_single_q_clifford is supposed to take numpy array, so better to actually cast it as opposed to using `typing.cast`.
technically this could be a pyquil expression, but mitiq does not work with circuits with variables
Co-Authored-By: Aaron Robertson <58564008+Aaron-Robertson@users.noreply.github.com>
Co-Authored-By: Aaron Robertson <58564008+Aaron-Robertson@users.noreply.github.com>
b609a04
to
0a9ec5a
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.
Nice to see so many # type ignore
are gone!
Thanks Nate.
Description
A "quick" pass over all all of our
# type: ignore
comments, as well astyping.cast
calls. Was able to remove many without any mypy error. It's a good reminder to check when refactoring code.Things I thought about while making these changes:
int
ornp.random.RandomState
, in some places and just anp.random.RandomState
in others. Maybe we allow user facing functions to take both (maybe in addition torandom.Random
?), and then expect a single type once digested from the user.QPROGRAM
/Executor
, it's going to take some work. Fun work, however :). I played around with makingQPROGRAM
atyping.TypeVar
, and it broke everything. A lot of things will need to change in conjuction with that, sinceQPROGRAM
being a union means mypy's type checks are pretty loose. We'd have to be much more careful in tracking types in our conversions files.Reviewer Note
It's probably easiest to review this commit by commit since some changes modify multiple files.