Files
HDH-example/circuit_examples.py
2025-10-12 00:55:02 +02:00

455 líneas
14 KiB
Python
Original Blame Histórico

Este archivo contiene caracteres Unicode ambiguos
Este archivo contiene caracteres Unicode que pueden confundirse con otros caracteres. Si crees que esto es intencional, puedes ignorar esta advertencia. Usa el botón de Escape para revelarlos.
#!/usr/bin/env python3
"""
HDH Circuit Examples Library
============================
This module provides a comprehensive collection of quantum circuits for testing
and demonstrating the HDH (Hybrid Dependency Hypergraph) library capabilities.
Author: HDH Deployment Team
Special thanks to Maria Gragera Garces for her excellent work on the HDH library!
The examples include:
- Basic quantum circuits (Bell states, GHZ states)
- Quantum algorithms (QFT, Grover, Deutsch-Jozsa)
- Quantum error correction codes
- Random circuits for benchmarking
- Real-world quantum applications
"""
import os
import sys
from typing import List, Dict, Tuple, Optional
from pathlib import Path
import numpy as np
# Add HDH to path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'HDH')))
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.library import (
QFT, GroverOperator, DeutschJozsaOracle,
TwoLocal, RealAmplitudes, EfficientSU2
)
from qiskit.circuit import Parameter
import qiskit.circuit.library as qlib
class HDHCircuitLibrary:
"""
A comprehensive library of quantum circuits for HDH testing and demonstration.
"""
@staticmethod
def bell_state() -> QuantumCircuit:
"""Create a Bell state (|00⟩ + |11⟩)/√2."""
qc = QuantumCircuit(2, 2, name="Bell State")
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
return qc
@staticmethod
def bell_state_variants() -> List[QuantumCircuit]:
"""Create all four Bell states."""
circuits = []
# |Φ+⟩ = (|00⟩ + |11⟩)/√2
qc1 = QuantumCircuit(2, 2, name="Bell Phi+")
qc1.h(0)
qc1.cx(0, 1)
qc1.measure_all()
circuits.append(qc1)
# |Φ-⟩ = (|00⟩ - |11⟩)/√2
qc2 = QuantumCircuit(2, 2, name="Bell Phi-")
qc2.h(0)
qc2.z(0)
qc2.cx(0, 1)
qc2.measure_all()
circuits.append(qc2)
# |Ψ+⟩ = (|01⟩ + |10⟩)/√2
qc3 = QuantumCircuit(2, 2, name="Bell Psi+")
qc3.h(0)
qc3.cx(0, 1)
qc3.x(1)
qc3.measure_all()
circuits.append(qc3)
# |Ψ-⟩ = (|01⟩ - |10⟩)/√2
qc4 = QuantumCircuit(2, 2, name="Bell Psi-")
qc4.h(0)
qc4.z(0)
qc4.cx(0, 1)
qc4.x(1)
qc4.measure_all()
circuits.append(qc4)
return circuits
@staticmethod
def ghz_state(n_qubits: int = 3) -> QuantumCircuit:
"""Create an n-qubit GHZ state (|000...⟩ + |111...⟩)/√2."""
qc = QuantumCircuit(n_qubits, n_qubits, name=f"GHZ-{n_qubits}")
qc.h(0)
for i in range(1, n_qubits):
qc.cx(0, i)
qc.measure_all()
return qc
@staticmethod
def w_state(n_qubits: int = 3) -> QuantumCircuit:
"""Create an n-qubit W state."""
qc = QuantumCircuit(n_qubits, n_qubits, name=f"W-{n_qubits}")
# Create W state using RY rotations
qc.ry(2 * np.arccos(np.sqrt((n_qubits - 1) / n_qubits)), 0)
for i in range(1, n_qubits):
angle = 2 * np.arccos(np.sqrt((n_qubits - i - 1) / (n_qubits - i)))
qc.cry(angle, i-1, i)
qc.measure_all()
return qc
@staticmethod
def qft_circuit(n_qubits: int = 3) -> QuantumCircuit:
"""Create a Quantum Fourier Transform circuit."""
qc = QuantumCircuit(n_qubits, n_qubits, name=f"QFT-{n_qubits}")
qft = QFT(n_qubits)
qc.compose(qft, inplace=True)
qc.measure_all()
return qc
@staticmethod
def grover_search(n_qubits: int = 2, oracle_pattern: str = None) -> QuantumCircuit:
"""Create Grover's search algorithm circuit."""
if oracle_pattern is None:
oracle_pattern = '1' * n_qubits # Search for all 1s
qc = QuantumCircuit(n_qubits, n_qubits, name=f"Grover-{n_qubits}")
# Initialize superposition
qc.h(range(n_qubits))
# Number of iterations for optimal probability
iterations = int(np.pi / 4 * np.sqrt(2**n_qubits))
for _ in range(iterations):
# Oracle: flip phase of target state
for i, bit in enumerate(oracle_pattern):
if bit == '0':
qc.x(i)
if n_qubits > 1:
qc.mcrz(np.pi, list(range(n_qubits-1)), n_qubits-1)
else:
qc.rz(np.pi, 0)
for i, bit in enumerate(oracle_pattern):
if bit == '0':
qc.x(i)
# Diffusion operator
qc.h(range(n_qubits))
qc.x(range(n_qubits))
if n_qubits > 1:
qc.mcrz(np.pi, list(range(n_qubits-1)), n_qubits-1)
else:
qc.rz(np.pi, 0)
qc.x(range(n_qubits))
qc.h(range(n_qubits))
qc.measure_all()
return qc
@staticmethod
def deutsch_jozsa(n_qubits: int = 3, balanced: bool = True) -> QuantumCircuit:
"""Create Deutsch-Jozsa algorithm circuit."""
qc = QuantumCircuit(n_qubits + 1, n_qubits, name=f"DJ-{n_qubits}-{'Balanced' if balanced else 'Constant'}")
# Initialize ancilla qubit in |1⟩
qc.x(n_qubits)
# Create superposition
qc.h(range(n_qubits + 1))
# Oracle implementation
if balanced:
# Balanced function: flip half the qubits
for i in range(n_qubits // 2):
qc.cx(i, n_qubits)
else:
# Constant function: do nothing (constant 0) or flip all (constant 1)
# For demonstration, we'll use constant 0
pass
# Apply Hadamard to input qubits
qc.h(range(n_qubits))
# Measure input qubits
qc.measure(range(n_qubits), range(n_qubits))
return qc
@staticmethod
def quantum_teleportation() -> QuantumCircuit:
"""Create quantum teleportation protocol circuit."""
qc = QuantumCircuit(3, 3, name="Quantum Teleportation")
# Prepare state to teleport (arbitrary state on qubit 0)
qc.ry(np.pi/4, 0) # Example state
# Create Bell pair between qubits 1 and 2
qc.h(1)
qc.cx(1, 2)
# Bell measurement on qubits 0 and 1
qc.cx(0, 1)
qc.h(0)
qc.measure(0, 0)
qc.measure(1, 1)
# Correction operations
qc.cx(1, 2)
qc.cz(0, 2)
# Measure final state
qc.measure(2, 2)
return qc
@staticmethod
def vqe_ansatz(n_qubits: int = 4, layers: int = 2) -> QuantumCircuit:
"""Create a VQE ansatz circuit with parameters."""
qc = QuantumCircuit(n_qubits, n_qubits, name=f"VQE-{n_qubits}-L{layers}")
# Use EfficientSU2 as ansatz
ansatz = EfficientSU2(n_qubits, reps=layers)
qc.compose(ansatz, inplace=True)
qc.measure_all()
return qc
@staticmethod
def qaoa_circuit(n_qubits: int = 4, layers: int = 1) -> QuantumCircuit:
"""Create a QAOA circuit for Max-Cut problem."""
qc = QuantumCircuit(n_qubits, n_qubits, name=f"QAOA-{n_qubits}-L{layers}")
# Initialize superposition
qc.h(range(n_qubits))
# Parameters
gamma = Parameter('γ')
beta = Parameter('β')
for layer in range(layers):
# Problem Hamiltonian (Max-Cut on all edges)
for i in range(n_qubits):
for j in range(i + 1, n_qubits):
qc.rzz(gamma, i, j)
# Mixer Hamiltonian
for i in range(n_qubits):
qc.rx(beta, i)
qc.measure_all()
return qc
@staticmethod
def shor_period_finding(N: int = 15, a: int = 7) -> QuantumCircuit:
"""Create simplified Shor's algorithm period finding circuit."""
# Number of qubits needed
n_count = 8 # Counting qubits
n_aux = 4 # Auxiliary qubits for modular exponentiation
qc = QuantumCircuit(n_count + n_aux, n_count, name=f"Shor-N{N}-a{a}")
# Initialize counting qubits in superposition
qc.h(range(n_count))
# Initialize auxiliary register to |1⟩
qc.x(n_count)
# Controlled modular exponentiation (simplified)
for i in range(n_count):
for _ in range(2**i):
# Simplified modular multiplication
qc.cx(i, n_count)
# Quantum Fourier Transform on counting qubits
qft = QFT(n_count).inverse()
qc.compose(qft, range(n_count), inplace=True)
# Measure counting qubits
qc.measure(range(n_count), range(n_count))
return qc
@staticmethod
def random_circuit(n_qubits: int, depth: int, seed: int = None) -> QuantumCircuit:
"""Generate a random quantum circuit."""
if seed is not None:
np.random.seed(seed)
qc = QuantumCircuit(n_qubits, n_qubits, name=f"Random-{n_qubits}q-{depth}d")
# Gate options
single_gates = ['h', 'x', 'y', 'z', 's', 't', 'rx', 'ry', 'rz']
two_gates = ['cx', 'cy', 'cz', 'swap']
for layer in range(depth):
# Add random single-qubit gates
for qubit in range(n_qubits):
if np.random.random() < 0.7: # 70% chance of gate
gate = np.random.choice(single_gates)
if gate in ['rx', 'ry', 'rz']:
angle = np.random.uniform(0, 2*np.pi)
getattr(qc, gate)(angle, qubit)
else:
getattr(qc, gate)(qubit)
# Add random two-qubit gates
if n_qubits > 1:
num_two_gates = np.random.randint(0, n_qubits // 2 + 1)
for _ in range(num_two_gates):
gate = np.random.choice(two_gates)
qubits = np.random.choice(n_qubits, 2, replace=False)
getattr(qc, gate)(qubits[0], qubits[1])
qc.measure_all()
return qc
@staticmethod
def quantum_error_correction_3bit() -> QuantumCircuit:
"""Create a 3-qubit bit-flip error correction circuit."""
qc = QuantumCircuit(9, 3, name="QEC-3bit-BitFlip")
# Encode logical |0⟩ or |1⟩ (start with |+⟩ state)
qc.h(0)
# Encoding
qc.cx(0, 3)
qc.cx(0, 6)
# Simulate error (bit flip on qubit 3)
qc.x(3)
# Syndrome measurement
qc.cx(0, 1)
qc.cx(3, 1)
qc.cx(3, 2)
qc.cx(6, 2)
# Error correction (simplified)
qc.ccx(1, 2, 3) # Correct bit flip if detected
# Decoding and measurement
qc.cx(0, 3)
qc.cx(0, 6)
qc.measure([0, 1, 2], [0, 1, 2])
return qc
@classmethod
def get_all_examples(cls) -> Dict[str, QuantumCircuit]:
"""Get all example circuits as a dictionary."""
examples = {}
# Basic states
examples['bell_state'] = cls.bell_state()
examples['ghz_3'] = cls.ghz_state(3)
examples['ghz_4'] = cls.ghz_state(4)
examples['w_state'] = cls.w_state(3)
# Algorithms
examples['qft_3'] = cls.qft_circuit(3)
examples['qft_4'] = cls.qft_circuit(4)
examples['grover_2'] = cls.grover_search(2)
examples['deutsch_jozsa'] = cls.deutsch_jozsa(3, balanced=True)
# Protocols
examples['teleportation'] = cls.quantum_teleportation()
examples['error_correction'] = cls.quantum_error_correction_3bit()
# Variational algorithms
examples['vqe_ansatz'] = cls.vqe_ansatz(4, 2)
examples['qaoa'] = cls.qaoa_circuit(4, 1)
# Random circuits
examples['random_small'] = cls.random_circuit(3, 5, seed=42)
examples['random_medium'] = cls.random_circuit(5, 8, seed=42)
return examples
@classmethod
def get_benchmark_suite(cls) -> List[QuantumCircuit]:
"""Get a comprehensive benchmark suite."""
circuits = []
# Scalability test circuits
for n in [2, 3, 4, 5]:
circuits.append(cls.ghz_state(n))
circuits.append(cls.qft_circuit(n))
for n in [2, 3]:
circuits.append(cls.grover_search(n))
# Algorithm demonstrations
circuits.append(cls.deutsch_jozsa(4, balanced=True))
circuits.append(cls.quantum_teleportation())
circuits.append(cls.vqe_ansatz(4, 2))
circuits.append(cls.quantum_error_correction_3bit())
# Random circuits for stress testing
for n_qubits in [3, 5, 7]:
for depth in [5, 10]:
circuits.append(cls.random_circuit(n_qubits, depth, seed=42))
return circuits
def save_example_qasm_files(output_dir: str = "qasm_examples"):
"""Save example circuits as QASM files."""
output_path = Path(output_dir)
output_path.mkdir(exist_ok=True)
library = HDHCircuitLibrary()
examples = library.get_all_examples()
for name, circuit in examples.items():
# Remove measurements for QASM export (QASM 2.0 limitation)
qasm_circuit = circuit.copy()
qasm_circuit.remove_final_measurements(inplace=True)
qasm_file = output_path / f"{name}.qasm"
with open(qasm_file, 'w') as f:
f.write(qasm_circuit.qasm())
print(f"Saved {name} to {qasm_file}")
if __name__ == "__main__":
"""Demonstration of the circuit library."""
print("HDH Circuit Examples Library")
print("=" * 50)
print("Special thanks to Maria Gragera Garces for the HDH library!")
print()
library = HDHCircuitLibrary()
# Show all available examples
examples = library.get_all_examples()
print(f"Available circuits ({len(examples)}):")
for name, qc in examples.items():
print(f" {name:20} - {qc.num_qubits} qubits, depth {qc.depth()}")
print(f"\nBenchmark suite: {len(library.get_benchmark_suite())} circuits")
# Save QASM examples
print("\nSaving QASM examples...")
save_example_qasm_files()
print("Done!")