Skip to content

Treat JAX and NumPy arrays as interchangeable when deserialising - #1253

Open
JOhnsonKC201 wants to merge 1 commit into
patrick-kidger:mainfrom
JOhnsonKC201:fix/deserialise-host-arrays
Open

Treat JAX and NumPy arrays as interchangeable when deserialising#1253
JOhnsonKC201 wants to merge 1 commit into
patrick-kidger:mainfrom
JOhnsonKC201:fix/deserialise-host-arrays

Conversation

@JOhnsonKC201

Copy link
Copy Markdown

Fixes #861.

Problem

Deserialising into a filter_eval_shape skeleton fails if the filter_spec
puts the result on the host:

like = eqx.filter_eval_shape(eqx.nn.Linear, 2, 2, key=key)
eqx.tree_deserialise_leaves(
    path, like,
    filter_spec=lambda f, x: jax.device_get(eqx.default_deserialise_filter_spec(f, x)),
)
# RuntimeError: Deserialised leaf at path '.weight' has changed type from
# <class 'jax.ShapeDtypeStruct'> in `like` to <class 'numpy.ndarray'> on disk.

Shape and dtype match. Only the array kind differs.

Cause

_assert_same_impl substitutes the concrete array type for a
ShapeDtypeStruct in like, then compares with typenew is not typeold. That
identity check leaves no room for a filter_spec to hand back a NumPy array
instead of a JAX one.

Fix

Compare array leaves by array-ness instead of exact type, which is what you
suggested in the issue: "Maybe we just consider all kinds of JAX and NumPy array
interchangeable?".

Every non-array leaf keeps the strict identity check, and the shape and dtype
checks immediately below are untouched, so real mismatches still raise. I
verified both: a shape mismatch still errors, and the behaviour for a
non-array like leaf is byte-for-byte the same as on main.

Test plan

  • New test_eval_shape_deserialise_to_host in tests/test_serialisation.py.
    It fails on main with the RuntimeError above and passes with this change.
  • Full suite: 472 passed, 4 skipped (baseline on a clean checkout was 471
    passed, 4 skipped, so the only difference is the added test).
  • ruff check and ruff format --check clean on both files.

Deserialising into a `filter_eval_shape` skeleton with a filter_spec that
calls jax.device_get failed: the ShapeDtypeStructs in `like` are compared
against the concrete array type, so a host-side np.ndarray was reported as
a change of type even though shape and dtype matched.

Compare array leaves by array-ness rather than exact type. Every other
leaf type keeps the strict identity check, and the shape and dtype checks
below are unchanged, so genuine mismatches are still caught.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eval_shape incompatible with deserializing directly to host due to __assert_same

1 participant