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)
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.
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.
Erik and Iain have done a great job of explaining why #evalpoly doesn't work and how to coerce it into working. If you just want to evaluate the polynomial, however, the easiest solution is probably just to use Polynomials.jl:
julia> using Polynomials
c = [1,2,3,4]
polyval(Poly(c), 0.5)
3.25
Related
I would like to have an algorithm for a n=a (where a is any number that I choose), such that
Julia understands that I declare the monomial variables x_{1}...x_{a} with #polyvar
Which package do you recommend me to do this?TypedPolynomials?
Thank you for your help
Normally you almost always use Vectors for things like this:
julia> a=3
3
julia> x = [n for n in 1:a]
3-element Vector{Int64}:
1
2
3
julia> x[2]
2
It is very hard to think about a scenario when it is not the best idea.
However, if you really need you can do a macro. The rule of thumb with Julia macros is "if you do not know how to write it perhaps you do not need it". Anyway here it is:
julia> macro var(n, initval)
exs = [ :( $(Symbol("x_",i)) = $initval ) for i in 1:n ]
esc(Expr(:block, exs...))
end
#var (macro with 1 method)
julia> #var(5, 77)
77
julia> x_4
77
When I try and run the inv() function on the example from the Julia documentation (v1.0.3), I get an error. The code is as follows (straight from the docs):
julia> M = [2 5; 1 3]
2×2 Array{Int64,2}:
2 5
1 3
julia> N = inv(M)
ERROR: MethodError: objects of type Array{Float64,2} are not callable
Use square brackets [] for indexing an Array.
It does work with pinv(), but I get some extremely small floating point values. Any ideas why I can't get inv() to work for this extremely simple case?
The error message suggests that you have previously defined a variable called inv which is a floating point matrix, and then try to use this matrix as a function, e.g.
julia> inv = rand(2, 2);
julia> M = [2 5; 1 3];
julia> inv(M)
ERROR: MethodError: objects of type Array{Float64,2} are not callable
Use square brackets [] for indexing an Array.
You can reach the inv function by restarting (and hence clearing the meaning of inv) or using the fully qualified name:
julia> import LinearAlgebra
julia> LinearAlgebra.inv(M)
2×2 Array{Float64,2}:
3.0 -5.0
-1.0 2.0
while working in julia programming, for creating an array instead of using a=[1:1:20...] i used a=[1:1:20] and it created an array saying "1-element Array{StepRange{Int64,Int64},1}".
What does this "1-element Array{StepRange{Int64,Int64},1}" mean? what StepRange means?
From the documentation of StepRange (type ?StepRange in the Julia REPL to see this):
StepRange{T, S} <: OrdinalRange{T, S}
Ranges with elements of type T with spacing of type S. The step
between each element is constant, and the range is defined in terms
of a start and stop of type T and a step of type S. Neither T nor S
should be floating point types. The syntax a:b:c with b > 1 and a,
b, and c all integers creates a StepRange.
So, for example
julia> typeof(1:1:20)
StepRange{Int64,Int64}
and
julia> [1:1:20]
1-element Array{StepRange{Int64,Int64},1}:
1:1:20
thus constructs a Vector (1D Array) containing one StepRange. If you want to materialize the lazy StepRange I would recommend collect(1:1:20) instead of using splatting ([1:1:20...]).
You can access start / step / stop fields of a StepRange using:
julia> r = 1:1:20
julia> r.start
1
julia> r.stop
20
julia> r.step
1
If we run:
#code_warntype deepcopy(rand(2))
at the Julia REPL, the output contains flagged values in the Body expression. Specifically, the two Any at the end of:
Body:
begin # deepcopy.jl, line 8:
GenSym(0) = (Base.Array)(Base.Any,32)::Array{Any,1}
return (Base.deepcopy_internal)(x::Array{Float64,1},$(Expr(:new, :((top(getfield))(Base,:ObjectIdDict)::Type{ObjectIdDict}), GenSym(0))))::Any
end::Any
I understand from this question that we usually don't need to worry about flagged values in the Body expression if our primary concern is type instability. So instead, my question is this:
Why does a fairly simple function from Base generate any flagged values in #code_warntype? I'm sure there are good reasons, but I am new at interpreting the output from #code_warntype, and had some trouble understanding the discussion of the Body expression from the official docs.
This is an example of a situation where type inference is unable to figure out the return type of a function. (Note the ::Any on the return value!) It is a problem, not because the computation itself will be slower because of type instability, but because the return type cannot be inferred, and so future computations using the return type will suffer from type instability.
You can see this effect by looking at allocations below:
julia> function f()
y = rand(10)
#time y[1] + y[10]
z = deepcopy(y)
#time z[1] + z[10]
end
f (generic function with 1 method)
julia> f(); # ignore output here on first compile
julia> f();
0.000000 seconds
0.000002 seconds (3 allocations: 48 bytes)
Note that the second operations requires allocations and takes time, because unboxing and dynamic dispatch are involved.
In the current nightly build of what will become 0.5 (which will likely be released within a few months), this has been fixed. Thus
julia> #code_warntype deepcopy(rand(2))
Variables:
#self#::Base.#deepcopy
x::Array{Float64,1}
Body:
begin # deepcopy.jl, line 8:
# meta: location dict.jl Type # dict.jl, line 338:
SSAValue(1) = (Core.ccall)(:jl_alloc_array_1d,(Core.apply_type)(Core.Array,Any,1)::Type{Array{Any,1}},(Core.svec)(Core.Any,Core.Int)::SimpleVector,Array{Any,1},0,32,0)::Array{Any,1}
# meta: pop location
return (Core.typeassert)((Base.deepcopy_internal)(x::Array{Float64,1},$(Expr(:new, :(Base.ObjectIdDict), SSAValue(1))))::Any,Array{Float64,1})::Array{Float64,1}
end::Array{Float64,1}
which has no type instability, and
julia> f()
0.000000 seconds
0.000000 seconds
which has no dynamic dispatch and no allocations.
I was delighted to learn that Julia allows a beautifully succinct way to form inner products:
julia> x = [1;0]; y = [0;1];
julia> x'y
1-element Array{Int64,1}:
0
This alternative to dot(x,y) is nice, but it can lead to surprises:
julia> #printf "Inner product = %f\n" x'y
Inner product = ERROR: type: non-boolean (Array{Bool,1}) used in boolean context
julia> #printf "Inner product = %f\n" dot(x,y)
Inner product = 0.000000
So while i'd like to write x'y, it seems best to avoid it, since otherwise I need to be conscious of pitfalls related to scalars versus 1-by-1 matrices.
But I'm new to Julia, and probably I'm not thinking in the right way. Do others use this succinct alternative to dot, and if so, when is it safe to do so?
There is a conceptual problem here. When you do
julia> x = [1;0]; y = [0;1];
julia> x'y
0
That is actually turned into a matrix * vector product with dimensions of 2x1 and 1 respectively, resulting in a 1x1 matrix. Other languages, such as MATLAB, don't distinguish between a 1x1 matrix and a scalar quantity, but Julia does for a variety of reasons. It is thus never safe to use it as alternative to the "true" inner product function dot, which is defined to return a scalar output.
Now, if you aren't a fan of the dots, you can consider sum(x.*y) of sum(x'y). Also keep in mind that column and row vectors are different: in fact, there is no such thing as a row vector in Julia, more that there is a 1xN matrix. So you get things like
julia> x = [ 1 2 3 ]
1x3 Array{Int64,2}:
1 2 3
julia> y = [ 3 2 1]
1x3 Array{Int64,2}:
3 2 1
julia> dot(x,y)
ERROR: `dot` has no method matching dot(::Array{Int64,2}, ::Array{Int64,2})
You might have used a 2d row vector where a 1d column vector was required.
Note the difference between 1d column vector [1,2,3] and 2d row vector [1 2 3].
You can convert to a column vector with the vec() function.
The error message suggestion is dot(vec(x),vec(y), but sum(x.*y) also works in this case and is shorter.
julia> sum(x.*y)
10
julia> dot(vec(x),vec(y))
10
Now, you can write x⋅y instead of dot(x,y).
To write the ⋅ symbol, type \cdot followed by the TAB key.
If the first argument is complex, it is conjugated.
Now, dot() and ⋅ also work for matrices.
Since version 1.0, you need
using LinearAlgebra
before you use the dot product function or operator.