Tatopenn's picture
Sync mirror with GitHub main (v8.1.62): subpackage refactor, mitigation promotions, MCP server refactor
2a87ede verified
Raw History Blame Contribute Delete
3.76 kB
"""
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)