Skip to content
Merged
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
2 changes: 1 addition & 1 deletion cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def to_unitary_matrix(
self,
qubit_order_key: Callable[[QubitId], Any] = None,
qubits_that_should_be_present: Iterable[QubitId] = (),
ignore_terminal_measurements = True,
ignore_terminal_measurements: bool = True,
ext: Extensions = None) -> np.ndarray:
"""Converts the circuit into a unitary matrix, if possible.

Expand Down
1 change: 1 addition & 0 deletions cirq/contrib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
"""

from cirq.contrib import jobs
from cirq.contrib.qcircuit_diagram import circuit_to_latex_using_qcircuit
139 changes: 139 additions & 0 deletions cirq/contrib/qcircuit_diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Callable, Optional

from cirq import abc, circuits, extension, ops
from cirq.contrib.qcircuit_diagrammable_gate import (
QCircuitDiagrammableGate,
fallback_qcircuit_extensions,
)


class _QCircuitQubit(ops.QubitId):
def __init__(self, sub: ops.QubitId) -> None:
self.sub = sub

def __str__(self):
# TODO: If qubit name ends with digits, turn them into subscripts.
return '\\lstick{\\text{' + str(self.sub) + '}}&'

def __eq__(self, other):
if not isinstance(other, _QCircuitQubit):
return NotImplemented
return self.sub == other.sub

def __ne__(self, other):
return not self == other

def __hash__(self):
return hash((_QCircuitQubit, self.sub))


class _QCircuitGate(ops.TextDiagrammableGate, metaclass=abc.ABCMeta):
def __init__(self, sub: QCircuitDiagrammableGate) -> None:
self.sub = sub

def text_diagram_exponent(self):
return 1

def text_diagram_wire_symbols(self,
qubit_count: Optional[int] = None,
use_unicode_characters: bool = True):
return self.sub.qcircuit_wire_symbols(qubit_count)


def _render(diagram: circuits.TextDiagramDrawer) -> str:
w = diagram.width()
h = diagram.height()

qwx = {(x, y + 1)
for x, y1, y2 in diagram.vertical_lines
for y in range(y1, y2)}

qw = {(x, y)
for y, x1, x2 in diagram.horizontal_lines
for x in range(x1, x2)}

rows = []
for y in range(h):
row = []
for x in range(w):
cell = []
key = (x, y)
v = diagram.entries.get(key)
if v is not None:
cell.append(' ' + v + ' ')
if key in qw:
cell.append('\\qw ')
if key in qwx:
cell.append('\\qwx ')
row.append(''.join(cell))
rows.append('&'.join(row) + '\qw')

grid = '\\\\\n'.join(rows)

output = '\Qcircuit @R=1em @C=0.75em { \\\\ \n' + grid + ' \\\\ \n \\\\ }'

return output


def _wrap_operation(op: ops.Operation,
ext: extension.Extensions) -> ops.Operation:
new_qubits = [_QCircuitQubit(e) for e in op.qubits]
new_gate = ext.try_cast(op.gate, QCircuitDiagrammableGate)
if new_gate is None:
new_gate = fallback_qcircuit_extensions.cast(op.gate,
QCircuitDiagrammableGate)
return ops.Operation(_QCircuitGate(new_gate), new_qubits)


def _wrap_moment(moment: circuits.Moment,
ext: extension.Extensions) -> circuits.Moment:
return circuits.Moment(_wrap_operation(op, ext)
for op in moment.operations)


def _wrap_circuit(circuit: circuits.Circuit,
ext: extension.Extensions) -> circuits.Circuit:
return circuits.Circuit(_wrap_moment(moment, ext)
for moment in circuit.moments)


def circuit_to_latex_using_qcircuit(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might just do circuit_to_latex. If we want to support something other than qcircuit we could pass some sort of type in to this to distiguish

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I also think that could be nice; but I'll wait until we have other supported formats before amalgamating.

circuit: circuits.Circuit,
ext: extension.Extensions = None,
qubit_order_key: Callable[[ops.QubitId], Any] = None) -> str:
"""Returns a QCircuit-based latex diagram of the given circuit.

Args:
circuit: The circuit to represent in latex.
ext: Extensions used when attempting to cast gates into
QCircuitDiagrammableGate instances (before falling back to the
default wrapping methods).
qubit_order_key: Determines the order of qubit wires in the diagram.

Returns:
Latex code for the diagram.
"""
if ext is None:
ext = extension.Extensions()
qcircuit = _wrap_circuit(circuit, ext)
diagram = qcircuit.to_text_diagram_drawer(
ext,
qubit_name_suffix='',
qubit_order_key=(None
if qubit_order_key is None
else lambda e: qubit_order_key(e.sub)))
return _render(diagram)
113 changes: 113 additions & 0 deletions cirq/contrib/qcircuit_diagrammable_gate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Optional

import cirq
from cirq import Extensions, ops
from cirq import abc


class QCircuitDiagrammableGate(ops.Gate, metaclass=abc.ABCMeta):
@abc.abstractmethod
def qcircuit_wire_symbols(self, qubit_count: Optional[int] = None):
pass


def _escape_text_for_latex(text):
escaped = (text
.replace('\\', '\\textbackslash{}')
.replace('^', '\\textasciicircum{}')
.replace('~', '\\textasciitilde{}')
.replace('_', '\\_')
.replace('{', '\\{')
.replace('}', '\\}')
.replace('$', '\\$')
.replace('%', '\\%')
.replace('&', '\\&')
.replace('#', '\\#'))
return '\\text{' + escaped + '}'


class _HardcodedQCircuitSymbolsGate(QCircuitDiagrammableGate):
def __init__(self, *symbols) -> None:
self.symbols = symbols

def qcircuit_wire_symbols(self, qubit_count=None):
return self.symbols


class _WrappedSymbolsQCircuitGate(QCircuitDiagrammableGate):
def __init__(self, sub: ops.TextDiagrammableGate) -> None:
self.sub = sub

def qcircuit_wire_symbols(self, qubit_count=None):
s = self.sub.text_diagram_wire_symbols()
s = [_escape_text_for_latex(e) for e in s]
e = self.sub.text_diagram_exponent()
if e != 1:
s[0] += '^{' + str(e) + '}'
return tuple('\\gate{' + e + '}' for e in s)


class _FallbackQCircuitSymbolsGate(QCircuitDiagrammableGate):
def __init__(self, sub: ops.Gate) -> None:
self.sub = sub

def qcircuit_wire_symbols(self, qubit_count=None):
name = str(self.sub)
if qubit_count is None:
qubit_count = 1
return tuple(_escape_text_for_latex('{}:{}'.format(name, i))
for i in range(qubit_count))


fallback_qcircuit_extensions = Extensions()
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
ops.TextDiagrammableGate,
_WrappedSymbolsQCircuitGate)
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
ops.RotXGate,
lambda gate:
_HardcodedQCircuitSymbolsGate('\\targ')
if gate.half_turns == 1
else None)
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
ops.MeasurementGate,
lambda gate: _HardcodedQCircuitSymbolsGate('\\meter'))
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
cirq.google.ExpWGate,
lambda gate:
_HardcodedQCircuitSymbolsGate('\\targ')
if gate.half_turns == 1 and gate.axis_half_turns == 0
else None)
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
ops.Rot11Gate,
lambda gate:
_HardcodedQCircuitSymbolsGate('\\control', '\\control')
if gate.half_turns == 1
else None)
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
ops.CNotGate,
lambda gate: _HardcodedQCircuitSymbolsGate('\\control', '\\targ'))
fallback_qcircuit_extensions.add_cast(
QCircuitDiagrammableGate,
ops.Gate,
_FallbackQCircuitSymbolsGate)
43 changes: 43 additions & 0 deletions cirq/contrib/qcircuit_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from cirq import ops, Circuit
from cirq.contrib import circuit_to_latex_using_qcircuit


def test_teleportation_diagram():
ali = ops.NamedQubit('alice')
car = ops.NamedQubit('carrier')
bob = ops.NamedQubit('bob')

circuit = Circuit.from_ops(
ops.H(car),
ops.CNOT(car, bob),
ops.X(ali)**0.5,
ops.CNOT(ali, car),
ops.H(ali),
[ops.MeasurementGate()(ali), ops.MeasurementGate()(car)],
ops.CNOT(car, bob),
ops.CZ(ali, bob))

diagram = circuit_to_latex_using_qcircuit(
circuit,
qubit_order_key=[ali, car, bob].index)
assert diagram.strip() == """
\\Qcircuit @R=1em @C=0.75em { \\\\
\\lstick{\\text{alice}}& \\qw &\\qw & \\gate{\\text{X}^{0.5}} \\qw & \\control \\qw & \\gate{\\text{H}} \\qw & \\meter \\qw &\\qw & \\control \\qw &\\qw\\\\
\\lstick{\\text{carrier}}& \\qw & \\gate{\\text{H}} \\qw & \\control \\qw & \\targ \\qw \\qwx &\\qw & \\meter \\qw & \\control \\qw &\\qw \\qwx &\\qw\\\\
\\lstick{\\text{bob}}& \\qw &\\qw & \\targ \\qw \\qwx &\\qw &\\qw &\\qw & \\targ \\qw \\qwx & \\control \\qw \\qwx &\\qw \\\\
\\\\ }
""".strip()