Source code for quantum_debugger.qml.ansatz.two_local

"""
TwoLocal Ansatz

A highly customizable ansatz with alternating rotation and entanglement layers.
Users can specify which gates to use in each layer.
"""

import numpy as np
from typing import Callable, List, Union
from ...core.circuit import QuantumCircuit


[docs] def two_local( num_qubits: int, rotation_blocks: Union[str, List[str]] = "ry", entanglement_blocks: str = "cnot", entanglement: str = "linear", reps: int = 1, ) -> Callable: """ Create a highly customizable TwoLocal ansatz. This ansatz alternates between rotation layers (single-qubit gates) and entanglement layers (two-qubit gates), allowing full customization. Args: num_qubits: Number of qubits rotation_blocks: Single-qubit gates to use. Can be: - Single gate: 'ry', 'rz', 'rx' - List of gates: ['ry', 'rz'] (alternates between them) entanglement_blocks: Two-qubit gate to use: - 'cnot': CNOT gates - 'cz': CZ gates - 'swap': SWAP gates entanglement: Connection pattern: - 'linear': Chain 0-1, 1-2, 2-3, ... - 'full': All-to-all - 'circular': Ring with wrap-around reps: Number of repetitions Returns: Function that builds circuit with given parameters Example: >>> # Simple RY + CNOT ansatz >>> ansatz = two_local(3, rotation_blocks='ry', reps=2) >>> >>> # Mixed rotations >>> ansatz = two_local(3, rotation_blocks=['ry', 'rz'], reps=2) """ # Normalize rotation_blocks to list if isinstance(rotation_blocks, str): rotation_blocks = [rotation_blocks] def build_circuit(params: np.ndarray) -> QuantumCircuit: """Build the TwoLocal circuit""" circuit = QuantumCircuit(num_qubits) param_idx = 0 for rep in range(reps + 1): # Rotation layer # Determine which rotation gates to use in this layer block_idx = rep % len(rotation_blocks) gate_type = rotation_blocks[block_idx] for qubit in range(num_qubits): if param_idx < len(params): angle = params[param_idx] param_idx += 1 # Apply the rotation gate if gate_type == "ry": circuit.ry(angle, qubit) elif gate_type == "rz": circuit.rz(angle, qubit) elif gate_type == "rx": circuit.rx(angle, qubit) else: raise ValueError(f"Unknown rotation gate: {gate_type}") # Entanglement layer (skip after last rotation) if rep < reps: # Get qubit pairs based on entanglement pattern pairs = _get_entanglement_pairs(num_qubits, entanglement) for control, target in pairs: if entanglement_blocks == "cnot": circuit.cnot(control, target) elif entanglement_blocks == "cz": circuit.cz(control, target) elif entanglement_blocks == "swap": circuit.swap(control, target) else: raise ValueError( f"Unknown entanglement gate: {entanglement_blocks}" ) return circuit # Calculate required parameters params_needed = (reps + 1) * num_qubits build_circuit.num_parameters = params_needed build_circuit.num_qubits = num_qubits build_circuit.reps = reps build_circuit.rotation_blocks = rotation_blocks build_circuit.entanglement_blocks = entanglement_blocks build_circuit.entanglement = entanglement return build_circuit
def _get_entanglement_pairs(num_qubits: int, pattern: str) -> List[tuple]: """ Get list of qubit pairs for entanglement. Args: num_qubits: Number of qubits pattern: Entanglement pattern Returns: List of (control, target) pairs """ if pattern == "linear": return [(i, i + 1) for i in range(num_qubits - 1)] elif pattern == "full": return [(i, j) for i in range(num_qubits) for j in range(i + 1, num_qubits)] elif pattern == "circular": return [(i, (i + 1) % num_qubits) for i in range(num_qubits)] else: raise ValueError(f"Unknown entanglement pattern: {pattern}") def count_gates(num_qubits: int, reps: int, entanglement: str) -> dict: """ Count the number of gates in a TwoLocal circuit. Args: num_qubits: Number of qubits reps: Number of repetitions entanglement: Entanglement pattern Returns: Dictionary with gate counts """ rotation_gates = (reps + 1) * num_qubits if entanglement == "linear": entanglement_gates = reps * (num_qubits - 1) elif entanglement == "full": entanglement_gates = reps * (num_qubits * (num_qubits - 1)) // 2 elif entanglement == "circular": entanglement_gates = reps * num_qubits else: entanglement_gates = 0 return { "rotation_gates": rotation_gates, "entanglement_gates": entanglement_gates, "total_gates": rotation_gates + entanglement_gates, "parameters": rotation_gates, }