Check if a record exists in a Julia array - julia

Say we have a matrix:
A = [1.0 2.0 3.0; 4.0 5.0 6.0] #2×3 Matrix{Float64}
and a record:
b = [1.0 2.0 3.0] #1×3 Matrix{Float64}
what is the most efficient way to check if record b exists in matrix A in Julia?
Doing b in A returns false.
And writing a nested for-loop when we may have a large matrix (many dimensions and many rows) to check seems inefficient.

For a 2D matrix like your example, the check is simple.
vec(b) in eachrow(A)
true
For large nD arrays, one can use
vec(b) in eachslice(A, dims=1)
true

If the rows in the matrix are unsorted I would just do:
findfirst(==(vec(b)), eachrow(A))
If the rows are sorted (which is recommended when you do the search many times) I would consider using searchsorted over a vector of views of array rows.

This answer simply augments and builds on top of suggestions provided by Przemyslaw Szufel and AboAmmar and is intended to provide an efficiency comparison for reference:
using BenchmarkTools
A = [1.0 2.0 3.0; 4.0 5.0 6.0]
b = [1.0 2.0 3.0]
function my_findfirst(A, b)
if(findfirst(==(vec(b)), eachrow(A)) !== nothing)
return true
end
return false
end
function my_eachslice(A, b)
if vec(b) in eachslice(A, dims=1)
return true
end
return false
end
function my_eachrow(A,b)
if (vec(b) in eachrow(A))
return true
end
return false
end
#btime my_findfirst($A, $b) #66.846 ns (2 allocations: 80 bytes)
#btime my_eachslice($A, $b) #305.164 ns (5 allocations: 144 bytes)
#btime my_eachrow($A, $b) # 66.846 ns (2 allocations: 80 bytes)

The cost of finding a row in a table depends greatly on exact circumstance. In the OP example, the table is tiny and the row is inside the table. Usually, tables are larger and the worst-case is when row isn't in the table. Essentially Databases are optimized for these operations and indexing is an important part of database query optimization.
The following is a simple variant of the other answers to this question. It uses the first column of matrix to filter candidate rows for further equality checking. In all but trivial cases, it will be faster. This is because accessing matrices by column is faster due to locality of memory access in Julia default matrix memory layout.
Takeaway point: Lots and lots of room for optimization, if it is necessary.
function my_scancolumnfirst(A, b)
N = size(A,1)
c = #view A[:,1]
bv = vec(b)
b1 = bv[1]
#inbounds for i=1:N
if c[i]==b1 && bv==#view A[i,:]
return true
end
end
return false
end
A sample benchmark:
A = rand(500,500);
b = A[350,:];
using BenchmarkTools
#btime my_findfirst($A, $b)
# 2.682 μs (0 allocations: 0 bytes)
# true
#btime my_scancolumnfirst($A, $b)
# 907.333 ns (0 allocations: 0 bytes)
# true
Custom version shows >2x improvement.

Related

Type-stability in Julia's product iterator

I am trying to make A in the following code type-stable.
using Primes: factor
function f(n::T, p::T, k::T) where {T<:Integer}
return rand(T, n * p^k)
end
function g(m::T, n::T) where {T<:Integer}
i = 0
for A in Iterators.product((f(n, p, T(k)) for (p, k) in factor(m))...)
i = sum(A)
end
return i
end
Note that f is type-stable. The variable A is not type-stable because the product iterator will return different sized tuples depending on the values of n and m. If there was an iterator like the product iterator that returned a Vector instead of a Tuple, I believe that the type-instability would go away.
Does anyone have any suggestions to make A type-stable in the above code?
Edit: I should add that f returns a variable-sized Vector of type T.
One way I have solved the type-stability is by doing this.
function g(m::T, n::T) where {T<:Integer}
B = Vector{T}[T[]]
for (p, k) in factor(m)
C = Vector{T}[]
for (b, r) in Iterators.product(B, f(n, p, T(k)))
c = copy(b)
push!(c, r)
push!(C, c)
end
B = C
end
for A in B
i = sum(A)
end
return i
end
This (and in particular, A) is now type-stable, but at the cost lots of memory. I'm not sure of a better way to do this.
It's not easy to get this completely type stable, but you can isolate the type instability with a function barrier. Convert the factorization to a tuple in an outer function, which you pass to an inner function which is type stable. This gives just one dynamic dispatch, instead of many:
# inner, type stable
function _g(n, tup)
i = 0
for A in Iterators.product((f(n, p, k) for (p, k) in tup)...)
i += sum(A) # or i = sum(A), whatever
end
return i
end
# outer function
g(m::T, n::T) where {T<:Integer} = _g(n, Tuple(factor(m)))
Some benchmarks:
julia> #btime g(7, 210); # OP version
149.600 μs (7356 allocations: 172.62 KiB)
julia> #btime g(7, 210); # my version
1.140 μs (6 allocations: 11.91 KiB)
You should expect to hit compilation occasionally, whenever you get a number that contains a new number of factors.

Performance of SIMD vectorized functions in Julia

I am trying to test the first function on the github page of SIMD.jl (https://github.com/eschnett/SIMD.jl). This is the script:
using SIMD
using BenchmarkTools
function vadd!(xs::Vector{T}, ys::Vector{T}, ::Type{Vec{N,T}}) where {N, T}
#assert length(ys) == length(xs)
#assert length(xs) % N == 0
lane = VecRange{N}(0)
#inbounds for i in 1:N:length(xs)
xs[lane + i] += ys[lane + i]
end
end
b1 = Vector{Float64}([2.0,2.0,3.0,4.0,2.0,2.0,3.0,4.0])
b2 = Vector{Float64}([2.0,2.0,3.0,4.0,2.0,2.0,3.0,4.0])
c1 = Vec{8,Float64}((2.0,2.0,3.0,4.0,2.0,2.0,3.0,4.0))
c1t = typeof(c1)
#btime b1+b2
println(b1+b2)
#btime vadd!($b1, $b2, Vec{8, Float64})
println(b1)
#btime vadd!($b1, $b2, $c1t)
The results I get are not fully clear to me:
57.739 ns (1 allocation: 144 bytes)
[4.0, 4.0, 6.0, 8.0, 4.0, 4.0, 6.0, 8.0]
18.400 ns (0 allocations: 0 bytes)
[2.1001006e7, 2.1001006e7, 3.1501509e7, 4.2002012e7, 2.1001006e7, 2.1001006e7, 3.1501509e7, 4.2002012e7]
127.009 ns (0 allocations: 0 bytes)
The first call takes 57.739 ns, which is reasonable since the '+' operation should be already vectorised and it takes some extra time to allocate the memory required to allocate the output vector.
The second call seems successful in terms of computational time (18.400 ns). However, the output stored in b1 is totally wrong. Any idea?
The third is unclear as well to me. I am simply passing the type Vec using an auxiliary variables and the resulting function is almost one order of magnitude slower. The output result is wrong as well, but this is in line with the result of the second call. Any clue on this speed loss?
Thank you in advance.

How to test if all elements in a array have the same value in Julia

I am trying to write a simple program that uses Julia to test if all elements of an array are the same. Is there a simple way to do this in Julia?
allunique tests if all elements of an array are unique. In order to test if all elements of an array are the same you can write e.g.:
function allequal(itr)
local x
isfirst = true
for v in itr
if isfirst
x = v
isfirst = false
else
isequal(x, v) || return false
end
end
return true
end
and now you have
julia> allequal([1,2,3])
false
julia> allequal([1,2,1])
false
julia> allequal([1,1,1])
true
You could write a shorter function like e.g.:
f1(itr) = length(Set(itr)) <= 1
but it probably will be slower (I have not run the benchmarks).
or you could write something like:
f2(itr) = length(itr) == 0 ? true : all(isequal(itr[1]), itr)
if your iterable has length defined and supports indexing.
length(itr)==0 || all( ==(itr[1]), itr)
This seems to be 3x faster than the proposed allequal function.
Some benchmarks:
julia> allequal_2(itr) = length(itr)==0 || all( ==(itr[1]), itr);
julia> const vv = ones(10000000)*3;
julia> #btime allequal($vv)
13.212 ms (0 allocations: 0 bytes)
true
julia> #btime allequal_2($vv)
4.178 ms (0 allocations: 0 bytes)
true
What is even more interesting it is 2x faster than the proposed very similar f2 function:
julia> #btime f2($vv)
9.509 ms (0 allocations: 0 bytes)

Julia function to return non-unique elements of an array

Julia base has the unique function that returns a vector containing only the unique elements of an array (or any iterable). I was looking for a nonunique function to return an array containing all the elements that appear at least twice in its input. As far as I can tell Julia does not have such a function, which I found a bit surprising.
My first attempt was as follows:
function nonunique(x::AbstractArray)
uniqueindexes = indexin(unique(x),x)
nonuniqueindexes = setdiff(1:length(x),uniqueindexes)
unique(x[nonuniqueindexes])
end
But inspired by Bogumił Kamiński's answer to indices of unique elements of vector in Julia I wrote a second version:
function nonunique(x::AbstractArray{T}) where T
uniqueset = Set{T}()
duplicatedset = Set{T}()
duplicatedvector = Vector{T}()
for i in x
if(i in uniqueset)
if !(i in duplicatedset)
push!(duplicatedset, i)
push!(duplicatedvector, i)
end
else
push!(uniqueset, i)
end
end
duplicatedvector
end
In my tests, this version is about 4 times faster. It has the nice property that the return is ordered in the order that the second (first repeat) of each set of equivalent elements originally appear. I believe that in is faster when checking for membership of a Set than an Array, which accounts for having the two variables duplicatedset and duplicatedvector.
Is it really necessary for me to "roll my own" nonunique function and can the second version be improved?
You can get higher performance by sorting the list and then searching for duplicates:
function nonunique2(x::AbstractArray{T}) where T
xs = sort(x)
duplicatedvector = T[]
for i=2:length(xs)
if (isequal(xs[i],xs[i-1]) && (length(duplicatedvector)==0 || !isequal(duplicatedvector[end], xs[i])))
push!(duplicatedvector,xs[i])
end
end
duplicatedvector
end
Here are sample results:
julia> x = rand(1:1000,1000);
julia> using BenchmarkTools
julia> nn = #btime nonunique($x);
42.240 μs (39 allocations: 71.23 KiB)
julia> nn2s = #btime nonunique2($x);
26.453 μs (10 allocations: 16.33 KiB)
julia> sort(nn) == sort(nn2s)
true
It will be much better if you can do in-place sorting:
function nonunique2!(x::AbstractArray{T}) where T
sort!(x)
duplicatedvector = T[]
for i=2:length(x)
if (isequal(x[i],x[i-1]) && (length(duplicatedvector)==0 || !isequal(duplicatedvector[end], x[i])))
push!(duplicatedvector,x[i])
end
end
duplicatedvector
end
Here are the results (the same data)
julia> nn2 = #btime nonunique2!($x)
9.813 μs (9 allocations: 8.39 KiB)
julia> sort(nn) == sort(nns)
true
To add to the answer above, as its limitation is that the type T must be sortable and it is not order-preserving I have two possible solutions.
Here is another non-order preserving solution that uses StatsBase.jl. It can be faster than the sorting solution or slower depending on the density of the duplicates (also it does more work, but in some applications this information might be useful):
nonunique3(x) = [k for (k, v) in countmap(x) if v > 1]
If you want to speed up the order preserving approach you could do something like:
function nonunique4(x::AbstractArray{T}) where T
status = Dict{T, Bool}()
duplicatedvector = Vector{T}()
for i in x
if haskey(status, i)
if status[i]
push!(duplicatedvector, i)
status[i] = false
end
else
status[i] = true
end
end
duplicatedvector
end
In general benchmarking them is tricky as performance will depend on:
density of duplicates and over double duplicates in x
the size of type T (e.g. if it were a very large immutable type things might change vs. standard situation)
Not really an answer (excellent answers are above) but a comment that the original implementation can be cleaned a little to:
function nonunique1(x::AbstractArray{T}) where T
uniqueset = Set{T}()
duplicatedset = Set{T}()
for i in x
if(i in uniqueset)
push!(duplicatedset, i)
else
push!(uniqueset, i)
end
end
collect(duplicatedset)
end
i.e. you don't need to check for existence before pushing to a set, and you don't need to fill a vector and set separately. It's still not as fast as the sorting implementation.

Julia pi approximation slow

I have pi approximation code very similar to that on official page:
function piaprox()
sum = 1.0
for i = 2:m-1
sum = sum + (1.0/(i*i))
end
end
m = parse(Int,ARGS[1])
opak = parse(Int,ARGS[2])
#time for i = 0:opak
piaprox()
end
When I try to compare time of C and Julia, then Julia is significantly slower, almost 38 sec for m = 100000000 (time of C is 0.1608328933 sec). Why this is happening?
julia> m=100000000
julia> function piaprox()
sum = 1.0
for i = 2:m-1
sum = sum + (1.0/(i*i))
end
end
piaprox (generic function with 1 method)
julia> #time piaprox()
28.482094 seconds (600.00 M allocations: 10.431 GB, 3.28% gc time)
I would like to mention two very important paragraphs from Performance Tips section of julia documentation:
Avoid global variables A global variable might have its value, and
therefore its type, change at any point. This makes it difficult for
the compiler to optimize code using global variables. Variables should
be local, or passed as arguments to functions, whenever possible.....
The macro #code_warntype (or its function variant code_warntype()) can
sometimes be helpful in diagnosing type-related problems.
julia> #code_warntype piaprox();
Variables:
sum::Any
#s1::Any
i::Any
It's clear from #code_warntype output that compiler could not recognize types of local variables in piaprox(). So we try to declare types and remove global variables:
function piaprox(m::Int)
sum::Float64 = 1.0
i::Int = 0
for i = 2:m-1
sum = sum + (1.0/(i*i))
end
end
julia> #time piaprox(100000000 )
0.009023 seconds (11.10 k allocations: 399.769 KB)
julia> #code_warntype piaprox(100000000);
Variables:
m::Int64
sum::Float64
i::Int64
#s1::Int64
EDIT
as #user3662120 commented, the super fast behavior of the answer is result of a mistake, without a return value LLVM might ignore the for loop, by adding a return line the #time result would be:
julia> #time piaprox(100000000)
0.746795 seconds (11.11 k allocations: 400.294 KB, 0.45% gc time)
1.644934057834575

Resources