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
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;
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 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.
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.
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]