""" 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, }