Skip to content

Support common notations in circuit_to_latex_using_qcircuit - PR for Issue #4685 #7151

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 8 commits into
base: main
Choose a base branch
from
Binary file not shown.
21 changes: 21 additions & 0 deletions cirq-core/cirq/contrib/qcircuit/pdf_test_files/test_pdf.tex
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this!

It's really great to provide an example file and rendered output. Would you be open to adding a couple more test cases? The issue #4685 only shows a particular example (namely, the particular case the OP was concerned about), but IMHO the issue is a little broader. This PR is an opportunity to cover a support slightly more operations. Especially welcome would be:

  • gates H, X, Y, Z gates
  • measurement operator

Examples of these gates can be seen in the Qcircuit tutorial and in the Cirq docs about basic circuits & gates. The gates in particular have some similarities, which suggests that the implementation in this PR could be done by looking for commonalities and writing generalized code, rather than very specific code for each individual case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I can add more test cases and cover these additional gates. I'll get working on that soon!

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
\documentclass{article}%
\usepackage[T1]{fontenc}%
\usepackage[utf8]{inputenc}%
\usepackage{lmodern}%
\usepackage{textcomp}%
\usepackage{lastpage}%
\usepackage{amsmath}%
\usepackage{qcircuit}%
%
\usepackage[utf8]{inputenc}%
%
\begin{document}%
\normalsize%
\Qcircuit @R=1em @C=0.75em {
\\
&\lstick{\text{q(0)}}& \qw&\ctrlo{} \qw &\control \qw &\targ \qw &\qswap \qw& \qw&\qw\\
&\lstick{\text{q(1)}}& \qw&\control \qw\qwx&\targ \qw\qwx&\ctrlo{} \qw\qwx&\qswap\qwx \qw&\qswap \qw&\qw\\
&\lstick{\text{q(2)}}& \qw&\targ \qw\qwx&\ctrlo{} \qw\qwx&\control \qw\qwx& \qw&\qswap\qwx \qw&\qw\\
\\
}%
\end{document}
53 changes: 40 additions & 13 deletions cirq-core/cirq/contrib/qcircuit/qcircuit_diagram_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def escape_text_for_latex(text):
return r'\text{' + escaped + '}'


def escape_text_for_latex_custom_control_gate(text):
return text.replace('(0)', r'\ctrlo{}').replace('@', r'\control').replace('X', r'\targ')


def get_multigate_parameters(args: protocols.CircuitDiagramInfoArgs) -> Optional[Tuple[int, int]]:
if (args.label_map is None) or (args.known_qubits is None):
return None
Expand Down Expand Up @@ -67,10 +71,20 @@ def hardcoded_qcircuit_diagram_info(op: ops.Operation) -> Optional[protocols.Cir
def convert_text_diagram_info_to_qcircuit_diagram_info(
info: protocols.CircuitDiagramInfo,
) -> protocols.CircuitDiagramInfo:
labels = [escape_text_for_latex(e) for e in info.wire_symbols]
if info.exponent != 1:
labels[0] += '^{' + str(info.exponent) + '}'
symbols = tuple(r'\gate{' + l + '}' for l in labels)
symbols = None
if (
len(info.wire_symbols) == 3
and '(0)' in info.wire_symbols
and '@' in info.wire_symbols
and 'X' in info.wire_symbols
):
labels = [escape_text_for_latex_custom_control_gate(e) for e in info.wire_symbols]
symbols = tuple(l for l in labels)
else:
labels = [escape_text_for_latex(e) for e in info.wire_symbols]
if info.exponent != 1:
labels[0] += '^{' + str(info.exponent) + '}'
symbols = tuple(r'\gate{' + l + '}' for l in labels)
return protocols.CircuitDiagramInfo(symbols)


Expand All @@ -89,17 +103,30 @@ def multigate_qcircuit_diagram_info(
info = protocols.circuit_diagram_info(op, args, default=None)

min_index, n_qubits = multigate_parameters
name = escape_text_for_latex(
str(op.gate).rsplit('**', 1)[0] if isinstance(op, ops.GateOperation) else str(op)
)
if (info is not None) and (info.exponent != 1):
name += '^{' + str(info.exponent) + '}'
box = r'\multigate{' + str(n_qubits - 1) + '}{' + name + '}'
ghost = r'\ghost{' + name + '}'

symbols = None
assert args.label_map is not None
assert args.known_qubits is not None
symbols = tuple(box if (args.label_map[q] == min_index) else ghost for q in args.known_qubits)
# Force exponent=1 to defer to exponent formatting given above.

if op.gate == ops.SWAP:
box = r'\qswap'
ghost = r'\qswap\qwx'
symbols = tuple(
box if (args.label_map[q] == min_index) else ghost for q in args.known_qubits
)
else:
name = escape_text_for_latex(
str(op.gate).rsplit('**', 1)[0] if isinstance(op, ops.GateOperation) else str(op)
)
if (info is not None) and (info.exponent != 1):
name += '^{' + str(info.exponent) + '}'
box = r'\multigate{' + str(n_qubits - 1) + '}{' + name + '}'
ghost = r'\ghost{' + name + '}'
symbols = tuple(
box if (args.label_map[q] == min_index) else ghost for q in args.known_qubits
)
# Force exponent=1 to defer to exponent formatting given above.

return protocols.CircuitDiagramInfo(symbols, connected=False)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ def test_get_qcircuit_diagram_info():
label_map=qubit_map,
)
actual_info = ccq.get_qcircuit_diagram_info(op, args)
expected_info = cirq.CircuitDiagramInfo(
(r'\ghost{\text{SWAP}}', r'\multigate{1}{\text{SWAP}}'), connected=False
)
expected_info = cirq.CircuitDiagramInfo((r'\qswap\qwx', r'\qswap'), connected=False)
assert actual_info == expected_info

qubit_map = {q: i for q, i in zip(qubits, (2, 5))}
Expand Down
65 changes: 65 additions & 0 deletions cirq-core/cirq/contrib/qcircuit/qcircuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import cirq.contrib.qcircuit as ccq
import cirq.testing as ct

# import cirq.contrib.qcircuit.qcircuit_pdf as pdf


def assert_has_qcircuit_diagram(actual: cirq.Circuit, desired: str, **kwargs) -> None:
"""Determines if a given circuit has the desired qcircuit diagram.
Expand Down Expand Up @@ -165,3 +167,66 @@ def test_sqrt_iswap_diagram():
\\
}""".strip()
assert_has_qcircuit_diagram(circuit, expected_diagram)


def test_latex_formatting():
# test for proper rendering of failing latex formats
q0, q1, q2 = cirq.LineQubit.range(3)

# custom gate with a control for zero and one
custom_gate = cirq.X.controlled(2, control_values=(0, 1))

circuit = cirq.Circuit(
custom_gate(q0, q1, q2),
custom_gate(q2, q0, q1),
custom_gate(q1, q2, q0),
cirq.SWAP(q0, q1),
cirq.SWAP(q1, q2),
)

expected_diagram = r"""
\Qcircuit @R=1em @C=0.75em {
\\
&\lstick{\text{q(0)}}& \qw&\ctrlo{} \qw &\control \qw &\targ \qw &\qswap \qw& \qw&\qw\\
&\lstick{\text{q(1)}}& \qw&\control \qw\qwx&\targ \qw\qwx&\ctrlo{} \qw\qwx&\qswap\qwx \qw&\qswap \qw&\qw\\
&\lstick{\text{q(2)}}& \qw&\targ \qw\qwx&\ctrlo{} \qw\qwx&\control \qw\qwx& \qw&\qswap\qwx \qw&\qw\\
\\
}""".strip()

assert_has_qcircuit_diagram(circuit, expected_diagram)


# def test_pdf_custom_gates():
# # test for proper rendering of failing latex formats in a pdf
# q0, q1, q2 = cirq.LineQubit.range(3)

# # custom gate with a control for zero and one
# custom_gate = cirq.X.controlled(2, control_values=(0, 1))

# circuit = cirq.Circuit(
# custom_gate(q0, q1, q2),
# custom_gate(q2, q0, q1),
# custom_gate(q1, q2, q0),
# cirq.SWAP(q0, q1),
# cirq.SWAP(q1, q2),
# )

# pdf_kwargs = {
# 'compiler': 'latexmk',
# 'compiler_args': ['-pdf', '-f', '-pdflatex=xelatex', '-quiet'],
# }

# remove = ['aux', 'fdb_latexmk', 'fls', 'log']

# # This test was facing errors due to technical issues in qcuircuit_pdf.py
# # that are out of scope of my issue (4685)
# # It will remain commented out until a resolution is found,
# # or until it is realized that this test is not necessary

# pdf.circuit_to_pdf_using_qcircuit_via_tex(
# circuit,
# "./cirq-core/cirq/contrib/qcircuit/pdf_test_files/test",
# pdf_kwargs,
# None,
# remove
# )
Comment on lines +199 to +232
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why this commented-out code is in the file. We don't typically want to leave commented-out code in the final versions of things, so it would be best to remove this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I will remove this - I was trying to test PDF output, but the required packages aren't included in the virtual environment set up, so the tests would not be able to run on everyone's machine.