Skip to content

Commit 0ab6417

Browse files
committed
[Accelerate] [Quadrature] New Quadrature Overlay
A class that simplifies approximating the definite integral of a function.
1 parent 909868f commit 0ab6417

File tree

3 files changed

+553
-68
lines changed

3 files changed

+553
-68
lines changed

stdlib/public/Darwin/Accelerate/CMakeLists.txt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@ cmake_minimum_required(VERSION 3.4.3)
22
include("../../../../cmake/modules/StandaloneOverlay.cmake")
33

44
add_swift_target_library(swiftAccelerate ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
5-
BNNS.swift.gyb
5+
BNNS.swift.gyb
6+
Quadrature.swift
67

7-
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
8-
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
9-
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR WATCHOS WATCHOS_SIMULATOR
10-
SWIFT_MODULE_DEPENDS_OSX Darwin CoreFoundation CoreGraphics Dispatch Foundation IOKit Metal ObjectiveC XPC # auto-updated
11-
os
12-
SWIFT_MODULE_DEPENDS_IOS Darwin CoreFoundation CoreGraphics Dispatch Foundation Metal ObjectiveC os # auto-updated
13-
SWIFT_MODULE_DEPENDS_TVOS Darwin CoreFoundation CoreGraphics Dispatch Foundation Metal ObjectiveC os # auto-updated
14-
SWIFT_MODULE_DEPENDS_WATCHOS Darwin CoreFoundation CoreGraphics Dispatch Foundation ObjectiveC os # auto-updated
8+
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
9+
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
10+
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR WATCHOS WATCHOS_SIMULATOR
11+
SWIFT_MODULE_DEPENDS_OSX Darwin CoreFoundation CoreGraphics Dispatch Foundation IOKit Metal ObjectiveC XPC # auto-updated
12+
os
13+
SWIFT_MODULE_DEPENDS_IOS Darwin CoreFoundation CoreGraphics Dispatch Foundation Metal ObjectiveC os # auto-updated
14+
SWIFT_MODULE_DEPENDS_TVOS Darwin CoreFoundation CoreGraphics Dispatch Foundation Metal ObjectiveC os # auto-updated
15+
SWIFT_MODULE_DEPENDS_WATCHOS Darwin CoreFoundation CoreGraphics Dispatch Foundation ObjectiveC os # auto-updated
1516

16-
DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_OSX}
17-
DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_IOS}
18-
DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_TVOS}
19-
DEPLOYMENT_VERSION_WATCHOS ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_WATCHOS}
17+
FRAMEWORK_DEPENDS Accelerate
18+
19+
DEPLOYMENT_VERSION_OSX ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_OSX}
20+
DEPLOYMENT_VERSION_IOS ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_IOS}
21+
DEPLOYMENT_VERSION_TVOS ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_TVOS}
22+
DEPLOYMENT_VERSION_WATCHOS ${SWIFTLIB_DEPLOYMENT_VERSION_SIMD_WATCHOS}
2023
)
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
@_exported import Accelerate
14+
15+
// MARK: Quadrature
16+
17+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
18+
/// A class that approximates the definite integral of a function over a finite interval.
19+
///
20+
/// The following code is an example of using a `Quadrature` instance to calculate the
21+
/// area under a curve defined by `y = sqrt(radius * radius - pow(x - radius, 2))`:
22+
///
23+
///
24+
/// let quadrature = Quadrature(integrator: .qags(maxIntervals: 10),
25+
/// absoluteTolerance: 1.0e-8,
26+
/// relativeTolerance: 1.0e-2)
27+
///
28+
/// let result = quadrature.integrate(over: 0.0 ... 25.0) { x in
29+
/// let radius: Double = 12.5
30+
/// return sqrt(radius * radius - pow(x - radius, 2))
31+
/// }
32+
///
33+
/// switch result {
34+
/// case .success(let integralResult, let estimatedAbsoluteError):
35+
/// print("quadrature success:", integralResult,
36+
/// estimatedAbsoluteError)
37+
/// case .failure(let error):
38+
/// print("quadrature error:", error.errorDescription)
39+
/// }
40+
///
41+
public class Quadrature {
42+
43+
private var integrateOptions = quadrature_integrate_options()
44+
private var integrand: ((Double) -> Double)!
45+
46+
47+
/// Initializes and returns a quadrature instance.
48+
///
49+
/// - Parameter integrator: An enumeration specifying the integration algorithm and relevant properties.
50+
/// - Parameter absoluteTolerance: Requested absolute tolerance on the result.
51+
/// - Parameter relativeTolerance: Requested relative tolerance on the result.
52+
public init(integrator: Integrator,
53+
absoluteTolerance: Double = 1.0e-8,
54+
relativeTolerance: Double = 1.0e-2){
55+
56+
self.integrator = integrator
57+
integrateOptions.abs_tolerance = absoluteTolerance
58+
integrateOptions.rel_tolerance = relativeTolerance
59+
}
60+
61+
/// The quadrature instance's integration algorithm and relevant properties.
62+
public var integrator: Integrator {
63+
set {
64+
switch newValue {
65+
case .qng:
66+
integrateOptions.integrator = QUADRATURE_INTEGRATE_QNG
67+
case .qag(let pointsPerInterval, let maxIntervals):
68+
integrateOptions.integrator = QUADRATURE_INTEGRATE_QAG
69+
integrateOptions.qag_points_per_interval = pointsPerInterval.rawValue
70+
integrateOptions.max_intervals = maxIntervals
71+
case .qags(let maxIntervals):
72+
integrateOptions.integrator = QUADRATURE_INTEGRATE_QAGS
73+
integrateOptions.max_intervals = maxIntervals
74+
}
75+
}
76+
get {
77+
let integrator: Integrator!
78+
79+
switch integrateOptions.integrator {
80+
case QUADRATURE_INTEGRATE_QNG:
81+
integrator = .qng
82+
case QUADRATURE_INTEGRATE_QAG:
83+
integrator = .qag(pointsPerInterval: QAGPointsPerInterval(rawValue: integrateOptions.qag_points_per_interval)!,
84+
maxIntervals: integrateOptions.max_intervals)
85+
case QUADRATURE_INTEGRATE_QAGS:
86+
integrator = .qags(maxIntervals: integrateOptions.max_intervals)
87+
default:
88+
integrator = nil
89+
}
90+
91+
return integrator
92+
}
93+
}
94+
95+
/// The quadrature instance's requested absolute tolerance on the result.
96+
public var absoluteTolerance: Double {
97+
set {
98+
integrateOptions.abs_tolerance = newValue
99+
}
100+
get {
101+
return integrateOptions.abs_tolerance
102+
}
103+
}
104+
105+
/// The quadrature instance's requested relative tolerance on the result.
106+
public var relativeTolerance: Double {
107+
set {
108+
integrateOptions.rel_tolerance = newValue
109+
}
110+
get {
111+
return integrateOptions.rel_tolerance
112+
}
113+
}
114+
115+
/// The maximum number of subintervals in the subdivision used by QAG and QAGS integrators.
116+
public var maxIntervals: Int {
117+
set {
118+
integrateOptions.max_intervals = newValue
119+
}
120+
get {
121+
return integrateOptions.max_intervals
122+
}
123+
}
124+
125+
/// Number of points per subinterval. Used by the QAG integrator only; other integrators ignore this value.
126+
public var qagPointsPerInterval: QAGPointsPerInterval {
127+
set {
128+
integrateOptions.qag_points_per_interval = newValue.rawValue
129+
}
130+
get {
131+
return QAGPointsPerInterval(rawValue: integrateOptions.qag_points_per_interval) ?? .default
132+
}
133+
}
134+
135+
/// Performs the integration over the supplied function.
136+
///
137+
/// - Parameter interval: The lower and upper bounds of the integration interval.
138+
/// - Parameter integrand: The function to integrate. The input value is `x` that's within the interval over which the integrand is being integrated, and the output value is the corresponding value `y = integrand(x)` at those points.
139+
public func integrate(over interval: ClosedRange<Double>,
140+
integrand: @escaping (Double) -> Double) -> Result {
141+
142+
self.integrand = integrand
143+
144+
let byteCount = MemoryLayout<Quadrature>.size
145+
let integrandPointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount,
146+
alignment: 1)
147+
integrandPointer.storeBytes(of: self,
148+
as: Quadrature.self)
149+
150+
var callback = quadrature_integrate_function(
151+
fun: { (arg: UnsafeMutableRawPointer?,
152+
n: Int,
153+
x: UnsafePointer<Double>,
154+
y: UnsafeMutablePointer<Double>
155+
) in
156+
157+
guard let quadrature = arg?.load(as: Quadrature.self) else {
158+
return
159+
}
160+
161+
(0 ..< n).forEach { i in
162+
y[i] = quadrature.integrand(x[i])
163+
}
164+
},
165+
fun_arg: integrandPointer)
166+
167+
var status = QUADRATURE_SUCCESS
168+
var estimatedAbsoluteError: Double = 0
169+
var result: Double = 0
170+
171+
withUnsafePointer(to: integrateOptions) { options in
172+
result = quadrature_integrate(&callback,
173+
interval.lowerBound,
174+
interval.upperBound,
175+
options,
176+
&status,
177+
&estimatedAbsoluteError,
178+
0,
179+
nil)
180+
}
181+
182+
if status == QUADRATURE_SUCCESS {
183+
return .success(integralResult: result,
184+
estimatedAbsoluteError: estimatedAbsoluteError)
185+
} else {
186+
return .failure(error: Error(quadratureStatus: status))
187+
}
188+
}
189+
190+
public enum Integrator {
191+
/// Simple non-adaptive automatic integrator using Gauss-Kronrod-Patterson quadrature coefficients.
192+
/// Evaluates 21, or 43, or 87 points in the interval until the requested accuracy is reached.
193+
case qng
194+
195+
/// Simple globally adaptive integrator.
196+
/// Allows selection of the number of Gauss-Kronrod points used in each subinterval, and the max number of subintervals.
197+
case qag(pointsPerInterval: QAGPointsPerInterval, maxIntervals: Int)
198+
199+
/// Global adaptive quadrature based on 21-point or 15-point (if at least one bound is infinite) Gauss–Kronrod quadrature within each subinterval, with acceleration by Peter Wynn's epsilon algorithm.
200+
/// If at least one of the interval bounds is infinite, this is equivalent to the QUADPACK QAGI routine. Otherwise, this is equivalent to the QUADPACK QAGS routine.
201+
case qags(maxIntervals: Int)
202+
}
203+
204+
public enum QAGPointsPerInterval: Int {
205+
case `default` = 0
206+
case fifteen = 15
207+
case twentyOne = 21
208+
case thirtyOne = 31
209+
case fortyOne = 41
210+
case fiftyOne = 51
211+
case sixtyOne = 61
212+
}
213+
214+
public enum Result {
215+
case success (integralResult: Double, estimatedAbsoluteError: Double)
216+
case failure (error: Error)
217+
}
218+
219+
public enum Error: Swift.Error {
220+
case generic
221+
case invalidArgument
222+
case `internal`
223+
case integrateMaxEval
224+
case badIntegrandBehaviour
225+
226+
init(quadratureStatus: quadrature_status) {
227+
switch quadratureStatus {
228+
case QUADRATURE_ERROR:
229+
self = .generic
230+
case QUADRATURE_INVALID_ARG_ERROR:
231+
self = .invalidArgument
232+
case QUADRATURE_INTERNAL_ERROR:
233+
self = .internal
234+
case QUADRATURE_INTEGRATE_MAX_EVAL_ERROR:
235+
self = .integrateMaxEval
236+
case QUADRATURE_INTEGRATE_BAD_BEHAVIOUR_ERROR:
237+
self = .badIntegrandBehaviour
238+
default:
239+
self = .internal
240+
}
241+
}
242+
243+
var errorDescription: String {
244+
switch self {
245+
case .generic:
246+
return "Generic error"
247+
case .invalidArgument:
248+
return "Invalid Argument"
249+
case .internal:
250+
return "This is a bug in the Quadrature code, please file a bug report."
251+
case .integrateMaxEval:
252+
return "The requested accuracy limit could not be reached with the allowed number of evals/subdivisions."
253+
case .badIntegrandBehaviour:
254+
return "Extremely bad integrand behaviour, or excessive roundoff error occurs at some points of the integration interval."
255+
}
256+
}
257+
}
258+
}

0 commit comments

Comments
 (0)