Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions demos/grover_evolve/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""Grover Search variant of the quantum-evolve GA demo.

Evolves a 4-qubit Grover's search state machine (target |1010>, 2 iterations)
using the cuquantum GPU backend for verification.

All GA machinery lives in demos/quantum_evolve/demo.py — this module just
overrides the design goal and backend, then delegates to main() there.

Usage:
python demos/grover_evolve/demo.py
python demos/grover_evolve/demo.py --backend qutip --population 5 --generations 4
"""

from __future__ import annotations

import sys
import textwrap
from pathlib import Path

# Make the sibling quantum_evolve package importable
sys.path.insert(0, str(Path(__file__).parent.parent))

import quantum_evolve.demo as _demo # noqa: E402

# ── Override goal and backend before main() reads the globals ─────────────────

_GROVER_GOAL = textwrap.dedent("""\
Design a quantum state machine that implements Grover's search algorithm
on 4 qubits, searching for the target state |1010>.

Requirements:
1. Four qubits in context: q0, q1, q2, q3
2. Initialization phase: apply Hadamard to all 4 qubits to create uniform superposition
3. Oracle phase: mark the target |1010> by flipping its phase (apply a
multi-controlled Z (or equivalent CNOT+H decomposition) that introduces
a -1 phase on |1010> and leaves all other amplitudes unchanged)
4. Diffusion phase: apply the Grover diffusion operator
(H^4 · (2|0><0| - I) · H^4) to amplify the target amplitude
5. Run 2 full Grover iterations (oracle + diffusion each time)
6. Measurement phase: measure all 4 qubits; the outcome |1010> should have
high probability (~97%)
7. Include at least 2 collapse branches in the measurement with probability guards
8. Mark the post-measurement states as [final]

State names must be valid identifiers (letters, digits, underscores only —
no Dirac ket notation in state names).
Include verification rules: unitarity, entanglement, no_cloning.""")

_demo.DEFAULT_DESIGN_GOAL = _GROVER_GOAL
_demo.DESIGN_GOAL = _GROVER_GOAL
_demo.BACKEND = "cuquantum"

# ── Entry point ───────────────────────────────────────────────────────────────

if __name__ == "__main__":
# _parse_args() reads sys.argv so --population, --generations etc. still work
_demo.asyncio.run(_demo.main())
41 changes: 40 additions & 1 deletion demos/quantum_evolve/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def _panel(title: str, content: str, color: str = C.BCYAN, width: int = 62):
FITNESS_TARGET = 99.0
MUTATION_RATE = 0.5
ELITISM = 1
BACKEND = "cuquantum" # set to "qutip" to disable GPU, or "none" to skip dynamic

DEFAULT_DESIGN_GOAL = textwrap.dedent("""\
Design a quantum state machine that implements the 3-qubit bit-flip
Expand Down Expand Up @@ -276,7 +277,36 @@ def _verify_source(source: str) -> tuple[bool, list[str]]:
if not parsed.file.machines:
return False, ["No machine definition found"]
machine = parsed.file.machines[0]
result = verify(machine, VerifyOptions(skip_completeness=True, skip_dynamic=True))

skip_dynamic = BACKEND == "none"
opts = VerifyOptions(
skip_completeness=True,
skip_dynamic=skip_dynamic,
backend=BACKEND if not skip_dynamic else "qutip",
)

gpu_before = 0
if BACKEND == "cuquantum" and not skip_dynamic:
try:
import cupy as cp
pool = cp.get_default_memory_pool()
gpu_before = pool.total_bytes()
except ImportError:
pass

result = verify(machine, opts)

if BACKEND == "cuquantum" and not skip_dynamic:
try:
import cupy as cp
cp.cuda.Stream.null.synchronize()
gpu_after = cp.get_default_memory_pool().total_bytes()
delta = gpu_after - gpu_before
if delta > 0:
print(f" {C.DIM}↳ GPU Δmem: +{delta:,} bytes{C.RESET}", flush=True)
except ImportError:
pass

errors = [f"{e.code}: {e.message}" for e in result.errors if e.severity == "error"]
return result.valid, errors
except Exception as e:
Expand Down Expand Up @@ -820,6 +850,7 @@ async def main():
_get_provider()
config = load_config()
_info(f"LLM provider: {C.WHITE}{config.provider} / {config.model}{C.RESET}")
_info(f"Verify backend: {C.WHITE}{BACKEND}{C.RESET}")
_info(f"Population: {C.WHITE}{POPULATION_SIZE}{C.RESET} "
f"Max generations: {C.WHITE}{MAX_GENERATIONS}{C.RESET}")
_info(f"Fitness target: {C.WHITE}{FITNESS_TARGET}{C.RESET}")
Expand Down Expand Up @@ -948,6 +979,12 @@ def _parse_args():
"--fitness-target", type=float, default=None,
help=f"Fitness target to converge (default: {FITNESS_TARGET})",
)
parser.add_argument(
"--backend", type=str, default=None,
choices=["cuquantum", "qutip", "none"],
help="Verification backend: cuquantum (GPU), qutip (CPU), none (skip dynamic). "
f"Default: {BACKEND}",
)
return parser.parse_args()


Expand All @@ -965,5 +1002,7 @@ def _parse_args():
MAX_GENERATIONS = args.generations
if args.fitness_target is not None:
FITNESS_TARGET = args.fitness_target
if args.backend is not None:
BACKEND = args.backend

asyncio.run(main())
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ Source = "https://github.com/jascal/q-orca-lang"

[project.optional-dependencies]
dev = ["pytest", "ruff"]
quantum = ["qiskit", "qutip"]
quantum = ["qiskit", "qutip", "qutip-qip"]
mcp = ["pyyaml"]
all = ["qiskit", "qutip", "pyyaml"]
all = ["qiskit", "qutip", "qutip-qip", "pyyaml"]

[project.scripts]
q-orca = "q_orca.cli:main"
Expand Down
5 changes: 2 additions & 3 deletions q_orca/backends/cuquantum_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from q_orca.ast import QMachineDef
from q_orca.backends.base import BackendAdapter, BackendResult, BackendUnavailableError
from q_orca.verifier.dynamic import dynamic_verify_gpu
from q_orca.verifier.types import QVerificationResult

# Detect availability at module load time
Expand Down Expand Up @@ -48,9 +49,7 @@ def verify(
"qutip-cuquantum is not installed. "
"Install with: pip install qutip-cuquantum"
)
# When actually available, delegate to QuTiP with cuQuantum provider
from q_orca.verifier.dynamic import dynamic_verify
result = dynamic_verify(machine)
result = dynamic_verify_gpu(machine)
backend_result = BackendResult(
name=self.name,
version=self.version,
Expand Down
Loading