--- name: mq-circuit-compiler description: "Compile and optimize quantum circuits for hardware execution using MindQuantum's compiler pipeline. Covers gate decomposition into native gate sets, DAG-based circuit optimization, SABRE qubit mapping for hardware topologies (grid, linear, custom), and circuit equivalence checking. Use when the user needs to compile a circuit for a specific quantum processor, map logical qubits to physical qubits, decompose gates, optimize circuit depth, define hardware topology, or check circuit equivalence." --- # Circuit Compilation and Hardware Mapping MindQuantum provides compiler and mapping APIs for gate decomposition, DAG-based circuit processing, and topology-aware qubit mapping. ## Compilation Pipeline Overview ```text Logical Circuit → Gate Decomposition → DAG Optimization → Qubit Mapping → Physical Circuit (to native gate set) (simplify) (SABRE) (with SWAPs) ``` ## Hardware Topology Define the qubit connectivity of your target device: ```python from mindquantum.device import QubitsTopology, GridQubits, LinearQubits, QubitNode # Predefined topologies linear = LinearQubits(5) # 0-1-2-3-4 chain grid = GridQubits(3, 3) # 3×3 grid (9 qubits) # Custom topology topo = QubitsTopology([QubitNode(i) for i in range(5)]) topo[0] >> topo[1] # Connect qubit 0 ↔ 1 topo[1] >> topo[2] # Connect qubit 1 ↔ 2 topo[2] >> topo[3] topo[3] >> topo[4] topo[0] >> topo[3] # Add diagonal connection # Inspect print(topo.edges_with_id()) # List of (qubit_a, qubit_b) pairs print(topo.all_qubit_id()) # List of qubit IDs ``` ### Modifying Topologies ```python # Remove a qubit (e.g., defective qubit on hardware) topo.remove_qubit_node(2) # Isolate a qubit (break all its connections) topo.isolate_with_near(3) ``` ### Visualizing Topologies ```python from mindquantum.io.display import draw_topology draw_topology(grid) # Show topology graph draw_topology(grid, compiled_circuit) # Highlight used edges ``` ## SABRE Qubit Mapping The SABRE algorithm maps logical qubits to physical qubits and inserts SWAP gates to satisfy connectivity constraints. ```python from mindquantum.core.circuit import Circuit from mindquantum.core.gates import H, RX, X from mindquantum.device import GridQubits from mindquantum.algorithm.mapping import SABRE # 1. Define logical circuit (may have non-local gates) circ = Circuit() circ += H.on(0) circ += X.on(2, 0) # CNOT: target 2, control 0; may not be connected circ += RX("a").on(1) circ += X.on(3, 1) circ += X.on(0, 3) # qubits 0 and 3 may not be connected # 2. Define hardware topology topo = GridQubits(2, 2) # Grid: 0 - 1 # | | # 2 - 3 # 3. Run SABRE solver = SABRE(circ, topo) new_circ, init_mapping, final_mapping = solver.solve( iter_num=5, # SABRE iterations w=0.5, # Weight for lookahead heuristic delta1=0.3, # Decay parameter for single-qubit gates delta2=0.2, # Decay parameter for two-qubit gates ) # 4. Results print(f"Original gates: {len(circ)}") print(f"Compiled gates: {len(new_circ)}") # includes inserted SWAPs print(f"Initial mapping: {init_mapping}") # logical → physical print(f"Final mapping: {final_mapping}") # 5. View compiled circuit new_circ.svg() ``` ### SABRE Parameters | Parameter | Type | Description | |-----------|------|-------------| | `iter_num` | int | Number of SABRE iterations. Increasing it runs more search iterations. Default 5. | | `w` | float | Weight for front-layer vs lookahead cost. Range [0, 1]. | | `delta1` | float | Decay parameter for single-qubit gates. | | `delta2` | float | Decay parameter for two-qubit gates. | ## Gate Decomposition Decompose complex gates into a native gate set: ```python from mindquantum.algorithm.compiler import decompose ``` ### Decomposition Rules The compiler package provides decomposition and rewrite rules, including: - Multi-controlled gates → cascaded Toffoli → CX + single-qubit - Arbitrary unitary → U3 + CX decomposition - Named gates (SWAP, Toffoli, etc.) → native primitives ### Using the DAG Representation The compiler converts circuits to Directed Acyclic Graphs for optimization: ```python from mindquantum.algorithm.compiler import DAGCircuit # Convert circuit to DAG dag = DAGCircuit(circ) # DAGCircuit exposes circuit dependency structure for compiler rules and inspection. ``` ## Circuit Equivalence Checking Verify that compilation preserved circuit semantics: ### Numerical Verification ```python import numpy as np from mindquantum.core.circuit import Circuit, dagger # Method 1: Matrix comparison (small circuits) original = Circuit().h(0).x(1, 0).rx("a", 0) compiled = Circuit().h(0).x(1, 0).rx("a", 0) # Replace with your compiled circuit # For fixed parameters params = {"a": 0.5} m1 = original.matrix(params) m2 = compiled.matrix(params) assert np.allclose(m1, m2), "Circuits are not equivalent!" # Method 2: Identity check # If A† · B = I, then A ≡ B check = dagger(original) + compiled m_check = check.matrix(params) assert np.allclose(m_check, np.eye(m_check.shape[0])), "Not equivalent!" ``` ### Random Parameter Verification For parameterized circuits, test with multiple random parameter sets: ```python param_names = original.params_name for _ in range(10): pr = {name: np.random.uniform(-np.pi, np.pi) for name in param_names} m1 = original.matrix(pr) m2 = compiled.matrix(pr) assert np.allclose(m1, m2, atol=1e-10), f"Mismatch at params={pr}" ``` ## Complete Compilation Workflow ```python from mindquantum.core.circuit import Circuit from mindquantum.core.gates import H, RY, RZ, X from mindquantum.device import GridQubits from mindquantum.algorithm.mapping import SABRE from mindquantum.io.display import draw_topology # 1. Build your algorithm circuit n_qubits = 6 circ = Circuit() for i in range(n_qubits): circ += H.on(i) for i in range(n_qubits - 1): circ += X.on(i + 1, i) for i in range(n_qubits): circ += RY(f"theta_{i}").on(i) # Long-range gate (not nearest-neighbor) circ += X.on(5, 0) # 2. Define target hardware topology topo = GridQubits(2, 3) # 2×3 grid for 6 qubits # 3. Map to hardware solver = SABRE(circ, topo) compiled, init_map, final_map = solver.solve(10, 0.5, 0.3, 0.2) # 4. Report print(f"SWAPs inserted: {len(compiled) - len(circ)}") print(f"Logical → Physical mapping: {init_map}") # 5. Visualize draw_topology(topo, compiled) compiled.svg() ``` ## Notes 1. **SABRE iterations:** `solver.solve()` exposes `iter_num`; increasing it runs more SABRE search iterations and increases compile time. 2. **Topology input:** `SABRE` requires a connected `QubitsTopology`; disconnected topologies raise `ValueError`. 3. **Inserted SWAPs:** The returned circuit may contain SWAP gates inserted by the mapper to satisfy topology constraints. 4. **Equivalence checks:** For small circuits, compare matrices at fixed parameter values to validate a compilation workflow.