From 083f60ffa286da523f4b66dd69823aeab932bb1f Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Tue, 22 Apr 2025 19:13:13 +0300 Subject: [PATCH] feat(args): add args functionality --- pyproject.toml | 3 ++- src/grovers_visualizer/main.py | 39 +++++++++++++++----------------- src/grovers_visualizer/parse.py | 40 +++++++++++++++++++++++++++++++++ uv.lock | 13 ++++++++++- 4 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 src/grovers_visualizer/parse.py diff --git a/pyproject.toml b/pyproject.toml index 28630df..09b6863 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,11 @@ [project] name = "grovers-visualizer" -version = "0.2.0" +version = "0.3.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.13" dependencies = [ + "argparse>=1.4.0", "numpy>=2.2.4", "qiskit-aer>=0.17.0", "qiskit[visualization]>=2.0.0", diff --git a/src/grovers_visualizer/main.py b/src/grovers_visualizer/main.py index 70960c4..1d1fce3 100644 --- a/src/grovers_visualizer/main.py +++ b/src/grovers_visualizer/main.py @@ -10,36 +10,23 @@ from math import asin, sqrt from typing import TYPE_CHECKING, Callable import matplotlib.pyplot as plt -from matplotlib.axes import Axes 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 draw_grover_circle, plot_amplitudes_live -from grovers_visualizer.state import QubitState from grovers_visualizer.utils import all_states, optimal_grover_iterations if TYPE_CHECKING: + from matplotlib.axes import Axes from matplotlib.figure import Figure -def plot_counts(ax: Axes, counts: dict[str, int], target_state: QubitState) -> None: - """Display a bar chart for the measurement results.""" - - # Sort the states - states = list(counts.keys()) - frequencies = [counts[s] for s in states] - - ax.clear() - ax.bar(states, frequencies, color="skyblue") - ax.set_xlabel("Measured State") - ax.set_ylabel("Counts") - ax.set_title(f"Measurement Counts for Target: {target_state}") - ax.set_ylim(0, max(frequencies) * 1.2) - - def main() -> None: - target_state = QubitState("1010") + 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) @@ -66,7 +53,7 @@ def main() -> None: 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(0.5) + plt.pause(args.speed) # Start with Hadamard qc = QuantumCircuit(n_qubits) @@ -74,14 +61,24 @@ def main() -> None: iterate_and_plot(None, "Hadamard (Initialization)", 0) iteration = 1 - while plt.fignum_exists(fig.number): + running = True + + def on_key(event) -> 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: 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 + if args.iterations > 0 and iteration > args.iterations: + break + fig.canvas.mpl_disconnect(cid) plt.ioff() - plt.show() if __name__ == "__main__": diff --git a/src/grovers_visualizer/parse.py b/src/grovers_visualizer/parse.py new file mode 100644 index 0000000..779a46d --- /dev/null +++ b/src/grovers_visualizer/parse.py @@ -0,0 +1,40 @@ +from argparse import ArgumentParser +from dataclasses import dataclass + +from grovers_visualizer.state import QubitState + + +@dataclass +class Args: + target: QubitState + iterations: int + speed: float + + +def parse_args() -> Args: + parser = ArgumentParser(description="Grover's Algorithm Visualizer") + parser.add_argument( + "target", + type=str, + help="Target bitstring (e.g., 1010)", + ) + parser.add_argument( + "-i", + "--iterations", + type=int, + default=0, + help="Number of Grover iterations (default: 0 (infinite))", + ) + parser.add_argument( + "-s", + "--speed", + type=float, + default=0.5, + help="Pause duration (seconds) between steps (deafult: 0.5)", + ) + ns = parser.parse_args() + return Args( + target=QubitState(ns.target), + iterations=ns.iterations, + speed=ns.speed, + ) diff --git a/uv.lock b/uv.lock index 624f865..02718e3 100644 --- a/uv.lock +++ b/uv.lock @@ -2,6 +2,15 @@ version = 1 revision = 1 requires-python = ">=3.13" +[[package]] +name = "argparse" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/dd/e617cfc3f6210ae183374cd9f6a26b20514bbb5a792af97949c5aacddf0f/argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4", size = 70508 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/94/3af39d34be01a24a6e65433d19e107099374224905f1e0cc6bbe1fd22a2f/argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314", size = 23000 }, +] + [[package]] name = "contourpy" version = "1.3.1" @@ -70,9 +79,10 @@ wheels = [ [[package]] name = "grovers-visualizer" -version = "0.2.0" +version = "0.3.0" source = { editable = "." } dependencies = [ + { name = "argparse" }, { name = "numpy" }, { name = "qiskit", extra = ["visualization"] }, { name = "qiskit-aer" }, @@ -91,6 +101,7 @@ dev = [ [package.metadata] requires-dist = [ + { name = "argparse", specifier = ">=1.4.0" }, { name = "numpy", specifier = ">=2.2.4" }, { name = "pyqt6", marker = "extra == 'mpl'", specifier = ">=6.9.0" }, { name = "qiskit", extras = ["visualization"], specifier = ">=2.0.0" },