Running on a GPU

Everything in the earlier examples runs on the CPU by default. This example samples the same g g > t t~ g mapping from the first example on a CUDA device instead. The mapping itself does not change. Only the Context it runs against, and the array library used to hand it data, are different: on a GPU, arrays are PyTorch tensors rather than NumPy arrays, since MadSpace interfaces with whichever array library the caller uses.

This requires a MadSpace build with the CUDA backend enabled (--cuda to madspace/install.py; the pre-built wheels for Linux include it). The same pattern works for HIP, using hip_device in place of cuda_device.

Selecting the device

cuda_device raises if the CUDA backend was not built into the installation:

import torch
import madspace as ms

device = ms.cuda_device()
context = ms.Context(device=device, thread_count=32)

Building a runtime bound to the context

mapping.map_forward is a CPU-only convenience, so a specific device needs the underlying Function and FunctionRuntime explicitly:

masses = [0.0, 0.0, 173.0, 173.0, 0.0]  # g g -> t t~ g
mapping = ms.PhaseSpaceMapping(masses, 13000.0, mode="rambo")

forward = ms.FunctionRuntime(mapping.forward_function(), context)

Sampling with PyTorch

Calling the runtime with a CUDA tensor keeps the whole computation on the device. The outputs come back in the same order as map_forward uses, just as a plain tuple rather than a namedtuple:

r = torch.rand((100000, mapping.random_dim()), device="cuda", dtype=torch.float64)
momenta, x1, x2, det = forward(r)
print(momenta.shape, momenta.device)
torch.Size([100000, 5, 4]) cuda:0

momenta, x1, x2 and det are ordinary CUDA tensors. Move them to the host with momenta.cpu() once the computation that needs them, such as a matrix-element evaluation, is also done.