diff --git a/src/grovers_visualizer/parse.py b/src/grovers_visualizer/args.py similarity index 62% rename from src/grovers_visualizer/parse.py rename to src/grovers_visualizer/args.py index 0b1665c..f94287e 100644 --- a/src/grovers_visualizer/parse.py +++ b/src/grovers_visualizer/args.py @@ -2,6 +2,7 @@ from argparse import ArgumentParser from dataclasses import dataclass from grovers_visualizer.state import QubitState +from grovers_visualizer.utils import get_app_version @dataclass @@ -14,9 +15,36 @@ class Args: def parse_args() -> Args: parser = ArgumentParser(description="Grover's Algorithm Visualizer") + + parse_opts(parser) + parse_cli(parser) + + ns = parser.parse_args() + + return Args( + target=ns.target, + iterations=ns.iterations, + speed=ns.speed, + ui=ns.ui, + ) + + +def parse_opts(base_parser: ArgumentParser) -> None: + parser = base_parser + + parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {get_app_version()}") + + +def parse_cli(base_parser: ArgumentParser) -> None: + parser = base_parser.add_argument_group("cli") + + parser.register("type", "qubit state", QubitState.from_str) + parser.add_argument( "target", - type=str, + type="qubit state", + default=QubitState.from_str("1111"), + nargs="?", help="Target bitstring (e.g., 1010)", ) parser.add_argument( @@ -34,10 +62,3 @@ def parse_args() -> Args: 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, - ) diff --git a/src/grovers_visualizer/cli.py b/src/grovers_visualizer/cli.py index 30497e9..b11a37c 100644 --- a/src/grovers_visualizer/cli.py +++ b/src/grovers_visualizer/cli.py @@ -7,8 +7,8 @@ from matplotlib.gridspec import GridSpec from qiskit import QuantumCircuit from qiskit.quantum_info import Statevector +from .args import Args 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 diff --git a/src/grovers_visualizer/main.py b/src/grovers_visualizer/main.py index 409f606..9bdf73d 100644 --- a/src/grovers_visualizer/main.py +++ b/src/grovers_visualizer/main.py @@ -6,8 +6,8 @@ simulation using Qiskit's Aer simulator, and visualizes the results using matplotlib. """ +from .args import parse_args from .cli import run_cli -from .parse import parse_args from .ui import run_dpg_ui diff --git a/src/grovers_visualizer/ui/__init__.py b/src/grovers_visualizer/ui/__init__.py index 1380fcc..b21726c 100644 --- a/src/grovers_visualizer/ui/__init__.py +++ b/src/grovers_visualizer/ui/__init__.py @@ -1,6 +1,6 @@ from importlib.util import find_spec -from grovers_visualizer.parse import Args +from grovers_visualizer.args import Args def is_dearpygui_available() -> bool: @@ -10,11 +10,11 @@ def is_dearpygui_available() -> bool: return False -def run_dpg_ui(_args: Args) -> None: +def run_dpg_ui(args: Args) -> None: if not is_dearpygui_available(): - print("DearPyGui is not installed. Install with: pip install .[ui]") + print("DearPyGui is not installed. Install with: pip install 'grovers-visualizer[ui]'") return from .dpg import run_dearpygui_ui - run_dearpygui_ui() + run_dearpygui_ui(args) diff --git a/src/grovers_visualizer/utils.py b/src/grovers_visualizer/utils.py index 4b7e82f..e90f9c6 100644 --- a/src/grovers_visualizer/utils.py +++ b/src/grovers_visualizer/utils.py @@ -1,6 +1,8 @@ +import tomllib from collections.abc import Iterator from itertools import product from math import floor, pi, sqrt +from pathlib import Path from .state import QubitState @@ -27,3 +29,16 @@ def get_bar_color(state: str, target_state: QubitState | None, iteration: int, o if optimal_iteration and is_optimal_iteration(iteration, optimal_iteration): return "green" return "orange" + + +def get_app_version(pyproject_path: str = "pyproject.toml") -> str: + """Reads the version from the [project] section of pyproject.toml.""" + path = Path(pyproject_path) + if not path.is_file(): + raise FileNotFoundError(f"{pyproject_path} not found.") + with path.open("rb") as f: + data = tomllib.load(f) + try: + return str(data["project"]["version"]) + except KeyError: + raise KeyError("Version not found in [project] section of pyproject.toml.")