mirror of
https://github.com/ml-explore/mlx.git
synced 2025-07-04 16:21:14 +08:00
* Nicer exceptions for ops on non-arrays
This commit is contained in:
parent
3fc993f82d
commit
0caf35f4b8
@ -15,7 +15,8 @@ MLX was developed with contributions from the following individuals:
|
|||||||
- Hinrik Snær Guðmundsson: Added `atleast_1d`, `atleast_2d`, `atleast_3d` ops.
|
- Hinrik Snær Guðmundsson: Added `atleast_1d`, `atleast_2d`, `atleast_3d` ops.
|
||||||
- Luca Arnaboldi: Added `Ceil` and `Floor` ops; implemented pickling, copy and deepcopy for mlx arrays.
|
- Luca Arnaboldi: Added `Ceil` and `Floor` ops; implemented pickling, copy and deepcopy for mlx arrays.
|
||||||
- Brian Keene & Atila Orhon, with Argmax Inc.: Added `fast.scaled_dot_product_attention`
|
- Brian Keene & Atila Orhon, with Argmax Inc.: Added `fast.scaled_dot_product_attention`
|
||||||
- AmirHossein Razlighi: Added chaining support for some of the ops in `nn.Module`. Comparison works for non array objects in `mlx.core.array`.
|
- AmirHossein Razlighi: Added chaining support for some of the ops in `nn.Module`. Comparison works for non array objects in `mlx.core.array`. Exception handling for invalid operations in `mlx.core.array`.
|
||||||
|
|
||||||
<a href="https://github.com/ml-explore/mlx/graphs/contributors">
|
<a href="https://github.com/ml-explore/mlx/graphs/contributors">
|
||||||
<img class="dark-light" src="https://contrib.rocks/image?repo=ml-explore/mlx&anon=0&columns=20&max=100&r=true" />
|
<img class="dark-light" src="https://contrib.rocks/image?repo=ml-explore/mlx&anon=0&columns=20&max=100&r=true" />
|
||||||
</a>
|
</a>
|
||||||
|
@ -690,6 +690,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__add__",
|
"__add__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("addition", v);
|
||||||
|
}
|
||||||
auto b = to_array(v, a.dtype());
|
auto b = to_array(v, a.dtype());
|
||||||
return add(a, b);
|
return add(a, b);
|
||||||
},
|
},
|
||||||
@ -697,6 +700,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__iadd__",
|
"__iadd__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace addition", v);
|
||||||
|
}
|
||||||
a.overwrite_descriptor(add(a, to_array(v, a.dtype())));
|
a.overwrite_descriptor(add(a, to_array(v, a.dtype())));
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
@ -705,18 +711,27 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__radd__",
|
"__radd__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("addition", v);
|
||||||
|
}
|
||||||
return add(a, to_array(v, a.dtype()));
|
return add(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__sub__",
|
"__sub__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("subtraction", v);
|
||||||
|
}
|
||||||
return subtract(a, to_array(v, a.dtype()));
|
return subtract(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__isub__",
|
"__isub__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace subtraction", v);
|
||||||
|
}
|
||||||
a.overwrite_descriptor(subtract(a, to_array(v, a.dtype())));
|
a.overwrite_descriptor(subtract(a, to_array(v, a.dtype())));
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
@ -725,18 +740,27 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__rsub__",
|
"__rsub__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("subtraction", v);
|
||||||
|
}
|
||||||
return subtract(to_array(v, a.dtype()), a);
|
return subtract(to_array(v, a.dtype()), a);
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__mul__",
|
"__mul__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("multiplication", v);
|
||||||
|
}
|
||||||
return multiply(a, to_array(v, a.dtype()));
|
return multiply(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__imul__",
|
"__imul__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace multiplication", v);
|
||||||
|
}
|
||||||
a.overwrite_descriptor(multiply(a, to_array(v, a.dtype())));
|
a.overwrite_descriptor(multiply(a, to_array(v, a.dtype())));
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
@ -745,18 +769,27 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__rmul__",
|
"__rmul__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("multiplication", v);
|
||||||
|
}
|
||||||
return multiply(a, to_array(v, a.dtype()));
|
return multiply(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__truediv__",
|
"__truediv__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("division", v);
|
||||||
|
}
|
||||||
return divide(a, to_array(v, a.dtype()));
|
return divide(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__itruediv__",
|
"__itruediv__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace division", v);
|
||||||
|
}
|
||||||
if (!issubdtype(a.dtype(), inexact)) {
|
if (!issubdtype(a.dtype(), inexact)) {
|
||||||
throw std::invalid_argument(
|
throw std::invalid_argument(
|
||||||
"In place division cannot cast to non-floating point type.");
|
"In place division cannot cast to non-floating point type.");
|
||||||
@ -769,30 +802,45 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__rtruediv__",
|
"__rtruediv__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("division", v);
|
||||||
|
}
|
||||||
return divide(to_array(v, a.dtype()), a);
|
return divide(to_array(v, a.dtype()), a);
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__div__",
|
"__div__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("division", v);
|
||||||
|
}
|
||||||
return divide(a, to_array(v, a.dtype()));
|
return divide(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__rdiv__",
|
"__rdiv__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("division", v);
|
||||||
|
}
|
||||||
return divide(to_array(v, a.dtype()), a);
|
return divide(to_array(v, a.dtype()), a);
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__floordiv__",
|
"__floordiv__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("floor division", v);
|
||||||
|
}
|
||||||
return floor_divide(a, to_array(v, a.dtype()));
|
return floor_divide(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__ifloordiv__",
|
"__ifloordiv__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace floor division", v);
|
||||||
|
}
|
||||||
a.overwrite_descriptor(floor_divide(a, to_array(v, a.dtype())));
|
a.overwrite_descriptor(floor_divide(a, to_array(v, a.dtype())));
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
@ -801,6 +849,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__rfloordiv__",
|
"__rfloordiv__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("floor division", v);
|
||||||
|
}
|
||||||
auto b = to_array(v, a.dtype());
|
auto b = to_array(v, a.dtype());
|
||||||
return floor_divide(b, a);
|
return floor_divide(b, a);
|
||||||
},
|
},
|
||||||
@ -808,12 +859,18 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__mod__",
|
"__mod__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("modulus", v);
|
||||||
|
}
|
||||||
return remainder(a, to_array(v, a.dtype()));
|
return remainder(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__imod__",
|
"__imod__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace modulus", v);
|
||||||
|
}
|
||||||
a.overwrite_descriptor(remainder(a, to_array(v, a.dtype())));
|
a.overwrite_descriptor(remainder(a, to_array(v, a.dtype())));
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
@ -822,6 +879,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__rmod__",
|
"__rmod__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("modulus", v);
|
||||||
|
}
|
||||||
return remainder(to_array(v, a.dtype()), a);
|
return remainder(to_array(v, a.dtype()), a);
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
@ -838,24 +898,36 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__lt__",
|
"__lt__",
|
||||||
[](const array& a, const ScalarOrArray v) -> array {
|
[](const array& a, const ScalarOrArray v) -> array {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("less than", v);
|
||||||
|
}
|
||||||
return less(a, to_array(v, a.dtype()));
|
return less(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__le__",
|
"__le__",
|
||||||
[](const array& a, const ScalarOrArray v) -> array {
|
[](const array& a, const ScalarOrArray v) -> array {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("less than or equal", v);
|
||||||
|
}
|
||||||
return less_equal(a, to_array(v, a.dtype()));
|
return less_equal(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__gt__",
|
"__gt__",
|
||||||
[](const array& a, const ScalarOrArray v) -> array {
|
[](const array& a, const ScalarOrArray v) -> array {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("greater than", v);
|
||||||
|
}
|
||||||
return greater(a, to_array(v, a.dtype()));
|
return greater(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__ge__",
|
"__ge__",
|
||||||
[](const array& a, const ScalarOrArray v) -> array {
|
[](const array& a, const ScalarOrArray v) -> array {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("greater than or equal", v);
|
||||||
|
}
|
||||||
return greater_equal(a, to_array(v, a.dtype()));
|
return greater_equal(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
@ -897,18 +969,27 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__pow__",
|
"__pow__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("power", v);
|
||||||
|
}
|
||||||
return power(a, to_array(v, a.dtype()));
|
return power(a, to_array(v, a.dtype()));
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__rpow__",
|
"__rpow__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("power", v);
|
||||||
|
}
|
||||||
return power(to_array(v, a.dtype()), a);
|
return power(to_array(v, a.dtype()), a);
|
||||||
},
|
},
|
||||||
"other"_a)
|
"other"_a)
|
||||||
.def(
|
.def(
|
||||||
"__ipow__",
|
"__ipow__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace power", v);
|
||||||
|
}
|
||||||
a.overwrite_descriptor(power(a, to_array(v, a.dtype())));
|
a.overwrite_descriptor(power(a, to_array(v, a.dtype())));
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
@ -930,6 +1011,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__and__",
|
"__and__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("bitwise and", v);
|
||||||
|
}
|
||||||
auto b = to_array(v, a.dtype());
|
auto b = to_array(v, a.dtype());
|
||||||
if (issubdtype(a.dtype(), inexact) ||
|
if (issubdtype(a.dtype(), inexact) ||
|
||||||
issubdtype(b.dtype(), inexact)) {
|
issubdtype(b.dtype(), inexact)) {
|
||||||
@ -946,6 +1030,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__iand__",
|
"__iand__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace bitwise and", v);
|
||||||
|
}
|
||||||
auto b = to_array(v, a.dtype());
|
auto b = to_array(v, a.dtype());
|
||||||
if (issubdtype(a.dtype(), inexact) ||
|
if (issubdtype(a.dtype(), inexact) ||
|
||||||
issubdtype(b.dtype(), inexact)) {
|
issubdtype(b.dtype(), inexact)) {
|
||||||
@ -964,6 +1051,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__or__",
|
"__or__",
|
||||||
[](const array& a, const ScalarOrArray v) {
|
[](const array& a, const ScalarOrArray v) {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("bitwise or", v);
|
||||||
|
}
|
||||||
auto b = to_array(v, a.dtype());
|
auto b = to_array(v, a.dtype());
|
||||||
if (issubdtype(a.dtype(), inexact) ||
|
if (issubdtype(a.dtype(), inexact) ||
|
||||||
issubdtype(b.dtype(), inexact)) {
|
issubdtype(b.dtype(), inexact)) {
|
||||||
@ -980,6 +1070,9 @@ void init_array(nb::module_& m) {
|
|||||||
.def(
|
.def(
|
||||||
"__ior__",
|
"__ior__",
|
||||||
[](array& a, const ScalarOrArray v) -> array& {
|
[](array& a, const ScalarOrArray v) -> array& {
|
||||||
|
if (!is_comparable_with_array(v)) {
|
||||||
|
throw_invalid_operation("inplace bitwise or", v);
|
||||||
|
}
|
||||||
auto b = to_array(v, a.dtype());
|
auto b = to_array(v, a.dtype());
|
||||||
if (issubdtype(a.dtype(), inexact) ||
|
if (issubdtype(a.dtype(), inexact) ||
|
||||||
issubdtype(b.dtype(), inexact)) {
|
issubdtype(b.dtype(), inexact)) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include <nanobind/nanobind.h>
|
#include <nanobind/nanobind.h>
|
||||||
@ -56,6 +57,19 @@ inline bool is_comparable_with_array(const ScalarOrArray& v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline nb::handle get_handle_of_object(const ScalarOrArray& v) {
|
||||||
|
return std::get<nb::object>(v).ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void throw_invalid_operation(
|
||||||
|
const std::string& operation,
|
||||||
|
const ScalarOrArray operand) {
|
||||||
|
std::ostringstream msg;
|
||||||
|
msg << "Cannot perform " << operation << " on an mlx.core.array and "
|
||||||
|
<< nb::type_name(get_handle_of_object(operand).type()).c_str();
|
||||||
|
throw std::invalid_argument(msg.str());
|
||||||
|
}
|
||||||
|
|
||||||
inline array to_array(
|
inline array to_array(
|
||||||
const ScalarOrArray& v,
|
const ScalarOrArray& v,
|
||||||
std::optional<Dtype> dtype = std::nullopt) {
|
std::optional<Dtype> dtype = std::nullopt) {
|
||||||
|
@ -203,6 +203,29 @@ class TestInequality(mlx_tests.MLXTestCase):
|
|||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
a >= tpl_
|
a >= tpl_
|
||||||
|
|
||||||
|
def test_invalid_op_on_array(self):
|
||||||
|
str_ = "hello"
|
||||||
|
a = mx.array([1, 2.5, 3.25])
|
||||||
|
lst_ = [1, 2.1, 3.25]
|
||||||
|
tpl_ = (1, 2.5, 3.25)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a * str_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a *= str_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a /= lst_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a // lst_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a % lst_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a**tpl_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a & tpl_
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
a | str_
|
||||||
|
|
||||||
|
|
||||||
class TestArray(mlx_tests.MLXTestCase):
|
class TestArray(mlx_tests.MLXTestCase):
|
||||||
def test_array_basics(self):
|
def test_array_basics(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user