Deminiko
Initial commit: QuantumArchitect-MCP quantum circuit MCP server with Gradio UI
6ce350d
"""
Circuit Visualizers - Generate ASCII art and diagrams of quantum circuits.
"""
from typing import Any
def visualize_circuit_ascii(circuit_data: dict[str, Any]) -> str:
"""
Generate ASCII art representation of a quantum circuit.
Args:
circuit_data: Circuit dictionary with gates and qubits
Returns:
ASCII art string
"""
num_qubits = circuit_data.get("num_qubits", 0)
gates = circuit_data.get("gates", [])
if num_qubits == 0:
return "Empty circuit"
# Track wire positions for each qubit
wires = [f"q[{i}]: " for i in range(num_qubits)]
max_prefix = max(len(w) for w in wires)
wires = [w.ljust(max_prefix) + "─" for w in wires]
for gate in gates:
name = gate.get("name", "?").upper()
qubits = gate.get("qubits", [])
params = gate.get("params", [])
if not qubits:
continue
# Format gate name
if params:
param_str = ",".join(f"{p:.2f}" if isinstance(p, float) else str(p) for p in params[:2])
gate_str = f"{name}({param_str})"
else:
gate_str = name
if len(qubits) == 1:
# Single qubit gate
q = qubits[0]
gate_box = f"┤{gate_str}├"
wires[q] += gate_box + "─"
# Pad other wires
for i in range(num_qubits):
if i != q:
wires[i] += "─" * (len(gate_box) + 1)
elif len(qubits) == 2:
# Two qubit gate (control-target style)
q0, q1 = qubits[0], qubits[1]
min_q, max_q = min(q0, q1), max(q0, q1)
if name in ("CX", "CNOT"):
# CNOT visualization
control_q = q0
target_q = q1
for i in range(num_qubits):
if i == control_q:
wires[i] += "●──"
elif i == target_q:
wires[i] += "⊕──"
elif min_q < i < max_q:
wires[i] += "│──"
else:
wires[i] += "───"
elif name in ("CZ",):
for i in range(num_qubits):
if i == q0:
wires[i] += "●──"
elif i == q1:
wires[i] += "●──"
elif min_q < i < max_q:
wires[i] += "│──"
else:
wires[i] += "───"
elif name == "SWAP":
for i in range(num_qubits):
if i == q0 or i == q1:
wires[i] += "×──"
elif min_q < i < max_q:
wires[i] += "│──"
else:
wires[i] += "───"
else:
# Generic two-qubit gate
gate_width = len(gate_str) + 2
for i in range(num_qubits):
if i == q0:
wires[i] += "●" + "─" * (gate_width - 1)
elif i == q1:
wires[i] += f"┤{gate_str}├"
elif min_q < i < max_q:
wires[i] += "│" + "─" * (gate_width - 1)
else:
wires[i] += "─" * gate_width
elif len(qubits) == 3:
# Three qubit gate (Toffoli, Fredkin)
min_q = min(qubits)
max_q = max(qubits)
for i in range(num_qubits):
if i in qubits:
if i == qubits[-1]:
if name in ("CCX", "TOFFOLI"):
wires[i] += "⊕──"
else:
wires[i] += f"┤{name}├"
else:
wires[i] += "●──"
elif min_q < i < max_q:
wires[i] += "│──"
else:
wires[i] += "───"
# Add final lines
for i in range(num_qubits):
wires[i] += "─"
return "\n".join(wires)
def circuit_to_latex(circuit_data: dict[str, Any]) -> str:
"""
Generate LaTeX/Qcircuit representation of a quantum circuit.
Args:
circuit_data: Circuit dictionary
Returns:
LaTeX code for Qcircuit
"""
num_qubits = circuit_data.get("num_qubits", 0)
gates = circuit_data.get("gates", [])
latex = r"\begin{quantikz}" + "\n"
# Build column-by-column
columns: list[list[str]] = []
current_col: list[str] = [r"\ket{0}"] * num_qubits
columns.append(current_col.copy())
for gate in gates:
name = gate.get("name", "").upper()
qubits = gate.get("qubits", [])
col: list[str] = [r"\qw"] * num_qubits
if len(qubits) == 1:
q = qubits[0]
if name == "H":
col[q] = r"\gate{H}"
elif name == "X":
col[q] = r"\gate{X}"
elif name == "Y":
col[q] = r"\gate{Y}"
elif name == "Z":
col[q] = r"\gate{Z}"
elif name in ("RX", "RY", "RZ"):
col[q] = rf"\gate{{{name}}}"
else:
col[q] = rf"\gate{{{name}}}"
elif len(qubits) == 2:
q0, q1 = qubits
diff = q1 - q0
if name in ("CX", "CNOT"):
col[q0] = rf"\ctrl{{{diff}}}"
col[q1] = r"\targ{}"
elif name == "CZ":
col[q0] = rf"\ctrl{{{diff}}}"
col[q1] = r"\gate{Z}"
elif name == "SWAP":
col[q0] = rf"\swap{{{diff}}}"
col[q1] = r"\targX{}"
columns.append(col)
# Build rows
for q in range(num_qubits):
row = " & ".join(col[q] for col in columns)
latex += row + r" \\" + "\n"
latex += r"\end{quantikz}"
return latex
def circuit_summary(circuit_data: dict[str, Any]) -> dict[str, Any]:
"""
Generate a summary of circuit properties.
Args:
circuit_data: Circuit dictionary
Returns:
Summary dictionary
"""
gates = circuit_data.get("gates", [])
num_qubits = circuit_data.get("num_qubits", 0)
gate_counts: dict[str, int] = {}
single_qubit_gates = 0
two_qubit_gates = 0
multi_qubit_gates = 0
parameterized_gates = 0
for gate in gates:
name = gate.get("name", "unknown").lower()
qubits = gate.get("qubits", [])
params = gate.get("params", [])
if name == "barrier":
continue
gate_counts[name] = gate_counts.get(name, 0) + 1
if len(qubits) == 1:
single_qubit_gates += 1
elif len(qubits) == 2:
two_qubit_gates += 1
else:
multi_qubit_gates += 1
if params:
parameterized_gates += 1
# Estimate depth (simplified)
qubit_depths = [0] * num_qubits
for gate in gates:
qubits = gate.get("qubits", [])
if gate.get("name", "").lower() == "barrier":
continue
max_depth = max((qubit_depths[q] for q in qubits), default=0)
for q in qubits:
qubit_depths[q] = max_depth + 1
depth = max(qubit_depths) if qubit_depths else 0
return {
"num_qubits": num_qubits,
"num_classical_bits": circuit_data.get("num_classical_bits", 0),
"depth": depth,
"total_gates": len([g for g in gates if g.get("name", "").lower() != "barrier"]),
"single_qubit_gates": single_qubit_gates,
"two_qubit_gates": two_qubit_gates,
"multi_qubit_gates": multi_qubit_gates,
"parameterized_gates": parameterized_gates,
"gate_breakdown": gate_counts,
}