Skip to content

Commit fba01a9

Browse files
Add GPU backend selection with load_cuda(), load_metal(), load_amdgpu(), load_oneapi() (#23)
* Add GPU backend selection functionality and example * Fix linting errors * Fix linting errors * Simplify example, updated README to have gpu selection * Fix linting errors * Install GPU backends on first use --------- Co-authored-by: Carlos Castillo Passi <cacp@stanford.edu>
1 parent dc06f99 commit fba01a9

4 files changed

Lines changed: 87 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ minutes. Subsequent runs are fast.
3737
import komamripy as km
3838
import numpy as np
3939

40+
km.load_cuda() # GPU backend selection, optional
41+
4042
sys = km.Scanner() # scanner hardware
4143
obj = km.brain_phantom2D() # 2D brain phantom
4244
seq = km.PulseDesigner.EPI_example() # example EPI sequence

examples/gpu_backend_selection.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""Simulate MRI acquisition with optional GPU backend acceleration.
2+
3+
This example demonstrates GPU backend selection. Uncomment one of the backend
4+
load functions to enable GPU acceleration (CUDA, Metal, AMDGPU, or oneAPI).
5+
Without a backend, simulation runs on CPU automatically.
6+
"""
7+
8+
import numpy as np
9+
10+
import komamripy as km
11+
12+
# Optional: Load a GPU backend to enable GPU acceleration
13+
# Uncomment ONE of these (backend must be installed):
14+
# km.load_cuda() # NVIDIA GPUs
15+
# km.load_metal() # Apple Silicon
16+
# km.load_amdgpu() # AMD GPUs
17+
# km.load_oneapi() # Intel GPUs (experimental)
18+
19+
# Define acquisition inputs
20+
sys = km.Scanner()
21+
obj = km.brain_phantom2D()
22+
seq = km.PulseDesigner.EPI_example()
23+
24+
# Simulate with KomaMRI (gpu=true by default if backend loaded)
25+
sim_params = {"return_type": "mat"}
26+
signal = km.simulate(obj, seq, sys, sim_params=sim_params)
27+
signal = np.asarray(signal).reshape(-1)

src/komamripy/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
Julia::
88
99
using KomaMRI
10+
using CUDA # Load GPU backend
11+
1012
sys = Scanner()
1113
obj = brain_phantom2D()
1214
seq = PulseDesigner.EPI_example()
@@ -18,6 +20,7 @@
1820
import komamripy as km
1921
import numpy as np
2022
23+
km.load_cuda() # Load GPU backend
2124
sys = km.Scanner()
2225
obj = km.brain_phantom2D()
2326
seq = km.PulseDesigner.EPI_example()
@@ -26,10 +29,21 @@
2629
2730
Simulation results are returned as Julia objects; use ``numpy.asarray`` to
2831
convert array-like results (such as a ``"mat"`` signal) into NumPy arrays.
32+
33+
GPU backends can be loaded with:
34+
- km.load_cuda() # NVIDIA GPUs
35+
- km.load_metal() # Apple Silicon
36+
- km.load_amdgpu() # AMD GPUs
37+
- km.load_oneapi() # Intel GPUs (experimental)
38+
39+
Once a backend is loaded, gpu=true is used by default in simulations.
2940
"""
3041

42+
from ._backends import load_amdgpu, load_cuda, load_metal, load_oneapi
3143
from ._session import get_julia
3244

45+
__all__ = ["load_cuda", "load_metal", "load_amdgpu", "load_oneapi"]
46+
3347
_JULIA_MODULE_ALIASES = {
3448
"base": "KomaMRIBase",
3549
"core": "KomaMRICore",

src/komamripy/_backends.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""GPU backend selection for komamripy.
2+
3+
Maps Python functions to Julia backend loading:
4+
- load_cuda() → using CUDA
5+
- load_metal() → using Metal
6+
- load_amdgpu() → using AMDGPU
7+
- load_oneapi() → using oneAPI
8+
9+
Once a backend is loaded, KomaMRI.jl automatically uses it with gpu=true by default.
10+
"""
11+
12+
from ._session import get_julia
13+
14+
15+
def load_cuda() -> None:
16+
"""Load CUDA backend: using CUDA"""
17+
jl = get_julia()
18+
if jl.Base.find_package("CUDA") is None:
19+
jl.seval('import Pkg; Pkg.add("CUDA")')
20+
jl.seval("using CUDA")
21+
22+
23+
def load_metal() -> None:
24+
"""Load Metal backend: using Metal"""
25+
jl = get_julia()
26+
if jl.Base.find_package("Metal") is None:
27+
jl.seval('import Pkg; Pkg.add("Metal")')
28+
jl.seval("using Metal")
29+
30+
31+
def load_amdgpu() -> None:
32+
"""Load AMDGPU backend: using AMDGPU"""
33+
jl = get_julia()
34+
if jl.Base.find_package("AMDGPU") is None:
35+
jl.seval('import Pkg; Pkg.add("AMDGPU")')
36+
jl.seval("using AMDGPU")
37+
38+
39+
def load_oneapi() -> None:
40+
"""Load oneAPI backend: using oneAPI (experimental)"""
41+
jl = get_julia()
42+
if jl.Base.find_package("oneAPI") is None:
43+
jl.seval('import Pkg; Pkg.add("oneAPI")')
44+
jl.seval("using oneAPI")

0 commit comments

Comments
 (0)