Skip to content

Commit 68988c6

Browse files
committed
Add pseudorandom number generator WyRand
1 parent 81acc87 commit 68988c6

5 files changed

Lines changed: 117 additions & 1 deletion

File tree

Package.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ let package = Package(
1212
targets: [
1313
.target(
1414
name: "Numerix",
15-
dependencies: ["VectorModule", "MatrixModule", "ShapedArrayModule"],
15+
dependencies: ["RandomModule", "VectorModule", "MatrixModule", "ShapedArrayModule"],
1616
cxxSettings: [.define("ACCELERATE_NEW_LAPACK", to: "1"), .define("ACCELERATE_LAPACK_ILP64", to: "1")],
1717
linkerSettings: [.linkedFramework("Accelerate")]
1818
),
19+
.target(
20+
name: "RandomModule"
21+
),
1922
.target(
2023
name: "VectorModule",
2124
cxxSettings: [.define("ACCELERATE_NEW_LAPACK", to: "1"), .define("ACCELERATE_LAPACK_ILP64", to: "1")],

Sources/Numerix/Numerix.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Exported modules for the Numerix package.
33
*/
44

5+
@_exported import RandomModule
56
@_exported import VectorModule
67
@_exported import MatrixModule
78
@_exported import ShapedArrayModule

Sources/RandomModule/Random.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Protocol for random vector.
3+
Random vector protocol and init.
4+
*/
5+
6+
import VectorModule
7+
8+
@_documentation(visibility: private)
9+
public protocol Random {
10+
static func random(size: Int, seed: UInt64?) -> Vector<Self>
11+
}
12+
13+
@_documentation(visibility: private)
14+
extension Float: Random {
15+
public static func random(size: Int, seed: UInt64?) -> Vector<Float> {
16+
var vec = Vector<Float>(size: size)
17+
var rng = WyRand(seed: seed)
18+
for i in 0..<size {
19+
vec[i] = rng.nextUniform()
20+
}
21+
return vec
22+
}
23+
}
24+
25+
@_documentation(visibility: private)
26+
extension Double: Random {
27+
public static func random(size: Int, seed: UInt64?) -> Vector<Double> {
28+
var vec = Vector<Double>(size: size)
29+
var rng = WyRand(seed: seed)
30+
for i in 0..<size {
31+
vec[i] = rng.nextUniform()
32+
}
33+
return vec
34+
}
35+
}
36+
37+
extension Vector {
38+
public static func random(size: Int, seed: UInt64? = nil) -> Vector where Scalar: Random {
39+
Scalar.random(size: size, seed: seed)
40+
}
41+
}

Sources/RandomModule/WyRand.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
WyRand pseudorandom number generator (PRNG) based on wyrand and wyhash functions by Wang Yi.
3+
4+
Original code for wyrand and wyhash functions
5+
https://github.com/wangyi-fudan/wyhash
6+
7+
Also see Daniel Lemire's article about wyhash
8+
https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/
9+
*/
10+
11+
import Foundation
12+
13+
public struct WyRand: RandomNumberGenerator {
14+
private var state : UInt64
15+
16+
public init(seed: UInt64? = nil) {
17+
state = seed ?? UInt64(abs(UUID().hashValue))
18+
}
19+
20+
public mutating func next() -> UInt64 {
21+
state &+= 0x2d358dccaa6c78a5
22+
let mul = state.multipliedFullWidth(by: state ^ 0x8bb84b93962eacc9)
23+
return mul.low ^ mul.high
24+
}
25+
26+
public mutating func nextUniform() -> Float {
27+
let x = self.next()
28+
return Float(x >> 40) * 0x1.0p-24
29+
}
30+
31+
public mutating func nextUniform() -> Double {
32+
let x = self.next()
33+
return Double(x >> 11) * 0x1.0p-53
34+
}
35+
}

Tests/RandomTests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Tests for random numbers and random vectors.
3+
*/
4+
5+
import Testing
6+
@testable import Numerix
7+
8+
struct RandomTests {
9+
10+
@Test func randomWyRand() {
11+
var rand = WyRand(seed: 12345)
12+
#expect(rand.next() == 13157676964440363053)
13+
14+
let a: Float = rand.nextUniform()
15+
#expect(a == 0.9815675)
16+
17+
let b: Double = rand.nextUniform()
18+
#expect(b == 0.7952823641045631)
19+
}
20+
21+
@Test func randomVector() {
22+
// No seed
23+
let a = Vector<Float>.random(size: 3)
24+
#expect(a[0] < 1.0)
25+
26+
let b = Vector<Double>.random(size: 3)
27+
#expect(b[0] < 1.0)
28+
29+
// With seed
30+
let c = Vector<Float>.random(size: 4, seed: 12345)
31+
#expect(c == Vector<Float>([0.71327907, 0.9815675, 0.79528236, 0.25364798]))
32+
33+
let d = Vector<Double>.random(size: 4, seed: 12345)
34+
#expect(d == Vector<Double>([0.7132790974854359, 0.9815675033121781, 0.7952823641045631, 0.25364800714420743]))
35+
}
36+
}

0 commit comments

Comments
 (0)