# Copyright 2018 The Cirq Developers
#
# 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.
import collections
from typing import Any, TYPE_CHECKING, Optional, Union, Tuple, TypeVar, Dict, \
overload, Iterable
from typing_extensions import Protocol
from cirq import value
if TYPE_CHECKING:
# pylint: disable=unused-import
import cirq
[docs]@value.value_equality
class CircuitDiagramInfo:
"""Describes how to draw an operation in a circuit diagram."""
[docs] def __init__(self,
wire_symbols: Tuple[str, ...],
exponent: Any = 1,
connected: bool = True) -> None:
"""
Args:
wire_symbols: The symbols that should be shown on the qubits
affected by this operation. Must match the number of qubits that
the operation is applied to.
exponent: An optional convenience value that will be appended onto
an operation's final gate symbol with a caret in front
(unless it's equal to 1). For example, the square root of X gate
has a text diagram exponent of 0.5 and symbol of 'X' so it is
drawn as 'X^0.5'.
connected: Whether or not to draw a line connecting the qubits.
"""
if isinstance(wire_symbols, str):
raise ValueError(
'Expected a Tuple[str] for wire_symbols but got a str.')
self.wire_symbols = wire_symbols
self.exponent = exponent
self.connected = connected
def _value_equality_values_(self):
return self.wire_symbols, self.exponent, self.connected
def __repr__(self):
return ('cirq.CircuitDiagramInfo(' +
'wire_symbols={!r}, '.format(self.wire_symbols) +
'exponent={!r}, '.format(self.exponent) +
'connected={!r})'.format(self.connected)
)
[docs]@value.value_equality
class CircuitDiagramInfoArgs:
"""A request for information on drawing an operation in a circuit diagram.
Attributes:
known_qubits: The qubits the gate is being applied to. None means this
information is not known by the caller.
known_qubit_count: The number of qubits the gate is being applied to
None means this information is not known by the caller.
use_unicode_characters: If true, the wire symbols are permitted to
include unicode characters (as long as they work well in fixed
width fonts). If false, use only ascii characters. ASCII is
preferred in cases where UTF8 support is done poorly, or where
the fixed-width font being used to show the diagrams does not
properly handle unicode characters.
precision: The number of digits after the decimal to show for numbers in
the text diagram. None means use full precision.
qubit_map: The map from qubits to diagram positions.
"""
UNINFORMED_DEFAULT = None # type: CircuitDiagramInfoArgs
[docs] def __init__(self,
known_qubits: Optional[Iterable['cirq.QubitId']],
known_qubit_count: Optional[int],
use_unicode_characters: bool,
precision: Optional[int],
qubit_map: Optional[Dict['cirq.QubitId', int]]) -> None:
self.known_qubits = (None if known_qubits is None
else tuple(known_qubits))
self.known_qubit_count = known_qubit_count
self.use_unicode_characters = use_unicode_characters
self.precision = precision
self.qubit_map = qubit_map
def _value_equality_values_(self):
return (self.known_qubits,
self.known_qubit_count,
self.use_unicode_characters,
self.precision,
None
if self.qubit_map is None else
tuple(sorted(self.qubit_map.items(), key=lambda e: e[0])))
def __repr__(self):
return (
'cirq.CircuitDiagramInfoArgs('
'known_qubits={!r}, '
'known_qubit_count={!r}, '
'use_unicode_characters={!r}, '
'precision={!r}, '
'qubit_map={!r})'.format(
self.known_qubits,
self.known_qubit_count,
self.use_unicode_characters,
self.precision,
self.qubit_map))
CircuitDiagramInfoArgs.UNINFORMED_DEFAULT = CircuitDiagramInfoArgs(
known_qubits=None,
known_qubit_count=None,
use_unicode_characters=True,
precision=3,
qubit_map=None)
[docs]class SupportsCircuitDiagramInfo(Protocol):
"""A diagrammable operation on qubits."""
def _circuit_diagram_info_(self, args: CircuitDiagramInfoArgs
) -> Union[str,
Iterable[str],
CircuitDiagramInfo]:
"""Describes how to draw an operation in a circuit diagram.
This method is used by the global `cirq.diagram_info` method. If this
method is not present, or returns NotImplemented, it is assumed that the
receiving object doesn't specify diagram info.
Args:
args: A DiagramInfoArgs instance encapsulating various pieces of
information (e.g. how many qubits are we being applied to) as
well as user options (e.g. whether to avoid unicode characters).
Returns:
A DiagramInfo instance describing what to show.
"""
TDefault = TypeVar('TDefault')
RaiseTypeErrorIfNotProvided = CircuitDiagramInfo(())
# pylint: disable=function-redefined
@overload
def circuit_diagram_info(val: Any,
args: Optional[CircuitDiagramInfoArgs] = None,
) -> CircuitDiagramInfo:
pass
@overload
def circuit_diagram_info(val: Any,
args: Optional[CircuitDiagramInfoArgs],
default: TDefault
) -> Union[CircuitDiagramInfo, TDefault]:
pass
@overload
def circuit_diagram_info(val: Any,
*,
default: TDefault
) -> Union[CircuitDiagramInfo, TDefault]:
pass
[docs]def circuit_diagram_info(val: Any,
args: Optional[CircuitDiagramInfoArgs] = None,
default=RaiseTypeErrorIfNotProvided):
"""Requests information on drawing an operation in a circuit diagram.
Calls _circuit_diagram_info_ on `val`. If `val` doesn't have
_circuit_diagram_info_, or it returns NotImplemented, that indicates that
diagram information is not available.
Args:
val: The operation or gate that will need to be drawn.
args: A CircuitDiagramInfoArgs describing the desired drawing style.
default: A default result to return if the value doesn't have circuit
diagram information. If not specified, a TypeError is raised
instead.
Returns:
If `val` has no _circuit_diagram_info_ method or it returns
NotImplemented, then `default` is returned (or a TypeError is
raised if no `default` is specified).
Otherwise, the value returned by _circuit_diagram_info_ is returned.
Raises:
TypeError:
`val` doesn't have circuit diagram information and `default` was
not specified.
"""
# Attempt.
if args is None:
args = CircuitDiagramInfoArgs.UNINFORMED_DEFAULT
getter = getattr(val, '_circuit_diagram_info_', None)
result = NotImplemented if getter is None else getter(args)
# Success?
if isinstance(result, str):
return CircuitDiagramInfo(wire_symbols=(result,))
if isinstance(result, collections.Iterable):
return CircuitDiagramInfo(wire_symbols=tuple(result))
if result is not NotImplemented:
return result
# Failure.
if default is not RaiseTypeErrorIfNotProvided:
return default
if getter is None:
raise TypeError(
"object of type '{}' "
"has no _circuit_diagram_info_ method.".format(type(val)))
raise TypeError("object of type '{}' does have a _circuit_diagram_info_ "
"method, but it returned NotImplemented.".format(type(val)))
# pylint: enable=function-redefined