mirror of
https://github.com/kristoferssolo/grovers-visualizer.git
synced 2025-10-21 20:10:35 +00:00
feat(circle): add triangulation circle
This commit is contained in:
parent
6346349266
commit
8aedc59760
@ -8,20 +8,24 @@ using matplotlib.
|
||||
|
||||
from collections.abc import Iterator
|
||||
from itertools import product
|
||||
from math import floor, pi, sqrt
|
||||
from typing import Callable
|
||||
from math import asin, cos, floor, pi, sin, sqrt
|
||||
from typing import TYPE_CHECKING, Callable
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from matplotlib.axes import Axes
|
||||
from matplotlib.container import BarContainer
|
||||
from matplotlib.patches import Circle
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.quantum_info import Statevector
|
||||
|
||||
from grovers_visualizer.gates import apply_phase_inversion, encode_target_state
|
||||
from grovers_visualizer.state import QubitState
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
|
||||
def oracle(qc: QuantumCircuit, target_state: QubitState) -> None:
|
||||
"""Oracle that flips the sign of the target state."""
|
||||
@ -84,11 +88,15 @@ def optimal_grover_iterations(n_qubits: int) -> int:
|
||||
return floor(pi / 4 * sqrt(2**n_qubits))
|
||||
|
||||
|
||||
def is_optimal_iteration(iteration: int, optimal_iteration: int) -> bool:
|
||||
return iteration % optimal_iteration == 0 and iteration != 0
|
||||
|
||||
|
||||
def get_bar_color(state: str, target_state: QubitState | None, iteration: int, optimal_iteration: int | None) -> str:
|
||||
"""Return the color for a bar based on state and iteration."""
|
||||
if state != target_state:
|
||||
return "skyblue"
|
||||
if optimal_iteration and iteration % optimal_iteration == 0 and iteration != 0:
|
||||
if optimal_iteration and is_optimal_iteration(iteration, optimal_iteration):
|
||||
return "green"
|
||||
return "orange"
|
||||
|
||||
@ -98,7 +106,7 @@ def plot_amplitudes_live(
|
||||
bars: BarContainer,
|
||||
statevector: Statevector,
|
||||
basis_states: list[str],
|
||||
step_label: str,
|
||||
iteration_label: str,
|
||||
iteration: int,
|
||||
target_state: QubitState | None = None,
|
||||
optimal_iteration: int | None = None,
|
||||
@ -110,7 +118,7 @@ def plot_amplitudes_live(
|
||||
bar.set_height(amp)
|
||||
bar.set_color(get_bar_color(state, target_state, iteration, optimal_iteration))
|
||||
|
||||
ax.set_title(f"Iteration {iteration}: {step_label}")
|
||||
ax.set_title(f"Iteration {iteration}: {iteration_label}")
|
||||
ax.set_ylim(-1, 1)
|
||||
|
||||
for l in ax.lines: # Remove previous mean line(s)
|
||||
@ -120,7 +128,50 @@ def plot_amplitudes_live(
|
||||
|
||||
if not ax.get_legend():
|
||||
ax.legend(loc="upper right")
|
||||
plt.pause(1)
|
||||
|
||||
|
||||
def draw_grover_circle(
|
||||
ax: Axes,
|
||||
iteration: int,
|
||||
optimal_iterations: int,
|
||||
theta: float,
|
||||
state_angle: float,
|
||||
) -> None:
|
||||
ax.clear()
|
||||
ax.set_aspect("equal")
|
||||
ax.set_xlim(-1.1, 1.1)
|
||||
ax.set_ylim(-1.1, 1.1)
|
||||
ax.set_xlabel("Unmarked amplitude")
|
||||
ax.set_ylabel("Target amplitude")
|
||||
ax.set_title("Grover State Vector Rotation")
|
||||
|
||||
# Draw unit circle
|
||||
circle = Circle((0, 0), 1, color="gray", fill=False)
|
||||
ax.add_artist(circle)
|
||||
|
||||
# Draw axes
|
||||
ax.axhline(0, color="black", linewidth=0.5)
|
||||
ax.axvline(0, color="black", linewidth=0.5)
|
||||
|
||||
# Draw labels
|
||||
ax.text(1.05, 0, "", va="center", ha="left", fontsize=10)
|
||||
ax.text(0, 1.05, "1", va="bottom", ha="center", fontsize=10)
|
||||
ax.text(-1.05, 0, "", va="center", ha="right", fontsize=10)
|
||||
ax.text(0, -1.05, "-1", va="top", ha="center", fontsize=10)
|
||||
|
||||
angle = state_angle + iteration * theta
|
||||
x, y = cos(angle), sin(angle)
|
||||
is_optimal = optimal_iterations and is_optimal_iteration(iteration, optimal_iterations)
|
||||
|
||||
# Arrow color: green at optimal, blue otherwise
|
||||
color = "green" if is_optimal else "blue"
|
||||
ax.arrow(0, 0, x, y, head_width=0.07, head_length=0.1, fc=color, ec=color, length_includes_head=True)
|
||||
|
||||
# Probability of target state is y^2
|
||||
prob = y**2
|
||||
ax.set_title(
|
||||
f"Grover State Vector Rotation\nIteration {iteration} | Probability of target: {prob:.2f}{' (optimal)' if is_optimal else ''}"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@ -128,16 +179,19 @@ def main() -> None:
|
||||
n_qubits = len(target_state)
|
||||
basis_states = [str(bit) for bit in all_states(n_qubits)]
|
||||
optimal_iterations = optimal_grover_iterations(n_qubits)
|
||||
theta = 2 * asin(1 / sqrt(2**n_qubits))
|
||||
state_angle = 0.5 * theta
|
||||
|
||||
plt.ion()
|
||||
fig, ax = plt.subplots(figsize=(8, 3))
|
||||
bars = ax.bar(basis_states, [0] * len(basis_states), color="skyblue")
|
||||
ax.set_xlabel("Basis State")
|
||||
ax.set_ylabel("Real Amplitude")
|
||||
ax.set_ylim(-1, 1)
|
||||
ax.set_title("Grover Amplitudes")
|
||||
fig: Figure
|
||||
ax_bar: Axes
|
||||
ax_circle: Axes
|
||||
fig, (ax_bar, ax_circle) = plt.subplots(1, 2, figsize=(12, 4))
|
||||
bars = ax_bar.bar(basis_states, [0] * len(basis_states), color="skyblue")
|
||||
ax_bar.set_ylim(-1, 1)
|
||||
ax_bar.set_title("Amplitudes (example)")
|
||||
|
||||
def step_and_plot(
|
||||
def iterate_and_plot(
|
||||
operation: Callable[[QuantumCircuit], None] | None,
|
||||
step_label: str,
|
||||
iteration: int,
|
||||
@ -145,17 +199,21 @@ def main() -> None:
|
||||
if operation is not None:
|
||||
operation(qc)
|
||||
sv = Statevector.from_instruction(qc)
|
||||
plot_amplitudes_live(ax, bars, sv, basis_states, step_label, iteration, target_state, optimal_iterations)
|
||||
plot_amplitudes_live(ax_bar, bars, sv, basis_states, step_label, iteration, target_state, optimal_iterations)
|
||||
draw_grover_circle(ax_circle, iteration, optimal_iterations, theta, state_angle)
|
||||
|
||||
plt.pause(1)
|
||||
|
||||
# Start with Hadamard
|
||||
qc = QuantumCircuit(n_qubits)
|
||||
qc.h(range(n_qubits))
|
||||
step_and_plot(None, "Hadamard (Initialization)", 0)
|
||||
iterate_and_plot(None, "Hadamard (Initialization)", 0)
|
||||
|
||||
iteration = 1
|
||||
while plt.fignum_exists(fig.number):
|
||||
step_and_plot(lambda qc: oracle(qc, target_state), "Oracle (Query Phase)", iteration)
|
||||
step_and_plot(lambda qc: diffusion(qc, n_qubits), "Diffusion (Inversion Phase)", iteration)
|
||||
iterate_and_plot(lambda qc: oracle(qc, target_state), "Oracle (Query Phase)", iteration)
|
||||
iterate_and_plot(lambda qc: diffusion(qc, n_qubits), "Diffusion (Inversion Phase)", iteration)
|
||||
|
||||
iteration += 1
|
||||
|
||||
plt.ioff()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user