Julia - UnderVarError - julia

The following code throws an "UndefVarError: g not defined"
function asdf()
if true
f(t) = t
else
g(t) = t
f(t) = g(t)
end
return f
end
w = asdf()
w(1)
but by replacing f(t) = g(t) by f = g, it works. Why?

This is a known bug https://github.com/JuliaLang/julia/issues/15602.
The short recommendation is not to define a function that goes to method table twice in the body of a function. Instead use a variable to which you assign two different functions (with different names or anonymous) in branches.
What you should do until this is fixed is:
function asdf()
if true
f = t -> t
else false
g(t) = t
f = g(t)
end
return f
end

Related

Multiple outputs used repeatedly in for loop Julia

I am using Julia and I've designed a for loop that takes the outputs of a function in one loop and uses them as the input of that function in the next loop (and over and over). When I run this code, Julia flags an "undefined" error, however, if I run the code in debug mode, it executes perfectly. For example, the code looks like this:
function do_command(a,b,c,d)
a = a + 1
b = split(b, keepempty=false)[1]
c = split(b, keepempty=false)[1]
if a == 1000
d = true
else
d = false
end
return a, b, c, d
end
for ii in 1:length(x)
if ii == 1
a = 0
b = "string something"
c = ""
d = false
end
a,b,c,d = do_command(a,b,c,d)
if d == true
print(string(b))
break
end
end
What am I doing wrong here?
An issue with your code is that for introduces a new scope for each iteration of the loop. That is to say: variable a created within the loop body at iteration 1 is not the same as variable a created within the loop body at iteration 2.
In order to fix your problem, you should declare variables outside the loop, so that at each iteration, references to them from within the loop body would actually refer to the same variables from the enclosing scope.
I'd go with something like this:
function do_command(a,b,c,d)
a = a + 1
b = split(b, keepempty=false)[1]
c = split(b, keepempty=false)[1]
if a == 1000
d = true
else
d = false
end
return a, b, c, d
end
# Let's create a local scope: it's good practice to avoid global variables
let
# All these variables are declared in the scope introduced by `let`
a = 0
b = "string something"
c = ""
d = false
for ii in 1:10 #length(x)
# now these names refer to the variables declared in the enclosing scope
a,b,c,d = do_command(a,b,c,d)
if d == true
print(string(b))
break
end
end
end

Zygote.Hessian: Mutating arrays is not supported

I'm trying to calculate the laplacian of neural network. This is my code:
using Flux
using Zygote
model = Chain(Dense(2,5,sigmoid), Dense(5,1))
function laplace(x)
a, b = size(x)
Δ = Zygote.Buffer(zeros(b))
deriv2 = sum(Diagonal(ones(a*b)).*Zygote.hessian(v -> sum(model(v)), x), dims=1)
for i=1:b
for j=1:a
Δ[i] += deriv2[(i-1)*a+j]
end
end
return copy(Δ)
end
gradient(x -> sum(laplace(x)), rand(2,5))
I'll see the same error even if I define a function like this:
function function(x)
return sum(Zygote.hessian(v -> sum(model(v)), x))
end
gradient(x -> function(x), rand(2,5))
Why do I get this error?

generating expressions and then checking them in Julia

My goal is to be able to generate a list of expressions, p.g., check that a number is in some interval, and then evaluate it.
I was able to do it in the following way.
First, a function genExpr that creates such an Expr:
function genExpr(a::Real, b::Real)::Expr
quote
x < $(a + b) && x > $(a - b)
end
end
Create two expressions:
e1 = genExpr(0,3)
e2 = genExpr(8,2)
Now, my problem is how to pass these expressions to a function along with a number x. Then, this function, checks if such a number satisfies both conditions. I was able to achieve it with the following function:
function applyTest(y::Real, vars::Expr...)::Bool
global x = y
for var in vars
if eval(var)
return true
end
end
return false
end
This works, but the appearance of global suggests the existence of a better way of obtaining the same goal. And that's my question: create a function with arguments a number and a list of Expr's. Such function returns true if any condition is satisfied and false otherwise.
This looks like a you are probably looking into using a macro:
macro genExpr(a::Real, b::Real)
quote
x-> x < $(a + b) && x > $(a - b)
end
end
function applyTest(y::Real, vars::Function...)::Bool
any(var(y) for var in vars)
end
Testing:
julia> e1 = #genExpr(0,3)
#15 (generic function with 1 method)
julia> e2 = #genExpr(8,2)
#17 (generic function with 1 method)
julia> applyTest(0,e1,e2)
true
However, with this simple code a function just generating a lambda would be as good:
function genExpr2(a::Real, b::Real)
return x-> x < (a + b) && x > (a - b)
end

Making a conditional inner function assignment

I want to make a conditional funtion assignment inside a function (by calling function bar1 or bar2) based on its arguments. The thing is, it must assign bar1 or bar2 to a one argument function.
This is an example of what I am doing:
function foo(a, b=false)
if b
f(t) = t*a; #bar1(t,a)
else
f(t) = t/a; #bar2(t,a)
end
return f(2.0);
end
Calling
foo(1.1, true)
Outputs
1.8181818181818181
Calling
foo(1.1, false)
Outputs
ERROR: UndefVarError: f not defined
Stacktrace:
[1] foo(::Float64, ::Bool) at C:\Users\pedro\Documents\codigos\Julia_HEM\compara_hem_neumann.jl:89
[2] top-level scope at none:0
Why is f not defined when b=false?
Edit
A workaround is to define, for each condition, a different function. For example
function foo(a, b=false)
if b
f(t) = t*a; #bar1(t,a)
return f(2.0);
else
g(t) = t/a; #bar2(t,a)
return g(2.0);
end
end
The question remains, though...
This is an open issue in Julia language (see https://github.com/JuliaLang/julia/issues/15602). It is recommended to use an anonymous function in such cases.
function foo(a, b=false)
if b
f = t -> t*a
else
f = t -> t/a
end
return f(2.0)
end
Note though that this will not be very efficient as the compiler is not able to infer the return type of foo (so probably you would have to add a declaration of function return type). Your answer in EDIT is type stable.

Minimising log function in cvxpy

I am trying to simulate an exact line search experiment using CVXPY.
objective = cvx.Minimize(func(x+s*grad(x)))
s = cvx.Variable()
constraints = [ s >= 0]
prob = cvx.Problem(objective, constraints)
obj = cvx.Minimize(prob)
(cvxbook byod pg472)
the above equation is my input objective function.
def func(x):
np.random.seed(1235813)
A = np.asmatrix(np.random.randint(-1,1, size=(n, m)))
b = np.asmatrix(np.random.randint(50,100,size=(m,1)))
c = np.asmatrix(np.random.randint(1,50,size=(n,1)))
fx = c.transpose()*x - sum(np.log((b - A.transpose()* x)))
return fx
Gradient Function
def grad(x):
np.random.seed(1235813)
A = np.asmatrix(np.random.randint(-1,1, size=(n, m)))
b = np.asmatrix(np.random.randint(50,100,size=(m,1)))
c = np.asmatrix(np.random.randint(1,50,size=(n,1)))
gradient = A * (1.0/(b - A.transpose()*x)) + c
return gradient
Using this to find the t "Step Size" by minimising the objective function results in an error 'AddExpression' object has no attribute 'log'.
I am new to CVXPY and Optimization. I would be grateful if someone could guide on how to fix the errors.
Thanks
You need to use CVXPY functions, not NumPy functions. Something like this should work:
def func(x):
np.random.seed(1235813)
A = np.asmatrix(np.random.randint(-1,1, size=(n, m)))
b = np.asmatrix(np.random.randint(50,100,size=(m,1)))
c = np.asmatrix(np.random.randint(1,50,size=(n,1)))
fx = c.transpose()*x - cvxpy.sum_entries(cvxpy.log((b - A.transpose()* x)))
return fx

Resources