Skip to content

Commit 5534fd4

Browse files
committed
Clean up Vector random methods and add more docs for random
1 parent 76a8a15 commit 5534fd4

5 files changed

Lines changed: 161 additions & 36 deletions

File tree

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
# Random Numbers
1+
# Random
22

33
Random number generation.
44

55
## Overview
66

7-
Numerix provides random number generators
7+
Numerix provides pseudorandom number generators (PRNGs) which are not cryptographically secure but offer much better performance than Swift's default random type methods.
88

99
## Topics
1010

1111
- ``Wyrand``
1212
- ``Xoshiro128Plus``
1313
- ``Xoroshiro128Plus``
1414
- ``Xoroshiro128PlusPlus``
15-
- ``Vector/random(size:seed:)``
15+
- ``Vector/random(_:)``
16+
- ``Vector/random(_:seed:)``
17+
- ``Vector/random(_:using:)``
1618
- ``Vector/randomDistribution(size:dist:)``
1719
- ``Vector/randomBNNS(size:bounds:seed:)``

Sources/Numerix/Random/Random.swift

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,25 @@ import Accelerate
66

77
@_documentation(visibility: private)
88
public protocol Random {
9-
static func random(size: Int, seed: UInt64?) -> Vector<Self>
9+
static func random(size: Int) -> Vector<Self>
10+
static func random(size: Int, seed: UInt64) -> Vector<Self>
11+
static func random<G: RandomNumberGenerator>(size: Int, rng: inout G) -> Vector<Self>
1012
static func randomBNNS(size: Int, bounds: (Self, Self), seed: UInt64, buffer: UnsafeMutableBufferPointer<Self>)
1113
}
1214

1315
@_documentation(visibility: private)
1416
extension Float: Random {
15-
public static func random(size: Int, seed: UInt64?) -> Vector<Float> {
17+
18+
public static func random(size: Int) -> Vector<Float> {
19+
var vec = Vector<Float>(size: size)
20+
var rng = Wyrand()
21+
for i in 0..<size {
22+
vec[i] = rng.next()
23+
}
24+
return vec
25+
}
26+
27+
public static func random(size: Int, seed: UInt64) -> Vector<Float> {
1628
var vec = Vector<Float>(size: size)
1729
var rng = Wyrand(seed: seed)
1830
for i in 0..<size {
@@ -21,6 +33,14 @@ extension Float: Random {
2133
return vec
2234
}
2335

36+
public static func random<G: RandomNumberGenerator>(size: Int, rng: inout G) -> Vector<Float> {
37+
var vec = Vector<Float>(size: size)
38+
for i in 0..<size {
39+
vec[i] = Float(rng.next() >> 40) * 0x1.0p-24
40+
}
41+
return vec
42+
}
43+
2444
public static func randomBNNS(
2545
size: Int, bounds: (Float, Float),
2646
seed: UInt64, buffer: UnsafeMutableBufferPointer<Float>
@@ -33,7 +53,17 @@ extension Float: Random {
3353

3454
@_documentation(visibility: private)
3555
extension Double: Random {
36-
public static func random(size: Int, seed: UInt64?) -> Vector<Double> {
56+
57+
public static func random(size: Int) -> Vector<Double> {
58+
var vec = Vector<Double>(size: size)
59+
var rng = Wyrand()
60+
for i in 0..<size {
61+
vec[i] = rng.next()
62+
}
63+
return vec
64+
}
65+
66+
public static func random(size: Int, seed: UInt64) -> Vector<Double> {
3767
var vec = Vector<Double>(size: size)
3868
var rng = Wyrand(seed: seed)
3969
for i in 0..<size {
@@ -42,6 +72,14 @@ extension Double: Random {
4272
return vec
4373
}
4474

75+
public static func random<G: RandomNumberGenerator>(size: Int, rng: inout G) -> Vector<Double> {
76+
var vec = Vector<Double>(size: size)
77+
for i in 0..<size {
78+
vec[i] = Double(rng.next() >> 11) * 0x1.0p-53
79+
}
80+
return vec
81+
}
82+
4583
public static func randomBNNS(
4684
size: Int, bounds: (Double, Double),
4785
seed: UInt64, buffer: UnsafeMutableBufferPointer<Double>
@@ -52,21 +90,49 @@ extension Double: Random {
5290

5391
extension Vector where Scalar: Random {
5492

55-
/// Create a vector of random values from a uniform distrubtion over [0, 1).
93+
/// Create a vector of random values from a uniform distribution over [0, 1) using the Wyrand generator.
94+
/// ```swift
95+
/// let vec = Vector<Double>.random(5)
96+
/// ```
97+
/// This uses the ``Wyrand`` pseudorandom number generator (PRNG) to generate the random values in the vector.
98+
/// The values are sampled from a uniform distribution [0, 1) which includes zero but excludes one.
99+
/// - Parameter size: Size of the vector.
100+
/// - Returns: Vector of random values.
101+
public static func random(_ size: Int) -> Vector {
102+
Scalar.random(size: size)
103+
}
104+
105+
/// Create a vector of random values from a uniform distribution over [0, 1) using a seed for the Wyrand generator.
56106
/// ```swift
57-
/// let vec = Vector<Double>.random(size: 5)
107+
/// let vec = Vector<Double>.random(5, seed: 123456)
58108
/// ```
59-
/// This uses the Wyrand pseudorandom number generator (PRNG) to generate the random values in the vector.
60-
/// Values are a uniform distribution over 0 to 1 excluding 1 which is denoted as [0, 1).
61-
/// Provide a seed input value to create a reproducible random vector.
109+
/// This uses the ``Wyrand`` pseudorandom number generator (PRNG) to generate the random values in the vector.
110+
/// The values are sampled from a uniform distribution [0, 1) which includes zero but excludes one. Provide a seed
111+
/// for reproducible results.
62112
/// - Parameters:
63113
/// - size: Size of the vector.
64-
/// - seed: Seed for the random number generator.
114+
/// - seed: Seed for the Wyrand generator.
65115
/// - Returns: Vector of random values.
66-
public static func random(size: Int, seed: UInt64? = nil) -> Vector {
116+
public static func random(_ size: Int, seed: UInt64) -> Vector {
67117
Scalar.random(size: size, seed: seed)
68118
}
69119

120+
/// Create a vector of random values from a uniform distribution over [0, 1) using a random number generator.
121+
/// ```swift
122+
/// var rng = Xoroshiro128Plus(seed: (1227493545477, 7213431619994))
123+
/// let a = Vector<Float>.random(5, using: &rng)
124+
/// ```
125+
/// The example uses the ``Xoroshiro128Plus`` pseudorandom number generator (PRNG) to generate random values to
126+
/// fill the vector. The generator is seeded with two integers otherwise random seeds are provided if none are
127+
/// given.
128+
/// - Parameters:
129+
/// - size: Size of the vector.
130+
/// - rng: Random number generator.
131+
/// - Returns: Vector or random values.
132+
public static func random<G: RandomNumberGenerator>(_ size: Int, using rng: inout G) -> Vector {
133+
Scalar.random(size: size, rng: &rng)
134+
}
135+
70136
/// Create a vector of random float values using BNNS.
71137
/// - Parameters:
72138
/// - size: Size of the vector.

Sources/Numerix/Random/Wyrand.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generat
99

1010
import Foundation
1111

12+
/// Wyrand is a fast 64-bit pseudorandom number generator (PRNG) for floating-point numbers. This is the default PRNG
13+
/// for Vector, Matrix, and ShapedArray types in Numerix.
1214
public struct Wyrand: RandomNumberGenerator {
1315
private var state: UInt64
1416

Sources/Numerix/Random/Xoroshiro.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ public struct Xoroshiro128Plus: RandomNumberGenerator {
3838
return result
3939
}
4040

41+
/// Generate a random single-precision value from a uniform distribution in [0, 1) which includes zero but
42+
/// excludes one.
43+
/// - Returns: Random single-precision value.
44+
public mutating func next() -> Float {
45+
Float(next() >> 40) * 0x1.0p-24
46+
}
47+
4148
/// Generate a random double-precision value from a uniform distribution in [0, 1) which includes zero but
4249
/// excludes one.
4350
/// - Returns: Random double-precision value.
@@ -72,6 +79,13 @@ public struct Xoroshiro128PlusPlus: RandomNumberGenerator {
7279
return result
7380
}
7481

82+
/// Generate a random single-precision value from a uniform distribution in [0, 1) which includes zero but
83+
/// excludes one.
84+
/// - Returns: Random single-precision value.
85+
public mutating func next() -> Float {
86+
Float(next() >> 40) * 0x1.0p-24
87+
}
88+
7589
/// Generate a random double-precision value from a uniform distribution in [0, 1) which includes zero but
7690
/// excludes one.
7791
/// - Returns: Random double-precision value.

Tests/RandomTests.swift

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,87 @@ import Numerix
88
struct RandomTests {
99

1010
@Test func wyrand() {
11-
var rand = Wyrand(seed: 12345)
12-
#expect(rand.next() == 13157676964440363053)
11+
var rng = Wyrand(seed: 12345)
12+
#expect(rng.next() == 13157676964440363053)
1313

14-
let a: Float = rand.next()
14+
let a: Float = rng.next()
1515
#expect(a == 0.9815675)
1616

17-
let b: Double = rand.next()
17+
let b: Double = rng.next()
1818
#expect(b == 0.7952823641045631)
1919
}
2020

21-
@Test func xoshiro() {
22-
var rng = Xoshiro128Plus(seed: (2719949631, 2719949631, 2719949631, 2719949631))
23-
let a: Float = rng.next()
24-
#expect(a == 0.26657522)
21+
@Test func wyrandVector() {
22+
// Without seed
23+
let a = Vector<Float>.random(5)
24+
#expect(a[0] < 1)
25+
26+
let aa = Vector<Double>.random(5)
27+
#expect(aa[0] < 1)
28+
29+
// With seed
30+
let b = Vector<Float>.random(5, seed: 123456)
31+
#expect(b == [0.81488985, 0.30684602, 0.31690413, 0.3039148, 0.18646216])
32+
33+
let bb = Vector<Double>.random(4, seed: 123456)
34+
#expect(bb == [0.8148898875141517, 0.3068460469993851, 0.3169041625894291, 0.30391479120110254])
35+
36+
// With generator
37+
var rng = Wyrand(seed: 123456)
38+
let c = Vector<Float>.random(5, using: &rng)
39+
#expect(c == [0.81488985, 0.30684602, 0.31690413, 0.3039148, 0.18646216])
40+
41+
var rngg = Wyrand(seed: 123456)
42+
let cc = Vector<Double>.random(4, using: &rngg)
43+
#expect(cc == [0.8148898875141517, 0.3068460469993851, 0.3169041625894291, 0.30391479120110254])
2544
}
2645

2746
@Test func xoroshiro() {
47+
// Xoroshiro128Plus
2848
var rng = Xoroshiro128Plus(seed: (12274935454779349997, 7213431619994351707))
29-
let a: Double = rng.next()
30-
#expect(a == 0.05646649603323106)
49+
#expect(rng.next() == 1041623001064150088)
50+
51+
let a: Float = rng.next()
52+
#expect(a == 0.061123848)
3153

54+
let b: Double = rng.next()
55+
#expect(b == 0.1307673047034703)
56+
57+
// Xoroshiro128PlusPlus
3258
var rngg = Xoroshiro128PlusPlus(seed: (12274935454779349997, 7213431619994351707))
33-
let b: Double = rngg.next()
34-
#expect(b == 0.8419936520290631)
59+
#expect(rngg.next() == 15532041410668181718)
60+
61+
let c: Float = rngg.next()
62+
#expect(c == 0.4994716)
63+
64+
let d: Double = rngg.next()
65+
#expect(d == 0.7990605775501556)
3566
}
3667

37-
@Test func randomVector() {
38-
// No seed
39-
let a = Vector<Float>.random(size: 3)
40-
#expect(a[0] < 1.0)
68+
@Test func xoroshiroVector() {
69+
// Xoroshiro128Plus vector
70+
var rng = Xoroshiro128Plus(seed: (12274935454779349997, 7213431619994351707))
71+
let a = Vector<Float>.random(5, using: &rng)
72+
#expect(a == [0.05646646, 0.061123848, 0.13076729, 0.672823, 0.86956143])
4173

42-
let b = Vector<Double>.random(size: 3)
43-
#expect(b[0] < 1.0)
74+
let b = Vector<Double>.random(4, using: &rng)
75+
#expect(b == [0.12803432662341308, 0.17036647757843948, 0.8789760758360461, 0.6166839170638875])
4476

45-
// With seed
46-
let c = Vector<Float>.random(size: 4, seed: 12345)
47-
#expect(c == Vector<Float>([0.71327907, 0.9815675, 0.79528236, 0.25364798]))
77+
// Xoroshiro128PlusPlus vector
78+
var rngg = Xoroshiro128PlusPlus(seed: (12274935454779349997, 7213431619994351707))
79+
let c = Vector<Float>.random(5, using: &rngg)
80+
#expect(c == [0.84199363, 0.4994716, 0.7990605, 0.74130404, 0.2992577])
4881

49-
let d = Vector<Double>.random(size: 4, seed: 12345)
50-
#expect(d == Vector<Double>([0.7132790974854359, 0.9815675033121781, 0.7952823641045631, 0.25364800714420743]))
82+
let d = Vector<Double>.random(4, using: &rngg)
83+
#expect(d == [0.6241821269000679, 0.18502002919976157, 0.2555743212406011, 0.5790431050892778])
84+
}
85+
86+
@Test func xoshiro() {
87+
var rng = Xoshiro128Plus(seed: (2719949631, 2719949631, 2719949631, 2719949631))
88+
#expect(rng.next() == 1144931966)
89+
90+
let a: Float = rng.next()
91+
#expect(a == 0.6332876)
5192
}
5293

5394
@Test func randomBNNSVector() {

0 commit comments

Comments
 (0)