Reputation: 880
I'm attempting to write a Newton-Raphson solver in Julia. The Newton-Raphson method is shown in this image.
f(x) = x^2.5 - 3x^1.5 - 10
fprime(x) = 2.5x^1.5 - 4.5x^0.5
x = zeros(1000)
x[1] = 10
δ = 1 # a relatively large number compared to what we want the error to be
iter = 1
while δ > 1e-6
x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
iter += 1
δ = abs(x[iter] - x[iter + 1])
if iter == 100
break
end
end
println("The solution is ")
show(x[iter])
However, when I run the code, I get an error saying iter
is not defined, even though I defined it just before the start of the loop. Is there some scoping problem I'm completely missing?
ERROR: LoadError: UndefVarError: iter not defined
Stacktrace:
[1] top-level scope at /Users/natemcintosh/Documents/Julia/Learning_julia.jl:11 [inlined]
[2] top-level scope at ./none:0
[3] include_string(::Module, ::String, ::String) at ./loading.jl:1002
[4] (::getfield(Atom, Symbol("##120#125")){String,String,Module})() at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:120
[5] withpath(::getfield(Atom, Symbol("##120#125")){String,String,Module}, ::String) at /Users/natemcintosh/.julia/packages/CodeTools/8CjYJ/src/utils.jl:30
[6] withpath at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:46 [inlined]
[7] #119 at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:117 [inlined]
[8] hideprompt(::getfield(Atom, Symbol("##119#124")){String,String,Module}) at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/repl.jl:76
[9] macro expansion at /Users/natemcintosh/.julia/packages/Atom/Pab0Z/src/eval.jl:116 [inlined]
[10] (::getfield(Atom, Symbol("##118#123")){Dict{String,Any}})() at ./task.jl:85
in expression starting at /Users/natemcintosh/Documents/Julia/Learning_julia.jl:10
I've tried printing x
at the beginning of the while
loop and it knows what x
is, but thinks iter
is undefined.
Upvotes: 2
Views: 2622
Reputation: 69949
First let me give the solution:
There are three possible approaches
Approach 1. Prepend global
before iter += 1
and change it to global iter += 1
and all will work (note however the comment below about δ
- because it will not work correctly unless you also prepend global
before δ = abs(x[iter] - x[iter + 1])
, i.e. the code will run but will produce wrong results - approaches 2 and 3 do not have this problem).
Approach 2. Wrap your code inside a function like this:
f(x) = x^2.5 - 3x^1.5 - 10
fprime(x) = 2.5x^1.5 - 4.5x^0.5
function sol(f, fprime)
x = zeros(1000)
x[1] = 10
δ = 1 # a relatively large number compared to what we want the error to be
iter = 1
while δ > 1e-6
x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
iter += 1
δ = abs(x[iter] - x[iter + 1])
if iter == 100
break
end
end
println("The solution is ")
show(x[iter])
end
sol(f, fprime) # now we call it
Solution 3. Wrap your code in a let
block by changing line function sol(f, fprime)
in solution 2 to simply say let
(you do not need to call sol
then).
Now the reason why you have to do it.
In Julia 1.0 while
introduces a new scope. The scoping rules in Julia 1.0 are that each variable that is assigned to inside a while
loop is considered a local variable (this has changed, because Julia 0.6 distinguished hard and soft local scope, in Julia 1.0 this distinction is gone - all local scopes are the same).
In your code you assign values to two variables: iter
and δ
inside the loop. This means that they are treated by Julia as local so you are not allowed to access their value before they have a value assigned inside the loop.
You want to read iter
in line x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
but assign a value to it only in the following line.
As for δ
the thing is more tricky. You assign a value to it, but it is used in a loop condition while δ > 1e-6
. However, this condition operates on variables defined in outer scope (global in the original case). So all will work, but the condition while δ > 1e-6
will always see that δ
is equal to 1
as it looks at the value of the variable outside of the loop. So this condition will never trigger (and you will always run 100 iterations). In summary the code that does what you want is (although if you did not fix δ
assignment you would not get a warning):
f(x) = x^2.5 - 3x^1.5 - 10
fprime(x) = 2.5x^1.5 - 4.5x^0.5
x = zeros(1000)
x[1] = 10
δ = 1 # a relatively large number compared to what we want the error to be
iter = 1
while δ > 1e-6
x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
global iter += 1
global δ = abs(x[iter] - x[iter + 1])
if iter == 100
break
end
end
println("The solution is ")
show(x[iter])
Finally notice that the line x[iter + 1] = x[iter] - f(x[iter])/fprime(x[iter])
works fine even if there is an assignment in it, because you do not rebind variable x
in it, but only change one element of an array (so x
points to the same address in memory and Julia treats it as a global variable all the time).
Also you might want to read this https://docs.julialang.org/en/latest/manual/variables-and-scoping/ in the Julia manual or the answer to this question Julia Variable scope is similar
Upvotes: 3