update
This commit is contained in:
841
previousVersion/0.0.2/src/interface.jl
Normal file
841
previousVersion/0.0.2/src/interface.jl
Normal file
@@ -0,0 +1,841 @@
|
||||
module interface
|
||||
|
||||
|
||||
export noNegative!, randomWithProb, randomChoiceWithProb, findIndex, limitvalue, replaceMoreThan,
|
||||
replaceLessThan, cartesianAssign!, sumAlongDim3, matMul_3Dto3D_manyTo1batch,
|
||||
matMul_3Dto4D_batchwise
|
||||
|
||||
using JSON3, DataStructures, Distributions, Flux, CUDA
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
noNegative!(a::AbstractVector) = replace!(x -> x < 0 ? 0 : x, a)
|
||||
findNotZero(x::AbstractVector) = findall( (!iszero).(x) )
|
||||
replaceMoreThan(i, target) = i > target ? target : i
|
||||
replaceLessThan(i, target) = i < target ? target : i
|
||||
precision(x::Array{<:Array}) = ( std(mean.(x)) / mean(mean.(x)) ) * 100
|
||||
precision(x::Array) = std(x) / mean(x) * 100
|
||||
replaceAt!(x::AbstractVector, ind::Number, value::Number) = x[ind] = value
|
||||
notZero(x::AbstractVector) = (!iszero).(x)
|
||||
Zero(x::AbstractVector) = iszero.(x)
|
||||
isNan(x::AbstractVector) = isnan.(x)
|
||||
isInf(x::Number) = abs(x) === Inf
|
||||
isInf(x::AbstractVector) = isinf.(x)
|
||||
isLess(x::AbstractVector, target::Number) = isless.(x, target)
|
||||
isMore(x::Number, target::Number) = x > target
|
||||
isMore(x::AbstractVector, target::Number) = isMore.(x, target)
|
||||
absolute(x::AbstractVector) = abs.(x)
|
||||
vecEleMul(x::AbstractVector, y::AbstractVector) = x .* y
|
||||
vecEleMul(x::Number, y::AbstractVector) = x .* y
|
||||
expDecay(initialValue::Number, decayFactor::Number, timePass::Number) =
|
||||
initialValue * (1 - decayFactor)^timePass
|
||||
mul!(x::AbstractVector, y::AbstractVector) = x .*= y
|
||||
mul(x::AbstractVector, y::AbstractVector) = x .* y
|
||||
|
||||
ReLu(x::Number) = max(0, x)
|
||||
|
||||
updateVector!(x::AbstractVector, target::Number) = x .= target
|
||||
updateVector!(x::AbstractVector, target::AbstractArray) = x .= target
|
||||
|
||||
function selectAdd!(x::AbstractVector, ind::AbstractVector, value::AbstractVector)
|
||||
@. x = x + (ind * value)
|
||||
end
|
||||
|
||||
|
||||
""" findIndex(input::String, target::Char)
|
||||
|
||||
Find target index inside a collection.
|
||||
Return 1) Bool array of matched target
|
||||
2) CartesianIndex of every matched target
|
||||
"""
|
||||
function findIndex(input::String, target::Char)
|
||||
match_position = []
|
||||
for i in input
|
||||
if i == target
|
||||
append!(match_position, 1)
|
||||
else
|
||||
append!(match_position, 0)
|
||||
end
|
||||
end
|
||||
match_index = findall(isequal.(match_position, 1))
|
||||
|
||||
return match_position, match_index
|
||||
end
|
||||
|
||||
function findIndex(input::Array, target::Number)
|
||||
match_position = isequal.(input, target)
|
||||
match_index = findall(match_position)
|
||||
return match_position, match_index
|
||||
end
|
||||
|
||||
# function findIndex(input::Array, target::Array)
|
||||
# match_position = isone.(zeros(length(input)))
|
||||
# for i in target
|
||||
# match_position = match_position + isequal.(input, i)
|
||||
# end
|
||||
|
||||
# match_position = replaceMoreThan.(match_position, 1)
|
||||
# match_index = findall(isone.(match_position)) # Findall donot work with Int64 vector [1, 0, 0, 1].
|
||||
# # It only works with BitVector. isone() converts Int64 vector [1, 0, 0, 1] into
|
||||
# # BitVector [1, 0, 0, 1]
|
||||
|
||||
# return match_position, match_index
|
||||
# end
|
||||
|
||||
function findIndex(input::Array, target::Symbol)
|
||||
match_position = isequal.(input, target)
|
||||
match_index = findall(match_position)
|
||||
|
||||
return match_position, match_index
|
||||
end
|
||||
|
||||
function findIndex(collection::Array{String}, target::String)
|
||||
match_position = isequal.(collection, target)
|
||||
match_index = findall(match_position)
|
||||
|
||||
return match_position, match_index
|
||||
end
|
||||
|
||||
function findIndex(collection::Array{String}, target::Array{String})
|
||||
match_position = nothing
|
||||
match_index = nothing
|
||||
|
||||
for i in target
|
||||
match_pos = isequal.(collection, i)
|
||||
match_ind = findall(match_pos)
|
||||
|
||||
if match_position === nothing
|
||||
match_position = match_pos
|
||||
|
||||
else
|
||||
match_position = hcat(match_position, match_pos)
|
||||
end
|
||||
|
||||
if match_index === nothing
|
||||
match_index = match_ind
|
||||
else
|
||||
match_index = hcat(match_index, match_ind)
|
||||
end
|
||||
end
|
||||
|
||||
return match_position, match_index
|
||||
end
|
||||
|
||||
function findIndex(collection::OrderedDict, target::Symbol)
|
||||
collection_keys = keys(collection)
|
||||
collection_keys_array = [i for i in collection_keys]
|
||||
match_position = isequal.(collection_keys_array, target)
|
||||
match_index = findall(match_position)
|
||||
|
||||
return match_position, match_index
|
||||
end
|
||||
|
||||
function findMax(collection::AbstractVector)
|
||||
maxValue, maxIndex = findmax(collection)
|
||||
matchPosition = isequal.(collection, maxValue)
|
||||
return maxValue, maxIndex, matchPosition
|
||||
end
|
||||
|
||||
|
||||
|
||||
""" read_textfile_by_index(folder_path::String, read_file_number::Integer=1)
|
||||
|
||||
with multiple text file in a folder,
|
||||
this function read x_th text file in a folder (filename is sorted by OS)
|
||||
|
||||
# Example
|
||||
utils.read_textfile_by_index(cleaned_data_path, 2)
|
||||
read 2nd txt file in a folder
|
||||
"""
|
||||
function read_textfile_by_index(folder_path::String, read_file_number::Integer=1)
|
||||
if isdir(folder_path)
|
||||
filenumber = length(readdir(folder_path))
|
||||
|
||||
if read_file_number > filenumber
|
||||
error("you specified read_file_number = $read_file_number which is out
|
||||
of range, the cleaned data folder has only $filenumber files")
|
||||
return nothing, nothing, nothing
|
||||
else
|
||||
content = 0
|
||||
# open each file in the directory and read
|
||||
filename = readdir(folder_path, join=true, sort=false)[read_file_number]
|
||||
f = open(filename)
|
||||
content = readlines(f)
|
||||
# content = read(f)
|
||||
close(f)
|
||||
end
|
||||
|
||||
return read_file_number, filename, content
|
||||
else
|
||||
error("ERROR no file or folder at $folder_path")
|
||||
return nothing, nothing, nothing
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
""" Array_to_JSON3_str(data::AbstractArray)
|
||||
|
||||
encode Array to JSON3 String
|
||||
|
||||
# Example
|
||||
|
||||
a = [1.23 4.7889; 9987.1 -123.07; -0.0027 -6.75]
|
||||
json3_str = Array_to_JSON3_str(a)
|
||||
|
||||
json3_str = {"Array":[1.23,9987.1,-0.0027,4.7889,-123.07,-6.75],"size":[3,2]}
|
||||
"""
|
||||
function Array_to_JSON3_str(data::AbstractArray)
|
||||
d = Dict("Array"=> data, "size"=>size(data))
|
||||
json3_str = JSON3.write(d)
|
||||
return json3_str
|
||||
end
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
""" JSON3_str_to_Array(json3_str::String)
|
||||
|
||||
decode JSON3 String to Array
|
||||
|
||||
# Example
|
||||
|
||||
json3_str = {"Array":[1.23,9987.1,-0.0027,4.7889,-123.07,-6.75],"size":[3,2]}
|
||||
a = JSON3_str_to_Array(json3_str)
|
||||
|
||||
a = [1.23 4.7889; 9987.1 -123.07; -0.0027 -6.75]
|
||||
"""
|
||||
function JSON3_str_to_Array(json3_str::String)
|
||||
d = JSON3.read(json3_str)
|
||||
array = reshape(Array(d.Array), (d.size[1], d.size[2]))
|
||||
return array
|
||||
end
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
""" Convert JSON3.read object to OrderedDict
|
||||
|
||||
# Example
|
||||
dict = dictionary(["a"=>4, "b"=>6])
|
||||
OrDict = OrderedDict(dict)
|
||||
jsonString = JSON3.write(OrDict) # use jsonString to exchange. One can save it to file or send it thru pub/sub
|
||||
jsonObject = JSON3.read(jsonString)
|
||||
OrDict2 = JSON3read_to_OrDict(jsonObject) # example here
|
||||
Adict2 = dictionary(OrDict2)
|
||||
|
||||
Andyferris's github https://github.com/andyferris/Dictionaries.jl
|
||||
"""
|
||||
function JSON3read_to_OrDict(x)
|
||||
dict = OrderedDict()
|
||||
for (k, v) in x
|
||||
k = string(k)
|
||||
dict[k] = v
|
||||
end
|
||||
return dict
|
||||
end
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
"""
|
||||
print time of cpu executtion at the line inwhich this macro is used
|
||||
"""
|
||||
macro timeline(expr)
|
||||
quote
|
||||
print("line ", $(__source__.line), ": ")
|
||||
@time $(esc(expr))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
batchindex(batch_counter::Number, batch_size::Number; offset=0) =
|
||||
(offset + (batch_counter-1) * batch_size + 1) : offset + (batch_counter * batch_size)
|
||||
|
||||
function flip_true_false(x::Bool)
|
||||
if x == true
|
||||
x = false
|
||||
elseif x == false
|
||||
x = true
|
||||
else
|
||||
error("undefined condition line $(@__LINE__)")
|
||||
end
|
||||
|
||||
return x
|
||||
end
|
||||
|
||||
function flip_true_false(x::Int)
|
||||
if x == 1
|
||||
x = 0
|
||||
elseif x == 0
|
||||
x = 1
|
||||
else
|
||||
throw("not define input of type $(typeof(x)) yet")
|
||||
end
|
||||
|
||||
return x
|
||||
end
|
||||
|
||||
"""
|
||||
Return drawed index
|
||||
# Example
|
||||
drawed_index = randomWithProb([0.5, 0.2, 0.3])
|
||||
|
||||
"""
|
||||
randomWithProb(probability::AbstractVector) = rand(Distributions.Categorical(probability)) # return drawed index
|
||||
|
||||
"""
|
||||
Draw from choices according to its probability.
|
||||
Probability range is 0.0 to 1.0 and all probability must summed up to 1
|
||||
(may get probability from NNlib's softmax function)
|
||||
|
||||
# Example
|
||||
|
||||
draw = draw_choices([true, false, nothing], [0.5, 0.2, 0.3])
|
||||
"""
|
||||
function randomChoiceWithProb(choices::Array, probability::Array)
|
||||
if length(choices) != length(probability)
|
||||
error("random is not possible, choices array length != probability array length")
|
||||
elseif sum(probability) != 1.0
|
||||
error("probability does not sum to 1.0")
|
||||
end
|
||||
|
||||
return choices[randomWithProb(probability)]
|
||||
end
|
||||
|
||||
function randomChoiceOnTarget(target::Number, targetMatch::Number, choices::AbstractVector,
|
||||
probability::AbstractVector)
|
||||
if length(choices) != length(probability)
|
||||
throw("random is not possible, choices array length != probability array length")
|
||||
end
|
||||
return target == targetMatch ? randomChoiceWithProb(choices, probability) : target
|
||||
# dist = Distributions.Categorical(probability)
|
||||
# draw_result = choices[rand(dist)]
|
||||
end
|
||||
|
||||
function randomChoiceOnTarget(target::AbstractVector, choiceList::AbstractVector,
|
||||
probability::AbstractVector)
|
||||
return randomChoiceOnTarget.(target, 1, (choiceList,), (probability,))
|
||||
end
|
||||
|
||||
function linearly_weighted_avg(a::Array)
|
||||
total = 0.0
|
||||
for (i, v) in enumerate(a)
|
||||
total = total + (i * v)
|
||||
end
|
||||
|
||||
return total / sum(a)
|
||||
end
|
||||
|
||||
|
||||
""" Convert String that is holded inside a variable to Symbol
|
||||
# Example
|
||||
x = "hello" # x is a variable holding String "hello" \n
|
||||
y = variable_to_symbol(x) # y holds :hello
|
||||
"""
|
||||
function variable_str_to_symbol(variable)
|
||||
semi = :($variable)
|
||||
symbol = Symbol(semi)
|
||||
|
||||
return symbol
|
||||
end
|
||||
|
||||
|
||||
""" get useable type of specified fieldname inside a composite struct
|
||||
|
||||
# Example
|
||||
julia> @Base.kwdef mutable struct some_struct
|
||||
a::Union{Bool, Nothing} = nothing
|
||||
b::Union{Float64, Nothing} = nothing
|
||||
c::Union{Int64, AbstractFloat} = 3.5
|
||||
d::Union{String, Nothing} = nothing
|
||||
end
|
||||
|
||||
julia> a = some_struct()
|
||||
julia> fieldname_useable_type(some_struct, :c) =result=> [Int64, Float64]
|
||||
"""
|
||||
function fieldname_useable_type(somestruct, fieldname::Symbol;
|
||||
test_types=[2.0, 2, true, :h, "str", 'c', missing, nothing])::Vector{DataType}
|
||||
new_instance = somestruct()
|
||||
useable_type = []
|
||||
|
||||
for i in test_types
|
||||
try
|
||||
new_instance.:($fieldname) = i
|
||||
type = typeof(new_instance.:($fieldname))
|
||||
if type ∉ useable_type
|
||||
push!(useable_type, type)
|
||||
end
|
||||
catch
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
return useable_type
|
||||
end
|
||||
|
||||
|
||||
function randomNoRepeat(drawOptions::Array, draw_number::Integer;
|
||||
exclude_list::Union{AbstractArray,Nothing}=nothing)
|
||||
draw_option = copy(drawOptions)
|
||||
draw_option = isnothing(exclude_list) ? draw_option :
|
||||
filter!(x -> x ∉ exclude_list, draw_option)
|
||||
shuffle!(draw_option)
|
||||
drawed_items = []
|
||||
while length(drawed_items) < draw_number
|
||||
push!(drawed_items, pop!(draw_option))
|
||||
end
|
||||
return drawed_items
|
||||
end
|
||||
|
||||
""" using cron to schedule backup job by
|
||||
1. sudo nano /etc/crontab <<< this is a system-wide cron file
|
||||
2. to execute julia file @ 2.00am everyday add the following line at the buttom of the file
|
||||
0 2 * * * root julia-1.7 /home/syncthing_backup_script.jl
|
||||
|
||||
Requirements using Dates
|
||||
"""
|
||||
function folderBackup(sourceFolderAbsolutePath::String, # absolute path to folder to be backuped
|
||||
backupFolderAbsolutePath::String; # absolute path to folder used to store backup file
|
||||
totalBackupFiles::Integer=7, # total backup file, the oldest will be deleted
|
||||
containerName::Union{Array{String}, Nothing}=nothing) # container using source_folder
|
||||
|
||||
sep = (Sys.iswindows() ? "\\" : '/')
|
||||
|
||||
if sourceFolderAbsolutePath[end] == sep
|
||||
sourceFolderAbsolutePath = sourceFolderAbsolutePath[1:end-1]
|
||||
end
|
||||
if backupFolderAbsolutePath[end] != sl
|
||||
backupFolderAbsolutePath = backupFolderAbsolutePath * sep
|
||||
end
|
||||
|
||||
if isdir(backupFolderAbsolutePath)
|
||||
else
|
||||
mkpath(backupFolderAbsolutePath)
|
||||
end
|
||||
|
||||
# stop running docker container service
|
||||
if containerName !== nothing
|
||||
println("stop running services")
|
||||
for i in containerName
|
||||
try run(`docker stop $i`) catch; end
|
||||
sleep(10) # wait for services to stop
|
||||
end
|
||||
end
|
||||
|
||||
# do backup
|
||||
println("doing backup now")
|
||||
timestamp = string(Dates.now())
|
||||
name = split(sourceFolderAbsolutePath, sep)[end] * "--"
|
||||
filename = name * timestamp * ".zip" # resulting compressed filename
|
||||
run(`chmod -R a+rwx $sourceFolderAbsolutePath`)
|
||||
# zip -r [destination+filename] [source folder to be zipped]
|
||||
run(`zip -r $(backupFolderAbsolutePath * filename) $sourceFolderAbsolutePath`)
|
||||
|
||||
# check if total backup file is more than user specified, if yes, delete the oldest backup
|
||||
backupFiles = readdir(backupFolderAbsolutePath)
|
||||
|
||||
while length(backupFiles) > totalBackupFiles
|
||||
run(`rm $(backupFolderAbsolutePath * backupFiles[1])`)
|
||||
backupFiles = readdir(backupFolderAbsolutePath)
|
||||
end
|
||||
|
||||
# start docker services
|
||||
if containerName !== nothing
|
||||
println("start services")
|
||||
for i in containerName
|
||||
try run(`docker start $i`) catch; end
|
||||
sleep(10) # wait for services to stop
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lowerclip!(data::AbstractVector, lowerbound::Number)
|
||||
replace!(x -> x < lowerbound ? lowerbound : x, data)
|
||||
end
|
||||
|
||||
function upperclip!(data::AbstractVector, upperbound::Number)
|
||||
replace!(x -> x > upperbound ? upperbound : x, data)
|
||||
end
|
||||
|
||||
function normalise(x::AbstractArray, mu, std)
|
||||
ϵ = oftype(x[1], 1e-5)
|
||||
μ = mu
|
||||
# σ = std(x, dims=dims, mean=μ, corrected=false) # use this when Zygote#478 gets merged
|
||||
σ = std
|
||||
return (x .- μ) ./ (σ .+ ϵ)
|
||||
end
|
||||
|
||||
function minMaxScaler(x::AbstractVector)
|
||||
min = findmin(x)[1]
|
||||
max = findmax(x)[1]
|
||||
|
||||
scaler(a::Number, min::Number, max::Number) = (a-min) / (max-min)
|
||||
return scaler.(x, min, max)
|
||||
end
|
||||
|
||||
""" a = [-1e200, -1e-200, 1e200, 1e-200] \n
|
||||
result = vtclamp.(a, 1e-6, 1e6, -1e6, -1e-6)
|
||||
"""
|
||||
function customclamp(x::Number, poslo::Number, poshi::Number,
|
||||
neglo::Number, neghi::Number)
|
||||
signx = sign(x)
|
||||
if signx == -1
|
||||
if neghi < x < 0
|
||||
return neghi
|
||||
elseif x < neglo
|
||||
return neglo
|
||||
else
|
||||
return x
|
||||
end
|
||||
elseif signx == +1
|
||||
if poshi < x
|
||||
return poshi
|
||||
elseif 0 < x < poslo
|
||||
return poslo
|
||||
else
|
||||
return x
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function unitVec(x::AbstractVector)
|
||||
y = √(sum(x.^2))
|
||||
return x./y
|
||||
end
|
||||
|
||||
function replaceAt!(x::AbstractVector, ind::AbstractVector, value::Number)
|
||||
for i in ind
|
||||
x[i] = value
|
||||
end
|
||||
end
|
||||
|
||||
function signbitVec(x::AbstractVector)
|
||||
sign = signbit.(x) * 1
|
||||
signVec = replace(s -> s == 0 ? -1 : s, sign)
|
||||
return signVec
|
||||
end
|
||||
|
||||
function deleteall!(x::AbstractVector)
|
||||
for i in 1:length(x)
|
||||
deleteat!(x, 1)
|
||||
end
|
||||
end
|
||||
|
||||
""" Select specific range of vectors in a dict, return a new dict
|
||||
# Example
|
||||
dict = Dict(:a => [1:5...],
|
||||
:b => [6:10...])
|
||||
|
||||
call -> selectRange(dict, 1:3)
|
||||
return -> Dict{Any, Any} with 2 entries:
|
||||
:a => [1, 2, 3]
|
||||
:b => [6, 7, 8]
|
||||
"""
|
||||
function selectRange(d::Dict{Symbol, <:AbstractVector}, range)
|
||||
newDict = Dict{Symbol, AbstractVector}()
|
||||
for (k, v) in d
|
||||
newDict[k] = v[range]
|
||||
end
|
||||
|
||||
return newDict
|
||||
end
|
||||
|
||||
""" Assign value to a given Dict by array of keys
|
||||
|
||||
# Example
|
||||
d = Dict(
|
||||
:a1=> Dict(:c=> 5),
|
||||
:a2=> Dict(
|
||||
:k=> 10,
|
||||
:b=> Dict(
|
||||
:s=> "target",
|
||||
)
|
||||
)
|
||||
)
|
||||
index = [:a2, :b, :s] \n
|
||||
assignDict!(d, [:a2, :b, :s], "wow")
|
||||
|
||||
return 1 if no target key in a given dict.
|
||||
"""
|
||||
function assignDict!(dict::Dict, accessArray::Array{Symbol}, valueToAssign)
|
||||
wd = nothing
|
||||
for i in accessArray
|
||||
println(i)
|
||||
if i != accessArray[end]
|
||||
if wd === nothing && haskey(dict, i)
|
||||
wd = Ref(dict[i])
|
||||
elseif wd.x !== nothing && haskey(wd.x, i)
|
||||
wd = Ref(wd.x[i])
|
||||
else
|
||||
return 1 # error, no target key in a given dict.
|
||||
end
|
||||
else
|
||||
wd.x[i] = valueToAssign
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
""" convert hour(0-23), minute(0-59) into julia time object
|
||||
# Example
|
||||
time
|
||||
"""
|
||||
function iTime(h::Integer, m::Integer)
|
||||
if h == 0
|
||||
h = 12
|
||||
ampm = "am"
|
||||
elseif 1 <= h <= 11
|
||||
ampm = "am"
|
||||
elseif h == 12
|
||||
ampm = "pm"
|
||||
elseif 13 <= h <= 23
|
||||
h = h - 12
|
||||
ampm = "pm"
|
||||
else
|
||||
error("hour out of range")
|
||||
end
|
||||
|
||||
m = m < 10 ? "0$m" : m
|
||||
t = "$h:$m$ampm"
|
||||
|
||||
return Time(t, "HH:MMp")
|
||||
end
|
||||
|
||||
""" replace a number according to the limit
|
||||
if value is lower than lowerbound return lowerbound replacement value
|
||||
if value is more than upperbound return upperbound replacement value
|
||||
|
||||
# Example
|
||||
limitvalue(4, (-5 => 0), (5 => 5))
|
||||
"""
|
||||
function limitvalue(v::Number, lowerbound::Pair, upperbound::Pair)
|
||||
lwLimit, lwReplace = lowerbound
|
||||
upLimit, upReplace = upperbound
|
||||
|
||||
if v < lwLimit
|
||||
v = lwReplace
|
||||
elseif v > upLimit
|
||||
v = upReplace
|
||||
else
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
""" assign matrix b to matrix a according to matrix b's CartesianIndex
|
||||
"""
|
||||
cartesianAssign!(a::CuArray, b::CuArray) = @cuda cartesianAssign!(a, b)
|
||||
|
||||
function cartesianAssign!(a, b)
|
||||
for (i, v) in enumerate(b)
|
||||
a[CartesianIndices(b)[i].I...] = v
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
function sumAlongDim3(a::Array)
|
||||
totalDim = length(size(a))
|
||||
|
||||
if totalDim == 3
|
||||
d1, d2, d3 = size(a)
|
||||
r = zeros(1, 1, d3)
|
||||
for i in 1:d3
|
||||
view(r, 1, 1, i) .= sum(a[:, :, i])
|
||||
end
|
||||
elseif totalDim == 4
|
||||
d1, d2, d3, d4 = size(a)
|
||||
r = zeros(1, 1, d3, d4)
|
||||
for j in 1:d4
|
||||
for i in 1:d3
|
||||
view(r, 1, 1, i, j) .= sum(a[:, :, i, j])
|
||||
end
|
||||
end
|
||||
else
|
||||
error("this condition is not define yet")
|
||||
end
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
|
||||
""" ELEMENT-wise multiply of each slice of 3D input matrix ,a, to all slice of 3D another matrix ,b, and
|
||||
concatenate at the 4th dimension.
|
||||
|
||||
Example
|
||||
julia> input = rand(32, 32, 128) # batch at 3rd dim
|
||||
julia> weight = rand(32, 32, 1024)
|
||||
julia> r = matMul_3Dto3D_manyTo1batch(input, weight);
|
||||
julia> size(r)
|
||||
(32, 32, 1024, 128)
|
||||
"""
|
||||
function matMul_3Dto3D_manyTo1batch(a::Array, b::Array; resultStorage::Union{Array, Nothing}=nothing)
|
||||
asize = [size(a)...]
|
||||
bsize = [size(b)...]
|
||||
if resultStorage === nothing
|
||||
resultStorage = similar(a, eltype(b), bsize[1], bsize[2], bsize[3], asize[3])
|
||||
end
|
||||
|
||||
c = [slice .* b for slice in eachslice(a, dims=3)]
|
||||
resultStorage .= cat(c..., dims=4)
|
||||
|
||||
return resultStorage
|
||||
end
|
||||
|
||||
# function matMul_3Dto3D_manyTo1batch(a::CuArray, b::CuArray; # XXX working code
|
||||
# resultStorage::Union{CuArray, Nothing}=nothing, threads=256)
|
||||
# asize = [size(a)...]
|
||||
# bsize = [size(b)...]
|
||||
# if resultStorage === nothing
|
||||
# resultStorage = similar(a, eltype(b), bsize[1], bsize[2], bsize[3], asize[3]) |> gpu
|
||||
# end
|
||||
# CUDA.@sync begin
|
||||
# @cuda threads=threads matMul_3Dto3D_manyTo1batch_gpu!(a, b, resultStorage)
|
||||
# end
|
||||
# return resultStorage
|
||||
# end
|
||||
|
||||
# function matMul_3Dto3D_manyTo1batch_gpu!(a, b, resultStorage) # XXX working code
|
||||
# _, _, _, p = size(resultStorage)
|
||||
|
||||
# index = threadIdx().x # this example only requires linear indexing, so just use `x`
|
||||
# stride = blockDim().x
|
||||
# for i in index:stride:p
|
||||
# view(resultStorage, :, :, :, i) .= view(a, :, :, i) .* b
|
||||
# end
|
||||
# return nothing
|
||||
# end
|
||||
|
||||
""" GPU kernel
|
||||
"""
|
||||
function matMul_3Dto3D_manyTo1batch_gpu!(a, b, resultStorage)
|
||||
_, _, batch = size(a) # This kernel use 1 thread per batch
|
||||
i = (blockIdx().x - 1) * blockDim().x + threadIdx().x
|
||||
if i <= batch # guard against unused threads to accessing memory out of bound
|
||||
# @cuprintln("thread $i")
|
||||
view(resultStorage, :, :, :, i) .= view(a, :, :, i) .* b
|
||||
end
|
||||
return nothing
|
||||
end
|
||||
|
||||
""" GPU version of batchMatEleMul
|
||||
|
||||
Example
|
||||
julia> using Flux, CUDA
|
||||
julia> device = Flux.CUDA.functional() ? gpu : cpu
|
||||
julia> if device == gpu CUDA.device!(0) end
|
||||
julia> input = rand(32, 32, 128) |> gpu; # 128-batches
|
||||
julia> weight = rand(32, 32, 1024) |> gpu; # 1-batch
|
||||
julia> r = matMul_3Dto3D_manyTo1batch(input, weight);
|
||||
julia> size(r)
|
||||
(32, 32, 1024, 128)
|
||||
"""
|
||||
function matMul_3Dto3D_manyTo1batch(a::CuArray, b::CuArray;
|
||||
resultStorage::Union{CuArray, Nothing}=nothing)
|
||||
asize = [size(a)...]
|
||||
bsize = [size(b)...]
|
||||
if resultStorage === nothing
|
||||
resultStorage = similar(a, eltype(b), bsize[1], bsize[2], bsize[3], asize[3]) |> gpu
|
||||
end
|
||||
|
||||
kernel = @cuda launch=false matMul_3Dto3D_manyTo1batch_gpu!(a, b, resultStorage)
|
||||
config = launch_configuration(kernel.fun)
|
||||
|
||||
# threads to be launched. Since one can't launch exact thread number the kernel needs,
|
||||
# one just launch threads more than this kernel needs then use a guard inside the kernel
|
||||
# to prevent unused threads to access memory.
|
||||
threads = min(1024, config.threads) # most NVIDIA gpu has 1024 threads per block
|
||||
blocks = cld(asize[3], threads) # This kernel use 1 thread per batch
|
||||
|
||||
CUDA.@sync begin
|
||||
kernel(a, b, resultStorage; threads, blocks)
|
||||
end
|
||||
return resultStorage
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
""" ELEMENT-wise multiply of each slice of 3D input matrix ,a, to all batch of another 4D matrix ,b, and
|
||||
concatenate at the 4th dimension.
|
||||
|
||||
Example
|
||||
julia>
|
||||
julia> a = rand(2,2,3) # 3-batches
|
||||
julia> b = rand(2,2,4,3) # 3-batches
|
||||
julia> r = GeneralUtils.matMul_3Dto4D_batchwise(a, b);
|
||||
julia> size(r)
|
||||
(2, 2, 4, 3)
|
||||
"""
|
||||
function matMul_3Dto4D_batchwise(a::Array, b::Array; resultStorage::Union{Array, Nothing}=nothing)
|
||||
asize = [size(a)...]
|
||||
bsize = [size(b)...]
|
||||
if asize[end] != bsize[end]
|
||||
error("batch number of a and b must be equal")
|
||||
end
|
||||
if resultStorage === nothing
|
||||
resultStorage = zeros(bsize[1], bsize[2], bsize[3], asize[3])
|
||||
end
|
||||
for i in 1:asize[3]
|
||||
view(resultStorage, :, :, :, i) .= a[:, :, i] .* b[:, :, :, i]
|
||||
end
|
||||
return resultStorage
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # module
|
||||
Reference in New Issue
Block a user