Skip to content

Make tests restore global random seed #5629

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

Closed

Conversation

pavoljuhas
Copy link
Collaborator

  • Remove RNG seeding where redundant
  • Restore global RNG state after tests that seed it to a constant
  • Add pytest fixture for restoring RNG state

Shared cleanup after tests that use np.random.seed() or random.seed().
Cleanup after tests that seed global random number generators in either
of stock random or numpy.random modules.
These instances of seeding do not appear to affect test results.
@pavoljuhas pavoljuhas requested review from mrwojtek, a team, vtomole and cduck as code owners June 27, 2022 21:53
@pavoljuhas pavoljuhas requested a review from maffoo June 27, 2022 21:53
@CirqBot CirqBot added the size: S 10< lines changed <50 label Jun 27, 2022
Copy link
Contributor

@maffoo maffoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that if test A requires a particular random seed it should be responsible for setting that seed, rather than relying on some other test B to reset the PRNG state before A executes. The restore_random_state decorator seems like it could introduce undesirable coupling between tests.

An alternative would be to create an autouse fixture that seeds the PRNGs to a known state before every test, which would give deterministic behavior everywhere without coupling.

@@ -39,6 +39,7 @@ def test_initialization_reproducible_with_seed(seed):
eq.add_equality_group(*mappings)


@pytest.mark.usefixtures('restore_random_state')
@pytest.mark.parametrize('graph_seed,state', [(random.randint(0, 2**32), np.random.get_state())])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why the np.random.get_state() call is here in the decorator. Normally parametrization is for running a test multiple times, but here we run only once. Also, it's not clear why we have to call np.random.set_state internally at all. Rather, it looks like we could do something like:

mappings = [get_seeded_initial_mapping(graph_seed, state) for _ in range(3)]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why the np.random.get_state() call is here in the decorator.

Good point. I will tweak it to get those values in the test body.

Also, it's not clear why we have to call np.random.set_state internally at all...

I think the intent is to test get_initial_mapping() with init_seed=None -

return ccr.initialization.get_initial_mapping(logical_graph, device_graph, init_seed)

I did not dig into the code, but it presumably uses a global numpy RNG in such case.

The test then fails without the np.random.set_state call. At any rate,
I can remove the restore_random_state fixture here, because there is no seeding of RNG to a constant.

Done in 4b89b70

for seed in range(10):
random.seed(seed)

for _ in range(10):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test might have random behavior, if any other tests forget to restore the random state. I think we should try to avoid such coupling between tests.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer random.shuffle in this test to depend on CIRQ_TESTING_RANDOM_SEED.
In its initial form it would always produce the same output per constant seeds 0,..,9.
and it also reseeded RNG to 9 for any tests calling it later.

@pavoljuhas
Copy link
Collaborator Author

It seems to me that if test A requires a particular random seed it should be responsible for setting that seed, rather than relying on some other test B to reset the PRNG state before A executes. The restore_random_state decorator seems like it could introduce undesirable coupling between tests.

The tests are supposed to run with a reproducible, but non-constant seeding of RNGs. This is accomplished with check/pytest* scripts setting the CIRQ_TESTING_RANDOM_SEED environment variable and conftest.py using it to seed the generators here.

What I am trying to accomplish is that test C which does not set any seed gets pseudo-random values from RNG functions that depend on the CIRQ_TESTING_RANDOM_SEED value. If it is preceded by test B that seeds global RNG and does not clean after itself, test C is always going to get the same "random" values dependent on the seed in B.

An alternative would be to create an autouse fixture that seeds the PRNGs to a known state before every test, which would give deterministic behavior everywhere without coupling.

That could be done with requiring pytest-randomly, but I feel that is out-of-scope here.

No need to parametrize one group of values, set them in the test body instead.
Also no need to restore_random_state as there is no seeding to a constant.
@pavoljuhas pavoljuhas requested a review from maffoo June 28, 2022 00:07
@pavoljuhas
Copy link
Collaborator Author

@maffoo - thank you for comments. PTAL when you get a chance.

@pavoljuhas
Copy link
Collaborator Author

Ping @maffoo, @vtomole - PTAL when you have a chance.

@pavoljuhas
Copy link
Collaborator Author

Replaced by #5868 where pytest-randomly resets random seed for each test case.

Closing as obsolete.

@pavoljuhas pavoljuhas closed this Sep 9, 2022
@pavoljuhas pavoljuhas deleted the let-tests-restore-random-seed branch October 10, 2022 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: S 10< lines changed <50
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants