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

Cache Circuit properties between mutations #6322

Merged
merged 6 commits into from Nov 1, 2023
Merged

Conversation

maffoo
Copy link
Contributor

@maffoo maffoo commented Oct 20, 2023

This simplifies working with frozen circuits and circuit identity by storing the FrozenCircuit instance returned by Circuit.freeze() and returning the same instance until it is "invalidated" by any mutations of the Circuit itself. Note that if we make additional changes to the Circuit implementation, such as adding new mutating methods, we will need to remember to add self._frozen = None statements to invalidate the frozen representation in those places as well. To make this easier to audit, I have put these invalidation statements immediately after any mutation operations on self._moments, even in places where some invalidations could be elided or pushed to the end of a method; it seems safer to keep these close together in the source code. I have also added an implementation note calling out this detail and reminding future developers to invalidate when needed if adding other Circuit mutations.

@maffoo maffoo requested review from vtomole, cduck and a team as code owners October 20, 2023 21:08
@CirqBot CirqBot added the size: M 50< lines changed <250 label Oct 20, 2023
@codecov
Copy link

codecov bot commented Oct 20, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (fec68ae) 97.83% compared to head (592bfed) 97.83%.
Report is 1 commits behind head on master.

❗ Current head 592bfed differs from pull request most recent head 73e895d. Consider uploading reports for the commit 73e895d to get more accurate results

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #6322   +/-   ##
=======================================
  Coverage   97.83%   97.83%           
=======================================
  Files        1108     1108           
  Lines       96266    96333   +67     
=======================================
+ Hits        94184    94251   +67     
  Misses       2082     2082           
Files Coverage Δ
cirq-core/cirq/circuits/circuit.py 98.52% <100.00%> (+0.07%) ⬆️
cirq-core/cirq/circuits/circuit_test.py 99.64% <100.00%> (+<0.01%) ⬆️
cirq-core/cirq/circuits/frozen_circuit.py 99.28% <100.00%> (+0.02%) ⬆️

... and 1 file with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@maffoo
Copy link
Contributor Author

maffoo commented Oct 25, 2023

Something to note is that there are a number of other circuit methods that could be optimized in the same way. For example, Circuit.all_qubits() requires traversing all operations in the circuit to compute the value, but we should be able to cache it until the circuit is mutated. This would improve performance in lots of real-world scenarios.

This simplifies working with frozen circuits and circuit identity by
storing the `FrozenCircuit` instance returned by `Circuit.freeze()` and
returning the same instance until it is "invalidated" by any mutations
of the Circuit itself. Note that if we make additional changes to the
Circuit implementation, such as adding new mutating methods, we will
need to remember to add `self._frozen = None` statements to invalidate
the frozen representation in those places as well. To reduce risk, I
have put these invalidation statements immediately after any mutation
operations on `self._moments`, even in places where some invalidations
could be elided or pushed to the end of a method; it seems safer to keep
these close together in the source code. I have also added an
implementation note calling out this detail and reminding future
developers to invalidate when needed if adding other `Circuit` mutations.
@maffoo maffoo changed the title Return same FrozenCircuit instance from Circuit.freeze() until mutated Cache Circuit properties between mutations Nov 1, 2023
@maffoo
Copy link
Contributor Author

maffoo commented Nov 1, 2023

I updated this to cache various other circuit parameters as well. To avoid slowing down mutation methods too much, I've changed this to call the self._mutated() method which resets cached properties only once in any given mutating method.

Copy link
Collaborator

@NoureldinYosri NoureldinYosri left a comment

Choose a reason for hiding this comment

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

Overall looks good, but I think we need tests of the form

for each property X that we cache
    query property X -> which caches the result
    for each method Y that mutates X
        do Y
        query X again and check that the result is correct

@@ -1769,6 +1773,12 @@ def __init__(
else:
self.append(flattened_contents, strategy=strategy)

def _mutated(self) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

shouldn't this also include _is_measurement and _parameter_names?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Fixed.

Repeated calls to `.freeze()` will return the same FrozenCircuit
instance as long as this circuit is not mutated.
"""
from cirq.circuits import FrozenCircuit
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: should this be a full import path cirq.circuits.frozen_circuit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was copied from what we had in AbstractCircuit.freeze previously. I don't feel strongly either way, though. Will change to use full import path.

@maffoo maffoo enabled auto-merge (squash) November 1, 2023 21:32
@maffoo maffoo merged commit 2f7d732 into master Nov 1, 2023
33 checks passed
@vtomole vtomole deleted the u/maffoo/circuit-freeze branch November 1, 2023 22:01
@vtomole vtomole restored the u/maffoo/circuit-freeze branch November 1, 2023 22:01
@maffoo maffoo deleted the u/maffoo/circuit-freeze branch November 1, 2023 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: M 50< lines changed <250
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants