The main problem is that my variables would be determined only after running the codes (because the number of variables is not fixed).
In old version ModelingToolkit.jl, I used the following codes to generate a variable.
my_var = Variable(Symbol(name))(t) # name is a string
However, it can’t work in the latest version. Here is the error.
ERROR: Sym name is not callable. Use #syms name(var1, var2,...) to create it as a callable.
I've checked SymbolicUtils.jl but didn't find other usages. How can I fix this problem?
You can use the #variables macro to create symbolic variables at runtime as well. The $ operator interpolates the runtime value.
julia> using ModelingToolkit
julia> z = :abc;
julia> k = #variables $z
1-element Vector{Num}:
abc
julia> k[1]
abc
Related
In Fortran, we know that we can in a module define a global variable (use the private property), so that we can use the subroutines in the module to set or change the values of that variable. See below,
module Mod
integer, parameter :: r8=selected_real_kind(15,9)
real(kind=r8), private, save :: var
contains
subroutine f(x)
real(kind=r8) :: x
var = 2.0_r8*x
end subroutine f
end
As we can see, we can call f(x) and set the var in the module to be 2x.
Now in Julia, it seems like below,
module Mod
global var
function f(x::Float64)
global var = 2.0*x
return nothing
end
I mean, in the function, if var is on the left hand side, do I have to specify the key word 'global' every time?
I also tried to give the global var a type, like
global var::Float64
But it gives me an error
syntax: type declarations on global variables are not yet supported
It seems i can only do either just specify nothing, like just
global var
or give it a value while setting its type,
global var=0.0::Float64
Is there better to just give the global var a type before using it?
This is a way to make what you want and to make it efficient:
julia> const x = Ref(0)
Base.RefValue{Int64}(0)
julia> function f(y)
x[] = y
end
f (generic function with 1 method)
julia> x[]
0
julia> f(10)
10
julia> x[]
10
julia> f("10")
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
julia> f(5.0)
5.0
julia> x[]
5
Comments:
I make x a const which makes sure that the compiler will know its type (so that it can optimize code)
I make x a Ref to allow mutability of its contents and to make it clear later in the x[] = y statement that x[] part refers to a global variable (as variable x is not defined in a local scope of f)
As you can see in the example I could not use "10" in the call of f as it does not have a matching type. Note though that I could use 5.0 (a float) as it can be converted to an integer.
I am using Julia to do some basic integration estimation and am getting an UndefVarError with the following code:
using ExcelReaders
er = ExcelReaders
etInt = 0
waveLen = er.readxl("AM0AM1_5.xls", "Spectra!A3:A2004")
eT = er.readxl("AM0AM1_5.xls", "Spectra!B3:B2004")
gTilt = er.readxl("AM0AM1_5.xls", "Spectra!C3:C2004")
dirSol = er.readxl("AM0AM1_5.xls", "Spectra!D3:D2004")
function trArea(r::Real, l::Real, v::Array, x::Int)
return ((1/2) * (v[x] + v[x+1]) * (r-l))
end
for x in 1:length(waveLen)-1
etInt += trArea(waveLen[x], waveLen[x+1], eT, x)
end
The error points to line 16. To my understanding, this means that etInt is undefined within the scope of the for-loop. Why is this the case with Julia?
To my understanding, this means that etInt is undefined within the scope of the for-loop.
Global variables do exist in all local scopes (such as a for-loop). However, from Julia version 1.0 on they are read-only in those local scopes. Write access to global variables must be made explicit.
To give a simple example, while
julia> x = 1
1
julia> for i in 1:3
#show x # only reading a global variable
end
x = 1
x = 1
x = 1
works just fine,
julia> for i in 1:3
x += 1 # writing to a global variable
#show x
end
ERROR: UndefVarError: x not defined
Stacktrace:
[1] top-level scope at .\REPL[3]:2 [inlined]
[2] top-level scope at .\none:0
doesn't work. This can be healed by putting an explicit global annotation:
julia> for i in 1:3
global x += 1
#show x
end
x = 2
x = 3
x = 4
For more information, see the scoping section in the Julia documentation.
Note that people have complained about this a lot and it's actively being discussed on github and discourse (e.g. here and here).
Note that the effects of these scoping rules, like the potentially unintuitive error message that you're getting, really only hit you when you operate in a global scope (like the REPL). If you put everything in a function - any local scope - you get the expected behavior:
julia> function f()
x = 1
for i in 1:3
x += 1 # no global necessary
#show x
end
nothing
end
f (generic function with 1 method)
julia> f()
x = 2
x = 3
x = 4
This is anyways what you should do to really get fast runtime as global variables are almost always bad for performance (see the Performance Tips).
Also notice that everything will work as expected in Jupyter notebooks using IJulia. The reason is that people are coming up with context dependent solutions as well, in this case SoftGlobalScope.jl. Something similar is/was being considered for the REPL. These are the main places where people work interactively.
So, to summarize, you can either just learn the scoping rules (which is dead simple) or you wait for some of the discussed "fixes" to land as has already been the case for jupyter notebooks.
It seems that there was scope change implemented in 1.0 that has affected this behavior in Julia in the context of REPL and notebooks. Some other users have complained about it e.g.,
https://discourse.julialang.org/t/undefvarerror-on-loop-iterator-in-while-loop/14747
https://github.com/JuliaLang/julia/issues/28789
It seems a proposal has been made to rectify the oddity:
https://discourse.julialang.org/t/new-scope-solution/16707/
I seem to be unable to change a value to missing in Julia version 0.6.4 (I believe it was allowed before 0.6).
Example code:
using Dataframes
x = zeros(5)
5-element Array{Float64,1}:
0.0
0.0
0.0
0.0
0.0
x[3] = missing
ERROR: MethodError: Cannot `convert` an object of type Missings.Missing to an
object of type Float64
This may have arisen from a call to the constructor Float64(...),
since type constructors fall back to convert methods.
Stacktrace:
[1] setindex!(::Array{Float64,1}, ::Missings.Missing, ::Int64) at ./array.jl:583
In this setting I am trying to encode certain indicies as missing values for an analysis. Is there a simple workaround?
missing in Julia is of its own type:
julia> typeof(missing)
Missings.Missing
In your case, it is particularly important to note that:
julia> Missing <: Float64
false
That is, Missing is not a subtype of Float64. Now, note that:
julia> typeof(zeros(5))
Array{Float64,1}
So you construct x, an array that should only contain Float64. Since missing is not a subtype of Float64, when you try to change one of the elements of x to missing, you get an error, in the same way you would get an error if you tried x[3] = "a string".
If you want an array to contain both the type Missing and the type Float64, then you need to specify up front that the elements of the array can be of type Missing or type Float64. In Julia v0.6 (which you specify in the question), you can do this via missings, which is located in the Missings.jl package, e.g.:
julia> x = missings(Float64, 2)
2-element Array{Union{Float64, Missings.Missing},1}:
missing
missing
julia> x[1] = 0.0
0.0
julia> x
2-element Array{Union{Float64, Missings.Missing},1}:
0.0
missing
In v1.0, the core functionality related to missing was moved into Base, so instead you would need:
julia> Array{Union{Float64,Missing}}(missing, 2)
2-element Array{Union{Missing, Float64},1}:
missing
missing
which is admittedly a little cumbersome. However, the missings syntax from v0.6 is still available for v1.0 in Missings.jl. It's just that many people may choose not to bother with this since the type Missing itself has moved to Base, so you don't need Missings.jl, unlike v0.6.
If you already have a pre-existing Array{Float64} and want to mark some of the elements as missing, then (as far as I know) you will need to re-construct the array. For example, in both v0.6 and v1.0 you could use:
julia> x = randn(2)
2-element Array{Float64,1}:
-0.642867
-1.17995
julia> y = convert(Vector{Union{Missing,Float64}}, x)
2-element Array{Union{Float64, Missings.Missing},1}:
-0.642867
-1.17995
julia> y[2] = missing
missing
Note that missing is typically envisaged to be used in datatypes like DataFrames, where a lot of this stuff happens automatically for you, and so you don't have to waste time typing out so many Unions. This might be one reason why the syntax is a little verbose when working with regular arrays like you are.
One final point: you could of course explicitly construct your arrays to accept any type, e.g. x = Any[1.0, 2.0] ; x[1] = missing. The downside is that now the compiler cannot generate type-efficient code for working with x and so you will lose the speed benefits of working in Julia.
I'm wondering whether it's possible to define a macro that can modify the values of an expression only if the values are of a specific type?
Here's a minimal example:
type Special
x::Int
end
f1(s, n::Special) = println("f1", s, n)
f2(s, n::Special) = println("f2", s, n)
x1 = Special(3)
x2 = Special(5)
expr = :(
f1("this is f1", x1),
f2("this is f2", x2)
)
Now a macro might be able to examine the values of the arguments to the functions, determine that x1 and x2 are of type Special, run some function to modify their values, say by changing 3 to 4 and 5 to 2 (it might involve comparing two values), then pass the expression back to the caller. The final result would be equivalent to calling:
f1("this is f1", 4)
f2("this is f2", 2)
I found that it's possible to access the values in a macro via:
eval(eval(filter(x -> typeof(eval(x)) == Special, expr.args[1].args))[1]).x
=> 3
but although this works it looks wrong, and I'm might either be doing it wrong or trying to do something too way out...
No, you should never try to check types or values inside macros. Using eval to figure out the type or value of something in a macro may work in very limited situations, but it'll break in almost every real use. Instead, just have the macro insert a call to a generic function — that's where Julia excels at picking apart types (as method dispatch) and values (within the method):
munge_special(x::Special) = Special(x.x + 42)
munge_special(x) = x
macro do_something_special(x)
return :(munge_special($(esc(x))))
end
julia> #do_something_special Special(2)
Special(44)
julia> #do_something_special 3
3
According to http://julia.readthedocs.org/en/latest/manual/integers-and-floating-point-numbers/, one should be able to do this:
julia> Float32(-1.5)
-1.5f0
Instead, I get:
julia> Float32(-1.5)
ERROR: type cannot be constructed
This happens for all other attempts to use this syntax, e.g. x = Int16(1)
I'm on 0.3.10.
You are on 0.3.10 but reading the manual for 0.5. In the manual for 0.3 http://julia.readthedocs.org/en/release-0.3/manual/integers-and-floating-point-numbers/
Values can be converted to Float32 easily:
julia> float32(-1.5)
-1.5f0
julia> typeof(ans)
Float32