stefan-m-lenz / JuliaConnectoR

A functionally oriented interface for calling Julia from R
Other
100 stars 6 forks source link

MethodError with juliaEval for using a julia function in R #6

Closed durraniu closed 3 years ago

durraniu commented 3 years ago

Thank you for your package. Please help with my question as follows.

Goal

I have written a function in julia that I want to now use in R. I have tested the function in julia, it works fine. But I think I am not using JuliaConnectoR the correct way. Please show me the correct way of using my julia function in R.

Data and julia function

If you need to get the data, you can download it here.

# loading library
library(JuliaConnectoR)

# load data
load(file = here::here("data", "tidy_data", "sdata.Rda"))

# create a new julia function and assign it in R
apply_wiedemann_julia <- juliaEval('function apply_wiedemann(df, V_DESIRED, FAKTORVmult, BMAXmult, BNULLmult, 
        L, W, AXadd, BXadd, angular_vel_threshold, EXadd,  OPDVadd)  

## Parameters --------------------------------------------------------------------
V_MAX = 44

L = L

angular_vel_threshold = angular_vel_threshold
CX = sqrt(W / angular_vel_threshold)

BMIN = -8
AX = L + AXadd 

## Time--------------------------------------------------------------------------
delta_T = (df[2,"frames"] - df[1,"frames"])/60
last_time = df[end, "time_complete"]
Time = 0:delta_T:last_time

ts = size(Time, 1)

## Empty vectors-----------------------------------------------
BMAX = Vector{Union{Float64,Missing}}(missing, ts)
vn_complete = Vector{Union{Float64,Missing}}(missing, ts)
vn_complete[1] = df[1,"ED_speed_mps"]

vn1_complete = df[!,"LV_speed_mps"]
dv = Vector{Union{Float64,Missing}}(missing, ts)
dv[1] = df[1,"LV_DV_mps"]

xn_complete = Vector{Union{Float64,Missing}}(missing, ts)
xn_complete[1] = df[1,"ED_position_m"]

xn1_complete = df[!,"LV_position_m"]

bn_complete = Vector{Union{Float64,Missing}}(missing, ts)

sn_complete = Vector{Union{Float64,Missing}}(missing, ts)
sn_complete[1] = df[1,"LV_spacing_m"]

BX = Vector{Union{Float64,Missing}}(missing, ts) ### an empty vector 
ABX = Vector{Union{Float64,Missing}}(missing, ts) ### an empty vector 

SDV = Vector{Union{Float64,Missing}}(missing, ts)### an empty vector 
B_App = Vector{Union{Float64,Missing}}(missing, ts) ### an empty vector 

bl = df[!, "LV_acc_mps2"]

B_Emg = Vector{Union{Float64,Missing}}(missing, ts)

SDX = Vector{Union{Float64,Missing}}(missing, ts)

CLDV = Vector{Union{Float64,Missing}}(missing, ts)

OPDV = Vector{Union{Float64,Missing}}(missing, ts)

cf_state_sim = Vector{Union{String,Missing}}(missing, ts)

## Unintentional Acceleration and Deceleration when the car is at V_DESIRED
# BNULL = BNULLmult * (RND4 + NRND) 
BNULL = BNULLmult 

FaktorV = V_MAX / (V_DESIRED + FAKTORVmult * (V_MAX - V_DESIRED))

# EX = EXadd + EXmult * (NRND - RND2)
EX = EXadd

for t in 1:(ts-60)

    #println("$t")

    ## Speed-dependent part of Minimum following distance
    BX[t] = ifelse(!ismissing(vn1_complete[t]),
        BXadd * sqrt(min(vn_complete[t], vn1_complete[t])),
        BXadd * sqrt(vn_complete[t])
        )

    ## Minimum following distance
    ABX[t] = AX + BX[t] 

    ## Speed-difference at which driver perceives that the lead vehicle is slow
    SDV[t] = ((sn_complete[t] - AX)/CX)^2 ###0.34 |

    ## Maximum following distance
    SDX[t] = AX + (EX * BX[t])

    ## Speed-difference when driver perceives that lead vehicle is slower
    CLDV[t] = SDV[t] * EX^2

    ## Speed-difference when driver perceives that lead vehicle is faster
    # OPDV = CLDV * (((-1) * OPDVadd) - (OPDVmult * NRND))
    OPDV[t] = CLDV[t] * ((-1) * OPDVadd)

    if ismissing(sn_complete[t]) | ismissing(dv[t])

        BMAX[t] = BMAXmult * (V_MAX - (vn_complete[t] * FaktorV)) 

        bn_complete[t] = BMAX[t]

        cf_state_sim[t] = "free_driving"

    elseif sn_complete[t] <= ABX[t] 

        B_Emg[t] = 0.5 * ((dv[t])^2 / (AX - sn_complete[t])) + bl[t] + 
            (BMIN * ((ABX[t] - sn_complete[t]) / (ABX[t] - AX)))

        bn_complete[t] = ifelse((B_Emg[t] < BMIN) | (B_Emg[t] > 0), BMIN, B_Emg[t])

        cf_state_sim[t] = "emergency_braking"

    elseif sn_complete[t] < SDX[t]

        if dv[t] > CLDV[t] 

            bn_complete[t] = BNULL

            cf_state_sim[t] = "following"

        elseif dv[t] > OPDV[t]

            bn_complete[t] = BNULL

            cf_state_sim[t] = "following"

        else 

            BMAX[t] = BMAXmult * (V_MAX - (vn_complete[t] * FaktorV)) 

            bn_complete[t] = BMAX[t]

            cf_state_sim[t] = "free_driving"

        end

    else 
        if dv[t] > SDV[t]

            B_App[t] = 0.5 * ((dv[t])^2 / (ABX[t] - sn_complete[t])) + bl[t]

            bn_complete[t] = ifelse((B_App[t] < BMIN), BMIN, B_App[t])

            cf_state_sim[t] = "approaching"
        else
            BMAX[t] = BMAXmult * (V_MAX - (vn_complete[t] * FaktorV)) 

            bn_complete[t] = BMAX[t]

            cf_state_sim[t] = "free_driving"
        end
    end

    vn_complete[t+1] = max(0, vn_complete[t] + (bn_complete[t] * delta_T))

    xn_complete[t+1] = xn_complete[t] - (vn_complete[t] * delta_T) + (0.5 * bn_complete[t] * (delta_T)^2)

    sn_complete[t+1] = xn_complete[t+1] - xn1_complete[t+1]

    dv[t+1] = vn_complete[t+1] - vn1_complete[t+1]

#     println("t: ", t, " dv: ", round(dv[t], digits=1), " SDV: ", round(SDV[t], digits=1), " vn_complete: ", round(vn_complete[t+1], digits=1))
end 

    one_liner = sqrt( sum( skipmissing( ( vn_complete .- df[!, "ED_speed_mps"] ).^2 ) ) / length(Time) )

    return(one_liner)

end')

Evaluating the function in R

You can see the method error as follows:

apply_wiedemann_julia(data = sdata,
                V_DESIRED = 30,
                FAKTORVmult = 0.02,
                BMAXmult = 0.08,
                BNULLmult = 0.25,
                L = unique(sdata$LV_length_m)[2],
                W = unique(sdata$LV_width_m)[2],
                AXadd = 5,
                BXadd = 5,
                angular_vel_threshold = 0.0001,
                EXadd = 2,
                OPDVadd = 2)

Error in handleCallbacksAndOutput() : Evaluation failed
Original error:
MethodError: no method matching apply_wiedemann(; OPDVadd=2.0, FAKTORVmult=0.02, angular_vel_threshold=0.0001, data=Main.RConnector.RDataFrame: (file.ID = ["A", "A", "A", "A", "A", "A", "A", "A", "A", "A"  …  "A", "A", "A", "A", "A", "A", "A", "A", "A", "A"], LV_length_m = Union{Missing, Float64}[missing, missing, missing, missing, missing, missing, missing, missing, missing, missing  …  5.39, 5.39, 5.39, 5.39, 5.39, 5.39, 5.39, 5.39, 5.39, 5.39], LV_width_m = Union{Missing, Float64}[missing, missing, missing, missing, missing, missing, missing, missing, missing, missing  …  2.29, 2.29, 2.29, 2.29, 2.29, 2.29, 2.29, 2.29, 2.29, 2.29], angular_vel_threshold = [0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241, 0.001848094327621241  …  0.001848094327621241, 0.001848094327621241, 0.001848094327621241,
stefan-m-lenz commented 3 years ago

You have defined the arguments in the Julia function as positional arguments. If you use named arguments in R, they will be used as keyword arguments in Julia. But your function does not accept keyword arguments. You need to define it like said in the error message via

apply_wiedemann(; OPDVadd=2.0, FAKTORVmult=0.02,....

Notice the semicolon in front? Then Julia can find the method.

durraniu commented 3 years ago

Thanks for your reply. I modified the function as follows:


apply_wiedemann_julia <- juliaEval('function apply_wiedemann_julia(;df, V_DESIRED, FAKTORVmult, BMAXmult, BNULLmult,
        L, W, AXadd, BXadd, angular_vel_threshold, EXadd,  OPDVadd) 

## function continued as before ##')

However, running the function throws a different error:

apply_wiedemann_julia(df = sdata,
                V_DESIRED = 30,
                FAKTORVmult = 0.02,
                BMAXmult = 0.08,
                BNULLmult = 0.25,
                L = unique(sdata$LV_length_m)[2],
                W = unique(sdata$LV_width_m)[2],
                AXadd = 5,
                BXadd = 5,
                angular_vel_threshold = 0.0001,
                EXadd = 2,
                OPDVadd = 2)

Error in handleCallbacksAndOutput() : Evaluation failed
Original error:
MethodError: no method matching getindex(::Main.RConnector.RDataFrame, ::Int64, ::String)
Closest candidates are:
  getindex(::Union{Tables.AbstractColumns, Tables.AbstractRow}, ::Int64) at C:\Users\umair\.julia\packages\Tables\UxLRG\src\Tables.jl:174
  getindex(::Union{Tables.AbstractColumns, Tables.AbstractRow}, !Matched::Symbol) at C:\Users\umair\.julia\packages\Tables\UxLRG\src\Tables.jl:175
Stacktrace:
 [1] Main.RConnector.Fail(::String, ::MethodError) at C:\Users\umair\Documents\R\win-library\4.0\JuliaConnectoR\Julia\reading.jl:6
 [2] evaluate_checked!(::Main.RConnector.Call) at C:\Users\umair\Documents\R\win-library\4.0\JuliaConnectoR\Julia\evaluating.jl:57
 [3] serve_repl(::Sockets.TCPSocket) at C:\Users\umair\Documents\R\win-library\4.0\JuliaConnectoR\Julia\communicating.jl:103
 [4] serve(::Int64; keeprunning::Bool, portfile::String) at C:\Users\umair\Documents\R\win-library\4.0\JuliaConnectoR\Julia\communicating.jl:73
 [5] top-level
stefan-m-lenz commented 3 years ago

The error is thrown because you try to index an RConnector.RDataFrame object and this is not defined.

You index the df via df[2,"frames"]. When you pass a data frame object to Julia, it is translated to an RConnector.RDataFrame object. This is an object that satisfies the Julia Tables interface. It is not a DataFrame object from the DataFrames package. You can, however, create such an object easily by calling

df = DataFrames.DataFrame(df)

Then you can index the resulting object.

I will think about whether it would make sense to add indexing to the RConnector.RDataFrame objects.

durraniu commented 3 years ago

Thank you very much for your help! It works now.

durraniu commented 3 years ago

However, strangely, it is slower than the same function in R. With juliaCall package it is faster but needs to have the dataframe assigned beforehand. I guess my best bet is to do everything in julia.

stefan-m-lenz commented 3 years ago

Transferring data from R to Julia needs extra time. If you assign the data frame beforehand, it will be faster later on because the data does not need to be communicated later.

juliaEval("using DataFrames")
df <- juliaCall("DataFrames.DataFrame", sdata)
apply_wiedemann_julia(df = df, ... )

For getting the best performance, the amount of data that needs to be communicated between R and Julia must be minimised. In your case, you could, e. g., read the data directly in Julia.

durraniu commented 3 years ago

Thank you so much. This has been a very pleasant learning experience.