Preserve subcircuits passed to [Frozen]Circuit.from_moments - #6320
Conversation
[Frozen]Circuit.from_moments
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #6320 +/- ##
=======================================
Coverage 97.89% 97.89%
=======================================
Files 1108 1108
Lines 96192 96209 +17
=======================================
+ Hits 94165 94186 +21
+ Misses 2027 2023 -4
☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
I think this further complicates the circuit construction logic, and we already have many different circuit construction paths. I'd like to understand the motivation better.
Can the user just directly call .to_op() at the call site to achieve the deserived effect? So for your examples:
circuit = Circuit.from_moments(
FrozenCircuit(Moment(X(q0), X(q1))).to_op()
)and
circuit = Circuit.from_moments(
FrozenCircuit(
Moment(X(q0)),
Moment(Y(q0)),
).to_op()
)Circuit.from_moments is well defined right now. I don't think we should add more magic and special casing unless absolutely required.
My motivation is to make the common case of dealing with subcircuits easier to write. This came up internally because we're trying to move more internal code to use subcircuits with
Yes, that is doable, though in most case where this comes up internally we're dealing with circuits that are produced in other places and they may not already be frozen, so you end up doing something like: cirq.Circuit.from_moments(
state_prep_circuit.freeze().to_op(),
main_circuit.freeze().to_op(),
measure_circuit.freeze().to_op(),
)This seems to me like unneccesary boilerplate compared to allowing the user to do: cirq.Circuit.from_moments(
state_prep_circuit, main_circuit, measure_circuit,
)
The proposed change still results in well-defined behavior for |
Yes, but this opens a can of worms where we can continue to make incremental updates and again end up in a situation like cirq.Circuit.from_moments(
cirq.Circuit(cirq.X(a), cirq.Y(a)), # works, creates a moment with circuit operation.
[cirq.X(a), cirq.Y(b)], # works, creates a moment with 2 non overlapping operations.
[cirq.X(a), cirq.Y(a)], # still fails, but this could also be wrapped in a circuit op?
# still fails, because of no auto flattening. This looks like a valid potential use case
# where we want a moment with a circuit operation on qubit `a` and a Z gate on
# qubit `b` ?
[cirq.Circuit(cirq.X(a), cirq.Y(a)), cirq.Z(b)],
)Instead, I'd suggest we either: a) Add a What do you think? |
I agree we want to avoid adding more logic. I think the thing I'd like to do is preserve structure that already exists in the arguments passed to
I don't see a reason to wrap this in a circuit op. The goal is to preserve the structure that is present in the args, not to add any new structure (especially not if it would require inspecting the contents beyond checking the type, e.g. traversing into an op tree). If you want
Yeah, I don't think we should do any auto-flattening, because that is changing the structure provided by the user. If you want to take a circuit and include its moments in the circuit produced by Circuit.from_moments(*circuit_as_moments, circuit_as_subcircuit)This PR preserves the first behavior of
I think a new flag on |
tanujkhattar
left a comment
There was a problem hiding this comment.
Okay, let's get this in and make sure we don't add more special casing to Circuit.from_moments.
We should mark this as a breaking change because cirq.Circuit.from_moments(cirq.Circuit(cirq.X(a), cirq.Y(b))) results in a different behavior now.
Ideally, we should not be doing breaking changes but I think this is a small enough corner case so we can have an exception.
cc @dstrain115 in case you have any concerns.
|
Thanks for the careful review, @tanujkhattar! I will keep this open for a while in case other people have comments. |
Preserving subcircuits is very useful internally because we use subcircuit identity when compiling to hardware to guide waveform reuse. Note that previously if a circuit was passed to
from_momentsit would be passed toMomentwhich would fail for any circuit that had multiple moments with gates on the same qubit, because those could not be collapsed into a single moment. Single-moment circuits would've worked in the past and they will continue to work but will now being contained in a circuit operation.Here's an example of a single-moment circuit:
Here's an example of a multi-moment circuit with repeated operations: