Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
  • 4 commits
  • 1 file changed
  • 0 commit comments
  • 1 contributor
Showing with 137 additions and 74 deletions.
  1. +137 −74 lib/CamlPaml/Q.ml
View
@@ -28,7 +28,17 @@ let m_of_cm cm = Gsl.Matrix.of_arrays (Array.map (Array.map real_of_complex) (Gs
let cm_of_m m = Gsl.Matrix_complex.of_arrays (Array.map (Array.map (fun re -> { Complex.re = re; im = 0. })) (Gsl.Matrix.to_arrays m))
-(* stupid wrappers *)
+let v_of_cv cv = Gsl.Vector.of_array (Array.map real_of_complex (Gsl.Vector_complex.to_array cv))
+
+let gemm ?c a b =
+ let m,n = Gsl.Matrix.dims a
+ let n',p = Gsl.Matrix.dims b
+ if n <> n' then invalid_arg "CamlPaml.Q.gemm: incompatible dimensions"
+ let c = match c with
+ | Some c -> c
+ | None -> Gsl.Matrix.create m p
+ Gsl.Blas.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:1. ~a:a ~b:b ~beta:0. ~c:c
+ c
let zgemm ?c a b =
let m,n = Gsl.Matrix_complex.dims a
@@ -40,6 +50,21 @@ let zgemm ?c a b =
Gsl.Blas.Complex.gemm ~ta:Gsl.Blas.NoTrans ~tb:Gsl.Blas.NoTrans ~alpha:Complex.one ~a:a ~b:b ~beta:Complex.zero ~c:c
c
+let diagm ?c d a =
+ let m = Gsl.Vector.length d
+ let (n,p) = Gsl.Matrix.dims a
+ if m <> n then invalid_arg "CamlPaml.Q.diagm: incompatible dimensions"
+ let c = match c with
+ | Some c -> c
+ | None -> Gsl.Matrix.create m p
+
+ for i = 0 to m-1 do
+ let d_i = d.{i}
+ for j = 0 to p-1 do
+ Bigarray.Array2.unsafe_set c i j (Bigarray.Array2.unsafe_get a i j *. d_i)
+
+ c
+
let zdiagm ?c d a =
let m = Gsl.Vector_complex.length d
let (n,p) = Gsl.Matrix_complex.dims a
@@ -51,7 +76,7 @@ let zdiagm ?c d a =
for i = 0 to m-1 do
let d_i = d.{i}
for j = 0 to p-1 do
- c.{i,j} <- Complex.mul a.{i,j} d_i
+ Bigarray.Array2.unsafe_set c i j (Complex.mul (Bigarray.Array2.unsafe_get a i j) d_i)
c
@@ -68,12 +93,21 @@ let zinvm m =
| _ -> assert false
module Diag = struct
- (* diagonalized Q = S*L*S' *)
+ (* diagonalized Q = S*L*S'
+ These are real for reversible models, complex for non-reversible models. *)
+ type eig_r = {
+ r_s : Gsl.Matrix.matrix; (* S = right eigenvectors (in the columns) *)
+ r_s' : Gsl.Matrix.matrix; (* S' = left eigenvectors (in the rows) *)
+ r_l : Gsl.Vector.vector (* diag(L) = eigenvalues *)
+ }
+ type eig_nr = {
+ nr_s : Gsl.Matrix_complex.matrix; (* S = right eigenvectors (in the columns) *)
+ nr_s' : Gsl.Matrix_complex.matrix; (* S' = left eigenvectors (in the rows) *)
+ nr_l : Gsl.Vector_complex.vector; (* diag(L) = eigenvalues *)
+ }
type t = {
q : Gsl.Matrix.matrix; (* Q *)
- s : Gsl.Matrix_complex.matrix; (* S = right eigenvectors (in the columns) *)
- s' : Gsl.Matrix_complex.matrix; (* S' = left eigenvectors (in the rows) *)
- l : Gsl.Vector_complex.vector; (* diag(L) = eigenvalues *)
+ eig : [`r of eig_r | `nr of eig_nr];
pi : Gsl.Vector.vector;
mutable have_pi : bool;
@@ -91,37 +125,49 @@ module Diag = struct
let qm = Gsl.Matrix.of_arrays qm
let l, s = Gsl.Eigen.nonsymmv ~protect:true (`M qm)
let s' = zinvm s
- { q = qm; s = s; s' = s'; l = l;
- pi = Gsl.Vector.create (fst (Gsl.Matrix.dims qm)); have_pi = false; memoized_to_Pt = None; tol = tol }
+
+ let rev = ref true
+ let n = Gsl.Vector_complex.length l
+ for i = 0 to n-1 do if not (check_real ~tol l.{i}) then rev := false
+
+ let eig =
+ if !rev then
+ `r { r_s = m_of_cm s; r_s' = m_of_cm s'; r_l = v_of_cv l }
+ else
+ `nr { nr_s = s; nr_s' = s'; nr_l = l }
+
+ { q = qm; eig;
+ pi = Gsl.Vector.create (fst (Gsl.Matrix.dims qm)); have_pi = false;
+ memoized_to_Pt = None; tol = tol }
let to_Q q = Gsl.Matrix.to_arrays q.q
- let reversible q =
- let rev = ref true
- let n = Gsl.Vector_complex.length q.l
- for i = 0 to n-1 do if not (check_real ~tol:q.tol q.l.{i}) then rev := false
- !rev
+ let reversible = function
+ | { eig = `r _ } -> true
+ | _ -> false
- (* TODO figure out what this returns for a non-reversible matrix *)
let equilibrium q =
if not q.have_pi then
+ let eig = match q.eig with
+ | `r eig -> eig
+ | `nr _ -> failwith "CamlPaml.Q.equilibrium: non-reversible model"
+ let n = Gsl.Vector.length eig.r_l
let min_L = ref infinity
let min_Lp = ref (-1)
- let n = Gsl.Vector_complex.length q.l
for i = 0 to n-1 do
- let nm_lz = Complex.norm q.l.{i}
- if nm_lz < !min_L then
- min_L := nm_lz
+ let mag_i = abs_float eig.r_l.{i}
+ if mag_i < !min_L then
+ min_L := mag_i
min_Lp := i
assert (!min_Lp >= 0)
if (abs_float !min_L) > q.tol then
- failwith "CamlPaml.Q.equilibrium: smallest-magnitude eigenvalue is unacceptably large; is this a valid rate matrix?"
- let lev = Gsl.Matrix_complex.row q.s' !min_Lp
- let mass = ref Complex.zero
+ failwith (sprintf "CamlPaml.Q.equilibrium: smallest-magnitude eigenvalue %e is unacceptably large; check rate matrix validity or increase tol" !min_L)
+ let lev = Gsl.Matrix.row eig.r_s' !min_Lp
+ let mass = ref 0.
for i = 0 to n-1 do
- mass := Complex.add !mass lev.{i}
+ mass := !mass +. lev.{i}
for i = 0 to n-1 do
- q.pi.{i} <- real_of_complex (Complex.div lev.{i} !mass)
+ q.pi.{i} <- lev.{i} /. !mass
q.have_pi <- true
Gsl.Vector.to_array q.pi
@@ -143,21 +189,34 @@ module Diag = struct
if x <= 0. then invalid_arg "CamlPaml.Q.scale: nonpositive scale factor"
let xq = Gsl.Matrix.copy q.q
Gsl.Matrix.scale xq x
- let xl = Gsl.Vector_complex.copy q.l
- let cx = { Complex.re = x; im = 0. }
- for i = 0 to (Gsl.Vector_complex.length xl) - 1 do
- xl.{i} <- Complex.mul xl.{i} cx
- { q with q = xq; l = xl; memoized_to_Pt = None }
+ let xeig = match q.eig with
+ | `r { r_s; r_s'; r_l } ->
+ let xl = Gsl.Vector.copy r_l
+ for i = 0 to (Gsl.Vector.length xl) - 1 do
+ xl.{i} <- xl.{i} *. x
+ `r { r_s; r_s'; r_l = xl }
+ | `nr { nr_s; nr_s'; nr_l } ->
+ let xl = Gsl.Vector_complex.copy nr_l
+ let cx = { Complex.re = x; im = 0. }
+ for i = 0 to (Gsl.Vector_complex.length xl) - 1 do
+ xl.{i} <- Complex.mul xl.{i} cx
+ `nr { nr_s; nr_s'; nr_l = xl }
+ { q with q = xq; eig = xeig; memoized_to_Pt = None }
let real_to_Pt q t =
if t < 0. then invalid_arg "CamlPaml.Q.to_Pt"
- let ct = { Complex.re = t; im = 0. }
- let expLt = Gsl.Vector_complex.copy q.l
-
- for i = 0 to Gsl.Vector_complex.length expLt - 1 do
- expLt.{i} <- Complex.exp (Complex.mul ct expLt.{i})
-
- let sm = m_of_cm (zgemm q.s (zdiagm expLt q.s'))
+ let sm = match q.eig with
+ | `r { r_s; r_s'; r_l } ->
+ let expLt = Gsl.Vector.copy r_l
+ for i = 0 to Gsl.Vector.length expLt - 1 do
+ expLt.{i} <- exp (t *. expLt.{i})
+ gemm r_s (diagm expLt r_s')
+ | `nr { nr_s; nr_s'; nr_l } ->
+ let ct = { Complex.re = t; im = 0. }
+ let expLt = Gsl.Vector_complex.copy nr_l
+ for i = 0 to Gsl.Vector_complex.length expLt - 1 do
+ expLt.{i} <- Complex.exp (Complex.mul ct expLt.{i})
+ m_of_cm (zgemm nr_s (zdiagm expLt nr_s'))
let n,_ = Gsl.Matrix.dims sm
for i = 0 to n-1 do
@@ -193,47 +252,51 @@ module Diag = struct
let to_Pt_gc q = q.memoized_to_Pt <- None
- let dPt_dt ~q ~t =
- let n,_ = Gsl.Matrix.dims q.q
- let (( * ),(+),(/)) = Complex.mul, Complex.add, Complex.div
- let exp= Complex.exp
- let s, l, s' = q.s, q.l, q.s'
- let ct = { Complex.re = t; Complex.im = 0. }
- Array.init n
- fun a ->
- Array.init n
- fun b ->
- let x = ref Complex.zero
- for c = 0 to n-1 do
- x := !x + (s.{a,c} * l.{c} * (exp (l.{c} * ct)) * s'.{c,b})
- real_of_complex !x
+ let dPt_dt ~q ~t = match q.eig with
+ | `r _ -> failwith "not implemented"
+ | `nr { nr_s; nr_s'; nr_l } ->
+ let n,_ = Gsl.Matrix.dims q.q
+ let (( * ),(+),(/)) = Complex.mul, Complex.add, Complex.div
+ let exp= Complex.exp
+ let s, l, s' = nr_s, nr_l, nr_s'
+ let ct = { Complex.re = t; Complex.im = 0. }
+ Array.init n
+ fun a ->
+ Array.init n
+ fun b ->
+ let x = ref Complex.zero
+ for c = 0 to n-1 do
+ x := !x + (s.{a,c} * l.{c} * (exp (l.{c} * ct)) * s'.{c,b})
+ real_of_complex !x
(* TODO in richly parameterized models, dQ_dx is likely to be sparse. *)
- let dPt_dQ_dx ~q ~t ~dQ_dx =
- let n,_ = Gsl.Matrix.dims q.q
- let dQt_dx = cm_of_m (Gsl.Matrix.of_arrays dQ_dx)
-
- if Gsl.Matrix_complex.dims dQt_dx <> Gsl.Matrix.dims q.q then
- invalid_arg "CamlPaml.Q.dP_dx: wrong dimension of dQ_dx"
-
- let ct = { Complex.re = t; Complex.im = 0. }
- let exp = Complex.exp
- let (( * ),(-),(/)) = Complex.mul, Complex.sub, Complex.div
-
- (* combos 1,3 or 2 (alone) -- 1,3 matches P&S? *)
- Gsl.Matrix_complex.scale dQt_dx ct (* 1 *)
-
- let f = Gsl.Matrix_complex.create n n
- for i = 0 to pred n do
- for j = 0 to pred n do
- if q.l.{i} = q.l.{j} then
- f.{i,j} <- exp (q.l.{i} * ct) (* * ct *) (* 2 *)
- else
- f.{i,j} <- (exp (q.l.{i} * ct) - exp (q.l.{j} * ct)) / ((q.l.{i} - q.l.{j}) * ct (* 3 *))
-
- (* ehh not being too gentle with the allocator/GC here *)
- Gsl.Matrix_complex.mul_elements f (zgemm q.s' (zgemm dQt_dx q.s))
- Gsl.Matrix.to_arrays (m_of_cm (zgemm q.s (zgemm f q.s')))
+ let dPt_dQ_dx ~q ~t ~dQ_dx = match q.eig with
+ | `r _ -> failwith "not implemented"
+ | `nr { nr_s; nr_s'; nr_l } ->
+ let n,_ = Gsl.Matrix.dims q.q
+ let dQt_dx = cm_of_m (Gsl.Matrix.of_arrays dQ_dx)
+
+ if Gsl.Matrix_complex.dims dQt_dx <> Gsl.Matrix.dims q.q then
+ invalid_arg "CamlPaml.Q.dP_dx: wrong dimension of dQ_dx"
+
+ let ct = { Complex.re = t; Complex.im = 0. }
+ let exp = Complex.exp
+ let (( * ),(-),(/)) = Complex.mul, Complex.sub, Complex.div
+
+ (* combos 1,3 or 2 (alone) -- 1,3 matches P&S? *)
+ Gsl.Matrix_complex.scale dQt_dx ct (* 1 *)
+
+ let f = Gsl.Matrix_complex.create n n
+ for i = 0 to pred n do
+ for j = 0 to pred n do
+ if nr_l.{i} = nr_l.{j} then
+ f.{i,j} <- exp (nr_l.{i} * ct) (* * ct *) (* 2 *)
+ else
+ f.{i,j} <- (exp (nr_l.{i} * ct) - exp (nr_l.{j} * ct)) / ((nr_l.{i} - nr_l.{j}) * ct (* 3 *))
+
+ (* ehh not being too gentle with the allocator/GC here *)
+ Gsl.Matrix_complex.mul_elements f (zgemm nr_s' (zgemm dQt_dx nr_s))
+ Gsl.Matrix.to_arrays (m_of_cm (zgemm nr_s (zgemm f nr_s')))
let logm (m:Gsl.Matrix.matrix) =
let n,n' = Gsl.Matrix.dims m

No commit comments for this range