layed groundwork for implementing interpretation
Some checks failed
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, 1.10) (push) Has been cancelled
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, 1.6) (push) Has been cancelled
CI / Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} (x64, ubuntu-latest, pre) (push) Has been cancelled

This commit is contained in:
Daniel 2024-07-21 13:45:57 +02:00
parent e1097202ab
commit c871487a55
4 changed files with 108 additions and 53 deletions

View File

@ -2,16 +2,16 @@ module ExpressionProcessing
export expr_to_postfix export expr_to_postfix
export PostfixType export PostfixType
export Operator, Add, Subtract, Multiply, Divide, Power, Abs, Log, Exp, Sqrt export Operator, ADD, SUBTRACT, MULTIPLY, DIVIDE, POWER, ABS, LOG, EXP, SQRT
export ElementType, FLOAT64, OPERATOR, INT64 export ElementType, EMPTY, FLOAT64, OPERATOR, INT64
export ExpressionElement export ExpressionElement
@enum Operator::Int64 Add=1 Subtract=2 Multiply=3 Divide=4 Power=5 Abs=6 Log=7 Exp=8 Sqrt=9 @enum Operator::Int64 ADD=1 SUBTRACT=2 MULTIPLY=3 DIVIDE=4 POWER=5 ABS=6 LOG=7 EXP=8 SQRT=9
@enum ElementType FLOAT64=1 OPERATOR=2 INT64=3 @enum ElementType EMPTY=0 FLOAT64=1 OPERATOR=2 INT64=3
struct ExpressionElement struct ExpressionElement
Type::ElementType Type::ElementType
value::Int64 # Reinterpret the stored value to type "ElementType" when using it Value::Int64 # Reinterpret the stored value to type "ElementType" when using it
end end
const PostfixType = Vector{ExpressionElement} const PostfixType = Vector{ExpressionElement}
@ -45,23 +45,23 @@ end
function get_operator(op::Symbol)::Operator function get_operator(op::Symbol)::Operator
if op == :+ if op == :+
return Add return ADD
elseif op == :- elseif op == :-
return Subtract return SUBTRACT
elseif op == :* elseif op == :*
return Multiply return MULTIPLY
elseif op == :/ elseif op == :/
return Divide return DIVIDE
elseif op == :^ elseif op == :^
return Power return POWER
elseif op == :abs elseif op == :abs
return Abs return ABS
elseif op == :log elseif op == :log
return Log return LOG
elseif op == :exp elseif op == :exp
return Exp return EXP
elseif op == :sqrt elseif op == :sqrt
return Sqrt return SQRT
end end
end end
@ -80,13 +80,13 @@ function convert_var_to_int(var::Symbol)::Int
end end
function convert_to_ExpressionElement(element)::ExpressionElement function convert_to_ExpressionElement(element)::ExpressionElement
value = reinterpret(Int64, element) Value = reinterpret(Int64, element)
if element isa Float64 if element isa Float64
return ExpressionElement(FLOAT64, value) return ExpressionElement(FLOAT64, Value)
elseif element isa Int64 elseif element isa Int64
return ExpressionElement(INT64, value) return ExpressionElement(INT64, Value)
elseif element isa Operator elseif element isa Operator
return ExpressionElement(OPERATOR, value) return ExpressionElement(OPERATOR, Value)
else else
error("Element was of type '$(typeof(element))', which is not supported.") error("Element was of type '$(typeof(element))', which is not supported.")
end end
@ -100,17 +100,17 @@ end
const SymbolTable64 = Dict{Tuple{Expr, Symbol},Float64} const SymbolTable64 = Dict{Tuple{Expr, Symbol},Float64}
"Replaces all the variables and parameters of the given expression with their corresponding value stored in the symtable "Replaces all the variables and parameters of the given expression with their corresponding Value stored in the symtable
# Arguments # Arguments
- `symtable::SymbolTable64`: Contains the values of all variables for each expression - `symtable::SymbolTable64`: Contains the values of all variables for each expression
- `originalExpr::Expr`: Contains a deep copy of the original expression. It is used to link the expression and variables to their according value stored in the symtable - `originalExpr::Expr`: Contains a deep copy of the original expression. It is used to link the expression and variables to their according Value stored in the symtable
" "
function replace_variables!(ex::Expr, symtable::SymbolTable64, originalExpr::Expr) function replace_variables!(ex::Expr, symtable::SymbolTable64, originalExpr::Expr)
for i in 1:length(ex.args) for i in 1:length(ex.args)
arg = ex.args[i] arg = ex.args[i]
if typeof(arg) === Expr if typeof(arg) === Expr
replace_variables!(arg, symtable, originalExpr) replace_variables!(arg, symtable, originalExpr)
elseif haskey(symtable, (originalExpr,arg)) # We found a variable/parameter and can replace it with the actual value elseif haskey(symtable, (originalExpr,arg)) # We found a variable/parameter and can replace it with the actual Value
ex.args[i] = symtable[(originalExpr,arg)] ex.args[i] = symtable[(originalExpr,arg)]
end end
end end

View File

@ -12,59 +12,93 @@ export interpret
" "
function interpret(expressions::Vector{ExpressionProcessing.PostfixType}, variables::Matrix{Float64}, parameters::Vector{Vector{Float64}}) function interpret(expressions::Vector{ExpressionProcessing.PostfixType}, variables::Matrix{Float64}, parameters::Vector{Vector{Float64}})
# TODO: # TODO:
# create CUDA array and fill it with the variables and parameters
# create CUDA array for calculation results # create CUDA array for calculation results
variableRows = size(variables, 1) variableRows = size(variables, 1)
cudaVars = CuArray(variables) cudaVars = CuArray(variables)
cudaParams = create_cuda_array(parameters, NaN64)
paramRows = get_max_parameter_rows(parameters) cudaExprs = create_cuda_array(expressions, ExpressionElement(EMPTY, 0))
p1aramCols = length(parameters) cudaStepsize = CuArray([get_max_inner_length(expressions), get_max_inner_length(parameters)]) # put into seperate cuArray, as this is static and would be inefficient to send seperatly to every kernel
cudaParams = CuArray{Float64}(undef, p1aramCols, paramRows) # length(parameters) == number of expressions
# TODO: Fill cudaParams
# TODO: Move CuArray(expression[i]) outside the loop for a more efficient transfer to GPU but leave kernel signature as is # Start kernel for each expression to ensure that no warp is working on different expressions
for i in eachindex(expressions) for i in eachindex(expressions)
cudaExpr = CuArray(expressions[i]) kernel = @cuda launch=false interpret_expression(cudaExprs, cudaVars, cudaParams, cudaStepsize, i)
kernel = @cuda launch=false interpret_expression(cudaExpr, cudaVars, cudaParams, i)
config = launch_configuration(kernel.fun) config = launch_configuration(kernel.fun)
threads = min(variableRows, config.threads) threads = min(variableRows, config.threads)
blocks = cld(variableRows, threads) blocks = cld(variableRows, threads)
kernel(cudaExpr, cudaVars, cudaParams, i; threads, blocks) kernel(cudaExprs, cudaVars, cudaParams, cudaStepsize, i; threads, blocks)
end end
end end
function interpret_expression(expression, variables, parameters, exprIndex::Int) function interpret_expression(expressions::CuDeviceArray{ExpressionElement}, variables::CuDeviceArray{Float64}, parameters::CuDeviceArray{Float64}, stepsize::CuDeviceArray{Int}, exprIndex::Int)
#TODO Implement interpreter firstExprIndex = (exprIndex - 1 * stepsize[1]) + 1 # Inclusive
lastExprIndex = firstExprIndex + stepsize[1] # Exclusive
firstParamIndex = (exprIndex - 1 * stepsize[2]) + 1 # Inclusive
# lastParamIndex = firstParamIndex + stepsize[2] # Exclusive (probably not needed)
for i in firstExprIndex:lastExprIndex
# TODO Implement interpreter
# - start at firstExprIndex and interpret until the first ExpressionElement is "Empty" or we reached lastExprIndex
end
return
end end
"Retrieves the number of entries for the largest inner vector" "Retrieves the number of entries for the largest inner vector"
function get_max_parameter_rows(params::Vector{Vector{T}})::Int where T function get_max_inner_length(vec::Vector{Vector{T}})::Int where T
maxLength = 0 maxLength = 0
for i in eachindex(params) @inbounds for i in eachindex(vec)
if length(params) > maxLength if length(vec[i]) > maxLength
maxLength = length(params) maxLength = length(vec[i])
end end
end end
return maxLength return maxLength
end end
"Returns a CuArray filed with the data provided. The inner vectors do not have to have the same length. All missing elements will be the value ```invalidElement```"
function create_cuda_array(data::Vector{Vector{T}}, invalidElement::T)::CuArray{T} where T
dataCols = get_max_inner_length(data)
dataRows = length(data)
dataMat = convert_to_matrix(data, invalidElement)
cudaArr = CuArray{T}(undef, dataCols, dataRows) # length(parameters) == number of expressions
copyto!(cudaArr, dataMat)
return cudaArr
end
"Converts a vector of vectors into a matrix. The inner vectors do not need to have the same length.
All entries that cannot be filled have ```invalidElement``` as their value
"
function convert_to_matrix(vec::Vector{Vector{T}}, invalidElement::T)::Matrix{T} where T
vecCols = get_max_inner_length(vec)
vecRows = length(vec)
vecMat = fill(invalidElement, vecCols, vecRows)
for i in eachindex(vec)
vecMat[:,i] = copyto!(vecMat[:,i], vec[i])
end
return vecMat
end
@deprecate InterpretExplicit!(op::Operator, x, y) interpret_expression(expression, variables, parameters, exprIndex::Int)
# @deprecate InterpretExplicit!(op::Operator, x, y) interpret_expression(expression, variables, parameters, exprIndex::Int)
# Kernel # Kernel
function InterpretExplicit!(op::Operator, x, y) function InterpretExplicit!(op::Operator, x, y)
index = (blockIdx().x - 1) * blockDim().x + threadIdx().x index = (blockIdx().x - 1) * blockDim().x + threadIdx().x
stride = gridDim().x * blockDim().x stride = gridDim().x * blockDim().x
if op == Add if op == ADD
# @cuprintln("Performing Addition") # Will only be displayed when the GPU is synchronized # @cuprintln("Performing Addition") # Will only be displayed when the GPU is synchronized
for i = index:stride:length(y) for i = index:stride:length(y)
@inbounds y[i] += x[i] @inbounds y[i] += x[i]
end end
return return
elseif op == Subtract elseif op == SUBTRACT
# @cuprintln("Performing Subtraction") # Will only be displayed when the GPU is synchronized # @cuprintln("Performing Subtraction") # Will only be displayed when the GPU is synchronized
for i = index:stride:length(y) for i = index:stride:length(y)
@inbounds y[i] -= x[i] @inbounds y[i] -= x[i]

View File

@ -14,18 +14,18 @@ parameters[1][1] = 5
@testset "Test conversion expression element" begin @testset "Test conversion expression element" begin
reference1 = ExpressionElement(FLOAT64, reinterpret(Int64, 1.0)) reference1 = ExpressionElement(FLOAT64, reinterpret(Int64, 1.0))
reference2 = ExpressionElement(INT64, reinterpret(Int64, 1)) reference2 = ExpressionElement(INT64, reinterpret(Int64, 1))
reference3 = ExpressionElement(OPERATOR, reinterpret(Int64, Add)) reference3 = ExpressionElement(OPERATOR, reinterpret(Int64, ADD))
@test isequal(reference1, ExpressionProcessing.convert_to_ExpressionElement(1.0)) @test isequal(reference1, ExpressionProcessing.convert_to_ExpressionElement(1.0))
@test isequal(reference2, ExpressionProcessing.convert_to_ExpressionElement(1)) @test isequal(reference2, ExpressionProcessing.convert_to_ExpressionElement(1))
@test isequal(reference3, ExpressionProcessing.convert_to_ExpressionElement(Add)) @test isequal(reference3, ExpressionProcessing.convert_to_ExpressionElement(ADD))
end end
@testset "Test conversion to postfix" begin @testset "Test conversion to postfix" begin
reference = PostfixType() reference = PostfixType()
append!(reference, [ExpressionProcessing.convert_to_ExpressionElement(1), ExpressionProcessing.convert_to_ExpressionElement(1.0), ExpressionProcessing.convert_to_ExpressionElement(2), ExpressionProcessing.convert_to_ExpressionElement(Multiply), append!(reference, [ExpressionProcessing.convert_to_ExpressionElement(1), ExpressionProcessing.convert_to_ExpressionElement(1.0), ExpressionProcessing.convert_to_ExpressionElement(2), ExpressionProcessing.convert_to_ExpressionElement(MULTIPLY),
ExpressionProcessing.convert_to_ExpressionElement(Add), ExpressionProcessing.convert_to_ExpressionElement(-1), ExpressionProcessing.convert_to_ExpressionElement(Add)]) ExpressionProcessing.convert_to_ExpressionElement(ADD), ExpressionProcessing.convert_to_ExpressionElement(-1), ExpressionProcessing.convert_to_ExpressionElement(ADD)])
postfix = expr_to_postfix(expressions[1]) postfix = expr_to_postfix(expressions[1])
@test isequal(reference, postfix) @test isequal(reference, postfix)

View File

@ -1,21 +1,42 @@
using CUDA
using .ExpressionProcessing using .ExpressionProcessing
using .Interpreter using .Interpreter
expressions = Vector{Expr}(undef, 1) expressions = Vector{Expr}(undef, 2)
variables = Matrix{Float64}(undef, 1,2) variables = Matrix{Float64}(undef, 2,2)
parameters = Vector{Vector{Float64}}(undef, 1) parameters = Vector{Vector{Float64}}(undef, 2)
# Resulting value should be 10 # Resulting value should be 10
expressions[1] = :(x1 + 1 * x2 + p1) expressions[1] = :(x1 + 1 * x2 + p1)
variables[1,1] = 2 expressions[2] = :(5 + x1 + 1 * x2 + p1 + p2)
variables[1,2] = 3 variables[1,1] = 2.0
variables[1,2] = 3.0
variables[2,1] = 2.0
variables[2,2] = 3.0
parameters[1] = Vector{Float64}(undef, 1) parameters[1] = Vector{Float64}(undef, 1)
parameters[1][1] = 5 parameters[2] = Vector{Float64}(undef, 2)
parameters[1][1] = 5.0
parameters[2][1] = 5.0
parameters[2][2] = 0.0
@testset "Test interpretation" begin @testset "Test interpretation" begin
postfixExpr = expr_to_postfix(expressions[1]) postfixExpr = expr_to_postfix(expressions[1])
postfixExprs = Vector{PostfixType}(undef, 1) postfixExprs = Vector([postfixExpr])
postfixExprs[1] = postfixExpr push!(postfixExprs, expr_to_postfix(expressions[2]))
Interpret(postfixExprs, variables, parameters) CUDA.@sync interpret(postfixExprs, variables, parameters)
end
@testset "Test conversion to matrix" begin
reference = Matrix{Float64}(undef, 2, 2)
reference[1,1] = 5.0
reference[2,1] = NaN64
reference[1,2] = 5.0
reference[2,2] = 0.0
# fill!(reference, [[], [5.0, 0.0]])
# reference = Matrix([5.0, NaN],
# [5.0, 0.0])
result = Interpreter.convert_to_matrix(parameters, NaN64)
@test isequal(result, reference)
end end