forked from mjambon/multiarmed
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmultiarmed.ml
More file actions
125 lines (112 loc) · 3.42 KB
/
Copy pathmultiarmed.ml
File metadata and controls
125 lines (112 loc) · 3.42 KB
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
open Printf
let default_random_fraction = 0.05
let default_update_contribution = 0.01
type t = {
estimates : float array;
picked : int array;
successes : int array;
random_fraction : float;
update_contribution : float;
rng : Random.State.t;
}
let init
?(random_fraction = default_random_fraction)
?(update_contribution = default_update_contribution)
n =
if n < 1 then
invalid_arg (sprintf "Multiarmed.init: %i" n);
if random_fraction > 1. || random_fraction < 0. then
invalid_arg (sprintf "Multiarmed.init: random_fraction %g"
random_fraction);
if update_contribution < 0. || update_contribution > 1. then
invalid_arg (sprintf "Multiarmed.init: update_contribution %g"
update_contribution);
{
estimates = Array.make n 1.0;
picked = Array.make n 0;
successes = Array.make n 0;
random_fraction;
update_contribution;
rng = Random.State.make_self_init ();
}
let array_max a =
assert (Array.length a > 0);
let index = ref 0 in
let value = ref a.(0) in
for i = 1 to Array.length a - 1 do
if a.(i) > !value then (
index := i;
value := a.(i)
)
done;
(!index, !value)
let pick x =
let a = x.estimates in
if Random.State.float x.rng 1. < x.random_fraction then
Random.State.int x.rng (Array.length a)
else
fst (array_max a)
let feedback x i success =
let a = x.estimates in
if i < 0 || i >= Array.length a then
invalid_arg (sprintf "Multiarmed.feedback: invalid index %i" i);
let contrib =
if success then
x.update_contribution
else
0.
in
a.(i) <- contrib +. (1. -. x.update_contribution) *. a.(i)
(*
Produce an array of random variables following Bernoulli distributions.
Optionally, the parameter p can change.
*)
let make_random_variables ?(change_p_every = 0) rng n =
printf "Expected values:\n";
let count = ref 0 in
let a = Array.init n (fun i -> fun () -> assert false) in
let rec make_var i =
let p = Random.State.float rng 1.0 in
printf "[%i] %.3f\n" i p;
fun () ->
let result = Random.State.float rng 1.0 <= p in
incr count;
if change_p_every > 0
&& !count mod change_p_every = 0
&& !count > 0 then
init_array ();
result
and init_array () =
Array.iteri (fun i _ -> a.(i) <- make_var i) a;
printf "--\n"
in
init_array ();
a
let test ?(n = 10) ?(trials = 100) ?(change_p_every = 1000) () =
printf "Number of random variables to choose from: %i\n" n;
printf "Number of trials: %i\n%!" trials;
if change_p_every < trials then
printf "Change all distributions every %i trials\n%!" change_p_every;
let x = init ~random_fraction: 0.05 ~update_contribution: 0.1 n in
let random_variables = make_random_variables ~change_p_every x.rng n in
let successes = ref 0 in
for i = 1 to trials do
let j = pick x in
let success = random_variables.(j) () in
feedback x j success;
if success then (
incr successes;
x.successes.(j) <- x.successes.(j) + 1
);
x.picked.(j) <- x.picked.(j) + 1;
done;
let global_success_rate = float !successes /. float trials in
printf "Individual success rates:\n";
Array.iteri (fun i r ->
let successes = x.successes.(i) in
let total = x.picked.(i) in
printf "[%i] %.3f %i/%i\n"
i (float successes /. float total) successes total
) x.estimates;
printf "Global success rate: %.3f\n" global_success_rate;
flush stdout