 # UndefVarError: θ not defined

Hi,

I stumbled upon this very nice example from @SebastianCallh in which Neural ODE are used to model and forecast the weather in the city of Delhi. I have a keen interest in learning how to apply differential equations using Julia, so I thought I could give it a try and adapt this code to work on my data.

Took some time to understand the basics of Julia (first timer coming from Python!), but in the end I managed to use @SebastianCallh example to put everything together as follows:

``````using Dates
using Plots
using Plotly
using DiffEqFlux
using Statistics
using DataFrames, CSV
using OrdinaryDiffEq, Flux, Random

function neural_ode(t, data_dim; saveat = t)
f = FastChain(FastDense(data_dim, 64, swish),
FastDense(64, 32, swish),
FastDense(32, data_dim))

node = NeuralODE(f, (minimum(t), maximum(t)), Tsit5(),
saveat = saveat, abstol = 1e-9,
reltol = 1e-9)
end

function train(node, θ, y, opt, maxiters, y0 = y[:, 1]; kwargs...)
predict(θ) = Array(node(y0, θ))
loss(θ) = begin
ŷ = predict(θ)
Flux.mse(ŷ, y)
end

θ = θ == nothing ? node.p : θ
res = DiffEqFlux.sciml_train(
loss, θ, opt,
maxiters = maxiters;
kwargs...
)
return res.minimizer
end

log_results(θs, losses) =
(θ, loss) -> begin
push!(θs, copy(θ))
push!(losses, loss)
false
end

################
# Main program #
################

meta_cols = ["rowid", "SiteId", "datetime", "longitude", "latitude"]
data_cols = ["datetime", "DryBulbTemperature_Celsius"]

# Loading 168 records of hourly temperature for a given week in July 2018
# Note: Contains 'missing' values that are casted to NaN and then ignored with filter(!isnan, )
path_in = "PATH/week_of_hourly_temperature_records.csv"

df["DryBulbTemperature_Celsius"] = replace(df["DryBulbTemperature_Celsius"], missing=>NaN)

# Normalizing and splitting on train/test
T = 72 # first 3 days for training, remaining 4 days for testing
mean_temp = mean(filter(!isnan, df["DryBulbTemperature_Celsius"])) # taking care of NaNs
stdev_temp = std(filter(!isnan, df["DryBulbTemperature_Celsius"]))

y = Matrix(df[:, ["DryBulbTemperature_Celsius"]] |>
y -> Matrix(y)' |>
y -> (y .- mean_temp) ./ stdev_temp)

train_dates, test_dates = df["datetime"][1:T], df["datetime"][T:end]
vals_train_norm, vals_test_norm = y[1:T], y[T:end]

# Starting with the analysis
Random.seed!(1);
maxiters = 150
θs, losses = [], []
θ = nothing
num_obs = 4:4:length(vals_train_norm)

for k in num_obs
chunk = filter(!isnan, vals_train_norm[1:k])
node = neural_ode(chunk, size(y, 1))

θ = train(
node, θ, chunk,
cb = log_results(θs, losses)
)

end
``````

However, I found this persistent error and I don’t know how to fix it:

``````ERROR: LoadError: **UndefVarError: θ not defined**
Stacktrace:
 top-level scope at /PATH/JuliaProjects/basics_of/attempt.jl:87
in expression starting at /PATH/JuliaProjects/basics_of/attempt.jl:78
``````

Because this θ is first set to “nothing” and it seems that will become iteratively calculated/updated. So it seems it makes sense to set up the variable with and empty value and let it change. I printed “node” and “f” and they contain values, so something starts happening, but then it crashes. I even changed the Greek symbol to “theta” just in case there was some problem there , but nothing.

Please, could you let me know what is missing in this code? How can I “define” theta properly? Perhaps an extra library import is required? Maybe the author can share the version creating these nice plots as well? You need a `global θ` declaration in this loop, since the loop is in global scope. (It wouldn’t show up if you were writing your loop in a function.)

This is the infamous global-scope rule (https://github.com/JuliaLang/julia/issues/28789); the error message should be clearer in Julia 1.5.

3 Likes

This is due to Julia’s scoping rules, which regularly trip up beginners. Variables defined in global scope (in this case, `θ`) aren’t visible within for loops unless they’re explicitly annotated as global variables. See here for further discussion.

A quick example:

``````julia>  θ = 0
0

julia> for i = 1:10
θ += 1
end
ERROR: UndefVarError: θ not defined
Stacktrace:
 top-level scope at .\REPL:2
``````
``````julia> for i = 1:10
global θ += 1
end

julia> θ
10
``````
2 Likes

@Irene I’m glad you found it useful, and welcome to Julia! @stevengj and @stillyslalom are absolutely right. The code you see in my blog post is inside a function which has different scoping rules. So it is totally fine to initialize a variable to `nothing`, but in this case the variable itself is not “seen” from inside the loop.

2 Likes

Well, this certainly did the trick! Thanks to all of your for your super fast response, much appreciated Indeed, I could not imagine it was a scope problem, but now that I know (and read) about that, I might spot this issue sooner the next time. Thanks!

1 Like

Actually, only if the error could add the substring “in this scope” would probably be sufficient and/or informative for the user. As in `"UndefVarError: θ not defined *in this scope*"`. Step by step, thanks for your efforts! 2 Likes

That change is a good suggestion, but this has already been changed more significantly in the upcoming 1.5 release. This works heuristically in the REPL and errors more clearly in a file. In the REPL:

``````julia> θ = 0
0

julia> for i = 1:10
θ += 1
end

julia> θ
10
``````

In a file:

``````┌ Warning: Assignment to `θ` in soft scope is ambiguous because
| a global variable by the same name exists: `θ` will be treated
| as a new local. Disambiguate by using `local θ` to suppress this
| warning or `global θ` to assign to the existing global variable.
└ @ string:5
ERROR: LoadError: UndefVarError: θ not defined
``````

Hopefully that’s clear enough that it would have helped you figure it out.

3 Likes

Well this is a fantastic error message!   1 Like