Permalink
fd3a0fb Aug 18, 2013
157 lines (130 sloc) 5.79 KB
open List
open Printf
let (|>) x f = f x
type t = {
tree : T.t;
qms : Q.Diag.t array;
pms : P.matrix array;
prior : float array option
}
let make ?prior t qms =
let qms = if Array.length qms = 1 then Array.make (T.size t - 1) qms.(0) else Array.copy qms
if Array.length qms <> T.size t - 1 then invalid_arg "CamlPaml.PhyloModel.make"
for i = 0 to T.root t - 1 do
if Q.Diag.dim qms.(i) <> Q.Diag.dim qms.(0) || T.branch t i < 0. then invalid_arg "CamlPaml.PhyloModel.make"
let pms = Array.init (T.size t - 1) (fun br -> Q.Diag.to_Pt qms.(br) (T.branch t br))
let prior = match prior with
| Some pr ->
if Array.length pr <> Q.Diag.dim qms.(0) then invalid_arg "CamlPaml.PhyloModel.make"
Some (Array.copy pr)
| None ->
if not (Q.Diag.reversible qms.(snd (T.children t (T.root t)))) then invalid_arg "CamlPaml.PhyloModel.make"
None
{ tree = T.copy t; qms; pms; prior }
let tree { tree } = T.copy tree
let q { qms } br = qms.(br)
let p { pms } br = pms.(br) (* Array.map Array.copy pms.(br) *)
let prior { tree; qms; prior } = match prior with
| Some pr -> Array.copy pr
| None -> Q.Diag.equilibrium qms.(snd (T.children tree (T.root tree))) (* q was verified reversible in [make], above*)
let prepare_lik ?workspace m leaves = PhyloLik.prepare ?workspace m.tree m.pms (prior m) leaves
let checksum = 1., 1e-6
let simulate m =
let branch_choosers = m.pms |> Array.map (fun pm -> (Array.map (Tools.random_chooser ~checksum:checksum) (Gsl.Matrix.to_arrays pm)))
let root_chooser = Tools.random_chooser ~checksum:checksum (prior m)
fun ?root ?a () ->
let t = m.tree
let a = match a with
| Some a -> a
| None -> Array.make (T.size t) (-1)
a.(T.root t) <- (match root with
| None -> root_chooser ()
| Some ch when ch >= 0 && ch < Q.Diag.dim m.qms.(0) -> ch
| _ -> invalid_arg "CamlPaml.PhyloModel.simulate (root)")
for i = T.root t - 1 downto 0 do
let p = a.(T.parent t i)
a.(i) <- branch_choosers.(i).(p) ()
a
module P14n = struct
type q_p14n = Expr.t array array
type model_p14n = {
q_p14ns : q_p14n array;
q_scale_p14ns : Expr.t array;
q_domains : Fit.domain array;
tree_shape : T.t;
tree_p14n : Expr.t array;
tree_domains : Fit.domain array
}
type instance = {
model : t;
p14n : model_p14n;
q_settings : float array;
tree_settings : float array
}
let fill_q_diagonal q =
let n = Array.length q
for i = 0 to n-1 do
if Array.length q.(i) <> n then invalid_arg "CamlPaml.PhyloModel.P14n.fill_q_diagonal"
let tot = ref (Expr.Val 0.)
for j = 0 to n-1 do
if i <> j then
tot := Expr.Add (q.(i).(j), !tot)
q.(i).(i) <- Expr.simplify (Expr.Sub (Expr.Val 0., !tot))
let instantiate_tree shape exprs domains settings =
Array.iteri (fun i domain -> if not (Fit.check_domain domain settings.(i)) then invalid_arg ("CamlPaml.P14n.instantiate_tree: domain violation on variable " ^ (string_of_int i))) domains
let tree = T.copy shape
for br = 0 to T.root tree - 1 do
T.put_branch tree br (Expr.eval exprs.(br) settings)
tree
let instantiate_q exprs scale_expr domains settings =
Array.iteri (fun i domain -> if not (Fit.check_domain domain settings.(i)) then invalid_arg ("CamlPaml.P14n.instantiate_q: domain violation on variable " ^ (string_of_int i))) domains
let scale = Expr.eval scale_expr settings
if scale <= 0. then
failwith "CamlPaml.P14n.instantiate_q: Q scale evaluated to a non-positive value"
let qm = exprs |> Array.map (Array.map (fun expr -> Expr.eval expr settings /. scale))
let q = Q.Diag.of_Q qm
q
let instantiate_qs p14ns scale_p14ns domains settings =
if Array.length p14ns <> Array.length scale_p14ns then invalid_arg ("CamlPaml.P14n.instantiate: different numbers of rate matrix and scale p14ns")
let previous = ref [] (* memoized results from previous branches...I'm assuming the number of independent rate matrix parameterizations to be sublinear in the size of the tree, otherwise this memoization is quadratic... *)
Array.init (Array.length p14ns)
fun br ->
try
let (_,_,q) =
find
function
| (q_p14n,scale_p14n,q) when q_p14n == p14ns.(br) && scale_p14n == scale_p14ns.(br) -> true
| _ -> false
!previous
q
with
| Not_found ->
let q = instantiate_q p14ns.(br) scale_p14ns.(br) domains settings
previous := (p14ns.(br),scale_p14ns.(br),q) :: !previous
q
let instantiate ?prior p14n ~q_settings ~tree_settings =
let qms = instantiate_qs p14n.q_p14ns p14n.q_scale_p14ns p14n.q_domains q_settings
let tree = instantiate_tree p14n.tree_shape p14n.tree_p14n p14n.tree_domains tree_settings
{ model = make ?prior tree qms; p14n = p14n; q_settings = Array.copy q_settings; tree_settings = Array.copy tree_settings }
let update ?prior ?q_settings ?tree_settings inst =
let pms_changed = ref false
let newq = match q_settings with
| Some q_settings ->
pms_changed := true
instantiate_qs inst.p14n.q_p14ns inst.p14n.q_scale_p14ns inst.p14n.q_domains q_settings
| None -> inst.model.qms
let newtree = match tree_settings with
| Some tree_settings ->
pms_changed := true
instantiate_tree inst.p14n.tree_shape inst.p14n.tree_p14n inst.p14n.tree_domains tree_settings
| None -> inst.model.tree
let model =
if !pms_changed then make ?prior newtree newq
else { inst.model with prior = prior }
{ inst with model = model;
q_settings = (match q_settings with Some set -> Array.copy set | None -> inst.q_settings);
tree_settings = (match tree_settings with Some set -> Array.copy set | None -> inst.tree_settings) }
let model { model = model } = model
let p14n { p14n = p14n } = p14n
let q_settings { q_settings = q_settings } = Array.copy q_settings
let tree_settings { tree_settings = tree_settings } = Array.copy tree_settings