Skip to content

Commit 36cfce0

Browse files
committed
Add random extensions for Matrix struct
1 parent 37cc48c commit 36cfce0

File tree

3 files changed

+130
-3
lines changed

3 files changed

+130
-3
lines changed

Sources/Numerix/Documentation.docc/Random.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Random number generation.
44

55
## Overview
66

7-
Numerix provides pseudorandom number generators (PRNGs) which are not cryptographically secure but offer much better performance than Swift's default random type methods.
7+
Numerix provides pseudorandom number generators (PRNGs) which are not cryptographically secure but offer much better performance than Swift's default random type methods. The generators can be used on their own or with Vector, Matrix, and ShapedArray structs to create bulk random values. By default the ``Wyrand`` generator is used with Vector, Matrix, and ShapedArray structs.
88

99
## Topics
1010

@@ -13,6 +13,8 @@ Numerix provides pseudorandom number generators (PRNGs) which are not cryptograp
1313
- ``Xoroshiro128Plus``
1414
- ``Xoroshiro128PlusPlus``
1515
- ``Vector/random(_:seed:)``
16+
- ``Matrix/random(_:_:seed:)``
1617
- ``Vector/random(_:using:)``
18+
- ``Matrix/random(_:_:using:)``
1719
- ``Vector/randomBNNS(size:bounds:seed:)``
1820
- ``Vector/randomDistribution(size:dist:)``

Sources/Numerix/Random/Random.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import Accelerate
99
@_documentation(visibility: private)
1010
public protocol Random {
1111
static func random(size: Int, seed: UInt64?) -> Vector<Self>
12+
static func random(rows: Int, columns: Int, seed: UInt64?) -> Matrix<Self>
13+
1214
static func random<G: RandomNumberGenerator>(size: Int, rng: inout G) -> Vector<Self>
15+
static func random<G: RandomNumberGenerator>(rows: Int, columns: Int, rng: inout G) -> Matrix<Self>
16+
1317
static func randomBNNS(size: Int, bounds: (Self, Self), seed: UInt64, buffer: UnsafeMutableBufferPointer<Self>)
1418
}
1519

@@ -25,6 +29,17 @@ extension Float: Random {
2529
return vec
2630
}
2731

32+
public static func random(rows: Int, columns: Int, seed: UInt64?) -> Matrix<Float> {
33+
var mat = Matrix<Float>(rows: rows, columns: columns)
34+
var rng = Wyrand(seed: seed)
35+
for i in 0..<rows {
36+
for j in 0..<columns {
37+
mat[i, j] = rng.next()
38+
}
39+
}
40+
return mat
41+
}
42+
2843
public static func random<G: RandomNumberGenerator>(size: Int, rng: inout G) -> Vector<Float> {
2944
var vec = Vector<Float>(size: size)
3045
for i in 0..<size {
@@ -33,6 +48,16 @@ extension Float: Random {
3348
return vec
3449
}
3550

51+
public static func random<G: RandomNumberGenerator>(rows: Int, columns: Int, rng: inout G) -> Matrix<Float> {
52+
var mat = Matrix<Float>(rows: rows, columns: columns)
53+
for i in 0..<rows {
54+
for j in 0..<columns {
55+
mat[i, j] = Float(rng.next() >> 40) * 0x1.0p-24
56+
}
57+
}
58+
return mat
59+
}
60+
3661
public static func randomBNNS(
3762
size: Int, bounds: (Float, Float),
3863
seed: UInt64, buffer: UnsafeMutableBufferPointer<Float>
@@ -55,6 +80,17 @@ extension Double: Random {
5580
return vec
5681
}
5782

83+
public static func random(rows: Int, columns: Int, seed: UInt64?) -> Matrix<Double> {
84+
var mat = Matrix<Double>(rows: rows, columns: columns)
85+
var rng = Wyrand(seed: seed)
86+
for i in 0..<rows {
87+
for j in 0..<columns {
88+
mat[i, j] = rng.next()
89+
}
90+
}
91+
return mat
92+
}
93+
5894
public static func random<G: RandomNumberGenerator>(size: Int, rng: inout G) -> Vector<Double> {
5995
var vec = Vector<Double>(size: size)
6096
for i in 0..<size {
@@ -63,6 +99,16 @@ extension Double: Random {
6399
return vec
64100
}
65101

102+
public static func random<G: RandomNumberGenerator>(rows: Int, columns: Int, rng: inout G) -> Matrix<Double> {
103+
var mat = Matrix<Double>(rows: rows, columns: columns)
104+
for i in 0..<rows {
105+
for j in 0..<columns {
106+
mat[i, j] = Double(rng.next() >> 11) * 0x1.0p-53
107+
}
108+
}
109+
return mat
110+
}
111+
66112
public static func randomBNNS(
67113
size: Int, bounds: (Double, Double),
68114
seed: UInt64, buffer: UnsafeMutableBufferPointer<Double>
@@ -117,3 +163,39 @@ extension Vector where Scalar: Random {
117163
return vec
118164
}
119165
}
166+
167+
extension Matrix where Scalar: Random {
168+
169+
/// Create a matrix of random values from a uniform distribution over [0, 1).
170+
/// ```swift
171+
/// let a = Matrix<Float>.random(2, 3)
172+
/// let b = Matrix<Double>.random(2, 2, seed: 123456)
173+
/// ```
174+
/// This method uses the ``Wyrand`` pseudorandom number generator (PRNG) to generate the random values in the
175+
/// matrix. The values are sampled from a uniform distribution [0, 1) which includes zero but excludes one. An
176+
/// optional seed can be provided for reproducible results.
177+
/// - Parameters:
178+
/// - rows: Number of matrix rows.
179+
/// - columns: Number of matrix columns.
180+
/// - seed: Seed for the Wyrand generator.
181+
/// - Returns: Matrix of random values.
182+
public static func random(_ rows: Int, _ columns: Int, seed: UInt64? = nil) -> Matrix {
183+
Scalar.random(rows: rows, columns: columns, seed: seed)
184+
}
185+
186+
/// Create a matrix of random values from a uniform distribution over [0, 1) using a random number generator.
187+
/// ```swift
188+
/// var rng = Xoroshiro128Plus(seed: (1227493545477, 7213431619994))
189+
/// let a = Matrix<Double>.random(5, 4, using: &rng)
190+
/// ```
191+
/// The example uses the ``Xoroshiro128Plus`` pseudorandom number generator (PRNG) to generate random values to
192+
/// fill the matrix. The generator is seeded with two integers otherwise random seeds are used if none are given.
193+
/// - Parameters:
194+
/// - rows: Number of matrix rows.
195+
/// - columns: Number of matrix columns.
196+
/// - rng: Random number generator.
197+
/// - Returns: Matrix of random values.
198+
public static func random<G: RandomNumberGenerator>(_ rows: Int, _ columns: Int, using rng: inout G) -> Matrix {
199+
Scalar.random(rows: rows, columns: columns, rng: &rng)
200+
}
201+
}

Tests/RandomTests.swift

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,31 @@ struct RandomTests {
4343
#expect(cc == [0.8148898875141517, 0.3068460469993851, 0.3169041625894291, 0.30391479120110254])
4444
}
4545

46+
@Test func wyrandMatrix() {
47+
// Without seed
48+
let a = Matrix<Float>.random(3, 3)
49+
#expect(a[0, 0] < 1)
50+
51+
let aa = Matrix<Double>.random(3, 3)
52+
#expect(aa[0, 0] < 1)
53+
54+
// With seed
55+
let b = Matrix<Float>.random(2, 2, seed: 123456)
56+
#expect(b == [[0.81488985, 0.30684602], [0.31690413, 0.3039148]])
57+
58+
let bb = Matrix<Double>.random(2, 2, seed: 123456)
59+
#expect(bb == [[0.8148898875141517, 0.3068460469993851], [0.3169041625894291, 0.30391479120110254]])
60+
61+
// With generator
62+
var rng = Wyrand(seed: 123456)
63+
let c = Matrix<Float>.random(2, 2, using: &rng)
64+
#expect(c == [[0.81488985, 0.30684602], [0.31690413, 0.3039148]])
65+
66+
var rngg = Wyrand(seed: 123456)
67+
let cc = Matrix<Double>.random(2, 2, using: &rngg)
68+
#expect(cc == [[0.8148898875141517, 0.3068460469993851], [0.3169041625894291, 0.30391479120110254]])
69+
}
70+
4671
@Test func xoroshiro() {
4772
// Xoroshiro128Plus
4873
var rng = Xoroshiro128Plus(seed: (12274935454779349997, 7213431619994351707))
@@ -66,15 +91,15 @@ struct RandomTests {
6691
}
6792

6893
@Test func xoroshiroVector() {
69-
// Xoroshiro128Plus vector
94+
// Xoroshiro128Plus
7095
var rng = Xoroshiro128Plus(seed: (12274935454779349997, 7213431619994351707))
7196
let a = Vector<Float>.random(5, using: &rng)
7297
#expect(a == [0.05646646, 0.061123848, 0.13076729, 0.672823, 0.86956143])
7398

7499
let b = Vector<Double>.random(4, using: &rng)
75100
#expect(b == [0.12803432662341308, 0.17036647757843948, 0.8789760758360461, 0.6166839170638875])
76101

77-
// Xoroshiro128PlusPlus vector
102+
// Xoroshiro128PlusPlus
78103
var rngg = Xoroshiro128PlusPlus(seed: (12274935454779349997, 7213431619994351707))
79104
let c = Vector<Float>.random(5, using: &rngg)
80105
#expect(c == [0.84199363, 0.4994716, 0.7990605, 0.74130404, 0.2992577])
@@ -83,6 +108,24 @@ struct RandomTests {
83108
#expect(d == [0.6241821269000679, 0.18502002919976157, 0.2555743212406011, 0.5790431050892778])
84109
}
85110

111+
@Test func xoroshiroMatrix() {
112+
// Xoroshiro128Plus
113+
var rng = Xoroshiro128Plus(seed: (12274935454779349997, 7213431619994351707))
114+
let a = Matrix<Float>.random(2, 2, using: &rng)
115+
#expect(a == [[0.05646646, 0.061123848], [0.13076729, 0.672823]])
116+
117+
let b = Matrix<Double>.random(2, 2, using: &rng)
118+
#expect(b == [[0.8695614471421985, 0.12803432662341308], [0.17036647757843948, 0.8789760758360461]])
119+
120+
// Xoroshiro128PlusPlus
121+
var rngg = Xoroshiro128PlusPlus(seed: (12274935454779349997, 7213431619994351707))
122+
let c = Matrix<Float>.random(2, 2, using: &rngg)
123+
#expect(c == [[0.84199363, 0.4994716], [0.7990605, 0.74130404]])
124+
125+
let d = Matrix<Double>.random(2, 2, using: &rngg)
126+
#expect(d == [[0.2992577376225274, 0.6241821269000679], [0.18502002919976157, 0.2555743212406011]])
127+
}
128+
86129
@Test func xoshiro() {
87130
var rng = Xoshiro128Plus(seed: (2719949631, 2719949631, 2719949631, 2719949631))
88131
#expect(rng.next() == 1144931966)

0 commit comments

Comments
 (0)