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
7 changes: 5 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,11 @@ sync-deps:
# the toolkit is installed AND an NVIDIA GPU is present. Pure Rust users
# and machines without a GPU skip this -- mirrors `pecos python build`.
if {{pecos}} cuda check -q 2>/dev/null && nvidia-smi -L 2>/dev/null | grep -q "^GPU "; then
echo "CUDA toolkit + NVIDIA GPU detected -- including CUDA Python packages"
SYNC_ARGS+=(--group cuda)
# Pick the cuda group matching the CUDA major (default cuda13).
CUDA_GROUP=cuda13
if nvcc --version 2>/dev/null | grep -qE "release 12[.,]"; then CUDA_GROUP=cuda12; fi
echo "CUDA toolkit + NVIDIA GPU detected -- including CUDA Python packages ($CUDA_GROUP)"
SYNC_ARGS+=(--group "$CUDA_GROUP")
fi
uv sync "${SYNC_ARGS[@]}"

Expand Down
21 changes: 16 additions & 5 deletions crates/pecos-cli/src/cli/cuda_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ fn run_validate(path: Option<String>) -> Result<()> {
}

/// CLI entry point for `pecos cuda setup-python`. Validates the toolkit is
/// present, then runs `uv sync --locked --group cuda` and prints next-step hints.
/// present, then runs `uv sync --locked --group <cuda12|cuda13>` (matching the
/// detected CUDA major) and prints next-step hints.
fn run_setup_python() -> Result<()> {
if find_cuda().is_none() {
eprintln!("Error: CUDA toolkit not found.");
Expand All @@ -239,17 +240,27 @@ fn run_setup_python() -> Result<()> {
Ok(())
}

/// Run `uv sync --locked --group cuda` to install Python CUDA packages.
/// Pick the CUDA-major dependency group (`cuda12` / `cuda13`) for the detected
/// toolkit, defaulting to `cuda13` (the recommended, latest path).
pub(super) fn cuda_python_group() -> &'static str {
let is_cuda12 = find_cuda()
.and_then(|path| get_cuda_version(&path).ok())
.is_some_and(|version| version.split('.').next() == Some("12"));
if is_cuda12 { "cuda12" } else { "cuda13" }
}

/// Run `uv sync --locked --group <cuda12|cuda13>` to install Python CUDA packages.
///
/// Reusable from other CLI commands (e.g. `pecos setup`) once they've already
/// confirmed the user wants this. Does NOT validate toolkit presence -- caller
/// is responsible for that check.
pub(super) fn install_cuda_python_packages() -> Result<()> {
println!("Installing CUDA Python packages (cupy, cuquantum, pytket-cutensornet)...");
let group = cuda_python_group();
println!("Installing CUDA Python packages (cupy, cuquantum, pytket-cutensornet) [{group}]...");
println!();

let status = Command::new("uv")
.args(["sync", "--locked", "--group", "cuda"])
.args(["sync", "--locked", "--group", group])
.status();

match status {
Expand All @@ -263,7 +274,7 @@ pub(super) fn install_cuda_python_packages() -> Result<()> {
eprintln!("Failed to install CUDA Python packages.");
eprintln!();
eprintln!("You may need to install manually:");
eprintln!(" uv sync --locked --group cuda");
eprintln!(" uv sync --locked --group {group}");
Err(Error::Cuda(
"Failed to install CUDA Python packages".to_string(),
))
Expand Down
4 changes: 2 additions & 2 deletions crates/pecos-cli/src/cli/setup_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ fn print_status_summary(skip_llvm: bool, skip_cuda: bool, skip_cmake: bool) {
if super::cuda_cmd::cuda_python_packages_installed() {
println!(" cupy: installed (CUDA Python packages synced)");
} else {
println!(" cupy: not installed (~500 MB via `uv sync --locked --group cuda`)");
println!(" cupy: not installed (~500 MB via `pecos cuda setup-python`)");
}
}

Expand Down Expand Up @@ -353,7 +353,7 @@ fn setup_cuda_python(mode: PromptMode) -> Result<()> {
}

if confirm(
"Install CUDA Python packages? (cupy, cuquantum, pytket-cutensornet via `uv sync --locked --group cuda`)",
"Install CUDA Python packages? (cupy, cuquantum, pytket-cutensornet via `pecos cuda setup-python`)",
true, // default yes when CUDA toolkit + NVIDIA GPU are present
mode,
) {
Expand Down
6 changes: 4 additions & 2 deletions docs/development/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ For developers who want to contribute or modify PECOS:
|---------------|-------------------------------------------------------------|-------------------------------|
| `examples` | Running notebooks under `examples/` or DataFrame benchmarks | `uv sync --group examples` |
| `numpy-compat`| Verifying older NumPy/SciPy minimums | `uv sync --group numpy-compat`|
| `cuda` | Building/running GPU simulators (requires CUDA toolkit) | `uv sync --group cuda` |
| `cuda13` | GPU simulators on CUDA 13 (Turing/CC 7.5+; requires CUDA toolkit) | `uv sync --group cuda13` |
| `cuda12` | GPU simulators on CUDA 12 (incl. V100/Volta) | `uv sync --group cuda12` |

Combine groups with multiple `--group` flags
(e.g. `uv sync --group examples --group cuda`).
(e.g. `uv sync --group examples --group cuda13`). Pick one CUDA major --
`cuda12` and `cuda13` are mutually exclusive.

6. **LLVM 14 Setup (Required for LLVM IR/QIS Support)**

Expand Down
80 changes: 48 additions & 32 deletions docs/user-guide/cuda-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ Both approaches require:

### Hardware Requirements

- **NVIDIA GPU** with Compute Capability 7.0 or higher
- **NVIDIA GPU** with Compute Capability **7.5 or higher** (Turing and newer) for the
default CUDA 13 path. Current cuStateVec (cuQuantum >= 25.09) and CUDA 13 dropped
Volta, so **V100 / CC 7.0** is supported only via the CUDA 12 path with an older,
Volta-capable cuQuantum pinned `cuquantum-python-cu12>=25.3,<25.9` (see below).
- To check your GPU: `nvidia-smi`
- To check compute capability: Visit [NVIDIA's GPU Compute Capability List](https://developer.nvidia.com/cuda-gpus)

Expand Down Expand Up @@ -119,22 +122,27 @@ If `nvcc` is not found, ensure CUDA's bin directory is in your PATH.
PECOS uses `uv` as the package manager. Install the CUDA-related Python packages:

```bash
# Install CUDA 13 packages
uv pip install cupy-cuda13x>=13.0.0
uv pip install cuquantum-python-cu13>=25.3.0
uv pip install pytket-cutensornet>=0.12.0
# CUDA 13 (Turing / CC 7.5+ GPUs):
uv pip install "cupy-cuda13x>=13.0.0" "cuquantum-python-cu13>=25.9.0" "pytket-cutensornet>=0.12.0"

# CUDA 12:
uv pip install "cupy-cuda12x>=13.0.0" "cuquantum-python-cu12>=25.3.0" "pytket-cutensornet>=0.12.0"
```

**Important**: Use packages matching your CUDA version:
- For CUDA 13: `cupy-cuda13x`, `cuquantum-python-cu13`
- For CUDA 12: `cupy-cuda12x`, `cuquantum-python-cu12`
**Important**:
- Install packages matching your CUDA major (CUDA 13 -> `*-cu13` / `*-cuda13x`; CUDA 12 -> `*-cu12` / `*-cuda12x`). Do **not** install both.
- PECOS requires the **`cuquantum.bindings` API (cuQuantum >= 25.03)**; older cuQuantum is rejected with a clear error when you use `CuStateVec`. The lowest CUDA 13 wheel is `25.9.0`.
- **V100 / Volta (CC 7.0)**: pin `cuquantum-python-cu12>=25.3,<25.9` (cuStateVec dropped Volta at 25.09).

### Step 5: Install PECOS with CUDA Support

#### Option A: Install from PyPI with CUDA extras

```bash
uv pip install quantum-pecos[cuda]
# CUDA 13 (recommended); use [cuda12] for CUDA 12. These pull the Python cuStateVec
# / MPS stack (CuPy + cuquantum-python). The Rust GPU backend is a separate install
# (see "Rust cuQuantum Bindings Setup" below): pip install pecos-rslib-cuda
uv pip install "quantum-pecos[cuda13]"
```

#### Option B: Install from source (for development)
Expand All @@ -148,8 +156,8 @@ just build-cuda # Build with CUDA support
just devc # Full dev cycle: clean + build-cuda + test
just devcl # Dev cycle + linting

# Option 2: Manual installation
uv pip install -e "./python/quantum-pecos[all,cuda]"
# Option 2: Manual installation (use [all,cuda12] for CUDA 12)
uv pip install -e "./python/quantum-pecos[all,cuda13]"
```

## Verification
Expand Down Expand Up @@ -203,14 +211,15 @@ uv run pytest python/quantum-pecos/tests/pecos/integration/state_sim_tests/test_

## Package Versions

Current recommended versions (as of 2025):
Minimum versions (CUDA 13 path shown; for CUDA 12 use the `-cu12` / `-cuda12x` equivalents):

| Package | Minimum | Purpose |
|---------|---------|---------|
| cupy-cuda13x | 13.0.0 | NumPy/SciPy for GPU |
| cuquantum-python-cu13 | 25.9.0 | cuQuantum Python API (the `cuquantum.bindings >= 25.03` surface; lowest cu13 wheel is 25.9.0) |
| pytket-cutensornet | 0.12.0 | MPS simulator |

| Package | Version | Release Date | Purpose |
|---------|---------|--------------|---------|
| cupy-cuda13x | 13.6.0+ | Aug 2025 | NumPy/SciPy for GPU |
| cuquantum-python-cu13 | 25.9.0+ | Sept 2025 | cuQuantum Python API |
| custatevec-cu13 | 1.10.0+ | Sept 2025 | State vector operations (included in cuquantum) |
| pytket-cutensornet | 0.12.0+ | 2025 | MPS simulator |
For CUDA 12 the floor is `cuquantum-python-cu12>=25.3.0`; **V100/Volta** needs `>=25.3,<25.9`.

## Troubleshooting

Expand All @@ -226,20 +235,27 @@ export LD_LIBRARY_PATH=/usr/local/cuda-13/lib64:$LD_LIBRARY_PATH
source ~/.bashrc
```

#### 2. `CuStateVec is None` or tests are skipped
#### 2. `CuStateVec` unavailable / constructing it raises

**Solution**: Python packages not properly installed or CUDA Toolkit version mismatch.
`import pecos` always works, but constructing `CuStateVec` (or
`QuantumSimulator("CuStateVec")`) without CuPy and a bindings-era cuQuantum raises
an actionable error naming what to install/upgrade. PECOS requires the
`cuquantum.bindings` API (cuQuantum >= 25.03); the legacy top-level
`cuquantum.custatevec` is not used.

```bash
# Verify installations
python -c "import cupy; print(cupy.__version__)"
python -c "from cuquantum import custatevec; print('OK')"
# Ask PECOS directly (no GPU needed):
python -c "from pecos.simulators.custatevec._cuquantum_compat import custatevec_available, custatevec_unavailable_reason; print(custatevec_available(), '|', custatevec_unavailable_reason())"

# Reinstall if needed
uv pip uninstall cupy-cuda13x cuquantum-python-cu13
uv pip install cupy-cuda13x cuquantum-python-cu13
# Or check the underlying packages:
python -c "import cupy; print(cupy.__version__)"
python -c "from cuquantum.bindings import custatevec; print('OK')"
```

If the reason says cuQuantum "predates the cuquantum.bindings API", upgrade to
`cuquantum-python-cu13>=25.9.0` (CUDA 13) or `cuquantum-python-cu12>=25.3.0`
(CUDA 12; V100/Volta `>=25.3,<25.9`).

#### 3. CUDA version mismatch errors

**Problem**: Mixing CUDA 12 and CUDA 13 packages.
Expand Down Expand Up @@ -281,7 +297,7 @@ If you encounter issues:
1. Check [NVIDIA cuQuantum Documentation](https://docs.nvidia.com/cuda/cuquantum/latest/)
2. Check [pytket-cutensornet GitHub Issues](https://github.com/Quantinuum/pytket-cutensornet/issues)
3. Check [PECOS GitHub Issues](https://github.com/PECOS-packages/PECOS/issues)
4. Verify your GPU compute capability is 7.0 or higher
4. Verify your GPU compute capability is 7.5+ (CUDA 13) or 7.0 (V100 via the capped CUDA 12 path)

## Alternative: Using Conda

Expand Down Expand Up @@ -451,12 +467,12 @@ To use GPU simulators in PECOS:

### Option A: Python cuQuantum Bindings (Easier Setup)

1. **Verify NVIDIA GPU** (Compute Capability 7.0+)
2. **Install CUDA Toolkit 13** (system-level)
3. **Install Python packages**: `cupy-cuda13x`, `cuquantum-python-cu13`, `pytket-cutensornet`
4. **Install PECOS with `[cuda]` extras**:
1. **Verify NVIDIA GPU** (Compute Capability 7.5+ for CUDA 13; V100/CC 7.0 only via the capped CUDA 12 path)
2. **Install CUDA Toolkit 13** (system-level; or CUDA 12)
3. **Install Python packages**: `cupy-cuda13x`, `cuquantum-python-cu13`, `pytket-cutensornet` (or the `-cu12`/`-cuda12x` equivalents)
4. **Install PECOS with the matching CUDA extra**:
```bash
uv pip install quantum-pecos[cuda]
uv pip install "quantum-pecos[cuda13]" # or [cuda12] for CUDA 12
# or for development:
just build-cuda
```
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/qec-guppy.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ results = (
.quantum(state_vector())
.noise(depolarizing_noise().with_uniform_probability(0.001))
.seed(42)
.run(1000)
.run(100)
)
```

Expand Down
3 changes: 2 additions & 1 deletion docs/user-guide/simulators.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ results = sim(Qasm(circuit)).quantum(CuStateVec).run(100)
- cuQuantum and cupy packages

```bash
pip install quantum-pecos[cuda]
# CUDA 13 (recommended); use [cuda12] for CUDA 12 / V100. Do not install both.
pip install "quantum-pecos[cuda13]"
```

See [CUDA Setup Guide](cuda-setup.md) for detailed installation instructions.
Expand Down
22 changes: 17 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ requires-python = ">=3.10"
dependencies = []

[project.optional-dependencies]
# Keep this in sync with the cuda dependency group below. The extra supports
# `uv sync --extra cuda`; the group supports `uv run --group cuda ...`.
cuda = ["quantum-pecos[cuda]==0.8.0.dev8"]
# Keep these in sync with the cuda dependency groups below. The extras support
# `uv sync --extra cuda13`; the groups support `uv run --group cuda13 ...`.
# Pick the extra matching your CUDA major -- do not install both.
cuda13 = ["quantum-pecos[cuda13]==0.8.0.dev8"]
cuda12 = ["quantum-pecos[cuda12]==0.8.0.dev8"]

[tool.uv.workspace]
members = [
Expand Down Expand Up @@ -67,12 +69,22 @@ numpy-compat = [ # NumPy/SciPy compatibility tests - verify compatibility with s
"numpy>=1.15.0",
"scipy>=1.1.0",
]
cuda = [ # CUDA Python packages for GPU-accelerated simulations (requires CUDA toolkit)
"quantum-pecos[cuda]==0.8.0.dev8",
cuda13 = [ # CUDA 13 Python packages for GPU-accelerated simulations (requires CUDA toolkit)
"quantum-pecos[cuda13]==0.8.0.dev8",
]
cuda12 = [ # CUDA 12 Python packages for GPU-accelerated simulations
"quantum-pecos[cuda12]==0.8.0.dev8",
]

[tool.uv]
default-groups = ["dev", "test"]
# CUDA 12 and CUDA 13 stacks are mutually exclusive (separate cupy/cuquantum
# wheels per CUDA major). Declare them conflicting so the lock never co-resolves
# both -- co-resolution downgrades cu12 to satisfy cu13.
conflicts = [
[{ extra = "cuda12" }, { extra = "cuda13" }],
[{ group = "cuda12" }, { group = "cuda13" }],
]
# Prevent uv from caching native workspace wheels - always use freshly built versions.
# This fixes stale code issues when uv sync/run reinstalls cached old wheels.
# See: https://github.com/astral-sh/uv/issues/11390
Expand Down
3 changes: 2 additions & 1 deletion python/quantum-pecos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ Certain simulators from `pecos.simulators` require external packages that are no

**Quick install** (after installing CUDA Toolkit):
```bash
uv pip install quantum-pecos[cuda]
# CUDA 13 (recommended); use [cuda12] for CUDA 12 / V100. Do not install both.
uv pip install "quantum-pecos[cuda13]"

# For development with CUDA support:
make build-cuda # Build with CUDA
Expand Down
33 changes: 22 additions & 11 deletions python/quantum-pecos/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,37 @@ all = [
"quantum-pecos[stim]",
]

# CUDA dependencies. See docs/user-guide/cuda-setup.md for detailed installation instructions
# Requires CUDA Toolkit 13 or 12 installed at system level first.
# For Ubuntu/Pop!_OS: sudo apt install cuda-toolkit-13
# These packages work with uv/pip once CUDA toolkit is installed.
# Note: CUDA packages require Python 3.11+ due to cuquantum-python-cu13 requirements
cuda = [
"pecos-rslib-cuda==0.8.0.dev8",
"cupy-cuda13x>=13.0.0; python_version >= '3.11'", # Use cupy-cuda12x for CUDA 12
"cuquantum-python-cu13>=25.3.0; python_version >= '3.11'", # Use cuquantum-python-cu12 for CUDA 12
# CUDA dependencies for the Python cuStateVec / MPS backends. See
# docs/user-guide/cuda-setup.md. Requires a system CUDA toolkit and a matching NVIDIA
# driver. Pick the extra matching your CUDA major -- do NOT install both (mixing
# cupy-cudaXXx wheels is unsupported). Python 3.11+ required.
# uv pip install -e .[cuda13] # CUDA 13, Turing/CC 7.5+ GPUs (recommended)
# uv pip install -e .[cuda12] # CUDA 12
# V100/Volta (CC 7.0): cuStateVec dropped Volta at 25.09, so pin
# cuquantum-python-cu12>=25.3,<25.9 manually (see cuda-setup.md).
# The Rust GPU backend (CudaStateVec/CudaStabilizer) is a separate install:
# pip install pecos-rslib-cuda
cuda13 = [
"cupy-cuda13x>=13.0.0; python_version >= '3.11'",
"cuquantum-python-cu13>=25.9.0; python_version >= '3.11'",
"pytket-cutensornet>=0.12.0; python_version >= '3.11'",
]
cuda12 = [
"cupy-cuda12x>=13.0.0; python_version >= '3.11'",
"cuquantum-python-cu12>=25.3.0; python_version >= '3.11'",
"pytket-cutensornet>=0.12.0; python_version >= '3.11'",
]
# Install with: uv pip install -e .[cuda]

# Dependency groups are defined at workspace root (pyproject.toml)
# Package-specific groups can be added here if needed

[tool.uv]
# CUDA 12 and CUDA 13 use different, mutually exclusive cupy/cuquantum wheels.
conflicts = [[{ extra = "cuda12" }, { extra = "cuda13" }]]

[tool.uv.sources]
pecos-rslib = { workspace = true }
pecos-rslib-llvm = { workspace = true }
pecos-rslib-cuda = { workspace = true }

[tool.hatch.build.targets.wheel]
packages = ["src/pecos"]
Expand Down
11 changes: 4 additions & 7 deletions python/quantum-pecos/src/pecos/simulators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,11 @@
)
from pecos.simulators.statevec import StateVec

# Attempt to import optional cuquantum and cupy packages (Python cuQuantum bindings)
# Python cuQuantum CuStateVec backend. Import always succeeds if the package is
# present; CuPy / cuQuantum availability is checked at construction time (like the
# Rust CudaStateVec below), so users get a clear error only when they use it.
try:
import cupy
import cuquantum

from pecos.simulators.custatevec.state import (
CuStateVec,
)
from pecos.simulators.custatevec.state import CuStateVec
except ImportError:
CuStateVec = None
Comment on lines +43 to 49

Expand Down
Loading
Loading