Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ number_features = 5
#define the
regressor = GepTensorRegressor(number_features,
gene_count=2, #2 works quite reliable
head_len=3) # 5 works quite reliable
head_len=3;
feature_names=["x1","x2","U1","U2","U3"]) # 5 works quite reliable

#create some testdata - testing simply on a few velocity vectors
size_test = 1000
Expand All @@ -126,7 +127,7 @@ x1 = [2.0 for _ in 1:size_test]

x2 = [0.0 for _ in 1:size_test]

a = 0.5 * u1 .+ x2 .* u2 + 2* u3
a = 0.5 * u1 .+ x2 .* u2 + 2 .* u3

inputs = (x1,x2,u1,u2,u3)

Expand All @@ -146,6 +147,10 @@ inputs = (x1,x2,u1,u2,u3)
end
end
fit!(regressor, epochs, population_size, loss_new)

#Print the best expression
lsg = regressor.best_models_[1]
print_karva_strings(lsg)
```

# Supported `Engines' for Symbolic Evaluation
Expand Down
35 changes: 29 additions & 6 deletions src/Entities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export Chromosome, Toolbox, EvaluationStrategy, StandardRegressionStrategy, Gene
export fitness, set_fitness!
export generate_gene, compile_expression!, generate_chromosome, generate_population
export genetic_operations!, replicate, gene_inversion!, gene_mutation!, gene_one_point_cross_over!, gene_two_point_cross_over!, gene_fussion!, split_karva
export print_karva_strings

using ..GepUtils
using ..TensorRegUtils
Expand Down Expand Up @@ -415,23 +416,45 @@ Vector{Int8} representing the K-expression of the chromosome
rolled_indices[idx+1] = @view genes[i:i+first(indices)-1]
end

!split && return vcat(rolled_indices...)
!split && return vcat(rolled_indices...)
return rolled_indices
end

@inline function split_karva(chromosome::Chromosome, coeffs::Int=2)
raw = _karva_raw(chromosome; split=true)
connectors = popfirst!(raw)[coeffs:end]
gene_count_per_factor = div(chromosome.toolbox.gene_count,coeffs)
gene_count_per_factor = div(chromosome.toolbox.gene_count, coeffs)
retval = []
for _ in 1:coeffs
temp_cons = splice!(connectors, 1:gene_count_per_factor-1)
temp_genes = reduce(vcat, splice!(raw,1:gene_count_per_factor))
push!(retval,vcat([temp_cons, temp_genes]...))
temp_genes = reduce(vcat, splice!(raw, 1:gene_count_per_factor))
push!(retval, vcat([temp_cons, temp_genes]...))
end
return retval
end

@inline function print_karva_strings(chromosome::Chromosome)
coeff_count = length(chromosome.toolbox.preamble_syms)
callback_ = Dict{Int8, Function}()

for (key, value) in chromosome.toolbox.callbacks
if Symbol(value) in keys(FUNCTION_STRINGIFY)
callback_[key] = FUNCTION_STRINGIFY[Symbol(value)]
elseif Symbol(value) in keys(TENSOR_STRINGIFY)
callback_[key] = TENSOR_STRINGIFY[Symbol(value)]
end
end

return compile_djl_datatype(
chromosome.expression_raw,
chromosome.toolbox.arrity_by_id,
callback_,
chromosome.toolbox.nodes,
1)


end

"""
generate_gene(headsyms::Vector{Int8}, tailsyms::Vector{Int8}, headlen::Int;
unarys::Vector{Int8}=[], unary_prob::Real=0.2)
Expand All @@ -451,10 +474,10 @@ Vector{Int8} representing gene
@inline function generate_gene(headsyms::Vector{Int8}, tailsyms::Vector{Int8}, headlen::Int;
unarys::Vector{Int8}=[], unary_prob::Real=0.2, tensor_prob::Real=0.2)
if !isempty(unarys) && rand() < unary_prob
heads = vcat(headsyms, rand(tailsyms,2))
heads = vcat(headsyms, rand(tailsyms, 2))
push!(heads, rand(unarys))
else
heads = vcat(headsyms, rand(tailsyms,2))
heads = vcat(headsyms, rand(tailsyms, 2))
end

head = rand(heads, headlen)
Expand Down
14 changes: 8 additions & 6 deletions src/GeneExpressionProgramming.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ import .TensorRegUtils:
DoubleContractionNode, DeviatoricNode,
ConstantNode, UnaryNode,
compile_to_flux_network,
TENSOR_NODES, TENSOR_NODES_ARITY
TENSOR_NODES, TENSOR_NODES_ARITY, TENSOR_STRINGIFY

# Import GEP core functionality
import .GepRegression:
Expand All @@ -129,7 +129,8 @@ import .GepUtils:
get_history_arrays,
train_test_split,
ARITY_LIB_COMMON,
FUNCTION_LIB_COMMON
FUNCTION_LIB_COMMON,
FUNCTION_STRINGIFY

# Import selection mechanisms
import .EvoSelection:
Expand Down Expand Up @@ -190,7 +191,8 @@ import .GepEntities:
generate_chromosome,
generate_population,
genetic_operations!,
split_karva
split_karva,
print_karva_strings

# Import regression wrapper functionality
import .RegressionWrapper:
Expand Down Expand Up @@ -225,13 +227,13 @@ export InputSelector,
DoubleContractionNode, DeviatoricNode,
ConstantNode, UnaryNode,
compile_to_flux_network,
TENSOR_NODES, TENSOR_NODES_ARITY
TENSOR_NODES, TENSOR_NODES_ARITY, TENSOR_STRINGIFY


# Export core GEP entities and operations
export Chromosome, Toolbox, fitness, set_fitness!,
generate_gene, compile_expression!, generate_chromosome, generate_population,
genetic_operations!, split_karva
genetic_operations!, split_karva, print_karva_strings

# Export regression components
export GepRegressor, GepTensorRegressor, fit!,
Expand Down Expand Up @@ -281,7 +283,7 @@ export HistoryRecorder, OptimizationHistory,
get_history_arrays

# Export common libraries
export ARITY_LIB_COMMON, FUNCTION_LIB_COMMON
export ARITY_LIB_COMMON, FUNCTION_LIB_COMMON, FUNCTION_STRINGIFY


end
7 changes: 5 additions & 2 deletions src/Gep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Applies correction operations to ensure dimensional homogeneity in chromosomes.
compile_expression!(population[i]; force_compile=true)
population[i].dimension_homogene = true
else
#population[i].fitness += distance
population[i].fitness = (population[i].fitness[1]+distance,)
end
end
end
Expand Down Expand Up @@ -299,7 +299,10 @@ The evolution process stops when either:

Threads.@threads for i in eachindex(population)
if isnan(mean(population[i].fitness))
cache_value = get(fit_cache, population[i].expression_raw, nothing)
cache_value = nothing
lock(cache_lock) do
cache_value = get(fit_cache, population[i].expression_raw, nothing)
end
if isnothing(cache_value)

population[i].fitness = compute_fitness(population[i], evalStrategy)
Expand Down
13 changes: 8 additions & 5 deletions src/RegressionWrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,8 @@ Dictionary containing default probabilities and parameters for genetic algorithm
These values can be adjusted to fine-tune the genetic algorithm's behavior.
"""
const GENE_COMMON_PROBS = Dict{String,AbstractFloat}(
"one_point_cross_over_prob" => 0.3,
"two_point_cross_over_prob" => 0.2,
"one_point_cross_over_prob" => 0.6,
"two_point_cross_over_prob" => 0.4,
"mutation_prob" => 1.0,
"mutation_rate" => 0.05,
"dominant_fusion_prob" => 0.1,
Expand Down Expand Up @@ -585,13 +585,14 @@ mutable struct GepTensorRegressor

function GepTensorRegressor(scalar_feature_amount::Int;
higher_dim_feature_amount::Int=0,
entered_non_terminals::Vector{Symbol}=[:+, :-, :*],
entered_non_terminals::Vector{Symbol}=[:+, :-, :*, :/],
entered_terminal_nums::Vector{<:AbstractFloat}=Float64[],
gene_connections::Vector{Symbol}=[:+, :*],
rnd_count::Int=0,
gene_count::Int=2,
head_len::Int=3,
number_of_objectives::Int=1
number_of_objectives::Int=1,
feature_names::Vector{String}=String[]
)
#Creating the feature Nodes -> asuming a data dict pointing to
cur_idx = Int8(1)
Expand All @@ -602,7 +603,8 @@ mutable struct GepTensorRegressor
tensor_syms_idx = Int8[]
tensor_function_idx = Int8[]
for _ in 1:scalar_feature_amount
nodes[cur_idx] = InputSelector(cur_idx)
feature_name = isempty(feature_names) ? "x$cur_idx" : feature_names[cur_idx]
nodes[cur_idx] = InputSelector(cur_idx, feature_name)
utilized_symbols[cur_idx] = Int8(0)
cur_idx += 1
end
Expand All @@ -628,6 +630,7 @@ mutable struct GepTensorRegressor

#callback - index => function
for elem in entered_non_terminals
@show elem
callbacks[cur_idx] = TENSOR_NODES[elem]
utilized_symbols[cur_idx] = TENSOR_NODES_ARITY[elem]
if elem in gene_connections
Expand Down
53 changes: 44 additions & 9 deletions src/TensorOps.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module TensorRegUtils

import Base: string
using Flux, LinearAlgebra, OrderedCollections, ChainRulesCore, Tensors, PrecompileTools


Expand All @@ -10,6 +11,15 @@ abstract type AbstractOperationNode{T} end
# Input selector with strict typing
struct InputSelector{T<:Integer}
idx::T
name::String
end

function InputSelector(idx::T) where T<:Integer
InputSelector{T}(idx, "x$idx")
end

function Base.string(node::InputSelector)
return "$(node.name)"
end

@inline function (l::InputSelector{T})(x::Tuple) where {T}
Expand Down Expand Up @@ -111,15 +121,18 @@ end
end



@inline function (l::MultiplicationNode{T})(x::Union{Tensor,SymmetricTensor}, y::Union{Tensor,SymmetricTensor}) where {T}
@fastmath dot(x, y)::Union{Tensor,SymmetricTensor,Number}
end

@inline function (l::DivisionNode{T})(x::Union{Tensor,SymmetricTensor,Number}, y::Number) where {T}
@inline function (l::DivisionNode{T})(x::Union{Tensor,SymmetricTensor}, y::Number) where {T}
@fastmath (x / y)::Union{Tensor,SymmetricTensor,Number}
end

@inline function (l::DivisionNode{T})(x::Number, y::Number) where {T}
@fastmath (x / y)::Number
end

@inline function (l::DivisionNode{T})(x::Any, y::Any) where {T}
Inf::Number
end
Expand Down Expand Up @@ -205,8 +218,12 @@ end
map((a, b) -> l(a, b), x, y)::AbstractVector
end

@inline function (l::DivisionNode{T})(x::AbstractVector, y::Number) where {T}
map(a -> l(a, y), x)::AbstractVector
@inline function (l::MultiplicationNode{T})(x::AbstractVector{Number}, y::AbstractVector{Number}) where {T}
(x .* y)::AbstractVector{Number}
end

@inline function (l::DivisionNode{T})(x::AbstractVector{Number}, y::AbstractVector{Number}) where {T}
(x ./ y)::AbstractVector{Number}
end

@inline function (l::DivisionNode{T})(x::AbstractVector, y::AbstractVector) where {T}
Expand Down Expand Up @@ -297,10 +314,6 @@ function compile_to_flux_network(rek_string::Vector, arity_map::OrderedDict, cal
return Chain(pop!(stack))
end

function string()

end

# Constant mappings
const TENSOR_NODES = Dict{Symbol,Type}(
:+ => AdditionNode,
Expand All @@ -323,6 +336,28 @@ const TENSOR_NODES = Dict{Symbol,Type}(
:deviator => DeviatoricNode
)

const TENSOR_STRINGIFY = Dict{Symbol,Function}(
:AdditionNode => (args...) -> "($(string(args[1])) + $(string(args[2])))",
:SubtractionNode => (args...) -> "($(string(args[1])) - $(string(args[2])))",
:MultiplicationNode => (args...) -> "($(string(args[1])) * $(string(args[2])))",
:DivisionNode => (args...) -> "($(string(args[1])) / $(string(args[2])))",
:PowerNode => (args...) -> "($(string(args[1]))^$(string(args[2])))",
:MinNode => (args...) -> "min($(string(args[1])),$(string(args[2])))",
:MaxNode => (args...) -> "max($(string(args[1])),$(string(args[2])))",
:InversionNode => A -> "inv($(string(A))",
:TraceNode => A -> "tr($(string(A))",
:DeterminantNode => A -> "det($(string(A))",
:SymmetricNode => A -> "sym($(string(A))",
:SkewNode => A -> "skew($(string(A))",
:VolumetricNode => A -> "vol($(string(A))",
:DeviatricNode => A -> "dev($(string(A))",
:TdotNode => (A, B) -> "($(string(A))·($(string(B))ᵀ",
:DottNode => (A, B) -> "($(string(A))ᵀ·($(string(B))",
:DoubleContractionNode => (A, B) -> "($(string(A)):($(string(B))",
:DeviatoricNode => A -> "dev($(string(A))"
)


const TENSOR_NODES_ARITY = Dict{Symbol,Int8}(
:+ => 2, :- => 2, :* => 2, :/ => 2, :^ => 2,
:min => 2, :max => 2,
Expand All @@ -340,7 +375,7 @@ export VolumetricNode, DeviatricNode, TdotNode, DottNode
export DoubleContractionNode, DeviatoricNode
export ConstantNode, UnaryNode
export compile_to_flux_network
export TENSOR_NODES, TENSOR_NODES_ARITY
export TENSOR_NODES, TENSOR_NODES_ARITY, TENSOR_STRINGIFY


@setup_workload begin
Expand Down
40 changes: 36 additions & 4 deletions src/Util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export create_history_recorder, record_history!, record!, close_recorder!
export HistoryRecorder, OptimizationHistory, get_history_arrays
export train_test_split
export FUNCTION_LIB_COMMON, ARITY_LIB_COMMON
export TensorNode, compile_network
export TensorNode, compile_network, FUNCTION_STRINGIFY

using OrderedCollections
using DynamicExpressions
Expand Down Expand Up @@ -184,6 +184,40 @@ const FUNCTION_LIB_COMMON = Dict{Symbol,Function}(
:sqrt => sqrt, :sign => sign
)


const FUNCTION_STRINGIFY = Dict{Symbol,Function}(
:+ => (args...) -> join(args, " + "),
:- => (args...) -> length(args) == 1 ? "-$(args[1])" : join(args, " - "),
:* => (args...) -> join(args, " * "),
:/ => (args...) -> join(args, " / "),
:^ => (args...) -> "$(args[1])^$(args[2])",
:min => (args...) -> "min($(join(args, ", ")))",
:max => (args...) -> "max($(join(args, ", ")))",
:abs => a -> "|$a|",
:round => a -> "round($a)",
:exp => a -> "e^($a)",
:log => a -> "ln($a)",
:log10 => a -> "log₁₀($a)",
:log2 => a -> "log₂($a)",
:sin => a -> "sin($a)",
:cos => a -> "cos($a)",
:tan => a -> "tan($a)",
:asin => a -> "arcsin($a)",
:acos => a -> "arccos($a)",
:atan => a -> "arctan($a)",

:sinh => a -> "sinh($a)",
:cosh => a -> "cosh($a)",
:tanh => a -> "tanh($a)",
:asinh => a -> "arcsinh($a)",
:acosh => a -> "arccosh($a)",
:atanh => a -> "arctanh($a)",

:sqr => a -> "($a)²",
:sqrt => a -> "√($a)",
:sign => a -> "sign($a)"
)

"""
ARITY_LIB_COMMON::Dict{Symbol,Int8}

Expand Down Expand Up @@ -690,10 +724,8 @@ See also: [`DynamicExpressions.Node`](@ref), [`Optim.optimize`](@ref), [`LineSea
end
end
end
#needs to be revised!
x0, refs = get_scalar_constants(current_node)


x0, refs = get_scalar_constants(current_node)
function opt_step(x::AbstractVector)
set_scalar_constants!(current_node,x, refs)
loss(current_node)
Expand Down