Skip to content

Add optional CuPy backend with auto-detection#36

Open
savowe wants to merge 37 commits into
masterfrom
heisenborg/cupy-backend
Open

Add optional CuPy backend with auto-detection#36
savowe wants to merge 37 commits into
masterfrom
heisenborg/cupy-backend

Conversation

@savowe

@savowe savowe commented Feb 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an optional cupy backend implementation and register it in backend discovery
  • support backend="cupy" / backend="auto" selection paths for Wavefunction
  • add CuPy-focused tests (with graceful skip when CuPy is unavailable)
  • add NumPy↔CuPy regression parity test for backend accuracy checks
  • add a backend benchmark utility (benchmarks/backend_benchmark.py) with optional speedup gating and JSON output
  • harden GPU CI to manual dispatch + actor gate, and add benchmark summary/artifact publishing

Notes

  • no API break: NumPy remains default
  • CuPy path is optional and only activated when requested/available
  • GPU workflow now uses the protected gpu-ci environment (RUNPOD_API_KEY, RUNPOD_SSH_PRIVATE_KEY expected there)

Testing

  • python -m pytest tests/ -v (19 passed, 5 skipped)
  • python benchmarks/backend_benchmark.py --size 64 --steps 5 --repeats 1 --warmup 0

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if name not in _BACKENDS:
available = sorted(_BACKENDS.keys())
raise ValueError(f"Unknown backend '{name}'. Available: {available}")
return _BACKENDS[name](**kwargs)

P1 Badge Reject unusable CuPy backend when selected explicitly

has_cupy() defines CuPy availability as getDeviceCount() > 0, but get_backend("cupy") only checks whether the class was imported, so environments with CuPy installed but no visible GPU (e.g., CPU-only runners with cupy-cuda12x present) still construct CupyBackend and fail later during array ops with CUDA runtime errors. This should fail fast in backend selection (consistent with has_cupy()) instead of deferring to a harder-to-diagnose runtime crash.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pytalises/backends/__init__.py Outdated

try: # optional dependency
from .cupy_backend import CupyBackend
except Exception: # pragma: no cover - depends on optional runtime

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Narrow CuPy import fallback to dependency errors only

Catching Exception while importing cupy_backend masks real implementation bugs (not just missing optional deps) by silently removing the backend from _BACKENDS; once that happens, has_cupy() returns False immediately and CuPy tests guarded by skipif(not has_cupy()) are skipped, allowing broken CuPy backend code to pass CI undetected. Restricting this fallback to import/dependency failures would preserve fail-fast behavior for genuine regressions.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d17701a800

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

except Exception: # pragma: no cover - depends on optional runtime
CupyBackend = None
else:
_BACKENDS["cupy"] = CupyBackend

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate CuPy backend selection on actual device availability

Registering cupy in _BACKENDS immediately after module import means get_backend("cupy") succeeds even when has_cupy() is False (e.g., CuPy installed but no visible CUDA device), so users only see a CUDA runtime failure later on first array allocation instead of a clear selection-time error. This also conflicts with the new availability contract used in tests/backend_test.py and will break environments that have CuPy wheels installed on CPU-only runners.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6b20a31ba4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


import numpy as np

import pytalises as pt

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fix direct benchmark script import path

Running the benchmark exactly as documented (python benchmarks/backend_benchmark.py ...) fails on a fresh checkout because direct script execution sets sys.path to benchmarks/, so pytalises is not importable unless the package was already installed. This makes the new benchmark utility unusable in the common local workflow and contradicts the README invocation.

Useful? React with 👍 / 👎.

Comment thread benchmarks/backend_benchmark.py Outdated
args.json_out.parent.mkdir(parents=True, exist_ok=True)
args.json_out.write_text(json.dumps(payload, indent=2), encoding="utf-8")

if args.min_speedup is not None and speedup is not None and speedup < args.min_speedup:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce --min-speedup when CuPy timings are unavailable

The --min-speedup gate only triggers when speedup is not None, so if CuPy is unavailable the script exits successfully even though a minimum speedup was explicitly requested. That silently turns a required performance gate into a no-op in misconfigured environments (e.g., missing GPU/CuPy), which can let CI pass without validating the intended benchmark condition.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8676183f06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread README.md Outdated
dt=1e-6,
variables={"Omega": 2.0, "Delta": 1.0},
options=pt.PropagationOptions(threads=4),
options=pt.PropagationOptions(backend="cupy", threads=4),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep quickstart backend consistent with created wavefunction

The quickstart now passes PropagationOptions(backend="cupy") while constructing Wavefunction(...) with the default backend ("auto"), so on normal CPU-only installs the wavefunction resolves to NumPy and psi.propagate(...) immediately raises the backend-mismatch ValueError from Propagator. This makes the primary README example fail in the common non-CUDA environment even though CuPy is described as optional.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d67fd22e3f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +69 to +70
mean_seconds=float(np.mean(arr)),
std_seconds=float(np.std(arr)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate repeat count before computing benchmark stats

When --repeats is 0 (or any input combination that leaves timings empty), np.mean/np.std produce NaN values and the script still exits successfully, which can silently bypass performance gating (e.g., --min-speedup check compares against NaN and does not fail). I reproduced this with python -m benchmarks.backend_benchmark --repeats 0 --warmup 0 --min-speedup 2, which returned exit code 0 while reporting invalid timings.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 317b9fad62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/gpu-ci.yml Outdated
Comment on lines +274 to +275
git fetch --depth 1 origin ${GITHUB_SHA}
git checkout --detach ${GITHUB_SHA}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor workflow ref input when checking out pod code

This step ignores the user-provided workflow_dispatch ref input and always tests ${GITHUB_SHA} in the RunPod VM. If someone dispatches the workflow with a non-empty ref (branch/tag/SHA), the local checkout uses that ref but the pod executes tests on a different revision, which can produce a false green result for code that was never actually validated.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@savowe

savowe commented Feb 18, 2026

Copy link
Copy Markdown
Owner Author

RunPod benchmark follow-up (RTX A4500, CuPy 13.6.0) after CI hardening ✅

Workflow runs:

Headline speedups (NumPy/CuPy):

  • free workload:
    • n=8192: 1.05x
    • n=16384: 3.23x
    • n=32768: 6.52x
    • n=65536: 10.28x
    • n=131072: 12.57x
  • potential workload:
    • n=2048: 1.20x
    • n=4096: 1.21x
    • n=8192: 1.23x
    • n=16384: 1.32x
    • n=32768: 1.33x
    • n=65536: 1.33x
    • n=131072: 1.39x

Parity remains tight across sweeps (max abs diff ~1e-14 for density and occupation).

Takeaway: small 1D grids are overhead-bound on GPU, but larger grids show clear CuPy gains, including strong wins on free propagation at very large sizes.

Heisenborg added 13 commits February 18, 2026 15:51
- New gpu_backend.rst covering installation, backend selection,
  performance characteristics, precision control, and troubleshooting
- Add to index.rst table of contents
- Update v2_migration.rst with link to GPU docs
- Add .readthedocs.yaml for reproducible RTD builds
- Update docs/requirements.txt with all necessary deps
  (sphinx, sphinx-rtd-theme, nbsphinx, pypandoc_binary)
- Suppress ambiguous cross-reference warnings in conf.py
  (expected due to legacy module duplicating class names)
- Tested: sphinx-build -W passes with no warnings
- Add animated diffraction grating GIF as hero visual
- Simplify quickstart to 5 lines
- Add comparison table vs QuTiP
- Collapse detailed examples into <details> blocks
- Add citation bibtex block
- Fix badges (removed broken workflow, added GPU badge)
- Include animation generation scripts in docs/assets/
- Longer propagation (200 frames, more time steps)
- Taller grid extent to show diffracted orders separating
- Higher initial momentum for clearer diffraction angles
- Remove QuTiP comparison (not really comparable tools)
- List specific physics domains where pyTALISES excels
- Cleaner, more positive framing
Repository owner deleted a comment from chatgpt-codex-connector Bot Feb 24, 2026
Repository owner deleted a comment from chatgpt-codex-connector Bot Feb 24, 2026
@savowe

savowe commented Feb 25, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Repository owner deleted a comment from chatgpt-codex-connector Bot Feb 25, 2026
Repository owner deleted a comment from chatgpt-codex-connector Bot Feb 25, 2026
Repository owner deleted a comment from chatgpt-codex-connector Bot Feb 25, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ba6772bad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +74 to +75
safe_builtins = {"__import__": __import__}
return eval(expr, {"__builtins__": safe_builtins}, scope)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove unrestricted imports from CuPy expression evaluation

The CuPy evaluator intentionally exposes __import__ and then runs raw eval on user-provided expressions, which means any expression string can execute arbitrary Python (for example through imported modules) when backend="cupy"; this is a real code-execution risk whenever expressions come from configs, notebooks, or other non-hardcoded inputs, and it is materially less safe than the NumPy/numexpr path.

Useful? React with 👍 / 👎.


return GpuEngine(backend)

raise ValueError(f"No engine registered for backend '{backend.name}'.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Support registered backends in engine creation

register_backend remains part of the public API, but create_engine now hard-fails for any backend whose name is not exactly "numpy" or "cupy"; as a result, a backend that can be successfully registered and instantiated cannot be used by Wavefunction anymore because initialization now unconditionally calls this factory.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant