Skip to content

Commit f6e0153

Browse files
committed
update cuda deps, move to KernelAbstractions
1 parent 061d4f8 commit f6e0153

25 files changed

Lines changed: 160 additions & 121 deletions

.github/workflows/CI.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
name: CI
22
on:
3-
- push
4-
- pull_request
3+
push:
4+
branches: [master]
5+
pull_request:
6+
types: [opened, synchronize, reopened]
57
jobs:
68
test:
79
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
@@ -10,11 +12,10 @@ jobs:
1012
fail-fast: false
1113
matrix:
1214
version:
13-
- '1.7'
14-
- '1.8'
15+
- '1.11'
16+
- '1.12'
1517
os:
1618
- ubuntu-latest
17-
- macOS-latest
1819
arch:
1920
- x64
2021
steps:

Project.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
name = "MatrixEnsembles"
22
uuid = "c7015dd7-3fb7-4a4c-827e-526313618491"
33
authors = ["Łukasz Pawela <lukasz.pawela@gmail.com>"]
4-
version = "0.1.4"
4+
version = "0.2.0"
55

66
[deps]
7-
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
87
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
8+
KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c"
99
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1010
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
11-
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1211
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
1312

13+
[weakdeps]
14+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
15+
16+
[extensions]
17+
MatrixEnsemblesCUDAExt = "CUDA"
18+
1419
[compat]
20+
CUDA = "5"
1521
DocStringExtensions = "0.8.3, 0.9"
16-
CUDA = "4"
17-
Requires = "1.1"
22+
KernelAbstractions = "0.9"
1823
StatsBase = "0.33.2, 0.34"
19-
julia = "1.4, 1.5"
24+
julia = "1.9"
2025

2126
[extras]
2227
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
28+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
2329

2430
[targets]
25-
test = ["Test"]
31+
test = ["Test", "CUDA"]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## Sampling random matrices on the GPU
66

7-
We have introduced an experimental implementation of sampling of random matrices and random quantum objects on the GPU. In order to use this feature, the `CUDA` package is required. To import `MatrixEnsembles` with GPU support use
7+
We have introduced an experimental implementation of sampling of random matrices and random quantum objects on the GPU using `KernelAbstractions.jl`. In order to use this feature, the `CUDA` package is required to load the necessary extension. To import `MatrixEnsembles` with GPU support use
88
```julia
99
using CUDA, MatrixEnsembles
1010
```

docs/Project.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[deps]
2+
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
3+
MatrixEnsembles = "c7015dd7-3fb7-4a4c-827e-526313618491"
4+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
5+
6+
[compat]
7+
Documenter = "1"
8+
MatrixEnsembles = "0.1, 0.2"
9+
CUDA = "5"

docs/make.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ using Documenter, MatrixEnsembles
22

33
format = Documenter.HTML(edit_link = "master",
44
prettyurls = get(ENV, "CI", nothing) == "true",
5+
assets = ["assets/favicon.ico"]
56
)
67

78
makedocs(
89
clean = true,
910
format = format,
1011
sitename = "MatrixEnsembles.jl",
1112
authors = "Łukasz Pawela",
12-
assets = ["assets/favicon.ico"],
1313
pages = [
1414
"Home" => "index.md",
1515
"Manual" => Any[
@@ -20,8 +20,8 @@ makedocs(
2020
]
2121
)
2222

23-
deploydocs(
24-
deps = Deps.pip("pygments", "mkdocs", "python-markdown-math"),
25-
target = "build",
26-
repo = "github.com/iitis/MatrixEnsembles.jl.git"
27-
)
23+
# deploydocs(
24+
# deps = Deps.pip("pygments", "mkdocs", "python-markdown-math"),
25+
# target = "build",
26+
# repo = "github.com/iitis/MatrixEnsembles.jl.git"
27+
# )

docs/src/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ states,
2020
* manipulating them with quantum channels
2121
* calculating functionals on these objects, *i.e. trace norm, diamond norm, entropy, fidelity*,
2222
* application of random matrix theory in quantum
23-
information processing.
23+
information processing,
24+
* random matrix sampling on GPU.
2425

2526
## [References](@id refs)
2627

docs/src/man/random.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,24 @@ o*o'
159159
```
160160
For convenience we provide the following type aliases `const COE = CircularEnsemble{1}`, `const CUE = CircularEnsemble{2}`, `const CSE = CircularEnsemble{4}`.
161161

162+
## GPU Sampling
163+
164+
`MatrixEnsembles.jl` supports sampling random matrices directly on the GPU using `CUDA.jl`. This functionality is provided via a package extension that is loaded automatically when `CUDA` is imported.
165+
166+
To use GPU sampling:
167+
```julia
168+
using CUDA
169+
using MatrixEnsembles
170+
171+
# Create an ensemble (e.g., Circular Unitary Ensemble)
172+
c = CUE(1024)
173+
174+
# Sample directly to a CuArray
175+
u = MatrixEnsembles.curand(c)
176+
```
177+
178+
The `curand` function mimics `rand` but uses the `CUDA.default_rng()` and optimized kernels (implemented with `KernelAbstractions.jl`) to generate matrices directly on the device, minimizing host-device data transfer.
179+
162180
## [References](@id refs_rand)
163181

164182
[1] B. Collins, I. Nechita, *Random matrix techniques in quantum information theory*, Journal of Mathematical Physics, 2016;57(1):015215.

ext/MatrixEnsemblesCUDAExt.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module MatrixEnsemblesCUDAExt
2+
3+
using MatrixEnsembles
4+
using CUDA
5+
using Random
6+
7+
import MatrixEnsembles: curand
8+
9+
function MatrixEnsembles.curand(d::QIContinuousMatrixDistribution)
10+
rand(CUDA.default_rng(), d)
11+
end
12+
13+
function __init__()
14+
CUDA.allowscalar(false)
15+
end
16+
17+
end

src/MatrixEnsembles.jl

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,27 @@
11
module MatrixEnsembles
22
using LinearAlgebra
3+
using KernelAbstractions
34
import Base: rand, size
45
using Random: GLOBAL_RNG, AbstractRNG
5-
using Requires
6-
76

87
export rand, size, QIContinuousMatrixDistribution
98
export curand
9+
1010
abstract type QIContinuousMatrixDistribution; end
1111

1212
rand(c::QIContinuousMatrixDistribution) = rand(GLOBAL_RNG, c)
1313

14+
"""
15+
curand(dist)
16+
17+
Generate a random sample from `dist` using the default CUDA RNG.
18+
Requires `CUDA.jl` to be loaded.
19+
"""
20+
function curand end
21+
1422
include("ginibre.jl")
1523
include("circular.jl")
1624
include("wigner.jl")
1725
include("wishart.jl")
1826

19-
function __init__()
20-
@require CUDA="052768ef-5323-5732-b1bb-66c8b64840ba" begin
21-
if CUDA.functional()
22-
const CuArray = CUDA.CuArray
23-
const CuVector = CUDA.CuVector
24-
const CuMatrix = CUDA.CuMatrix
25-
const CuSVD = CUDA.CUSOLVER.CuSVD
26-
const CuQR = CUDA.CUSOLVER.CuQR
27-
28-
CUDA.allowscalar(false)
29-
include("cuda/circular.jl")
30-
include("cuda/ginibre.jl")
31-
include("cuda/wigner.jl")
32-
include("cuda/wishart.jl")
33-
end
34-
end
35-
end
3627
end

src/circular.jl

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,50 @@ const COE = CircularEnsemble{1}
1515
const CUE = CircularEnsemble{2}
1616
const CSE = CircularEnsemble{4}
1717

18+
@kernel function identity_kernel!(A)
19+
I, J = @index(Global, NTuple)
20+
A[I, J] = I == J ? 1 : 0
21+
end
22+
23+
@kernel function symplectic_kernel!(A)
24+
I, J = @index(Global, NTuple)
25+
# Check if we are in the same 2x2 block diagonal
26+
ki = (I - 1) ÷ 2
27+
kj = (J - 1) ÷ 2
28+
29+
if ki == kj
30+
r = (I - 1) % 2
31+
c = (J - 1) % 2
32+
33+
if r == 0 && c == 1
34+
A[I, J] = -1
35+
elseif r == 1 && c == 0
36+
A[I, J] = 1
37+
else
38+
A[I, J] = 0
39+
end
40+
else
41+
A[I, J] = 0
42+
end
43+
end
44+
1845
function _qr_fix!(z::AbstractMatrix)
1946
q, r = qr!(z)
2047
d = diag(r)
2148
ph = d./abs.(d)
2249
idim = size(r, 1)
23-
q = Matrix(q)[:, 1:idim]
24-
q = transpose(ph) .* q
50+
51+
# Generic densification: Use kernel to create identity
52+
m = size(q, 1)
53+
dest = similar(z, m, idim)
54+
55+
backend = KernelAbstractions.get_backend(dest)
56+
kernel = identity_kernel!(backend)
57+
kernel(dest, ndrange=size(dest))
58+
59+
q_dense = q * dest
60+
61+
transpose(ph) .* q_dense
2562
end
2663

2764
function _qr_fix(z::AbstractMatrix)
@@ -44,7 +81,12 @@ end
4481
function rand(rng::AbstractRNG, c::CSE)
4582
z = rand(rng, c.g)
4683
u = _qr_fix!(z)
47-
ur = cat([[0 -1; 1 0] for _=1:c.d÷2]..., dims=[1,2])
84+
85+
ur = similar(z, eltype(z), c.d, c.d)
86+
backend = KernelAbstractions.get_backend(ur)
87+
kernel = symplectic_kernel!(backend)
88+
kernel(ur, ndrange=size(ur))
89+
4890
ur*u*ur'*transpose(u)
4991
end
5092

0 commit comments

Comments
 (0)