MophongLT / plugins /phase_estimation /phase_estimation.py
gaialive's picture
Upload 170 files
227c43a verified
"""
High-Fidelity Quantum Phase Estimation Simulation using Google Cirq with Detailed Math Logging.
Quantum Phase Estimation (QPE) is a quantum algorithm used to estimate the eigenphase of an
eigenvector of a unitary operator. It is a critical subroutine in many quantum algorithms including
Shor's algorithm and HHL algorithm for linear systems.
"""
import cirq
import numpy as np
import math
from cirq.contrib.svg import circuit_to_svg
def inverse_qft_circuit(qubits):
"""
Constructs a circuit that performs the inverse Quantum Fourier Transform (QFT†).
Args:
qubits: List of qubits
Returns:
A cirq.Circuit implementing the inverse QFT
"""
n = len(qubits)
circuit = cirq.Circuit()
# Process qubits in reverse order for inverse QFT
for i in range(n-1, -1, -1):
# Apply H gate
circuit.append(cirq.H(qubits[i]))
# Apply controlled phase rotations with conjugated phases
for j in range(i):
k = i - j
# Phase rotation by -2π/2^k
circuit.append(cirq.CZPowGate(exponent=-1/(2**(k))).on(qubits[j], qubits[i]))
return circuit
def controlled_unitary(control, target, phase_angle):
"""
Creates a controlled unitary operation for a given phase angle.
Args:
control: Control qubit
target: Target qubit
phase_angle: Phase angle (in radians)
Returns:
A cirq.Circuit implementing the controlled unitary
"""
# For simplicity, we use controlled phase rotations
# In a real QPE, this would be a controlled version of the unitary operator
return cirq.Circuit([
cirq.CZPowGate(exponent=phase_angle/(2*math.pi)).on(control, target)
])
def phase_estimation_circuit(phase_qubits, target_qubit, phase_angle, precision_bits):
"""
Constructs a circuit that performs Quantum Phase Estimation.
Args:
phase_qubits: Qubits used for phase estimation
target_qubit: Qubit that is an eigenstate of the unitary operator
phase_angle: True phase angle to estimate (in radians)
precision_bits: Number of bits of precision
Returns:
A cirq.Circuit implementing QPE
"""
n = len(phase_qubits)
circuit = cirq.Circuit()
# Step 1: Initialize target qubit to the eigenstate of the unitary
# For a phase gate, |1⟩ is an eigenstate
circuit.append(cirq.X(target_qubit))
# Step 2: Apply Hadamard gates to create superposition of phase qubits
circuit.append(cirq.H.on_each(*phase_qubits))
# Step 3: Apply controlled unitary operations
for i, qubit in enumerate(phase_qubits):
# Apply U^(2^i) controlled by the i-th qubit
power = 2**(n-i-1) # Powers decrease from most significant to least
for _ in range(power):
circuit.append(controlled_unitary(qubit, target_qubit, phase_angle))
# Step 4: Apply inverse QFT to the phase register
circuit.append(inverse_qft_circuit(phase_qubits))
return circuit
def add_noise(circuit, noise_prob):
"""
Adds realistic quantum noise to the circuit.
Args:
circuit: A Cirq circuit
noise_prob: Probability of depolarizing noise
Returns:
A circuit with added noise operations
"""
if noise_prob <= 0:
return circuit
noisy_ops = []
for op in circuit.all_operations():
noisy_ops.append(op)
for q in op.qubits:
noisy_ops.append(cirq.DepolarizingChannel(noise_prob).on(q))
return cirq.Circuit(noisy_ops)
def binary_to_phase(binary_str):
"""
Converts a binary string to a phase between 0 and 1.
Args:
binary_str: A string of '0's and '1's
Returns:
A float representing the phase
"""
if not binary_str:
return 0.0
return sum(int(bit) * 2**(-i-1) for i, bit in enumerate(binary_str))
def phase_to_binary(phase, precision_bits):
"""
Converts a phase between 0 and 1 to a binary string.
Args:
phase: A float between 0 and 1
precision_bits: Number of binary digits to include
Returns:
A binary string representation
"""
binary = ""
for i in range(precision_bits):
phase *= 2
bit = int(phase)
binary += str(bit)
phase -= bit
return binary
def run_phase_estimation(precision_bits=3, target_phase=0.125, noise_prob=0.0):
"""
Runs the Quantum Phase Estimation algorithm.
Args:
precision_bits: Number of bits of precision
target_phase: True phase to estimate (between 0 and 1)
noise_prob: Probability of depolarizing noise
Returns:
Dictionary with QPE results and visualization
"""
log = []
log.append("=== Quantum Phase Estimation Simulation ===")
# Validate target phase
target_phase = max(0.0, min(1.0, target_phase))
phase_angle = 2 * math.pi * target_phase
log.append(f"Target phase: {target_phase} (fraction of 2π)")
log.append(f"Phase angle: {phase_angle} radians")
# Create qubits
phase_qubits = [cirq.NamedQubit(f'p{i}') for i in range(precision_bits)]
target_qubit = cirq.NamedQubit('t')
# Create circuit
circuit = phase_estimation_circuit(phase_qubits, target_qubit, phase_angle, precision_bits)
log.append(f"Created QPE circuit with {precision_bits} precision qubits")
# Add noise if specified
if noise_prob > 0:
circuit = add_noise(circuit, noise_prob)
log.append(f"Added noise with probability {noise_prob}")
# Add measurements
measure_circuit = cirq.Circuit()
measure_circuit.append(cirq.measure(*phase_qubits, key='phase'))
# Combine circuits
full_circuit = circuit + measure_circuit
# Run the circuit
simulator = cirq.Simulator()
result = simulator.run(full_circuit, repetitions=1)
# Process the measurement results
measurements = result.measurements['phase'][0]
measured_state = ''.join([str(bit) for bit in measurements])
# Convert binary result to phase
estimated_phase = binary_to_phase(measured_state)
phase_error = abs(estimated_phase - target_phase)
log.append(f"Measured state: |{measured_state}⟩")
log.append(f"Estimated phase: {estimated_phase}")
log.append(f"Absolute error: {phase_error}")
# Generate circuit SVG for visualization
circuit_svg = circuit_to_svg(full_circuit)
# Theoretical analysis
expected_accuracy = 1 / (2**precision_bits)
log.append(f"\nTheoretical Notes:")
log.append(f"- With {precision_bits} qubits, we expect accuracy of approximately {expected_accuracy}")
log.append(f"- Theoretical best binary approximation with {precision_bits} bits: {phase_to_binary(target_phase, precision_bits)}")
log.append(f"- Corresponding phase: {binary_to_phase(phase_to_binary(target_phase, precision_bits))}")
if noise_prob > 0:
log.append(f"- Noise will reduce accuracy, with more effect on higher-precision bits")
# Return results
return {
"precision_bits": precision_bits,
"target_phase": target_phase,
"measured_state": measured_state,
"estimated_phase": float(estimated_phase),
"phase_error": float(phase_error),
"theoretical_accuracy": float(expected_accuracy),
"noise_prob": noise_prob,
"circuit_svg": circuit_svg,
"log": "\n".join(log)
}
if __name__ == '__main__':
# Run QPE examples
qpe_simple = run_phase_estimation(3, 0.125) # 1/8 = 0.001 in binary
qpe_complex = run_phase_estimation(5, 0.3) # 0.3 is not precisely representable in binary
qpe_noisy = run_phase_estimation(4, 0.25, 0.01) # 1/4 = 0.01 in binary, with noise