+ All Categories
Home > Documents > Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1...

Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1...

Date post: 18-Aug-2020
Category:
Upload: others
View: 0 times
Download: 0 times
Share this document with a friend
81
Transcript
Page 1: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Gary Fredericks Purely Random Clojure/West 2015 1 / 81

Page 2: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random

1 user> (def r (java.util.Random. 42))

2 #'user/r

3 user> (.nextInt r)

4 -1170105035

5 user> (.nextInt r)

6 234785527

Gary Fredericks Purely Random Clojure/West 2015 2 / 81

Page 3: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random in Clojure

1 (defn create

2 [seed]

3 {:state (bit-xor seed 0x5deece66d)})

4

5 (defn next-int

6 [{:keys [^long state]}]

7 (let [new-state (-> state

8 (unchecked-multiply 0x5deece66d)

9 (unchecked-add 0xb))

10 x (-> new-state

11 (bit-shift-right 16)

12 (unchecked-int))]

13 [x {:state new-state}]))

Gary Fredericks Purely Random Clojure/West 2015 3 / 81

Page 4: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Mutable vs. Immutable

1 user> (def r (java.util.Random. 42))

2 #'user/r

3 user> (.nextInt r)

4 -1170105035

5 user> (.nextInt r)

6 234785527

78 ;; immutable version

9 user> (def r (create 42))

10 #'user/r

11 user> r

12 {:state 25214903879}

13 user> (next-int r)

14 [-1170105035 {:state 8602080079250839110}]

15 user> (next-int r)

16 [-1170105035 {:state 8602080079250839110}]

17 user> (next-int (second *1))

18 [234785527 {:state 7522434139496587225}]

Gary Fredericks Purely Random Clojure/West 2015 4 / 81

Page 5: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Roadmap

Splittability and Composition

Basic Example, De�nitions

Case Study: test.checkImplementing Splittable RNGs in Clojure

Poorly

Better

Faster

Gary Fredericks Purely Random Clojure/West 2015 5 / 81

Page 6: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Splittability and Composition

Gary Fredericks Purely Random Clojure/West 2015 6 / 81

Page 7: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Splittability and Composition

A Tale of Two Seqs

Gary Fredericks Purely Random Clojure/West 2015 7 / 81

Page 8: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Requirements

1 (defn pair-of-lazy-seqs

2 "Given a seed, returns [xs ys]

3 where xs and ys are both

4 (different) lazy infinite seqs

5 of random numbers."

6 [seed]

7 ;; ???

8 )

Gary Fredericks Purely Random Clojure/West 2015 8 / 81

Page 9: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

With java.util.Random

1 (defn pair-of-lazy-seqs

2 [seed]

3 (let [r (java.util.Random. seed)]

4 [(repeatedly #(.nextInt r))

5 (repeatedly #(.nextInt r))]))

Gary Fredericks Purely Random Clojure/West 2015 9 / 81

Page 10: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Let's use it

1 (let [[xs ys] (pair-of-lazy-seqs 42)]

2 [(take 4 xs) (take 4 ys)])

3 =>

4 [(-1170105035 234785527 -1360544799 205897768)

5 (1325939940 -248792245 1190043011 -1255373459)]

Gary Fredericks Purely Random Clojure/West 2015 10 / 81

Page 11: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Let's use it

1 (let [[xs ys] (pair-of-lazy-seqs 42)]

2 [(take 4 xs) (take 4 ys)])

3 =>

4 [(-1170105035 234785527 -1360544799 205897768)

5 (1325939940 -248792245 1190043011 -1255373459)]

6

7 (let [[xs ys] (pair-of-lazy-seqs 42)]

8 [(first xs) (first ys)])

9 => [-1170105035 234785527]

Gary Fredericks Purely Random Clojure/West 2015 11 / 81

Page 12: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

With java.util.Random

1 (defn pair-of-lazy-seqs

2 [seed]

3 (let [r (java.util.Random. seed)]

4 [(repeatedly #(.nextInt r))

5 (repeatedly #(.nextInt r))]))

Gary Fredericks Purely Random Clojure/West 2015 12 / 81

Page 13: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

With the immutable clojure RNG

1 (defn random-nums

2 [rng]

3 (lazy-seq

4 (let [[x rng2] (next-int rng)]

5 (cons x (random-nums rng2)))))

6

7 (defn pair-of-lazy-seqs

8 [seed]

9 (let [rng (create seed)]

10 [(random-nums rng)

11 (random-nums ; ????

12 )]))

Gary Fredericks Purely Random Clojure/West 2015 13 / 81

Page 14: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Concept Space

Gary Fredericks Purely Random Clojure/West 2015 14 / 81

Page 15: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

With a splittable RNG

1 (defn random-nums2 [rng]3 (lazy-seq4 (let [[rng1 rng2] (split rng)5 x (rand-int rng1)]6 (cons x (random-nums rng2)))))7

8 (defn pair-of-lazy-seqs9 [seed]10 (let [rng (create seed)11 [rng1 rng2] (split rng)]12 [(random-nums rng1)13 (random-nums rng2)]))

Gary Fredericks Purely Random Clojure/West 2015 15 / 81

Page 16: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Splittability and Composition

test.check

Gary Fredericks Purely Random Clojure/West 2015 16 / 81

Page 17: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

gen-xs-and-x

1 (def gen-xs-and-x

2 "Generates a pair [xs x] where xs is a list of

3 numbers and x is a number in that list."

4 (gen/bind (gen/not-empty (gen/list gen/nat))

5 (fn [xs]

6 (gen/tuple (gen/return xs)

7 (gen/elements xs)))))

8

9 (gen/sample gen-xs-and-x)

10 =>

11 ([(0) 0]

12 [(3 3 0) 0]

13 [(1 2) 2]

14 [(2 0 3 1) 1]

15 [(4 0 1 3) 1]

16 ...)

Gary Fredericks Purely Random Clojure/West 2015 17 / 81

Page 18: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

lists-don't-have-duplicates

1 (def lists-don't-have-duplicates

2 (prop/for-all [[xs x] gen-xs-and-x]

3 (let [x-count (->> xs

4 (filter #{x})

5 (count))]

6 (= 1 x-count))))

Gary Fredericks Purely Random Clojure/West 2015 18 / 81

Page 19: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

test.check shrinking

1 user> (quick-check 100 lists-don't-have-duplicates)

2 {:fail [[(4 4 5 4 2) 4]],

3 :failing-size 6,

4 :num-tests 7,

5 :result false,

6 :seed 1426989885725,

7 :shrunk {:depth 3,

8 :result false,

9 :smallest [[(4 4) 4]],

10 :total-nodes-visited 16}}

Gary Fredericks Purely Random Clojure/West 2015 19 / 81

Page 20: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

test.check shrink tree

Gary Fredericks Purely Random Clojure/West 2015 20 / 81

Page 21: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

test.check shrink tree

Gary Fredericks Purely Random Clojure/West 2015 21 / 81

Page 22: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

test.check shrink tree

Gary Fredericks Purely Random Clojure/West 2015 22 / 81

Page 23: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

test.check

The Problem

the lazy shrink-tree is nondeterministic

The Solution

Use an immutable, splittable RNG.

But where do you �nd such a thing?

Gary Fredericks Purely Random Clojure/West 2015 23 / 81

Page 24: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Splittability and Composition

Summary

Gary Fredericks Purely Random Clojure/West 2015 24 / 81

Page 25: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Okay So

Linear RNGs hinder composition

Programs are either nondeterministic or impossible to write

Splittable RNGs are less common, but composition-friendly

test.check impl is fragile because of its linear RNG

Gary Fredericks Purely Random Clojure/West 2015 25 / 81

Page 26: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementations

Gary Fredericks Purely Random Clojure/West 2015 26 / 81

Page 27: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementing Splittable RNGs in Clojure

Poorly

Better

Faster

Gary Fredericks Purely Random Clojure/West 2015 27 / 81

Page 28: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementations

Low Quality Implementations

Gary Fredericks Purely Random Clojure/West 2015 28 / 81

Page 29: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random

Gary Fredericks Purely Random Clojure/West 2015 29 / 81

Page 30: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random

Gary Fredericks Purely Random Clojure/West 2015 30 / 81

Page 31: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random

Gary Fredericks Purely Random Clojure/West 2015 31 / 81

Page 32: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random

Gary Fredericks Purely Random Clojure/West 2015 32 / 81

Page 33: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as a lazy seq

Gary Fredericks Purely Random Clojure/West 2015 33 / 81

Page 34: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random: splitting the seq

Gary Fredericks Purely Random Clojure/West 2015 34 / 81

Page 35: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random

Gary Fredericks Purely Random Clojure/West 2015 35 / 81

Page 36: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as 1 32-count sequence

Gary Fredericks Purely Random Clojure/West 2015 36 / 81

Page 37: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as 2 16-count sequences

Gary Fredericks Purely Random Clojure/West 2015 37 / 81

Page 38: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as 4 8-count sequences

Gary Fredericks Purely Random Clojure/West 2015 38 / 81

Page 39: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as 8 4-count sequences

Gary Fredericks Purely Random Clojure/West 2015 39 / 81

Page 40: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as 16 2-count sequences

Gary Fredericks Purely Random Clojure/West 2015 40 / 81

Page 41: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.Random as 32 1-count sequences

Gary Fredericks Purely Random Clojure/West 2015 41 / 81

Page 42: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Haskell's System.Random

1 stdSplit :: StdGen -> (StdGen, StdGen)

2 stdSplit std@(StdGen s1 s2)

3 = (left, right)

4 where

5 -- no statistical foundation for this!

6 left = StdGen new_s1 t2

7 right = StdGen t1 new_s2

89 new_s1 | s1 == 2147483562 = 1

10 | otherwise = s1 + 1

1112 new_s2 | s2 == 1 = 2147483398

13 | otherwise = s2 - 1

1415 StdGen t1 t2 = snd (next std)

Gary Fredericks Purely Random Clojure/West 2015 42 / 81

Page 43: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

The Lesson

Splittabilizing a linear algorithm can be tricky.

Gary Fredericks Purely Random Clojure/West 2015 43 / 81

Page 44: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementations

High Quality Implementations

Gary Fredericks Purely Random Clojure/West 2015 44 / 81

Page 45: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Splittable Pseudorandom Number Generatorsusing Cryptographic Hashing

Koen Claessen Michał H. PałkaChalmers University of Technology

[email protected] [email protected]

AbstractWe propose a new splittable pseudorandom number generator(PRNG) based on a cryptographic hash function. Splittable PRNGs,in contrast to linear PRNGs, allow the creation of two (seemingly)independent generators from a given random number generator.Splittable PRNGs are very useful for structuring purely functionalprograms, as they avoid the need for threading around state. Weshow that the currently known and used splittable PRNGs are eithernot efficient enough, have inherent flaws, or lack formal argumentsabout their randomness. In contrast, our proposed generator canbe implemented efficiently, and comes with a formal statementsand proofs that quantify how ‘random’ the results are that aregenerated. The provided proofs give strong randomness guaranteesunder assumptions commonly made in cryptography.

Categories and Subject Descriptors D.1.1 [Programming Tech-niques]: Applicative (Functional) Programming; D.3.3 [Program-ming Languages]: Language Constructs and Features

General Terms Algorithms, Languages

Keywords splittable pseudorandom number generators, provablesecurity, Haskell

1. IntroductionSplittable pseudorandom number generators (PRNGs) are very usefulfor structuring purely functional programs that deal with random-ness. They allow different parts of the program to independently(without interaction) generate random values, thus avoiding thethreading of a random seed through the whole program [10]. More-over, splittable PRNGs are essential when generating random infinitevalues, such as random infinite lists in a lazy language, or randomfunctions. In addition, deterministic distribution of parallel randomnumber streams, which is of interest to the High-Performance Com-puting community [22, 26], can be realised using splitting.

In Haskell, the standard module System.Random provides adefault implementation of a splittable generator StdGen, with thefollowing API:

split :: StdGen -> (StdGen, StdGen)next :: StdGen -> (Int, StdGen)

[Copyright notice will appear here once ’preprint’ option is removed.]

The function split creates two new, independent generators froma given generator. The function next can be used to create onerandom value. A user of this API is not supposed to use both nextand split on the same argument; doing so voids all warrantiesabout promised randomness.

The property-based testing framework QUICKCHECK [13]makes heavy use of splitting. Let us see it in action. Considerthe following simple (but somewhat contrived) property:

newtype Int14 = Int14 Intderiving Show

instance Arbitrary Int14 wherearbitrary = Int14 ‘fmap‘ choose (0, 13)

prop_shouldFail (_, Int14 a) (Int14 b) = a /= b

We define a new type Int14 for representing integers from 0 to 13.Next, we create a random generator for it that randomly picks anumber from 0 to 13. Finally, we define a property, which states thattwo randomly picked Int14 numbers, one of which is a componentof a randomly picked pair, are always unequal.

Testing the property yields the following result:

*Main> quickCheckWithstdArgs { maxSuccess = 10000 } prop_shouldFail

+++ OK, passed 10000 tests.

Even though the property is false (we would expect one of every 14tests to fail), all 10000 tests succeed!

The reason for this surprising behaviour is a previously unknownflaw in the standard Haskell pseudorandom number generatorused by QUICKCHECK during testing. The PRNG should pick allcombinations of numbers 0–13 for a and b, but in fact combinationswhere a and b are the same number are never picked.

It turns out that the StdGen standard generator used in currentHaskell compilers contains an ad hoc implementation of splitting.The current implementation is the source of the randomness flaw1

demonstrated above. The flaw requires a particular pattern of splitoperations to manifest and results in very strong correlation ofgenerated numbers. In fact, when 13 in the Int14 generator isreplaced by other numbers from range 1–500, the problem arises for465 of them! Unfortunately, this pattern of splits is simple and likelyto arise often in typical usage of QuickCheck. Because of this, wecannot be sure that QuickCheck properties that pass a large numberof tests are true with high probability.

Unfortunately, research devoted to pseudorandom generationhas mainly concentrated on linear generators, which do not supporton-demand splitting. Several attempts have been made at extending

1 http://hackage.haskell.org/trac/ghc/ticket/3575 and .../3620

To appear in the Proceedings of Haskell Symposium 2013 1 2013/9/15

Gary Fredericks Purely Random Clojure/West 2015 45 / 81

Page 46: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Splitting Tree

1 (let [rng1 (make-rng seed)

2 [rng2 rng3] (split rng1)

3 x1 (rand-long rng2)

4 [rng4 rng5] (split rng3)

5 x2 (rang-long rng4)]

6 "hooray")

Gary Fredericks Purely Random Clojure/West 2015 46 / 81

Page 47: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linear Tree

Gary Fredericks Purely Random Clojure/West 2015 47 / 81

Page 48: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Balanced Tree

Gary Fredericks Purely Random Clojure/West 2015 48 / 81

Page 49: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Pseudorandom Function

Gary Fredericks Purely Random Clojure/West 2015 49 / 81

Page 50: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Tree Path

Gary Fredericks Purely Random Clojure/West 2015 50 / 81

Page 51: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

SHA1Random

1 (deftype SHA1Random [seed path]2

3 IRandom4

5 (rand-long [_]6 (bytes->long (sha1 (str seed path))))7

8 (split [_]9 [(SHA1Random. seed (conj path 0))10 (SHA1Random. seed (conj path 1))]))11

12 (defn sha1-random13 [seed]14 (SHA1Random. seed []))

Gary Fredericks Purely Random Clojure/West 2015 51 / 81

Page 52: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementations

Testing Quality

Gary Fredericks Purely Random Clojure/West 2015 52 / 81

Page 53: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Dieharder

#=============================================================================#

# dieharder version 3.31.1 Copyright 2003 Robert G. Brown #

#=============================================================================#

Usage:

dieharder [-a] [-d dieharder test number] [-f filename] [-B]

[-D output flag [-D output flag] ... ] [-F] [-c separator]

[-g generator number or -1] [-h] [-k ks_flag] [-l]

[-L overlap] [-m multiply_p] [-n ntuple]

[-p number of p samples] [-P Xoff]

[-o filename] [-s seed strategy] [-S random number seed]

[-n ntuple] [-p number of p samples] [-o filename]

[-s seed strategy] [-S random number seed]

[-t number of test samples] [-v verbose flag]

[-W weak] [-X fail] [-Y Xtrategy]

[-x xvalue] [-y yvalue] [-z zvalue]

Gary Fredericks Purely Random Clojure/West 2015 53 / 81

Page 54: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization

Gary Fredericks Purely Random Clojure/West 2015 54 / 81

Page 55: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Right Linear

Gary Fredericks Purely Random Clojure/West 2015 55 / 81

Page 56: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Left Linear

Gary Fredericks Purely Random Clojure/West 2015 56 / 81

Page 57: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Alternating

Gary Fredericks Purely Random Clojure/West 2015 57 / 81

Page 58: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Balanced

Gary Fredericks Purely Random Clojure/West 2015 58 / 81

Page 59: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Right Lumpy

Gary Fredericks Purely Random Clojure/West 2015 59 / 81

Page 60: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Left Lumpy

Gary Fredericks Purely Random Clojure/West 2015 60 / 81

Page 61: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Linearization - Fibonacci

Gary Fredericks Purely Random Clojure/West 2015 61 / 81

Page 62: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Dieharder Results

Algorithm Linearization PASSED WEAK FAIL

j.u.Random (inherent) 95 13 6

SHA1 left-linear 111 3 0SHA1 right-linear 112 2 0SHA1 alternating 114 0 0SHA1 left-lumpy 110 4 0SHA1 right-lumpy 112 2 0SHA1 balanced 112 2 0SHA1 �bonacci 109 5 0

Gary Fredericks Purely Random Clojure/West 2015 62 / 81

Page 63: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementations

Less Slow Implementations

Gary Fredericks Purely Random Clojure/West 2015 63 / 81

Page 64: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Varying the hash function

Try a faster (noncryptographic?) pseudorandomfunction, test its quality.

Gary Fredericks Purely Random Clojure/West 2015 64 / 81

Page 65: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

java.util.SplittableRandom

1 public class SplittableRandom{

2

3 public SplittableRandom(long seed){...}

4

5 public long nextLong(){...};

6

7 public SplittableRandom split(){...};

8

9 }

Gary Fredericks Purely Random Clojure/West 2015 65 / 81

Page 66: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

The java.util.SplittableRandom Algorithm

Gary Fredericks Purely Random Clojure/West 2015 66 / 81

Page 67: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

(SplittableRandom. 24)

Gary Fredericks Purely Random Clojure/West 2015 67 / 81

Page 68: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

(-> 24 (SplittableRandom.) (.nextLong))

Gary Fredericks Purely Random Clojure/West 2015 68 / 81

Page 69: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

(-> 24 (SplittableRandom.) (.split))

Gary Fredericks Purely Random Clojure/West 2015 69 / 81

Page 70: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

(deftype IJUSR ...)

1 (deftype IJUSR [^long gamma ^long state]

2 IRandom

3 (rand-long [_]

4 (-> state (+ gamma) (mix-64)))

5 (split [this]

6 (let [state1 (+ gamma state)

7 state2 (+ gamma state1)

8 new-state (mix-64 state1)

9 new-gamma (mix-gamma state2)]

10 [(IJUSR. gamma state2)

11 (IJUSR. new-gamma new-state)])))

Gary Fredericks Purely Random Clojure/West 2015 70 / 81

Page 71: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Benchmarks

linear

left-linear

right-lin

ear

alternatin

g

left-lumpy

right-lu

mpy

balan

ced

�bonacci

0

50

100

150

milliseconds

Criterium tests XORing 1,000,000 random numbers

JUR IJUSR

Gary Fredericks Purely Random Clojure/West 2015 71 / 81

Page 72: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Benchmarks w/ SHA1

linear

left-linear

right-lin

ear

alternatin

g

left-lumpy

right-lu

mpy

balan

ced

�bonacci

0

500

1,000

milliseconds

Criterium tests XORing 1,000,000 random numbers

JUR IJUSR SHA1

Gary Fredericks Purely Random Clojure/West 2015 72 / 81

Page 73: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Dieharder Results

Algorithm Linearization PASSED WEAK FAIL

j.u.Random (inherent) 95 13 6

SHA1 left-linear 111 3 0SHA1 right-linear 112 2 0SHA1 alternating 114 0 0SHA1 left-lumpy 110 4 0SHA1 right-lumpy 112 2 0SHA1 balanced 112 2 0SHA1 �bonacci 109 5 0

IJUSR left-linear 108 6 0IJUSR right-linear 111 3 0IJUSR alternating 109 5 0IJUSR left-lumpy 113 1 0IJUSR right-lumpy 114 0 0IJUSR balanced 114 0 0IJUSR �bonacci 111 3 0

Gary Fredericks Purely Random Clojure/West 2015 73 / 81

Page 74: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Implementations

Summary

Gary Fredericks Purely Random Clojure/West 2015 74 / 81

Page 75: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Okay So

Linear RNGs cannot be trivially splittabilized

Recent research provides promising options

Gary Fredericks Purely Random Clojure/West 2015 75 / 81

Page 76: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Epilogue

Gary Fredericks Purely Random Clojure/West 2015 76 / 81

Page 77: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Convert test.check to JavaUtilSplittableRandom

[org.clojure/test.check "0.8.0-ALPHA"]

Gary Fredericks Purely Random Clojure/West 2015 77 / 81

Page 78: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Slowdown

Measuring the slowdown on test.check's own test suite.

(bench (clojure.test/run-all-tests))

Before 3.06 ± 0.045 seconds

After 3.56 ± 0.058 seconds

16.3% slower

lein benchmark-task 20 test

Before 7.62 ± 0.182 seconds

After 8.34 ± 0.210 seconds

9.3% slower

Gary Fredericks Purely Random Clojure/West 2015 78 / 81

Page 79: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Empossibleized Future Features

Parallelizing tests

Resuming shrinks

Parallelized shrinks

Custom shrinking algorithms

Generating lazy seqs

Replaying a particular test with a speci�c "seed"

Gary Fredericks Purely Random Clojure/West 2015 79 / 81

Page 80: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

We Have Come Now To The End

Splittable RNGs are necessary for composingfunctional programs

There are existing splittable algorithms, includingjava.util.SplittableRandom

Using the SplittableRandom algorithm madetest.check more robust

Gary Fredericks Purely Random Clojure/West 2015 80 / 81

Page 81: Gary Fredericks Purely Random Clojure/West 2015 1 / 81Apr 21, 2015  · java.util.Random 1 user>(defr(java.util.Random.42)) 2 #'user/r 3 user>(.nextIntr) 4-1170105035 5 user>(.nextIntr)

Thank You

And also thanks to

Reid Draper

Alex Miller

BibliographyClaessen, K. ; Palka, M. (2013) "Splittable Pseudorandom Number Generators usingCryptographic Hashing". Proceedings of Haskell Symposium 2013 pp. 47-58.

Guy L. Steele, Jr., Doug Lea, and Christine H. Flood. 2014. Fast splittable pseudorandomnumber generators. In Proceedings of the 2014 ACM International Conference on ObjectOriented Programming Systems Languages & Applications (OOPSLA '14). ACM, New York,NY, USA, 453-472. DOI=10.1145/2660193.2660195http://doi.acm.org/10.1145/2660193.2660195

Gary Fredericks Purely Random Clojure/West 2015 81 / 81


Recommended