Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions equinox/internal/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jax
import jax.core
import jax.experimental.custom_partitioning as custom_partitioning
import jax.interpreters.ad as ad
import jax.interpreters.batching as batching
import jax.interpreters.mlir as mlir
Expand All @@ -13,12 +14,40 @@
from ._unvmap import unvmap_any, unvmap_max


def _error_impl(pred, index, *x, msgs):
def _raises_impl(msgs, index, x):
def raises(_index):
raise RuntimeError(msgs[_index.item()])

struct = jax.eval_shape(lambda: x)
return lax.cond(pred, lambda: jax.pure_callback(raises, struct, index), lambda: x)
return jax.pure_callback(raises, struct, index)


def _partition_raises(msgs, arg_shapes, arg_shardings, result_shape, result_sharding):
return _raises_impl, result_sharding, arg_shardings


def _infer_sharding_from_operands_raises(msgs, arg_shapes, arg_shardings, shape):
del msgs, arg_shapes, shape
_, output_sharding = arg_shardings
return output_sharding


_raises = custom_partitioning.custom_partitioning(_raises_impl, static_argnums=(0,))
_raises.def_partition(_partition_raises, _infer_sharding_from_operands_raises)


def _error_impl(pred, index, *x, msgs):
def _raises_wrapper(x):
# Iterate to avoid a JAX bug that means `custom_partitioning` has to return a
# single output.
# TODO: just use ft.partial(_raises, msgs, index) once this is no logner
# necessary.
new_x = []
for xi in x:
new_x.append(_raises(msgs, index, xi))
return tuple(new_x)

return lax.cond(pred, _raises_wrapper, lambda x: x, x)


def _error_abstract(pred, index, *x, msgs):
Expand Down
2 changes: 2 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import random
import typing
import warnings
Expand All @@ -12,6 +13,7 @@
"ignore",
category=beartype.roar.BeartypeDecorHintPep585DeprecationWarning, # pyright: ignore
)
os.environ["XLA_FLAGS"] = "--xla_force_host_platform_device_count=2"


@pytest.fixture()
Expand Down
16 changes: 16 additions & 0 deletions tests/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import jax
import jax.experimental.mesh_utils as mesh_utils
import jax.numpy as jnp
import jax.sharding as sharding
import pytest

import equinox.internal as eqxi
Expand Down Expand Up @@ -77,3 +79,17 @@ def f(x, y, z):
return y

f(1.0, 1.0, True)


def test_sharding():
x = jnp.array([0, 1])
mesh = sharding.Mesh(mesh_utils.create_device_mesh((2,)), ["i"])
spec = sharding.PartitionSpec("i")
named_sharding = sharding.NamedSharding(mesh, spec)
x = jax.device_put(x, named_sharding)

@jax.jit
def f(x):
return eqxi.error_if(x, x > 5, "oh no")

f(x)
11 changes: 4 additions & 7 deletions tests/test_pmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from .helpers import shaped_allclose as _shaped_allclose


(cpu,) = jax.devices("cpu")
filter_pmap: Any = ft.partial(eqx.filter_pmap, devices=[cpu])
devices = (jax.devices("cpu")[0],)
filter_pmap: Any = ft.partial(eqx.filter_pmap, devices=devices)


def shaped_allclose(x, y, **kwargs):
Expand Down Expand Up @@ -203,15 +203,12 @@ def f(x):
y = x + 1
return jax.lax.psum(y, axis_name="device")

n = jax.local_device_count()
output = filter_pmap(f, axis_name="device")(jnp.zeros(n))
output = filter_pmap(f, axis_name="device")(jnp.zeros(1))

assert shaped_allclose(output, n * jnp.ones(n))
assert shaped_allclose(output, jnp.ones(1))


def test_map_non_jax():
devices = jax.local_devices()

# this contains a non-jax value for the `activation` field
# and will therefore break filter_pmap if not filtered out
# at input and output
Expand Down