mirror of
https://github.com/kristoferssolo/grovers-visualizer.git
synced 2025-10-21 20:10:35 +00:00
chore(ui): setup dependencies
This commit is contained in:
parent
995e8ff45d
commit
50e843a884
@ -5,20 +5,21 @@ description = "Add your description here"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argparse>=1.4.0",
|
"argparse==1.4.0",
|
||||||
"numpy>=2.2.4",
|
"numpy==2.2.4",
|
||||||
"qiskit-aer>=0.17.0",
|
"pyqt6==6.9.0",
|
||||||
"qiskit[visualization]>=2.0.0",
|
"qiskit-aer==0.17.0",
|
||||||
|
"qiskit[visualization]==2.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
grovers-visualizer = "grovers_visualizer.main:main"
|
grovers-visualizer = "grovers_visualizer.main:main"
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
mpl = ["pyqt6>=6.9.0"]
|
ui = ["dearpygui==2.0.0"]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = ["mypy>=1.15.0", "ruff>=0.11.4"]
|
dev = ["mypy~=1.15", "ruff~=0.11"]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
|
|||||||
79
src/grovers_visualizer/cli.py
Normal file
79
src/grovers_visualizer/cli.py
Normal 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()
|
||||||
@ -6,84 +6,14 @@ simulation using Qiskit's Aer simulator, and visualizes the results
|
|||||||
using matplotlib.
|
using matplotlib.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from math import asin, sqrt
|
from .cli import run_cli
|
||||||
from typing import Callable
|
from .parse import parse_args
|
||||||
|
from .ui import run_dpg_ui
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
if args.ui:
|
||||||
target_state = args.target
|
run_dpg_ui(args)
|
||||||
n_qubits = len(target_state)
|
else:
|
||||||
basis_states = [str(bit) for bit in all_states(n_qubits)]
|
run_cli(args)
|
||||||
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()
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ class Args:
|
|||||||
target: QubitState
|
target: QubitState
|
||||||
iterations: int
|
iterations: int
|
||||||
speed: float
|
speed: float
|
||||||
|
ui: bool
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> Args:
|
def parse_args() -> Args:
|
||||||
@ -32,9 +33,11 @@ def parse_args() -> Args:
|
|||||||
default=0.5,
|
default=0.5,
|
||||||
help="Pause duration (seconds) between steps (deafult: 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()
|
ns = parser.parse_args()
|
||||||
return Args(
|
return Args(
|
||||||
target=QubitState.from_str(ns.target),
|
target=QubitState.from_str(ns.target),
|
||||||
iterations=ns.iterations,
|
iterations=ns.iterations,
|
||||||
speed=ns.speed,
|
speed=ns.speed,
|
||||||
|
ui=ns.ui,
|
||||||
)
|
)
|
||||||
|
|||||||
16
src/grovers_visualizer/ui.py
Normal file
16
src/grovers_visualizer/ui.py
Normal 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
|
||||||
33
uv.lock
33
uv.lock
@ -51,6 +51,17 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 },
|
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dearpygui"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/58/4e1744d2af72a3aa432fd82aeb97dcbc43351b17f837d879b4c4926bd16c/dearpygui-2.0.0-cp313-cp313-macosx_10_6_x86_64.whl", hash = "sha256:3bf0aa19baedb4f130b8de636d4644740ffd9b7008481794c574bf59240808dc", size = 2223494 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/e0/09885f99df476d03e573183a755dc515cd88565cdbf036c385f0dda41dbb/dearpygui-2.0.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:ae6fc9aa3390b29387c2bc2bf3ac7a92c10eae2b479818171864fc29a7de344e", size = 1873562 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d6/2b/0714feed9e072377d575cb8801ac13a909f7880ca51c9dd6f2d768bcd1ba/dearpygui-2.0.0-cp313-cp313-manylinux1_x86_64.whl", hash = "sha256:f02b0ab56700a775d7e6446e3c424d5bed3386efe721a04518d02461851daadf", size = 2636153 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/55/9e/a957ffc21d12a1fb66d611f82871944565112b44afa0856bb15c6ece77d5/dearpygui-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e3d52057f49773b10808962806711c3b3119e829d36407afb84ad50522edc9b0", size = 1814308 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dill"
|
name = "dill"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@ -84,13 +95,14 @@ source = { editable = "." }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "argparse" },
|
{ name = "argparse" },
|
||||||
{ name = "numpy" },
|
{ name = "numpy" },
|
||||||
|
{ name = "pyqt6" },
|
||||||
{ name = "qiskit", extra = ["visualization"] },
|
{ name = "qiskit", extra = ["visualization"] },
|
||||||
{ name = "qiskit-aer" },
|
{ name = "qiskit-aer" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
mpl = [
|
ui = [
|
||||||
{ name = "pyqt6" },
|
{ name = "dearpygui" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dev-dependencies]
|
[package.dev-dependencies]
|
||||||
@ -101,18 +113,19 @@ dev = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "argparse", specifier = ">=1.4.0" },
|
{ name = "argparse", specifier = "==1.4.0" },
|
||||||
{ name = "numpy", specifier = ">=2.2.4" },
|
{ name = "dearpygui", marker = "extra == 'ui'", specifier = "==2.0.0" },
|
||||||
{ name = "pyqt6", marker = "extra == 'mpl'", specifier = ">=6.9.0" },
|
{ name = "numpy", specifier = "==2.2.4" },
|
||||||
{ name = "qiskit", extras = ["visualization"], specifier = ">=2.0.0" },
|
{ name = "pyqt6", specifier = "==6.9.0" },
|
||||||
{ name = "qiskit-aer", specifier = ">=0.17.0" },
|
{ name = "qiskit", extras = ["visualization"], specifier = "==2.0.0" },
|
||||||
|
{ name = "qiskit-aer", specifier = "==0.17.0" },
|
||||||
]
|
]
|
||||||
provides-extras = ["mpl"]
|
provides-extras = ["ui"]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "mypy", specifier = ">=1.15.0" },
|
{ name = "mypy", specifier = "~=1.15" },
|
||||||
{ name = "ruff", specifier = ">=0.11.4" },
|
{ name = "ruff", specifier = "~=0.11" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user