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 PostfixType
export Operator, Add, Subtract, Multiply, Divide, Power, Abs, Log, Exp, Sqrt
export ElementType, FLOAT64, OPERATOR, INT64
export Operator, ADD, SUBTRACT, MULTIPLY, DIVIDE, POWER, ABS, LOG, EXP, SQRT
export ElementType, EMPTY, FLOAT64, OPERATOR, INT64
export ExpressionElement
@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 Operator::Int64 ADD=1 SUBTRACT=2 MULTIPLY=3 DIVIDE=4 POWER=5 ABS=6 LOG=7 EXP=8 SQRT=9
@enum ElementType EMPTY=0 FLOAT64=1 OPERATOR=2 INT64=3
struct ExpressionElement
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
const PostfixType = Vector{ExpressionElement}
@ -45,23 +45,23 @@ end
function get_operator(op::Symbol)::Operator
if op == :+
return Add
return ADD
elseif op == :-
return Subtract
return SUBTRACT
elseif op == :*
return Multiply
return MULTIPLY
elseif op == :/
return Divide
return DIVIDE
elseif op == :^
return Power
return POWER
elseif op == :abs
return Abs
return ABS
elseif op == :log
return Log
return LOG
elseif op == :exp
return Exp
return EXP
elseif op == :sqrt
return Sqrt
return SQRT
end
end
@ -80,13 +80,13 @@ function convert_var_to_int(var::Symbol)::Int
end
function convert_to_ExpressionElement(element)::ExpressionElement
value = reinterpret(Int64, element)
Value = reinterpret(Int64, element)
if element isa Float64
return ExpressionElement(FLOAT64, value)
return ExpressionElement(FLOAT64, Value)
elseif element isa Int64
return ExpressionElement(INT64, value)
return ExpressionElement(INT64, Value)
elseif element isa Operator
return ExpressionElement(OPERATOR, value)
return ExpressionElement(OPERATOR, Value)
else
error("Element was of type '$(typeof(element))', which is not supported.")
end
@ -100,17 +100,17 @@ end
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
- `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)
for i in 1:length(ex.args)
arg = ex.args[i]
if typeof(arg) === Expr
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)]
end
end

View File

@ -12,59 +12,93 @@ export interpret
"
function interpret(expressions::Vector{ExpressionProcessing.PostfixType}, variables::Matrix{Float64}, parameters::Vector{Vector{Float64}})
# TODO:
# create CUDA array and fill it with the variables and parameters
# create CUDA array for calculation results
variableRows = size(variables, 1)
cudaVars = CuArray(variables)
paramRows = get_max_parameter_rows(parameters)
p1aramCols = length(parameters)
cudaParams = CuArray{Float64}(undef, p1aramCols, paramRows) # length(parameters) == number of expressions
# TODO: Fill cudaParams
cudaParams = create_cuda_array(parameters, NaN64)
cudaExprs = create_cuda_array(expressions, ExpressionElement(EMPTY, 0))
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
# 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)
cudaExpr = CuArray(expressions[i])
kernel = @cuda launch=false interpret_expression(cudaExpr, cudaVars, cudaParams, i)
kernel = @cuda launch=false interpret_expression(cudaExprs, cudaVars, cudaParams, cudaStepsize, i)
config = launch_configuration(kernel.fun)
threads = min(variableRows, config.threads)
blocks = cld(variableRows, threads)
kernel(cudaExpr, cudaVars, cudaParams, i; threads, blocks)
kernel(cudaExprs, cudaVars, cudaParams, cudaStepsize, i; threads, blocks)
end
end
function interpret_expression(expression, variables, parameters, exprIndex::Int)
#TODO Implement interpreter
function interpret_expression(expressions::CuDeviceArray{ExpressionElement}, variables::CuDeviceArray{Float64}, parameters::CuDeviceArray{Float64}, stepsize::CuDeviceArray{Int}, exprIndex::Int)
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
"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
for i in eachindex(params)
if length(params) > maxLength
maxLength = length(params)
@inbounds for i in eachindex(vec)
if length(vec[i]) > maxLength
maxLength = length(vec[i])
end
end
return maxLength
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
function InterpretExplicit!(op::Operator, x, y)
index = (blockIdx().x - 1) * blockDim().x + threadIdx().x
stride = gridDim().x * blockDim().x
if op == Add
if op == ADD
# @cuprintln("Performing Addition") # Will only be displayed when the GPU is synchronized
for i = index:stride:length(y)
@inbounds y[i] += x[i]
end
return
elseif op == Subtract
elseif op == SUBTRACT
# @cuprintln("Performing Subtraction") # Will only be displayed when the GPU is synchronized
for i = index:stride:length(y)
@inbounds y[i] -= x[i]

View File

@ -14,18 +14,18 @@ parameters[1][1] = 5
@testset "Test conversion expression element" begin
reference1 = ExpressionElement(FLOAT64, reinterpret(Int64, 1.0))
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(reference2, ExpressionProcessing.convert_to_ExpressionElement(1))
@test isequal(reference3, ExpressionProcessing.convert_to_ExpressionElement(Add))
@test isequal(reference3, ExpressionProcessing.convert_to_ExpressionElement(ADD))
end
@testset "Test conversion to postfix" begin
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),
ExpressionProcessing.convert_to_ExpressionElement(Add), ExpressionProcessing.convert_to_ExpressionElement(-1), ExpressionProcessing.convert_to_ExpressionElement(Add)])
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)])
postfix = expr_to_postfix(expressions[1])
@test isequal(reference, postfix)

View File

@ -1,21 +1,42 @@
using CUDA
using .ExpressionProcessing
using .Interpreter
expressions = Vector{Expr}(undef, 1)
variables = Matrix{Float64}(undef, 1,2)
parameters = Vector{Vector{Float64}}(undef, 1)
expressions = Vector{Expr}(undef, 2)
variables = Matrix{Float64}(undef, 2,2)
parameters = Vector{Vector{Float64}}(undef, 2)
# Resulting value should be 10
expressions[1] = :(x1 + 1 * x2 + p1)
variables[1,1] = 2
variables[1,2] = 3
expressions[2] = :(5 + x1 + 1 * x2 + p1 + p2)
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][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
postfixExpr = expr_to_postfix(expressions[1])
postfixExprs = Vector{PostfixType}(undef, 1)
postfixExprs[1] = postfixExpr
postfixExprs = Vector([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