You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
DeepHash fails on JAX arrays in tripartite make with equinox Modules #1427
deepdiff.DeepHash (used by DataJoint's tripartite make for referential integrity checks) fails when the fetched data contains JAX arrays, as is common when using equinox Modules as data containers.
Minimal Reproduction
importjax.numpyasjnpimportdeepdiff# JAX 0-d scalardeepdiff.DeepHash(jnp.float32(1.0))
# TypeError: iteration over a 0-d array# JAX 1-d arraydeepdiff.DeepHash(jnp.ones(3))
# TypeError: iteration over a 0-d array# equinox Module with JAX fields (common pattern for datasets)importequinoxaseqxclassMyDataset(eqx.Module):
data: jnp.ndarrayscalar: jnp.ndarraydef__init__(self):
self.data=jnp.ones((3, 4))
self.scalar=jnp.float32(1.0)
deepdiff.DeepHash(MyDataset())
# TypeError: iteration over a 0-d array
Root Cause
DeepHash._hash checks isinstance(obj, Iterable) (line ~79 of deephash.py). JAX arrays implement __iter__, so they match. But JAX 0-d arrays (scalars) raise TypeError: iteration over a 0-d array when iterated — unlike numpy 0-d arrays which deepdiff handles via the numbers type check (numpy scalars are registered as numbers).
This affects the tripartite make pattern because _populate1 calls DeepHash(fetched_data) on whatever make_fetch returns. If the fetched data includes JAX arrays (e.g., a dataset loaded from the database), the integrity check fails before computation even starts.
Workaround
We monkey-patch DeepHash._hash to convert JAX arrays to numpy before hashing:
Thanks for the detailed report. This is a deepdiff limitation — JAX arrays implement __iter__ but 0-d scalars raise on iteration, which breaks DeepHash.
Immediate workaround (no DataJoint changes needed)
Since you control what make_fetch yields, you can normalize the data before yielding it. JAX's tree.map already knows how to traverse equinox Modules and pytrees:
importnumpyasnpimportjaxdefto_hashable(obj):
"""Convert JAX arrays to numpy for DeepHash compatibility."""returnjax.tree.map(
lambdax: np.asarray(x) ifhasattr(x, '__jax_array__') elsex, obj
)
This converts JAX arrays (including 0-d scalars inside equinox Modules) to numpy before DeepHash sees them. The conversion only affects the hashing step — your compute stage still receives the original JAX data from the re-fetch.
Longer term
We're considering adding a hash_preprocessor callback to populate() so this can be configured without modifying each make method. We'd also welcome a bug report upstream on deepdiff for the Iterable check on 0-d arrays.
Activity
dimitri-yatsenko commentedon Apr 6, 2026
Thanks for the detailed report. This is a deepdiff limitation — JAX arrays implement
__iter__but 0-d scalars raise on iteration, which breaksDeepHash.Immediate workaround (no DataJoint changes needed)
Since you control what
make_fetchyields, you can normalize the data before yielding it. JAX'stree.mapalready knows how to traverse equinox Modules and pytrees:Then in your tripartite make, wrap the yield:
This converts JAX arrays (including 0-d scalars inside equinox Modules) to numpy before
DeepHashsees them. The conversion only affects the hashing step — your compute stage still receives the original JAX data from the re-fetch.Longer term
We're considering adding a
hash_preprocessorcallback topopulate()so this can be configured without modifying eachmakemethod. We'd also welcome a bug report upstream on deepdiff for theIterablecheck on 0-d arrays.