(* File: most_prob.ml AIFAD - Automated Induction of Functions over Algebraic Datatypes Author: Markus Mottl email: markus.mottl@gmail.com WWW: http://www.ocaml.info Copyright (C) 2002 Austrian Research Institute for Artificial Intelligence Copyright (C) 2003- Markus Mottl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) (* $Id: most_prob.ml,v 1.4 2006/01/17 00:23:38 mottl Exp $ *) open Utils open Algdt_types open Algdt_utils open Entropy_utils (* Calculation of most frequent leaf models *) (* Utility functions *) let is_atomic_var fspec var = array_forall array_is_empty fspec.(var.tp) let is_one_atomic_var fspec vars = Array.length vars = 1 && is_atomic_var fspec vars.(0) let most_prob_cnstr var = let colli cnstr (_, max_freq as acc) freq = if max_freq < freq then cnstr, freq else acc in fst (array_fold_left1i colli (0, var.histo.(0)) var.histo) let make_many_sub_vars fspec fspec_tp histo = let make_sub_var cnstr freq = let fspec_tp_cnstr = fspec_tp.(cnstr) in let n_sub_vars = Array.length fspec_tp_cnstr in if n_sub_vars = 0 then [||] else Array.init n_sub_vars (fun sub_var_ix -> let sub_tp = fspec_tp_cnstr.(sub_var_ix) in { samples = Array.make freq dummy_fdsum; tp = sub_tp; histo = Array.make (Array.length fspec.(sub_tp)) 0; }) in Array.mapi make_sub_var histo let calc_scaled_probs f_total_samples histo n_samples = let ps = calc_probs histo n_samples in let f_samples = float n_samples in let rescale i p = ps.(i) <- p *. f_samples /. f_total_samples in Array.iteri rescale ps; ps let make_sub_ixs histo = Array.make (Array.length histo) 0 (* Independent version: assumes independence of products! *) let calc_many_sub_vars fspec { samples = samples; tp = tp; histo = histo } = let fspec_tp = fspec.(tp) in let many_sub_vars = make_many_sub_vars fspec fspec_tp histo in let sub_ixs = make_sub_ixs histo in for sample_ix = 0 to Array.length samples - 1 do match samples.(sample_ix) with | FDStrct (cnstr, subs) -> let sub_ix = sub_ixs.(cnstr) in sub_ixs.(cnstr) <- sub_ix + 1; let sub_vars = many_sub_vars.(cnstr) in for sub_var_ix = 0 to Array.length subs - 1 do let { samples = sub_samples; histo = sub_histo } = sub_vars.(sub_var_ix) in let sub_sample = subs.(sub_var_ix) in sub_samples.(sub_ix) <- sub_sample; let sub_cnstr = fdsum_cnstr sub_sample in sub_histo.(sub_cnstr) <- sub_histo.(sub_cnstr) + 1 done | _ -> () done; many_sub_vars let rec indep_most_prob_sum fspec f_total_samples var = let n_samples = Array.length var.samples in let histo = var.histo in let scale = float n_samples /. f_total_samples in if is_atomic_var fspec var then let best_cnstr = most_prob_cnstr var in FDAtom best_cnstr, calc_prob histo best_cnstr n_samples *. scale else let fspec_tp = fspec.(var.tp) in let many_sub_vars = calc_many_sub_vars fspec var in let colli cnstr (_, best_sh as acc) sub_vars = if histo.(cnstr) = 0 then acc else let sub_res = Array.make (Array.length sub_vars) dummy_fdsum in let colli sub_var_ix sh sub_var = let fdsum, sub_sh = indep_most_prob_sum fspec f_total_samples sub_var in sub_res.(sub_var_ix) <- fdsum; sh +. sub_sh in let sub_sh = array_fold_lefti colli 0.0 sub_vars in let sh = sub_sh +. calc_prob histo cnstr n_samples *. scale in if sh > best_sh then if array_is_empty fspec_tp.(cnstr) then FDAtom cnstr, sh else FDStrct (cnstr, sub_res), sh else acc in array_fold_lefti colli (dummy_fdsum, 0.0) many_sub_vars let indep_most_prob_sums fspec vars = let f_total_samples = float (Array.length vars.(0).samples) in let cnv var = fst (indep_most_prob_sum fspec f_total_samples var) in Array.map cnv vars (* Dependent version *) let make_zeros_ref _ = ref 0.0, ref 0.0 let rec dep_most_prob_sum fspec p_all irefs { samples = samples; tp = tp; histo = histo } = let fspec_tp = fspec.(tp) in let n_samples = Array.length samples in let ps = calc_probs histo n_samples in let is = calc_probs_icont ps in let make_sub_irefs cnstr freq = if array_is_empty fspec_tp.(cnstr) then [||] else Array.init freq make_zeros_ref in let many_sub_irefs = Array.mapi make_sub_irefs histo in let many_sub_vars = make_many_sub_vars fspec fspec_tp histo in let sub_ixs = make_sub_ixs histo in for sample_ix = 0 to n_samples - 1 do match samples.(sample_ix) with | FDAtom cnstr -> let sh_ref, all_ref = irefs.(sample_ix) in let icont_p_all = is.(cnstr) *. p_all in sh_ref := !sh_ref +. ps.(cnstr) *. icont_p_all; all_ref := !all_ref +. icont_p_all | FDStrct (cnstr, subs) -> let (sh_ref, all_ref as iref) = irefs.(sample_ix) in let icont_p_all = is.(cnstr) *. p_all in sh_ref := !sh_ref +. ps.(cnstr) *. icont_p_all; all_ref := !all_ref +. icont_p_all; let sub_ix = sub_ixs.(cnstr) in sub_ixs.(cnstr) <- sub_ix + 1; many_sub_irefs.(cnstr).(sub_ix) <- iref; let sub_vars = many_sub_vars.(cnstr) in for sub_var_ix = 0 to Array.length subs - 1 do let { samples = sub_samples; histo = sub_histo } = sub_vars.(sub_var_ix) in let sub_sample = subs.(sub_var_ix) in sub_samples.(sub_ix) <- sub_sample; let sub_cnstr = fdsum_cnstr sub_sample in sub_histo.(sub_cnstr) <- sub_histo.(sub_cnstr) + 1 done done; let acti cnstr sub_vars = if histo.(cnstr) > 0 then let act = dep_most_prob_sum fspec (p_all *. ps.(cnstr)) many_sub_irefs.(cnstr) in Array.iter act sub_vars in Array.iteri acti many_sub_vars let dep_most_prob_sums fspec vars = if is_one_atomic_var fspec vars then [| FDAtom (most_prob_cnstr vars.(0)) |] else let n_total_samples = Array.length vars.(0).samples in let irefs = Array.init n_total_samples make_zeros_ref in let act = dep_most_prob_sum fspec 1.0 irefs in let _ = Array.iter act vars in let colli ix (best_ix, best_ratio as acc) (sh_ref, all_ref) = let ratio = !sh_ref /. !all_ref in if ratio > best_ratio then ix, ratio else acc in let best_ix, _ = array_fold_lefti colli (0, 0.0) irefs in Array.map (fun var -> var.samples.(best_ix)) vars