File size: 3,763 Bytes
2a87ede
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"""

Plain-text circuit diagrams -- the fast way to sanity-check what a

gate-tuple list actually builds without running it, the same job

Qiskit's `circuit.draw()` or Cirq's text diagrams do.



Uses plain ASCII ('-', '|', '*') rather than Unicode box-drawing

characters deliberately: a diagram meant to be printed to a terminal or

written to a log file needs to survive whatever console encoding the

caller happens to have (this was tested against, and would otherwise

crash on, a default Windows cp1252 console).

"""

__all__ = ['draw_circuit']

_LABELS = {
    'h': 'H', 'x': 'X', 'y': 'Y', 'z': 'Z', 's': 'S', 'sdg': 'Sdg',
    't': 'T', 'tdg': 'Tdg', 'sx': 'Sx', 'id': 'I',
    'rx': 'Rx', 'ry': 'Ry', 'rz': 'Rz', 'p': 'P', 'u1': 'P', 'phase': 'P',
}
_TWO_QUBIT_TARGET_SYMBOL = {'cx': 'X', 'cz': 'Z', 'cy': 'Y', 'cp': 'P', 'crz': 'Rz'}


def draw_circuit(circuit, n_qubits):
    """

    Render a circuit (in the gate-tuple form run_circuit accepts) as a

    plain-text diagram, one gate per column, one line per qubit.



    Parameters

    ----------

    circuit : list[tuple]

        Gate ops, as passed to `DenseSVSimulator.run_circuit`. Supports

        every gate this package's transpiler recognizes: single-qubit

        static and parametric gates, cx/cz/cy/cp/crz, swap, and ccx.

    n_qubits : int

        Number of qubit rows to draw, must be >= 1 (should cover every

        qubit index referenced in `circuit`).



    Returns

    -------

    str

        Multi-line diagram, one row per qubit (``q0: --H----*--``, ...).

        Print it, or split on '\\n' to inspect individual rows.



    Examples

    --------

    >>> import dense_evolution as de

    >>> print(de.draw_circuit([('h', 0), ('cx', 0, 1)], n_qubits=2))

    q0: --H----*--

    q1: -------X--

    """
    if n_qubits < 1:
        raise ValueError(f"draw_circuit needs at least 1 qubit, got {n_qubits}")

    prefixes = [f"q{q}: " for q in range(n_qubits)]
    prefix_width = max(len(p) for p in prefixes)
    rows = [p.ljust(prefix_width) for p in prefixes]

    for op in circuit:
        name = op[0]
        col = {}  # qubit -> symbol to draw in this column (None = plain wire)

        if name == 'swap':
            a, b = op[1], op[2]
            lo, hi = min(a, b), max(a, b)
            for q in range(n_qubits):
                col[q] = 'x' if q in (a, b) else ('|' if lo < q < hi else None)
        elif name == 'ccx':
            c1, c2, tgt = op[1], op[2], op[3]
            lo, hi = min(c1, c2, tgt), max(c1, c2, tgt)
            for q in range(n_qubits):
                if q in (c1, c2):
                    col[q] = '*'
                elif q == tgt:
                    col[q] = 'X'
                else:
                    col[q] = '|' if lo < q < hi else None
        elif name in _TWO_QUBIT_TARGET_SYMBOL:
            ctrl, tgt = op[1], op[2]
            lo, hi = min(ctrl, tgt), max(ctrl, tgt)
            for q in range(n_qubits):
                if q == ctrl:
                    col[q] = '*'
                elif q == tgt:
                    col[q] = _TWO_QUBIT_TARGET_SYMBOL[name]
                else:
                    col[q] = '|' if lo < q < hi else None
        else:
            q = op[1]
            col[q] = _LABELS.get(name, name.upper())

        label_width = max((len(v) for v in col.values() if v is not None), default=1)
        seg_width = label_width + 4  # 2 filler chars on each side

        for q in range(n_qubits):
            content = col.get(q)
            if content is None:
                rows[q] += '-' * seg_width
            else:
                rows[q] += '--' + content.center(label_width) + '--'

    return '\n'.join(rows)