New dispatch for isless function does not work - julia

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.

Related

new() for Julia Arrays

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.

Adding StatsBase.jl weights methods to a function in Julia

I am trying to add methods to a function similar to the example function my_function below. These methods should be dispatched when any of the subtypes of AbstractWeights from the StatsBase.jl package are passed.
I don't encounter any problem when writing the example function with Abstract and Primitive types from the Base package. E.g.
function my_function(v::Array{<:Real,1})
return sum(v .* 3)/length(v)
end
function my_function(v::Array{<:Real,1}, w::Array{<:Real,1})
return sum(v .* w .* 3)/sum(w)
end
v = [1,2,3]
w = [3,2,1]
my_function(v)
# 6.0
my_function(v, w)
# 5.0
However, when adding the methods for the types from StatsBase.jl I get MethodError errors:
using StatsBase
my_function(v::Array{<:Real,1}, w::Array{<:AbstractWeights,1}) = my_function(v,w)
my_function(v::Array{<:Real,1}, w::Array{Weights,1}) = my_function(v,w)
my_function(v, pweights(w))
# ERROR: LoadError: MethodError: no method matching my_function(::Vector{Int64}, ::ProbabilityWeights{Int64, Int64, Vector{Int64}})
my_function(v, weights(w))
# ERROR: LoadError: MethodError: no method matching my_function(::Vector{Int64}, ::Weights{Int64, Int64, Vector{Int64}})
How can I write the methods for the StatsBase.jl weights types above?
If the function worked well, the following should be true
my_function(v, w) == my_function(v, weights(w)) == my_function(v, pweights(w)) == my_function(v, fweights(w))
# true
There are several issues here:
AbstractWeights is already a vector, so you do not need to wrap it in a vector;
your implementation is recursive (the method calls itself).
So the way you should implement the code is:
my_function(v::Vector{<:Real}, w::AbstractWeights) = sum(v .* w .* 3)/sum(w)

How do i convert a SymPy expression into polynomials?

I want to change a SymPy expression, for example x+y ( x=symbols("x") and the same thing for y), into a polynomial, and then get the generators of this polynomial and the length of this polynomial.
I've tried
op=x+y
op = op[:as_poly](domain="C")
op_a = op.x[:gens]
nab = op[:length]()
but it seems that it doesn't work .
The error that i'm getting is this:
ERROR: MethodError: no method matching getindex(::Sym, ::Symbol)
Closest candidates are:
getindex(::Sym, ::Sym...) at /Users/midow/.julia/packages/SymPy/1Cwgd/src/utils.jl:18
getindex(::Number) at number.jl:75
getindex(::Number, ::Integer) at number.jl:77
...
Stacktrace:
[1] top-level scope at REPL[11]:1
As #phipsgabler mentioned, the interface changed from getindex to getproperty so
using SymPy
x=symbols("x")
y=symbols("y")
op = op.as_poly(domain="C")
op_a = op.x.gens
nab = op.length()
should give you what you want.

Evaluate expression with local variables

I'm writing a genetic program in order to test the fitness of randomly generated expressions. Shown here is the function to generate the expression as well a the main function. DIV and GT are defined elsewhere in the code:
function create_single_full_tree(depth, fs, ts)
"""
Creates a single AST with full depth
Inputs
depth Current depth of tree. Initially called from main() with max depth
fs Function Set - Array of allowed functions
ts Terminal Set - Array of allowed terminal values
Output
Full AST of typeof()==Expr
"""
# If we are at the bottom
if depth == 1
# End of tree, return function with two terminal nodes
return Expr(:call, fs[rand(1:length(fs))], ts[rand(1:length(ts))], ts[rand(1:length(ts))])
else
# Not end of expression, recurively go back through and create functions for each new node
return Expr(:call, fs[rand(1:length(fs))], create_single_full_tree(depth-1, fs, ts), create_single_full_tree(depth-1, fs, ts))
end
end
function main()
"""
Main function
"""
# Define functional and terminal sets
fs = [:+, :-, :DIV, :GT]
ts = [:x, :v, -1]
# Create the tree
ast = create_single_full_tree(4, fs, ts)
#println(typeof(ast))
#println(ast)
#println(dump(ast))
x = 1
v = 1
eval(ast) # Error out unless x and v are globals
end
main()
I am generating a random expression based on certain allowed functions and variables. As seen in the code, the expression can only have symbols x and v, as well as the value -1. I will need to test the expression with a variety of x and v values; here I am just using x=1 and v=1 to test the code.
The expression is being returned correctly, however, eval() can only be used with global variables, so it will error out when run unless I declare x and v to be global (ERROR: LoadError: UndefVarError: x not defined). I would like to avoid globals if possible. Is there a better way to generate and evaluate these generated expressions with locally defined variables?
Here is an example for generating an (anonymous) function. The result of eval can be called as a function and your variable can be passed as parameters:
myfun = eval(Expr(:->,:x, Expr(:block, Expr(:call,:*,3,:x) )))
myfun(14)
# returns 42
The dump function is very useful to inspect the expression that the parsers has created. For two input arguments you would use a tuple for example as args[1]:
julia> dump(parse("(x,y) -> 3x + y"))
Expr
head: Symbol ->
args: Array{Any}((2,))
1: Expr
head: Symbol tuple
args: Array{Any}((2,))
1: Symbol x
2: Symbol y
typ: Any
2: Expr
[...]
Does this help?
In the Metaprogramming part of the Julia documentation, there is a sentence under the eval() and effects section which says
Every module has its own eval() function that evaluates expressions in its global scope.
Similarly, the REPL help ?eval will give you, on Julia 0.6.2, the following help:
Evaluate an expression in the given module and return the result. Every Module (except those defined with baremodule) has its own 1-argument definition of eval, which evaluates expressions in that module.
I assume, you are working in the Main module in your example. That's why you need to have the globals defined there. For your problem, you can use macros and interpolate the values of x and y directly inside the macro.
A minimal working example would be:
macro eval_line(a, b, x)
isa(a, Real) || (warn("$a is not a real number."); return :(throw(DomainError())))
isa(b, Real) || (warn("$b is not a real number."); return :(throw(DomainError())))
return :($a * $x + $b) # interpolate the variables
end
Here, #eval_line macro does the following:
Main> #macroexpand #eval_line(5, 6, 2)
:(5 * 2 + 6)
As you can see, the values of macro's arguments are interpolated inside the macro and the expression is given to the user accordingly. When the user does not behave,
Main> #macroexpand #eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
:((Main.throw)((Main.DomainError)()))
a user-friendly warning message is provided to the user at parse-time, and a DomainError is thrown at run-time.
Of course, you can do these things within your functions, again by interpolating the variables --- you do not need to use macros. However, what you would like to achieve in the end is to combine eval with the output of a function that returns Expr. This is what the macro functionality is for. Finally, you would simply call your macros with an # sign preceding the macro name:
Main> #eval_line(5, 6, 2)
16
Main> #eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
ERROR: DomainError:
Stacktrace:
[1] eval(::Module, ::Any) at ./boot.jl:235
EDIT 1. You can take this one step further, and create functions accordingly:
macro define_lines(linedefs)
for (name, a, b) in eval(linedefs)
ex = quote
function $(Symbol(name))(x) # interpolate name
return $a * x + $b # interpolate a and b here
end
end
eval(ex) # evaluate the function definition expression in the module
end
end
Then, you can call this macro to create different line definitions in the form of functions to be called later on:
#define_lines([
("identity_line", 1, 0);
("null_line", 0, 0);
("unit_shift", 0, 1)
])
identity_line(5) # returns 5
null_line(5) # returns 0
unit_shift(5) # returns 1
EDIT 2. You can, I guess, achieve what you would like to achieve by using a macro similar to that below:
macro random_oper(depth, fs, ts)
operations = eval(fs)
oper = operations[rand(1:length(operations))]
terminals = eval(ts)
ts = terminals[rand(1:length(terminals), 2)]
ex = :($oper($ts...))
for d in 2:depth
oper = operations[rand(1:length(operations))]
t = terminals[rand(1:length(terminals))]
ex = :($oper($ex, $t))
end
return ex
end
which will give the following, for instance:
Main> #macroexpand #random_oper(1, [+, -, /], [1,2,3])
:((-)([3, 3]...))
Main> #macroexpand #random_oper(2, [+, -, /], [1,2,3])
:((+)((-)([2, 3]...), 3))
Thanks Arda for the thorough response! This helped, but part of me thinks there may be a better way to do this as it seems too roundabout. Since I am writing a genetic program, I will need to create 500 of these ASTs, all with random functions and terminals from a set of allowed functions and terminals (fs and ts in the code). I will also need to test each function with 20 different values of x and v.
In order to accomplish this with the information you have given, I have come up with the following macro:
macro create_function(defs)
for name in eval(defs)
ex = quote
function $(Symbol(name))(x,v)
fs = [:+, :-, :DIV, :GT]
ts = [x,v,-1]
return create_single_full_tree(4, fs, ts)
end
end
eval(ex)
end
end
I can then supply a list of 500 random function names in my main() function, such as ["func1, func2, func3,.....". Which I can eval with any x and v values in my main function. This has solved my issue, however, this seems to be a very roundabout way of doing this, and may make it difficult to evolve each AST with each iteration.

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 ...

Resources