chore(ui): setup dependencies

This commit is contained in:
2025-04-24 15:02:09 +03:00
parent 995e8ff45d
commit 50e843a884
6 changed files with 135 additions and 93 deletions

View File

@@ -0,0 +1,79 @@
from math import asin, sqrt
from typing import Callable
import matplotlib.pyplot as plt
from matplotlib.backend_bases import KeyEvent
from matplotlib.gridspec import GridSpec
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from .circuit import diffusion, oracle
from .parse import Args
from .plot import SinePlotData, plot_amplitudes, plot_circle, plot_sine
from .utils import all_states, optimal_grover_iterations
def run_cli(args: Args) -> None:
target_state = args.target
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 = plt.figure(figsize=(14, 6))
gs = GridSpec(2, 2, width_ratios=(3, 1), figure=fig)
ax_bar = fig.add_subplot(gs[0, 0])
ax_sine = fig.add_subplot(gs[1, 0])
ax_circle = fig.add_subplot(gs[:, 1])
bars = ax_bar.bar(basis_states, [0] * len(basis_states), color="skyblue")
ax_bar.set_ylim(-1, 1)
ax_bar.set_title("Amplitudes (example)")
sine_data = SinePlotData()
def plot_bar(
operation: Callable[[QuantumCircuit], None] | None,
step_label: str,
iteration: int,
) -> None:
if operation is not None:
operation(qc)
sv = Statevector.from_instruction(qc)
plot_amplitudes(ax_bar, bars, sv, basis_states, step_label, iteration, target_state, optimal_iterations)
# Start with Hadamard
qc = QuantumCircuit(n_qubits)
qc.h(range(n_qubits))
plot_bar(None, "Hadamard (Initialization)", 0)
iteration = 1
running = True
def on_key(event: KeyEvent) -> None:
nonlocal running
if event.key == "q":
running = False
cid = fig.canvas.mpl_connect("key_press_event", on_key)
while plt.fignum_exists(fig.number) and running:
plot_bar(lambda qc: oracle(qc, target_state), "Oracle (Query Phase)", iteration)
plot_bar(lambda qc: diffusion(qc, n_qubits), "Diffusion (Inversion Phase)", iteration)
plot_circle(ax_circle, iteration, optimal_iterations, theta, state_angle)
sine_data.calc_and_append_probability(iteration, theta)
plot_sine(ax_sine, sine_data)
plt.pause(args.speed)
iteration += 1
if args.iterations > 0 and iteration > args.iterations:
break
fig.canvas.mpl_disconnect(cid)
plt.ioff()
if __name__ == "__main__":
main()

View File

@@ -6,84 +6,14 @@ simulation using Qiskit's Aer simulator, and visualizes the results
using matplotlib.
"""
from math import asin, sqrt
from typing import Callable
import matplotlib.pyplot as plt
from matplotlib.backend_bases import KeyEvent
from matplotlib.gridspec import GridSpec
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
from grovers_visualizer.circuit import diffusion, oracle
from grovers_visualizer.parse import parse_args
from grovers_visualizer.plot import SinePlotData, plot_amplitudes, plot_circle, plot_sine
from grovers_visualizer.utils import all_states, optimal_grover_iterations
from .cli import run_cli
from .parse import parse_args
from .ui import run_dpg_ui
def main() -> None:
args = parse_args()
target_state = args.target
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 = plt.figure(figsize=(14, 6))
gs = GridSpec(2, 2, width_ratios=(3, 1), figure=fig)
ax_bar = fig.add_subplot(gs[0, 0])
ax_sine = fig.add_subplot(gs[1, 0])
ax_circle = fig.add_subplot(gs[:, 1])
bars = ax_bar.bar(basis_states, [0] * len(basis_states), color="skyblue")
ax_bar.set_ylim(-1, 1)
ax_bar.set_title("Amplitudes (example)")
sine_data = SinePlotData()
def plot_bar(
operation: Callable[[QuantumCircuit], None] | None,
step_label: str,
iteration: int,
) -> None:
if operation is not None:
operation(qc)
sv = Statevector.from_instruction(qc)
plot_amplitudes(ax_bar, bars, sv, basis_states, step_label, iteration, target_state, optimal_iterations)
# Start with Hadamard
qc = QuantumCircuit(n_qubits)
qc.h(range(n_qubits))
plot_bar(None, "Hadamard (Initialization)", 0)
iteration = 1
running = True
def on_key(event: KeyEvent) -> None:
nonlocal running
if event.key == "q":
running = False
cid = fig.canvas.mpl_connect("key_press_event", on_key)
while plt.fignum_exists(fig.number) and running:
plot_bar(lambda qc: oracle(qc, target_state), "Oracle (Query Phase)", iteration)
plot_bar(lambda qc: diffusion(qc, n_qubits), "Diffusion (Inversion Phase)", iteration)
plot_circle(ax_circle, iteration, optimal_iterations, theta, state_angle)
sine_data.calc_and_append_probability(iteration, theta)
plot_sine(ax_sine, sine_data)
plt.pause(args.speed)
iteration += 1
if args.iterations > 0 and iteration > args.iterations:
break
fig.canvas.mpl_disconnect(cid)
plt.ioff()
if __name__ == "__main__":
main()
if args.ui:
run_dpg_ui(args)
else:
run_cli(args)

View File

@@ -9,6 +9,7 @@ class Args:
target: QubitState
iterations: int
speed: float
ui: bool
def parse_args() -> Args:
@@ -32,9 +33,11 @@ def parse_args() -> Args:
default=0.5,
help="Pause duration (seconds) between steps (deafult: 0.5)",
)
parser.add_argument("--ui", action="store_true", help="Run with DearPyGui UI")
ns = parser.parse_args()
return Args(
target=QubitState.from_str(ns.target),
iterations=ns.iterations,
speed=ns.speed,
ui=ns.ui,
)

View File

@@ -0,0 +1,16 @@
from importlib.util import find_spec
from .parse import Args
def is_dearpygui_available() -> bool:
try:
return find_spec("dearpygui.dearpygui") is not None
except ModuleNotFoundError:
return False
def run_dpg_ui(_args: Args) -> None:
if not is_dearpygui_available():
print("DearPyGui is not installed. Install with: pip install .[ui]")
return