Repository navigation
support python mlx.array creation from list of mlx.array's - #325
Conversation
| enum PyScalarT { | ||
| pybool = 0, | ||
| pyint = 1, | ||
| pyfloat = 2, | ||
| pycomplex = 3, | ||
| }; | ||
|
|
There was a problem hiding this comment.
Replace it with Dtype since we'd have to deal with all possible mlx dtypes when create from list of mlx.arrays. Not just python primitive types.
|
|
||
| Val val; | ||
| const uint8_t size; | ||
| uint8_t size; |
There was a problem hiding this comment.
Have to drop the constness here since we'd need the Dtype to be copy-assignable, which we are going to need in validate_shape() so that we have a Dtype defined first and assigned later to get the final promoted type.
There was a problem hiding this comment.
Can you keep this const? See comment below.
| fill_vector(l.template cast<py::tuple>(), vals); | ||
| } else if (py::isinstance<array>(l)) { | ||
| auto arr = py::cast<array>(l); | ||
| arr.eval(); |
There was a problem hiding this comment.
Need to call eval() here since arr may come from transformations so that it does not hold the actual raw data.. without the eval() then the data<T>() pointer would be uninitialized (actually zero) so that we would end up with segmentation fault.
|
Thanks for adding this. I was thinking we should use concatenate and have this be part of the compute graph rather than force an eval. Concretely when you do |
|
@awni Thanks. I see, yeah that makes sense. I am gonna renovate the code a bit. |
352c625 to
ebe6ddd
Compare
| throw std::invalid_argument( | ||
| "Initialization encountered non-uniform length."); | ||
| } |
| size_t size = 1; | ||
| for (auto s : shape) { | ||
| size *= s; |
There was a problem hiding this comment.
For some reason the size here is never used, so I deleted this block.
|
Hi @awni ! May I bug you to review again? Thanks! And... Happy New Year 🥳 |
|
Hi @Alex-X-W , There's two things I think we need to fix with this:
|
awni
left a comment
There was a problem hiding this comment.
Left a comment with requested changes.
ebe6ddd to
97ad937
Compare
|
|
||
| if (all_python_primitive_elements) { | ||
| // `pl` does not contain mlx arrays | ||
| return array_from_list(pl, type, dtype, shape); |
There was a problem hiding this comment.
This code path handles only the code path when all elements are python primitive types
| mx.int64, | ||
| mx.float16, | ||
| mx.float32, | ||
| mx.bfloat16, |
awni
left a comment
There was a problem hiding this comment.
This is looking really nice!!!
My main comment is I would prefer not to undo the constness of the dtype.val unless there is a strong need and I don't think there is in this case (see inline comments). Could you make the change, then I think we are good to go?
|
|
||
| Val val; | ||
| const uint8_t size; | ||
| uint8_t size; |
There was a problem hiding this comment.
Can you keep this const? See comment below.
| } else if (py::isinstance<array>(l)) { | ||
| all_python_primitive_elements = false; | ||
| auto arr = py::cast<array>(l); | ||
| if (arr.shape().size() + idx + 1 == shape.size() && |
There was a problem hiding this comment.
nit: arr.ndim() instead of arr.shape().size()
| Dtype type = bool_; | ||
| for (auto l : list) { | ||
| PyScalarT t; | ||
| Dtype t = bool_; |
There was a problem hiding this comment.
To avoid needing to change the const of the dtype value, you could revert this to use the PyScalarT and if you get an array, just set it bool_ (lowest order type) and let the stack handle the type promotion if you actually get any arrays (which it already will by default).
Alternatively you could put it in a lambda and add the types to a vector and the reduce the vector with promotion
I think the first option is simpler though.
…type promotions automatically
|
Thank you @awni for the pointers! They all make sense to me. |
awni
left a comment
There was a problem hiding this comment.
Awesome!! I left one more nit. Could you check it then we can merge?
| return get_shape(l.template cast<py::tuple>(), shape); | ||
| } else if (py::isinstance<array>(l)) { | ||
| auto arr = py::cast<array>(l); | ||
| shape.insert(shape.end(), arr.shape().begin(), arr.shape().end()); |
There was a problem hiding this comment.
nit: I think shape = arr.shape() is sufficient here.
There was a problem hiding this comment.
@awni no...
if a, b = mx.array([1]), mx.array([2])
then in evaluating mx.array([a, b]) on line 198 with shape = arr.shape() we'd end up with shape = [1], but actually the shape should be [2, 1]. shape = arr.shape() will drop a dimension in the process of list unrolling at line 189. You will see what I mean if you see the full block of the get_shape()
Lines 188 to 202 in 1bcee20
There was a problem hiding this comment.
Sorry I misunderstood this code, thanks for clarifying.
|
PS checked cpu/gpu tests pass + formatting passes |
awni
left a comment
There was a problem hiding this comment.
🚀 Thanks so much for adding this!
limit the range in symmetric_matrices
Proposed changes
Adds support for python mlx.array create from list of mlx.array's. Trying to tackle issue 287.
Checklist
Put an
xin the boxes that apply.pre-commit run --all-filesto format my code / installed pre-commit prior to committing changes