ndlike 0.2.2__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ndlike-0.2.2/src/ndlike.egg-info → ndlike-0.2.4}/PKG-INFO +1 -1
- {ndlike-0.2.2 → ndlike-0.2.4}/pyproject.toml +1 -1
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/jax_functions.py +30 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/jax_operators.py +2 -2
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/jax.py +2 -2
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/jax_functions.py +5 -2
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/numpy.py +41 -32
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/torch_operators.py +2 -2
- {ndlike-0.2.2 → ndlike-0.2.4/src/ndlike.egg-info}/PKG-INFO +1 -1
- {ndlike-0.2.2 → ndlike-0.2.4}/LICENSE +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/README.md +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/setup.cfg +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/__init__.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/array_like.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/jax_like.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/__init__.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/_common_functions.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/numpy_functions.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/torch.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/protected_axis/torch_functions.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/py.typed +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/torch_functions.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike/torch_like.py +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike.egg-info/SOURCES.txt +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike.egg-info/dependency_links.txt +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike.egg-info/requires.txt +0 -0
- {ndlike-0.2.2 → ndlike-0.2.4}/src/ndlike.egg-info/top_level.txt +0 -0
|
@@ -318,6 +318,34 @@ def jax_divmod(
|
|
|
318
318
|
return jnp.divmod(x1, x2) # ty: ignore[invalid-argument-type]
|
|
319
319
|
|
|
320
320
|
|
|
321
|
+
@jax_function_dispatch("x")
|
|
322
|
+
def jax_frexp(x: _JaxFamily | JaxArrayLike, /) -> tuple[jax.Array | _JaxFamily, jax.Array | _JaxFamily]:
|
|
323
|
+
"""Decompose values into mantissas and exponents, mirroring ``jax.numpy.frexp``.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
x: Input values.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
A tuple of floating-point mantissas and integer exponents for native
|
|
330
|
+
inputs, or the result supplied by a custom override.
|
|
331
|
+
"""
|
|
332
|
+
return jnp.frexp(x) # ty: ignore[invalid-argument-type]
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
@jax_function_dispatch("x")
|
|
336
|
+
def jax_modf(x: _JaxFamily | JaxArrayLike, /) -> tuple[jax.Array | _JaxFamily, jax.Array | _JaxFamily]:
|
|
337
|
+
"""Split values into fractional and integral parts, mirroring ``jax.numpy.modf``.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
x: Input values.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
A tuple of fractional and integral parts for native inputs, or the result
|
|
344
|
+
supplied by a custom override.
|
|
345
|
+
"""
|
|
346
|
+
return jnp.modf(x) # ty: ignore[invalid-argument-type]
|
|
347
|
+
|
|
348
|
+
|
|
321
349
|
@jax_function_dispatch("x1", "x2")
|
|
322
350
|
def jax_power(x1: _JaxFamily | JaxArrayLike, x2: _JaxFamily | JaxArrayLike, /) -> jax.Array | _JaxFamily:
|
|
323
351
|
"""Raise operands to powers elementwise, mirroring ``jax.numpy.power``.
|
|
@@ -966,6 +994,7 @@ __all__ = [
|
|
|
966
994
|
"jax_equal",
|
|
967
995
|
"jax_expand_dims",
|
|
968
996
|
"jax_floor_divide",
|
|
997
|
+
"jax_frexp",
|
|
969
998
|
"jax_function_dispatch",
|
|
970
999
|
"jax_greater",
|
|
971
1000
|
"jax_greater_equal",
|
|
@@ -976,6 +1005,7 @@ __all__ = [
|
|
|
976
1005
|
"jax_matmul",
|
|
977
1006
|
"jax_matrix_transpose",
|
|
978
1007
|
"jax_mean",
|
|
1008
|
+
"jax_modf",
|
|
979
1009
|
"jax_moveaxis",
|
|
980
1010
|
"jax_multiply",
|
|
981
1011
|
"jax_negative",
|
|
@@ -70,8 +70,8 @@ class JaxOperatorsMixin: # noqa: PLW1641
|
|
|
70
70
|
Subclasses can define ``__array_ufunc__`` to override or disable that behavior.
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
|
-
#
|
|
74
|
-
__array_priority__ =
|
|
73
|
+
# Let NumPy handle its own infix operations without forced deferral.
|
|
74
|
+
__array_priority__ = 0
|
|
75
75
|
|
|
76
76
|
__add__ = _binary(jf.jax_add)
|
|
77
77
|
__radd__ = _binary(jf.jax_add, reflected=True)
|
|
@@ -442,8 +442,8 @@ class JaxAxisProtected[J: JaxLike | jax.Array | np.ndarray](JaxLikeImplementatio
|
|
|
442
442
|
operands: Original operands in expression order.
|
|
443
443
|
|
|
444
444
|
Returns:
|
|
445
|
-
The field values to validate and reconstruct. For ``divmod``,
|
|
446
|
-
hook is called separately for
|
|
445
|
+
The field values to validate and reconstruct. For ``divmod``,
|
|
446
|
+
``frexp``, and ``modf``, this hook is called separately for each output.
|
|
447
447
|
"""
|
|
448
448
|
del func, operands
|
|
449
449
|
return values
|
|
@@ -678,6 +678,8 @@ def _batch_comparison(values: dict[str, Any], template: JaxAxisProtectedInternal
|
|
|
678
678
|
jf.jax_floor_divide,
|
|
679
679
|
jf.jax_remainder,
|
|
680
680
|
jf.jax_divmod,
|
|
681
|
+
jf.jax_frexp,
|
|
682
|
+
jf.jax_modf,
|
|
681
683
|
jf.jax_power,
|
|
682
684
|
jf.jax_matmul,
|
|
683
685
|
jf.jax_bitwise_and,
|
|
@@ -721,9 +723,10 @@ def protected_operator_function(
|
|
|
721
723
|
operation = jax.vmap(operation, in_axes=in_axes, out_axes=-1)
|
|
722
724
|
results[name] = operation(*field_args)
|
|
723
725
|
|
|
726
|
+
multiple_outputs = func in (jf.jax_divmod, jf.jax_frexp, jf.jax_modf)
|
|
724
727
|
result_sets = (
|
|
725
728
|
tuple({name: value[index] for name, value in results.items()} for index in range(2))
|
|
726
|
-
if
|
|
729
|
+
if multiple_outputs
|
|
727
730
|
else (results,)
|
|
728
731
|
)
|
|
729
732
|
reconstructed = []
|
|
@@ -734,4 +737,4 @@ def protected_operator_function(
|
|
|
734
737
|
processed = owner._postprocess_elementwise_result(values, func=func, operands=args) # noqa: SLF001
|
|
735
738
|
_validate_operator_result(processed, template)
|
|
736
739
|
reconstructed.append(template.create(processed))
|
|
737
|
-
return tuple(reconstructed) if
|
|
740
|
+
return tuple(reconstructed) if multiple_outputs else reconstructed[0]
|
|
@@ -377,7 +377,9 @@ class NumpyAxisProtected[T: NumpyLike | np.ndarray](NumpyLikeImplementation[T],
|
|
|
377
377
|
else:
|
|
378
378
|
out_values.append(None)
|
|
379
379
|
|
|
380
|
-
if
|
|
380
|
+
if len(protected_axes) != 1 and any(
|
|
381
|
+
item is not None and values is None for item, values in zip(out_items, out_values, strict=True)
|
|
382
|
+
):
|
|
381
383
|
msg = "non-protected out is only supported for single-field protected objects."
|
|
382
384
|
raise TypeError(msg)
|
|
383
385
|
|
|
@@ -398,7 +400,8 @@ class NumpyAxisProtected[T: NumpyLike | np.ndarray](NumpyLikeImplementation[T],
|
|
|
398
400
|
raise TypeError(msg)
|
|
399
401
|
return normalize_axis(axis, batch_ndim)
|
|
400
402
|
|
|
401
|
-
|
|
403
|
+
output_count = ufunc.nout if method == "__call__" else 1
|
|
404
|
+
results: list[dict[str, NumpyProtectedValue]] = [{} for _ in range(output_count)]
|
|
402
405
|
for name, axes_count in protected_axes.items():
|
|
403
406
|
field_kwargs = dict(kwargs)
|
|
404
407
|
|
|
@@ -445,43 +448,49 @@ class NumpyAxisProtected[T: NumpyLike | np.ndarray](NumpyLikeImplementation[T],
|
|
|
445
448
|
|
|
446
449
|
result = getattr(ufunc, method)(*field_input_items, **field_kwargs)
|
|
447
450
|
|
|
448
|
-
if
|
|
449
|
-
|
|
451
|
+
result_items = result if output_count > 1 else (result,)
|
|
452
|
+
for index, result_item in enumerate(result_items):
|
|
453
|
+
if out is not None and out_items[index] is not None:
|
|
454
|
+
continue
|
|
450
455
|
|
|
451
|
-
|
|
452
|
-
|
|
456
|
+
item = result_item
|
|
457
|
+
if axes_count == 0 and not hasattr(item, "ndim"):
|
|
458
|
+
item = np.asarray(item)
|
|
453
459
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
460
|
+
result_ndim = value_ndim(item)
|
|
461
|
+
result_shape = value_shape(item)
|
|
462
|
+
original_shape = value_shape(self_values[name])
|
|
463
|
+
if result_ndim < axes_count:
|
|
464
|
+
msg = f"Ufunc operation removed protected trailing axes for field {name!r}."
|
|
465
|
+
raise ValueError(msg)
|
|
466
|
+
if protected_shape(result_shape, axes_count) != protected_shape(original_shape, axes_count):
|
|
467
|
+
msg = f"Ufunc operation modified protected trailing axes for field {name!r}."
|
|
468
|
+
raise ValueError(msg)
|
|
469
|
+
|
|
470
|
+
if isinstance(item, np.ndarray):
|
|
471
|
+
item = self._postprocess_ufunc_result(item, ufunc=ufunc, method=method)
|
|
463
472
|
|
|
464
|
-
|
|
465
|
-
result = self._postprocess_ufunc_result(result, ufunc=ufunc, method=method)
|
|
473
|
+
results[index][name] = cast("NumpyProtectedValue", item)
|
|
466
474
|
|
|
467
|
-
|
|
475
|
+
outputs: list[object] = []
|
|
476
|
+
for index, values in enumerate(results):
|
|
477
|
+
if out is not None and out_items[index] is not None:
|
|
478
|
+
outputs.append(out_items[index])
|
|
479
|
+
continue
|
|
468
480
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
481
|
+
expected_batch_shape: tuple[int, ...] | None = None
|
|
482
|
+
for name, value in values.items():
|
|
483
|
+
axes_count = protected_axes[name]
|
|
484
|
+
current_batch_shape = batch_shape(value_shape(value), axes_count)
|
|
485
|
+
if expected_batch_shape is None:
|
|
486
|
+
expected_batch_shape = current_batch_shape
|
|
487
|
+
elif current_batch_shape != expected_batch_shape:
|
|
488
|
+
msg = "Ufunc operation produced inconsistent batch-shapes across protected fields."
|
|
489
|
+
raise ValueError(msg)
|
|
473
490
|
|
|
474
|
-
|
|
475
|
-
for name, value in results.items():
|
|
476
|
-
axes_count = protected_axes[name]
|
|
477
|
-
current_batch_shape = batch_shape(value_shape(value), axes_count)
|
|
478
|
-
if expected_batch_shape is None:
|
|
479
|
-
expected_batch_shape = current_batch_shape
|
|
480
|
-
elif current_batch_shape != expected_batch_shape:
|
|
481
|
-
msg = "Ufunc operation produced inconsistent batch-shapes across protected fields."
|
|
482
|
-
raise ValueError(msg)
|
|
491
|
+
outputs.append(self.with_protected_values(values))
|
|
483
492
|
|
|
484
|
-
return
|
|
493
|
+
return tuple(outputs) if output_count > 1 else outputs[0]
|
|
485
494
|
|
|
486
495
|
def __array_function__(
|
|
487
496
|
self,
|
|
@@ -66,8 +66,8 @@ class TorchOperatorsMixin: # noqa: PLW1641
|
|
|
66
66
|
Subclasses can define ``__array_ufunc__`` to override or disable that behavior.
|
|
67
67
|
"""
|
|
68
68
|
|
|
69
|
-
#
|
|
70
|
-
__array_priority__ =
|
|
69
|
+
# Let NumPy handle its own infix operations without forced deferral.
|
|
70
|
+
__array_priority__ = 0
|
|
71
71
|
|
|
72
72
|
__add__ = _binary(torch.add)
|
|
73
73
|
__radd__ = _binary(torch.add, reflected=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|