Iterate over arrays in Julia - julia

This is the function I want to use. I am trying to use temperature data & precipiation data of a whole week. This means the arguments: temp & precip, will be an array of length 7. How do i make this work?
function humidityindex(temp, precip)
moist_effect = 0
temp_effect = 0
for i in 1:size(precip)
moist_effect += ((precip[i]/100) * (1-(i/10)))
temp_effect -= ((temp[i]/25) * (1-(i/10)))
end
effect = temp_effect + moist_effect
return effect
end
The function results in the following MethodError:
julia> t = rand(7); p = rand(7);
julia> humidityindex(t, p)
ERROR: MethodError: no method matching (::Colon)(::Int64, ::Tuple{Int64})
Closest candidates are:
Any(::T, ::Any, ::T) where T<:Real at range.jl:41
Any(::A, ::Any, ::C) where {A<:Real, C<:Real} at range.jl:10
Any(::T, ::Any, ::T) where T at range.jl:40
...
Stacktrace:
[1] humidityindex(::Array{Float64,1}, ::Array{Float64,1}) at ./REPL[1]:4
[2] top-level scope at REPL[3]:1

The problem is how you create the iteration space: for i in 1:size(precip). size in Julia returns a tuple. You want to use length instead (or size(precip, 1) for the size in the first dimension).
function humidityindex(temp, precip)
moist_effect = 0
temp_effect = 0
for i in 1:length(precip) # <-- Updated this line
moist_effect += ((precip[i]/100) * (1-(i/10)))
temp_effect -= ((temp[i]/25) * (1-(i/10)))
end
effect = temp_effect + moist_effect
return effect
end

The answer given by the first Fredrik is the answer to your question. This is simply a short and efficient way of calculating the same thing.
moist_effect((i,x)) = (x/100) * (1-(i/10))
temp_effect((i,x)) = -(x/25) * (1-(i/10))
function humidityindex(temp, precip)
sum(moist_effect, enumerate(precip)) + sum(temp_effect, enumerate(temp))
end
Notice the tuple destructuring in moist_effect((i,x)), I added this since enumerate iterates tuples of indices and values.
The function sum has a method that accepts a function as its first argument. This method applies that function to all elements before summing them up.

Related

print a function with plots in julia

I'm currently trying to print the following function in my plot and I'm not allowed to use any imports or other libraries:
using Plots
amplitude = 1
frequenz_E = 329.63
frequenz_G_SHARP = 415.30
frequenz_B = 493.88
k(t) = amplitude * sin * (2 * pi * frequenz_E * t)
p = plot(0:0.005:0.001, k , label="E = 329.63 Hz", title="Triade sound wave", xlabel="t (seconds)", ylabel="y(t)")
display(p)
I do want to start at 0 and then increase the value by 0.005 up to 0.01.
Now if I want to print the function, I do get the following error:
include("/home/user/Applied_Mathmatics/Assignment_01/templates/waves.jl")
ERROR: LoadError: MethodError: no method matching *(::Int64, ::typeof(sin))
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...) at operators.jl:529
*(::ChainRulesCore.NotImplemented, ::Any) at /home/user/.julia/packages/ChainRulesCore/8NXnp/src/tangent_arithmetic.jl:37
*(::ChainRulesCore.ZeroTangent, ::Any) at /home/user/.julia/packages/ChainRulesCore/8NXnp/src/tangent_arithmetic.jl:104
How can I work around the error and just print the function?
The output should look like this (I did not implement all functions yet):
As the error states, you cannot multiply a function with an Integer. You should write
k(t) = amplitude * sin(2 * pi * frequenz_E * t)
And by the way the range 0:0.005:0.001 is just one element, i.e., 0.. You can try that by
julia> collect(0:0.005:0.001)
1-element Vector{Float64}:
0.0
In Julia the middle element is the step and the third element is the end of the range. Thus something like 0:0.001:0.005 makes more sense.

Julia - generate an array of functions programmatically

I want to generate an array of functions programmatically, with a loop so that each successive function depends on the previous.
For example in pseudo-code:
f_array = [f1, f2, f3]
with:
f1(x) = x
f2(x) = 3 * f1(x)
f3(x) = 3 * f2(x)
so that I could call:
f_array[3](x)
and get the result of f3(x)
Here is what I have tried:
# create simple function just to initialize
f(x)=x
# initialize array of functions
N = 3
f_array = fill(f, N)
# now we update each function
for i in 2:N
f_array[i] = (f(x)= 3 * f_array[i-1](x))
end
I get an error:
ERROR: MethodError: Cannot convert an object of type getfield(Main,
Symbol("#f#15")){Int64} to an object of type typeof(f)
I cannot find a solution at the moment. Any help would be appreciated.
When you use fill with f it sets expected type for the elements of f_array to f, in the code below I am switching to abstract type to make it possible to have any function in the array
# create simple function just to initialize
f(x)=x
# initialize array of functions
N = 3
f_array = Array{Function}(undef, N);
f_array[1] = f;
# now we update each function
for i in 2:N
f_array[i] = x -> 3 * f_array[i-1](x)
end
print(f_array[3](2))
which produces a value of 18
In the mean time, I also found a way using metaprogramming. I post this here as it could be useful for others:
f1(x) = x
for i in 2:N
prog = "f$i(x) = 3 * f$(i-1)(x)"
exp = Meta.parse(prog)
eval(exp)
end
f3(2)
# 18
I'd write Yegor's answer as
f_array = Function[identity]
for i in 2:3
push!(f_array, x -> 3f_array[i-1](x))
end
But more importantly, this is a well known pattern: iterated function application. And it is already implemented, not in Base, but for example in IterTools.jl, by which you should be able to write:
f_array(start, N) = collect(Iterators.take(iterated(x -> 3x, start), N))
(I didn't test this, though.)

Julia Metaprogramming: Function for Mathematical Series

I'm trying to build a function that will output an expression to be assigned to a new in-memory function. I might be misinterpreting the capability of metaprogramming but, I'm trying to build a function that generates a math series and assigns it to a function such as:
main.jl
function series(iter)
S = ""
for i in 1:iter
a = "x^$i + "
S = S*a
end
return chop(S, tail=3)
end
So, this will build the pattern and I'm temporarily working with it in the repl:
julia> a = Meta.parse(series(4))
:(x ^ 1 + x ^ 2 + x ^ 3 + x ^ 4)
julia> f =eval(Meta.parse(series(4)))
120
julia> f(x) =eval(Meta.parse(series(4)))
ERROR: cannot define function f; it already has a value
Obviously eval isn't what I'm looking for in this case but, is there another function I can use? Or, is this just not a viable way to accomplish the task in Julia?
The actual error you get has to do nothing with metaprogramming, but with the fact that you are reassigning f, which was assigned a value before:
julia> f = 10
10
julia> f(x) = x + 1
ERROR: cannot define function f; it already has a value
Stacktrace:
[1] top-level scope at none:0
[2] top-level scope at REPL[2]:1
It just doesn't like that. Call either of those variables differently.
Now to the conceptual problem. First, what you do here is not "proper" metaprogramming in Julia: why deal with strings and parsing at all? You can work directly on expressions:
julia> function series(N)
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :(x ^ $i))
end
return S
end
series (generic function with 1 method)
julia> series(3)
:(x ^ 1 + x ^ 2 + x ^ 3)
This makes use of the fact that + belongs to the class of expressions that are automatically collected in repeated applications.
Second, you don't call eval at the appropriate place. I assume you meant to say "give me the function of x, with the body being what series(4) returns". Now, while the following works:
julia> f3(x) = eval(series(4))
f3 (generic function with 1 method)
julia> f3(2)
30
it is not ideal, as you newly compile the body every time the function is called. If you do something like that, it is preferred to expand the code once into the body at function definition:
julia> #eval f2(x) = $(series(4))
f2 (generic function with 1 method)
julia> f2(2)
30
You just need to be careful with hygiene here. All depends on the fact that you know that the generated body is formulated in terms of x, and the function argument matches that. In my opinion, the most Julian way of implementing your idea is through a macro:
julia> macro series(N::Int, x)
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :($x ^ $i))
end
return S
end
#series (macro with 1 method)
julia> #macroexpand #series(4, 2)
:(2 ^ 1 + 2 ^ 2 + 2 ^ 3 + 2 ^ 4)
julia> #series(4, 2)
30
No free variables remaining in the output.
Finally, as has been noted in the comments, there's a function (and corresponding macro) evalpoly in Base which generalizes your use case. Note that this function does not use code generation -- it uses a well-designed generated function, which in combination with the optimizations results in code that is usually equal to the macro-generated code.
Another elegant option would be to use the multiple-dispatch mechanism of Julia and dispatch the generated code on type rather than value.
#generated function series2(p::Val{N}, x) where N
S = Expr(:call, :+)
for i in 1:N
push!(S.args, :(x ^ $i))
end
return S
end
Usage
julia> series2(Val(20), 150.5)
3.5778761722367333e43
julia> series2(Val{20}(), 150.5)
3.5778761722367333e43
This task can be accomplished with comprehensions. I need to RTFM...
https://docs.julialang.org/en/v1/manual/arrays/#Generator-Expressions

How do I evaluate the function in only one of its variables in Scilab

How do I evaluate the function in only one of its variables, that is, I hope to obtain another function after evaluating the function. I have the following piece of code.
deff ('[F] = fun (x, y)', 'F = x ^ 2-3 * y ^ 2 + x * y ^ 3');
fun (4, y)
I hope to get 16-3y ^ 2 + 4y ^ 3
If what you want to do is to write x = f(4,y), and later just do x(2) to get -36, that is called partial application:
Intuitively, partial function application says "if you fix the first arguments of the function, you get a function of the remaining arguments".
This is a very useful feature, and very common Functional Programming Languages, such as Haskell, but even JS and Python now are able to do it. It is also possible to do this in MATLAB and GNU/Octave using anonymous functions (see this answer). In Scilab, however, this feature is not available.
Workround
Nonetheless, Scilab itself uses a workarounds to carry a function with its arguments without fully evaluating. You see this being used in ode(), fsolve(), optim(), and others:
Create a list containing the function and the arguments to partial evaluation: list(f,arg1,arg2,...,argn)
Use another function to evaluate such list and the last argument: evalPartList(list(...),last_arg)
The implementation of evalPartList() can be something like this:
function y = evalPartList(fList,last_arg)
//fList: list in which the first element is a function
//last_arg: last argument to be applied to the function
func = fList(1); //extract function from the list
y = func(fList(2:$),last_arg); //each element of the list, from second
//to last, becomes an argument
endfunction
You can test it on Scilab's console:
--> deff ('[F] = fun (x, y)', 'F = x ^ 2-3 * y ^ 2 + x * y ^ 3');
--> x = list(fun,4)
x =
x(1)
[F]= x(1)(x,y)
x(2)
4.
--> evalPartList(x,2)
ans =
36.
This is a very simple implementation for evalPartList(), and you have to be careful not to exceed or be short on the number of arguments.
In the way you're asking, you can't.
What you're looking is called symbolic (or formal) computational mathematics, because you don't pass actual numerical values to functions.
Scilab is numerical software so it can't do such thing. But there is a toolbox scimax (installation guide) that rely on a the free formal software wxmaxima.
BUT
An ugly, stupid but still sort of working solution is to takes advantages of strings :
function F = fun (x, y) // Here we define a function that may return a constant or string depending on the input
fmt = '%10.3E'
if (type(x)==type('')) & (type(y)==type(0)) // x is string is
ys = msprintf(fmt,y)
F = x+'^2 - 3*'+ys+'^2 + '+x+'*'+ys+'^3'
end
if (type(y)==type('')) & (type(x)==type(0)) // y is string so is F
xs = msprintf(fmt,x)
F = xs+'^2 - 3*'+y+'^2 + '+xs+'*'+y+'^3'
end
if (type(y)==type('')) & (type(x)==type('')) // x&y are strings so is F
F = x+'^2 - 3*'+y+'^2 + '+x+'*'+y+'^3'
end
if (type(y)==type(0)) & (type(x)==type(0)) // x&y are constant so is F
F = x^2 - 3*y^2 + x*y^3
end
endfunction
// Then we can use this 'symbolic' function
deff('F2 = fun2(y)',' F2 = '+fun(4,'y'))
F2=fun2(2) // does compute fun(4,2)
disp(F2)

MethodError, despite being (somehow) defined

I am trying to do something like this:
function outer(x::Array{Float64}, y::Array{Float64}=nothing)
if (y == nothing)
function inner(y::Array{Float64})
return x .* y
end
return inner
else
return x .+ y
end
end
outer([2.], [3.]) # to return 5, works
outer([2.])([3.]) # to return 6, fails.
outer([2.], [3.]) works just fine.
The problem is that outer([2.])([3.]) raises a MethodError stating:
MethodError: no method matching outer(::Array{Float64,1}, ::Void)
Closest candidates are:
outer(::Array{Float64,N} where N) at In[1]:2
outer(::Array{Float64,N} where N, ::Array{Float64,N} where N) at In[1]:2
Stacktrace:
[1] outer(::Array{Float64,1}) at ./In[1]:2
[2] include_string(::String, ::String) at ./loading.jl:522
The weird bit is that under Closest candidates, the single-argument outer(::Array{Float64,N} where N) is the first candidate. So why does it not work with the single argument?
Note: outer([2.], )([3.]), outer([2.], nothing)([3.]), outer([2.], [nothing])([3.]) all produce the same (similar) error.
This can be reproduced using a single argument function too:
function outer(y::Array{Float64}=nothing)
if (y == nothing)
function inner(y::Array{Float64})
return y .* y
end
return inner
else
return y .+ y
end
end
outer([2.])
1-element Array{Float64,1}:
4.0
outer()([3.])
MethodError: no method matching outer(::Void)
Closest candidates are:
outer() at In[6]:2
outer(::Array{Float64,N} where N) at In[6]:2
outer(::Array{Float64,N} where N, ::Array{Float64,N} where N) at In[1]:2
Stacktrace:
[1] outer() at ./In[6]:2
[2] include_string(::String, ::String) at ./loading.jl:522
And again, there is a zero-argument function outer() listed first in the Closest candidates list!
Basically, the above example is a MWE representing a log-sum/likelihood evaluation, where x is the data and y is the parameter of a model. I am trying to return a function in the parameter of the model to optimise using MLE, or return the log-sum if the parameter is passed.
In this analogy, outer computes log-sum, given data and parameter, or returns inner as a function of the parameter, which can be optimised.
You can reproduce this with simply:
f(x::Float64=nothing) = x
f()
When you add = nothing that sets nothing as a default argument. And also adds f() as a method. But when you call f() then julia will try to run f(nothing) as nothing is your default argument. That will then error as nothing is of type Void and you asserted that the argument must be Float64.
For example you could (but shouldn't) use f(x::Union{Float64,Void}=nothing) = x to get around this. But it'd be much better to use a Nullable which is exactly for interacting with a value that may or may not exist.
Read more about Nullables here or type ?Nullable in the REPL.
Edit by OP
MWE:
function outer(x::Array{Float64}, y=Nullable{Array{Float64}}())
if isnull(y)
function inner(y::Array{Float64})
return x .* y
end
return inner
else
return x .+ y
end
end
outer([2.])([3.]) # Produces 6,
outer([2.], [3.]) # Produces 5.
Works as expected.
Readability counts so maybe you have to reconsider define 2 methods explicitly (and simple as it is in Julian way):
outer(x::Array{Float64}, y::Array{Float64}) = x .+ y
outer(x::Array{Float64}) = y -> x .* y

Resources