Import NVIDIA/warp from GitHub.
PiperOrigin-RevId: 892794388 Change-Id: Ic681071dc68bfaecaaab4e131c1c464dca99f9b5
This commit is contained in:
committed by
Copybara-Service
parent
1640d6a4bc
commit
0b54638df4
@@ -16,8 +16,8 @@ jax-cuda12-pjrt==0.5.3; python_version >= '3.10' \
|
|||||||
jax-cuda12-pjrt==0.4.30; python_version == '3.9' \
|
jax-cuda12-pjrt==0.4.30; python_version == '3.9' \
|
||||||
--hash=sha256:895d0198ad99638fcaf976c47592e2a543eef79ea15fabd24a402d055390c328 \
|
--hash=sha256:895d0198ad99638fcaf976c47592e2a543eef79ea15fabd24a402d055390c328 \
|
||||||
--hash=sha256:c36fb1e0c236563bf3a87e70f4d1ab28a31d7cf5d722c9ede30c4172116e8bcb
|
--hash=sha256:c36fb1e0c236563bf3a87e70f4d1ab28a31d7cf5d722c9ede30c4172116e8bcb
|
||||||
warp-lang==1.11.1 \
|
warp-lang==1.12.0 \
|
||||||
--hash=sha256:1ad11f1fa775269e991a3d55039152c8a504baf86701c849b485cb8e66c49d15 \
|
--hash=sha256:c78c3701d5cad86c30ef5017410d294ec46a396bb0d502ee1c98743494f3a62f \
|
||||||
--hash=sha256:8b098f41e71d421d80ee7562e38aa8380ff6b0d3b4c6ee866cfbdef733ac5bdc \
|
--hash=sha256:a1436f60a1881cd94f787e751a83fc0987626be2d3e2b4e74c64a6947c6d1266 \
|
||||||
--hash=sha256:5d0904b0eefcc81f39ba65375427a3de99006088aa43e24a9011263f07d0cd07 \
|
--hash=sha256:a2d6decba693aba5b828573c4414fd6a3f4c4a934db9c322736ef2b3fa99fe76 \
|
||||||
--hash=sha256:15dc10aa51fb0fdbe1ca16d52e5fadca35a47ffd9d0c636826506f96bb2e7c41
|
--hash=sha256:697248edd2f1e2952f50e3db33b214af76173641a8894aacc467bed6dc247f8a
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from functools import reduce
|
|||||||
import warp as wp
|
import warp as wp
|
||||||
from warp._src.context import type_str
|
from warp._src.context import type_str
|
||||||
from warp._src.jax import get_jax_device
|
from warp._src.jax import get_jax_device
|
||||||
from warp._src.types import array_t, launch_bounds_t, strides_from_shape
|
from warp._src.types import array_t, launch_bounds_t, matches_array_class, strides_from_shape
|
||||||
from warp._src.utils import warn
|
from warp._src.utils import warn
|
||||||
|
|
||||||
_wp_module_name_ = "warp.jax_experimental.custom_call"
|
_wp_module_name_ = "warp.jax_experimental.custom_call"
|
||||||
@@ -340,7 +340,7 @@ def _create_jax_warp_primitive():
|
|||||||
wtype = warg.type
|
wtype = warg.type
|
||||||
rtt = ir.RankedTensorType(actual.type)
|
rtt = ir.RankedTensorType(actual.type)
|
||||||
|
|
||||||
if not isinstance(wtype, wp.array):
|
if not matches_array_class(wtype, wp.array):
|
||||||
raise Exception("Only contiguous arrays are supported for Jax kernel arguments")
|
raise Exception("Only contiguous arrays are supported for Jax kernel arguments")
|
||||||
|
|
||||||
if not base_type_is_compatible(wtype.dtype, rtt.element_type):
|
if not base_type_is_compatible(wtype.dtype, rtt.element_type):
|
||||||
@@ -364,7 +364,7 @@ def _create_jax_warp_primitive():
|
|||||||
for warg in wp_kernel.adj.args[len(args) :]:
|
for warg in wp_kernel.adj.args[len(args) :]:
|
||||||
wtype = warg.type
|
wtype = warg.type
|
||||||
|
|
||||||
if not isinstance(wtype, wp.array):
|
if not matches_array_class(wtype, wp.array):
|
||||||
raise Exception("Only contiguous arrays are supported for Jax kernel arguments")
|
raise Exception("Only contiguous arrays are supported for Jax kernel arguments")
|
||||||
|
|
||||||
# Infer dimensions from the first input.
|
# Infer dimensions from the first input.
|
||||||
|
|||||||
+143
-35
@@ -29,19 +29,23 @@ import warp as wp
|
|||||||
from warp._src.codegen import get_full_arg_spec, make_full_qualified_name
|
from warp._src.codegen import get_full_arg_spec, make_full_qualified_name
|
||||||
from warp._src.context import CudaMemcpyKind
|
from warp._src.context import CudaMemcpyKind
|
||||||
from warp._src.jax import get_jax_device
|
from warp._src.jax import get_jax_device
|
||||||
from warp._src.types import array_t, launch_bounds_t, strides_from_shape, type_size_in_bytes, type_to_warp
|
from warp._src.types import (
|
||||||
|
array_t,
|
||||||
|
launch_bounds_t,
|
||||||
|
matches_array_class,
|
||||||
|
strides_from_shape,
|
||||||
|
type_size_in_bytes,
|
||||||
|
type_to_warp,
|
||||||
|
)
|
||||||
|
|
||||||
from .xla_ffi import *
|
from .xla_ffi import *
|
||||||
|
|
||||||
_wp_module_name_ = "warp.jax_experimental.ffi"
|
_wp_module_name_ = "warp.jax_experimental.ffi"
|
||||||
|
|
||||||
# Type alias for differentiable kernel cache key
|
|
||||||
DiffKernelCacheKey = tuple[Callable, tuple, int, str, tuple[str, ...]]
|
|
||||||
|
|
||||||
# Holders for the custom callbacks to keep them alive.
|
# Holders for the custom callbacks to keep them alive.
|
||||||
_FFI_KERNEL_REGISTRY: dict[str, FfiKernel] = {}
|
_FFI_KERNEL_REGISTRY: dict[tuple, FfiKernel] = {}
|
||||||
_FFI_DIFF_KERNEL_REGISTRY: dict[DiffKernelCacheKey, Callable] = {}
|
_FFI_DIFF_KERNEL_REGISTRY: dict[tuple, Callable] = {}
|
||||||
_FFI_CALLABLE_REGISTRY: dict[str, FfiCallable] = {}
|
_FFI_CALLABLE_REGISTRY: dict[tuple, FfiCallable] = {}
|
||||||
_FFI_CALLBACK_REGISTRY: dict[str, ctypes.CFUNCTYPE] = {}
|
_FFI_CALLBACK_REGISTRY: dict[str, ctypes.CFUNCTYPE] = {}
|
||||||
_FFI_REGISTRY_LOCK = threading.Lock()
|
_FFI_REGISTRY_LOCK = threading.Lock()
|
||||||
|
|
||||||
@@ -61,6 +65,21 @@ def check_jax_version():
|
|||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def collapse_batch_dims(shape, desired_ndim):
|
||||||
|
# roll leading batch dims into one
|
||||||
|
while len(shape) > desired_ndim:
|
||||||
|
shape = (shape[0] * shape[1], *shape[2:])
|
||||||
|
return shape
|
||||||
|
|
||||||
|
|
||||||
|
def compute_batch_size(shape, batch_ndim):
|
||||||
|
# compute product of batch dims at front
|
||||||
|
batch_size = 1
|
||||||
|
for i in range(batch_ndim):
|
||||||
|
batch_size *= shape[i]
|
||||||
|
return batch_size
|
||||||
|
|
||||||
|
|
||||||
class GraphMode(IntEnum):
|
class GraphMode(IntEnum):
|
||||||
"""CUDA graph capture modes for :func:`warp.jax_experimental.jax_callable`.
|
"""CUDA graph capture modes for :func:`warp.jax_experimental.jax_callable`.
|
||||||
|
|
||||||
@@ -91,7 +110,7 @@ class FfiArg:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.type = type
|
self.type = type
|
||||||
self.in_out = in_out
|
self.in_out = in_out
|
||||||
self.is_array = isinstance(type, wp.array)
|
self.is_array = matches_array_class(type, wp.array)
|
||||||
|
|
||||||
if self.is_array:
|
if self.is_array:
|
||||||
if hasattr(type.dtype, "_wp_scalar_type_"):
|
if hasattr(type.dtype, "_wp_scalar_type_"):
|
||||||
@@ -125,7 +144,15 @@ class FfiLaunchDesc:
|
|||||||
|
|
||||||
class FfiKernel:
|
class FfiKernel:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, kernel, num_outputs, vmap_method, launch_dims, output_dims, in_out_argnames, module_preload_mode
|
self,
|
||||||
|
kernel,
|
||||||
|
num_outputs,
|
||||||
|
vmap_method,
|
||||||
|
launch_dims,
|
||||||
|
output_dims,
|
||||||
|
in_out_argnames,
|
||||||
|
module_preload_mode,
|
||||||
|
has_side_effect=False,
|
||||||
):
|
):
|
||||||
self.kernel = kernel
|
self.kernel = kernel
|
||||||
self.name = generate_unique_name(kernel.func)
|
self.name = generate_unique_name(kernel.func)
|
||||||
@@ -134,6 +161,7 @@ class FfiKernel:
|
|||||||
self.launch_dims = launch_dims
|
self.launch_dims = launch_dims
|
||||||
self.output_dims = output_dims
|
self.output_dims = output_dims
|
||||||
self.module_preload_mode = module_preload_mode
|
self.module_preload_mode = module_preload_mode
|
||||||
|
self.has_side_effect = has_side_effect
|
||||||
self.first_array_arg = None
|
self.first_array_arg = None
|
||||||
self.launch_id = 0
|
self.launch_id = 0
|
||||||
self.launch_descriptors = {}
|
self.launch_descriptors = {}
|
||||||
@@ -250,7 +278,8 @@ class FfiKernel:
|
|||||||
out_types.append(get_jax_output_type(input_arg, input_value.shape))
|
out_types.append(get_jax_output_type(input_arg, input_value.shape))
|
||||||
|
|
||||||
# launch dimensions
|
# launch dimensions
|
||||||
if launch_dims is None:
|
infer_launch_dims = launch_dims is None
|
||||||
|
if infer_launch_dims:
|
||||||
# use the shape of the first input array
|
# use the shape of the first input array
|
||||||
if self.first_array_arg is not None:
|
if self.first_array_arg is not None:
|
||||||
launch_dims = get_warp_shape(self.input_args[self.first_array_arg], args[self.first_array_arg].shape)
|
launch_dims = get_warp_shape(self.input_args[self.first_array_arg], args[self.first_array_arg].shape)
|
||||||
@@ -284,6 +313,7 @@ class FfiKernel:
|
|||||||
out_types,
|
out_types,
|
||||||
vmap_method=vmap_method,
|
vmap_method=vmap_method,
|
||||||
input_output_aliases=self.input_output_aliases,
|
input_output_aliases=self.input_output_aliases,
|
||||||
|
has_side_effect=self.has_side_effect,
|
||||||
)
|
)
|
||||||
|
|
||||||
# preload on the specified devices
|
# preload on the specified devices
|
||||||
@@ -303,7 +333,9 @@ class FfiKernel:
|
|||||||
|
|
||||||
# save launch data to be retrieved by callback
|
# save launch data to be retrieved by callback
|
||||||
launch_id = self.launch_id
|
launch_id = self.launch_id
|
||||||
self.launch_descriptors[launch_id] = FfiLaunchDesc(static_inputs, launch_dims)
|
self.launch_descriptors[launch_id] = FfiLaunchDesc(
|
||||||
|
static_inputs, launch_dims if not infer_launch_dims else None
|
||||||
|
)
|
||||||
self.launch_id += 1
|
self.launch_id += 1
|
||||||
|
|
||||||
return call(*args, launch_id=launch_id)
|
return call(*args, launch_id=launch_id)
|
||||||
@@ -343,19 +375,23 @@ class FfiKernel:
|
|||||||
assert num_inputs == self.num_inputs
|
assert num_inputs == self.num_inputs
|
||||||
assert num_outputs == self.num_outputs
|
assert num_outputs == self.num_outputs
|
||||||
|
|
||||||
launch_bounds = launch_bounds_t(launch_desc.launch_dims)
|
|
||||||
|
|
||||||
# first kernel param is the launch bounds
|
# first kernel param is the launch bounds
|
||||||
kernel_params = (ctypes.c_void_p * (1 + self.num_kernel_args))()
|
kernel_params = (ctypes.c_void_p * (1 + self.num_kernel_args))()
|
||||||
kernel_params[0] = ctypes.addressof(launch_bounds)
|
|
||||||
|
|
||||||
arg_refs = []
|
arg_refs = []
|
||||||
|
batch_size = None
|
||||||
|
|
||||||
# input and in-out args
|
# input and in-out args
|
||||||
for i, input_arg in enumerate(self.input_args):
|
for i, input_arg in enumerate(self.input_args):
|
||||||
if input_arg.is_array:
|
if input_arg.is_array:
|
||||||
buffer = inputs[i].contents
|
buffer = inputs[i].contents
|
||||||
shape = buffer.dims[: input_arg.type.ndim]
|
shape = buffer.dims[: buffer.rank - input_arg.dtype_ndim]
|
||||||
|
if buffer.rank > input_arg.jax_ndim:
|
||||||
|
# handle batching
|
||||||
|
shape = collapse_batch_dims(shape, input_arg.type.ndim)
|
||||||
|
if batch_size is None:
|
||||||
|
batch_size = compute_batch_size(
|
||||||
|
buffer.dims[: buffer.rank], buffer.rank - input_arg.jax_ndim
|
||||||
|
)
|
||||||
strides = strides_from_shape(shape, input_arg.type.dtype)
|
strides = strides_from_shape(shape, input_arg.type.dtype)
|
||||||
arg = array_t(buffer.data, 0, input_arg.type.ndim, shape, strides)
|
arg = array_t(buffer.data, 0, input_arg.type.ndim, shape, strides)
|
||||||
kernel_params[i + 1] = ctypes.addressof(arg)
|
kernel_params[i + 1] = ctypes.addressof(arg)
|
||||||
@@ -370,12 +406,34 @@ class FfiKernel:
|
|||||||
# pure output args (skip in-out FFI buffers)
|
# pure output args (skip in-out FFI buffers)
|
||||||
for i, output_arg in enumerate(self.output_args):
|
for i, output_arg in enumerate(self.output_args):
|
||||||
buffer = outputs[i + self.num_in_out].contents
|
buffer = outputs[i + self.num_in_out].contents
|
||||||
shape = buffer.dims[: output_arg.type.ndim]
|
shape = buffer.dims[: buffer.rank - output_arg.dtype_ndim]
|
||||||
|
if buffer.rank > output_arg.jax_ndim:
|
||||||
|
# handle batching
|
||||||
|
shape = collapse_batch_dims(shape, output_arg.type.ndim)
|
||||||
|
if batch_size is None:
|
||||||
|
batch_size = compute_batch_size(
|
||||||
|
buffer.dims[: buffer.rank], buffer.rank - output_arg.jax_ndim
|
||||||
|
)
|
||||||
strides = strides_from_shape(shape, output_arg.type.dtype)
|
strides = strides_from_shape(shape, output_arg.type.dtype)
|
||||||
arg = array_t(buffer.data, 0, output_arg.type.ndim, shape, strides)
|
arg = array_t(buffer.data, 0, output_arg.type.ndim, shape, strides)
|
||||||
kernel_params[num_inputs + i + 1] = ctypes.addressof(arg)
|
kernel_params[num_inputs + i + 1] = ctypes.addressof(arg)
|
||||||
arg_refs.append(arg) # keep a reference
|
arg_refs.append(arg) # keep a reference
|
||||||
|
|
||||||
|
# determine launch bounds
|
||||||
|
if launch_desc.launch_dims is None:
|
||||||
|
# infer launch dims from argument shape, works with vmap
|
||||||
|
arr = arg_refs[self.first_array_arg]
|
||||||
|
launch_dims = arr.shape[: arr.ndim]
|
||||||
|
else:
|
||||||
|
# use specified launch dims
|
||||||
|
launch_dims = launch_desc.launch_dims
|
||||||
|
if batch_size is not None:
|
||||||
|
# roll batch size into the first launch dimension
|
||||||
|
launch_dims = (batch_size * launch_dims[0], *launch_dims[1:])
|
||||||
|
|
||||||
|
launch_bounds = launch_bounds_t(launch_dims)
|
||||||
|
kernel_params[0] = ctypes.addressof(launch_bounds)
|
||||||
|
|
||||||
# get device and stream
|
# get device and stream
|
||||||
device = wp.get_cuda_device(get_device_ordinal_from_callframe(call_frame.contents))
|
device = wp.get_cuda_device(get_device_ordinal_from_callframe(call_frame.contents))
|
||||||
stream = get_stream_from_callframe(call_frame.contents)
|
stream = get_stream_from_callframe(call_frame.contents)
|
||||||
@@ -808,7 +866,7 @@ class FfiCallable:
|
|||||||
for i, arg in enumerate(self.input_args):
|
for i, arg in enumerate(self.input_args):
|
||||||
if arg.is_array:
|
if arg.is_array:
|
||||||
buffer = inputs[i].contents
|
buffer = inputs[i].contents
|
||||||
shape = buffer.dims[: buffer.rank - arg.dtype_ndim]
|
shape = collapse_batch_dims(buffer.dims[: buffer.rank - arg.dtype_ndim], arg.type.ndim)
|
||||||
arr = wp.array(ptr=buffer.data, dtype=arg.type.dtype, shape=shape, device=device)
|
arr = wp.array(ptr=buffer.data, dtype=arg.type.dtype, shape=shape, device=device)
|
||||||
arg_list.append(arr)
|
arg_list.append(arr)
|
||||||
else:
|
else:
|
||||||
@@ -819,7 +877,7 @@ class FfiCallable:
|
|||||||
# pure output args (skip in-out FFI buffers)
|
# pure output args (skip in-out FFI buffers)
|
||||||
for i, arg in enumerate(self.output_args):
|
for i, arg in enumerate(self.output_args):
|
||||||
buffer = outputs[i + self.num_in_out].contents
|
buffer = outputs[i + self.num_in_out].contents
|
||||||
shape = buffer.dims[: buffer.rank - arg.dtype_ndim]
|
shape = collapse_batch_dims(buffer.dims[: buffer.rank - arg.dtype_ndim], arg.type.ndim)
|
||||||
arr = wp.array(ptr=buffer.data, dtype=arg.type.dtype, shape=shape, device=device)
|
arr = wp.array(ptr=buffer.data, dtype=arg.type.dtype, shape=shape, device=device)
|
||||||
arg_list.append(arr)
|
arg_list.append(arr)
|
||||||
|
|
||||||
@@ -1095,6 +1153,7 @@ def jax_kernel(
|
|||||||
in_out_argnames=None,
|
in_out_argnames=None,
|
||||||
module_preload_mode=ModulePreloadMode.CURRENT_DEVICE,
|
module_preload_mode=ModulePreloadMode.CURRENT_DEVICE,
|
||||||
enable_backward: bool = False,
|
enable_backward: bool = False,
|
||||||
|
has_side_effect: bool = False,
|
||||||
):
|
):
|
||||||
"""Create a JAX callback from a Warp kernel.
|
"""Create a JAX callback from a Warp kernel.
|
||||||
|
|
||||||
@@ -1103,21 +1162,23 @@ def jax_kernel(
|
|||||||
Args:
|
Args:
|
||||||
kernel: The Warp kernel to launch.
|
kernel: The Warp kernel to launch.
|
||||||
num_outputs: Specify the number of output arguments if greater than 1.
|
num_outputs: Specify the number of output arguments if greater than 1.
|
||||||
This must include the number of ``in_out_arguments``.
|
This must include the number of ``in_out_arguments``.
|
||||||
vmap_method: String specifying how the callback transforms under ``vmap()``.
|
vmap_method: String specifying how the callback transforms under ``vmap()``.
|
||||||
This argument can also be specified for individual calls.
|
This argument can also be specified for individual calls.
|
||||||
launch_dims: Specify the default kernel launch dimensions. If None, launch
|
launch_dims: Specify the default kernel launch dimensions. If None, launch
|
||||||
dimensions are inferred from the shape of the first array argument.
|
dimensions are inferred from the shape of the first array argument.
|
||||||
This argument can also be specified for individual calls.
|
This argument can also be specified for individual calls.
|
||||||
output_dims: Specify the default dimensions of output arrays. If None, output
|
output_dims: Specify the default dimensions of output arrays. If None, output
|
||||||
dimensions are inferred from the launch dimensions.
|
dimensions are inferred from the launch dimensions.
|
||||||
This argument can also be specified for individual calls.
|
This argument can also be specified for individual calls.
|
||||||
in_out_argnames: Names of arguments that are both inputs and outputs (aliased buffers).
|
in_out_argnames: Names of arguments that are both inputs and outputs (aliased buffers).
|
||||||
These must be array arguments that appear before any pure output arguments in the
|
These must be array arguments that appear before any pure output arguments in the
|
||||||
kernel signature. The number of in-out arguments is included in ``num_outputs``.
|
kernel signature. The number of in-out arguments is included in ``num_outputs``.
|
||||||
Not supported when ``enable_backward=True``.
|
Not supported when ``enable_backward=True``.
|
||||||
module_preload_mode: Specify the devices where the module should be preloaded.
|
module_preload_mode: Specify the devices where the module should be preloaded.
|
||||||
enable_backward: Enable automatic differentiation for this kernel.
|
enable_backward: Enable automatic differentiation for this kernel.
|
||||||
|
has_side_effect: Whether the custom call has side effects. When True,
|
||||||
|
the FFI call will be executed even when the outputs are not used.
|
||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
- All kernel arguments must be contiguous arrays or scalars.
|
- All kernel arguments must be contiguous arrays or scalars.
|
||||||
@@ -1129,21 +1190,41 @@ def jax_kernel(
|
|||||||
|
|
||||||
check_jax_version()
|
check_jax_version()
|
||||||
|
|
||||||
|
if isinstance(output_dims, dict):
|
||||||
|
hashable_output_dims = tuple(sorted(output_dims.items()))
|
||||||
|
elif hasattr(output_dims, "__len__"):
|
||||||
|
hashable_output_dims = tuple(output_dims)
|
||||||
|
else:
|
||||||
|
hashable_output_dims = output_dims
|
||||||
|
|
||||||
|
if hasattr(launch_dims, "__len__"):
|
||||||
|
hashable_launch_dims = tuple(launch_dims)
|
||||||
|
else:
|
||||||
|
hashable_launch_dims = launch_dims
|
||||||
|
|
||||||
if not enable_backward:
|
if not enable_backward:
|
||||||
key = (
|
key = (
|
||||||
kernel.func,
|
kernel.func,
|
||||||
kernel.sig,
|
kernel.sig,
|
||||||
num_outputs,
|
num_outputs,
|
||||||
vmap_method,
|
vmap_method,
|
||||||
tuple(launch_dims) if launch_dims else launch_dims,
|
hashable_launch_dims,
|
||||||
tuple(sorted(output_dims.items())) if output_dims else output_dims,
|
hashable_output_dims,
|
||||||
module_preload_mode,
|
module_preload_mode,
|
||||||
|
has_side_effect,
|
||||||
)
|
)
|
||||||
|
|
||||||
with _FFI_REGISTRY_LOCK:
|
with _FFI_REGISTRY_LOCK:
|
||||||
if key not in _FFI_KERNEL_REGISTRY:
|
if key not in _FFI_KERNEL_REGISTRY:
|
||||||
new_kernel = FfiKernel(
|
new_kernel = FfiKernel(
|
||||||
kernel, num_outputs, vmap_method, launch_dims, output_dims, in_out_argnames, module_preload_mode
|
kernel,
|
||||||
|
num_outputs,
|
||||||
|
vmap_method,
|
||||||
|
launch_dims,
|
||||||
|
output_dims,
|
||||||
|
in_out_argnames,
|
||||||
|
module_preload_mode,
|
||||||
|
has_side_effect=has_side_effect,
|
||||||
)
|
)
|
||||||
_FFI_KERNEL_REGISTRY[key] = new_kernel
|
_FFI_KERNEL_REGISTRY[key] = new_kernel
|
||||||
|
|
||||||
@@ -1173,7 +1254,7 @@ def jax_kernel(
|
|||||||
static_args = []
|
static_args = []
|
||||||
for i, p in enumerate(parameters[:num_inputs]):
|
for i, p in enumerate(parameters[:num_inputs]):
|
||||||
param_type = p.annotation
|
param_type = p.annotation
|
||||||
if not isinstance(param_type, wp.array):
|
if not matches_array_class(param_type, wp.array):
|
||||||
if param_type in wp._src.types.value_types:
|
if param_type in wp._src.types.value_types:
|
||||||
static_args.append(i)
|
static_args.append(i)
|
||||||
else:
|
else:
|
||||||
@@ -1183,7 +1264,7 @@ def jax_kernel(
|
|||||||
# determine launch dimensions from the shape of the first input array
|
# determine launch dimensions from the shape of the first input array
|
||||||
for i, p in enumerate(parameters[:num_inputs]):
|
for i, p in enumerate(parameters[:num_inputs]):
|
||||||
param_type = p.annotation
|
param_type = p.annotation
|
||||||
if isinstance(param_type, wp.array):
|
if matches_array_class(param_type, wp.array):
|
||||||
arg = call_args[i]
|
arg = call_args[i]
|
||||||
arg_shape = tuple(arg.shape)
|
arg_shape = tuple(arg.shape)
|
||||||
if hasattr(param_type.dtype, "_wp_scalar_type_"):
|
if hasattr(param_type.dtype, "_wp_scalar_type_"):
|
||||||
@@ -1203,7 +1284,13 @@ def jax_kernel(
|
|||||||
fwd_kernel_wrapper.__annotations__ = {p.name: p.annotation for p in parameters}
|
fwd_kernel_wrapper.__annotations__ = {p.name: p.annotation for p in parameters}
|
||||||
fwd_kernel_wrapper.__annotations__["return"] = None
|
fwd_kernel_wrapper.__annotations__["return"] = None
|
||||||
|
|
||||||
jax_fwd_kernel = jax_callable(fwd_kernel_wrapper, num_outputs=num_outputs, vmap_method=vmap_method)
|
jax_fwd_kernel = jax_callable(
|
||||||
|
fwd_kernel_wrapper,
|
||||||
|
num_outputs=num_outputs,
|
||||||
|
vmap_method=vmap_method,
|
||||||
|
module_preload_mode=module_preload_mode,
|
||||||
|
has_side_effect=has_side_effect,
|
||||||
|
)
|
||||||
|
|
||||||
# backward arguments only include static args once
|
# backward arguments only include static args once
|
||||||
bwd_arg_count = 2 * parameter_count - len(static_args)
|
bwd_arg_count = 2 * parameter_count - len(static_args)
|
||||||
@@ -1285,6 +1372,8 @@ def jax_kernel(
|
|||||||
bwd_kernel_wrapper,
|
bwd_kernel_wrapper,
|
||||||
num_outputs=len(bwd_input_params) - len(static_args),
|
num_outputs=len(bwd_input_params) - len(static_args),
|
||||||
vmap_method=vmap_method,
|
vmap_method=vmap_method,
|
||||||
|
module_preload_mode=module_preload_mode,
|
||||||
|
has_side_effect=has_side_effect,
|
||||||
)
|
)
|
||||||
|
|
||||||
differentiable_input_indices = [i for i in range(num_inputs) if i not in static_args]
|
differentiable_input_indices = [i for i in range(num_inputs) if i not in static_args]
|
||||||
@@ -1331,7 +1420,7 @@ def jax_kernel(
|
|||||||
if ann is None:
|
if ann is None:
|
||||||
continue
|
continue
|
||||||
# Check if annotation is a warp array type (annotation is an instance of wp.array)
|
# Check if annotation is a warp array type (annotation is an instance of wp.array)
|
||||||
is_array_ann = isinstance(ann, wp.array)
|
is_array_ann = matches_array_class(ann, wp.array)
|
||||||
if not is_array_ann:
|
if not is_array_ann:
|
||||||
continue
|
continue
|
||||||
dtype_ndim = 0
|
dtype_ndim = 0
|
||||||
@@ -1355,6 +1444,15 @@ def jax_kernel(
|
|||||||
jax_func = jax.custom_vjp(jax_fwd_kernel, nondiff_argnums=tuple(static_args))
|
jax_func = jax.custom_vjp(jax_fwd_kernel, nondiff_argnums=tuple(static_args))
|
||||||
jax_func.defvjp(fwd_function, bwd_function)
|
jax_func.defvjp(fwd_function, bwd_function)
|
||||||
|
|
||||||
|
key = (
|
||||||
|
kernel.func,
|
||||||
|
kernel.sig,
|
||||||
|
num_outputs,
|
||||||
|
vmap_method,
|
||||||
|
module_preload_mode,
|
||||||
|
has_side_effect,
|
||||||
|
)
|
||||||
|
|
||||||
if static_args:
|
if static_args:
|
||||||
static_names = [parameters[i].name for i in static_args]
|
static_names = [parameters[i].name for i in static_args]
|
||||||
|
|
||||||
@@ -1364,7 +1462,7 @@ def jax_kernel(
|
|||||||
_user_callable.__signature__ = signature
|
_user_callable.__signature__ = signature
|
||||||
|
|
||||||
# Cache differentiable wrapper
|
# Cache differentiable wrapper
|
||||||
key = (kernel.func, kernel.sig, num_outputs, vmap_method, tuple(sorted(static_names)))
|
key = (*key, tuple(sorted(static_names)))
|
||||||
with _FFI_REGISTRY_LOCK:
|
with _FFI_REGISTRY_LOCK:
|
||||||
cached = _FFI_DIFF_KERNEL_REGISTRY.get(key)
|
cached = _FFI_DIFF_KERNEL_REGISTRY.get(key)
|
||||||
if cached is None:
|
if cached is None:
|
||||||
@@ -1373,7 +1471,7 @@ def jax_kernel(
|
|||||||
return _FFI_DIFF_KERNEL_REGISTRY[key]
|
return _FFI_DIFF_KERNEL_REGISTRY[key]
|
||||||
|
|
||||||
# Cache differentiable wrapper (no static args)
|
# Cache differentiable wrapper (no static args)
|
||||||
key = (kernel.func, kernel.sig, num_outputs, vmap_method, ())
|
key = (*key, ())
|
||||||
with _FFI_REGISTRY_LOCK:
|
with _FFI_REGISTRY_LOCK:
|
||||||
cached = _FFI_DIFF_KERNEL_REGISTRY.get(key)
|
cached = _FFI_DIFF_KERNEL_REGISTRY.get(key)
|
||||||
if cached is None:
|
if cached is None:
|
||||||
@@ -1426,6 +1524,8 @@ def jax_callable(
|
|||||||
graph_cache_max: Maximum number of cached graphs captured using ``GraphMode.WARP``.
|
graph_cache_max: Maximum number of cached graphs captured using ``GraphMode.WARP``.
|
||||||
If ``None``, use ``warp.jax_experimental.get_jax_callable_default_graph_cache_max()``.
|
If ``None``, use ``warp.jax_experimental.get_jax_callable_default_graph_cache_max()``.
|
||||||
module_preload_mode: Specify the devices where the module should be preloaded.
|
module_preload_mode: Specify the devices where the module should be preloaded.
|
||||||
|
has_side_effect: Whether the custom call has side effects. When True,
|
||||||
|
the FFI call will be executed even when the outputs are not used.
|
||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
- All kernel arguments must be contiguous arrays or scalars.
|
- All kernel arguments must be contiguous arrays or scalars.
|
||||||
@@ -1440,14 +1540,22 @@ def jax_callable(
|
|||||||
if graph_cache_max is None:
|
if graph_cache_max is None:
|
||||||
graph_cache_max = FfiCallable.default_graph_cache_max
|
graph_cache_max = FfiCallable.default_graph_cache_max
|
||||||
|
|
||||||
|
if isinstance(output_dims, dict):
|
||||||
|
hashable_output_dims = tuple(sorted(output_dims.items()))
|
||||||
|
elif hasattr(output_dims, "__len__"):
|
||||||
|
hashable_output_dims = tuple(output_dims)
|
||||||
|
else:
|
||||||
|
hashable_output_dims = output_dims
|
||||||
|
|
||||||
# Note: we don't include graph_cache_max in the key, it is applied below.
|
# Note: we don't include graph_cache_max in the key, it is applied below.
|
||||||
key = (
|
key = (
|
||||||
func,
|
func,
|
||||||
num_outputs,
|
num_outputs,
|
||||||
graph_mode,
|
graph_mode,
|
||||||
vmap_method,
|
vmap_method,
|
||||||
tuple(sorted(output_dims.items())) if output_dims else output_dims,
|
hashable_output_dims,
|
||||||
module_preload_mode,
|
module_preload_mode,
|
||||||
|
has_side_effect,
|
||||||
)
|
)
|
||||||
|
|
||||||
with _FFI_REGISTRY_LOCK:
|
with _FFI_REGISTRY_LOCK:
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ dependencies = [
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
warp = [
|
warp = [
|
||||||
"warp-lang==1.11.1",
|
"warp-lang==1.12.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
|
|||||||
Reference in New Issue
Block a user