diff --git a/qiskit/visualization/text.py b/qiskit/visualization/text.py index d2cbdfea512c..7a6dec09afdb 100644 --- a/qiskit/visualization/text.py +++ b/qiskit/visualization/text.py @@ -430,6 +430,7 @@ class Bullet(DirectOnQuWire): def __init__(self, top_connect="", bot_connect="", conditional=False, label=None, bottom=False): super().__init__("■") + self.conditional = conditional self.top_connect = top_connect self.bot_connect = "║" if conditional else bot_connect if label and bottom: @@ -451,6 +452,7 @@ class OpenBullet(DirectOnQuWire): def __init__(self, top_connect="", bot_connect="", conditional=False, label=None, bottom=False): super().__init__("o") + self.conditional = conditional self.top_connect = top_connect self.bot_connect = "║" if conditional else bot_connect if label and bottom: @@ -1033,6 +1035,10 @@ def _set_ctrl_state(self, node, conditional, ctrl_text, bottom): ctrl_qubits = node.qargs[:num_ctrl_qubits] cstate = f"{op.ctrl_state:b}".rjust(num_ctrl_qubits, "0")[::-1] for i in range(len(ctrl_qubits)): + # For sidetext gate alignment, need to set every Bullet with + # conditional on if there's a condition. + if op.condition is not None: + conditional = True if cstate[i] == "1": gates.append(Bullet(conditional=conditional, label=ctrl_text, bottom=bottom)) else: @@ -1502,3 +1508,5 @@ def connect_with(self, wire_char): if label: for affected_bit in affected_bits: affected_bit.right_fill = len(label) + len(affected_bit.mid) + if isinstance(affected_bit, (Bullet, OpenBullet)) and affected_bit.conditional: + affected_bit.left_fill = len(label) + len(affected_bit.mid) diff --git a/releasenotes/notes/fix-phase-gate-condition-text-display-3e1595ad508d225c.yaml b/releasenotes/notes/fix-phase-gate-condition-text-display-3e1595ad508d225c.yaml new file mode 100644 index 000000000000..eee0499d08cd --- /dev/null +++ b/releasenotes/notes/fix-phase-gate-condition-text-display-3e1595ad508d225c.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes a problem in the ``text`` circuit drawer when gates that + use side text, such as the ``CPhaseGate`` and ``RZZGate``, when + displayed with conditions would not display properly. diff --git a/test/ipynb/mpl/circuit/references/sidetext_condition.png b/test/ipynb/mpl/circuit/references/sidetext_condition.png new file mode 100644 index 000000000000..c603118cb629 Binary files /dev/null and b/test/ipynb/mpl/circuit/references/sidetext_condition.png differ diff --git a/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py index d9162052584c..5c4eac00792c 100644 --- a/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py +++ b/test/ipynb/mpl/circuit/test_circuit_matplotlib_drawer.py @@ -35,6 +35,7 @@ ZGate, SGate, U1Gate, + CPhaseGate, ) from qiskit.circuit.library import MCXVChain from qiskit.extensions import HamiltonianGate @@ -863,6 +864,14 @@ def test_conditions_with_bits_reverse(self): circuit, cregbundle=False, reverse_bits=True, filename="cond_bits_reverse.png" ) + def test_sidetext_with_condition(self): + """Test that sidetext gates align properly with conditions""" + qr = QuantumRegister(2, "q") + cr = ClassicalRegister(2, "c") + circuit = QuantumCircuit(qr, cr) + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + self.circuit_drawer(circuit, cregbundle=False, filename="sidetext_condition.png") + def test_fold_with_conditions(self): """Test that gates with conditions draw correctly when folding""" qr = QuantumRegister(3) diff --git a/test/python/visualization/references/test_latex_sidetext_condition.tex b/test/python/visualization/references/test_latex_sidetext_condition.tex new file mode 100644 index 000000000000..53726b5296bf --- /dev/null +++ b/test/python/visualization/references/test_latex_sidetext_condition.tex @@ -0,0 +1,14 @@ +\documentclass[border=2px]{standalone} + +\usepackage[braket, qm]{qcircuit} +\usepackage{graphicx} + +\begin{document} +\scalebox{1.0}{ +\Qcircuit @C=1.0em @R=0.8em @!R { \\ + \nghost{{q}_{0} : } & \lstick{{q}_{0} : } & \ctrl{1} & \dstick{\hspace{2.0em}\mathrm{P}\,(\mathrm{\frac{\pi}{2}})} \qw & \qw & \qw & \qw & \qw\\ + \nghost{{q}_{1} : } & \lstick{{q}_{1} : } & \control \qw & \qw & \qw & \qw & \qw & \qw\\ + \nghost{{c}_{0} : } & \lstick{{c}_{0} : } & \cw & \cw & \cw & \cw & \cw & \cw\\ + \nghost{{c}_{1} : } & \lstick{{c}_{1} : } & \control \cw^(0.0){^{\mathtt{}}} \cwx[-2] & \cw & \cw & \cw & \cw & \cw\\ +\\ }} +\end{document} \ No newline at end of file diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py index 1dec8c6dceb9..cef69b972d07 100644 --- a/test/python/visualization/test_circuit_latex.py +++ b/test/python/visualization/test_circuit_latex.py @@ -22,7 +22,7 @@ from qiskit.visualization import circuit_drawer from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile from qiskit.test.mock import FakeTenerife -from qiskit.circuit.library import XGate, MCXGate, RZZGate, SwapGate, DCXGate +from qiskit.circuit.library import XGate, MCXGate, RZZGate, SwapGate, DCXGate, CPhaseGate from qiskit.extensions import HamiltonianGate from qiskit.circuit import Parameter, Qubit, Clbit from qiskit.circuit.library import IQP @@ -645,6 +645,16 @@ def test_conditions_with_bits_reverse(self): ) self.assertEqualToReference(filename) + def test_sidetext_with_condition(self): + """Test that sidetext gates align properly with a condition""" + filename = self._get_resource_path("test_latex_sidetext_condition.tex") + qr = QuantumRegister(2, "q") + cr = ClassicalRegister(2, "c") + circuit = QuantumCircuit(qr, cr) + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + circuit_drawer(circuit, cregbundle=False, filename=filename, output="latex_source") + self.assertEqualToReference(filename) + if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index a57f98221e40..07cb4f5eb51d 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -635,6 +635,81 @@ def test_text_cp(self): circuit.append(CPhaseGate(pi / 2), [qr[2], qr[0]]) self.assertEqual(str(_text_circuit_drawer(circuit)), expected) + def test_text_cu1_condition(self): + """Test cu1 with condition""" + expected = "\n".join( + [ + " ", + "q_0: ────────■────────", + " │U1(π/2) ", + "q_1: ────────■────────", + " ║ ", + "q_2: ────────╫────────", + " ║ ", + "c_0: ════════╬════════", + " ║ ", + "c_1: ════════■════════", + " ", + "c_2: ═════════════════", + " ", + ] + ) + qr = QuantumRegister(3, "q") + cr = ClassicalRegister(3, "c") + circuit = QuantumCircuit(qr, cr) + circuit.append(CU1Gate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + + def test_text_rzz_condition(self): + """Test rzz with condition""" + expected = "\n".join( + [ + " ", + "q_0: ────────■────────", + " │ZZ(π/2) ", + "q_1: ────────■────────", + " ║ ", + "q_2: ────────╫────────", + " ║ ", + "c_0: ════════╬════════", + " ║ ", + "c_1: ════════■════════", + " ", + "c_2: ═════════════════", + " ", + ] + ) + qr = QuantumRegister(3, "q") + cr = ClassicalRegister(3, "c") + circuit = QuantumCircuit(qr, cr) + circuit.append(RZZGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + + def test_text_cp_condition(self): + """Test cp with condition""" + expected = "\n".join( + [ + " ", + "q_0: ───────■───────", + " │P(π/2) ", + "q_1: ───────■───────", + " ║ ", + "q_2: ───────╫───────", + " ║ ", + "c_0: ═══════╬═══════", + " ║ ", + "c_1: ═══════■═══════", + " ", + "c_2: ═══════════════", + " ", + ] + ) + qr = QuantumRegister(3, "q") + cr = ClassicalRegister(3, "c") + circuit = QuantumCircuit(qr, cr) + circuit.append(CPhaseGate(pi / 2), [qr[0], qr[1]]).c_if(cr[1], 1) + self.assertEqual(str(_text_circuit_drawer(circuit, initial_state=False)), expected) + def test_text_cu1_reverse_bits(self): """cu1 drawing with reverse_bits""" expected = "\n".join(