-
Notifications
You must be signed in to change notification settings - Fork 87
/
CCRandom.ml
243 lines (194 loc) · 6.34 KB
/
CCRandom.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
(* This file is free software, part of containers. See file "license" for more details. *)
(** {1 Random Generators} *)
open CCShims_
include Random
type state = Random.State.t
type 'a t = state -> 'a
type 'a random_gen = 'a t
let return x _st = x
let flat_map f g st = f (g st) st
let (>>=) g f st = flat_map f g st
let map f g st = f (g st)
let (>|=) g f st = map f g st
let delay f st = f () st
let _choose_array a st =
if Array.length a = 0 then invalid_arg "CCRandom.choose_array";
a.(Random.State.int st (Array.length a))
let choose_array a st =
try Some (_choose_array a st st) with Invalid_argument _ -> None
let choose l =
let a = Array.of_list l in
choose_array a
let choose_exn l =
let a = Array.of_list l in
fun st -> _choose_array a st st
let choose_return l = _choose_array (Array.of_list l)
exception Pick_from_empty
let pick_list l =
let n = List.length l in
if n=0 then raise Pick_from_empty;
fun st ->
List.nth l (Random.State.int st n)
(*$Q
Q.(list small_int) (fun l -> \
l=[] || List.mem (run (pick_list l)) l)
*)
let pick_array a =
let n = Array.length a in
if n=0 then raise Pick_from_empty;
fun st -> Array.get a (Random.State.int st n)
let int i st = Random.State.int st i
let small_int = int 100
let int_range i j st = i + Random.State.int st (j-i+1)
let float f st = Random.State.float st f
let small_float = float 100.0
let float_range i j st = i +. Random.State.float st (j-.i)
(* TODO: sample functions *)
let replicate n g st =
let rec aux acc n =
if n = 0 then acc else aux (g st :: acc) (n-1)
in aux [] n
(* Sample without replacement using rejection sampling. *)
let sample_without_duplicates (type elt) ~cmp k (rng:elt t) st=
let module S = Set.Make(struct type t=elt let compare = cmp end) in
let rec aux s k =
if k <= 0 then
S.elements s
else
let x = rng st in
if S.mem x s then
aux s k
else
aux (S.add x s) (k-1)
in
if k<=0 then invalid_arg "sample_without_duplicates";
aux S.empty k
let list_seq l st = List.map (fun f -> f st) l
let split i st =
if i < 2 then None
else
let j = 1 + Random.State.int st (i-1) in
Some (j, i-j)
let _diff_list ~last l =
let rec diff_list acc = function
| [a] -> Some ( (last - a)::acc )
| a::( b::_ as r ) -> diff_list ( (b-a)::acc ) r
| [] -> None
in
diff_list [] l
(* Partition of an int into [len] integers uniformly.
We first sample (len-1) points from the set {1,..i-1} without replacement.
We sort these points and add back 0 and i, we have thus
x_0 = 0 < x_1 < x_2 < … < x_{len-1} < i = x_{len}.
If we define, y_k = x_{k+1} - x_{k} for k in 0 … (len-1), then by construction
∑_k y_k = ∑_k (x_{k+1} - x_k ) = x_{len} - x_0 = i. *)
let split_list i ~len st =
if len <= 1 then invalid_arg "Random.split_list";
if i >= len then (
let xs = sample_without_duplicates ~cmp:compare (len-1) (int_range 1 (i-1)) st in
_diff_list ~last:i (0::xs)
) else
None
(*$Q
Q.(pair small_int small_int) (fun (i,j) -> \
let len, n = 2+min i j, max i j in \
let l = QCheck.Gen.generate1 (split_list n ~len) in \
match l with None -> true | Some l -> l<> [] && List.for_all (fun x->x>0) l)
*)
let retry ?(max=10) g st =
let rec aux n =
match g st with
| None when n=0 -> None
| None -> aux (n-1) (* retry *)
| Some _ as res -> res
in
aux max
let rec try_successively l st = match l with
| [] -> None
| g :: l' ->
begin match g st with
| None -> try_successively l' st
| Some _ as res -> res
end
let (<?>) a b = try_successively [a;b]
exception Backtrack
let _choose_array_call a f st =
try
f (_choose_array a st)
with Invalid_argument _ -> raise Backtrack
let fix ?(sub1=[]) ?(sub2=[]) ?(subn=[]) ~base fuel st =
let sub1 = Array.of_list sub1
and sub2 = Array.of_list sub2
and subn = Array.of_list subn in
(* recursive function with fuel *)
let rec make fuel st =
if fuel=0 then raise Backtrack
else if fuel=1 then base st
else
_try_otherwise 0
[| _choose_array_call sub1 (fun f -> f (make (fuel-1)) st)
; _choose_array_call sub2
(fun f ->
match split fuel st with
| None -> raise Backtrack
| Some (i,j) -> f (make i) (make j) st
)
; _choose_array_call subn
(fun (len,f) ->
let len = len st in
match split_list fuel ~len st with
| None -> raise Backtrack
| Some l' ->
f (fun st -> List.map (fun x -> make x st) l') st
)
; base (* base case then *)
|]
and _try_otherwise i a =
if i=Array.length a then raise Backtrack
else try
a.(i) st
with Backtrack ->
_try_otherwise (i+1) a
in
make (fuel st) st
let pure x _st = x
let (<*>) f g st = f st (g st)
include CCShimsMkLet_.Make(struct
type nonrec 'a t = 'a t
let (>>=) = (>>=)
let (>|=) = (>|=)
let monoid_product a1 a2 st = a1 st, a2 st
end)
let __default_state = Random.State.make_self_init ()
let run ?(st=__default_state) g = g st
let uniformity_test ?(size_hint=10) k rng st =
let histogram = Hashtbl.create size_hint in
let add x = let n = try Hashtbl.find histogram x with Not_found -> 0 in
Hashtbl.replace histogram x (n + 1) in
let () =
for _i = 0 to ( k - 1 ) do
add (rng st)
done in
let cardinal = float_of_int (Hashtbl.length histogram) in
let kf = float_of_int k in
(* average number of points assuming an uniform distribution *)
let average = kf /. cardinal in
(* The number of points is a sum of random variables with binomial distribution *)
let p = 1. /. cardinal in
(* The variance of a binomial distribution with average p is *)
let variance = p *. (1. -. p ) in
(* Central limit theorem: a confidence interval of 4σ provides a false positive rate
of 0.00634% *)
let confidence = 4. in
let std = confidence *. (sqrt (kf *. variance)) in
let predicate _key n acc =
let (<) (a : float) b = Stdlib.(<) a b in
acc && abs_float (average -. float_of_int n) < std in
Hashtbl.fold predicate histogram true
(*$T split_list
run ~st:(QCheck_runner.random_state()) ( uniformity_test 50_000 (split_list 10 ~len:3) )
*)
(*$R
let open Containers in
ignore @@ List.random_choose [1;2;3] (Random.get_state())
*)