Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia @evalpoly macro with varargs

Tags:

julia

I'm trying to grok using Julia's @evalpoly macro. It works when I supply the coefficients manually, but I've been unable to puzzle out how to provide these via an array

julia> VERSION
v"0.3.5"

julia> @evalpoly 0.5 1 2 3 4
3.25

julia> c = [1, 2, 3, 4]
4-element Array{Int64,1}: 
 1
 2
 3
 4

julia> @evalpoly 0.5 c
ERROR: BoundsError()

julia> @evalpoly 0.5 c...
ERROR: BoundsError()

julia> @evalpoly(0.5, c...)
ERROR: BoundsError()

Can someone point me in the right direction on this?

Added after seeing the great answers to this question

There is one subtlety to that I hadn't seen until I played with some of these answers. The z argument to @evalpoly can be a variable, but the coefficients are expected to be literals

julia> z = 0.5
0.5

julia> @evalpoly z 1 2 3 4
3.25

julia> @evalpoly z c[1] c[2] c[3] c[4]
ERROR: c not defined

Looking at the output of the expansion of this last command, one can see that it is indeed the case that z is assigned to a variable in the expansion but that the coefficients are inserted literally into the code.

julia> macroexpand(:@evalpoly z c[1] c[2] c[3] c[4])
:(if Base.Math.isa(z,Base.Math.Complex)
        #291#t = z
        #292#x = Base.Math.real(#291#t)
        #293#y = Base.Math.imag(#291#t)
        #294#r = Base.Math.+(#292#x,#292#x)
        #295#s = Base.Math.+(Base.Math.*(#292#x,#292#x),Base.Math.*(#293#y,#293#y))
        #296#a2 = c[4]
        #297#a1 = Base.Math.+(c[3],Base.Math.*(#294#r,#296#a2))
        #298#a0 = Base.Math.+(Base.Math.-(c[2],Base.Math.*(#295#s,#296#a2)),Base.Math.*(#294#r,#297#a1))
        Base.Math.+(Base.Math.*(#298#a0,#291#t),Base.Math.-(c[1],Base.Math.*(#295#s,#297#a1)))
    else 
        #299#t = z
        Base.Math.+(Base.Math.c[1],Base.Math.*(#299#t,Base.Math.+(Base.Math.c[2],Base.Math.*(#299#t,Base.Math.+(Base.Math.c[3],Base.Math.*(#299#t,Base.Math.c[4]))))))
    end)
like image 888
Mike Satteson Avatar asked Jan 21 '15 21:01

Mike Satteson


2 Answers

I don't believe what you are trying to do is possible, because @evalpoly is a macro - that means it generates code at compile-time. What it is generating is a very efficient implementation of Horner's method (in the real number case), but to do so it needs to know the degree of the polynomial. The length of c isn't known at compile time, so it doesn't (and cannot) work, whereas when you provide the coefficients directly it has everything it needs.

The error message isn't very good though, so if you can, you could file an issue on the Julia Github page?

UPDATE: In response to the update to the question, yes, the first argument can be a variable. You can think of it like this:

function dostuff()
  z = 0.0
  # Do some stuff to z
  # Time to evaluate a polynomial!
  y = @evalpoly z 1 2 3 4
  return y
end

is becoming

function dostuff()
  z = 0.0
  # Do some stuff to z
  # Time to evaluate a polynomial!
  y = z + 2z^2 + 3z^3 + 4z^4
  return y
end

except, not that, because its using Horners rule, but whatever. The problem is, it can't generate that expression at compile time without knowing the number of coefficients. But it doesn't need to know what z is at all.

like image 128
IainDunning Avatar answered Sep 19 '22 00:09

IainDunning


Macros in Julia are applied to their arguments. To make this work, you need to ensure that c is expanded before @evalpoly is evaluated. This works:

function f()
    c=[1,2,3,4]
    @eval @evalpoly 0.5 $(c...)
end

Here, @eval evaluates its argument, and expands $(c...). Later, @evalpoly sees five arguments.

As written, this is probably not efficient since @eval is called every time the function f is called. You need to move the call to @eval outside the function definition:

c=[1,2,3,4]
@eval begin
    function f()
        @evalpoly 0.5 $(c...)
    end
end

This calls @eval when f is defined. Obviously, c must be known at this time. Whenever f is actually called, c is not used any more; it is only used while f is being defined.

like image 21
Erik Schnetter Avatar answered Sep 20 '22 00:09

Erik Schnetter