UndefVarError in for loop

Below is my MWE. I do not know why I am having the below error though I am defining it at the beginning?

using LinearAlgebra;
using BenchmarkTools
function curMatRLC(NodeVoltage, dt)
  if NodeVoltage[1,1]==11
    return s = ones(3*5) - dt;
  else
    return s = ones(3*5) + dt;
  end
end
NodeVoltage  = zeros(3,5);
A_Mat = ones(3*5,3*5); 
I_Mat = ones(3*5);
@btime begin
  global iter = 0; 
  for t in 0:1e-6:0.03
    global iter += 1;
    I_Mat[1:3*5] = curMatRLC(NodeVoltage,dt);
    V_Mat = lu(A_Mat) \ I_Mat;
    NodeVoltage  = reshape(V_Mat[1:3*5], 3, :);
  end
end
ERROR: LoadError: UndefVarError: NodeVoltage not defined
Stacktrace:
  [1] macro expansion

If you are ever benchmarking, then you should never have the global identifier anywhere in sight. Put everything in a function (taking A_mat and I_mat as inputs, and tell call it as @btime my_function($A_mat, $I_mat)

2 Likes

Global variables are not in local scope without the global keyword. See Scope of Variables · The Julia Language. Adding the line global NodeVoltage to your innermost for loop should remove the error. However, it is better to write a function for that part of the code and pass NodeVoltage to that function. Then you won’t have to worry about scope and your code will be faster.

2 Likes

Got it, thank you!

Thank for your reply. But why the error is only for NodeVoltage not for A_mat and I_mat, though they are all supposed to be global variables being used in local scope of for loop?

Sorry, I didn’t actually test. You will need to declare those as globals too. I was just writing based on the current error.

1 Like

But again, do @btime myfunction!(NodeVoltage, A_Mat, and I_Mat) instead of adding the global statements.

1 Like

@btime myfunction!($NodeVoltage,$A_Mat,$I_Mat) or it will end up global.

2 Likes

Reading from global variables, is always allowed, but re-assigning them is only allowed (if you explicitly declared them as global. (Note that I_Mat[1:3*5] = ... is not re-assigning I_Mat itself, but rather is mutating the contents of I_Mat.)

2 Likes

@stevengj
Thanks for your valuable reply. It seems that I have a lack in differentiating between re-assigning and mutating. Is it possible to explain this to me or direct me to its documentation?

@jlperla Thanks for your reply. What does the $ really do be before the variables?

It is just because you are benchmarking. You do not need the $ otherwise.

From BenchmarkTools:

If the expression you want to benchmark depends on external variables, you should use $ to “interpolate” them into the benchmark expression to avoid the problems of benchmarking with globals. Essentially, any interpolated variable $x or expression $(...) is “pre-computed” before benchmarking begins:

Your I_Mat is a Vector which is a type of mutable container meaning you can change what is held inside of I_Mat. When you write I_Mat[1:3*5] = new_value the values stored at those locations are changed in place (mutated). Whereas, if you write I_Mat = new_value, a new location in memory is allocated and assigned the name I_Mat (re-assignment). The difference is likely not important for you at this stage as long as you avoid global variables.

1 Like

@Nathan_Boyer
1- So is it always good to use indexes for the elements of vector (or matrix) as in I_Mat[1:3*5] = new_value, to avoid re-assignment?
2- After this new assignment of I_Mat = new_value , what will happen to the old location of I_Mat in memory? will be still occupied (but useless) of freed?

It depends on whether you have other variables bound to that array. If there are no other references to the array, it will be garbage collected (eventually) .

@Jeff_Emanuel The other variables that are bound to that array I_Mat are actually bound to the name not location, correct? So, by using the re-assignment, these variable will be bound to the name of the re-assignment, right?
If not right, could you please give me an example?

a=ones(5)
b=a
a=zeros(3)

After this code runs, b is bound to the original result of ones. It gets bound to the same value that a is bound to. It is not an alias for a. a then gets bound to the new array created by zeros.

Thank you, I got it.

I updated your MWE to illustrate the general form of what I was describing above. I chose not to mutate for clarity. You may want to mutate if speed is more important.

using LinearAlgebra, BenchmarkTools

function curMatRLC(NodeVoltage, dt)
  if NodeVoltage[1,1]==11
    return s = ones(3*5) .- dt;
  else
    return s = ones(3*5) .+ dt;
  end
end

function march!(NodeVoltage, A_Mat)
  dt = 1e-6
  for t in 0:dt:0.03 
    local I_Mat = curMatRLC(NodeVoltage, dt)
    local V_Mat = lu(A_Mat) \ I_Mat
    NodeVoltage = reshape(V_Mat, 3, :)
  end
  return NodeVoltage, A_Mat, I_Mat, V_Mat
end

NodeVoltage  = zeros(3,5)
A_Mat = rand(3*5, 3*5)
(NodeVoltage, A_Mat, I_Mat, V_Mat) = march!(NodeVoltage, A_Mat)
@btime march!($NodeVoltage, $A_Mat)

Below are some issues I encountered getting your code to run with some explanations that you may find helpful:

  1. dt wasn’t previously defined. I defined it as a scalar, so I could use it in the definition of t, but then I had to add a broadcasting . to the addition inside curMatRLC.
  2. The result of curMatRLC doesn’t change with time right now. I assume you will need to pass another variable to it to represent whatever ones(3*5) is supposed to be, so that s changes.
  3. The V_Mat calculation gave me an error because everything was 1’s, so I had to change A_Mat to be random numbers.
  4. Scope became an issue again when returning values from my function since I_Mat and V_Mat are only defined inside the for loop. I added the local keyword to fix this. If you care about the values before the end time though, you probably want to define a vector to store your results at every timestep outside of the loop and then update the elements of that vector in the loop instead of using local.
1 Like

@Nathan_Boyer Thank you very much for your time and explanations.
Why you put ! in march!?