Skip to content

More complete tests #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 113 additions & 76 deletions src/IntelVectorMath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,130 @@ function __init__()
end
end

## list all functions
# all functions that work for Floats and ComplexFloats
unary_real_complex = (
(:acos, :acos!, :Acos),
(:asin, :asin!, :Asin),
(:acosh, :acosh!, :Acosh),
(:asinh, :asinh!, :Asinh),
(:sqrt, :sqrt!, :Sqrt),
(:exp, :exp!, :Exp),
(:log, :log!, :Ln),
)

binary_real_complex = (
(:pow, :pow!, :Pow, true),
(:divide, :divide!, :Div, true),
)

# all functions that work for Floats only
unary_real = (
(:cbrt, :cbrt!, :Cbrt),
(:expm1, :expm1!, :Expm1),
(:log1p, :log1p!, :Log1p),
(:log2, :log2!, :Log2),
(:abs, :abs!, :Abs),
(:abs2, :abs2!, :Sqr),
(:ceil, :ceil!, :Ceil),
(:floor, :floor!, :Floor),
(:round, :round!, :Round),
(:trunc, :trunc!, :Trunc),
(:cospi, :cospi!, :Cospi),
(:sinpi, :sinpi!, :Sinpi),
(:tanpi, :tanpi!, :Tanpi),
(:acospi, :acospi!, :Acospi),
(:asinpi, :asinpi!, :Asinpi),
(:atanpi, :atanpi!, :Atanpi),
(:cosd, :cosd!, :Cosd),
(:sind, :sind!, :Sind),
(:tand, :tand!, :Tand),
# Enabled only for Real. MKL guarantees higher accuracy, but at a
# substantial performance cost.
(:atan, :atan!, :Atan),
(:cos, :cos!, :Cos),
(:sin, :sin!, :Sin),
(:tan, :tan!, :Tan),
(:atanh, :atanh!, :Atanh),
(:cosh, :cosh!, :Cosh),
(:sinh, :sinh!, :Sinh),
(:tanh, :tanh!, :Tanh),
(:log10, :log10!, :Log10),
# now in SpecialFunctions (make smart, maybe?)
(:erf, :erf!, :Erf),
(:erfc, :erfc!, :Erfc),
(:erfinv, :erfinv!, :ErfInv),
(:erfcinv, :erfcinv!, :ErfcInv),
(:lgamma, :lgamma!, :LGamma),
(:gamma, :gamma!, :TGamma),
# Not in Base
(:inv_cbrt, :inv_cbrt!, :InvCbrt),
(:inv_sqrt, :inv_sqrt!, :InvSqrt),
(:pow2o3, :pow2o3!, :Pow2o3),
(:pow3o2, :pow3o2!, :Pow3o2),
)

binary_real = (
(:atan, :atan!, :Atan2, false),
(:hypot, :hypot!, :Hypot, false),
# Not in Base
(:atanpi, :atanpi!, :Atan2pi, false),
)

unary_complex_in = (
(:abs, :abs!, :Abs),
(:angle, :angle!, :Arg),
)

unary_complex_inout = (
(:conj, :conj!, :Conj),
)

## define functions from previous list for all eligible input types

for t in (Float32, Float64, ComplexF32, ComplexF64)
# Unary, real or complex
def_unary_op(t, t, :acos, :acos!, :Acos)
def_unary_op(t, t, :asin, :asin!, :Asin)
def_unary_op(t, t, :acosh, :acosh!, :Acosh)
def_unary_op(t, t, :asinh, :asinh!, :Asinh)
def_unary_op(t, t, :sqrt, :sqrt!, :Sqrt)
def_unary_op(t, t, :exp, :exp!, :Exp)
def_unary_op(t, t, :log, :log!, :Ln)
for (f, f!, f_mkl) in unary_real_complex
def_unary_op(t, t, f, f!, f_mkl)
end

# # Binary, real or complex
def_binary_op(t, t, :pow, :pow!, :Pow, true)
def_binary_op(t, t, :divide, :divide!, :Div, true)
for (f, f!, f_mkl, broadcast) in binary_real_complex
def_binary_op(t, t, f, f!, f_mkl, broadcast)
end
end

for t in (Float32, Float64)
# Unary, real-only
def_unary_op(t, t, :cbrt, :cbrt!, :Cbrt)
def_unary_op(t, t, :expm1, :expm1!, :Expm1)
def_unary_op(t, t, :log1p, :log1p!, :Log1p)
def_unary_op(t, t, :log2, :log2!, :Log2)
def_unary_op(t, t, :abs, :abs!, :Abs)
def_unary_op(t, t, :abs2, :abs2!, :Sqr)
def_unary_op(t, t, :ceil, :ceil!, :Ceil)
def_unary_op(t, t, :floor, :floor!, :Floor)
def_unary_op(t, t, :round, :round!, :Round)
def_unary_op(t, t, :trunc, :trunc!, :Trunc)
# Unary, real only
for (f, f!, f_mkl) in unary_real
def_unary_op(t, t, f, f!, f_mkl)
end

# Enabled only for Real. MKL guarantees higher accuracy, but at a
# substantial performance cost.
def_unary_op(t, t, :atan, :atan!, :Atan)
def_unary_op(t, t, :cos, :cos!, :Cos)
def_unary_op(t, t, :sin, :sin!, :Sin)
def_unary_op(t, t, :tan, :tan!, :Tan)
def_unary_op(t, t, :atanh, :atanh!, :Atanh)
def_unary_op(t, t, :cosh, :cosh!, :Cosh)
def_unary_op(t, t, :sinh, :sinh!, :Sinh)
def_unary_op(t, t, :tanh, :tanh!, :Tanh)
def_unary_op(t, t, :log10, :log10!, :Log10)

# Unary, real-only
def_unary_op(t, t, :cospi, :cospi!, :Cospi)
def_unary_op(t, t, :sinpi, :sinpi!, :Sinpi)
def_unary_op(t, t, :tanpi, :tanpi!, :Tanpi)
def_unary_op(t, t, :acospi, :acospi!, :Acospi)
def_unary_op(t, t, :asinpi, :asinpi!, :Asinpi)
def_unary_op(t, t, :atanpi, :atanpi!, :Atanpi)
def_unary_op(t, t, :cosd, :cosd!, :Cosd)
def_unary_op(t, t, :sind, :sind!, :Sind)
def_unary_op(t, t, :tand, :tand!, :Tand)
for (f, f!, f_mkl, broadcast) in binary_real
def_binary_op(t, t, f, f!, f_mkl, broadcast)
end

# Unary, complex-only
for (f, f!, f_mkl) in unary_complex_inout
def_unary_op(Complex{t}, Complex{t}, f, f!, f_mkl)
end
for (f, f!, f_mkl) in unary_complex_in
def_unary_op(Complex{t}, t, f, f!, f_mkl)
end

### cis is special, IntelVectorMath function is based on output
def_unary_op(t, Complex{t}, :cis, :cis!, :CIS; vmltype=Complex{t})

def_one2two_op(t, t, :sincos, :sincos!, :SinCos)

# now in SpecialFunctions (make smart, maybe?)
def_unary_op(t, t, :erf, :erf!, :Erf)
def_unary_op(t, t, :erfc, :erfc!, :Erfc)
def_unary_op(t, t, :erfinv, :erfinv!, :ErfInv)
def_unary_op(t, t, :erfcinv, :erfcinv!, :ErfcInv)
def_unary_op(t, t, :lgamma, :lgamma!, :LGamma)
def_unary_op(t, t, :gamma, :gamma!, :TGamma)
# Not in Base
def_unary_op(t, t, :inv_cbrt, :inv_cbrt!, :InvCbrt)
def_unary_op(t, t, :inv_sqrt, :inv_sqrt!, :InvSqrt)
def_unary_op(t, t, :pow2o3, :pow2o3!, :Pow2o3)
def_unary_op(t, t, :pow3o2, :pow3o2!, :Pow3o2)
# Binary, complex-only. These are more accurate but performance is
# either equivalent to Base or slower.
# def_binary_op(Complex{t}, Complex{t}, (:+), :add!, :Add, false)
# def_binary_op(Complex{t}, Complex{t}, (:.+), :add!, :Add, true)
# def_binary_op(Complex{t}, Complex{t}, (:.*), :multiply!, :Mul, true)
# def_binary_op(Complex{t}, Complex{t}, (:-), :subtract!, :Sub, false)
# def_binary_op(Complex{t}, Complex{t}, (:.-), :subtract!, :Sub, true)
# def_binary_op(Complex{t}, Complex{t}, :multiply_conj, :multiply_conj!, :Mul, false)

# # .^ to scalar power
# mklfn = Base.Meta.quot(Symbol("$(vml_prefix(t))Powx"))
Expand All @@ -98,28 +157,6 @@ for t in (Float32, Float64)
# out
# end
# end

# # Binary, real-only
def_binary_op(t, t, :atan, :atan!, :Atan2, false)
def_binary_op(t, t, :atanpi, :atanpi!, :Atan2pi, false)
def_binary_op(t, t, :hypot, :hypot!, :Hypot, false)

# Unary, complex-only
def_unary_op(Complex{t}, Complex{t}, :conj, :conj!, :Conj)
def_unary_op(Complex{t}, t, :abs, :abs!, :Abs)
def_unary_op(Complex{t}, t, :angle, :angle!, :Arg)

### cis is special, IntelVectorMath function is based on output
def_unary_op(t, Complex{t}, :cis, :cis!, :CIS; vmltype = Complex{t})

# Binary, complex-only. These are more accurate but performance is
# either equivalent to Base or slower.
# def_binary_op(Complex{t}, Complex{t}, (:+), :add!, :Add, false)
# def_binary_op(Complex{t}, Complex{t}, (:.+), :add!, :Add, true)
# def_binary_op(Complex{t}, Complex{t}, (:.*), :multiply!, :Mul, true)
# def_binary_op(Complex{t}, Complex{t}, (:-), :subtract!, :Sub, false)
# def_binary_op(Complex{t}, Complex{t}, (:.-), :subtract!, :Sub, true)
# def_binary_op(Complex{t}, Complex{t}, :multiply_conj, :multiply_conj!, :Mul, false)
end

export VML_LA, VML_HA, VML_EP, vml_set_accuracy, vml_get_accuracy
Expand Down
56 changes: 46 additions & 10 deletions test/common.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
using SpecialFunctions
# unary functions that accept floats as inputs
const base_unary_real = (
(Base, :acos, (-1, 1)),
(Base, :acospi, (-1, 1)),
(Main, :acospi, (-1, 1)),
(Base, :asin, (-1, 1)),
(Base, :asinpi, (-1, 1)),
(Main, :asinpi, (-1, 1)),
(Base, :atan, (-50, 50)),
(Base, :atanpi, (-50, 50)),
(Main, :atanpi, (-50, 50)),
(Base, :cos, (-1000, 1000)),
(Base, :cosd, (-10000, 10000)),
(Base, :cospi, (-300, 300)),
Expand All @@ -14,7 +14,7 @@ const base_unary_real = (
(Base, :sinpi, (-300, 300)),
(Base, :tan, (-1000, 1000)),
(Base, :tand, (-10000, 10000)),
(Base, :tanpi, (-300, 300)),
(Main, :tanpi, (-300, 300)),
(Base, :acosh, (1, 1000)),
(Base, :asinh, (-1000, 1000)),
(Base, :atanh, (-1, 1)),
Expand All @@ -41,12 +41,19 @@ const base_unary_real = (
(SpecialFunctions, :erfinv, (-1, 1)),
(SpecialFunctions, :erfcinv, (0, 2)),
(SpecialFunctions, :lgamma, (0, 1000)),
(SpecialFunctions, :gamma, (0, 36))
(SpecialFunctions, :gamma, (0, 36)),
(Main, :inv_sqrt, (0, 1000)),
(Main, :inv_cbrt, (0, 1000)),
(Main, :pow2o3, (-1000, 1000)),
(Main, :pow3o2, (0, 1000)),
)

const base_binary_real = (
(Base, :atan, (-1, 1), (-1, 1)),
(Base, :hypot, (-1000, 1000), (-1000, 1000)),
(Main, :divide, (-1000, 1000), (-1000, 1000)),
(Main, :pow, (0, 100), (-5, 20)),
(Main, :atanpi, (-1, 1), (-1, 1)),
# (getfield(Base, :./), (-1000, 1000), (-1000, 1000)),
# (getfield(Base, :.^), (0, 100), (-5, 20))
)
Expand Down Expand Up @@ -74,10 +81,39 @@ const base_unary_complex = (
# (cis, (-1000, 1000))
)

# const base_binary_complex = (
# # (getfield(Base, :./), (-1000, 1000), (-1000, 1000)),
# # ((:.^), (0, 100), (-2, 10))
# )
const base_binary_complex = (
(Main, :divide, (-1000, 1000), (-1000, 1000)),
(Main, :pow, (0, 100), (-2, 10)),
)

@testset "Check completeness of tests" begin
@testset "Unary real input" begin
have_test_domains = getfield.(base_unary_real, 2)
# :cis is a special case
defined_functions = (getfield.(IVM.unary_real, 1)..., getfield.(IVM.unary_real_complex, 1)..., :cis)

@test isempty(symdiff(have_test_domains, defined_functions))
end

@testset "Binary real input" begin
have_test_domains = getfield.(base_binary_real, 2)
defined_functions = (getfield.(IVM.binary_real, 1)..., getfield.(IVM.binary_real_complex, 1)...)
@test isempty(symdiff(have_test_domains, defined_functions))
end

@testset "Unary complex input" begin
have_test_domains = getfield.(base_unary_complex, 2)
defined_functions = (getfield.(IVM.unary_complex_in, 1)..., getfield.(IVM.unary_complex_inout, 1)..., getfield.(IVM.unary_real_complex, 1)...)
@test isempty(symdiff(have_test_domains, defined_functions))
end

@testset "Binary complex input" begin
have_test_domains = getfield.(base_binary_complex, 2)
defined_functions = getfield.(IVM.binary_real_complex, 1)
@test isempty(symdiff(have_test_domains, defined_functions))
end

end

function randindomain(t::Type{T}, n, domain) where {T<:Real}
d1 = convert(t, domain[1])
Expand Down
28 changes: 28 additions & 0 deletions test/nonbase-functions.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# :inv_sqrt
inv_sqrt(x) = 1 / sqrt(x)
# inv_cbrt
inv_cbrt(x) = 1 / cbrt(x)

# :pow2o3
pow2o3(x) = cbrt(x^2)
# x^(2 / 3) also doable but not as fast, and different accuracy
# :pow3o2
pow3o2(x) = sqrt(x^3)

# pow
pow(x, y) = x^y
# divide
divide(x, y) = x / y

# :lgamma
# Redefining for testing to avoid deprecation warning
SpecialFunctions.lgamma(x::Union{Float64,Float32}) = (logabsgamma(x))[1]

atanpi(x, y) = atan(x, y) / pi
atanpi(x) = atan(x) / pi

acospi(x) = acos(x) / pi
asinpi(x) = asin(x) / pi

# MKL has higher accuracy on Float32s
tanpi(x) = oftype(x, Base.tan(widen(x) * pi))
10 changes: 2 additions & 8 deletions test/real.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,8 @@ const fns = [[x[1:2] for x in base_unary_real]; [x[1:2] for x in base_binary_rea

@testset "$fn" begin

if fn === :acospi || fn === :asinpi || fn === :atanpi
fn′ = getproperty(mod, Symbol(string(fn)[1:end-2]))
base_fn = x -> oftype(x, fn′(widen(x)) / pi)
elseif fn === :tanpi
base_fn = x -> oftype(x, Base.tan(widen(x) * pi))
else
base_fn = getproperty(mod, fn)
end
base_fn = getproperty(mod, fn)

vml_fn = getproperty(IntelVectorMath, fn)
vml_fn! = getproperty(IntelVectorMath, Symbol(fn, "!"))

Expand Down
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import MKL_jll
using Test
using IntelVectorMath
using SpecialFunctions

include("nonbase-functions.jl")
include("common.jl")
include("real.jl")
include("complex.jl")