mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
212 lines
7.3 KiB
Swift
212 lines
7.3 KiB
Swift
// RUN: %target-run-simple-swiftgyb(-Xfrontend -enable-experimental-forward-mode-differentiation)
|
|
// REQUIRES: executable_test
|
|
|
|
#if canImport(Darwin)
|
|
import Darwin.C.tgmath
|
|
#elseif canImport(Glibc)
|
|
import Glibc
|
|
#elseif os(Windows)
|
|
import CRT
|
|
#else
|
|
#error("Unsupported platform")
|
|
#endif
|
|
|
|
#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64))
|
|
typealias TestLiteralType = Float80
|
|
#else
|
|
typealias TestLiteralType = Double
|
|
#endif
|
|
|
|
import StdlibUnittest
|
|
import _Differentiation
|
|
|
|
let DerivativeTests = TestSuite("TGMath")
|
|
|
|
func expectEqualWithTolerance<T>(_ expected: TestLiteralType, _ actual: T,
|
|
ulps allowed: T = 3,
|
|
file: String = #file, line: UInt = #line)
|
|
where T: BinaryFloatingPoint {
|
|
if actual == T(expected) || actual.isNaN && expected.isNaN || actual.isInfinite && expected.isInfinite {
|
|
return
|
|
}
|
|
// Compute error in ulp, compare to tolerance.
|
|
let absoluteError = T(abs(TestLiteralType(actual) - expected))
|
|
let ulpError = absoluteError / T(expected).ulp
|
|
expectTrue(ulpError <= allowed,
|
|
"\(actual) != \(expected) as \(T.self)" +
|
|
"\n \(ulpError)-ulp error exceeds \(allowed)-ulp tolerance.",
|
|
file: file, line: line)
|
|
}
|
|
|
|
func computeDividedDifference<T: BinaryFloatingPoint> (
|
|
_ f: (T, T) -> T,
|
|
_ x: T,
|
|
_ y: T,
|
|
eps: T = 0.01
|
|
) -> (dfdx: T, dfdy: T) {
|
|
let dfdx = (f(x + eps, y) - f(x, y)) / eps
|
|
let dfdy = (f(x, y + eps) - f(x, y)) / eps
|
|
return (dfdx, dfdy)
|
|
}
|
|
|
|
func checkGradient<T: BinaryFloatingPoint & Differentiable>(
|
|
_ f: @differentiable(reverse) (T, T) -> T,
|
|
_ x: T,
|
|
_ y: T,
|
|
ulps: T = 192)
|
|
where T == T.TangentVector {
|
|
let eps = T(0.01)
|
|
let grad = gradient(at: x, y, of: f)
|
|
let (dfdx, dfdy) = computeDividedDifference(f, x, y, eps: eps)
|
|
expectEqualWithTolerance(TestLiteralType(dfdx), grad.0, ulps: ulps)
|
|
expectEqualWithTolerance(TestLiteralType(dfdy), grad.1, ulps: ulps)
|
|
}
|
|
|
|
func checkDerivative<T: BinaryFloatingPoint & Differentiable>(
|
|
_ f: @differentiable(reverse) (T, T) -> T,
|
|
_ x: T,
|
|
_ y: T,
|
|
ulps: T = 192)
|
|
where T == T.TangentVector {
|
|
let eps = T(0.01)
|
|
let deriv = derivative(at: x, y, of: f)
|
|
let (dfdx, dfdy) = computeDividedDifference(f, x, y, eps: eps)
|
|
expectEqualWithTolerance(TestLiteralType(dfdx + dfdy), deriv, ulps: ulps)
|
|
}
|
|
|
|
%for op in ['derivative', 'gradient']:
|
|
%for T in ['Float', 'Float80']:
|
|
|
|
%if T == 'Float80':
|
|
#if !(os(Windows) || os(Android)) && (arch(i386) || arch(x86_64))
|
|
%end
|
|
|
|
DerivativeTests.test("${op}_${T}") {
|
|
expectEqualWithTolerance(7.3890560989306502274, ${op}(at: 2 as ${T}, of: exp))
|
|
expectEqualWithTolerance(2.772588722239781145, ${op}(at: 2 as ${T}, of: exp2))
|
|
expectEqualWithTolerance(7.3890560989306502274, ${op}(at: 2 as ${T}, of: expm1))
|
|
expectEqualWithTolerance(0.5, ${op}(at: 2 as ${T}, of: log))
|
|
expectEqualWithTolerance(0.21714724095162590833, ${op}(at: 2 as ${T}, of: log10))
|
|
expectEqualWithTolerance(0.7213475204444817278, ${op}(at: 2 as ${T}, of: log2))
|
|
expectEqualWithTolerance(0.33333333333333333334, ${op}(at: 2 as ${T}, of: log1p))
|
|
expectEqualWithTolerance(5.774399204041917612, ${op}(at: 2 as ${T}, of: tan))
|
|
expectEqualWithTolerance(-0.9092974268256816954, ${op}(at: 2 as ${T}, of: cos))
|
|
expectEqualWithTolerance(-0.416146836547142387, ${op}(at: 2 as ${T}, of: sin))
|
|
expectEqualWithTolerance(1.154700538379251529, ${op}(at: 0.5 as ${T}, of: asin))
|
|
expectEqualWithTolerance(-1.154700538379251529, ${op}(at: 0.5 as ${T}, of: acos))
|
|
expectEqualWithTolerance(0.8, ${op}(at: 0.5 as ${T}, of: atan))
|
|
expectEqualWithTolerance(3.7621956910836314597, ${op}(at: 2 as ${T}, of: sinh))
|
|
expectEqualWithTolerance(3.6268604078470187677, ${op}(at: 2 as ${T}, of: cosh))
|
|
expectEqualWithTolerance(0.07065082485316446565, ${op}(at: 2 as ${T}, of: tanh))
|
|
expectEqualWithTolerance(0.44721359549995793928, ${op}(at: 2 as ${T}, of: asinh))
|
|
expectEqualWithTolerance(0.5773502691896257645, ${op}(at: 2 as ${T}, of: acosh))
|
|
expectEqualWithTolerance(1.3333333333333333334, ${op}(at: 0.5 as ${T}, of: atanh))
|
|
expectEqualWithTolerance(0.020666985354092053575, ${op}(at: 2 as ${T}, of: erf))
|
|
expectEqualWithTolerance(-0.020666985354092053575, ${op}(at: 2 as ${T}, of: erfc))
|
|
expectEqualWithTolerance(0.35355339059327376222, ${op}(at: 2 as ${T}, of: { sqrt($0) }))
|
|
expectEqualWithTolerance(0, ${op}(at: 2 as ${T}, of: { ceil($0) }))
|
|
expectEqualWithTolerance(0, ${op}(at: 2 as ${T}, of: { floor($0) }))
|
|
expectEqualWithTolerance(0, ${op}(at: 2 as ${T}, of: { round($0) }))
|
|
expectEqualWithTolerance(0, ${op}(at: 2 as ${T}, of: { trunc($0) }))
|
|
|
|
// Differential operator specific tests.
|
|
|
|
// fma
|
|
let dfma = ${op}(at: 4 as ${T}, 5 as ${T}, 6 as ${T}, of: fma)
|
|
%if op == 'gradient':
|
|
expectEqualWithTolerance(5, dfma.0)
|
|
expectEqualWithTolerance(4, dfma.1)
|
|
expectEqualWithTolerance(1, dfma.2)
|
|
%else: # if op == 'derivative'
|
|
expectEqualWithTolerance(10, dfma)
|
|
%end
|
|
|
|
// remainder, fmod
|
|
for a in -10...10 {
|
|
let x = ${T}(a)
|
|
for b in -10...10 {
|
|
let y = ${T}(b)
|
|
guard b != 0 && remainder(x, y).sign == remainder(x + ${T}(0.001), y).sign &&
|
|
remainder(x, y).sign == remainder(x, y + ${T}(0.001)).sign
|
|
else { continue }
|
|
%if op == 'gradient':
|
|
checkGradient({ remainder($0, $1) }, x, y)
|
|
checkGradient({ fmod($0, $1) }, x, y)
|
|
%else: # if op == 'derivative'
|
|
// TODO(TF-1108): Implement JVPs for `remainder` and `fmod`.
|
|
%end
|
|
}
|
|
}
|
|
|
|
// pow
|
|
let eps:${T} = 0.01
|
|
let ulps:${T} = eps/eps.ulp
|
|
|
|
// Checks for negative base.
|
|
for a in -3..<0 {
|
|
let x = ${T}(a)
|
|
for b in -3...3 {
|
|
let y = ${T}(b)
|
|
let expectedDx = y * pow(x, y - 1)
|
|
let expectedDy = ${T}.zero
|
|
let dpow = ${op}(at: x, y, of: pow)
|
|
%if op == 'gradient':
|
|
expectEqualWithTolerance(TestLiteralType(expectedDx), dpow.0)
|
|
expectEqualWithTolerance(TestLiteralType(expectedDy), dpow.1)
|
|
%else: # if op == 'derivative'
|
|
expectEqualWithTolerance(TestLiteralType(expectedDx + expectedDy), dpow)
|
|
%end
|
|
}
|
|
}
|
|
|
|
// Checks for 0 base.
|
|
for b in -3...3 {
|
|
let y = ${T}(b)
|
|
var expectedValues: (dx: ${T}, dy: ${T})?
|
|
if y.isLess(than: 0) {
|
|
expectedValues = (dx: ${T}.infinity, dy: ${T}.nan)
|
|
} else if y.isZero {
|
|
expectedValues = (dx: ${T}.nan, dy: ${T}.zero)
|
|
} else if !y.isEqual(to: 1) {
|
|
expectedValues = (dx: ${T}.zero, dy: ${T}.zero)
|
|
}
|
|
if let (expectedDx, expectedDy) = expectedValues {
|
|
let dpow = ${op}(at: 0.0, y, of: pow)
|
|
%if op == 'gradient':
|
|
expectEqualWithTolerance(TestLiteralType(expectedDx), dpow.0)
|
|
expectEqualWithTolerance(TestLiteralType(expectedDy), dpow.1)
|
|
%else: # if op == 'derivative'
|
|
expectEqualWithTolerance(TestLiteralType(expectedDx + expectedDy), dpow)
|
|
%end
|
|
} else {
|
|
%if op == 'gradient':
|
|
checkGradient({ pow($0, $1) }, 0.0, y, ulps: ulps)
|
|
%else: # if op == 'derivative'
|
|
checkDerivative({ pow($0, $1) }, 0.0, y, ulps: ulps)
|
|
%end
|
|
}
|
|
}
|
|
|
|
// Checks for positive base.
|
|
for a in 1...3 {
|
|
let x = ${T}(a)
|
|
for b in -3...3 {
|
|
let y = ${T}(b)
|
|
%if op == 'gradient':
|
|
checkGradient({ pow($0, $1) }, x, y, ulps: ulps)
|
|
%else: # if op == 'derivative'
|
|
checkDerivative({ pow($0, $1) }, x, y, ulps: ulps)
|
|
%end
|
|
}
|
|
}
|
|
}
|
|
|
|
%if T == 'Float80':
|
|
#endif
|
|
%end
|
|
|
|
%end # for T in ['Float', 'Float80']:
|
|
%end # for op in ['derivative', 'gradient']:
|
|
|
|
runAllTests()
|