crystal-data / num.cr

Scientific computing in pure Crystal
MIT License
151 stars 12 forks source link

Feature Request: Sub-Tensor selection #27

Closed jtanderson closed 4 years ago

jtanderson commented 4 years ago

In many scenarios it is helpful to have read+write slicing within a sub-matrix/tensor:

a = [[3, 4, 5, 6], [5, 6, 7, 8]].to_tensor
b = [[11, 12, 13]].to_tensor
a[1, 1..] = b

puts a # same as a[.., ..] or a[..., ...] etc.

# [[3, 4,  5, 6],
#  [5, 11, 12, 13]]

puts a[0..1, 1...3] # note inclusive vs. exclusive ranges

# [[4, 5],
#  [11, 12]]
christopherzimmerman commented 4 years ago

Hi @jtanderson, thanks for the issue. I just want to clarify what the actual feature request is, as all of these code samples should already work.

One note about your first assignment, that should not work, and I have added in a check that will correctly raise a shape error, since you can't assign a sequence to an array. The correct assignment would be a[1, 1..] = b[0], since [1, 3] cannot broadcast against [3] inplace.

I tested the above script on the current master branch, everything else should already be implemented.

jtanderson commented 4 years ago

Oh, nice! I should have tested with some actual code, but I was just looking for the operators in the tensor folder files and didn't see them!

For the first assignment, shouldn't a[1, 1..] == [[4, 5, 6]] due to the nil-terminated range in the second param? In that case, you'd be assigning a 1-by-3 into a 1-by-3.

christopherzimmerman commented 4 years ago

When you slice an axis of a Tensor using an integer, that dimension is removed from the final shape. So since you access Axis 1, Element 1, the result of the slice would be [6, 7, 8], with a shape of [3] You could access with a one-element range, and the assignment would work, since the dimension wouldn't be removed.

a[1...2, 1..] = b
# [[ 3,  4,  5,  6],
#  [ 5, 11, 12, 13]]

Here is the difference in slices:

puts a[1, 1..]
puts a[1...2, 1..]

# [6, 7, 8]
# [[6, 7, 8]]

Also to your point about the operators, most of the slicing/broadcasting functionality is defined in array/array.cr, although the methods should show up in the documentation linked in the README.