1

I'm new to julia and I'm trying to plot a function over a range of values that I defined myself with Gadfly. The function itself is pretty simple.

function metropolis(dU, b)
    if dU < 0
        1
    else
        exp(-dU * b)
    end
end

When I try to plot this function I get an InexactError thrown from julia.

using Gadfly
x = linspace(-5, 5, 100)
b = 1
plot(x=x, y=metropolis.(x, b), Geom.line)

The exact error is

Stacktrace:
[1] apply_scale_typed!(::Array{Int64,1}, ::Array{Real,1},  ::Gadfly.Scale.ContinuousScale) at /home/max/.julia/v0.6/Gadfly/src/scale.jl:249
[2] apply_scale(::Gadfly.Scale.ContinuousScale, ::Array{Gadfly.Aesthetics,1}, ::Gadfly.Data, ::Vararg{Gadfly.Data,N} where N) at /home/max/.julia/v0.6/Gadfly/src/scale.jl:206
[3] apply_scales(::IterTools.Distinct{Base.ValueIterator{Dict{Symbol,Gadfly.ScaleElement}},Gadfly.ScaleElement}, ::Array{Gadfly.Aesthetics,1}, ::Gadfly.Data, ::Vararg{Gadfly.Data,N} where N) at /home/max/.julia/v0.6/Gadfly/src/scale.jl:33
[4] apply_scales(::IterTools.Distinct{Base.ValueIterator{Dict{Symbol,Gadfly.ScaleElement}},Gadfly.ScaleElement}, ::Gadfly.Data) at /home/max/.julia/v0.6/Gadfly/src/scale.jl:52
[5] render_prepare(::Gadfly.Plot) at /home/max/.julia/v0.6/Gadfly/src/Gadfly.jl:670
[6] render(::Gadfly.Plot) at /home/max/.julia/v0.6/Gadfly/src/Gadfly.jl:748
[7] show at /home/max/.julia/v0.6/Gadfly/src/Gadfly.jl:952 [inlined]
[8] limitstringmime(::MIME{Symbol("image/svg+xml")}, ::Gadfly.Plot) at /home/max/.julia/v0.6/IJulia/src/inline.jl:24
[9] display_dict(::Gadfly.Plot) at /home/max/.julia/v0.6/IJulia/src/execute_request.jl:29
[10] execute_request(::ZMQ.Socket, ::IJulia.Msg) at /home/max/.julia/v0.6/IJulia/src/execute_request.jl:182
[11] eventloop(::ZMQ.Socket) at /home/max/.julia/v0.6/IJulia/src/eventloop.jl:8
[12] (::IJulia.##14#17)() at ./task.jl:335

But strangely when I use -x it works

plot(x=x, metropolis.(-x, b), Geom.line)

This is only reversing the order of the values. This behavior is very strange to me. I appreciate any help.

Max Linke
  • 1,705
  • 2
  • 18
  • 24
  • The answer you've been given explains the situation well. But FYI, a neat way to write your function is simply: `metropolis(dU, b) = dU < 0 ? 1.0 : Float64(exp(-dU*b))`. This is now type-stable, since the return from the `exp` call is always converted to `Float64` if possible, or else errors out if, e.g. one of your inputs in `Complex`. If you might encounter inputs like `Complex`, then the simplest solution is to just add a second method dealing explicitly with that case. – Colin T Bowers Oct 26 '17 at 22:33

1 Answers1

3

The reason is that your function is not type stable - its return value depends on values of arguments and not only on their types. Here is the simplest (but not the fastest) fix showing you a proper way to code the function:

function metropolis(dU, b)
    if dU < 0
        zero(exp(-dU * b))
    else
        exp(-dU * b)
    end
end

In general - ensure that both branches of if-else return the same type.

The reason that Gadfly fails is due to this part of code: https://github.com/GiovineItalia/Gadfly.jl/blob/master/src/scale.jl#L194

and probably should be fixed as it performs break on the first concrete type, which is actually an incorrect decision for Array{Real}.

Colin T Bowers
  • 18,106
  • 8
  • 61
  • 89
Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
  • Is there a way to construct a variable of type `T` inside of the function when I use the signature `metropolis{T<:Real}(dU::T, b::T)`? – Max Linke Oct 26 '17 at 19:40
  • The correct signature in current Julia is `metropolis(dU::T, b::T) where T<:Real`. Then you can convert anything to `T` inside a function by writing `T([some value])`. Note however that `exp` does not have to return value of type `T` if both `dU` and `b` are of type `T`, e.g. `exp(1)` is `Float64` although its argument is `Int`. – Bogumił Kamiński Oct 26 '17 at 20:12