new() for Julia Arrays - julia

I struggle with writing a proper constructor for my struct PERK. What gives my trouble is the usage of new() with my member arrays. The code looks like this:
abstract type PERKTYPE end
struct PERK <: PERKTYPE
NumStages::Int
ACoeffs::Array{BigFloat}
c::Array{BigFloat}
function PERK()
ACoeffsFile = open("some/existing/file.txt", "r")
NumStages = countlines(ACoeffsFile)
close(ACoeffsFile)
#println(NumStages)
ACoeffs = Array{BigFloat}(undef, NumStages, 2)
# Fille ACoeffs with data from file, omitted here
c = Array{BigFloat}(undef, NumStages)
for i in 1:NumStages
c[i] = (i-1)/(2*(NumStages-1))
end
new(NumStages) # Fine
new(ACoeffs, c) # Not working if un-commented
end # PERK()
end # struct PERK
I get the error
ERROR: LoadError: MethodError: Cannot `convert` an object of type Matrix{BigFloat} to an object of type Int64
Closest candidates are:
convert(::Type{T}, ::LLVM.GenericValue) where T<:Signed at ~/.julia/packages/LLVM/gE6U9/src/execution.jl:27
convert(::Type{T}, ::LLVM.ConstantInt) where T<:Signed at ~/.julia/packages/LLVM/gE6U9/src/core/value/constant.jl:89
convert(::Type{T}, ::Ptr) where T<:Integer at ~/Software/julia-1.7.2/share/julia/base/pointer.jl:23
...
Stacktrace:
[1] PERK()
# Trixi ~/.../methods_PERK.jl:26
What is going on here? Of course, I am not interested in any conversion of my arrays at all.

I can be wrong here, but new() creates an PERK struct. Meaning a struct with three arguments. If you call new() with just a NumStages it is fine, because it is the first argument of the struct. However
new(ACoeffs, c)
tries to feed an array ACoeffs into NumStages which yields the error you see.
You can overcome this using
new(NumStages, ACoeffs, c)
Hope this helps a little.

Related

New dispatch for isless function does not work

I need to be able to compare instances of a struct named Hijri, so I have defined a new method for the isless() function as follows:
function isless(a::Hijri, b::Hijri)
tuple_a = datetuple(a)
tuple_b = datetuple(b)
return tuple_a < tuple_b
end
datetuple() returns values of the Hijri struct as a tuple.
This works fine:
#show isless(a, b)
However, this one
#show a < b
throws an error:
ERROR: LoadError: MethodError: no method matching isless(::Main.HijriConverter.Hijri, ::Main.HijriConverter.Hijri)
Closest candidates are:
isless(::Any, ::Missing) at ~/Downloads/julia-1.7.0/share/julia/base/missing.jl:88
isless(::Missing, ::Any) at ~/Downloads/julia-1.7.0/share/julia/base/missing.jl:87
Stacktrace:
[1] <(x::Main.HijriConverter.Hijri, y::Main.HijriConverter.Hijri)
# Base ./operators.jl:352
[2] top-level scope
# show.jl:1047
in expression starting at /home/jafar_isbarov/Documents/projects/hijri/HijriConverter.jl/src/HijriConverter.jl:1
What could be the reason?
I had to import isless from Base:
import Base: isless
or use Base.isless (instead of isless) in my function definition.
function Base.isless(...)
end
This question was originally answered on Julia Discourse.

How to lock the variable type in Julia?

I want to lock the type of a variable in Julia, how to do? For example, I define an array called weight,
weight = Array{Float64,1}([1,2,3])
Now I want to lock the type of weight as Array{Float64,1}, is it possible?
I mean if do not lock the type of weight, then if I mistakenly or casually do
weight = 1
Then weight will become an Int64 variable, so it is not longer a 1D array. This is obviously not what I want.
I just want to make sure that once I defined weight as 1D Float64 array, then if I change the type of weight, I want Julia gives me an error saying that the type of weight has been changed which is not allowed. Is it possible? Thanks!
This is useful because by doing this, it may preventing me from forgetting weight is an 1D array, and therefore preventing bugs.
For global variables use const:
julia> const weight = Array{Float64,1}([1,2,3])
3-element Vector{Float64}:
1.0
2.0
3.0
julia> weight[1]=11
11
julia> weight=99
ERROR: invalid redefinition of constant weight
Note that redefining the reference will throw a warning:
julia> const u = 5
5
julia> u=11
WARNING: redefinition of constant u. This may fail, cause incorrect answers, or produce other errors
You can circumvent it by using the Ref type:
julia> const z = Ref{Int}(5)
Base.RefValue{Int64}(5)
julia> z[] = 11
11
julia> z[] = "hello"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
In functions use local with type declaration:
julia> function f()
local a::Int
a="hello"
end;
julia> f()
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
You'd normally write:
weight::Vector{Float64} = Array{Float64,1}([1,2,3])
...but this doesn't seem to be possible in global scope:
julia> weight::Vector{Float64} = Array{Float64,1}([1,2,3])
ERROR: syntax: type declarations on global variables are not yet supported
Stacktrace:
[1] top-level scope
# REPL[8]:1
However, you can do it in local scope or in a struct:
julia> function fun()
weight::Vector{Float64} = Array{Float64,1}([1,2,3])
weight = 1
end
fun (generic function with 1 method)
julia> fun()
ERROR: MethodError: Cannot `convert` an object of type Int64 to an object of type Vector{Float64}
Closest candidates are:
convert(::Type{T}, ::AbstractArray) where T<:Array at array.jl:532
convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.6/LinearAlgebra/src/factorization.jl:58
convert(::Type{T}, ::T) where T<:AbstractArray at abstractarray.jl:14
...
Stacktrace:
[1] fun()
# Main ./REPL[10]:3
[2] top-level scope
# REPL[11]:1
You could use const, but then redefinition with a value of the same type will cause a warning:
julia> const weight = Array{Float64,1}([1,2,3]);
julia> weight = [2.]
WARNING: redefinition of constant weight. This may fail, cause incorrect answers, or produce other errors.
1-element Vector{Float64}:
2.0

Array of structs with generic function members?

I'm trying construct an array of structs where each instance contains a different function. I want to add these to an array in a loop.
Here's an example:
struct mystruc{F}
σ::F
end
a = [mystruc(relu)]
for i in 1:3
append!(a, [mystruc(identity), ])
end
As a side note, I have the option to preallocate the array I just couldn't figure out how to do with this type of struct.
Each function has a type, which is exclusive to that function:
julia> typeof(x -> x) == typeof(x -> x)
false
Here we created the function x -> x twice, they are two different functions, so their types are not the same.
In your construction of a, you create an Array of that specific type:
julia> a = [mystruc(relu)]
1-element Array{mystruc{typeof(relu)},1}:
mystruc{typeof(relu)}(relu)
julia> typeof(a)
Array{mystruc{typeof(relu)},1}
So when you push another function, we get an error, because this array can only contain objects of the type mystruc{typeof(relu)}.
julia> push!(a, mystruc(x -> 2x))
ERROR: MethodError: Cannot `convert` an object of type
mystruc{var"#3#4"} to an object of type
mystruc{typeof(relu)}
Closest candidates are:
convert(::Type{T}, ::T) where T at essentials.jl:171
mystruc{typeof(relu)}(::Any) where F at REPL[2]:2
Solution
When you construct a, tell Julia that the array will contain mystruc with any function:
julia> a = mystruc{<:Function}[mystruc(relu)]
and now it works!
julia> push!(a, mystruc(x -> 2x))
2-element Array{mystruc{#s1} where #s1<:Function,1}:
mystruc{typeof(relu)}(relu)
mystruc{var"#5#6"}(var"#5#6"())

Structs of Arrays: Type Error

I do not get the difference between the following two struct definitions. The first works without any problems, while the second gives a Type Error:
julia> struct point1
xy::Vector{Float64}
end
julia> struct point2
xy::Array{Float64,1}(2)
end
ERROR: TypeError: point2: in type definition, expected Type, got Array{Float64,1}
Stacktrace:
[1] eval(::Module, ::Any) at ./boot.jl:235
[2] eval(::Any) at ./boot.jl:234
[3] macro expansion at /Users/.julia/v0.6/Atom/src/repl.jl:186 [inlined]
[4] anonymous at ./<missing>:?
Any ideas?
The issue here is that Array{Float64,1}(2) isn't a type (i.e. typeof(Array{Float64,1}(2)) != DataType) but a newly initialized instance of a Array{Float64,1}.
If you would like to fix the dimension of the array field xy you can
1) make constructors throw errors/warnings if someone tries to initialize with an array of wrong size (this doesn't effect performance of course)
2) use StaticArrays.jl to maybe speed up your code
Pkg.add("StaticArrays")
using StaticArrays
struct point3
xy::SVector{2,Float64}
end
Test:
julia> p = point3(#SVector rand(2))
point3([0.621778, 0.083348])
julia> p = point3(rand(2))
point3([0.737558, 0.405582])
julia> p = point3(rand(3))
ERROR: Dimension mismatch. Expected input array of length 2, got length 3
Solved it:
struct point2
xy::Array{Float64,1}
end
The problem was the definition of the dimension ...

How to make type attribute be a function of other type attributes?

To declare a new composite type, we use the following syntax
type foo
a::Int64
b::Int64
end
and instantiate like such
x = foo(1,3)
Is there some way to have type attributes that always just a function of other attributes? For example, is there some way to do the following (which is invalid syntax)...
type foo
a::Int64
b::Int64
c = a + b
end
My current workaround is just to define a function which calculates c and returns an instance of the type, like so...
type foo
a::Int64
b::Int64
c::Int64
end
function foo_maker(a, b)
return foo(a, b, a+b)
end
Is there a more elegant solution? Possibly one that can be contained within the type definition?
EDIT - 3/7/14
With Cristóvão's suggestion in mind, I've ended up declaring constructors like the following to allow for keyword args and attributes calculated upon instantiation
# Type with optional keyword argument structure
type LargeType
# Declare all the attributes in order up top
q::Int64
w::Int64
e::Int64
r::Int64
t::Int64
y::Int64
a::Number
b::Number
c::Number
# Declare Longer constructor with stuff going on in the body
LargeType(;q=1,w=1,e=1,r=1,t=1,y=1) = begin
# Large Constructor Example
a = round(r^t - log(pi))
b = a % t
c = a*b
# Return new instance with correctly ordered arguments
return new(q,w,e,r,t,y,a,b,c)
end
end
println(LargeType(r=2,t=5))
Try this:
julia> type foo
a::Int64
b::Int64
c::Int64
foo(a::Int64, b::Int64) = new(a, b, a+b)
end
julia> foo(1,2)
foo(1,2,3)
julia> foo(4,5,6)
no method foo(Int64, Int64, Int64)
However, that won't prevent one from manually changing a, b or c and rendering c inconsistent. To prevent that, if it presents no other problems, you can make foo immutable:
julia> immutable foo
...
There isn't any way to do this currently, but there might be in the future:
https://github.com/JuliaLang/julia/issues/1974

Resources