Sparse array filled with constant in Julia - julia

I have a matrix, M, with very simple form: M[i,j]=a if i==j and M[i,j]=b everywhere else. M is very large (>10000) so I cannot initialize it or store it without using some sort of sparse matrix. It seems that the best way to store this matrix would be to use some sort of sparse matrix where non-entries are set to b instead of zero.
I have tried
M = spdiagm((a-b), N, N) .+ b
But doing it this way stores M as a 10 by 10 matrix with 100 entries which seems to mean that there is no compression.
Is there a better way to initialize this matrix?

It depends on what you want to do with your matrix, but if you are only interested in performing matrix-vector products, you can use the FillArrays.jl package in conjunction with the LinearMaps.jl package:
julia> using LinearMaps, FillArrays
julia> n=100 # can be bigger
100
julia> a=2.0
2.0
julia> b=3.0
3.0
julia> M=(a-b)*LinearMap(Eye(n,n))+b*LinearMap(Ones(n,n))
100×100 LinearMaps.LinearCombination{Float64} with 2 maps:
100×100 LinearMaps.ScaledMap{Float64} with scale: -1.0 of
100×100 LinearMaps.WrappedMap{Float64} of
100×100 Eye{Float64}
100×100 LinearMaps.ScaledMap{Float64} with scale: 3.0 of
100×100 LinearMaps.WrappedMap{Float64} of
100×100 Ones{Float64}
Then you can perform operations like
julia> x=rand(n);
julia> M*x;
julia> x'*M;
julia> M*M
100×100 LinearMaps.CompositeMap{Float64} with 2 maps:
100×100 LinearMaps.LinearCombination{Float64} with 2 maps:
100×100 LinearMaps.ScaledMap{Float64} with scale: -1.0 of
100×100 LinearMaps.WrappedMap{Float64} of
100×100 Eye{Float64}
100×100 LinearMaps.ScaledMap{Float64} with scale: 3.0 of
100×100 LinearMaps.WrappedMap{Float64} of
100×100 Ones{Float64}
100×100 LinearMaps.LinearCombination{Float64} with 2 maps:
100×100 LinearMaps.ScaledMap{Float64} with scale: -1.0 of
100×100 LinearMaps.WrappedMap{Float64} of
100×100 Eye{Float64}
100×100 LinearMaps.ScaledMap{Float64} with scale: 3.0 of
100×100 LinearMaps.WrappedMap{Float64} of
100×100 Ones{Float64}
The matrices are stored using a "lazy" approach and not expanded in the memory, hence n can be big. However one must take care that M is not an AbstractMatrix :
julia> supertype(typeof(M))
LinearMap{Float64}

Related

How to narrow element type of a Vector or Array in Julia?

Consider a situation where I first create a Vector with eltype of Any incrementally; after that, I want to narrow the element type of it. How can I do that?
julia> vec = Any[1, 2, 3.]
3-element Vector{Any}:
1
2
3.0
I can use something like convert(Vector{Real}, vec). But in this case, I'm specifying the type manually while I want Julia to decide the best suitable eltype for it.
This can be achieved by broadcasting the identity function on each element of the given container (either an Array or a Vector):
julia> narrowed = identity.(vec)
3-element Vector{Real}:
1
2
3.0
Note that this doesn't lead to promoting (or demoting) the type of each individual element of the given container:
julia> typeof(narrowed[1]), typeof(narrowed[3])
(Int64, Float64)
Additional Point
However, in the case of acquainting with related functions in Julia, This can be done verbosely by using the typejoin function to achieve the type join of the container's elements. According to the concise doc of the function:
typejoin(T, S)
Return the closest common ancestor of T and S, i.e. the narrowest type from which they both inherit.
The argument of the typejoin should be a subtype of Core.Type{T} (However, it seems more sensible to define it as typejoin(T...) since it can get an indefinite number of positional arguments, not just two.)
julia> typeof.(vec)
3-element Vector{DataType}:
Int64
Int64
Float64
julia> typejoin(typeof.(vec)...)
Real
julia> convert(Vector{typejoin(typeof.(vec)...)}, vec)
3-element Vector{Real}:
1
2
3.0
Small type unions (such as Union{Int, Float64} are handled in Julia much faster and than abstract types and hence you should avoid vectors of abstract elements such as Vector{Real} in favor of unions of concrete types such as Union{Int, Float64}.
Having that said here is a code that makes such union:
julia> Vector{Union{Set(typeof.(vec))...}}(vec)
3-element Vector{Union{Float64, Int64}}:
1
2
3.0
And here is a simple test that shows that for a 100 element vector the performance difference is 4x:
julia> a1 = Vector{Union{Int, Float64}}(rand(100));
julia> a2 = Vector{Real}(rand(100));
julia> #btime minimum($a1);
428.643 ns (0 allocations: 0 bytes)
julia> #btime minimum($a2);
2.000 μs (102 allocations: 1.59 KiB)
You can use promote like this:
v = Any[1, 2, 3.0]
first.(promote.(v))
3-element Vector{Real}:
1
2
3.0000
v = Any[1, 2, 3.0, 3 + 2im]
first.(promote.(v))
4-element Vector{Number}:
1
2
3.0000
3 + 2im
But, you might be interested more in getting a vector of concrete supertypes of the elements, especially for performance purposes. So, you can use this:
v = Any[1, 2, 3.0]
reduce(vcat, promote(v...))
3-element Vector{Float64}:
1.0
2.0
3.0
v = Any[1, 2, 3.0, 3 + 2im]
reduce(vcat, promote(v...))
4-element Vector{ComplexF64}:
1.0 + 0.0im
2.0 + 0.0im
3.0 + 0.0im
3.0 + 2.0im
Or, simply:
v = Any[1, 2, 3.0];
[v...]
3-element Vector{Float64}:
1.0
2.0
3.0
v = Any[1, 2, 3.0, 3+2im];
[v...]
4-element Vector{ComplexF64}:
1.0 + 0.0im
2.0 + 0.0im
3.0 + 0.0im
3.0 + 2.0im

Julia - console behaving differently than include("myfile.jl")

I would like to execute the following code, which works perfectly well when I type every line into my Julia console on Windows 10, but throws an error because of the mismatching type LinearAlgebra.Adjoint{Float64,Array{Float64,2}} (my subsequent code expects Array{Float64,2}).
This is the code:
x = [0.2, 0.1, 0.2]
y = [-0.5 0.0 0.5]
fx = x * y
fy = fx'
return fx::Array{Float64,2}, fy::Array{Float64,2}
There is a TypeError, because fy seems to be of type LinearAlgebra.Adjoint{Float64,Array{Float64,2}} instead of Array{Float64,2}.
How can I do a transpose and get a "normal" Array{Float64,2} object ?
And why does this work when I type every line into my Julia console, but does not when I run the file via include("myfile.jl") ?
Use collect to have a copy of actual data rather than a transformed view of the original (note that this rule applies to many other similar situations):
julia> x = [0.2, 0.1, 0.2];
julia> y = [-0.5 0.0 0.5];
julia> fx = x * y
3×3 Array{Float64,2}:
-0.1 0.0 0.1
-0.05 0.0 0.05
-0.1 0.0 0.1
julia> fy = fx'
3×3 LinearAlgebra.Adjoint{Float64,Array{Float64,2}}:
-0.1 -0.05 -0.1
0.0 0.0 0.0
0.1 0.05 0.1
julia> fy = collect(fx')
3×3 Array{Float64,2}:
-0.1 -0.05 -0.1
0.0 0.0 0.0
0.1 0.05 0.1
To get a normal Matrix{Float64} use:
fy = permutedims(fx)
or
fy = Matrix(fx')
Those two are not 100% equivalent in general as fx' is a recursive adjoint operation (conjugate transpose), while permutedims is a non-recursive transpose, but in your case they will give the same result.
What does recursive adjoint mean exactly?
recursive: the conjugate transpose is applied recursively to all entries of the array (in your case you have array of numbers and transpose of a number is the same number so this does not change anything);
adjoint: if you would have complex numbers then the operation would return their complex conjugates (in your case you have real numbers so this does not change anything);
Here is an example when both things matter:
julia> x = [[im, -im], [1-im 1+im]]
2-element Array{Array{Complex{Int64},N} where N,1}:
[0+1im, 0-1im]
[1-1im 1+1im]
julia> permutedims(x)
1×2 Array{Array{Complex{Int64},N} where N,2}:
[0+1im, 0-1im] [1-1im 1+1im]
julia> Matrix(x')
1×2 Array{AbstractArray{Complex{Int64},N} where N,2}:
[0-1im 0+1im] [1+1im; 1-1im]
However, unless you really need to you do not have to do it if you really need to get a conjugate transpose of your data. It is enough to change type assertion to
return fx::Array{Float64,2}, fy::AbstractArray{Float64,2}
or
return fx::Matrix{Float64}, fy::AbstractMatrix{Float64}
Conjugate transpose was designed to avoid unnecessary allocation of data and most of the time this will be more efficient for you (especially with large matrices).
Finally the line:
return fx::Array{Float64,2}, fy::Array{Float64,2}
throws an error also in the Julia command line (not only when run from a script).

How to use comprehensions on linspace to create matrix

I would like to produce an n x 3 matrix where n is the number of pixels (width * height).
x = linspace(-1, 1, width)
y = linspace(-1, 1, height)
r = 1.0
viewDirections = [[i j 1.0] for i in x for j in y]
However, when I run this I get a:
16-element Array{Array{Float64,2},1}
and not my desired a 16x3 Array{Float64,2}. I am obviously not using comprehensions properly to construct matrices. I tried using comprehensions to create an array of tuples, but I can't then convert those tuples into a matrix.
The problem here is array comprehension will give us a nested array instead of a Matrix. This is the right behavior of comprehension, it won't do extra guesswork for us, so we need to convert the nested array to matrix manually, which can be done using vcat with splating operator(...):
julia> vcat(viewDirections...)
6×3 Array{Float64,2}:
-1.0 -1.0 1.0
-1.0 1.0 1.0
0.0 -1.0 1.0
0.0 1.0 1.0
1.0 -1.0 1.0
1.0 1.0 1.0
It seems like you're constructing homogeneous coordinates from 2D Euclidean space. Using Base.Iterators.product is a more concise and robust way to create the iterator:
julia> w = linspace(-1,1,3)
-1.0:1.0:1.0
julia> h = linspace(-1,1,2)
-1.0:2.0:1.0
julia> r = 1.0
1.0
julia> viewDirections = [collect(i) for i in Iterators.product(w, h, r)]
3×2 Array{Array{Float64,1},2}:
[-1.0, -1.0, 1.0] [-1.0, 1.0, 1.0]
[0.0, -1.0, 1.0] [0.0, 1.0, 1.0]
[1.0, -1.0, 1.0] [1.0, 1.0, 1.0]
julia> hcat(viewDirections...).'
6×3 Array{Float64,2}:
-1.0 -1.0 1.0
0.0 -1.0 1.0
1.0 -1.0 1.0
-1.0 1.0 1.0
0.0 1.0 1.0
1.0 1.0 1.0
Note that, the order of coordinates is different from your original version, that's because Julia is column-major, Iterators.product will iterate the rightest dimension "outestly" i.e. [[i j r] for j in y for i in x ]. If the order is important in your use case, just pay attention to it.
Here are some benchmark results when width/height goes large:
julia> w = linspace(-1,1,300)
-1.0:0.006688963210702341:1.0
julia> h = linspace(-1,1,200)
-1.0:0.010050251256281407:1.0
julia> foo(w,h,r) = hcat([collect(i) for i in Iterators.product(w, h, r)]...).'
julia> bar(w,h,r) = vcat([[i j r] for i in w for j in h]...)
julia> #btime foo($w,$h,$r);
6.172 ms (60018 allocations: 10.99 MiB)
julia> #btime bar($w,$h,$r);
11.294 ms (360028 allocations: 17.02 MiB)

Perspective warp an image in Julia

I have an image and a 3x3 perspective projection matrix M. How do I apply the transform on the image?
I tried to use the warp(img, tform) function but don't know how to construct the transform object from the matrix.
Tried tform = PerspectiveMap() ∘ inv(LinearMap(M)), no idea if this is a correct to create the transform, but it fails with:
ERROR: Inverse transformation for CoordinateTransformations.PerspectiveMap has not been defined.
There are two components to the answer:
You have to define a transformation that takes a 2-vector to a 2-vector
If the transformation isn't invertible, then you have to specify the range of indices of the final image manually.
For the first, the following suffices:
julia> using StaticArrays, CoordinateTransformations
julia> M = #SMatrix [1 0 0; 0 1 0; -1/1000 0 1] # a 3x3 perspective transformation matrix
3×3 StaticArrays.SArray{Tuple{3,3},Float64,2,9}:
1.0 0.0 0.0
0.0 1.0 0.0
-0.001 0.0 1.0
julia> tform = PerspectiveMap() ∘ inv(LinearMap(M))
(CoordinateTransformations.PerspectiveMap() ∘ LinearMap([1.0 0.0 0.0; -0.0 1.0 0.0; 0.001 -0.0 1.0]))
julia> tform(#SVector([1,1,1])) # this takes a 3-vector as input and returns a 2-vector
2-element SVector{2,Float64}:
0.999001
0.999001
julia> push1(x) = push(x, 1)
push1 (generic function with 1 method)
julia> tform2 = PerspectiveMap() ∘ inv(LinearMap(M)) ∘ push1 # here's one that takes a 2-vector as input (appends 1 to the 2-vector)
(::#55) (generic function with 1 method)
julia> tform2(#SVector([1,1]))
2-element SVector{2,Float64}:
0.999001
0.999001
Now let's try this on an image. We'll create an output image that has the same indices as the input image, although you can choose any indices you want:
julia> using Images, TestImages
julia> img = testimage("lighthouse");
julia> imgw = warp(img, tform2, indices(img)); # 3rd argument sets the indices
julia> using ImageView
julia> imshow(imgw)
img looks like this:
and imgw looks like this:

Cos, Acos with complex numbers

In Matlab I can write:
real(cos(kron(acos(-1.25),[0:4])))
and get:
1.0000 -1.2506 2.1282 -4.0725 8.0583
How to do the same in Julia. acos does not work with numbers less than -1.0. Even if I write:
r = max(-1.25,-1)
v = collect(0:4).';
cc =kron(acos(r),v)
I get only this:
1.0 -1.0 1.0 -1.0 1.0
It seems that I need to make cos/acos work with complex numbers.
Is this what you are looking for?
julia> real(cos.(kron(acos(complex(-1.25)),(0:4)')))
1×5 RowVector{Float64,Array{Float64,1}}:
1.0 -1.25 2.125 -4.0625 8.03125
or
julia> real(cos.(kron(acos(complex(-1.25)),0:4)))
5-element Array{Float64,1}:
1.0
-1.25
2.125
-4.0625
8.03125
Looks like Julia's acos requires a complex argument for a complex output.

Resources