Skip to content

Commit 6f91d15

Browse files
Krastanov-agentClaudeclaudeKrastanov
authored
Add GPU tests for CUDA, AMDGPU, and OpenCL backends (#205)
* Add CLAUDE.md documentation file Adds comprehensive documentation for Claude Code users including: - Project structure overview - Development commands for testing and documentation - Key dependencies and version requirements - Contributing guidelines 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Add code formatting guidelines to CLAUDE.md Includes: - Commands to remove trailing whitespaces from .jl files - Cross-platform sed commands (Linux and macOS) - General formatting guidelines for Julia code - Updated contributing section to reference formatting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Add GPU tests for CUDA, AMDGPU, and OpenCL backends Implements comprehensive GPU testing infrastructure: - GPU test structure based on QuantumClifford.jl patterns - Support for CUDA, AMDGPU (ROCm), and OpenCL backends - Basic operations testing: operators, kets, bras - Matrix operations: multiplication, adjoint, trace, norm - Test filtering by environment variables (CUDA_TEST, AMDGPU_TEST, OpenCL_TEST) - Buildkite CI configuration for automated GPU testing - Uses Adapt.jl for seamless CPU-GPU array adaptation Tests verified working locally with OpenCL backend. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Update CLAUDE.md with master version and add pull requirement - Uses CLAUDE.md content from master branch to resolve conflicts - Adds requirement to pull latest changes before starting new features - Emphasizes keeping repository up to date in contributing guidelines 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Improve test setup and add branch pull requirement to CLAUDE.md - Add Pkg to test/Project.toml for proper dependency management - Remove manual TestItemRunner addition (already in test environment) - Update CLAUDE.md with branch pull requirement - Recommend using Pkg.test() over direct runtests.jl calls - Add examples for GPU-specific test commands - All 61 OpenCL tests passing with new setup 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Remove incorrect alternative test command from CLAUDE.md The direct runtests.jl approach was incorrect and shouldn't be recommended. Only Pkg.test() provides the proper test environment. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Add newline formatting guidelines and fix missing newlines - Add section in CLAUDE.md for ensuring files end with newlines - Provide sed command to add missing newlines: `sed -i '$a\' *.jl` - Apply the command to fix existing files missing newlines - Update formatting guidelines to emphasize proper file endings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * manual changes * Revert formatting-only changes and add PR focus guideline Remove newline additions from 28 test files that were not part of the GPU tests feature. Add guideline to CLAUDE.md about keeping PRs focused and avoiding mixing feature work with formatting changes to unrelated files. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * minor clarifications * comment out the AMDGPU tests --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: Stefan Krastanov <[email protected]> Co-authored-by: Stefan Krastanov <[email protected]>
1 parent 1c54e8a commit 6f91d15

File tree

12 files changed

+375
-2
lines changed

12 files changed

+375
-2
lines changed

.buildkite/pipeline.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
env:
2+
CODECOV_TOKEN: adb3f22a-231a-4f7b-8ed4-7c6c56453cbe
3+
JULIA_NUM_THREADS: auto
4+
PYTHON: ""
5+
PYCALL_DEBUG_BUILD: yes
6+
7+
steps:
8+
- label: "Package Tests"
9+
plugins:
10+
- JuliaCI/julia#v1:
11+
version: "1"
12+
- QuantumSavory/julia-xvfb#v1:
13+
- JuliaCI/julia-test#v1: ~
14+
- JuliaCI/julia-coverage#v1:
15+
codecov: true
16+
command:
17+
- echo "Julia depot path $${JULIA_DEPOT_PATH}"
18+
- julia --project='~' -e '
19+
using Pkg;
20+
pkg"dev .";'
21+
22+
- label: "JET Tests"
23+
plugins:
24+
- JuliaCI/julia#v1:
25+
version: "1"
26+
- QuantumSavory/julia-xvfb#v1:
27+
- JuliaCI/julia-test#v1: ~
28+
- JuliaCI/julia-coverage#v1:
29+
codecov: true
30+
env:
31+
JET_TEST: true
32+
command:
33+
- echo "Julia depot path $${JULIA_DEPOT_PATH}"
34+
- julia --project='~' -e '
35+
using Pkg;
36+
pkg"dev .";'
37+
38+
- label: "CUDA Tests"
39+
agents:
40+
queue: cuda
41+
plugins:
42+
- JuliaCI/julia#v1:
43+
version: "1"
44+
- JuliaCI/julia-test#v1: ~
45+
- JuliaCI/julia-coverage#v1:
46+
codecov: true
47+
env:
48+
CUDA_TEST: true
49+
command:
50+
- echo "Julia depot path $${JULIA_DEPOT_PATH}"
51+
- julia --project='~' -e '
52+
using Pkg;
53+
pkg"dev .";'
54+
55+
# - label: "AMDGPU Tests"
56+
# agents:
57+
# queue: rocm
58+
# plugins:
59+
# - JuliaCI/julia#v1:
60+
# version: "1"
61+
# - JuliaCI/julia-test#v1: ~
62+
# - JuliaCI/julia-coverage#v1:
63+
# codecov: true
64+
# env:
65+
# AMDGPU_TEST: true
66+
# command:
67+
# - echo "Julia depot path $${JULIA_DEPOT_PATH}"
68+
# - julia --project='~' -e '
69+
# using Pkg;
70+
# pkg"dev .";'
71+
72+
- label: "OpenCL Tests"
73+
plugins:
74+
- JuliaCI/julia#v1:
75+
version: "1"
76+
- QuantumSavory/julia-xvfb#v1:
77+
- JuliaCI/julia-test#v1: ~
78+
- JuliaCI/julia-coverage#v1:
79+
codecov: true
80+
env:
81+
OpenCL_TEST: true
82+
command:
83+
- echo "Julia depot path $${JULIA_DEPOT_PATH}"
84+
- julia --project='~' -e '
85+
using Pkg;
86+
pkg"dev .";'
87+
88+
- label: "Downstream QuantumOptics"
89+
plugins:
90+
- JuliaCI/julia#v1:
91+
version: "1"
92+
- QuantumSavory/julia-xvfb#v1:
93+
command:
94+
- echo "Julia depot path $${JULIA_DEPOT_PATH}"
95+
- julia --project=$(mktemp -d) -e '
96+
using Pkg;
97+
pkg"dev .";
98+
Pkg.add("QuantumOptics");
99+
Pkg.build("QuantumOptics");
100+
Pkg.test("QuantumOptics");'

CLAUDE.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ QuantumOpticsBase.jl provides the base functionality for QuantumOptics.jl. It im
2121
```bash
2222
# Run all tests
2323
julia --project=. -e "using Pkg; Pkg.test()"
24+
25+
# Run with only specific GPU backend tests
26+
CUDA_TEST=true julia --project=. -e "using Pkg; Pkg.test()"
27+
AMDGPU_TEST=true julia --project=. -e "using Pkg; Pkg.test()"
28+
OpenCL_TEST=true julia --project=. -e "using Pkg; Pkg.test()"
2429
```
2530

2631
### Building Documentation
@@ -84,13 +89,21 @@ Special test configurations:
8489
## Code Formatting
8590

8691
### Removing Trailing Whitespaces
87-
Before committing, ensure there are no trailing whitespaces in Julia files:
92+
Before committing, ensure there are no trailing whitespaces in Julia files. Do not format files that are not part of the specific feature under development.
8893

8994
```bash
9095
# Remove trailing whitespaces from all .jl files (requires gnu tools)
9196
find . -type f -name '*.jl' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
9297
```
9398

99+
### Ensuring Files End with Newlines
100+
Ensure all Julia files end with a newline to avoid misbehaving CLI tools. Do not format files that are not part of the specific feature under development.
101+
102+
```bash
103+
# Add newline to end of all .jl files that don't have one
104+
find . -type f -name '*.jl' -exec sed -i '$a\' {} \+
105+
```
106+
94107
### General Formatting Guidelines
95108
- Use 4 spaces for indentation (no tabs)
96109
- Remove trailing whitespaces from all lines
@@ -101,8 +114,13 @@ find . -type f -name '*.jl' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
101114
## Contributing
102115

103116
This package follows standard Julia development practices:
117+
- **Always pull latest changes first**: Before creating any new feature or starting work, ensure you have the latest version by running `git pull origin master` (or `git pull origin main`)
118+
- **Pull before continuing work**: Other maintainers might have modified the branch you are working on. Always call `git pull` before continuing work on an existing branch
119+
- **Push changes to remote**: Always push your local changes to the remote branch to keep the PR up to date: `git push origin <branch-name>`
120+
- **Run all tests before submitting**: Before creating or updating a PR, always run the full test suite to ensure nothing is broken: `julia --project=. -e "using Pkg; Pkg.test()"`
104121
- Fork and create feature branches
105122
- Write tests for new functionality
106123
- Ensure documentation builds successfully
107124
- Follow code formatting guidelines above
108-
- All tests must pass before merging
125+
- All tests must pass before merging
126+
- **Keep PRs focused**: A PR should implement one self-contained change. Avoid mixing feature work with formatting changes to unrelated files, even for improvements like adding missing newlines. Format unrelated files in separate commits or PRs.

test/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
77
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
88
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
99
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
10+
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
1011
QuantumInterface = "5717a53b-5d69-4fa3-b976-0bf2f97ca1e5"
1112
QuantumOptics = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c"
1213
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Test parameters
2+
const test_sizes = [2, 4, 8]
3+
const max_rows = 16
4+
const round_count = 2
5+
const block_sizes = fill(64, round_count)
6+
const batch_sizes = fill(1, round_count)
7+
8+
# Helper function for distance comparison
9+
D(op1::AbstractOperator, op2::AbstractOperator) = abs(tracedistance_nh(dense(op1), dense(op2)))
10+
D(x1::StateVector, x2::StateVector) = norm(x2-x1)
11+
12+
# Test tolerances for GPU computations
13+
const GPU_TOL = 1e-10

test/gpu/implementation/imports.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# GPU backend imports
2+
import Adapt
3+
using QuantumOpticsBase
4+
using LinearAlgebra, Random, Test
5+
using SparseArrays
6+
7+
# For memory management in GPU tests
8+
using GPUArrays: AllocCache, @cached, unsafe_free!
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
function test_basic_operations(AT, synchronize)
2+
"""Test basic quantum operations on GPU arrays."""
3+
cache = AllocCache()
4+
5+
for n in test_sizes
6+
for r in 1:round_count
7+
@cached cache begin
8+
9+
@testset "Basic Operations - Size $n" begin
10+
# Create test bases
11+
b1 = GenericBasis(n)
12+
b2 = GenericBasis(n)
13+
14+
# Test operator creation and adaptation
15+
cpu_op, gpu_op = create_test_operator(b1, b2, AT)
16+
17+
@test typeof(gpu_op.data) <: AT
18+
@test verify_gpu_result(cpu_op, gpu_op)
19+
20+
# Test ket creation and adaptation
21+
cpu_ket, gpu_ket = create_test_ket(b1, AT)
22+
23+
@test typeof(gpu_ket.data) <: AT
24+
@test verify_gpu_result(cpu_ket, gpu_ket)
25+
26+
# Test bra creation and adaptation
27+
cpu_bra, gpu_bra = create_test_bra(b1, AT)
28+
29+
@test typeof(gpu_bra.data) <: AT
30+
@test verify_gpu_result(cpu_bra, gpu_bra)
31+
32+
# Test operator multiplication (both on GPU)
33+
cpu_op2, gpu_op2 = create_test_operator(b2, b1, AT)
34+
35+
cpu_result = cpu_op * cpu_op2
36+
gpu_result = gpu_op * gpu_op2
37+
synchronize()
38+
39+
@test verify_gpu_result(cpu_result, gpu_result)
40+
41+
# Test adjoint/dagger operation
42+
cpu_dag = dagger(cpu_op)
43+
gpu_dag = dagger(gpu_op)
44+
synchronize()
45+
46+
@test verify_gpu_result(cpu_dag, gpu_dag)
47+
48+
# Test trace operation
49+
if n == length(b2) # Square matrices only
50+
cpu_trace = tr(cpu_op)
51+
gpu_trace = tr(gpu_op)
52+
synchronize()
53+
54+
@test isapprox(cpu_trace, gpu_trace, atol=GPU_TOL)
55+
end
56+
57+
# Test norm operations
58+
cpu_norm = norm(cpu_ket)
59+
gpu_norm = norm(gpu_ket)
60+
synchronize()
61+
62+
@test isapprox(cpu_norm, gpu_norm, atol=GPU_TOL)
63+
end
64+
65+
end
66+
end
67+
end
68+
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
include("imports.jl")
2+
include("definitions.jl")
3+
include("utilities.jl")
4+
include("test_basic_operations.jl")
5+
6+
@inline function test_platform(AT, synchronize)
7+
@testset "Basic Operations" begin
8+
test_basic_operations(AT, synchronize)
9+
end
10+
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Utility functions for GPU testing
2+
3+
function create_test_operator(basis_l, basis_r, AT)
4+
"""Create a test operator and adapt it to the specified array type."""
5+
data = rand(ComplexF64, length(basis_l), length(basis_r))
6+
cpu_op = DenseOperator(basis_l, basis_r, data)
7+
gpu_op = Adapt.adapt(AT, cpu_op)
8+
return cpu_op, gpu_op
9+
end
10+
11+
function create_test_ket(basis, AT)
12+
"""Create a test ket and adapt it to the specified array type."""
13+
data = rand(ComplexF64, length(basis))
14+
normalize!(data)
15+
cpu_ket = Ket(basis, data)
16+
gpu_data = Adapt.adapt(AT, data)
17+
gpu_ket = Ket(basis, gpu_data)
18+
return cpu_ket, gpu_ket
19+
end
20+
21+
function create_test_bra(basis, AT)
22+
"""Create a test bra and adapt it to the specified array type."""
23+
data = rand(ComplexF64, length(basis))
24+
normalize!(data)
25+
cpu_bra = Bra(basis, data)
26+
gpu_data = Adapt.adapt(AT, data)
27+
gpu_bra = Bra(basis, gpu_data)
28+
return cpu_bra, gpu_bra
29+
end
30+
31+
function verify_gpu_result(cpu_result, gpu_result, tolerance=GPU_TOL)
32+
"""Verify that GPU computation matches CPU result within tolerance."""
33+
cpu_data = cpu_result isa AbstractOperator ? cpu_result.data : cpu_result.data
34+
gpu_data_cpu = Array(gpu_result isa AbstractOperator ? gpu_result.data : gpu_result.data)
35+
return isapprox(cpu_data, gpu_data_cpu, atol=tolerance)
36+
end

test/gpu/test_platform_AMDGPU.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@testitem "AMDGPU" tags = [:amdgpu] begin
2+
3+
include("implementation/test_platform.jl")
4+
5+
using AMDGPU: ROCArray, AMDGPU
6+
const AT = ROCArray
7+
8+
const can_run = AMDGPU.functional()
9+
10+
@testset "Device availability" begin
11+
@test can_run
12+
end
13+
14+
if can_run
15+
synchronize() = AMDGPU.synchronize()
16+
test_platform(AT, synchronize)
17+
else
18+
@info "Skipping AMDGPU tests - AMDGPU not functional"
19+
end
20+
21+
end

test/gpu/test_platform_CUDA.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@testitem "CUDA" tags = [:cuda] begin
2+
3+
include("implementation/test_platform.jl")
4+
5+
using CUDA: CuArray, CUDA
6+
const AT = CuArray
7+
8+
const can_run = CUDA.functional()
9+
10+
@testset "Device availability" begin
11+
@test can_run
12+
end
13+
14+
if can_run
15+
synchronize() = CUDA.synchronize()
16+
test_platform(AT, synchronize)
17+
else
18+
@info "Skipping CUDA tests - CUDA not functional"
19+
end
20+
21+
end

0 commit comments

Comments
 (0)