Identify which rows (or columns) have values in sparse Matrix - julia

I need to identify the rows (/columns) that have defined values in a large sparse Boolean Matrix. I want to use this to 1. slice (actually view) the Matrix by those rows/columns; and 2. slice (/view) vectors and matrices that have the same dimensions as the margins of a Matrix. I.e. the result should probably be a Vector of indices / Bools or (preferably) an iterator.
I've tried the obvious:
a = sprand(10000, 10000, 0.01)
cols = unique(a.colptr)
rows = unique(a.rowvals)
but each of these take like 20ms on my machine, probably because they allocate about 1MB (at least they allocate cols and rows). This is inside a performance-critical function, so I'd like the code to be optimized. The Base code seems to have an nzrange iterator for sparse matrices, but it is not easy for me to see how to apply that to my case.
Is there a suggested way of doing this?
Second question: I'd need to also perform this operation on views of my sparse Matrix - would that be something like x = view(a,:,:); cols = unique(x.parent.colptr[x.indices[:,2]]) or is there specialized functionality for this? Views of sparse matrices appear to be tricky (cf https://discourse.julialang.org/t/slow-arithmetic-on-views-of-sparse-matrices/3644 – not a cross-post)
Thanks a lot!

Regarding getting the non-zero rows and columns of a sparse matrix, the following functions should be pretty efficient:
nzcols(a::SparseMatrixCSC) = collect(i
for i in 1:a.n if a.colptr[i]<a.colptr[i+1])
function nzrows(a::SparseMatrixCSC)
active = falses(a.m)
for r in a.rowval
active[r] = true
end
return find(active)
end
For a 10_000x10_000 matrix with 0.1 density it takes 0.2ms and 2.9ms for cols and rows, respectively. It should also be quicker than method in question (apart from the correctness issue as well).
Regarding views of sparse matrices, a quick solution would be to turn view into a sparse matrix (e.g. using b = sparse(view(a,100:199,100:199))) and use functions above. In code:
nzcols(b::SubArray{T,2,P}) where {T,P<:AbstractSparseArray} = nzcols(sparse(b))
nzrows(b::SubArray{T,2,P}) where {T,P<:AbstractSparseArray} = nzrows(sparse(b))
A better solution would be to customize the functions according to view. For example, when the view uses UnitRanges for both rows and columns:
# utility predicate returning true if element of sorted v in range r
inrange(v,r) = searchsortedlast(v,last(r))>=searchsortedfirst(v,first(r))
function nzcols(b::SubArray{T,2,P,Tuple{UnitRange{Int64},UnitRange{Int64}}}
) where {T,P<:SparseMatrixCSC}
return collect(i+1-start(b.indexes[2])
for i in b.indexes[2]
if b.parent.colptr[i]<b.parent.colptr[i+1] &&
inrange(b.parent.rowval[nzrange(b.parent,i)],b.indexes[1]))
end
function nzrows(b::SubArray{T,2,P,Tuple{UnitRange{Int64},UnitRange{Int64}}}
) where {T,P<:SparseMatrixCSC}
active = falses(length(b.indexes[1]))
for c in b.indexes[2]
for r in nzrange(b.parent,c)
if b.parent.rowval[r] in b.indexes[1]
active[b.parent.rowval[r]+1-start(b.indexes[1])] = true
end
end
end
return find(active)
end
which work faster than the versions for the full matrices (for 100x100 submatrix of above 10,000x10,000 matrix cols and rows take 16μs and 12μs, respectively on my machine, but these are unstable results).
A proper benchmark would use fixed matrices (or at least fix the random seed). I'll edit this line with such a benchmark if I do it.

In case the indices are not ranges, the fallback to converting to a sparse matrix works, but here are versions for indices which are Vectors. If the indices are mixed, yet another set of versions needs to be made. Quite repetitive, but this is the strength of Julia, when the versions are done, the code will choose optimized methods correctly using the types in the caller without too much effort.
function sortedintersecting(v1, v2)
i,j = start(v1), start(v2)
while i <= length(v1) && j <= length(v2)
if v1[i] == v2[j] return true
elseif v1[i] > v2[j] j += 1
else i += 1
end
end
return false
end
function nzcols(b::SubArray{T,2,P,Tuple{Vector{Int64},Vector{Int64}}}
) where {T,P<:SparseMatrixCSC}
brows = sort(unique(b.indexes[1]))
return [k
for (k,i) in enumerate(b.indexes[2])
if b.parent.colptr[i]<b.parent.colptr[i+1] &&
sortedintersecting(brows,b.parent.rowval[nzrange(b.parent,i)])]
end
function nzrows(b::SubArray{T,2,P,Tuple{Vector{Int64},Vector{Int64}}}
) where {T,P<:SparseMatrixCSC}
active = falses(length(b.indexes[1]))
for c in b.indexes[2]
active[findin(b.indexes[1],b.parent.rowval[nzrange(b.parent,c)])] = true
end
return find(active)
end
-- ADDENDUM --
Since it was noted nzrows for Vector{Int} indices is a bit slow, this is an attempt to improve its speed by replacing findin with a version exploiting sortedness:
function findin2(inds,v,w)
i,j = start(v),start(w)
res = Vector{Int}()
while i<=length(v) && j<=length(w)
if v[i]==w[j]
push!(res,inds[i])
i += 1
elseif (v[i]<w[j]) i += 1
else j += 1
end
end
return res
end
function nzrows(b::SubArray{T,2,P,Tuple{Vector{Int64},Vector{Int64}}}
) where {T,P<:SparseMatrixCSC}
active = falses(length(b.indexes[1]))
inds = sortperm(b.indexes[1])
brows = (b.indexes[1])[inds]
for c in b.indexes[2]
active[findin2(inds,brows,b.parent.rowval[nzrange(b.parent,c)])] = true
end
return find(active)
end

Related

Get a number from an array of digits

To split a number into digits in a given base, Julia has the digits() function:
julia> digits(36, base = 4)
3-element Array{Int64,1}:
0
1
2
What's the reverse operation? If you have an array of digits and the base, is there a built-in way to convert that to a number? I could print the array to a string and use parse(), but that sounds inefficient, and also wouldn't work for bases > 10.
The previous answers are correct, but there is also the matter of efficiency:
sum([x[k]*base^(k-1) for k=1:length(x)])
collects the numbers into an array before summing, which causes unnecessary allocations. Skip the brackets to get better performance:
sum(x[k]*base^(k-1) for k in 1:length(x))
This also allocates an array before summing: sum(d.*4 .^(0:(length(d)-1)))
If you really want good performance, though, write a loop and avoid repeated exponentiation:
function undigit(d; base=10)
s = zero(eltype(d))
mult = one(eltype(d))
for val in d
s += val * mult
mult *= base
end
return s
end
This has one extra unnecessary multiplication, you could try to figure out some way of skipping that. But the performance is 10-15x better than the other approaches in my tests, and has zero allocations.
Edit: There's actually a slight risk to the type handling above. If the input vector and base have different integer types, you can get a type instability. This code should behave better:
function undigits(d; base=10)
(s, b) = promote(zero(eltype(d)), base)
mult = one(s)
for val in d
s += val * mult
mult *= b
end
return s
end
The answer seems to be written directly within the documentation of digits:
help?> digits
search: digits digits! ndigits isdigit isxdigit disable_sigint
digits([T<:Integer], n::Integer; base::T = 10, pad::Integer = 1)
Return an array with element type T (default Int) of the digits of n in the given base,
optionally padded with zeros to a specified size. More significant digits are at higher
indices, such that n == sum([digits[k]*base^(k-1) for k=1:length(digits)]).
So for your case this will work:
julia> d = digits(36, base = 4);
julia> sum([d[k]*4^(k-1) for k=1:length(d)])
36
And the above code can be shortened with the dot operator:
julia> sum(d.*4 .^(0:(length(d)-1)))
36
Using foldr and muladd for maximum conciseness and efficiency
undigits(d; base = 10) = foldr((a, b) -> muladd(base, b, a), d, init=0)

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.

Diagonalizing sparse unitary matrix

I have to gather the eigenvalues of a sparse unitary matrix.
Basically there is just an element different from zero in each
row and column (it's the transfer matrix of some Markovian process).
My question here is how to proceed, what would be the best choice
among all the suite of functions. I have seen that eigs could help,
but I also saw that one has to choose the inital vector.
The following code eventually defines pdeig which returns the eigenvalues of a matrix which is a pdmatrix i.e. a product of a permutation and diagonal matrix, or in other words a matrix like the question describes. Calculating the eigenvectors quickly is also possible (they have an explicit formula):
issquare(m) = all(x->x==size(m,1),size(m))
isunique(v) = v == unique(v)
permmatrix(sigma) =
[i==sigma[j] ? 1.0 : 0.0 for i=1:length(sigma),j=1:length(sigma)]
mat2perm(m) = [findfirst(m[:,i]) for i=1:size(m,1)]
function ispdmatrix(m) # used to verify input matrix form
(r,c,v) = findnz(m)
return issquare(m) && isunique(r) && isunique(c)
end
function pdfact(m::Matrix) # factor into permutation/dilation
ispdmatrix(m) || error("input matrix must be a PD matrix")
n = size(m,1)
p = mat2perm(m)
d = [p[i]>0 ? m[p[i],i] : zero(eltype(m)) for i=1:n]
return (p,d)
end
# return eigenvalues from factored pdmatrix
function pdeig(p::Vector{Int},d::Vector)
n = length(p)
active = trues(n)
eigv = Vector{Complex{eltype(d)}}(0)
for i=1:n
if !active[i]
continue
end
if p[i]>0
j=1
cump = d[i]
k=p[i]
active[i]=false
while active[k] > 0
j+=1
cump *= d[k]
active[k] = false
k=p[k]
end
append!(eigv,[cump^(1.0/j)*exp(2*im*π*m/j) for m=1:j])
else
push!(eigv,0.0 + 0.0im)
end
end
return eigv
end
pdeig(m::Matrix) = pdeig(pdfact(m)...)
n = 4 # testing vector to matrix transformation of permutations
σ=randperm(n)
#assert mat2perm(permmatrix(σ))==σ
For example, the following:
m = [ 0.0 1.0 0.0 ; 2.0 0.0 0.0 ; 0.0 0.0 0.0 ]
pdeig(m)
Outputs:
3-element Array{Complex{Float64},1}:
-1.41421+1.73191e-16im
1.41421-3.46382e-16im
0.0+0.0im
Since these matrices are diagonalizable, the eigenvalues should provide the diagonal matrix (just use diagm on them).
These matrices are very structured, and a proper Julia treatment would define a type for these matrices and then define the various linear algebra functions to dispatch on this type.
In case of errors, just add a comment, and I will try to fix them (or if I happen to see a nice refactoring then I'll edit).
BTW the calculations introduce small numerical errors, these should not be a problem and can be eliminated with proper rounding (so no need to get scared of -1.0 being -1.0+1.234234e-16im)

Julia - Iterating over combinations of keys in a dictionary

Is there a nifty way to iterate over combinations of keys in a dictionary?
my dictionary has values like:
[1] => [1,2], [2,3] => [15], [3] => [6,7,8], [4,9,11] => [3], ...
what I need to do is fetch all combinations of keys that are of length 1:n where n might be fx 3
So as in the example above, I would want to iterate over
[[1], [3], [2,3], [[1],[1,2]], [[3],[2,3]], [4,9,11]]
I know I could just collect the keys, but my dictionary is rather large and I am in the middle of redesigning the entire algorithm because it starts swapping insanely when n > 3, reducing efficiency terribly
tl;dr is there a way to create a combinatoric iterator from a dictionary without collect-ing the dictionary?
The following is a straight forward implementation, which tries to minimize a bit on going through the dictionary. Additionally it uses OrderedDict so holding key indices makes sense (since Dicts don't promise consistent key iteration each time and thus meaningful key indexing).
using Iterators
using DataStructures
od = OrderedDict([1] => [1,2], [2,3] => [15], [3] => [6,7,8], [4,9,11] => [3])
sv = map(length,keys(od)) # store length of keys for quicker calculations
maxmaxlen = sum(sv) # maximum total elements in good key
for maxlen=1:maxmaxlen # replace maxmaxlen with lower value if too slow
#show maxlen
gsets = Vector{Vector{Int}}() # hold good sets of key _indices_
for curlen=1:maxlen
foreach(x->push!(gsets,x),
(x for x in subsets(collect(1:n),curlen) if sum(sv[x])==maxlen))
end
# indmatrix is necessary to run through keys once in next loop
indmatrix = zeros(Bool,length(od),length(gsets))
for i=1:length(gsets) for e in gsets[i]
indmatrix[e,i] = true
end
end
# gkeys is the vector of vecotrs of keys i.e. what we wanted to calculate
gkeys = [Vector{Vector{Int}}() for i=1:length(gsets)]
for (i,k) in enumerate(keys(od))
for j=1:length(gsets)
if indmatrix[i,j]
push!(gkeys[j],k)
end
end
end
# do something with each set of good keys
foreach(x->println(x),gkeys)
end
Is this more efficient that what you currently have? It would also be better to put the code in a function or turn it into a Julia task which produces the next keys set each iteration.
--- UPDATE ---
Using the answer about iterators from tasks in https://stackoverflow.com/a/41074729/3580870
An improved iterator-ified version is:
function keysubsets(n,d)
Task() do
od = OrderedDict(d)
sv = map(length,keys(od)) # store length of keys for quicker calculations
maxmaxlen = sum(sv) # maximum total elements in good key
for maxlen=1:min(n,maxmaxlen) # replace maxmaxlen with lower value if too slow
gsets = Vector{Vector{Int}}() # hold good sets of key _indices_
for curlen=1:maxlen
foreach(x->push!(gsets,x),(x for x in subsets(collect(1:n),curlen) if sum(sv[x])==maxlen))
end
# indmatrix is necessary to run through keys once in next loop
indmatrix = zeros(Bool,length(od),length(gsets))
for i=1:length(gsets) for e in gsets[i]
indmatrix[e,i] = true
end
end
# gkeys is the vector of vecotrs of keys i.e. what we wanted to calculate
gkeys = [Vector{Vector{Int}}() for i=1:length(gsets)]
for (i,k) in enumerate(keys(od))
for j=1:length(gsets)
if indmatrix[i,j]
push!(gkeys[j],k)
end
end
end
# do something with each set of good keys
foreach(x->produce(x),gkeys)
end
end
end
Which now enables iterating over all keysubsets up to combined size 4 in this way (after running the code from the other StackOverflow answer):
julia> nt2 = NewTask(keysubsets(4,od))
julia> collect(nt2)
10-element Array{Array{Array{Int64,1},1},1}:
Array{Int64,1}[[1]]
Array{Int64,1}[[3]]
Array{Int64,1}[[2,3]]
Array{Int64,1}[[1],[3]]
Array{Int64,1}[[4,9,11]]
Array{Int64,1}[[1],[2,3]]
Array{Int64,1}[[2,3],[3]]
Array{Int64,1}[[1],[4,9,11]]
Array{Int64,1}[[3],[4,9,11]]
Array{Int64,1}[[1],[2,3],[3]]
(the definition of NewTask from the linked StackOverflow answer is necessary).

Julia: swap gives errors

I'm using Julia 0.3.4
I'm trying to write LU-decomposition using Gaussian elimination. So I have to swap rows. And here's my problem:
If I'm using a,b = b,a I get an error,
but if I'm using:
function swapRows(row1, row2)
temp = row1
row1 = row2
row2 = temp
end
then everything works just fine.
Am I doing something wrong or it's a bug?
Here's my source code:
function lu_t(A::Matrix)
# input value: (A), where A is a matrix
# return value: (L,U), where L,U are matrices
function swapRows(row1, row2)
temp = row1
row1 = row2
row2 = temp
return null
end
if size(A)[1] != size(A)[2]
throw(DimException())
end
n = size(A)[1] # matrix dimension
U = copy(A) # upper triangular matrix
L = eye(n) # lower triangular matrix
for k = 1:n-1 # direct Gaussian elimination for each column `k`
(val,id) = findmax(U[k:end,k]) # find max pivot element and it's row `id`
if val == 0 # check matrix for singularity
throw(SingularException())
end
swapRows(U[k,k:end],U[id,k:end]) # swap row `k` and `id`
# U[k,k:end],U[id,k:end] = U[id,k:end],U[k,k:end] - error
for i = k+1:n # for each row `i` > `k`
μ = U[i,k] / U[k,k] # find elimination coefficient `μ`
L[i,k] = μ # save to an appropriate position in lower triangular matrix `L`
for j = k:n # update each value of the row `i`
U[i,j] = U[i,j] - μ⋅U[k,j]
end
end
end
return (L,U)
end
###### main code ######
A = rand(4,4)
#time (L,U) = lu_t(A)
#test_approx_eq(L*U, A)
The swapRows function is a no-op and has no effect whatsoever – all it does is swap around some local variable names. See various discussions of the difference between assignment and mutation:
https://groups.google.com/d/msg/julia-users/oSW5hH8vxAo/llAHRvvFVhMJ
http://julia.readthedocs.org/en/latest/manual/faq/#i-passed-an-argument-x-to-a-function-modified-it-inside-that-function-but-on-the-outside-the-variable-x-is-still-unchanged-why
http://julia.readthedocs.org/en/latest/manual/faq/#why-does-x-y-allocate-memory-when-x-and-y-are-arrays
The constant null doesn't mean what you think it does – in Julia v0.3 it's a function that computes the null space of a linear transformation; in Julia v0.4 it still means this but has been deprecated and renamed to nullspace. The "uninteresting" value in Julia is called nothing.
I'm not sure what's wrong with your commented out row swapping code, but this general approach does work:
julia> X = rand(3,4)
3x4 Array{Float64,2}:
0.149066 0.706264 0.983477 0.203822
0.478816 0.0901912 0.810107 0.675179
0.73195 0.756805 0.345936 0.821917
julia> X[1,:], X[2,:] = X[2,:], X[1,:]
(
1x4 Array{Float64,2}:
0.478816 0.0901912 0.810107 0.675179,
1x4 Array{Float64,2}:
0.149066 0.706264 0.983477 0.203822)
julia> X
3x4 Array{Float64,2}:
0.478816 0.0901912 0.810107 0.675179
0.149066 0.706264 0.983477 0.203822
0.73195 0.756805 0.345936 0.821917
Since this creates a pair of temporary arrays that we can't yet eliminate the allocation of, this isn't the most efficient approach. If you want the most efficient code here, looping over the two rows and swapping pairs of scalar values will be faster:
function swapRows!(X, i, j)
for k = 1:size(X,2)
X[i,k], X[j,k] = X[j,k], X[i,k]
end
end
Note that it is conventional in Julia to name functions that mutate one or more of their arguments with a trailing !. Currently, closures (i.e. inner functions) have some performance issues, so you'll want such a helper function to be defined at the top-level scope instead of inside of another function the way you've got it.
Finally, I assume this is an exercise since Julia ships with carefully tuned generic (i.e. it works for arbitrary numeric types) LU decomposition: http://docs.julialang.org/en/release-0.3/stdlib/linalg/#Base.lu.
-
It's quite simple
julia> A = rand(3,4)
3×4 Array{Float64,2}:
0.241426 0.283391 0.201864 0.116797
0.457109 0.138233 0.346372 0.458742
0.0940065 0.358259 0.260923 0.578814
julia> A[[1,2],:] = A[[2,1],:]
2×4 Array{Float64,2}:
0.457109 0.138233 0.346372 0.458742
0.241426 0.283391 0.201864 0.116797
julia> A
3×4 Array{Float64,2}:
0.457109 0.138233 0.346372 0.458742
0.241426 0.283391 0.201864 0.116797
0.0940065 0.358259 0.260923 0.578814

Resources