Using gather() to retrieve rows from 3d tensor with 2d tensor in Pytorch - multidimensional-array

I'm new to Pytorch and having an issue with the gather() function:
I have a 3d tensor, x[i,j,k]:
x=tensor([[[1,2,3],
[4,5,6],
[7,8,9]],
[[10,11,12],
[13,14,15],
[16,17,18]]])
I have an index tensor:
index=tensor([[1,2,0]])
I want to use the values of index to iterate over x[j] and fetch the (complete) rows. I've tried gather() with all dims, squeezing, unsqueezing and it never seems to get the output I'm looking for, which would be:
output=tensor([[[4,5,6],
[7,8,9],
[1,2,3]],
[[13,14,15],
[16,17,18],
[10,11,12]]])
I've also tried repeating the values of index to get the same shape as x but it did not work.
I know I can do this with an if loop, but I'm pretty sure I can do it with gather() as well. Thanks for the help

Let us set up the two tensors x and index:
>>> x = torch.arange(1,19).view(2,3,3)
>>> index = torch.tensor([[1,2,0]])
What you are looking for is the torch.gather operation:
out[i][j][k] = x[i][index[i][j][k]][k]
In other to apply this function, you need to expand index to the same shape as out. Additionally, a transpose operation is required to flip your original index tensor.
>>> i = index.T.expand_as(x)
tensor([[[1, 1, 1],
[2, 2, 2],
[0, 0, 0]],
[[1, 1, 1],
[2, 2, 2],
[0, 0, 0]]])
If you compare with the pseudo code line above, you can see how every element of i represents the row of the original tensor x the operator will gather values from.
Applying the function gets us to the desired result:
x.gather(dim=1, index=index.T.expand_as(x))
tensor([[[ 4, 5, 6],
[ 7, 8, 9],
[ 1, 2, 3]],
[[13, 14, 15],
[16, 17, 18],
[10, 11, 12]]])

Related

How can I concisely define multidimensional `Vec`s in Rust?

When initializing a multidimensional Vec in Rust, I can use the vec!-macro like this:
vec![vec![0; 100]; 200]
However, this gets messy for Vecs of higher dimensions. Currently, I am using this:
vec![vec![vec![vec![vec![vec![vec![vec![0; N-1]; N-1]; N-1]; N-1]; 2]; 2]; 2]; 2]
This is not very concise, and also the order in which the dimensions are written is reverse to the indexing order. Is there a more concise way to do this? I am looking for something like
vec![0; 2, 2, 2, 2, N-1, N-1, N-1, N-1]
The ndarray crate allows you to have an N-dimensional array. For anything above 6 dimensions, you can use the ArrayD type. You can create a dynamic dymension using IxDyn - documentation with examples.
Example for a 7x7x7...x7 array initialization and element access:
let mut array_7d = ArrayD::<f64>::zeros(IxDyn(&[7, 7, 7, 7, 7, 7, 7, 7]));
let index = IxDyn(&[0, 0, 0, 0, 0, 0, 0, 0]);
array_7d[&index] = 1.0;

Julia idiomatic way to split vector to subvectors based on condition

Let's say I have a vector a = [1, 0, 1, 2, 3, 4, 5, 0, 5, 6, 7, 8, 0, 9, 0] and I want to split it to smaller vectors based on a condition depending on value in that array. E.g. value being zero.
Thus I want to obtain vector of following vectors
[1, 0]
[1, 2, 3, 4, 5, 0]
[5, 6, 7, 8, 0]
[9, 0]
So far this was working for me as a naive solution, but it loses the type.
function split_by_λ(a::Vector, λ)
b = []
temp = []
for i in a
push!(temp, i)
if λ(i)
push!(b, temp)
temp = []
end
end
b
end
split_by_λ(a, isequal(0))
Then I tried to play with ranges, which feels a bit more idiomatic, and does not lose the type.
function split_by_λ(a::Vector, λ)
idx = findall(λ, a)
ranges = [(:)(i==1 ? 1 : idx[i-1]+1, idx[i]) for i in eachindex(idx)]
map(x->a[x], ranges)
end
split_by_λ(a, isequal(0))
but it still feels very cumbersome regarding it's a rather simple task.
Is there something I'm missing, some easier way?
Maybe someone has a shorter idea but here is mine:
julia> inds = vcat(0,findall(==(0),a),length(a))
julia> getindex.(Ref(a), (:).(inds[1:end-1].+1,inds[2:end]))
5-element Array{Array{Int64,1},1}:
[1, 0]
[1, 2, 3, 4, 5, 0]
[5, 6, 7, 8, 0]
[9, 0]
[]
Or if you want to avoid copying a
julia> view.(Ref(a), (:).(inds[1:end-1].+1,inds[2:end]))
5-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
[1, 0]
[1, 2, 3, 4, 5, 0]
[5, 6, 7, 8, 0]
[9, 0]
0-element view(::Array{Int64,1}, 16:15) with eltype Int64
Pretty much the same as Przemyslaw's answer, but maybe less cryptic dense:
function split_by(λ, a::Vector)
first, last = firstindex(a), lastindex(a)
splits = [first-1; findall(λ, a); last]
s1, s2 = #view(splits[1:end-1]), #view(splits[2:end])
return [view(a, i1+1:i2) for (i1, i2) in zip(s1, s2)]
end
Also, I changed the signature to the conventional one of "functions first", which allows you to use do-blocks. Additionally, this should work with offset indexing.
One could surely get rid of the intermediate allocations, but I think that gets ugly without yield:
function split_by(λ, a::Vector)
result = Vector{typeof(view(a, 1:0))}()
l = firstindex(a)
r = firstindex(a)
while r <= lastindex(a)
if λ(a[r])
push!(result, #view(a[l:r]))
l = r + 1
end
r += 1
end
push!(result, #view(a[l:end]))
return result
end

Does slice or index of chainer.Variable to get item in chainer has backward ability?

Does the following code chainer.Variable still have ability to hold graph and can backward (gradient flow) after slice(a[0,1] or index(a[0]):
>>> a = chainer.Variable(np.array([[1,2,3],[10,11,12]]))
>>> a
variable([[ 1, 2, 3],
[10, 11, 12]])
>>> a[0]
variable([1, 2, 3])
>>> a[0, 1]
variable([1])
Yes. Indexing of chainer.Variable supports backprop.

Find all cycles of given length (networkx)

Given an undirected graph how do you go about finding all cycles of length n (using networkx if possible). So input would be the Graph and n and the function would return all cycles of that length.
You can use networkx.cycle_basis.
>>> G = networkx.Graph()
>>> networkx.add_cycle(G, [0, 1, 2, 3])
>>> networkx.add_cycle(G, [0, 3, 4, 5])
>>> print(networkx.cycle_basis(G))
[[3, 4, 5, 0], [1, 2, 3, 0]]
>>> print(networkx.cycle_basis(G, root = 2))
[[1, 2, 3, 0]]
Then, you can check the length of each list as you see fit.

SML: Combining Two Lists

I have the following function for combining two lists into one. It's supposedly of type:
# : 'a list * 'a list -> 'a list
fun # (nil, k) = k
| # (x::l, k) = x :: #(l,k);
Let's say we have two lists: [1, 2, 3] and [4, 5, 6]. If I call:
#([1, 2, 3], [4, 5, 6])
1::#([2, 3], [4, 5, 6])
1::2::#([3], [4, 5, 6])
1::2::3::#(nil, [4, 5, 6])
But here we reach the base case and our # call returns the list [4, 5, 6], yielding:
1::2::3::[4, 5, 6]
which is obviously not what I want. Is the function definition correct or am I misunderstanding something?
Yes your function definition is correct.
:: or Cons as it is called in Lisp and other functional programming languages is used for creating lists. It takes a value and a list (which may be empty) and creates a new list with the former prepended to the latter. So for example 42::[17, 23] equals [42, 17, 23].
Cons is right associative which means that your list
1::2::3::[4,5,6]
can be written as
(1::(2::(3::[4,5,6])))
and by successive reductions we get
[1,2,3,4,5,6]

Resources