Skip to content

Commit

Permalink
Use Swift Numerics for elementary functions instead of Darwin/Glibc/etc
Browse files Browse the repository at this point in the history
Also lets us use root instead of exp(log(x)/k), which is more accurate in some cases (but mainly is just nicer to read).

The downside is that this introduces a dependency for Algorithms, where previously it had none. I think that Numerics is an OK thing to depend on (especially for these functions, which are available from 0.0.1), but it _is_ a _massive increase_ in the number of dependencies that the package has, and we should give that some thought.
  • Loading branch information
stephentyrone committed Oct 16, 2020
1 parent a96b90c commit 4d4bbbe
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 19 deletions.
5 changes: 4 additions & 1 deletion Package.swift
Expand Up @@ -20,11 +20,14 @@ let package = Package(
targets: ["Algorithms"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-numerics", from: "0.0.1"),
],
targets: [
.target(
name: "Algorithms",
dependencies: []),
dependencies: [
.product(name: "RealModule", package: "swift-numerics"),
]),
.testTarget(
name: "SwiftAlgorithmsTests",
dependencies: ["Algorithms"]),
Expand Down
26 changes: 8 additions & 18 deletions Sources/Algorithms/RandomSample.swift
Expand Up @@ -9,17 +9,9 @@
//
//===----------------------------------------------------------------------===//

// For log(_:) and exp(_:)
#if canImport(Glibc)
// For log(_:) and root(_:_:)
@_implementationOnly
import Glibc
#elseif canImport(ucrt)
@_implementationOnly
import ucrt
#elseif canImport(Darwin)
@_implementationOnly
import Darwin
#endif
import RealModule

//===----------------------------------------------------------------------===//
// randomStableSample(count:)
Expand Down Expand Up @@ -90,15 +82,15 @@ extension Collection {
// https://dl.acm.org/doi/pdf/10.1145/198429.198435

fileprivate func nextW<G: RandomNumberGenerator>(
k: Double, using rng: inout G
k: Int, using rng: inout G
) -> Double {
exp(log(Double.random(in: 0..<1, using: &rng)) / k)
Double.root(.random(in: 0..<1, using: &rng), k)
}

fileprivate func nextOffset<G: RandomNumberGenerator>(
w: Double, using rng: inout G
) -> Int {
Int(log(Double.random(in: 0..<1, using: &rng)) / log(1 - w))
Int(Double.log(.random(in: 0..<1, using: &rng)) / .log(1 - w))
}

extension Collection {
Expand Down Expand Up @@ -129,11 +121,10 @@ extension Collection {
result.append(self[i])
formIndex(after: &i)
}

let dk = Double(k)

while i < endIndex {
// Calculate the next value of w.
w *= nextW(k: dk, using: &rng)
w *= nextW(k: k, using: &rng)

// Find index of the next element to swap into the reservoir.
let offset = nextOffset(w: w, using: &rng)
Expand Down Expand Up @@ -198,10 +189,9 @@ extension Sequence {
result.append(el)
}

let dk = Double(k)
while true {
// Calculate the next value of w.
w *= nextW(k: dk, using: &rng)
w *= nextW(k: k, using: &rng)

// Find the offset of the next element to swap into the reservoir.
var offset = nextOffset(w: w, using: &rng) + 1
Expand Down

0 comments on commit 4d4bbbe

Please sign in to comment.