Difference between == and === in Julia arrays and vectors - arrays

Why typeof(a) == typeof(v) is true and typeof(a) === typeof(v) is also true, but a == v is true and a === v is false??
julia> a = Array([1,2,3])
3-element Vector{Int64}:
1
2
3
julia> v = Vector([1,2,3])
3-element Vector{Int64}:
1
2
3
julia> typeof(a) == typeof(v)
true
julia> typeof(a) === typeof(v)
true
julia> a == v
true
julia> a === v
false

=== determines whether compared objects are identical, in the sense that no program could distinguish them.
So typeof(a) and typeof(v) are identical (the type is Vector{Int64} (alias for Array{Int64, 1}) in both cases) so they compare with === as true.
However a and v, although they have the same contents do not have the same memory location (you will likely get different values):
julia> pointer(a)
Ptr{Int64} #0x000001e6025161b0
julia> pointer(v)
Ptr{Int64} #0x000001e602524f90
so they are distinguishable, thus different when compared by ===.
The == is different as it considers just the equality of values. In this case a and v are arrays of the same shape and storing the same numbers, so comparing them using == returns true.
In a sense == is less strict than === when doing a comparison.
Note that e.g. if you used tuples instead of arrays:
julia> t1 = (1, 2, 3)
(1, 2, 3)
julia> t2 = (1, 2, 3)
(1, 2, 3)
julia> t1 === t2
true
The reason is that tuples are not allocated so even if you create them twice they are in this case considered identical when being compared with === (they are not distinguishable).
Finally notice that if you write:
julia> a2 = a
3-element Vector{Int64}:
1
2
3
julia> a2 === a
true
The reason is that a2 and a variables point to the same array (i.e. array having the same location in memory).

Related

How to get a value in a numpy julia array passing as argument a list

Let's imagine I have a numpy array in Julia
A = np.array([[1,2],[3,4]])
I want to get the value at position say (1,1), which is, in Julia, 1.
I would like to pass a list as argument :
I = [1,1] such that println(A[I]) returns 1 as expected.
I can't find a way to do that. In python, I know that we can pass a tuple to a numpy array, but It doesn't work in Julia.
Is there an easy was to do so?
2 equivalent ways, both using splatting (...):
julia> A = [1 2; 3 4]
2×2 Matrix{Int64}:
1 2
3 4
julia> I = (1, 1)
(1, 1)
julia> A[I...]
1
julia> getindex(A, I...)
1

Julia conditional assignment based on | (or) in multidimensional array

I have a 3D array, and based on the values in one "row" using the or operator "|", I would like to substitute values in a different row. I've attempted to build a mwe, here goes:
# create new array
Pop = fill(Int8(3), 1, 3, 4)
# change values in one row of array
Pop[:,:,2] = [5,6,7]
# change values in second row of array
Pop[:,:,4] = [9,10,11]
# attempt conditional substitution, if element-wise value of Pop[:,:,2] equals either 6 or 7, then substitute the corresponding element wise (across row in dimension 3) for 8
Pop[:,:,2] == 6|7 Pop[:,:,3] .= 8
# to produce this end result
Pop[:,:,3] = [3,8,8]
Pop
I'm missing something in the syntax for the substitution. Thx. J
This works, but requires a temporary variable.
julia> temp = Pop[:,:,3]
1×3 Array{Int8,2}:
3 3 3
julia> temp[(Pop[:,:,2] .== 6) .| (Pop[:,:,2] .== 7)] .= 8
2-element view(::Array{Int8,1}, [2, 3]) with eltype Int8:
8
8
julia> Pop[:,:,3] = temp
1×3 Array{Int8,2}:
3 8 8
I think it's clearer to separate the stages:
julia> Pop = fill(Int8(3), 1, 3, 4);
julia> Pop[:,:,2] .= [5 6 7];
julia> ind = findall(x -> x==6 || x==7, Pop[:,:,2])
2-element Vector{CartesianIndex{2}}:
CartesianIndex(1, 2)
CartesianIndex(1, 3)
julia> Pop[ind, 3] .= 8;
julia> Pop[:, :, 3]
1×3 Matrix{Int8}:
3 8 8
You could also write ind == findall(in((6,7)), #view Pop[:,:,2]). You can write ind_bool = (Pop[:,:,2] .== 6) .| (Pop[:,:,2] .== 7) but I'm not sure Pop[ind_bool, 3] .= 8 will work on all versions of Julia.
Another way is just to write an explicit loop. We could loop separately over for i in axes(Pop,1), j in axes(Pop,2) but we can also use these multi-dimensional CartesianIndex things again:
julia> Pop = fill(Int8(3), 1, 3, 4);
julia> Pop[:,:,2] .= [5 6 7];
julia> for i in CartesianIndices(#view Pop[:,:,2])
x = Pop[i,2]
if x==6 | x==7
Pop[i,3] = 8
end
end
julia> Pop[:, :, 3]
1×3 Matrix{Int8}:
3 3 8
Thanks to help from #mcabbott and #Nathan Boyer, I've got a solution that works, even managed to add more conditions and it seems to work. I'll post the mwe solution.
# create new array
Pop = fill(Int8(3), 1, 3, 4)
# change some values
Pop[:,:,2] = [5,6,7]
Pop[:,:,4] = [9,10,9]
# what does it look like
Pop
# code to check condition values
# create temp array to be altered if conditions met
temp = Pop[:,:,3]
# see which indexes meet conditions, in this case
# array "Pop[:,:,2]" contains either 6 or 7 ... and ...
# array "Pop[:,:,4]" contains values >= 10
temp[(Pop[:,:,2] .== 6) .|
(Pop[:,:,2] .== 7) .&
(Pop[:,:,4] .>= 10)] .= 8
# make changes to original array based on temp
Pop[:,:,3] = temp
# check if it did the job correctly
Pop

What is the mathematical explanation of adding 2d Array and 1d Array?

I can't seem to replicate this numpy arithmetic. I'm using Julia, but want to know the mathematical explanation for this code. It seems to break what I know about Linear Algebra.
X = np.arange(-5, 5, 0.2).reshape(-1, 1)
X.shape ## (50, 1)
test = np.sum(X**2, 1).reshape(-1, 1) + np.sum(X**2, 1)
test.shape ## (50, 50)
In Julia, I would write
X = reshape(collect(range(-5, stop=5, length=N)), :, 1);
size(X) ## (50, 1)
test = sum(X.^2, dims=2) + vec(sum(X.^2, dims=2));
size(test) ## (50, 1)
I'm trying to think how a 50x50 matrix would be the result of adding two vectors? I know numpy uses a lot of broadcasting under the hood, but it doesn't seem clear to me what this is doing.
What is the mathematical notation or Julia equivalent for what numpy is doing here?
Your are doing a lot of stuff that really obscures your point, which, I believe, concerns how to add arrays of different shapes.
Python:
In [21]: x = np.random.rand(5, 1)
In [22]: x.shape
Out[22]: (5, 1)
In [23]: y = np.random.rand(1, 4)
In [24]: y.shape
Out[24]: (1, 4)
In [25]: (x + y).shape
Out[25]: (5, 4)
Julia:
julia> x = rand(5);
julia> y = rand(1, 4);
julia> x + y
ERROR: DimensionMismatch("dimensions must match")
julia> x .+ y
5×4 Array{Float64,2}:
1.95779 1.31897 1.23345 1.32423
1.78126 1.14244 1.05692 1.14771
1.08306 0.444243 0.35872 0.449509
1.69756 1.05874 0.97322 1.06401
1.18661 0.547789 0.462265 0.553054
julia> size(x .+ y)
(5, 4)
As you can tell, Python broadcasts arrays by default, while Julia requires that you specifically ask for it, by using the dot operator, ..
It is exactly because it does not make sense to add two arrays of different shapes, that Julia does not broadcast by default. Similarly, with multiplication, * and .* differ:
julia> A = [1 2; 3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> B = [4 5; 6 7]
2×2 Array{Int64,2}:
4 5
6 7
julia> A * B
2×2 Array{Int64,2}:
16 19
36 43
julia> A .* B
2×2 Array{Int64,2}:
4 10
18 28
The ordinary * is matrix multiplication, while the latter is elementwise array multiplication.
Another example:
julia> A = [1 2 3; 4 5 6]
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> b = [7, 8]
2-element Array{Int64,1}:
7
8
julia> A * b
ERROR: DimensionMismatch("matrix A has dimensions (2,3), vector B has length 2")
julia> A .* b
2×3 Array{Int64,2}:
7 14 21
32 40 48

Filling Array with 2d function values in Julia

I am wondering if there is a 1 liner to do this assignment in the array in Julia:
h = .1
L = 1
x = 0:h:L
n = length(x)
discretized = zeros(n,n)
#really any old function
f(x,y) = x*y + cos(x) + sin(y)
for i in 1:n
for j in 1:n
discretized[i, j] = f(x[i], x[j])
end
end
Or do I explicitly have to write out the loops?
You could broadcast the function over an array an its transpose - julia will return the result as a 2d Array:
x = 0:0.1:1
f(x,y) = x*y + cos(x) + sin(y)
A = f.(x,x') # the `.` before the bracket broadcasts the dimensions
# 11×11 Array{Float64,2}
or if have more complicated expressions or functions and don't want to write out lots of dots use the #. macro, e.g:
A = #. f(x,x') + x^2
Once A already exists, you can also do
#. A = f(x,x') + x^2
which uses .= to write the result locally to each element of A, and hence is non-allocating.
Broadcasting goes much further than this easy extension of scalar functions to arrays, allowing "fusion" of multiple calculations into a single fast operation https://julialang.org/blog/2017/01/moredots
You could do:
discretized = [f(i, j) for i in x, j in x]
For more information, see https://docs.julialang.org/en/v1/manual/arrays/#Comprehensions-1
Edit: Based on the comments, here's a brief overview of what the : operator does in indexing:
julia> a = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> a[:]
3-element Array{Int64,1}:
1
2
3
julia> ans === a
false
julia> a[:] .= [2, 3, 4]
3-element view(::Array{Int64,1}, :) with eltype Int64:
2
3
4
julia> a
3-element Array{Int64,1}:
2
3
4

Julia language: sub vs. slice function

Could someone explain in simple terms the difference between julia's v0.4 function:
sub and slice (and maybe slicedim)
Some simple example would be greatly appriciated.
Thanks a lot
The difference is that slice drops all dimensions "sliced" with a scalar (non-vector), while sub often retains them. For example:
julia> A = rand(3,3)
3x3 Array{Float64,2}:
0.403464 0.229403 0.924686
0.953741 0.175086 0.49139
0.0290678 0.705564 0.567355
julia> a = slice(A, 2, :) # this will be 1-dimensional
3-element SubArray{Float64,1,Array{Float64,2},(Int64,Colon),2}:
0.953741
0.175086
0.49139
julia> b = sub(A, 2, :) # this will be 2-dimensional
1x3 SubArray{Float64,2,Array{Float64,2},(UnitRange{Int64},Colon),2}:
0.953741 0.175086 0.49139
julia> size(a)
(3,)
julia> size(b)
(1,3)
There's one exception: sub drops dimensions indexed with a scalar if they are "trailing" dimensions, meaning there are no later dimensions indexed with a vector:
julia> a = slice(A, :, 2)
3-element SubArray{Float64,1,Array{Float64,2},(Colon,Int64),2}:
0.229403
0.175086
0.705564
julia> b = sub(A, :, 2)
3-element SubArray{Float64,1,Array{Float64,2},(Colon,Int64),2}:
0.229403
0.175086
0.705564
julia> size(a)
(3,)
julia> size(b)
(3,)
If you slice with a range, then you get behavior like sub:
julia> a = slice(A, 2:2, :)
1x3 SubArray{Float64,2,Array{Float64,2},(UnitRange{Int64},Colon),1}:
0.953741 0.175086 0.49139
julia> size(a)
(1,3)
It's not the length of the index that matters, it's the type: any dimension indexed with a non-scalar will be retained.

Resources