Pass the formula as a function parameter to Julia

I'm trying to create a function that would allow me to change the formula and coefficients in Julia. Am I 80% sure I should use anonymous functions for this?

This SO post using python is a more discrete example of what I'm trying to accomplish (specifically, a basic python example by chepner rather than using a library). Pass formula as function parameter in python

I also found this SO record using Julia, which uses a type to store the necessary parameters and then passes them to the functions. How to pass a list of function parameters to Julia

Using them as a base, this is what I have created so far:

#Create composite type type Params formula b1::Float64 b2::Float64 end #create instance of type and load foo=Params((b1,b2,X)-> X^b1+X+b2,0.004,0.005) #create function function DoMath(X,p::Params) p.formula(X,varargs...) #?? end 
  • Am I on the right track regarding how to structure this using compound types and / or lambdas? I don’t have training in CS, and I confuse my way through many concepts, trying to get to know Julia.

  • What is the correct syntax for a function that can allow the user to change the formula and any coefficients. for a given X? Ultimately, I present something with features such as:

     DoMath(4) #some default formula with changing X DoMath(4, X*b1 +X*b2) #change X and change formula DoMath(4, (X,b1,b2,b3)->X*b1+X*b2+x*b3) # change x, change formula to a 3 parameter function 

thanks

Update: I got it working by following the @Chris syntax. One thing I had to redo with is to use

  (p::Params)(x) = p.formula(x,pb) #pb, not just b otherwise error 

and I had to wrap 2.0 and 3.0 in an array before calling

  p = Params((x,b)->x*b[1]+b[2],[2.0,3.0]) 
+5
source share
2 answers

The idea is to create a callable type. The type of the called type is any type that has a "call". The function f is a callable type because you can call it: f(x) for example. However, functions are not the only things that can act as functions. Indeed, in Julia, functions are basically callable types, which are <: Function .

So, let's build one for your example. Enter your data type that you want:

 type Params b1::Float64 b2::Float64 end 

Now add a call to Params . Say we want to make x*b1 + b2 . We can make a call that does this:

 (p::Params)(x) = x*p.b1 + p.b2 

Let's see how it works. Make pairs:

 p = Params(2.0,3.0) 

Now we can calculate the formula using its call:

 p(4) # 4*2+3 = 11 

Now let's see that p acts as a function that uses internal data. It is he.

The rest is built from the same foundation. You must respect the fact that Julia's types are not dynamic. This is for good reason. However, let me say that you do not know how much you want. Then you can just have a field like array b :

 type Params b::Vector{Float64} end (p::Params)(x) = x*b[1] + b[2] 

Now say that you wanted to change the formula. Then you can have a formula field:

 type Params formula b::Vector{Float64} end 

and make a call by entering values ​​into this:

 (p::Params)(x) = p.formula(x,b) 

Now, if the user has done:

 p = Params((x,b)->x*b[1]+b[2],2.0,3.0) 

then as before:

 p(4) # 4*2+3 = 11 

Yay acts the same and still uses internal data.

But since p is just any ol type, we can change the fields. So we can change:

 p.formula = (x,b)-> x*b[1]+x*b[2]+b[3] push!(pb,2.0) # pb == [2.0,3.0,2.0] 

and call again, now using the updated fields:

 p(4) # 4*2 + 4*3 + 2 = 22 

Indeed, as @LyndonWhite pointed out, ParameterizedFunctions.jl implements something similar. The strategy for this is called types.

Additional small detail

Some libraries were built (incorrectly), requiring the user to pass a function. So here we have p that "acts as a function", but some libraries do not accept it.

However, there is a quick fix. Just do it <:Function . Example:

 type Params <: Function b1::Float64 b2::Float64 end 

Now things that require a function will take your p , since that is <:Function . This is the only way to indicate that in Julia Function is an abstract type, and each Function is just a callable type, which is a subtype of Function .

+12
source

Here is a similar model that I am currently using to solve these problems with "fixed parameters" and "changing parameters." Fixed parameters do not often change when you run a specific program (for example, b1 , b2 , b3 ). Changing parameters are ordinary variables (e.g. x ) that almost continue to change between each function call. In many cases, to solve this problem, it is enough to use additional arguments or keyword arguments, but if we want to simultaneously change both the function and its parameters, this solution may not be ideal. As a response, this post suggested it was better to create a type and use multiple sending. However, we also need to manually unpack type in the function body. In fact, we can write a more general wapper function using the @generated macro:

 abstract FormulaModel immutable Foo{F<:Function} <: FormulaModel formula2fixedparams::F fixedParam1::Float64 fixedParam2::Float64 end Foo(f, b1=5., b2=10.) = Foo(f, b1, b2) Foo() = Foo((x,b1,b2)->x^b1+x+b2) # default formula immutable Bar{F<:Function} <: FormulaModel formula3fixedparams::F fixed1::Float64 fixed2::Float64 fixed3::Float64 end Bar(b1,b2,b3) = Bar((x,b1,b2,b3)->b1*x+b2*x+b3*x, b1, b2, b3) Bar() = Bar(1.,2.,3.) @generated function DoMath(model::FormulaModel, changingParams...) fixedParams = [:(getfield(model, $i)) for i = 2:nfields(model)] func = :(getfield(model, 1)) # prepare some stuff quote # do something $func(changingParams..., $(fixedParams...)) # do something else end end julia> DoMath(Foo(), 4) 1038.0 julia> DoMath(Foo((x,y,b1,b2)->(b1*x+b2*y)), 4, 10) 120.0 julia> DoMath(Bar(), 4) 24.0 
+4
source

Source: https://habr.com/ru/post/1265898/


All Articles