Description
Description of the issue
When controlling an operation that has an exponent, the exponent should be drawn on the target qubit in the circuit diagram to clearly convey what's the underlying operation which is being controlled.
However, ControlledOperation
's circuit drawing function has multiple bugs. The code snippet to determine the exponent index of a ControlledOperation
is given below:
# Existing code snippet in `_circuit_diagram_info_` of `ControlledOperation` to determine the `exponent_qubit_index`
exponent_qubit_index = None if sub_info.exponent_qubit_index is None else sub_info.exponent_qubit_index + 1,
- If the underlying operation does not specify
exponent_qubit_index
, then theControlledOperation
always draws the exponent on the last (largest lexicographically ordered) qubit becauseexponent_qubit_index
staysNone
even though thesub_info.exponent
can be non-zero. - If the underlying operation does specify an
exponent_qubit_index
, then theControlledOperation
draws the exponent onsub_info.exponent_qubit_index + 1
instead ofsub_info.exponent_qubit_index + len(self.control_values)
as a result the exponent get's shifted by an incorrect value and gets drawn on one of the control qubits if the number of controls are > 1.
See the example below for more details:
How to reproduce the issue
In [2]: cirq.Circuit([
...: cirq.Moment( # First case where underlying sub operation has an exponent but it's exponent_qubit_index = None. ControlledOperation always draws the exponent on the last qubit.
...: cirq.ControlledOperation(sub_operation=(cirq.X**1.5).on(cirq.LineQubit(1)),control_values=((1,), (1,), (1,)),controls=(cirq.LineQubit(3), cirq.LineQubit(0), cirq.LineQubit(2))),
...: ),
...: cirq.Moment(# Second case where underlying sub operation has an exponent_qubit_index = 0. ControlledOperation just shifts it by 1 instead of len(control_values).
...: cirq.ControlledOperation(sub_operation=cirq.PhaseGradientGate(num_qubits=2, exponent=0.5).on(cirq.LineQubit(3), cirq.LineQubit(2)),control_values=((1,), (1,)),controls=(cirq.LineQubit(0), cirq.LineQubit(1))),
...: ),
...: ])
Out[2]:
0: ───@────────@───────
│ │
1: ───X────────@^0.5───
│ │
2: ───@────────#2──────
│ │
3: ───@^-0.5───Grad────```
Expected output of the above circuit should be:
0: ───@────────@──────────
│ │
1: ───X^-0.5───@──────────
│ │
2: ───@────────#2─────────
│ │
3: ───@────────Grad^0.5───
Note that fixing this would be a breaking change for diagrams.
Also, a similar bug exists in QuirkInputRotationOperation
, which always draws the exponent on qubit 0 if the underlying operation is not a ControlledOperation
and hence operations like CXPowGate
, which specify an explicit exponent_qubit_index
but are not instances of ControlledOperation
will have a wrong output.
Cirq version
0.13.0.dev