open System
let random = new Random()
let flip f a b = f b a
let keepLeft f (x,y) = x, f y
let keepRight f (x,y) = f x, y
let fst3 (a,_,_) = a
let fst_snd3 (a,b,_) = a,b
let scaleTo rmin rmax rangemin rangemax value =
let adjrmin, adjrmax, adjval =
if rangemin < 0. then 0., -rangemin + rangemax , -rangemin + value
else rangemin, rangemax , value //translate to 0
(adjval - adjrmin)/(adjrmax - adjrmin) * (rmax-rmin) + rmin
module Array =
let inline normalize (a: _[]) =
let tot = Array.sum a
Array.map (flip (/) tot) a
let inline normalizeWeights (a: ('a * 'b) []) =
let tot = Array.sumBy snd a
Array.map (keepLeft (flip (/) tot)) a
let cdf p =
p |> Array.fold (fun (total, list) v ->
let cd = v + total
cd, cd::list) (0., [])
|> snd
|> Array.ofList
let getDiscreteSampleFromCDF (pcdf:float[]) =
let k, pcdlen = random.NextDouble() * pcdf.[0], pcdf.Length - 1
let rec cummProb idx = if k > pcdf.[idx] then cummProb (idx - 1) else idx
abs(cummProb pcdlen
- pcdlen
)
let discreteSample p = cdf p |> getDiscreteSampleFromCDF
let round (n:int) (x:float) = Math.Round(x,n)
//////////////////////////////
type Go = ``Do 🛑`` | ``Do 🚙``
type Decision = ``Do 😏`` of float [] | ``Do 🚦``
let moves = [|``Do 🛑``;``Do 🚙``|]
let multiplicativeWeightsUpdate rate minAmount maxAmount (oldweights:float []) (results:seq<float>) =
let lossbounded = scaleTo -1. 1. minAmount maxAmount
results |> Seq.mapi (fun i r -> oldweights.[i] * (1. + rate * lossbounded (min r maxAmount)))
|> Seq.toArray
|> Array.normalize
let learningExpert2 prevWeights rate res = multiplicativeWeightsUpdate rate -100. 1. prevWeights res
let sampleLight p = if random.NextDouble() < p then ``Do 🛑``,``Do 🚙`` else ``Do 🚙``,``Do 🛑``
let randomLight () = if random.NextDouble() < 0.5 then ``Do 🛑`` else ``Do 🚙``
let faultyLight p2 p = if random.NextDouble() < p then randomLight(),randomLight() else sampleLight p2
//============
let drive =
function
| (``Do 🛑``,``Do 🛑``) -> (0.,0.)
| (``Do 🛑``,``Do 🚙``) -> (0.,1.)
| (``Do 🚙``,``Do 🛑``) -> (1.,0.)
| (``Do 🚙``,``Do 🚙``) -> (-100.,-100.)
let inline getExpertVerbose indicated signal (rules:_[]) =
printfn "Signal = %A" signal
let other = match indicated with None -> "N/A" | Some a -> string a
printfn "Other signal: %s" other
let cs = [|for (p,r,f) in rules do
match f indicated signal with
| Some e -> printfn "Rule %s with weight %A matched" r p; yield e, p
| None -> ()|]
|> Array.normalizeWeights
printfn "Normalized %A" cs
let cs' = Array.groupBy fst cs |> Array.map (fun (s,c) -> s, Array.sumBy snd c)
printfn "%A" cs'
let ps = Array.map snd cs
fst (cs.[discreteSample ps])
let inline getExpert2 indicated signal (rules:_[]) =
let cs = [|for (p,_,f) in rules do
match f indicated signal with
| Some c -> yield c,p
| None -> ()|] |> Array.normalizeWeights
let ps = Array.map snd cs
fst(cs.[discreteSample ps])
let learnExperts3 signal rate opmove (rules:_[]) =
let costs =
[|for (p,_,f) in rules do
match f (Some opmove) signal with //<== change (Some opmove) to (None), see what happens
| Some choice -> yield (p * fst(drive (choice,opmove)))
| None -> yield 0.|]
let w = Array.map fst3 rules
Array.Copy(learningExpert2 w rate costs,w,w.Length)
for i in 0..w.Length - 1 do
let _,name,f = rules.[i]
rules.[i] <- w.[i],name,f
let learnExpertsVerbose signal rate opmove (rules:_[]) =
let costs = [|for (p,hh,f) in rules do
match f (Some opmove) signal with
| Some choice ->
printfn "%A" ((p ,hh,opmove,choice, fst(drive (choice,opmove))) )
yield (p * fst(drive (choice,opmove)))
| None -> yield 0.|]
let w = Array.map fst3 rules
Array.Copy(learningExpert2 w rate costs,w,w.Length)
for i in 0..w.Length - 1 do
let _,name,f = rules.[i]
rules.[i] <- w.[i],name,f
let rules =
let num_moves = (float moves.Length)*(float moves.Length)*(float moves.Length)
[|for m in moves do
for m2 in moves do
for m3 in moves ->
let rulename
= sprintf "if signal=%A && other=%A then %A" m m2 m3
1./num_moves,
rulename,
fun other signal ->
match other with
| None -> if signal = m then Some m3 else None
| Some indicated -> if signal = m && indicated = m2 then Some m3 else None|]
let gatherStats n look1 look2 otherweight heroweights =
let lists = ResizeArray()
for i in 0..n do
let light1, light2 = sampleLight 0.5
let p1mov = getExpert2 (if look1 then Some light2 else None) light1 heroweights
let p2mov = getExpert2 (if look2 then Some p1mov else None) light2 otherweight
lists.Add(p1mov,p2mov)
lists
let round100 x
= sprintf "%A%%" (round
2 ((float x
)*100.
))
let learner2 () =
let mutable heroweights = Array.map id rules
let mutable otherweight = Array.map id rules
let rate = 0.5
for _ in 0..99999 do
let light1, light2 = sampleLight 0.5
let p1mov = getExpert2 (Some light2) light1 heroweights // <== Change (Some light2) to None
let p2mov = getExpert2 (Some p1mov) light2 otherweight
learnExperts3 light1 rate p2mov heroweights
learnExperts3 light2 rate p1mov otherweight
let gathered = gatherStats 999 true true otherweight heroweights
gathered.ToArray()
|> Array.groupBy id
|> Array.map (keepLeft (Array.length >> float))
|> Array.normalizeWeights
|> Array.map (keepLeft round100)
|> printfn "%A\n"
printfn "Rules 1: %A" (Array.map fst_snd3 heroweights |> Array.filter (fst >> round 2 >> (<>) 0.) |> Array.map (keepRight round100))
printfn "Rules 2: %A" (Array.map fst_snd3 otherweight |> Array.filter (fst >> round 2 >> (<>) 0.) |> Array.map (keepRight round100))
learner2()