Skip to content

Fix classical control diagram symbols and svg #7233

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions cirq-core/cirq/contrib/svg/svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,31 @@ def tdd_to_svg(
t = (
f'<svg xmlns="http://www.w3.org/2000/svg" '
f'width="{col_starts[-1]}" height="{row_starts[-1]}">'
'''
<defs>
<marker id="arrow"
viewBox="0 0 10 10"
refX="9"
refY="5"
markerUnits="strokeWidth"
markerWidth="5"
markerHeight="5"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<filter id="double" filterUnits="userSpaceOnUse">
<feMorphology in="SourceGraphic" result="a" operator="dilate" radius="1" />
<feComposite in="SourceGraphic" in2="a" operator="xor" />
</filter>
</defs>
'''
)

# Developers: uncomment below to draw green lines to debug
# col_starts and row_starts
# t += _debug_spacing(col_starts, row_starts)

for yi, xi1, xi2, _, _ in tdd.horizontal_lines:
for yi, xi1, xi2, _, doubled in tdd.horizontal_lines:
xi1 = cast(int, xi1)
xi2 = cast(int, xi2)
x1 = col_starts[xi1] + col_widths[xi1] / 2
Expand All @@ -203,17 +221,26 @@ def tdd_to_svg(
stroke = QBLUE
else:
stroke = 'black' # pragma: no cover
t += f'<line x1="{x1}" x2="{x2}" y1="{y}" y2="{y}" stroke="{stroke}" stroke-width="1" />'
t += f'<line x1="{x1}" x2="{x2}" y1="{y}" y2="{y}" stroke="{stroke}" stroke-width="1"'
if doubled:
t += ' filter="url(#double)"'
t += ' />'

for xi, yi1, yi2, _, _ in tdd.vertical_lines:
for xi, yi1, yi2, _, doubled in tdd.vertical_lines:
arrow = doubled and tdd.entries[int(xi), int(yi2)].text == 'V'
yi1 = yi_map[yi1]
yi2 = yi_map[yi2]
y1 = row_starts[yi1] + row_heights[yi1] / 2
y2 = row_starts[yi2] + row_heights[yi2] / 2

xi = cast(int, xi)
x = col_starts[xi] + col_widths[xi] / 2
t += f'<line x1="{x}" x2="{x}" y1="{y1}" y2="{y2}" stroke="black" stroke-width="3" />'
t += f'<line x1="{x}" x2="{x}" y1="{y1}" y2="{y2}" stroke="black" stroke-width="3"'
if doubled:
t += ' filter="url(#double)"'
if arrow:
t += ' marker-end="url(#arrow)"'
t += ' />'

for (xi, yi), v in tdd.entries.items():
yi = yi_map[yi]
Expand All @@ -238,7 +265,7 @@ def tdd_to_svg(
if v.text == '×':
t += _text(x, y + 3, '×', fontsize=40)
continue
if v.text == '':
if v.text == '' or v.text == 'V':
continue

v_text = fixup_text(v.text)
Expand Down
4 changes: 3 additions & 1 deletion cirq-core/cirq/contrib/svg/svg_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ def test_svg():
cirq.PhasedXPowGate(exponent=0.123, phase_exponent=0.456).on(c),
cirq.Z(a),
cirq.measure(a, b, c, key='z'),
cirq.MatrixGate(np.eye(2)).on(a),
cirq.MatrixGate(np.eye(2)).on(a).with_classical_controls('z'),
)
)
assert '?' in svg_text
assert '<svg' in svg_text
assert '</svg>' in svg_text
assert 'double' in svg_text
assert 'arrow' in svg_text


def test_svg_noise():
Expand Down
8 changes: 4 additions & 4 deletions cirq-core/cirq/ops/classically_controlled_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ClassicallyControlledOperation(raw_types.Operation):
1: ─────────────╫───X───
║ ║
control_key: ═══@═══^═══
control_key: ═══V═══@═══
>>> circuit2 = cirq.Circuit([
... cirq.measure(a, key='control_key1'),
... cirq.measure(b, key='control_key2'),
Expand All @@ -71,9 +71,9 @@ class ClassicallyControlledOperation(raw_types.Operation):
║║
2: ───────────────╫╫────X───
║║ ║
control_key1: ════@╬════^═══
control_key1: ════V╬════@═══
║ ║
control_key2: ═════@════^═══
control_key2: ═════V════@═══
└──┘
"""

Expand Down Expand Up @@ -189,7 +189,7 @@ def _circuit_diagram_info_(
control_label_count = 0
if args.label_map is not None:
control_label_count = len({k for c in self._conditions for k in c.keys})
wire_symbols = sub_info.wire_symbols + ('^',) * control_label_count
wire_symbols = sub_info.wire_symbols + ('@',) * control_label_count
if control_label_count == 0 or any(
not isinstance(c, value.KeyCondition) for c in self._conditions
):
Expand Down
98 changes: 49 additions & 49 deletions cirq-core/cirq/ops/classically_controlled_operation_test.py

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion cirq-core/cirq/ops/measurement_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def _circuit_diagram_info_(
if self.key not in label_map:
symbols[0] += f"('{self.key}')"
if self.key in label_map:
symbols += '@'
symbols += 'V'

return protocols.CircuitDiagramInfo(symbols)

Expand Down
2 changes: 1 addition & 1 deletion cirq-core/cirq/ops/pauli_measurement_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def _circuit_diagram_info_(
if self.key not in label_map:
symbols[0] += f"('{self.key}')"
if self.key in label_map:
symbols += '@'
symbols += 'V'

return protocols.CircuitDiagramInfo(tuple(symbols))

Expand Down
6 changes: 3 additions & 3 deletions cirq-core/cirq/transformers/merge_k_qubit_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def test_merge_complex_circuit_preserving_moment_structure():
│ │ ║
2: ───H───X───────────────────────────X───────────────Z───────────M───╫───
║ ║
a: ═══════════════════════════════════════════════════════════════@═══^═══
a: ═══════════════════════════════════════════════════════════════V═══@═══
''',
)
component_id = 0
Expand All @@ -155,7 +155,7 @@ def rewriter_merge_to_circuit_op(op: 'cirq.CircuitOperation') -> 'cirq.OP_TREE':
│ │ ║
2: ───#2──────────────────────────────────────────────────────────────────X───────────[ 2: ───Z─── ][3]───M───────────────────────╫───
║ ║
a: ═══════════════════════════════════════════════════════════════════════════════════════════════════════@═══════════════════════^═══''',
a: ═══════════════════════════════════════════════════════════════════════════════════════════════════════V═══════════════════════@═══''',
)

component_id = 0
Expand Down Expand Up @@ -185,7 +185,7 @@ def rewriter_replace_with_decomp(op: 'cirq.CircuitOperation') -> 'cirq.OP_TREE':
│ │ ║
2: ───T[1]───iSwap^0.5───T[1]─────────────────────────────X───────────T[3]────────M─────────────────────────╫───
║ ║
a: ═══════════════════════════════════════════════════════════════════════════════@═════════════════════════^═══''',
a: ═══════════════════════════════════════════════════════════════════════════════V═════════════════════════@═══''',
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def test_merge_single_qubit_moments_to_phxz():
║ ║
2: ───────T───────Y───────T───────Z──────────────────╫───╫───────
║ ║
a: ══════════════════════════════════════════════════@═══^═══════
a: ══════════════════════════════════════════════════V═══@═══════
''',
)
context = cirq.TransformerContext(tags_to_ignore=("nocompile",))
Expand All @@ -173,7 +173,7 @@ def test_merge_single_qubit_moments_to_phxz():
║ ║
2: ───PhXZ(a=0.25,x=0,z=0.25)────Y───PhXZ(a=0.25,x=0,z=0.25)────Z──────────────────╫───╫───────
║ ║
a: ════════════════════════════════════════════════════════════════════════════════@═══^═══════
a: ════════════════════════════════════════════════════════════════════════════════V═══@═══════
''',
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_decompose_operations_to_target_gateset_default():
│ │ ║ │
1: ───────×───────×───────────╫───X───T───×───T───
║ ║
m: ═══════════════════════════@═══^═══════════════''',
m: ═══════════════════════════V═══@═══════════════''',
)
context = cirq.TransformerContext(tags_to_ignore=("ignore",))
c_new = _decompose_operations_to_target_gateset(c_orig, context=context)
Expand All @@ -69,7 +69,7 @@ def test_decompose_operations_to_target_gateset_default():
│ │ │ │ ║ │ │ │
1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×───────────╫───X───T───Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───T───
║ ║
m: ══════════════════════════════════════════════════════════════════════@═══^══════════════════════════════════════════════════════════
m: ══════════════════════════════════════════════════════════════════════V═══@══════════════════════════════════════════════════════════
''',
)

Expand Down Expand Up @@ -104,7 +104,7 @@ def test_decompose_operations_to_target_gateset():
│ │ │ │ ║ │ │ │
1: ───────X───@───X───────×───────────╫───X───H───X───@───X───H───
║ ║
m: ═══════════════════════════════════@═══^═══════════════════════''',
m: ═══════════════════════════════════V═══@═══════════════════════''',
)

with pytest.raises(ValueError, match="Unable to convert"):
Expand Down Expand Up @@ -168,7 +168,7 @@ def test_optimize_for_target_gateset():
│ │ ║ ║ │
3: ───#4────X───────────╫───@───#4───────
║ ║
m: ═════════════════════@═══^════════════
m: ═════════════════════V═══@════════════
''',
)
gateset = MatrixGateTargetGateset()
Expand All @@ -186,7 +186,7 @@ def test_optimize_for_target_gateset():
│ │ │ │ ║ ║ │ │ │ │
3: ────────────────────────M[2]───M[2]────────M[2]────X───────────╫───@────────M[2]────M[2]───M[2]────M[2]──────────────────────
║ ║
m: ═══════════════════════════════════════════════════════════════@═══^═════════════════════════════════════════════════════════
m: ═══════════════════════════════════════════════════════════════V═══@═════════════════════════════════════════════════════════
└────────┘ └────────┘ └────────┘
''',
)
Expand Down
4 changes: 2 additions & 2 deletions cirq-core/cirq/transformers/stratify_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ def test_does_not_move_ccos_behind_measurement():
║ ║
2: ───╫───╫───X───
║ ║
m: ═══@═══^═══════
m: ═══V═══@═══════
''',
)
c_out = cirq.stratified_circuit(
Expand All @@ -304,7 +304,7 @@ def test_does_not_move_ccos_behind_measurement():
║ ║
2: ────╫X────╫───────
║ ║
m: ════@═════^═══════
m: ════V═════@═══════
└──┘
''',
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def test_two_qubit_compilation_merge_and_replace_inefficient_component():
│ │ │ │ │ ║
1: ───────X───────@───────────────Z───────X───X───Z───────X───╫───X───
║ ║
m: ═══════════════════════════════════════════════════════════@═══^═══
m: ═══════════════════════════════════════════════════════════V═══@═══
''',
)
c_new = cirq.optimize_for_target_gateset(
Expand All @@ -199,7 +199,7 @@ def test_two_qubit_compilation_merge_and_replace_inefficient_component():
│ │ │ │ ║
1: ───────X───────@───────────────X───X───Y───X───Z───╫───X───
║ ║
m: ═══════════════════════════════════════════════════@═══^═══
m: ═══════════════════════════════════════════════════V═══@═══
''',
)

Expand Down
4 changes: 2 additions & 2 deletions examples/quantum_teleportation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
│ ║ ║
2: ─────────────────X─────────────╫────╫X────Z───
║ ║║ ║
alice: ═══════════════════════════@════╬^════╬═══
alice: ═══════════════════════════V════╬@════╬═══
║ ║
msg: ══════════════════════════════════@═════^═══
msg: ══════════════════════════════════V═════@═══
└──┘

Bloch Sphere of Message After Random X and Y Gates:
Expand Down