Skip to content

Commit f49d1af

Browse files
committed
Moved sampling-based tests out of unit and into an exectuable target.
1 parent 5eb865d commit f49d1af

File tree

4 files changed

+127
-76
lines changed

4 files changed

+127
-76
lines changed

Package.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,40 @@
1313
import PackageDescription
1414

1515
let package = Package(
16+
1617
name: "swift-numerics",
1718
products: [
1819
.library(name: "ComplexModule", targets: ["ComplexModule"]),
1920
.library(name: "Numerics", targets: ["Numerics"]),
2021
.library(name: "RealModule", targets: ["RealModule"]),
2122
],
23+
2224
dependencies: [
25+
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.1.0"),
2326
],
27+
2428
targets: [
29+
// User-facing modules
2530
.target(name: "ComplexModule", dependencies: ["RealModule"]),
2631
.target(name: "Numerics", dependencies: ["ComplexModule", "RealModule"]),
2732
.target(name: "RealModule", dependencies: ["_NumericsShims"]),
2833

34+
// Implementation details
2935
.target(name: "_NumericsShims", dependencies: []),
3036
.target(name: "_TestSupport", dependencies: ["Numerics"]),
3137

38+
// Unit test bundles
3239
.testTarget(name: "ComplexTests", dependencies: ["_TestSupport"]),
3340
.testTarget(name: "RealTests", dependencies: ["_TestSupport"]),
41+
42+
// Test executables
43+
.target(
44+
name: "ComplexElementaryFunctions",
45+
dependencies: [
46+
"Numerics", "_TestSupport",
47+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
48+
],
49+
path: "Tests/Executable"
50+
)
3451
]
3552
)

Tests/ComplexTests/ElementaryFunctionTests.swift

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -112,70 +112,6 @@ final class ElementaryFunctionTests: XCTestCase {
112112
}
113113
}
114114

115-
// TODO: pull this out into a separate binary target, not run as part of the
116-
// normal tests.
117-
func testExpMinusOne_FloatVsDouble() {
118-
// Walk grid points of the form (n + nπ/16 i) comparing Float and Double,
119-
// finding the worst componentwise and normwise errors for Float.
120-
let reals = (-100 ... 100).map { Float($0) }
121-
let imags = (-100 ... 100).map { Float($0) * .pi / 16 }
122-
var componentError = Double(Float.ulpOfOne)
123-
var complexError = Double(Float.ulpOfOne)
124-
var componentMaxInput = Complex<Float>.zero
125-
var complexMaxInput = Complex<Float>.zero
126-
for x in reals {
127-
for y in imags {
128-
let tst = Complex.expMinusOne(Complex(x,y))
129-
let ref = Complex.expMinusOne(Complex(Double(x), Double(y)))
130-
if tst == Complex<Float>(ref) { continue }
131-
let thisError = relativeError(tst, ref)
132-
if thisError > complexError {
133-
complexMaxInput = Complex(x,y)
134-
complexError = thisError
135-
}
136-
let thisComponentError = max(
137-
relativeError(tst.real, ref.real),
138-
relativeError(tst.imaginary, ref.imaginary)
139-
)
140-
if thisComponentError > componentError {
141-
componentMaxInput = Complex(x,y)
142-
componentError = thisComponentError
143-
}
144-
}
145-
}
146-
// Now sample randomly-generated points in an interesting strip along
147-
// the real axis.
148-
var g = SystemRandomNumberGenerator()
149-
for _ in 0 ..< 10_000 {
150-
let z = Complex(Float.random(in: -100 ... 100, using: &g),
151-
Float.random(in: -2 * .pi ... 2 * .pi, using: &g))
152-
let tst = Complex.expMinusOne(z)
153-
let ref = Complex.expMinusOne(Complex<Double>(z))
154-
if tst == Complex<Float>(ref) { continue }
155-
let thisError = relativeError(tst, ref)
156-
if thisError > complexError {
157-
complexMaxInput = z
158-
complexError = thisError
159-
}
160-
let thisComponentError = max(
161-
relativeError(tst.real, ref.real),
162-
relativeError(tst.imaginary, ref.imaginary)
163-
)
164-
if thisComponentError > componentError {
165-
componentMaxInput = z
166-
componentError = thisComponentError
167-
}
168-
}
169-
print("Worst complex norm error seen for expMinusOne was \(complexError)")
170-
print("For input \(complexMaxInput).")
171-
print("Reference result: \(Complex.expMinusOne(Complex<Double>(complexMaxInput)))")
172-
print(" Observed result: \(Complex.expMinusOne(complexMaxInput))")
173-
print("Worst componentwise error seen for expMinusOne was \(componentError)")
174-
print("For input \(componentMaxInput).")
175-
print("Reference result: \(Complex.expMinusOne(Complex<Double>(componentMaxInput)))")
176-
print(" Observed result: \(Complex.expMinusOne(componentMaxInput))")
177-
}
178-
179115
func testFloat() {
180116
testExp(Float.self)
181117
testExpMinusOne(Float.self)
@@ -193,15 +129,3 @@ final class ElementaryFunctionTests: XCTestCase {
193129
}
194130
#endif
195131
}
196-
197-
func relativeError(_ tst: Float, _ ref: Double) -> Double {
198-
let scale = max(ref.magnitude, Double(Float.leastNormalMagnitude))
199-
let error = (Double(tst) - ref).magnitude
200-
return error / scale
201-
}
202-
203-
func relativeError(_ tst: Complex<Float>, _ ref: Complex<Double>) -> Double {
204-
let scale = max(ref.magnitude, Double(Float.leastNormalMagnitude))
205-
let error = (Complex<Double>(tst) - ref).magnitude
206-
return error / scale
207-
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- Error.swift ------------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Numerics
14+
15+
func relativeError(_ tst: Float, _ ref: Double) -> Double {
16+
let scale = max(ref.magnitude, Double(Float.leastNormalMagnitude))
17+
let error = (Double(tst) - ref).magnitude
18+
return error / scale
19+
}
20+
21+
func componentwiseError(_ tst: Complex<Float>, _ ref: Complex<Double>) -> Double {
22+
return max(relativeError(tst.real, ref.real),
23+
relativeError(tst.imaginary, ref.imaginary))
24+
}
25+
26+
func relativeError(_ tst: Complex<Float>, _ ref: Complex<Double>) -> Double {
27+
let scale = max(ref.magnitude, Double(Float.leastNormalMagnitude))
28+
let error = (Complex<Double>(tst) - ref).magnitude
29+
return error / scale
30+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===--- main.swift -------------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Numerics
14+
import _TestSupport
15+
import ArgumentParser
16+
import Foundation
17+
18+
struct ComplexElementaryFunctions: ParsableCommand {
19+
func run() throws {
20+
// Walk grid points of the form (n + nπ/16 i) comparing Float and Double,
21+
// finding the worst componentwise and normwise errors for Float.
22+
let reals = (-100 ... 100).map { Float($0) }
23+
let imags = (-100 ... 100).map { Float($0) * .pi / 16 }
24+
var componentError = Double(Float.ulpOfOne)
25+
var complexError = Double(Float.ulpOfOne)
26+
var componentMaxInput = Complex<Float>.zero
27+
var complexMaxInput = Complex<Float>.zero
28+
for x in reals {
29+
for y in imags {
30+
let z = Complex(x, y)
31+
let tst = Complex.expMinusOne(z)
32+
let ref = Complex.expMinusOne(Complex<Double>(z))
33+
if tst == Complex<Float>(ref) { continue }
34+
let thisError = relativeError(tst, ref)
35+
if thisError > complexError {
36+
complexMaxInput = z
37+
complexError = thisError
38+
}
39+
let thisComponentError = componentwiseError(tst, ref)
40+
if thisComponentError > componentError {
41+
componentMaxInput = z
42+
componentError = thisComponentError
43+
}
44+
}
45+
}
46+
47+
// Now sample randomly-generated points in an interesting strip along
48+
// the real axis.
49+
var g = SystemRandomNumberGenerator()
50+
for _ in 0 ..< 10_000 {
51+
let z = Complex(Float.random(in: -100 ... 100, using: &g),
52+
Float.random(in: -2 * .pi ... 2 * .pi, using: &g))
53+
let tst = Complex.expMinusOne(z)
54+
let ref = Complex.expMinusOne(Complex<Double>(z))
55+
if tst == Complex<Float>(ref) { continue }
56+
let thisError = relativeError(tst, ref)
57+
if thisError > complexError {
58+
complexMaxInput = z
59+
complexError = thisError
60+
}
61+
let thisComponentError = componentwiseError(tst, ref)
62+
if thisComponentError > componentError {
63+
componentMaxInput = z
64+
componentError = thisComponentError
65+
}
66+
}
67+
68+
print("Worst complex norm error seen for expMinusOne was \(complexError)")
69+
print("For input \(complexMaxInput).")
70+
print("Reference result: \(Complex.expMinusOne(Complex<Double>(complexMaxInput)))")
71+
print(" Observed result: \(Complex.expMinusOne(complexMaxInput))")
72+
73+
print("Worst componentwise error seen for expMinusOne was \(componentError)")
74+
print("For input \(componentMaxInput).")
75+
print("Reference result: \(Complex.expMinusOne(Complex<Double>(componentMaxInput)))")
76+
print(" Observed result: \(Complex.expMinusOne(componentMaxInput))")
77+
}
78+
}
79+
80+
ComplexElementaryFunctions.main()

0 commit comments

Comments
 (0)