455 líneas
14 KiB
Python
455 líneas
14 KiB
Python
#!/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!") |