Search code examples
juliaevalexpression-evaluation

Julia - ARM Remez code eval() world age mismatch


I'm still trying to compile the original ARM Remez code Julia 0.4 under modern dialect v1.9.3 and it is putting up some resistance. It now compiles OK but fails at runtime. There are two distinct problems one is fundamental: world age mismatch "function is too new" and way beyond my present understanding and the other is probably trivial but I can't see the wood for the trees. This is the really hard one. It fails to @eval sin(x) internally.

martin@martin-virtual-machine:~/Downloads$ julia ARMremez.jl 0 1 5 0 'sin(x)'
   Resolving package versions...
   No Changes to `~/.julia/environments/v1.9/Project.toml`
   No Changes to `~/.julia/environments/v1.9/Manifest.toml`
ERROR: LoadError: MethodError: no method matching (::var"#53#54")(::BigFloat)
The applicable method may be too new: running in world age 33517, while current world is 33518.

Closest candidates are:
 (::var"#53#54")(::Any) (method too new to be called from this world context.)
 @ Main none:1

Stacktrace:
 [1] (::var"#func#49"{var"#53#54"})(x::BigFloat)
  @ Main ~/Downloads/ARMremez.jl:1229
 [2] ratfn_leastsquares(f::var"#func#49"{var"#53#54"}, xvals::Vector{BigFloat}, n::Int64, d::Int64, w::var"#47#51")
  @ Main ~/Downloads/ARMremez.jl:348
 [3] ratfn_minimax(f::var"#func#49"{var"#53#54"}, interval::Tuple{BigFloat, BigFloat}, n::Int64, d::Int64, w::var"#47#51")
  @ Main ~/Downloads/ARMremez.jl:819
 [4] main()
  @ Main ~/Downloads/ARMremez.jl:1253
 [5] top-level scope
  @ ~/Downloads/ARMremez.jl:1330
  in expression starting at /home/martin/Downloads/ARMremez.jl:1330

 # the relevant sourcecode after my modification is below f is the parameter 'sin(x)' from the commandline

f = eval(Meta.parse("x -> " * argwords[5]))

# Wrap the user-provided function with a function of our own. This
# arranges to detect silly FP values (inf,nan) early and diagnose
# them sensibly, and also lets us log all evaluations of the
# function in case you suspect it's doing the wrong thing at some
# special-case point.
@noinline function func(x)
    y = f(x)
    @debug("f", x, " -> ", y)
    if !isfinite(y)
        error("f(" * string(x) * ") returned non-finite value " * string(y))
    end
    return y
end

I have tried adding the incantation @noinline as recommended in @eval world age mismatch but still got a very similar error (I'm probably doing it wrong). I can work out from the stack trace that the failure is in executing the wrapped implementation of evaluating the user supplied function 'sin(x)' from the command line, but after that I am stuck. I find it amazing that the environment can magic up BigFloat sin(x) to order inside the already running code.

The other is too trivial to start a thread about and must be really obvious to anyone well experienced with Julia but it certainly isn't obvious to me. I still haven't got the hang of declaring typed arrays in Julia 1.9.3 The specific line that I can't get past this time is

# original line in v0.4 code
# a = Array(BigFloat, n+1)

# my best guess according to the syntax tree
a = Array{BigFloat}{undef,n+1}

I tried lots of of other combos but got precisely nowhere. I can declare an array that is initialised with values OK but I cannot figure out how to declare a 1D array of length N :( A lamentable failure on my part...


Solution

  • Array construction

    The easy question first: you almost have it, it just needs ordinary parentheses for the last part: a = Array{BigFloat}(undef,n+1). One way to remember it is curly braces { ... } are used for type specification, and parentheses ( ... ) make a function call. So here we're specifying an Array containing type BigFloat (which goes in curlies because it's a type), and we want to call the constructor for that Array (so parentheses because it's a call).
    Or you can just use zeros which you might find simpler to remember: zeros(BigFloat, n+1).

    World age issues

    The @noinline suggestion in the linked question is from a comment that's mentioned as not working, the actual solution suggested there is invokelatest, as given in the answer there. To implement that here, you can change

    function func(x)
        y = f(x)
    

    in the original code to

    function func(x)
        y = invokelatest(f, x)
    

    and similarly also change

        function wrapped_weight(x,y)
            ww = w(x,y)
    

    to

        function wrapped_weight(x,y)
            ww = invokelatest(w, x,y)
    

    However, this changes every call to these functions to be a dynamic runtime-determined call, which can slow down the program - it looks like f and w are being called from a main while true loop, so this could be a lot of slowdown.

    If you find that this makes it unworkably slow, you can instead move the first half of the main function (these lines) to be outside main in global scope. Putting the evals in global scope makes it so that f and w are created in the same world age as ratfn_leastsquares and other functions, so should avoid the world age problem without needing invokelatest.