Implement dynamical decoupling.#6515
Conversation
|
Post the draft for general suggestions. And see how the CI goes compared with local CI. |
eliottrosenberg
left a comment
There was a problem hiding this comment.
LGTM up to some nits and suggestions. Once this is merged, we can discuss some possible ways to augment it. Thanks!
| ) -> list['cirq.Gate']: | ||
| match schema: | ||
| case _DynamicalDecouplingSchema.XX_PAIR: | ||
| return _repeat_sequence([cirq.XPowGate(), cirq.XPowGate()], num_idle_moments) |
There was a problem hiding this comment.
I think you want cirq.X instead of cirq.XPowGate().
| case _DynamicalDecouplingSchema.XX_PAIR: | ||
| return _repeat_sequence([cirq.XPowGate(), cirq.XPowGate()], num_idle_moments) | ||
| case _DynamicalDecouplingSchema.YY_PAIR: | ||
| return _repeat_sequence([cirq.YPowGate(), cirq.YPowGate()], num_idle_moments) |
There was a problem hiding this comment.
Similarly, I think you want cirq.Y here.
| raise ValueError('Invalid dynamical decoupling sequence. Expect more than one gates.') | ||
| matrices = [cirq.unitary(gate) for gate in dd_sequence] | ||
| product = reduce(np.matmul, matrices) | ||
| if not np.array_equal(product, np.eye(2)): |
There was a problem hiding this comment.
You could use cirq.equal_up_to_global_phase() here (since we don't care if the sequence differs from the identity by a global phase).
| class _DynamicalDecouplingSchema(enum.Enum): | ||
| """Supported schemes of dynamical decoupling.""" | ||
|
|
||
| XX_PAIR = 'XX_PAIR' |
There was a problem hiding this comment.
We may also want one that is does X X**-1. X and X**-1 have the same unitary but are implemented differently on hardware. Similarly for Y.
snichet
left a comment
There was a problem hiding this comment.
A couple suggestions for optimal implementation on hardware.
| dd_model=DynamicalDecouplingModel.from_schema("XX_PAIR"), | ||
| ) | ||
|
|
||
| # Insert one XX_PAIR dynamical decoupling sequence in idle moments. |
| return self.schema, self.base_dd_sequence | ||
|
|
||
|
|
||
| def add_dynamical_decoupling( |
There was a problem hiding this comment.
Might be nice to have an option only to insert DD gates on moments that only have single qubits gates. On the hardware, the calibrations for the gates do not include calibrating single qubits and two qubits gates concurrently.
There was a problem hiding this comment.
Good idea, tracking this here. Feel free to put more thoughts there, thanks!
| num_idle_moments=moment_id - last_busy_moment_by_qubits[q] - 1 | ||
| ) | ||
| for idx, gate in enumerate(insert_gates): | ||
| insert_into.append((last_busy_moment_by_qubits[q] + idx + 1, gate.on(q))) |
There was a problem hiding this comment.
It seems like this is only inserting X gates on consecutive idle moments. I think the next step would be to divide the circuit into moments that are Clifford, then insert X gates on all idle single qubit moments (and perhaps also idle 2 qubit moments, but the caveat is my comment above). Then use the fact that Clifford circuits can be quickly and efficiently simulated to put the correct Clifford gate at the end of the group of moments, to inverse all the previous inserted gates. For CZ gates, this should just be a X, Y or Z gate, i believe.
There was a problem hiding this comment.
Good idea, tracking this here. Feel free to put more thoughts there, thanks!
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6515 +/- ##
==========================================
- Coverage 97.79% 97.79% -0.01%
==========================================
Files 1124 1126 +2
Lines 95705 95784 +79
==========================================
+ Hits 93595 93670 +75
- Misses 2110 2114 +4 ☔ View full report in Codecov by Sentry. |
| ) | ||
|
|
||
|
|
||
| @value.value_equality |
There was a problem hiding this comment.
it doesn't look like we mutate the properties of this class, consider making this a dataclass/attrs.frozen e.g.
@attrs.frozen
class DynamicalDecouplingModel:
schema: Optional[_DynamicalDecouplingSchema] = attrs.field(default=None, validator=...)
base_dd_sequence: Optional[list['cirq.Gate']] = attrs.field(default=None, validator=...)
...etcactually I don't think this class is needed.. consider removing it, this will simplify your design and make your life easier as you won't need to worry about json
There was a problem hiding this comment.
Done.
Context: I was not so sure about whether to add a model for dynamical decoupling. I chose to add a model class as I expected the dynamical decoupling schema might get more and more complex in the future. While for now, we only need to handle the case of "Supported schemes" and "Customized sequence". A function is good enough, and we do not need to worry about json.
| circuit: 'cirq.AbstractCircuit', | ||
| *, | ||
| context: Optional['cirq.TransformerContext'] = None, | ||
| dd_model: DynamicalDecouplingModel = DynamicalDecouplingModel.from_schema("X_XINV"), |
There was a problem hiding this comment.
instead of dd_model try
spin_echo_sequence: Sequence[cirq.Gate] = (cirq.X, cirq.X),There was a problem hiding this comment.
Updated with schema.
My understanding is "dynamical decoupling sequence" is more general than "spin echo sequence", correct me if I was wrong @eliottrosenberg .
| dd_model: DynamicalDecouplingModel = DynamicalDecouplingModel.from_schema("X_XINV"), | ||
| ) -> 'cirq.Circuit': | ||
| """Add dynamical decoupling gate operations to a given circuit. | ||
|
|
There was a problem hiding this comment.
does this preserve the moment structure? consider adding this to the docstring.
There was a problem hiding this comment.
It preserves the moment structure.
Updated docstring.
NoureldinYosri
left a comment
There was a problem hiding this comment.
just a little bit more
| except ValueError: | ||
| raise | ||
| else: | ||
| is_valid, error_message = _validate_dd_sequence(schema) |
There was a problem hiding this comment.
this is a golang style, prefer to just raise the error inside _validate_dd_sequence
| last_busy_moment_by_qubits: Dict['cirq.Qid', int] = {q: 0 for q in circuit.all_qubits()} | ||
| insert_into: list[Tuple[int, 'cirq.OP_TREE']] = [] | ||
|
|
||
| if isinstance(schema, str): |
There was a problem hiding this comment.
consider grouping this logic into a function (e.g. _parse_dd_sequence(schema))
NoureldinYosri
left a comment
There was a problem hiding this comment.
@babacry congratulations on your first PR
|
Thanks for the careful review, everyone! |
Add dynamical decoupling operations to circuits for idle moments.
Two methods are provided to apply dynamical decoupling.
base_dd_sequence.We repeat given dd sequence in continuous idle moments until the remaining idle moments isn't long enough for another repetition.