Outer product matrix multiplication - julia

Trying to find the outer product of two matrices A and B
Here is what I have attempted:
function product(A, B)
n_a, m_a = size(A)
n_b, m_b = size(B)
AB = Array{Float64}(undef, n_a, m_b)
for i in 1:n_a
for j in 1:m_b
AB[i, j] = A[:, j] * B[j, :]'
end
end
return AB
end
product(A, B)
I get an error when attempting to run: Cannot `convert` an object of type Matrix{Float64} to an object of type Float64

I'm not exactly sure what you mean by "outer product of matrices", I'm only familiar with an outer product of vectors (which creates a matrix). Could you clarify what output you're looking for with an example?
In any event to address the immediate issue: The stacktrace is pointing to this line in your function:
AB[i, j] = A[:, j] * B[j, :]'
Let's take two example matrices and see what happens for i=j=1:
julia> x = [1 2; 3 4]
2×2 Matrix{Int64}:
1 2
3 4
julia> y = [2 3; 4 5]
2×2 Matrix{Int64}:
2 3
4 5
julia> x[:, 1] * y[1, :]'
2×2 Matrix{Int64}:
2 3
6 9
so the way you are slicing and transposing your matrices means you are calculating an outer product (as I know it) of two vectors, which gives you a matrix. Given that AB is defined as a matrix of Float64s, the location AB[i, j] can only hold a Float64 value, but you are trying to assign a Matrix{Float64} to it.
Again I'm not sure what exactly you are trying to achieve here, but if it's just a "normal" matrix multiplication, you should have
AB[i, j] = A[i, :]' * B[:, j]
in your inner loop. Making that change gives me:
julia> product(x, y) == x * y
true
for the x and y defined above.

Based on the image you linked to in the comment, you're calculating an array of matrices. This can be done as follows:
AB = [A[:,j]*B[j,:]' for j=1:size(A,2)]
Though, I don't believe that's the correct definition for matrix outer product. I think it should be like this:
AB = [r*c' for r in eachrow(A), c in eachcol(B)]
This will give you a [n_a*m_b] matrix of [m_a*n_b] matrices.

From the linked image and the text, I think the formula tries to show another representation for the usual matrix product, which is useful, especially when doing rank decomposition of matrices (as sums of rank one matrices).
As a concrete example, first defining a couple of matrices:
julia> using Random
julia> Random.seed!(1234);
julia> A = rand(1:10,(2,3))
2×3 Matrix{Int64}:
4 3 4
6 9 4
julia> B = rand(1:10,(3,4))
3×4 Matrix{Int64}:
10 8 1 7
8 6 2 10
5 8 5 7
Now define a product as a sum of outer products of vectors, and see it gives the same result as a usual matrix product:
julia> product(A, B) = sum( [ A[:,i] * B[i,:]' for i=1:size(A,2) ] )
product (generic function with 1 method)
julia> product(A,B)
2×4 Matrix{Int64}:
84 82 30 86
152 134 44 160
julia> A*B
2×4 Matrix{Int64}:
84 82 30 86
152 134 44 160
In this example, the resulting matrix can be at most rank 3, the number of columns in A (and rows in B). In fact, it is rank 2 because of number of rows, but in general this product is done on tall times flat matrices and then the rank restriction is meaningful.

Related

Use apply() in R to automate drawing specific row values from a matrix and do matrix operations

I have two matrices.
Matrix A with variables x, y, a binary variable t1, and row numbers (row1, row2,....) that have values that correspond to rows in Matrix B.
Matrix B has two columns with values that correspond to the x, y variables in Matrix A.
I want to multiply each row of matrix A with values drawn from matrix B for every value of row1 and row2, find the sum and multiply it with the row value from column t1 of matrix A.
Matrix A
x
y
t1
row1
row2
1
2
1
1
2
3
4
1
4
5
2
2
0
4
5
Matrix B
Rand_x
Rand_y
0.01
0.252
0.78
-0.12
-0.77
0.004
-0.97
0.04
0.17
0.104
The row-wise operation would look something like this. The final output would be M, a 3x1 matrix.
([1 2] %% t([0.01 0.252]) + [1 2] %% t([0.78 -0.12]))*1 = 1.054
([3 4] %% t([-0.97 0.04]) + [1 2] %% t([0.17 0.104]))*1 = -0.512
([2 2] %% t([-0.97 0.04]) + [1 2] %% t([0.17 0.104]))*0 = 0
Matrix M
M
1.054
-0.512
0
I initially used for loops to loop over each matrix row and the values of row1, row 2, row 3..... Unfortunately for large matrices, computation time is in several minues (sometimes even hours). I would like to use apply() function to speed this.
What would be the most efficient way to do this?
I am new to R and stackoverflow. I apologize if my explanation was not clear.
The figures in your example don't look right, so you might need to change it a bit, but here is how to do it (or at least something similar) with apply...
apply(mat_A, 1, function(x) (x[1:2] %*% mat_B[x[4],] +
x[1:2] %*% mat_B[x[5],]) * x[3])
[1] 1.054 -1.824 0.000
The function operates on each row of mat_A. Note that I think you want %*% for matrix multiplication, rather than %% for modulo, and to drop the t() transpose.

Apply function with multiple arguments to a vector in Julia

I would like to apply function with multiple arguments to a vector.
It seems that both map() and map!() can be helpful.
It works perfect if function has one argument:
f = function(a)
a+a
end
x=[1,2,3,4,5]
map(f, x)
output: [2, 4, 6, 8, 10]
However, it is not clear how to pass arguments to the function, if possible, and the vector to broadcast, if the function has multiple arguments.
f = function(a,b)
a*b
end
However, non of the following working:
b=3
map(f(a,b), x, 3)
map(f, x, 3)
map(f, a=x, b=3)
map(f(a,b), x, 3)
map(f(a,b), a=x,b=3)
Expected output:
[3,6,9,12,15]
Use broadcast - just as you suggested in the question:
julia> f = function(a,b)
a*b
end
#1 (generic function with 1 method)
julia> x=[1,2,3,4,5]
5-element Vector{Int64}:
1
2
3
4
5
julia> b=3
3
julia> f.(x, b)
5-element Vector{Int64}:
3
6
9
12
15
map does not broadcast, so if b is a scalar you would manually need to write:
julia> map(f, x, Iterators.repeated(b, length(x)))
5-element Vector{Int64}:
3
6
9
12
15
You can, however, pass two iterables to map without a problem:
julia> map(f, x, x)
5-element Vector{Int64}:
1
4
9
16
25
One possible solution is to create an anonymous function inside map as follows -->
x = [1, 2, 3, 4, 5]
b = 3
f = function(a, b)
a * b
end
map(x -> f(x, b), x)
which produces below output-->
5-element Vector{Int64}:
3
6
9
12
15
Explanation :- Anonymous function is taking values from vector as its first argument and 2nd argument is fixed with b = 3.
A few other options:
julia> map(Base.splat(func), Iterators.product(3, x))
5-element Vector{Int64}:
3
6
9
12
15
Iterators.product returns a list of tuples (3, 1), (3, 2), etc. Since our function func takes multiple separate arguments and not a tuple, we use Base.splat on it which takes the tuple and splats it into separate arguments to pass on to func.
julia> using SplitApplyCombine: product
julia> product(func, x, 3)
5-element Vector{Int64}:
3
6
9
12
15
SplitApplyCombine.jl's product function can directly map a given function over each combination (Cartesian product) of the given arguments.
julia> map(func, x, Iterators.cycle(3))
5-element Vector{Int64}:
3
6
9
12
15
A difference from the two previous ways is that if the shorter argument was a vector with more than one element in it, the previous methods would apply the function to each combination of elements from the two arguments, whereas this one would behave like Python's zip_longest, repeating the shorter vector until they were the same length (and then applying the function).
julia> y = [10, 1000];
julia> SplitApplyCombine.product(func, x, y) # previous method
5×2 Matrix{Int64}:
10 1000
20 2000
30 3000
40 4000
50 5000
julia> map(func, x, Iterators.cycle(y))
5-element Vector{Int64}:
10
2000
30
4000
50

How to insert an element at a specific position of an empty vector?

In R we can create an empty vector where it is possible to insert an element in any position of this vector.
Example:
> x <- c()
> x[1] = 10
> x[4] = 20
The final result is:
> x
[1] 10 NA NA 20
I would like to do something similar using Julia, but couldn't find a way to do this.
The “append” function do not perform something like that.
Could anyone help?
You need to do this in two steps:
First resize the vector or create a vector with an appropriate size.
Next set the elements accordingly.
Since you are coming from R I assume you want the vector to be initially filled with missing values. Here is the way to do this.
In my example I assume you want to store integers in the vector. Before both options load the Missings.jl package:
using Missings
Option 1. Start with an empty vector
julia> x = missings(Int, 0)
Union{Missing, Int64}[]
julia> resize!(x, 4)
4-element Vector{Union{Missing, Int64}}:
missing
missing
missing
missing
julia> x[1] = 10
10
julia> x[4] = 40
40
julia> x
4-element Vector{Union{Missing, Int64}}:
10
missing
missing
40
Option 2. Preallocate a vector
julia> x = missings(Int, 4)
4-element Vector{Union{Missing, Int64}}:
missing
missing
missing
missing
julia> x[1] = 10
10
julia> x[4] = 40
40
The reason why Julia does not resize the vectors automatically is for safety. Sometimes it would be useful, but most of the time if x is an empty vector and you write x[4] = 40 it is a bug in the code and Julia catches such cases.
EDIT
What you can do is:
function setvalue(vec::Vector, idx, val)
#assert idx > 0
if idx > length(vec)
resize!(vec, idx)
end
vec[idx] = val
return vec
end

Creating an array with values that obey a criteria of smallest distance to the mean

I am trying to build credible bands in Julia, however, there is a technical procedure that I am not aware of how to do. The code is the following:
#Significance level 95%
alpha_sign=0.05
#Genrate random values
N_1 = 100
Fs_1 = Array{Float64}(undef, length(x), N_1);
x = 0.0:0.01:1.0
for k in 1:100
f = rand(postΠ)
Fs_1[:,k] = f.(x)
end
# sup|theta_i(t)-average|
dif_b=Array{Float64}(undef, length(x),N);
for k in 1:100
dif_b[:,k] = Fs_1[:,k]-average_across
end
#Defining a function that allows to compute the n smallest values
using Base.Sort
function smallestn(a, n)
sort(a; alg=Sort.PartialQuickSort(n))[1:n]
end
#Compute the maximum of the difference across time
sup_b=Array{Float64}(undef, N_1)
for k in 1:100
sup_b[k]=(maximum(abs.(dif_b[:,k] )))
end
#Build a matrix with the smallest distances
N_min=(1-alpha_sign)*N
using Base.Sort
min_sup_b=smallestn(sup_b,95)
To simplify the problem I am creating this example:
Imagine I have the matrix down there and I want to create a matrix with the values that are closest to the mean. I am able to compute the distances and store into a vector as displayed in the code above and later get the smallest values but I need to get back to the original matrix to extract those values.
X=[1,2,7,4,5]
av_X=mean(X,dims=1)
Question:
I am able to compute the distances and store them into a vector as displayed in the code above and later get the smallest values but I need to get back to the original matrix to extract those values.
How do I do that?
Thanks in advance!
using Statistics
arr = rand(1:20, (4, 4))
colmeans = [mean(col) for col in eachcol(arr)]
deltas = map(cart -> abs(arr[cart] - colmeans[first(Tuple(cart))]) => cart, CartesianIndices(arr))
sorteddeltas = sort(deltas, lt = (x, y) -> first(x) < first(y), dims=1)
sarr = zeros(Int, (4, 4))
for (i, d) in enumerate(sorteddeltas)
sarr[i] = arr[last(d)]
end
println(arr) # [7 1 2 15; 18 7 14 10; 3 11 10 13; 7 14 20 8]
println(colmeans) # [8.75, 8.25, 11.5, 11.5]
println(sarr) # [7 11 10 13; 7 7 14 10; 3 14 2 8; 18 1 20 15]
println(sarr') # [7 7 3 18; 11 7 14 1; 10 14 2 20; 13 10 8 15]
This should give you a sorted list of pairs of the distances from the mean of each column, with the second part of the pair the Cartesian coordinates of the original matrix.
sarr is the original matrix sorted column-major by closeness to the mean for each column.
I think the function you are looking for is findmin(). It gives both the minimum value and its index.
julia> x = randn(5)
5-element Vector{Float64}:
-0.025159738348978562
-0.24720173332739662
-0.32508319212563325
0.9470582053428686
1.1467087893336048
julia> findmin(x)
(-0.32508319212563325, 3)
If you want to do this for every column in a matrix, you can do something like:
julia> X = randn(3, 5)
3×5 Matrix{Float64}:
1.06405 1.03267 -0.826687 -1.68299 0.00319586
-0.129021 0.0615327 0.0756477 1.05258 0.525504
0.569748 -0.0877886 -1.48372 0.823895 0.319364
julia> min_inds = [findmin(X[:, i]) for i = 1:5]
5-element Vector{Tuple{Float64, Int64}}:
(-0.12902069012799203, 2)
(-0.08778864856976668, 3)
(-1.4837211369655696, 3)
(-1.6829919363620507, 1)
(0.003195860366775878, 1)

broadcasting vector multiplication with a 3 vector and n vector in julia

I have a 3-vector c = [0.7, 0.5, 0.2] and I want to multiply it with everything in an n-vector x = rand((-1,1),n) such that I get a resulting n+2-vector y where y[i] == x[i]*c[3] + x[i-1]*c[2] + x[i-2]*c[1]
How should I do this in julia? I feel like there should be a way to broadcast the smaller 3 vector to all the values in the n vector. And for the edge cases, if i-1 or i-2 is out of bounds I just want zero for those components.
If I understand your question correctly you want a convolution, with a twist that in a standard convolution the vector c would be reversed. You can use e.g. DSP.jl for this.
Is this what you want?
julia> using DSP
julia> c = [0.7, 0.5, 0.2]
3-element Array{Float64,1}:
0.7
0.5
0.2
julia> conv([10, 100, 1000, 10000], reverse(c))
6-element Array{Float64,1}:
1.9999999999996967
25.0
257.0000000000003
2569.9999999999995
5700.0
6999.999999999998
You can also manually implement it using dot from the LinearAlgebra module like this:
julia> using LinearAlgebra
julia> x = [10, 100, 1000, 10000]
4-element Array{Int64,1}:
10
100
1000
10000
julia> y = [0;0;x;0;0]
8-element Array{Int64,1}:
0
0
10
100
1000
10000
0
0
julia> [dot(#view(y[i:i+2]), c) for i in 1:length(x)+2]
6-element Array{Float64,1}:
2.0
25.0
257.0
2570.0
5700.0
7000.0
Here's one approach that uses ShiftedArrays.jl.
using ShiftedArrays
c = [0.7, 0.5, 0.2]
Create lagged versions of x, with initial zeros:
x = 1:5
xminus1 = lag(x, 1, default=0)
xminus2 = lag(x, 2, default=0)
Horizontally concatenate the vectors and use matrix multiplication with c:
X = [xminus2 xminus1 x]
X * c
Here's what X and X * c look like at the REPL:
julia> X = [xminus2 xminus1 x]
5×3 Array{Int64,2}:
0 0 1
0 1 2
1 2 3
2 3 4
3 4 5
julia> X * c
5-element Array{Float64,1}:
0.2
0.9
2.3
3.7
5.1
Note that this produces an output vector of length length(x), not length(x) + 2. I'm not sure how it would make sense for the output to be of length length(x) + 2, as you requested in the question.
I have a package for doing such things. The simplest use is like this:
julia> c = [0.7, 0.5, 0.2]; # from question
julia> x = [10, 100, 1000, 10_000]; # from another answer
julia> using Tullio, OffsetArrays
julia> #tullio y[i] := x[i]*c[3] + x[i-1]*c[2] + x[i-2]*c[1]
2-element OffsetArray(::Vector{Float64}, 3:4) with eltype Float64 with indices 3:4:
257.0
2570.0
julia> #tullio y[i] := x[i+k-3] * c[k] # sum over all k, range of i that's safe
2-element OffsetArray(::Array{Float64,1}, 3:4) with eltype Float64 with indices 3:4:
257.0
2570.0
Since eachindex(c) == 1:3, that's the range of k-values which this sums over, and the range of i is as big as it can be so that i+k-3 stays inside eachindex(x) == 1:4.
To extend the range of i by padding x with two zeros in each direction, write pad(i+k-3, 2). And to compute the shift of i needed to produce an ordinary 1-based Array, write i+_ on the left (and then the -3 makes no difference). Then:
julia> #tullio y[i+_] := x[pad(i+k, 2)] * c[k]
6-element Array{Float64,1}:
2.0
25.0
257.0
2570.0
5700.0
7000.0
On larger arrays, this won't be very fast (at the moment) as it must check at every step whether it is inside x or out in the padding. It's very likely that DSP.conv is a bit smarter about this. (Edit -- DSP.jl seems never to be faster for this c; with a kernel of length 1000 it's faster with 10^6 elements in x.)

Resources