mirror of
https://github.com/Cian-H/nanoconc.git
synced 2025-12-23 22:52:01 +00:00
Implemented property based testing via PyCall.jl and hypothesis.py
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
[deps]
|
||||
AirspeedVelocity = "1c8270ee-6884-45cc-9545-60fa71ec23e4"
|
||||
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
||||
PropCheck = "ca382230-33be-11e9-0059-d981d03070e4"
|
||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||
|
||||
@@ -67,10 +67,10 @@ if abspath(PROGRAM_FILE) == @__FILE__
|
||||
current_package_version = Pkg.TOML.parsefile("$ROOT_DIR/Project.toml")["version"]
|
||||
|
||||
function display_to_file(io, x)
|
||||
show(IOContext(io, :limit => false), "text/plain", x)
|
||||
show(IOContext(io, :limit => false, :color => true), "text/plain", x)
|
||||
end
|
||||
|
||||
open("$ROOT_DIR/benchmarks/$current_package_version.txt", "w") do io
|
||||
open("$ROOT_DIR/benchmarks/$current_package_version.ansi", "w") do io
|
||||
println(io, "C Implementation")
|
||||
display_to_file(io, result[1])
|
||||
println(io, "\n\nFortran Implementation")
|
||||
|
||||
@@ -53,6 +53,6 @@ done
|
||||
|
||||
# And, finally, we can compile the C, and Fortran implementations
|
||||
cd $bhmie_dir
|
||||
gcc -shared -fPIC -o bhmie-c/bhmie.so bhmie-c/bhmie.c bhmie-c/complex.c bhmie-c/nrutil.c -lm -Wno-builtin-declaration-mismatch -Wno-implicit-function-declaration
|
||||
gfortran -shared -fPIC -o bhmie-f/bhmie.so bhmie-f/bhmie.f
|
||||
gfortran -shared -fPIC -o bhmie-f/bhmie_f77.so bhmie-f/bhmie_f77.f
|
||||
gcc -g -shared -fPIC -o bhmie-c/bhmie.so bhmie-c/bhmie.c bhmie-c/complex.c bhmie-c/nrutil.c -lm -Wno-builtin-declaration-mismatch -Wno-implicit-function-declaration
|
||||
gfortran -g -shared -fPIC -o bhmie-f/bhmie.so bhmie-f/bhmie.f
|
||||
gfortran -g -shared -fPIC -o bhmie-f/bhmie_f77.so bhmie-f/bhmie_f77.f
|
||||
@@ -1,9 +1,13 @@
|
||||
using Test
|
||||
using Random
|
||||
using PropCheck
|
||||
using Debugger
|
||||
using PyCall
|
||||
|
||||
|
||||
include("../anchors.jl")
|
||||
|
||||
import .Anchors: TEST_DIR, SRC_DIR
|
||||
import .Anchors: TEST_DIR, SRC_DIR, ROOT_DIR
|
||||
|
||||
if !@isdefined TestUtils
|
||||
include(joinpath(TEST_DIR, "testutils.jl"))
|
||||
@@ -15,85 +19,129 @@ if !@isdefined FFIWraps
|
||||
include(joinpath(TEST_DIR, "ffi_wraps.jl"))
|
||||
end
|
||||
|
||||
# function julia_vs_c(args::Tuple{Float64, Float64, Float64, UInt32, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}})
|
||||
# x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im = args
|
||||
function julia_vs_c(x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im)
|
||||
cxref, cxs1, cxs2 = ComplexF64(cxref_re, cxref_im), ComplexF64.(cxs1_re, cxs1_im), ComplexF64.(cxs2_re, cxs2_im)
|
||||
x_c, cxref_c, nang_c, cxs1_c, cxs2_c = Float32(x), ComplexF32(cxref), UInt32(nang), ComplexF32.(cxs1), ComplexF32.(cxs2)
|
||||
return isapprox(
|
||||
miemfp.bhmie(x, cxref, nang),
|
||||
FFIWraps.bhmie_c(x_c, cxref_c, nang_c, cxs1_c, cxs2_c),
|
||||
rtol=0.1,
|
||||
)
|
||||
|
||||
# Set up the Python environment
|
||||
run(`$ROOT_DIR/setup_venv.sh`)
|
||||
ENV["PYTHON"] = joinpath(ROOT_DIR, ".venv/bin/python")
|
||||
|
||||
@pyinclude(joinpath(TEST_DIR, "miemfp_tests.py"))
|
||||
|
||||
|
||||
miemfp.bhmie(
|
||||
x::Float64,
|
||||
cxref::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
) = miemfp.bhmie(x, cxref, UInt32(nang))
|
||||
|
||||
function miemfp.bhmie(
|
||||
x::Float64,
|
||||
cxref::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
event::PyObject,
|
||||
)
|
||||
event.clear()
|
||||
result = miemfp.bhmie(x, cxref, nang, s1, s2)
|
||||
event.set()
|
||||
return result
|
||||
end
|
||||
|
||||
# function julia_vs_fortran(args::Tuple{Float64, Float64, Float64, UInt32, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}})
|
||||
# x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im = args
|
||||
function julia_vs_fortran(x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im)
|
||||
cxref, cxs1, cxs2 = ComplexF64(cxref_re, cxref_im), ComplexF64.(cxs1_re, cxs1_im), ComplexF64.(cxs2_re, cxs2_im)
|
||||
x_f, cxref_f, nang_f, cxs1_f, cxs2_f = Float32(x), ComplexF32(cxref), Int32(nang), ComplexF32.(cxs1), ComplexF32.(cxs2)
|
||||
b = miemfp.bhmie(x, cxref, nang)
|
||||
f = FFIWraps.bhmie_fortran(x_f, cxref_f, nang_f, cxs1_f, cxs2_f)
|
||||
# open("bhmie_julia_vs_fortran.txt", "a") do io
|
||||
# println(io, "julia: ", b)
|
||||
# println(io, "fortran: ", f)
|
||||
# end
|
||||
# return is_approx(b, f)
|
||||
return isapprox(
|
||||
miemfp.bhmie(x, cxref, nang),
|
||||
FFIWraps.bhmie_fortran(x_f, cxref_f, nang_f, cxs1_f, cxs2_f),
|
||||
rtol=0.1,
|
||||
)
|
||||
end
|
||||
|
||||
# function julia_vs_fortran77(args::Tuple{Float64, Float64, Float64, UInt32, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}})
|
||||
# x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im = args
|
||||
function julia_vs_fortran77(x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im)
|
||||
cxref, cxs1, cxs2 = ComplexF64(cxref_re, cxref_im), ComplexF64.(cxs1_re, cxs1_im), ComplexF64.(cxs2_re, cxs2_im)
|
||||
x_f, cxref_f, nang_f, cxs1_f, cxs2_f = Float32(x), ComplexF32(cxref), Int32(nang), ComplexF32.(cxs1), ComplexF32.(cxs2)
|
||||
return isapprox(
|
||||
miemfp.bhmie(x, cxref, nang),
|
||||
FFIWraps.bhmie_fortran77(x_f, cxref_f, nang_f, cxs1_f, cxs2_f),
|
||||
rtol=0.1,
|
||||
)
|
||||
end
|
||||
|
||||
f64_gen = PropCheck.itype(Float64)
|
||||
UInt32_gen = PropCheck.itype(UInt32)
|
||||
f64_vec_gen = PropCheck.vector(isample(1:100), f64_gen)
|
||||
bhmie_gen = PropCheck.interleave(
|
||||
f64_gen,
|
||||
f64_gen,
|
||||
f64_gen,
|
||||
UInt32_gen,
|
||||
f64_vec_gen,
|
||||
f64_vec_gen,
|
||||
f64_vec_gen,
|
||||
f64_vec_gen,
|
||||
FFIWraps.bhmie_fortran(
|
||||
x::Float64,
|
||||
refrel::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
) = FFIWraps.bhmie_fortran(
|
||||
Float32(x),
|
||||
ComplexF32(refrel),
|
||||
Int32(nang),
|
||||
ComplexF32.(s1),
|
||||
ComplexF32.(s2),
|
||||
)
|
||||
|
||||
@testset "bhmie" begin
|
||||
function FFIWraps.bhmie_fortran(
|
||||
x::Float64,
|
||||
refrel::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
event::PyObject,
|
||||
)
|
||||
event.clear()
|
||||
result = FFIWraps.bhmie_fortran(x, refrel, nang, s1, s2)
|
||||
event.set()
|
||||
return result
|
||||
end
|
||||
|
||||
FFIWraps.bhmie_fortran77(
|
||||
x::Float64,
|
||||
refrel::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
) = FFIWraps.bhmie_fortran77(
|
||||
Float32(x),
|
||||
ComplexF32(refrel),
|
||||
Int32(nang),
|
||||
ComplexF32.(s1),
|
||||
ComplexF32.(s2),
|
||||
)
|
||||
|
||||
function FFIWraps.bhmie_fortran77(
|
||||
x::Float64,
|
||||
refrel::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
event::PyObject,
|
||||
)
|
||||
event.clear()
|
||||
result = FFIWraps.bhmie_fortran77(x, refrel, nang, s1, s2)
|
||||
event.set()
|
||||
return result
|
||||
end
|
||||
|
||||
FFIWraps.bhmie_c(
|
||||
x::Float64,
|
||||
refrel::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
) = FFIWraps.bhmie_c(
|
||||
Float32(x),
|
||||
ComplexF32(refrel),
|
||||
UInt32(nang),
|
||||
ComplexF32.(s1),
|
||||
ComplexF32.(s2),
|
||||
)
|
||||
|
||||
function FFIWraps.bhmie_c(
|
||||
x::Float64,
|
||||
refrel::ComplexF64,
|
||||
nang::Int64,
|
||||
s1::Vector{ComplexF64},
|
||||
s2::Vector{ComplexF64},
|
||||
event::PyObject,
|
||||
)
|
||||
event.clear()
|
||||
result = FFIWraps.bhmie_c(x, refrel, nang, s1, s2)
|
||||
event.set()
|
||||
return result
|
||||
end
|
||||
|
||||
@testset "miemfp" begin
|
||||
@testset "miemfp.bhmie" begin
|
||||
c_check = PropCheck.check(julia_vs_c, bhmie_gen)
|
||||
c_result = c_check == true
|
||||
if !c_result
|
||||
println("Fail vs C, PropCheck:")
|
||||
display(c_check)
|
||||
end
|
||||
@test c_result
|
||||
f_check = PropCheck.check(julia_vs_fortran, bhmie_gen)
|
||||
f_result = f_check == true
|
||||
if !f_result
|
||||
println("Fail vs Fortran, PropCheck:")
|
||||
display(f_check)
|
||||
end
|
||||
@test f_result
|
||||
f77_check = PropCheck.check(julia_vs_fortran77, bhmie_gen)
|
||||
f77_result = f77_check == true
|
||||
if !f77_result
|
||||
println("Fail vs Fortran77, PropCheck:")
|
||||
display(f77_check)
|
||||
end
|
||||
@test f77_result
|
||||
event1, event2 = py"asyncio.Event"(), py"asyncio.Event"()
|
||||
event1.set(), event2.set()
|
||||
result, output = py"compare_bhmie_functions"(miemfp.bhmie, FFIWraps.bhmie_fortran, event1, event2)
|
||||
@test result
|
||||
result, output = py"compare_bhmie_functions"(miemfp.bhmie, FFIWraps.bhmie_fortran77, event1, event2)
|
||||
@test result
|
||||
result, output = py"compare_bhmie_functions"(miemfp.bhmie, FFIWraps.bhmie_c, event1, event2)
|
||||
@test result
|
||||
end
|
||||
end
|
||||
49
test/miemfp_tests.py
Normal file
49
test/miemfp_tests.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from typing import List, Tuple
|
||||
import asyncio
|
||||
import numpy as np
|
||||
from hypothesis import errors, given, settings, strategies as st # type: ignore
|
||||
|
||||
def compare_bhmie_functions(f1, f2, event1: asyncio.Event, event2: asyncio.Event) -> Tuple[bool, str]:
|
||||
async def async_closure(
|
||||
x: float,
|
||||
cxref: Tuple[float, float],
|
||||
cxs1: List[Tuple[float, float]],
|
||||
cxs2: List[Tuple[float, float]],
|
||||
) -> bool:
|
||||
cxref = complex(*cxref)
|
||||
cxs1 = [complex(*c) for c in cxs1]
|
||||
cxs2 = [complex(*c) for c in cxs2]
|
||||
|
||||
# This is to ensure that only one instance of each function is running at a time
|
||||
# to avoid memory issues in the FFI code
|
||||
await event1.wait()
|
||||
f1_result = f1(x, cxref, 2, cxs1, cxs2)[:2]
|
||||
await event2.wait()
|
||||
f2_result = f2(x, cxref, 2, cxs1, cxs2)[:2]
|
||||
|
||||
return np.all(np.isclose(f1_result, f2_result))
|
||||
|
||||
@settings(deadline=None)
|
||||
@given(
|
||||
# Must be bigger than an atom but still nanoscale
|
||||
x=st.floats(min_value=0.1, max_value=100),
|
||||
# Refractive indeces must be within a physically reasonable range
|
||||
cxref=st.tuples(st.floats(min_value=0.1, max_value=4.0), st.floats(min_value=0.1, max_value=4.0)),
|
||||
cxs1=st.lists(st.tuples(st.floats(min_value=0.1, allow_infinity=False), st.floats(min_value=0.1, allow_infinity=False)), min_size=10, max_size=100),
|
||||
cxs2=st.lists(st.tuples(st.floats(min_value=0.1, allow_infinity=False), st.floats(min_value=0.1, allow_infinity=False)), min_size=10, max_size=100),
|
||||
)
|
||||
def sync_closure(
|
||||
x: float,
|
||||
cxref: Tuple[float, float],
|
||||
cxs1: List[Tuple[float, float]],
|
||||
cxs2: List[Tuple[float, float]],
|
||||
) -> bool:
|
||||
assert asyncio.run(async_closure(x, cxref, cxs1, cxs2))
|
||||
|
||||
try:
|
||||
sync_closure()
|
||||
return True, "Test passed"
|
||||
except AssertionError as e:
|
||||
return False, f"AssertionError: {str(e)}"
|
||||
except errors.HypothesisException as e:
|
||||
return False, f"HypothesisException: {str(e)}"
|
||||
@@ -22,4 +22,9 @@ function test_from_serialized(fn::Function, filename::String)
|
||||
@test deep_compare([fn(a...; kw...) for (a, kw) in argskwargs], out)
|
||||
end
|
||||
|
||||
end # module TestUtils
|
||||
function asymmetric_floatvec_to_complexvec(vec_a::Vector{Float32}, vec_b::Vector{Float32})
|
||||
shortest = min(length(vec_a), length(vec_b))
|
||||
ComplexF32.(vec_a[1:shortest], vec_b[1:shortest])
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user