Related
I'm an economics student slowly switching from MATLAB to Julia.
Currently, my problem is that I don't know how to declare (preallocate) a vector that could store interpolations.
Specifically, when I execute something close to:
function MyFunction(i)
# x, y vectors are some functions of 'i' defined here
f = LinearInterpolation(x,y,extrapolation_bc=Line())
return f
end
g = Vector{Function}(undef, N)
for i = 1:N
g[i] = MyFunction(i)
end
I get:
ERROR: LoadError: MethodError: Cannot `convert` an object of type Interpolations.Extrapolation{Float64,1,Interpolations.GriddedInterpolation{Float64,1,Float64,Gridded{Linear},Tuple{Array{Float64,1}}},Gridded{Linear},Line{Nothing}} to an object of type Function
If I, instead of g=Vector{Function}(undef, N), declare g=zeros(N), I get a similar error message (ending with with ...Float64 rather than with ... Function).
When I, instead, declare:
g = Interpolations.Extrapolation{Float64,1,Interpolations.GriddedInterpolation{Float64,1,Float64,Gridded{Linear},Tuple{Array{Float64,1}}},Gridded{Linear},Line{Nothing}}(N)
I get:
LoadError: MethodError: no method matching Interpolations.Extrapolation{Float64,1,Interpolations.GriddedInterpolation{Float64,1,Float64,Gridded{Linear},Tuple{Array{Float64,1}}},Gridded{Linear},Line{Nothing}}(::Int64) Closest candidates are: Interpolations.Extrapolation{Float64,1,Interpolations.GriddedInterpolation{Float64,1,Float64,Gridded{Linear},Tuple{Array{Float64,1}}},Gridded{Linear},Line{Nothing}}(::Any, !Matched::Any) where {T, N, ITPT, IT, ET}
When I don't declare "g" at all, then I get:
ERROR: LoadError: UndefVarError: g not defined
Finally, when I declare:
g = Vector{Any}(undef, N)
the code works, though I'm afraid this might induce some type-change of a variable g, thereby slowing down my performance-sensitive code.
How, ideally then, should I declare g in this case?
EDIT:
In reality, my problem is a bit more complex, more like the following:
function MyFunction(i)
# x, y vectors are some functions of 'i' defined here
f = LinearInterpolation(x,y,extrapolation_bc=Line())
h = is a T-vector of some functions of x,y
A = is some matrix depending on x,y
return h, A, f
end
h = Matrix{Function}(undef, T, N)
A = zeros(T,I,N)
g = Vector{Any}(undef, N)
for i = 1:N
h[:,i], A[:,:,i], g[i] = MyFunction(i)
end
So, when I use either comprehension or broadcasting (like h, A, g = [MyFunction(i) for i in 1:N] or h, A, g = MyFunction.(1:N)), as users Benoit and DNS suggested below, the outputs of my function are 3 tuples, h, A, g, each containing {h[i], A[i], g[i]} for i=1,2,3. If I use only 1 output variable on the LHS, instead, i.e.: MyOutput = [MyFunction(i) for i in 1:N] or MyOutput[i] = MyFunction.(1:N), then MyOutput becomes a vector with N tuple entries, every tuple consisting of {h[i], A[i], g[i]} i=1,2,3,...,N. I bet there's a way of extracting these elements from the tuples in MyOutput and filling them inside h[:,i], A[:,:,i], g[i], but that seems a bit cumbersome and slow.
You could do
f = MyFunction(1)
g = Vector{typeof(f)}(undef, N)
g[1] = f
for i = 2:N
g[i] = MyFunction(i)
end
I think also map should figure out the type:
map(MyFunction, 1:N)
A simple solution is to use a comprehension:
g = [MyFunction(i) for i in 1:N]
or elegantly use the dot syntax:
g = MyFunction.(1:N)
(Credit to DNF for the dot-syntax solution suggested in the comments.)
I am trying to write a functionality, (using macro, generated function or something), that effectively vectorizes Julia function calls to functions that I've written. Basically, I'm trying to write my own version of the #. macro, but instead, I'd like it to accept functions instead of a for loop--- if I understand this correctly. Here are some documents that I've read on the subject:
https://docs.julialang.org/en/v1/manual/functions/#man-vectorized-1
https://github.com/JuliaLang/julia/blob/master/base/broadcast.jl
https://julialang.org/blog/2017/01/moredots
https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#Code-Generation-1
Here is my preliminary toy example that I'm working with to achieve such a functionality:
function add!(v_add::Vector{Float64}, a_add::Float64, j::Int64)
v_add[j] = v_add[j]+a_add
end
function add!(v_add::Vector{Float64}, a_add::Float64)
for j in 1:length(v_add)
v_add[j] = v_add[j]+a_add
end
end
macro vectorize(args)
print("\n****************** args\n")
print(args)
print("\n******************\n")
e = :(:call,
$args[1],
$args[2],
$args[3])
print("\n****************** expression\n")
show(e)
print(e)
print("\n******************\n")
return e
end
function test!(v_test, a_test)
# # Traverse vector twice
# add!(v_test, a_test)
# add!(v_test, a_test)
# Traverse vector once
args = [
add!, v_test, a_test,
add!, v_test, a_test
]
e = #vectorize(args)
# eval(e) # Next step
end
v_main = Vector([Float64(i) for i in 1:3])
a_main = Float64(2.0)
print("\n",v_main, "\n")
Main.test!(v_main, a_main)
print("\n",v_main, "\n")
The problem I'm having so far is that I can't even get the de-vectorized version running using macros. This example results in the LoadError: UndefVarError: args not defined. I would definitely appreciate any help in getting this script working as expected (input is [1, 2, 3], and output should be [5, 6, 7]).
Any help/suggestions are greatly appreciated.
Update
More concretely, given the following defined functions:
function add!(v::Vector{Float64}, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::Vector{Float64}, a::Float64, j::Int64)
v[j]+= a
end
I would like to be able to use a macro to convert the following lines of code:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
#vectorize_I_would_like_to_define(
# I don't know the exact form that the args to this macro should take.
add!(v, a),
add!(v, b)
)
To generate code that is compiled like this:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
add!(v, a, j)
add!(v, b, j)
end
My goal is to write code that requires a single memory traversal.
Even better, if I could generate code that looks like this at compile time:
v = [Float64(j) for j in 1:10]
a = 1
b = 2
for j in 1:length(v)
v[j]+= a # taken from add!(v::Vector{Float64}, a::Float64, j::Int64)
v[j]+= b # taken from add!(v::Vector{Float64}, a::Float64, j::Int64)
end
But I'm not sure if this is as feasable for more complex cases that I'm considering compared to this toy example.
** Update 2**
Here is a MWE of #Bogumił Kamiński's solution---except that I've moved the macro call into a function, so now it doesn't work because it complains that v_test is not defined.
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
function add!(v::Vector{Float64}, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::Vector{Float64}, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
function test!(v_test, a_test, b_test)
#vectorize(
add!(v_test, a_test),
add!(v_test, b_test)
)
end
test!(v, a, b)
Is this what you want?
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
quote
for j in 1:length(v)
$expr
end
end
end
and now
function add!(v::Vector{Float64}, a::Float64)
for j in 1:length(v)
v[j]+= a
end
end
function add!(v::Vector{Float64}, a::Float64, j::Int64)
v[j]+= a
end
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
#vectorize(add!(v, a), add!(v, b))
Note that I have changed a and b definitions as your add! required Float64 as a second argument.
EDIT: If you want to use this macro inside a function the simplest thing to do is to esc its whole return value:
macro vectorize(args...)
expr = :()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, :j)
expr = :($expr; $a)
end
esc(quote
for j in 1:length(v)
$expr
end
end)
end
Then you can define e.g.:
function f()
v = [Float64(j) for j in 1:10]
a = 1.0
b = 2.0
#vectorize(add!(v, a), add!(v, b))
v
end
and run f() to get the same result as above in global scope.
EDIT 2: I just realized that actually I have to sanitize j as otherwise the following code will fail:
test!(v_test, j, b_test) =
#vectorize(add!(v_test, j), add!(v_test, b_test))
Here is how you should do it:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
push!(a.args, j)
expr = :($expr; $a)
end
esc(quote
for $j in 1:length(v)
$expr
end
end)
end
As you can see developing macros is a non-obvious task (hopefully the final recipe is bug-free :)).
EDIT 3: Here is the code that correctly handles length. Also now in each expression actually you can pass a different value as a first argument (so you can independently process different vectors). If you do want to process the same vector check is a.args[2] is always the same symbol:
macro vectorize(args...)
expr = :()
j = gensym()
for arg in args
a = deepcopy(arg) # for safety in case arg is also used somewhere else
var = a.args[2]
push!(a.args, j)
q = quote
for $j in 1:length($var)
$a
end
end
expr = :($expr; $q)
end
esc(expr)
end
I would like to implement a function duration = timer(n, f, arguments_of_f) that would measure how much time does a method f with arguments arguments_of_f need to run n times. My attempt was the following:
function duration = timer(n, f, arguments_of_f)
duration = 0;
for i=1:n
t0 = cputime;
f(arguments_of_f);
t1 = cputime;
duration += t1 - t0;
end
In another file, I have
function y = f(x)
y = x + 1;
end
The call d1 = timer(100, #f, 3); works as expected.
In another file, I have
function y = g(x1, x2)
y = x1 + x2;
end
but the call d2 = timer(100, #g, 1, 2); gives an error about undefined
argument x2, which is, when I look back, somehow expected, since I pass only
1 to g and 2 is never used.
So, how to implement the function timer in Octave, so that the call like
timer(4, #g, x1, ... , xK) would work? How can one pack the xs together?
So, I am looking for the analogue of Pythons *args trick:
def use_f(f, *args):
f(*args)
works if we define def f(x, y): return x + y and call use_f(f, 3, 4).
You don't need to pack all the arguments together, you just need to tell Octave that there is more than one argument coming and that they are all necessary. This is very easy to do using variadic arguments.
Your original implementation is nearly spot on: the necessary change is minimal. You need to change the variable arguments_to_f to the special name varargin, which is a magical cell array containing all your arbitrary undeclared arguments, and pass it with expansion instead of directly:
function duration = timer(n, f, varargin)
duration = 0;
for i=1:n
t0 = cputime;
f(varargin{:});
t1 = cputime;
duration += t1 - t0;
end
That's it. None of the other functions need to change.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Passing a function as argument to another function
Below is a simple code for the bisection method. I would like to know how to be able to pass in any function I choose as a parameter instead of hard coding functions.
% This is an implementation of the bisection method
% for a solution to f(x) = 0 over an interval [a,b] where f(a) and f(b)
% Input: endpoints (a,b),Tolerance(TOL), Max # of iterations (No).
% Output: Value p or error message.
function bjsect(a,b,TOL,No)
% Step 0
if f(a)*f(b)>0
disp('Function fails condition of f(a),f(b) w/opposite sign'\n);
return
end
% Step 1
i = 1;
FA = f(a);
% Step 2
while i <= No
% Step 3
p = a +(b - a)/2;
FP = f(p);
% Step 4
if FP == 0 || (b - a)/2 < TOL
disp(p);
return
end
% Step 5
i = i + 1;
% Step 6
if FA*FP > 0
a = p;
else
b = p;
end
% Step 7
if i > No
disp('Method failed after No iterations\n');
return
end
end
end
% Hard coded test function
function y = f(x)
y = x - 2*sin(x);
end
I know this is an important concept so any help is greatly appreciated.
The simplest method is using anonymous functions. In your example, you would define your anonymous function outside bjsect using:
MyAnonFunc = #(x) (x - 2 * sin(x));
You can now pass MyAnonFunc into bjsect as an argument. It has the object type of function handle, which can be validated using isa. Inside bjsect simply use MyAnonFunc as if it is a function, ie: MyAnonFunc(SomeInputValue).
Note, you can of course wrap any function you've written in an anonymous function, ie:
MyAnonFunc2 = #(x) (SomeOtherCustomFunction(x, OtherInputArgs));
is perfectly valid.
EDIT: Oops, just realized this is almost certainly a duplicate of another question - thanks H. Muster, I'll flag it.
I use anonymous functions for simple data value transforms. The anonymous functions are defined with the following syntax
sqr = #(x) x.^2;
I would like to have a simple anonymous function that returns more than one output that can be used as follows . . .
[b,a] = myAnonymousFunc(x);
The Matlab documentation suggests that this is possible, but it does not give an example of the syntax needed to define such a function.
http://www.mathworks.co.uk/help/techdoc/matlab_prog/f4-70115.html#f4-71162
What is the syntax to define such a function [in a single line, like the code example at the top of my post]?
Does this do what you need?
>> f = #(x)deal(x.^2,x.^3);
>> [a,b]=f(3)
a =
9
b =
27
With this example, you need to ensure that you only call f with exactly two output arguments, otherwise it will error.
EDIT
At least with recent versions of MATLAB, you can return only some of the output arguments using the ~ syntax:
>> [a,~]=f(3)
a =
9
>> [~,b]=f(3)
b =
27
If you'd rather not skip outputs using tilde ~ nor output a cell array, you'd only need an auxiliary anonymous function:
deal2 = #(varargin) deal(varargin{1:nargout});
myAnonymousFunc = #(x) deal2(x.^2, x.^3);
then you can obtain just the first output argument or both first and second one:
x = 2;
[b,a] = myAnonymousFunc(x)
b = myAnonymousFunc(x)
results:
b =
4
a =
8
b =
4
You can get multiple outputs from an anonymous function if the function being called returns more than a single output. See this blog post on the MathWorks website for examples of this in action.
There are two ways to get multiple outputs from an anonymous function:
Call a function which returns multiple outputs
From the blog post linked to, they use the eig function like so
fdoubleEig = #(x) eig(2*x)
[e, v] = fdoubleEig(magic(3))
Alternatively you can construct an anonymous function which returns multiple outputs using the deal function.
Here is one I made up:
>>> f = #(x, y, z) deal(2*x, 3*y, 4*z)
>>> [a, b, c] = f(1, 2, 3)
a =
2
b =
6
c =
12
Edit: As noted by Sam Roberts, and in the blog post I link to, you must use the correct number of output arguments when using deal, otherwise an error is thrown. One way around this is to return a cell of results. For example
>>> f = #(x, y, z) {2*x, 3*y, 4*z}
>>> t = f(1, 2, 3)
>>> [a, b, c] = t{:}
a =
2
b =
6
c =
12