max_ll_util.cpp.html mathcode2html   
 Source file:   max_ll_util.cpp
 Converted:   Sun May 10 2015 at 16:06:29
 This documentation file will not reflect any later changes in the source file.

$$\phantom{******** If you see this on the webpage then the browser could not locate *********}$$
$$\phantom{******** jsMath/easy/load.js or the variable root is set wrong in this file *********}$$
$$\newcommand{\vector}[1]{\left[\begin{array}{c} #1 \end{array}\right]}$$ $$\newenvironment{matrix}{\left[\begin{array}{cccccccccc}} {\end{array}\right]}$$ $$\newcommand{\A}{{\cal A}}$$ $$\newcommand{\W}{{\cal W}}$$

/* Copyright 2008-2014 Research Foundation State University of New York   */

/* This file is part of QUB Express.                                      */

/* QUB Express 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 3 of the License, or      */
/* (at your option) any later version.                                    */

/* QUB Express 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,     */
/* named LICENSE.txt, in the QUB Express program directory.  If not, see  */
/* <http://www.gnu.org/licenses/>.                                        */

/*

See also:

Up: Index

*/

#ifdef _WIN32
  #include <windows.h>
#else
  #include <stdlib.h>
  #define BOOL int
  #define TRUE 1
  #define FALSE 0
#endif

#include <iostream>
#include <fstream>
using std::cout;
using std::cerr;
using std::endl;
#include <set>
using std::set;
#include <map>
using std::map;
#include <string.h>

#include "max_ll_util.h"
#include "ublas_matrixutil.h"

//std::ofstream logg;

#define MIN_ACTIVE_Q 0.0

/*
The Q matrix of rate constants (probability per second) is computed from intrinsic rate constants K0 and K1, and potentially Ligand- and Voltage-sensitive. \[Q_{a,b} = K0_{a,b} * Ligand_{a,b} * e^{K1_{a,b} * Voltage_{a,b}}\] \[Q_{a,a} = - \sum_i Q_{a,i}\]
*/
QUBFAST_API void BuildQ(int Ns, matrix<double>& Q, matrix<double>& K0, matrix<double>& K1, matrix<int>& Ligand, matrix<int>& Voltage, double *Constants)
{
  if ( ((int) Q.size1()) != Ns || ((int) Q.size2()) != Ns )
    Q.resize(Ns, Ns);
  for (int a=0; a<Ns; ++a) {
    double rowsum = 0.0;
    for (int b=0; b<Ns; ++b) {
      if (a != b) {
	double k = K1(a,b);
	if ( Voltage(a,b) )	  k *= Constants[ Voltage(a,b) ];
	k = exp(k);
	k *= K0(a,b);
	if ( Ligand(a,b) ) {
	  double lig = Constants[ Ligand(a,b) ];
	  k *= lig ? lig : MIN_ACTIVE_Q;
	}
	Q(a,b) = k;
	rowsum += k;
      }
    }
    Q(a,a) = - rowsum;
  }
}

QUBFAST_API void BuildQ_p(int Ns, matrix<double>& Q, matrix<double>& K0, matrix<double>& K1, matrix<double>& K2, matrix<int>& Ligand, matrix<int>& Voltage, matrix<int>& Pressure, double *Constants)
{
  if ( ((int) Q.size1()) != Ns || ((int) Q.size2()) != Ns )
    Q.resize(Ns, Ns);
  for (int a=0; a<Ns; ++a) {
    double rowsum = 0.0;
    for (int b=0; b<Ns; ++b) {
      if (a != b) {
	double k = K0(a,b);
	if ( Ligand(a,b) ) {
	  double lig = Constants[ Ligand(a,b) ];
	  k *= lig ? lig : MIN_ACTIVE_Q;
	}
	double kExp = 0.0;
	if ( Voltage(a,b) )
	  kExp += K1(a,b) * Constants[Voltage(a,b)];
	if ( Pressure(a,b) )
	  kExp += K2(a,b) * Constants[Pressure(a,b)];
	k *= exp(kExp);
	Q(a,b) = k;
	rowsum += k;
      }
    }
    Q(a,a) = - rowsum;
  }
}

/*
"Qe" aka \({}^eQ\) is the "apparent" rate constant matrix, given that events with duration <= tdead are not recorded. MIL computes the probability of staying in class a for duration t using the submatrix \({}^eQ_{aa}\) \[A(a, t) = e^{{}^eQ_{aa} (t-tdead)}\]
*/
QUBFAST_API void QtoQe(matrix<double>& Q, int *clazz, double tdead, matrix<double>& eQ)
{
  /*
    

eQ is partitioned into its Na x Nb submatrices, for classes a and b (likewise partition Q) each partition is computed separately. Notation: going from class a to class b, z is "any class but a", and c is "any class but a or b". Iz is the identity matrix with dimensions of Qzz.

\(a = b\): \({}^eQ_{aa} = Q_{aa} - Q_{az} * (I_z - e^{Q_{zz} * tdead}) * Q_{za}\)

(accounts for going to another class for a short, then returning undetected)

\(a \ne b\): \({}^eQ_{ab} = (Q_{ab} - (Q_{ac} * (I_c - e^{Q_{cc} * tdead}) * Q^{-1}_{cc} * Q_{cb}) * e^{Q_{bb} * tdead}\)

(accounts for going to class c for a short, on the way from a to b, then staying in b long enough to be detected)

  */

  int n = (int) Q.size1();
  if ( ((int) eQ.size1()) != n || ((int) eQ.size2()) != n )
    eQ.resize(n, n);

  if ( tdead == 0.0 ) {
    for (int i=0; i<n; ++i)
      for (int j=0; j<n; ++j)
	eQ(i,j) = Q(i,j);
    return;
  }

  set<int> classet; // no duplicates, sorted increasing, for iteration
  for (int i=0; i<n; ++i)
    classet.insert(clazz[i]);
  for (set<int>::iterator cls_i = classet.begin(); cls_i != classet.end(); ++cls_i) {
    std::vector<int> states, outs;
    for (int i=0; i<n; ++i)
      if ( *cls_i == clazz[i] )
	states.push_back(i);
      else
	outs.push_back(i);
    int Na = (int) states.size();
    int Nz = (int) outs.size();
    int *sts = & (states[0]);
    int *ots = & (outs[0]);
    matrix<double> Qaa(Na, Na);        matrix_to_partition(Q, Na, Na, sts, sts, Qaa);
    matrix<double> Qaz(Na, Nz);        matrix_to_partition(Q, Na, Nz, sts, ots, Qaz);
    matrix<double> Qza(Nz, Na);        matrix_to_partition(Q, Nz, Na, ots, sts, Qza);
    matrix<double> Qzz(Nz, Nz);        matrix_to_partition(Q, Nz, Nz, ots, ots, Qzz);
    matrix<double> Iz(Nz, Nz);         fill_identity(Iz);
    matrix<double> Az(Nz, Nz);         expm(Qzz, tdead, Az);
    matrix<double> Qzi;                invm(Qzz, Qzi);
    matrix<double> missed_returning;   missed_returning.resize(Na, Na);
    noalias(missed_returning) = prod(matrix<double>(prod(matrix<double>(prod(Qaz, matrix<double>(Iz - Az))), Qzi)), Qza);
    matrix<double> eQaa(Na, Na);
    noalias(eQaa) = Qaa - missed_returning;
    matrix_from_partition(eQ, Na, Na, sts, sts, eQaa);
    
    for (set<int>::iterator cls_j = classet.begin(); cls_j != classet.end(); ++cls_j) {
      if ( *cls_j != *cls_i ) {
	std::vector<int> bees, cees;
	for (int i=0; i<n; ++i)
	  if ( *cls_j == clazz[i] )
	    bees.push_back(i);
	  else if ( *cls_i != clazz[i] )
	    cees.push_back(i);
	int Nb = (int) bees.size();
	int Nc = (int) cees.size();
	int *bs = Nb ? &(bees[0]) : NULL;
	int *cs = Nc ? &(cees[0]) : NULL;
	matrix<double> Qab(Na, Nb);    matrix_to_partition(Q, Na, Nb, sts, bs, Qab);
	matrix<double> Qbb(Nb, Nb);    matrix_to_partition(Q, Nb, Nb, bs, bs, Qbb);
	matrix<double> escaped_indirect(Na, Nb);
	if ( Nc ) {
	  matrix<double> Qac(Na, Nc);  matrix_to_partition(Q, Na, Nc, sts, cs, Qac);
	  matrix<double> Qcc(Nc, Nc);  matrix_to_partition(Q, Nc, Nc, cs, cs, Qcc);
	  matrix<double> Qcb(Nc, Nb);  matrix_to_partition(Q, Nc, Nb, cs, bs, Qcb);
	  matrix<double> Ic(Nc, Nc);   fill_identity(Ic);
	  matrix<double> Ac(Nc, Nc);   expm(Qcc, tdead, Ac);
	  matrix<double> Qci(Nc, Nc);  invm(Qcc, Qci);
	  noalias(escaped_indirect)  = prod( matrix<double>(prod( matrix<double>(prod(Qac, Ic - Ac)), Qci )), Qcb);
	}
	else {
	  // no third class? can't escape indirectly
	  fill_const(escaped_indirect, 0.0);
	}
	matrix<double> Ab(Nb, Nb);     expm(Qbb, tdead, Ab);
	matrix<double> eQab(Na, Nb);
	noalias(eQab) = prod(Qab - escaped_indirect, Ab);
	matrix_from_partition(eQ, Na, Nb, sts, bs, eQab);
      }
    }

  }
}

/*

As discussed in (Milescu 2005), it can be preferable to start an experiment at equilibrium. Entry probabilities (P0) are then no longer constant, but a function of rate constants. We use the "direct" method given in Neher and Sakmann:

Defining \(S = [Q | 1]\) and \(u\) a row vector of ones, \[P_{eq} = u \cdot (S \cdot S^T)^{-1}\]

*/
QUBFAST_API void QtoPe(matrix<double>& Q, matrix<double>& Pe)
{
  int N = (int) Q.size1();
  if ( ((int) Pe.size1()) != 1 || ((int) Pe.size2()) != N )
    Pe.resize(1, N);
  matrix<double> S(N, N+1);
  matrix<double> U(1, N);
  for (int i=0; i<N; ++i) {
    for (int j=0; j<=N; ++j)
      S(i,j) = (j < N) ? Q(i,j) : 0.0;
    S(i,N) = 1.0;
    U(0,i) = 1.0;
  }
  matrix<double> SST(N, N);     noalias(SST) = prod(S, trans(S));
  matrix<double> SSTI(N, N);    invm(SST, SSTI);
  noalias(Pe) = prod(U, SSTI);
}



QUBFAST_API double alpha_logscale(vector<double>& alpha)
{
  double tot = 0.0;
  for (vector<double>::iterator ii = alpha.begin(); ii != alpha.end(); ++ii)
    tot += *ii;
  double scale = tot ? (1.0 / tot) : 1e10;
  for (vector<double>::iterator ii = alpha.begin(); ii != alpha.end(); ++ii)
    *ii *= scale;
  // std::cerr << scale << std::endl;
  return - log(scale);
}