Scope of variables in Pluto

Hi everybody!

I am new to Julia, POMDPs, and Pluto… and I am using all three to try and simulate a POMDP problem. I know that I am probably making some mistakes in my POMDP, but my immediate error is:

UndefVarError: i not defined

I am curious as to why I receive this error when ‘i’ is defined within the same Pluto cell. :thinking:

I also receive warnings (image posted below) about the other POMDP variables s, r_total, d, b as well.

Nowhere in my code are any of these variables defined globally.

I appreciate any insight that can help me to solve this challenge. :smiling_face_with_three_hearts:

So far I have tried different browsers, including using incognito mode, and still receive this error.

If I comment out ‘i’, and all parts of the code that rely on ‘i’, then the next error I receive is "UndefVarError: ‘b’ not defined :man_facepalming: so I think that this points to scope of these variables defined within the begin / end block of the cell, and the scope within the while loop.

begin
	up = DiscreteUpdater(pomdp);
	b0 = uniform_belief(pomdp);
	s = rand(initialstate(pomdp));
	
	b = initialize_belief(up, b0);
	r_total = 0.0;
	d = 1.0;
	i = 0  # index of current bin
	while (!isterminal(pomdp, s)) && (i < 60)
	    determine_observations(i,1)
		a = action(policy, b)
	    s, o, r = @gen(:sp,:o,:r)(pomdp, s, a)
	    r_total += d*r
	    d *= discount(pomdp)
	    b = update(up, b, a, o)
		i += 1
	end
end

There are several issues here, but basically there is confusion over if you are trying to use globals or not. In general, in Julia, you shouldn probably minimize the use of globals unless you really need them.

One solution is to use a let block to create a local scope around the loop.

i = let i = 0, d = 1.0, s = rand(initialstate(pomdp)) # etc
    while (!isterminal(pomdp, s)) && (i < 60)
    end
    i # or whatever variables you want to return  
end

Thank you @mkitti for your response. I had no intention of using any global variables. Edit: In fact, I had not declared any of these variables outside of this cell in Pluto. The only things that exist outside of this cell is my function determine_observations(), pomdp, and policy.

I was able to find let blocks or keyword let in julia documentation.

Can you explain to me what I had done wrong, or what I need to be aware of to avoid the need for let blocks?

There is a way to avoid let here, but I would recommend against it.

begin doesn’t create a new scope, so the variables defined in cell with a begin block are considered global. However, you can use local to restrict the scope of a single variable, so the following are equivalent:

julia> begin
           local x = 1
           x
       end
1

julia> isdefined(@__MODULE__, :x)
false

julia> let x = 2
           x
       end
2

julia> isdefined(@__MODULE__, :x)
false

My suggestion is: Don’t do this. As you probably noticed, Julia scope rules are awfully complicated, so the best thing to do here is to write small functions, or when prototyping, using let.

1 Like

I’m not getting any errors like yours.

so I’m not sure I believe this:

Maybe you have another begin-end block somewhere which is also using a global i.

Try running a minimal, self-contained problem first and then expand it to the full problem, so it is easier to diagnose your issue.

1 Like

I don’t use Pluto, but here the issue seems to be about what’s the definition of a global variable. This would require a really long answer, but just to be brief, take global variables as anything that is outside a function or a let block.

The word begin does nothing actually in your case, so all variables you define there are global variables. I think that you wanted to use let instead of begin, which is what you could have in mind when you were using begin.

More generally, the use of global variables is discouraged in all programming languages, but in Julia is way more problematic: it basically completely kills performance. Also, I’d recommend getting used to organizing your code in terms of functions, as it’s the most performant way to use Julia (not only in terms of syntax, but literally in terms of speed).

Yes, thanks @Nathan_Boyer, I totally agree with your approach to troubleshooting :100:. I do have another function within the same Pluto notebook that also uses a while loop. There are also variables initialized outside of the while loop (within the same cell) that are used within the while loop for an index and other purposes. No problems running that code. The difference is that everything is initialized within the function… so maybe I will put all of this code inside a function too… rather than begin/end block. There is something specific to this block that is deceiving me… probably something simple because I am a n00b. :sweat_smile:

I have tried other things like changing the variable names… like ‘i’ to ‘ii’ (and so forth) but it has not made a difference. The code looks straightforward and benign, so I am really puzzled :person_shrugging:

Thanks @alfaromartino for your reply. I have used ‘i’ in other local contexts, but have not declared it globally. I stay away from globals … but I do use mutable structs :flushed: … which if I understand correctly, are global by nature (once instantiated). I hope that mutable structs are OK from a performance and code cleanliness perspective.

Thank you @savq

I put the POMDP simulation code from this cell inside a function rather than using a begin/end block and the code was able to run without issue. :tada:

Thank you to everyone who has responded :smiling_face_with_three_hearts: to help me troubleshoot this.

I am still curious to learn why I had this challenge? Even when I broke the code into separate cells (8 cells total) to avoid using a begin/end block I still ran into the issue. I’m sure that I had to make some n00b mistake somewhere… but to receive the same error for both ‘i’ and ‘b’ (after commentintg out all 'i’s) … seems

function testPOMDP()
	
	up = DiscreteUpdater(pomdp); 
	b0 = uniform_belief(pomdp);
	s = rand(initialstate(pomdp));
	
	b = initialize_belief(up, b0);
	r_total = 0.0;
	d = 1.0;
	i = 0  # index of current bin
	while (!isterminal(pomdp, s)) && (i < 60)
	    determine_observations(i,1)
		a = action(policy, b)
	    s, o, r = @gen(:sp,:o,:r)(pomdp, s, a)
	    r_total += d*r
	    d *= discount(pomdp)
	    b = update(up, b, a, o)
		println("got to ",i, " with r_total ",r_total)
		i += 1
	end
end

See the documentation here. If you scroll down, it explains when your message appears.

julia> code = """
       s = 0 # global
       for i = 1:10
           t = s + i # new local `t`
           s = t # new local `s` with warning
       end
       s, # global
       @isdefined(t) # global
       """;

julia> include_string(Main, code)
┌ Warning: Assignment to `s` in soft scope is ambiguous because a global variable by the same name exists: `s` will be treated as a new local. Disambiguate by using `local s` to suppress this warning or `global s` to assign to the existing global variable.
└ @ string:4
ERROR: LoadError: UndefVarError: `s` not defined

The rules of scope change depending on how you run your code, which includes Pluto (I don’t use Pluto, but I’m guessing from your message that it follows the same rules as if you work non-interactively, which means executing the code through a file).

If you’re new to Julia, I’d recommend avoiding Pluto for now. Instead, use a code editor (e.g., VS Code) and run your script there (this is referred to as running your code interactively). When you work with for-loops and while loops inside a function, the rules are always the same, and that’s why you don’t get the error. Otherwise, you need to analyze some ambiguous cases. That’s another reason why the use of functions is preferred in Julia.

In any case, notice that you’re still using global variables in the function. Ask yourself “is there any variable I’m mentioning in the function’s body that isn’t part of the function’s argument or defined locally?”. If your answer is yes, then you’re using global variables. Examples in your code pomdp and policy (unless they’re functions, don’t what their definitions are, but I guess they’re variables defined outside the function).

1 Like

Thank you again @alfaromartino. I thought this section of the docs link was particularly interesting: :books:

In my code there are only two instance where I define “i” by assignment, and both are in local context.

Once within my testPOMDP() function (formerly begin/end block) and the other within a plotting function used to iterate a matrix. Both instances are within functions, so locally scoped. Other instances are when ‘i’ is used in for loops to iterate.

I think we can agree that somewhere these variables (‘i’ and ‘b’) are being viewed as global, but I am completely sure that I have not specifically declared them as such.

I agree that coding small functions was the right thing to do, and ultimately, when I put my POMDP simulation in function, was the easiest way to proceed. :smiley:

Thanks again!