def _draw_moment_in_diagram(moment: Moment, ext: Extensions, use_unicode_characters: bool, qubit_map: Dict[QubitId, int], out_diagram: TextDiagramDrawer, precision: Optional[int]): if not moment.operations: return [] x0 = out_diagram.width() for op in moment.operations: indices = [qubit_map[q] for q in op.qubits] y1 = min(indices) y2 = max(indices) # Find an available column. x = x0 while any( out_diagram.content_present(x, y) for y in range(y1, y2 + 1)): x += 1 # Draw vertical line linking the gate's qubits. if y2 > y1: out_diagram.vertical_line(x, y1, y2) # Print gate qubit labels. symbols = _get_operation_text_diagram_symbols(op, ext, use_unicode_characters, precision) for s, q in zip(symbols, op.qubits): out_diagram.write(x, qubit_map[q], s) # Add an exponent to the first label. exponent = _get_operation_text_diagram_exponent(op, ext, precision) if exponent is not None: out_diagram.write(x, y1, '^' + exponent)
def to_text_diagram_drawer( self, ext: Extensions = None, use_unicode_characters: bool = True, qubit_name_suffix: str = '', precision: Optional[int] = 3, qubit_order: ops.QubitOrderOrList = ops.QubitOrder.DEFAULT, ) -> TextDiagramDrawer: """Returns a TextDiagramDrawer with the circuit drawn into it. Args: ext: For extending gates to implement TextDiagrammableGate. use_unicode_characters: Determines if unicode characters are allowed (as opposed to ascii-only diagrams). qubit_name_suffix: Appended to qubit names in the diagram. precision: Number of digits to use when representing numbers. qubit_order: Determines how qubits are ordered in the diagram. Returns: The TextDiagramDrawer instance. """ if ext is None: ext = Extensions() qubits = ops.QubitOrder.as_qubit_order(qubit_order).order_for( self.qubits()) qubit_map = {qubits[i]: i for i in range(len(qubits))} diagram = TextDiagramDrawer() for q, i in qubit_map.items(): diagram.write(0, i, str(q) + qubit_name_suffix) for moment in [Moment()] * 2 + self.moments + [Moment()]: _draw_moment_in_diagram(moment, ext, use_unicode_characters, qubit_map, diagram, precision) w = diagram.width() for i in qubit_map.values(): diagram.horizontal_line(i, 0, w) return diagram
def to_text_diagram( self: 'cirq.Moment', *, xy_breakdown_func: Callable[['cirq.Qid'], Tuple[Any, Any]] = _default_breakdown, extra_qubits: Iterable['cirq.Qid'] = (), use_unicode_characters: bool = True, precision: Optional[int] = None, include_tags: bool = True, ): """ Args: xy_breakdown_func: A function to split qubits/qudits into x and y components. For example, the default breakdown turns `cirq.GridQubit(row, col)` into the tuple `(col, row)` and `cirq.LineQubit(x)` into `(x, 0)`. extra_qubits: Extra qubits/qudits to include in the diagram, even if they don't have any operations applied in the moment. use_unicode_characters: Whether or not the output should use fancy unicode characters or stick to plain ASCII. Unicode characters look nicer, but some environments don't draw them with the same width as ascii characters (which ruins the diagrams). precision: How precise numbers, such as angles, should be. Use None for infinite precision, or an integer for a certain number of digits of precision. include_tags: Whether or not to include operation tags in the diagram. Returns: The text diagram rendered into text. """ # Figure out where to place everything. qs = set(self.qubits) | set(extra_qubits) points = {xy_breakdown_func(q) for q in qs} x_keys = sorted({pt[0] for pt in points}, key=_SortByValFallbackToType) y_keys = sorted({pt[1] for pt in points}, key=_SortByValFallbackToType) x_map = {x_key: x + 2 for x, x_key in enumerate(x_keys)} y_map = {y_key: y + 2 for y, y_key in enumerate(y_keys)} qubit_positions = {} for q in qs: a, b = xy_breakdown_func(q) qubit_positions[q] = x_map[a], y_map[b] from cirq.circuits.text_diagram_drawer import TextDiagramDrawer diagram = TextDiagramDrawer() def cleanup_key(key: Any) -> Any: if isinstance(key, float) and key == int(key): return str(int(key)) return str(key) # Add table headers. for key, x in x_map.items(): diagram.write(x, 0, cleanup_key(key)) for key, y in y_map.items(): diagram.write(0, y, cleanup_key(key)) diagram.horizontal_line(1, 0, len(x_map) + 2) diagram.vertical_line(1, 0, len(y_map) + 2) diagram.force_vertical_padding_after(0, 0) diagram.force_vertical_padding_after(1, 0) # Add operations. for op in self.operations: args = protocols.CircuitDiagramInfoArgs( known_qubits=op.qubits, known_qubit_count=len(op.qubits), use_unicode_characters=use_unicode_characters, qubit_map=None, precision=precision, include_tags=include_tags, ) info = protocols.CircuitDiagramInfo._op_info_with_fallback( op, args=args) symbols = info._wire_symbols_including_formatted_exponent(args) for label, q in zip(symbols, op.qubits): x, y = qubit_positions[q] diagram.write(x, y, label) if info.connected: for q1, q2 in zip(op.qubits, op.qubits[1:]): # Sort to get a more consistent orientation for diagonals. # This reduces how often lines overlap in the diagram. q1, q2 = sorted([q1, q2]) x1, y1 = qubit_positions[q1] x2, y2 = qubit_positions[q2] if x1 != x2: diagram.horizontal_line(y1, x1, x2) if y1 != y2: diagram.vertical_line(x2, y1, y2) return diagram.render()