version Lux.jl:
using Lux, Random
struct CActivation <: Function
ptr::Ptr{Cvoid}
end
@inline (f::CActivation)(x::Float64) =
ccall(f.ptr, Cdouble, (Cdouble,), x)
const PS = @NamedTuple{
weight::Matrix{Float64},
bias::Vector{Float64}
}
struct Network{L1,L2}
l1::L1
l2::L2
p1::PS
p2::PS
ni::Int64
no::Int64
end
const L1 = typeof(Lux.Dense(1 => 1, CActivation(C_NULL)))
const L2 = typeof(Lux.Dense(1 => 1))
const NNType = Network{L1,L2}
const NETWORKS = Dict{Int64,NNType}()
const NEXT_ID = Ref{Int64}(0)
function build_nn(ni::Int64, no::Int64, nh::Int64,
act::Ptr{Cvoid})
rng = Random.Xoshiro(0)
l1 = Lux.Dense(ni => nh, CActivation(act))
l2 = Lux.Dense(nh => no)
p1 = (
weight = randn(rng, Float64, nh, ni) ./ sqrt(ni),
bias = zeros(Float64, nh)
)
p2 = (
weight = randn(rng, Float64, no, nh) ./ sqrt(nh),
bias = zeros(Float64, no)
)
return Network(l1, l2, p1, p2, ni, no)
end
Base.@ccallable function make_nn(
ni::Int64, no::Int64, nh::Int64,
act::Ptr{Cvoid}
)::Int64
(ni > 0 && no > 0 && nh > 0 && act != C_NULL) ||
return 0
nn = build_nn(ni, no, nh, act)
id = (NEXT_ID[] += 1)
NETWORKS[id] = nn
return id
end
Base.@ccallable function run_nn(
id::Int64,
px::Ptr{Float64},
py::Ptr{Float64},
batchsize::Int64
)::Cint
haskey(NETWORKS, id) || return -1
(px != C_NULL && py != C_NULL && batchsize > 0) ||
return -1
nn = NETWORKS[id]
x = unsafe_wrap(Matrix{Float64}, px, (nn.ni, batchsize))
y = unsafe_wrap(Matrix{Float64}, py, (nn.no, batchsize))
h, _ = nn.l1(x, nn.p1, NamedTuple())
result, _ = nn.l2(h, nn.p2, NamedTuple())
copyto!(y, result)
return 0
end
Base.@ccallable function free_nn(id::Int64)::Cvoid
delete!(NETWORKS, id)
return nothing
end
with trimming:
% juliac --output-lib libcarre --project=. main.jl --trim
✓ Compiling...
% gfortran main.f90 -L. -lcarre -o test_nn
% ./test_nn
Inputs:
1.0000000000000000 2.0000000000000000 3.0000000000000000 4.0000000000000000 5.0000000000000000 6.0000000000000000
Outputs:
0.11682082443574934 0.26708492893181329 0.41734903342787721