package bechamel
Yet Another Benchmark in OCaml
Install
Dune Dependency
Authors
Maintainers
Sources
bechamel-0.5.0.tbz
sha256=2f9aa544395fa62ea067352782988bf94d977162d4d3ee6e321e49a29c5ec868
sha512=80af7d3015fd3e63514b6241190f7fc96a1329480d98526dc426c8e24fdf2f196917a9bdae72ac7234e21b2835c38a5ac989cfc00360d9530afe52f97f58c79f
doc/src/bechamel/analyze.ml.html
Source file analyze.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 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
module OLS = struct module Ci95 = struct (* 95% confidence interval *) type t = { r : float; l : float } let pp ppf x = Fmt.pf ppf "@[<hov>%f to %f@]" x.r x.l let bad = { r = neg_infinity; l = neg_infinity } end (* Linear regression inputs *) let make_lr_inputs ?indices ~responder ~predictors m = let responder_accessor = Measurement_raw.get ~label:responder in let predictors_accessor = Array.map (fun label -> Measurement_raw.get ~label) predictors in match indices with | Some indices -> ( Array.map (fun i -> Array.map (fun a -> a m.(i)) predictors_accessor) indices , Array.map (fun i -> responder_accessor m.(i)) indices ) | None -> ( Array.init (Array.length m) (fun i -> Array.map (fun a -> a m.(i)) predictors_accessor) , Array.init (Array.length m) (fun i -> responder_accessor m.(i)) ) type t = { predictors : string array ; responder : string ; value : (v, [ `Msg of string ]) result } and v = { estimates : float array ; ci95 : Ci95.t array option ; r_square : float option } let r_square m ~responder ~predictors r = let predictors_matrix, responder_vector = make_lr_inputs ~responder ~predictors m in let sum_responder = Array.fold_left ( +. ) 0. responder_vector in let mean = sum_responder /. float (Array.length responder_vector) in let tot_ss = ref 0. in let res_ss = ref 0. in let predicted i = let x = ref 0. in for j = 0 to Array.length r - 1 do x := !x +. (predictors_matrix.(i).(j) *. r.(j)) done; !x in for i = 0 to Array.length responder_vector - 1 do tot_ss := !tot_ss +. ((responder_vector.(i) -. mean) ** 2.); res_ss := !res_ss +. ((responder_vector.(i) -. predicted i) ** 2.) done; 1. -. (!res_ss /. !tot_ss) (* XXX(dinosaure): see core_bench and [(1/e)^bootstrap_threshold < 0.05/predictors] which describe area on top of logarithm curve (where maximum seems close to 6~7). *) let bootstrap_threshold = 10 let can_bootstrap ~responder ~predictors m = let matrix, _ = make_lr_inputs ~responder ~predictors m in let non_zero = Array.make (Array.length predictors) 0 in let non_zero_cols = ref 0 in Array.iter (fun row -> for i = 0 to Array.length non_zero - 1 do if row.(i) <> 0.0 then ( non_zero.(i) <- non_zero.(i) + 1; if non_zero.(i) = bootstrap_threshold then incr non_zero_cols) done) matrix; if !non_zero_cols = Array.length non_zero then true else false let () = Random.self_init () let random_indices_in_place ~max arr = let len = Array.length arr in for i = 0 to len - 1 do arr.(i) <- Random.int max done let quantile_of_array ?(failures = 0) ~len ~low ~high arr = Array.sort (compare : float -> float -> int) arr; let index q = int_of_float ((float len *. q) +. (0.5 *. float failures)) in let extended_get i = if i >= len then infinity else arr.(i) in let l = extended_get ((min : int -> int -> int) (index low) (len - 1)) in let r = extended_get ((max : int -> int -> int) (index high) failures) in Ci95.{ l; r } let bootstrap ~trials m ~responder ~predictors = let p = Array.length predictors in match can_bootstrap ~responder ~predictors m with | false -> assert false | true -> let bootstrap_fails = ref 0 in let indices = Array.make (Array.length m) 0 in let bootstrap_coeffs = Array.init p (fun _ -> Array.make trials 0.0) in for i = 0 to trials - 1 do random_indices_in_place indices ~max:(Array.length m); let matrix, vector = make_lr_inputs ~indices ~responder ~predictors m in match Linear_algebra.ols ~in_place:true matrix vector with | Ok bt_ols_result -> for p = 0 to p - 1 do bootstrap_coeffs.(p).(i) <- bt_ols_result.(p) done | _ -> incr bootstrap_fails; for p = 0 to p - 1 do bootstrap_coeffs.(p).(i) <- neg_infinity done done; Array.init p (fun i -> if trials = 0 then Ci95.bad else quantile_of_array bootstrap_coeffs.(i) ~failures:!bootstrap_fails ~len:trials ~low:0.025 ~high:0.975) (* Ordinary Least Square *) let ols ?bootstrap:(trials = 0) ?r_square:(do_r_square = false) ~responder ~predictors m = let matrix, vector = make_lr_inputs ~responder ~predictors m in match Linear_algebra.ols ~in_place:true matrix vector with | Ok estimates -> let r_square = if do_r_square then Some (r_square m ~responder ~predictors estimates) else None in let ci95 = match trials with | 0 -> None | trials -> Some (bootstrap ~trials ~responder ~predictors m) in { predictors; responder; value = Ok { estimates; ci95; r_square } } | Error _ as err -> { predictors; responder; value = err } let pp ~predictors ~responder ppf v = Fmt.pf ppf "{ @["; for i = 0 to Array.length predictors - 1 do Fmt.pf ppf "%s per %s = %f" responder predictors.(i) v.estimates.(i); (match v.ci95 with | Some ci95 -> Fmt.pf ppf " (confidence: %a)" Ci95.pp ci95.(i) | None -> ()); Fmt.pf ppf ";@ " done; Fmt.pf ppf "r² = %a@] }" Fmt.(Dump.option float) v.r_square let pp ppf x = match x.value with | Ok v -> pp ~predictors:x.predictors ~responder:x.responder ppf v | Error (`Msg err) -> Format.fprintf ppf "%s" err let predictors { predictors; _ } = Array.to_list predictors let responder { responder; _ } = responder let estimates { value; _ } = match value with | Ok { estimates; _ } -> Some (Array.to_list estimates) | Error _ -> None let r_square { value; _ } = match value with Ok { r_square; _ } -> r_square | Error _ -> None end module RANSAC = struct (* returns [a, b] such that [f(x) = a*x + b] minimize the distance between [sum(fun (x -> (f(x) - v(x))^2)] *) let affine_adjustment (r : (float * float) array) = let len = float (Array.length r) in let mean_x = let sum_x = Array.fold_right (fun (x, _) acc -> x +. acc) r 0. in sum_x /. len in let mean_y = let sum_y = Array.fold_right (fun (_, y) acc -> y +. acc) r 0. in sum_y /. len in let variance_x = let sumvar = Array.fold_right (fun (x, _) acc -> let v = x -. mean_x in (v *. v) +. acc) r 0. in sumvar /. len in let covariance_x_y = let sumcovar = Array.fold_right (fun (x, y) acc -> let v = (x -. mean_x) *. (y -. mean_y) in v +. acc) r 0. in sumcovar /. len in let a = covariance_x_y /. variance_x in let b = mean_y -. (a *. mean_x) in (a, b) let quality data (a, b) = let acc = ref 0. in for i = 0 to Array.length data - 1 do let x, y = data.(i) in let diff = let d = (a *. x) +. b -. y in d *. d in acc := !acc +. diff done; !acc /. float (Array.length data) let ransac_filter_distance (x, y) (a, b) = let level = max x (max y (max a b)) in abs_float ((a *. x) +. b -. y) /. level let ransac_param data = { Ransac.model = affine_adjustment ; data ; subset_size = 10 ; rounds = 100 ; distance = ransac_filter_distance ; filter_distance = 0.05 ; minimum_valid = Array.length data / 3 ; error = quality } let sum a = Array.fold_left ( +. ) 0. a let standard_error ~a ~b (r : (float * float) array) = let estimate x = (a *. x) +. b in let dy (x, y) = let d = y -. estimate x in d *. d in let sum_dy = sum (Array.map dy r) in let mean_x = sum (Array.map (fun (x, _) -> x) r) /. float (Array.length r) in let dx (x, _) = let d = x -. mean_x in d *. d in sqrt (sum_dy /. float (Array.length r - 2)) /. sqrt (sum (Array.map dx r)) type t = { predictor : string ; responder : string ; mean_value : float ; constant : float ; max_value : float * float ; min_value : float * float ; standard_error : float } let pp ppf t = Fmt.pf ppf "{ @[<hov>%s per %s = %f;@ standard-error = %f;@] }" t.responder t.predictor t.mean_value t.standard_error let result_column ~predictor ~responder m = ( Measurement_raw.get ~label:predictor m , Measurement_raw.get ~label:responder m ) let ransac ?(filter_outliers = true) ~predictor ~responder ml = let a = Array.map (result_column ~predictor ~responder) ml in let mean_value, constant = if filter_outliers then match Ransac.ransac (ransac_param a) with | None -> (* Couldn't extract a model, just return crude affine adjustment *) affine_adjustment a | Some { Ransac.model; _ } -> model else affine_adjustment a in let min_value = Array.fold_left (fun (row_min, val_min) (row, value) -> let value = (value -. constant) /. row in if val_min < value || value <= 0. then (row_min, val_min) else (row, value)) (0., max_float) a in let correct_float f = classify_float f = FP_normal in let max_value = Array.fold_left (fun (row_max, val_max) (row, value) -> let value = (value -. constant) /. row in if val_max > value || not (correct_float value) then (row_max, val_max) else (row, value)) (0., min_float) a in let standard_error = standard_error ~a:mean_value ~b:constant a in { predictor ; responder ; mean_value ; constant ; min_value ; max_value ; standard_error } let responder { responder; _ } = responder let predictor { predictor; _ } = predictor let mean { mean_value; _ } = mean_value let constant { constant; _ } = constant let min { min_value; _ } = min_value let max { max_value; _ } = max_value let error { standard_error; _ } = standard_error end type 'a t = | OLS : { predictors : string array; r_square : bool; bootstrap : int } -> OLS.t t | RANSAC : { filter_outliers : bool; predictor : string } -> RANSAC.t t let ols ~r_square ~bootstrap ~predictors = OLS { predictors; r_square; bootstrap } let ransac ~filter_outliers ~predictor = RANSAC { filter_outliers; predictor } let one : type a. a t -> Measure.witness -> Benchmark.t -> a = fun kind e { lr = m; _ } -> let label = Measure.label e in match kind with | OLS { predictors; r_square; bootstrap } -> OLS.ols ~bootstrap ~r_square ~predictors ~responder:label m | RANSAC { filter_outliers; predictor } -> RANSAC.ransac ~filter_outliers ~predictor ~responder:label m let all : type a. a t -> Measure.witness -> (string, Benchmark.t) Hashtbl.t -> (string, a) Hashtbl.t = fun kind e ms -> let ret = Hashtbl.create (Hashtbl.length ms) in Hashtbl.iter (fun name m -> Hashtbl.add ret name (one kind e m)) ms; ret let merge : type a. a t -> Measure.witness list -> (string, a) Hashtbl.t list -> (string, (string, a) Hashtbl.t) Hashtbl.t = fun _ instances results -> let ret = Hashtbl.create (List.length instances) in List.iter2 (fun instance result -> Hashtbl.add ret (Measure.label instance) result) instances results; ret
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>