For-loop with the dimension flexibility of broadcasting - julia

With the aid of broadcasting, the following code will work whether x, y, and z are scalars, vectors of size n, or any combination thereof.
b = zeros(n)
b .= x.*y.*z .+ x
However, I'd like a for-loop. The following for-loop only works when x is a vector of size n, y is a scalar, and z is a scalar.
for i = 1:n
b[i] = x[i]*y*z + x[i]
end
To write the equivalent of b .= x.*y.*z .+ x as a for-loop for any case, I can only think of writing a for-loop for every combination of x, y, and z within if-statements. This can get messy with more variables in more complicated math expressions.
Is there a more elegant way to do what I'd like than using many if-statements?

You could define a wrapper type that indexing into it will give array indexing if wrapped variable is array and repeats the same value for all indices for scalars. I have an example below but it probably is not as efficient as using broadcast. And it is not checking if array lengths are consistent. However, a custom wrapper type would alleviate the situation.
julia> function f(x,y,z)
lx,ly,lz = length(x),length(y),length(z)
maxlen = max(lx,ly,lz)
cx = cycle(x)
cy = cycle(y)
cz = cycle(z)
b = zeros(maxlen)
#inbounds for (xi,yi,zi,i) in zip(cx,cy,cz,1:maxlen)
b[i] = xi*yi*zi+xi
end
return b
end
f (generic function with 1 method)
julia> f(1:3,21,2)
3-element Array{Float64,1}:
43.0
86.0
129.0

Related

Outer products among more than three vectors in Julia

I am looking for a way to calculate outer products among more than three vectors in Julia.
Let a, b, c and d are vectors that each size is I, J, K and L, respectively. Then, their outer products of them are defined as a tensor T whose size is I x J x K x L, and each element is defined as
T[i,j,k,l] = a[i]*b[j]*c[k]*d[l]
Is there any helpful function in Julia to get T from vectors?
What you're looking for here is kron. Taking the Kronecker product of vectors will give vectors, so you can get the desired tensor by reshaping, so in your case
reshape(kron(d,c,b,a),(I,J,K,L))
or more generally
reshape(kron(d,c,b,c),length.((a,b,c,d))). I usually define a function to remember the ordering for me
outer(v...) = reshape(kron(reverse(v)...),length.(v))
So this is how you take the outer product of vectors, of course you could ask whether you can do similar for tensors more generally. Here is a little more tedious to deal with the sizes, but yea you can do the same trick
outer(v...) = reshape(kron(reverse(vec.(v))...),tuple(vcat(collect.(size.(v))...)...))
You can just broadcast * after reshaping:
julia> a,b,c,d = (rand(1+i) for i in 1:4);
julia> t4 = a .* permutedims(b) .* reshape(c,1,1,:) .* reshape(d,1,1,1,:);
julia> summary(t4)
"2×3×4×5 Array{Float64, 4}"
My package translates the notation in the question to precisely this broadcast:
julia> using TensorCast
julia> #cast t4c[i,j,k,l] := a[i] * b[j] * c[k] * d[l];
julia> t4c == t4
true
This operation is similar to kron, but somewhat confusingly the order of its arguments isn't what you'd expect for column-major arrays.
julia> kron(b,a) ≈ vec(a .* b') ≈ #cast t2[(i,j)] := a[i] * b[j]
true
julia> kron(d,c,b,a) ≈ #cast _[(i,j,k,l)] := a[i] * b[j] * c[k] * d[l]
true

Optim Julia parameter meaning

I'm trying to use Optim in Julia to solve a two variable minimization problem, similar to the following
x = [1.0, 2.0, 3.0]
y = 1.0 .+ 2.0 .* x .+ [-0.3, 0.3, -0.1]
function sqerror(betas, X, Y)
err = 0.0
for i in 1:length(X)
pred_i = betas[1] + betas[2] * X[i]
err += (Y[i] - pred_i)^2
end
return err
end
res = optimize(b -> sqerror(b, x, y), [0.0,0.0])
res.minimizer
I do not quite understand what [0.0,0.0] means. By looking at the document http://julianlsolvers.github.io/Optim.jl/v0.9.3/user/minimization/. My understanding is that it is the initial condition. However, if I change that to [0.0,0., 0.0], the algorithm still work despite the fact that I only have two unknowns, and the algorithm gives me three instead of two minimizer. I was wondering if anyone knows what[0.0,0.0] really stands for.
It is initial value. optimize by itself cannot know how many values your sqerror function takes. You specify it by passing this initial value.
For example if you add dimensionality check to sqerror you will get a proper error:
julia> function sqerror(betas::AbstractVector, X::AbstractVector, Y::AbstractVector)
#assert length(betas) == 2
err = 0.0
for i in eachindex(X, Y)
pred_i = betas[1] + betas[2] * X[i]
err += (Y[i] - pred_i)^2
end
return err
end
sqerror (generic function with 2 methods)
julia> optimize(b -> sqerror(b, x, y), [0.0,0.0,0.0])
ERROR: AssertionError: length(betas) == 2
Note that I also changed the loop condition to eachindex(X, Y) to ensure that your function checks if X and Y vectors have aligned indices.
Finally if you want performance and reduce compilation cost (so e.g. assuming you do this optimization many times) it would be better to define your optimized function like this:
objective_factory(x, y) = b -> sqerror(b, x, y)
optimize(objective_factory(x, y), [0.0,0.0])

Taking a forawrd derivative with ForwardDiff

I am trying to use ForwardDIff to compute the gradient of a function. My code is very simple:
buffer1 = 1.
buffer2 = 1.0-buffer1
buffers = [buffer1; buffer2];
K0 = [[100., 200.] [0.01, 0.001]];
q_sat = [100, 150];
K = exp.(log.(K0) * buffers);
f(x::Vector) = q_sat[1]*(K[1]*x[1]/(1+alpha[1]*K[1]*x[1]+alpha[2]*K[2]*x[2]))
x = [0.5; 0.5]
g = ForwardDiff.gradient(f(x), x)
The first lines are about defining some constants and then I define the function that takes in a vector and returns a real number. When trying to comput the gradient of f I get the error:
MethodError: objects of type Float64 are not callable
What could the problem be?
You want to differentiate f (the function), not f(x) (some scalar), so it must be
ForwardDiff.gradient(f, x)

In Julia, Transpose operator

I am trying to use a transpose operator over a vector in order to perform am element-wise addition.
For example, I want to add a column vector a = [a1;a2;a3] to a row vector b = [b1,b2] I should get matrix
M = a+b = [a1+b1, a1+b2; a2+b1, a2+b2; a3+b1, a3+b2].
In MATLAB it is equivalent (if both vectors are row vectors) M = a.'+b
I am trying to get the same in Julia but here is the problem, there is no .' operator in Julia starting from 1.0 version. There is the transpose operator which does not work in broadcasting mode. The adjoint operator is not Valid for me because I work with complex numbers.
a = Vector{ComplexF64}([1+3im,2])
b = Vector{ComplexF64}([0,0,0])
Z = zeros(ComplexF64,3,2)
G = zeros(ComplexF64,3,2)
#. Z = b + a' # Works but takes the complex conjugate
#. Z = b + transpose(a) # DOES NOT WORK!!!! The error is " DimensionMismatch("array could not be broadcast to match destination") "
Z = b .+ transpose(a) # Works but not efficient
#. Z = b + conj(a')
The third case Z = b .+ transpose(a) is not efficient because it makes 2 loops first one for addition b .+ transpose(a), than it runs the second loop one for the assignment of b .+ transpose(a) to Z. While the other 3 cases do it within one loop.
So which is the fastest way?
And why transpose doesn't within Broadcasting?
Thank you in advance
For Hermitian You can just type:
a' .+ b
Example
julia> a = ComplexF64.([1+3im,2])
2-element Array{Complex{Float64},1}:
1.0 + 3.0im
2.0 + 0.0im
julia> b = ComplexF64.([10,20,30])
3-element Array{Complex{Float64},1}:
10.0 + 0.0im
20.0 + 0.0im
30.0 + 0.0im
julia> a' .+ b
3×2 Array{Complex{Float64},2}:
11.0-3.0im 12.0+0.0im
21.0-3.0im 22.0+0.0im
31.0-3.0im 32.0+0.0im
If you want to have just transposition you could define your own unary operator (perhaps from the list of unused unary operators):
¬(a) = permutedims(a)
Now you could do
julia> ¬a
1×2 Matrix{ComplexF64}:
1.0+3.0im 2.0+0.0im

Element-wise compound assignment operator in Julia

Consider the following two functions,
function f(x)
x = x .+ 1
end
function g(x)
x .+= 1
end
My understanding is that they should behave identically, since a .+= b is just syntactic sugar for a = a .+ b. However f does not mutate a global variable passed to it as an argument, while g does.
Can anyone explain to me what is going on here?
Thanks.
This is almost right: x .+= 1 is syntactic sugar for x .= x .+ 1 which does in-place elementwise assignment whereas x = x .+ 1 rebinds x to the new array produced by doing elementwise addition.

Resources