# How to define a variable function in advance

**URL:** https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609
**Category:** General Usage
**Created:** [March 27, 2020, 5:59pm UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609 "2020-03-27T17:59:51Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![dmetivie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmetivie/32/6926_2.png) [@dmetivie](https://discourse.julialang.org/u/dmetivie)
#### Post date: [March 27, 2020, 5:59pm UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/1 "2020-03-27T17:59:51Z")

</div>

I want to define families of polynomial functions for which I can compute the coefficients in advance. For example, (here I stop at degree 3)

```julia
function poly1(n::Int64, x)
    if n == 0
        return 1.
    elseif n == 1
        return x
    elseif n == 2
        return x^2 +0.595238x-7.875
    elseif n == 3
        return x^3 + 1.62521x^2 -12.8858x -8.111
    end
end

```

I can also do a more generic code like

```julia
function poly2(n::Int64, x, coeff)
    if n == 0 
        return 1.
    else
        X = [x^j for j in 0:n]
	    return coeff[n]'*X
    end
end

```

where `coeff` is an array of array here  
`coeff = [[0, 1.0],[-7.875, 0.595238, 1.0],[-8.111, -12.8858, 1.62521, 1.0] ]`

The first one is far more efficient

```julia
using BenchmarkTools
@btime poly1(0,1.3);
0.021 ns (0 allocations: 0 bytes)
@btime poly1(1,1.3);
0.021 ns (0 allocations: 0 bytes)
@btime poly1(2,1.3);
0.021 ns (0 allocations: 0 bytes)

@btime poly2(0,1.3,coeff);
26.810 ns (1 allocation: 16 bytes)
@btime poly2(1,1.3,coeff);
94.884 ns (2 allocations: 112 bytes)
@btime poly2(2,1.3,coeff);
 103.483 ns (2 allocations: 128 bytes)

```

My question is how to define automatically in advance (once I know the `coeff` array), my polynomial functions for `n=0:nmax` as in `poly1` the most efficiently? (Because I need to call these functions a lot of time after)

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [March 27, 2020, 6:05pm UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/2 "2020-03-27T18:05:19Z")

</div>

> [@dmetivie](#):
>
> `x^3 + 1.62521x^2 -12.8858x -8.111`

Note that this is a terrible way to evaluate polynomials. Use `@evalpoly(x, -8.111, -12.8858, 1.62521, 1)`, which basically expands to [Horner’s rule](https://en.wikipedia.org/wiki/Horner%27s_method).

```julia
X = [x^j for j in 0:n]
return coeff[n]'*X

```

This is also a terrible way to evaluate polynomials, even given a coefficient array. Use Horner’s rule. (If you use the Polynomials.jl package to store polynomials, it will do this. Or just use `evalpoly` in Julia 1.4.)

You can use code generation to generate functions for given polynomials, but I would do this as a last resort. First, fix your code.

In fact, if you store the polynomial coefficients as tuples rather than arrays, the [`evalpoly` function](https://github.com/JuliaLang/julia/pull/32753), which I believe was added in Julia 1.4, should be basically as fast as inlined code generation.

---

<div class="post-metadata">

### Author: ![dmetivie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmetivie/32/6926_2.png) [@dmetivie](https://discourse.julialang.org/u/dmetivie)
#### Post date: [March 27, 2020, 9:03pm UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/3 "2020-03-27T21:03:21Z")

</div>

I don’t have Julia v1.4 (still on v1.0). I can do

```julia
function poly1_correct(n::Int64, x)
    if n == 0
        return 1.
    elseif n == 1
        return x
    elseif n == 2
        return @evalpoly(x,-7.875,0.595238,1)
    elseif n == 3
        return @evalpoly(x, -8.111,-12.8858,1.62521,1)
    end
end

```

For the one with `coeff` I am not sur how to code that  
I tried

```julia
for n in 1:3
    @eval begin
        function g(n,x)
           @evalpoly x $(coeff[n]...)
        end
    end
end

```

it works fast  
`0.021 ns (0 allocations: 0 bytes)`  
but it does not work because it returns only `g(3,x)`. Note that I could not get it working without the first `@eval`.  
Maybe with Julia 1.4 it will work more smoothly?

```julia

```

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [March 28, 2020, 2:55am UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/4 "2020-03-28T02:55:27Z")

</div>

You can always implement Horner’s method yourself…

I’m not sure why you have to use Julia 1.0 and not 1.4, but you can easily adapt the implementation of `evalpoly` from Julia 1.4 into your Julia 1.0 code: [https://github.com/JuliaLang/julia/blob/712731e1a1e9e85a272343ba2e773707ca90a466/base/math.jl#L101-L193](https://github.com/JuliaLang/julia/blob/712731e1a1e9e85a272343ba2e773707ca90a466/base/math.jl#L101-L193)

For example, the simplest generic method is:

```julia
# evaluate p(x), where the p argument is the coefficients of a polynomial
function evalpoly(x, p)
    N = length(p)
    ex = p[end]
    for i in N-1:-1:1
        ex = muladd(x, ex, p[i])
    end
    ex
end

```

---

<div class="post-metadata">

### Author: ![dmetivie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmetivie/32/6926_2.png) [@dmetivie](https://discourse.julialang.org/u/dmetivie)
#### Post date: [April 1, 2020, 2:23am UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/5 "2020-04-01T02:23:35Z")

</div>

Thanks for the tip. I agree that Horner’s rule is better:

```julia
function poly_3(n::Int64, x, coeff)
    if n == 0 
        return 1.
    else
        return evalpoly(x,coeff[n])
    end
end

@btime poly_3(0,1.3,coeff);
@btime poly_3(1,1.3,coeff);
@btime poly_3(2,1.3,coeff);
@btime poly_3(3,1.3,coeff);
  27.258 ns (1 allocation: 16 bytes)
  34.941 ns (1 allocation: 16 bytes)
  37.842 ns (1 allocation: 16 bytes)
  37.923 ns (1 allocation: 16 bytes)

```

But it still does not match the timing of `poly1` function defined at the top of the thread.  
Is there a way to define these functions coefficients before usage?  
It is not only for this problem but a more generic questions (I have polynomial product with fixed coefficients that I also want to pre-evaluate because I need to call them a lot of time).  
You mentioned code generation I tried to look it up but I am not sure how to apply it here, any advices?  
Thanks for the help.

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [April 1, 2020, 4:13am UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/6 "2020-04-01T04:13:16Z")

</div>

> [@dmetivie](#):
>
> But it still does not match the timing of `poly1` function defined at the top of the thread.

Yes, because that inlines/unrolls the loop. That’s the advantage of using tuples (whose size is known to the compiler) with the new `evalpoly` implementation in Julia 1.4.

---

<div class="post-metadata">

### Author: ![dmetivie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmetivie/32/6926_2.png) [@dmetivie](https://discourse.julialang.org/u/dmetivie)
#### Post date: [April 2, 2020, 1:16am UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/7 "2020-04-02T01:16:26Z")

</div>

I installed Julia 1.4

```julia
@btime evalpoly(1.3,(-8.111, -12.8858, 1.62521, 1.0))
0.021 ns (0 allocations: 0 bytes)

```

but

```julia
 c3 = (-8.111, -12.8858, 1.62521, 1.0)
@btime evalpoly(1.3,c3)
 22.939 ns (1 allocation: 16 bytes)

```

How are we suppose to define these tuples without writting all the coefficients explicitly? I cannot find an example of that.

Also, my other question still holds, how when you have two (or more) functions `f(x)` and `g(x)` can you define and ‘pre-compile’ `h(x) = g(x)*f(x)` ?

---

<div class="post-metadata">

### Author: ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)
#### Post date: [April 2, 2020, 1:21am UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/8 "2020-04-02T01:21:12Z")

</div>

> [@dmetivie](#):
>
> c3 = (-8.111, -12.8858, 1.62521, 1.0) @btime evalpoly(1.3,c3)

You are not benchmarking correctly; you need to “interpolate” to avoid accessing the global variable `c3`. Unfortunately currently you need to do this as follows with the strange `Ref` syntax and the `[]` indexing:

```julia
julia> @btime evalpoly(1.3, $(Ref(c3))[])
  1.652 ns (0 allocations: 0 bytes)
-19.9189351

```

The `0.021 ns` in the first benchmark suggests / indicates that the compiler is optimizing the whole calculation to just calculating the result.

Where do you want to take the coefficients from?  
You may want to check the [GitHub - JuliaAlgebra/StaticPolynomials.jl: Fast evaluation of multivariate polynomials](https://github.com/JuliaAlgebra/StaticPolynomials.jl) package.

I don’t understand your question about `g(x) * f(x)`.

---

<div class="post-metadata">

### Author: ![pixel27](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pixel27/32/8902_2.png) [@pixel27](https://discourse.julialang.org/u/pixel27)
#### Post date: [April 2, 2020, 1:58am UTC](https://discourse.julialang.org/t/how-to-define-a-variable-function-in-advance/36609/9 "2020-04-02T01:58:07Z")

</div>

If you search the forums on the document about performance and global variables you will find descriptions of why accessing global variables has bad performance and how to test around it. A simple way to do it is:

```julia
@btime evalpoly(1.3, $c3)

```

However like @dpsanders said, if the time is less than a nano second the compiler is doing optimizations on you and you are not benchmarking the actual operation.
