qubx_model.cpp.html | mathcode2html |
Source file: qubx_model.cpp | |
Converted: Wed Jan 6 2016 at 15:24:07 | |
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/>. */ #ifdef _WIN32 #include <windows.h> #else #include <stdlib.h> #define BOOL int #define TRUE 1 #define FALSE 0 #endif #include <string.h> #include <iostream> #include <fstream> #define _USE_MATH_DEFINES #include <math.h> #include <string.h> #include <set> #include <map> #include <vector> #include <algorithm> #include "qubx_model_storage.h" #include "ublas_matrixutil.h" #include "qub_constraints.h" #include "qub_findloops.h" #include "max_ll_util.h" using std::cout; using std::cerr; using std::endl; using std::set; using std::map; using std::pair; using std::copy; using std::min; using std::max; qubx_model_storage::qubx_model_storage(int& Nstate_, int& Nclass_, int *class_of_state, callbk_reportfun report_cb_, void *report_data_, int &IeqFv_, double &0_, double &var0_, double &Vrev_, double &Vleak_, double &Gleak_ ) : Nstate(Nstate_), Nclass(Nclass_), IeqFv(IeqFv_), clazz(Nstate_), P0(Nstate_), amp(Nclass_), var(Nclass_), d_amp(Nclass_), d_var(Nclass_), K0(Nstate_, Nstate_), K1(Nstate_, Nstate_), d_K0(Nstate_, Nstate_), d_K1(Nstate_, Nstate_), L(Nstate_, Nstate_), V(Nstate_, Nstate_), report_cb(report_cb_), report_data(report_data_), output(report_cb_, report_data_), amp0(amp0_), var0(var0_), Vrev(Vrev_), Vleak(Vleak_), Gleak(Gleak_), K2(Nstate_, Nstate_), d_K2(Nstate_, Nstate_), P(Nstate_, Nstate_) { if ( class_of_state ) memcpy(clazz.p, class_of_state, Nstate_*sizeof(int)); } extern "C" QUBFAST_API qubx_model* qubx_model_create(int Nstate, int Nclass, int* class_of_state, callbk_reportfun report_cb, void *report_data) { qubx_model *m = new qubx_model; m->version = 2; m->Nstate = Nstate; m->Nclass = Nclass; qubx_model_storage *st = new qubx_model_storage(m->Nstate, m->Nclass, class_of_state, report_cb, report_data, m->IeqFv, m->amp0, m->var0, m->Vrev, m->Vleak, m->Gleak); m->storage = st; m->clazz = st->clazz.p; m->usePeq = m->IeqFv = 0; m->Nchannel = 1; m->stop_flag = 0; m->progress = 0; if ( Nstate > 0 ) { m->amp0 = m->var0 = 0.0; m->P0 = st->P0.p; m->amp = st->amp.p; m->var = st->var.p; m->d_amp = st->d_amp.p; m->d_var = st->d_var.p; m->K0 = st->K0.pp; m->K1 = st->K1.pp; m->K2 = st->K2.pp; m->d_K0 = st->d_K0.pp; m->d_K1 = st->d_K1.pp; m->d_K2 = st->d_K2.pp; m->L = st->L.pp; m->V = st->V.pp; m->P = st->P.pp; } return m; } extern "C" QUBFAST_API void qubx_model_free(qubx_model *m) { delete (qubx_model_storage*) m->storage; delete m; } #ifdef EMSCRIPTEN extern "C" EMSCRIPTEN_KEEPALIVE void qubx_model_to_console(qubx_model *m) { EM_ASM_ARGS({ console.log('Nstate:', $0, 'Nclass:', $1, 'clazz[0]:', $2, 'clazz[1]:', $3, 'P0[0]:', $4, 'P0[1]:', $5); }, m->Nstate, m->Nclass, m->clazz[0], m->clazz[1], m->P0[0], m->P0[1]); EM_ASM_ARGS({ console.log('usePeq:', $0, 'IeqFv:', $1, 'Nchannel:', $2, 'iVoltage:', $3); }, m->usePeq, m->IeqFv, m->Nchannel, m->iVoltage); EM_ASM_ARGS({ console.log('Gleak:', $0, 'Vleak:', $1, 'Vrev:', $2, 'amp0', $3, 'var0', $4); }, m->Gleak, m->Vleak, m->Vrev, m->amp0, m->var0); EM_ASM_ARGS({ console.log('Amp[0]', $0, 'Amp[1]', $1, 'Var[0]', $2, 'Var[1]', $3); }, m->amp[0], m->amp[1], m->var[0], m->var[1]); EM_ASM_ARGS({ console.log('k0 00:', $0, 'k0 01:', $1, 'k0 10:', $2, 'k0 11:', $3); }, m->K0[0][0], m->K0[0][1], m->K0[1][0], m->K0[1][1]); EM_ASM_ARGS({ console.log('k0 00:', $0, 'k0 01:', $1, 'k0 10:', $2, 'k0 11:', $3); }, m->K0[0][0], m->K0[0][1], m->K0[1][0], m->K0[1][1]); EM_ASM_ARGS({ console.log('k1 00:', $0, 'k1 01:', $1, 'k1 10:', $2, 'k1 11:', $3); }, m->K1[0][0], m->K1[0][1], m->K1[1][0], m->K1[1][1]); EM_ASM_ARGS({ console.log('l 00:', $0, 'l 01:', $1, 'l 10:', $2, 'l 11:', $3); }, m->L[0][0], m->L[0][1], m->L[1][0], m->L[1][1]); EM_ASM_ARGS({ console.log('v 00:', $0, 'v 01:', $1, 'v 10:', $2, 'v 11:', $3); }, m->V[0][0], m->V[0][1], m->V[1][0], m->V[1][1]); EM_ASM_ARGS({ console.log('stop:', $0, 'progress:', $1); }, m->stop_flag, m->progress); EM_ASM_ARGS({ console.log('&m:', $0, 'L:', $1, 'L[0]:', $2); }, m, m->L, m->L[0]); } #endif //-------------------------------------------------------------------------------- // The CChannelCombine class iterates through all combinations of states to make // a multi channel model. Ex( Ch=2, States=3 ) : [2 0 0] [1 1 0] [0 2 0] [1 0 1] // [0 1 1] [0 0 2]. A c function equivalent is also in qublib nexcom0() //-------------------------------------------------------------------------------- class CChannelCombine { private : int m_iChannels; int m_iStates; int m_iTemp; int m_iStateIndex; bool m_lContinue; int * m_aStates; public : // Initialize and create the first combination CChannelCombine(int iChannels, int iStates); // Check if another combination exists bool GetNextCombine(); // Access the state elements with a 0 based index ( internally stored as 1 based array ) int State0(int i); ~CChannelCombine(); }; CChannelCombine :: CChannelCombine(int iChannels, int iStates ) { m_aStates = new int[iStates+1]; m_iChannels=iChannels; m_iStates=iStates; m_aStates[1]=iChannels; m_iTemp=iChannels; m_iStateIndex=0; for( int iState=2; iState<=iStates; ++iState) m_aStates[iState]=0; m_lContinue=true; } bool CChannelCombine :: GetNextCombine( ) { if( m_aStates[m_iStates] == m_iChannels ) return false; if(m_iTemp > 1) m_iStateIndex=0; m_iStateIndex++; m_iTemp = m_aStates[m_iStateIndex]; m_aStates[m_iStateIndex] = 0; m_aStates[1] = m_iTemp - 1; m_aStates[m_iStateIndex + 1] = m_aStates[m_iStateIndex + 1] + 1; return true; } int CChannelCombine :: State0(int i){ return m_aStates[i+1]; } CChannelCombine :: ~CChannelCombine() { delete [] m_aStates; } //----------------------------------------------------------------------- // Slightly Similar to a numerical recipes algorithm eclazz, but this // returns a sorted aClasses array (though it is less efficient). // aData - array of values. iData - number of values. // iClasses - output number of classes. aClasses - output class values. // ia[0..iData-1] - output index to aClasses for each aData[0..iData-1] // This function iteratively finds the minimum value in the list, then // considers all values within 1.0e-5 of that to be equivalent class and // removes them from the list until all data have been placed in classes. void eclass(float * aData, int iData, int & iClasses, float * aClasses, int * ia, double openDirection) { int i,k; float amin; int iCopyPos; // While scanning aClasses array, position being copied to ( = current - # of elements at current class already processed ) bool lCompare = ( openDirection > 0 ); // whether to find min or max ( ascending or descending ) int * aRemain = new int[iData]; // Array of indices of items to be classified for (i=0; i<iData; i++) aRemain[i] = i; iClasses = 0; while( iData > 0 ) { // Find the minimum (or maximum ) value in the remaining aRemain list k = aRemain[0]; amin = aData[k]; for( i=1; i<iData; i++ ) { k=aRemain[i]; if( lCompare == (aData[k]<amin)) amin=aData[k]; } // add amin to aClasses[] array of classes and all aData[k]~= amin set ia[k]=iClasses aClasses[iClasses]=amin; iCopyPos=0; for (i=0; i<iData; i++) { k = aRemain[i]; if (fabs(aData[k]-amin)<1.0e-5) ia[k]=iClasses; // include as result for this minimum ... else aRemain[iCopyPos++]=k; // ... or copy back to array for next iteration } iData=iCopyPos; iClasses++; } delete [] aRemain; } //----------------------------------------------------------------------------- // multi-channel Markov model creation : this function will make a multichannel // markov model out of a single channel markov model // Input nchannel, nstate, npath, state, path, ratio // Output nmutistate, nmutipath, nmuticlass, mutistate, mutipath //----------------------------------------------------------------------- extern "C" QUBFAST_API void qubx_mutimakv_size(int nchannel, int nstate, QUBFAST_VAR_NOT_USED int npath, QUBFAST_VAR_NOT_USED int *path, int *nmutistate, int *nmutipath) // returns upper bound on array sizes needed for mutistate and mutipath, via args { int i, iPaths=0, iState; //-- Precalc model size for memory allocation // Calculates the number of Combinations of channels/states per row in mutistate. // For each state, add to temp log(state+nchann)-log(state) and then antilog on the sum //float tmp=0.0; //for( n=1; n<nstate; n++) // tmp += float(log(double(n+nchannel))-log(double(n))); //n = int(exp(tmp)+1); //estimates nMutiState - may be 1 greater than necc. //*nmutistate = n * (nstate+1); //----- Fill in mutistate , acond, iPaths. // mutistate is an integer array of (estimated nMutiState) * (nstate+1) // Each (nState+1) group is a 1-based array of number of channels in each state, // and [0] element is set to the eclass result class for the row // iterate through combinations of states / channels and set nMutiState // aCond is the effective conductance <aCond[]> = sum[all states] ( channels * ratio ) // iPaths is the number of mutistate connections which have only one channel transition *nmutistate = 0; CChannelCombine oMeta(nchannel, nstate); do { for ( i=0; i<nstate; ++i ) { iState=oMeta.State0(i); if( iState > 0 ) // pre calc # of paths iPaths += (nstate-1); } ++(*nmutistate); } while( oMeta.GetNextCombine() ); //----- // precalc the number of transition paths for memory allocation // iterates through all combinations of mutistates and counts those // which differ only by 1 transition. // New method : for each non zero state, add paths for the number of // places one of the channels could go. This is precalced above. // This calculates a maximum possible, but the actual number used is less /* n=0; for(k=0; k<nmutistate; k++) for (l=0; l<nmutistate; l++) { s=0; for (i=0; i<nstate; i++) s += int(fabs(aMutiState[k][i+1]-aMutiState[l][i+1])); if (s==2) n++; } mutipath = new int[n*4]; */ *nmutipath = iPaths; } extern "C" QUBFAST_API void qubx_mutimakv(int nchannel, int nstate, int npath, int *state, int *path, float *ratio, int nmutistate, int nmutipath, int* nmuticlass, int* mutistate, int* mutipath) // mutistate = new int[nmutistate * (nstate + 1)] // mutipath = new int[nmutipath * 4] { int i,k=0,l=0,m,n,s,t, iPaths=0, iState; //-- Determine whether open is negative or positive and use to set sort order // for muticlasses in eclass(). // Note : The try catch is probably not effective here. Accessing beyond the // bounds of the array would likely run into more data and not throw an exception. // Better to ensure that the passed array ratio[] has an element [1] always. double openDirection; try { openDirection = ratio[1]; } catch (...) { openDirection = 1.0; } float * aCond = new float[nmutistate]; // Conductance class sums // Create a 2d style array for accessing mutistate array int ** aMutiState = new int * [nmutistate]; for( i=0; i<nmutistate; ++i ) aMutiState[i] = &(mutistate[i*(nstate+1)]); //----- Fill in mutistate , acond, iPaths. // mutistate is an integer array of (estimated nMutiState) * (nstate+1) // Each (nState+1) group is a 1-based array of number of channels in each state, // and [0] element is set to the eclass result class for the row // iterate through combinations of states / channels and set nMutiState // aCond is the effective conductance <aCond[]> = sum[all states] ( channels * ratio ) // iPaths is the number of mutistate connections which have only one channel transition nmutistate=0; CChannelCombine oMeta(nchannel, nstate); do { aCond[nmutistate]=0.0; for ( i=0; i<nstate; ++i ) { iState=oMeta.State0(i); aMutiState[nmutistate][i+1]=iState; aCond[nmutistate] += ((float) iState) * ratio[state[i]]; if( iState > 0 ) // pre calc # of paths iPaths += (nstate-1); } ++nmutistate; } while( oMeta.GetNextCombine() ); //----- conductance classes float * aClasses = new float[nmutistate]; // output buffer, discarded unused int * ia = new int[nmutistate]; // mapping of data to classes eclass( aCond, nmutistate, *nmuticlass, aClasses, ia, openDirection); // Fill C,ia with classes for (n=0; n<nmutistate; n++) aMutiState[n][0] = ia[n]; delete [] aCond; delete [] aClasses; delete [] ia; // transition paths - for each mutipath pair (m,n), if they differ // by only 1 channel state change then find the original states (k,l) // and check if a path exists between k & l -->> add a mutipath if so. // // Note : Consider a transition where two channels change state in a // single delta T - is this accounted for elsewhere ? {JB} // In a simple 2 state 3 channel model currently (C,C,C) we are only // setting mutipaths for (C,C,O),(C,O,C),(O,C,C) but all states // including (O,O,O) are reachable in a single delta T ? // // Ans: A=exp(Q*dt) doesn't have any zeros -- it allows for many quick transitions within one dt. {CN} nmutipath = 0; for( m=0; m<nmutistate; m++ ) { for( n=0; n<nmutistate; n++ ) { s=0; for( i=0; i<nstate; i++) s += abs(aMutiState[m][i+1]-aMutiState[n][i+1]); if (s==2) { // if muti[m] can become muti[n] by only 1 channel changing state // Find k,l the source and dest states for (i=0; i<nstate; i++) { t = aMutiState[m][i+1]-aMutiState[n][i+1]; if (t==1) k = i; if (t==-1) l = i; } // Check for a path from k to l in model for (i=0; i<npath; i++) { if (path[i*4+0]==k && path[i*4+1]==l) { // add a nutipath mutipath[nmutipath*4+0] = m; mutipath[nmutipath*4+1] = n; mutipath[nmutipath*4+2] = k; mutipath[nmutipath*4+3] = l; nmutipath++; } } } } } delete [] aMutiState; } void ktomutik(int nstate, QUBFAST_VAR_NOT_USED int npath, QUBFAST_VAR_NOT_USED int* path, int nmutistate, int* mutistate, int nmutipath, int* mutipath, double *k0, double *k1, double *k2, int *L, int *V, int *P, double *mutik0, double *mutik1, double *mutik2, int *mutiL, int *mutiV, int *mutiP) { int I,J,i,j,m; // rate constants for (I=0; I<nmutistate; I++) { for (J=0; J<nmutistate; J++) { mutik0[I*nmutistate+J] = 0.0; mutik1[I*nmutistate+J] = 0.0; mutik2[I*nmutistate+J] = 0.0; mutiL[I*nmutistate+J] = 0; mutiV[I*nmutistate+J] = 0; mutiP[I*nmutistate+J] = 0; } } for (m=0; m<nmutipath; m++) { I = mutipath[m*4+0]; J = mutipath[m*4+1]; i = mutipath[m*4+2]; j = mutipath[m*4+3]; mutik0[I*nmutistate+J] = mutistate[I*(nstate+1)+i+1]*k0[i*nstate+j]; mutik1[I*nmutistate+J] = k1[i*nstate+j]; mutik2[I*nmutistate+J] = k2[i*nstate+j]; mutiL[I*nmutistate+J] = L[i*nstate+j]; mutiV[I*nmutistate+J] = V[i*nstate+j]; mutiP[I*nmutistate+J] = P[i*nstate+j]; } } qubx_multi_model_storage::qubx_multi_model_storage(int Nstate, int& npath_, int& nmutistate_, int& nmutipath_, int& nmuticlass_) : nmutistate(nmutistate_), nmutipath(nmutipath_), nmuticlass(nmuticlass_), npath(npath_), class_order(Nstate) {} void qubx_multi_model_storage::setup(int nmutistate_, int nmutipath_, qubx_multi_model *mm) { nmutistate = nmutistate_; nmutipath = nmutipath_; cmutistate.resize(nmutistate_); mutistate.resize(nmutistate_, mm->single_model->Nstate+1); mutipath.resize(nmutipath_, 4); mm->cmutistate = cmutistate.p; mm->mutistate = mutistate.p; mm->mutipath = mutipath.p; } extern "C" QUBFAST_API qubx_multi_model* qubx_multi_model_create(qubx_model *single_model) { qubx_multi_model *mm = new qubx_multi_model; mm->version = 1; mm->single_model = single_model; qubx_multi_model_storage *st = new qubx_multi_model_storage(single_model->Nstate, mm->npath, mm->nmutistate, mm->nmutipath, mm->nmuticlass); mm->storage = st; mm->class_order = st->class_order.p; int nstate = single_model->Nstate; // renumber classes to be contiguous starting at 0: std::vector<int> occupied(single_model->Nclass); for (int i=0; i<nstate; ++i) occupied[single_model->clazz[i]] = 1; int nclass = 0; for (int i=0; i<single_model->Nclass; ++i) { if ( occupied[i] ) { mm->class_order[nclass] = i; occupied[i] = nclass++; } } mm->nclass_order = nclass; std::vector<int> clazz(nstate); for (int i=0; i<nstate; ++i) clazz[i] = occupied[single_model->clazz[i]]; // derive legacy path, npath int npath = 0; for (int a=0; a<nstate; ++a) { for (int b=a+1; b<nstate; ++b) { if ( single_model->K0[a][b] ) npath += 2; } } st->npath = npath; st->path.resize(npath, 4); mm->path = st->path.p; int ipath = 0; for (int a=0; a<nstate; ++a) { for (int b=a+1; b<nstate; ++b) { if ( single_model->K0[a][b] ) { st->path.m(ipath, 0) = st->path.m(ipath+1, 1) = a; st->path.m(ipath, 1) = st->path.m(ipath+1, 0) = b; ipath += 2; } } } // ratio: excess class amplitude (minus baseline) fq::vector<float> ratio( nclass ); float amp0 = (float) (single_model->iVoltage ? 0.0 : single_model->amp[0]); for (int i=0; i<nclass; ++i ) ratio[i] = ((float) single_model->amp[mm->class_order[i]]) - amp0; qubx_mutimakv_size(single_model->Nchannel, nstate, npath, mm->path, &mm->nmutistate, &mm->nmutipath); st->setup(mm->nmutistate, mm->nmutipath, mm); qubx_mutimakv( single_model->Nchannel, nstate, npath, &clazz[0], mm->path, ratio, mm->nmutistate, mm->nmutipath, &mm->nmuticlass, mm->mutistate, mm->mutipath); for (int i=0; i<mm->nmutistate; ++i ) mm->cmutistate[i] = mm->mutistate[i*(nstate+1)+0]; // mutistate[i][0] -- muticlass of mutistate i qubx_model_storage *single_st = (qubx_model_storage *) single_model->storage; qubx_model *multi_model = qubx_model_create(mm->nmutistate, mm->nmuticlass, mm->cmutistate, single_st->report_cb, single_st->report_data); mm->multi_model = multi_model; multi_model->usePeq = single_model->usePeq; multi_model->Nchannel = 1; multi_model->Gleak = single_model->Gleak; multi_model->Vleak = single_model->Vleak; multi_model->Vrev = single_model->Vrev; memcpy(multi_model->clazz, mm->cmutistate, mm->nmutistate*sizeof(int)); return mm; } extern "C" QUBFAST_API void qubx_multi_model_free(qubx_multi_model *mm) { qubx_model_free(mm->multi_model); delete (qubx_multi_model_storage*) mm->storage; delete mm; } extern "C" QUBFAST_API void qubx_multi_model_update(qubx_multi_model *mm) { int nstate = mm->single_model->Nstate; ktomutik(nstate, mm->npath, mm->path, mm->nmutistate, mm->mutistate, mm->nmutipath, mm->mutipath, mm->single_model->K0[0], mm->single_model->K1[0], mm->single_model->K2[0], mm->single_model->L[0], mm->single_model->V[0], mm->single_model->P[0], mm->multi_model->K0[0], mm->multi_model->K1[0], mm->multi_model->K2[0], mm->multi_model->L[0], mm->multi_model->V[0], mm->multi_model->P[0]); // state entry probability is not dealt with here... int Nclass = mm->nclass_order; fq::vector<int> clazz(nstate); fq::vector<int> cls_renumber(mm->single_model->Nclass); for (int i=0; i<Nclass; ++i) cls_renumber[ mm->class_order[i] ] = i; for (int i=0; i<nstate; ++i) clazz[i] = cls_renumber[ mm->single_model->clazz[i] ]; fq::vector<double> exc_amp(Nclass); fq::vector<double> exc_var(Nclass); double amp0, var0; if ( mm->single_model->iVoltage ) { mm->multi_model->IeqFv = mm->single_model->IeqFv; mm->multi_model->iVoltage = mm->single_model->iVoltage; mm->multi_model->amp0 = mm->single_model->amp0; mm->multi_model->var0 = mm->single_model->var0; amp0 = var0 = 0.0; for (int i=0; i<Nclass; ++i) { exc_amp[i] = mm->single_model->amp[ mm->class_order[i] ]; exc_var[i] = mm->single_model->var[ mm->class_order[i] ]; } } else { amp0 = mm->multi_model->amp0 = mm->single_model->amp[0]; var0 = mm->multi_model->var0 = mm->single_model->var[0]; for (int i=0; i<Nclass; ++i) { exc_amp[i] = mm->single_model->amp[ mm->class_order[i] ] - amp0; exc_var[i] = mm->single_model->var[ mm->class_order[i] ] - var0; } } double *mutiamp = mm->multi_model->amp; double *mutivar = mm->multi_model->var; for (int i=0; i<mm->nmuticlass; i++) { mutiamp[i] = amp0; // closed_amp + sum(open_amp === ratio) //mutixms[i] = 0.0; // sqrt( sum( xms*xms ) ) // now this should probably be // sqrt( closed_noise^2 + sum(excess_noise^2) ) ?? mutivar[i] = var0; // will be sqrt( closed_noise^2 [var0] + sum(excessNoise^2) ) //float sum=0.0; // # states contributing to this mutistate; should always be == nchannel int k; for (k=0; mm->mutistate[k*(nstate+1)+0]!=i; k++) // mutistate[k][0] ; // find the first mutistate in this muticlass. // if (e.g. 2 sub-opens == 1 open 1 closed) // then the first example may not have representative s.d. and allXFixed for (int j=0; j<nstate; j++) { // mutistate[k][j+1] int countStateJ = mm->mutistate[k*(nstate+1)+j+1]; mutiamp[i] += countStateJ*exc_amp[clazz[j]]; mutivar[i] += countStateJ*exc_var[clazz[j]]; } } } qubx_constrained_storage::qubx_constrained_storage(int& Npar_, int& NparR_, int& NfPar_, int& Ncns_, int& NcnsR_) : Npar(Npar_), NparR(NparR_), NfPar(NfPar_), Ncns(Ncns_), NcnsR(NcnsR_), Ain(3*Npar, Npar), Acns(Npar, Npar), Ainv(Npar, Npar), Ascal(Npar, Npar), Asci(Npar, Npar), Bin(3*Npar), Bcns(Npar), Bscal(Npar), pars(Npar), fPars_unscaled(Npar), fPars(Npar), kR_indices(Npar_) { Ncns = NfPar = 0; } int qubx_constrained_storage::setup() { if ( ! NfPar ) { constraint_indices.resize(Ncns); IdentifyFixedParams(Npar, Ncns, pars.p, Ain.pp, Bin.p, &NparR, kR_indices.p, &NcnsR, constraint_indices.p); parsR.resize(NparR); AinR.resize(NcnsR, NparR); BinR.resize(NcnsR); EliminateFixedParams(Npar, Ncns, pars.p, Ain.pp, Bin.p, NparR, kR_indices.p, NcnsR, constraint_indices.p, parsR.p, AinR.pp, BinR.p); ReduceConstraints(NparR, &NcnsR, AinR.pp, BinR.p); NfPar = NparR - NcnsR; } if ( NcnsR < NparR ) { int rtn = SetupLinearConstraints(NcnsR, AinR.pp, BinR.p, NparR, parsR.p, fPars_unscaled.p, Acns.pp, Bcns.p, Ainv.pp, NULL, NULL); if ( 0 == rtn ) { SetupStartAtOnes(NfPar, fPars_unscaled.p, Ascal.pp, Bscal.p, Asci.pp); for (int i=0; i<NfPar; ++i) fPars.p[i] = 1.0; } return rtn; } return NfPar; } void qubx_constrained_storage::free_to_pars() { if ( NcnsR < NparR ) { ParsToK(NfPar, NfPar, fPars.p, Ascal.pp, Asci.pp, Bscal.p, fPars_unscaled.p); ParsToK(NfPar, NparR, fPars_unscaled.p, Acns.pp, Ainv.pp, Bcns.p, parsR.p); Kr_to_K(parsR.p, NparR, pars.p, kR_indices.p); } } void qubx_constrained_storage::pars_to_free() { if ( Ncns < Npar ) { for (int i=0; i<NparR; ++i) parsR.p[i] = pars.p[kR_indices.p[i]]; KtoPars(NparR, NfPar, parsR.p, Acns.pp, Ainv.pp, Bcns.p, fPars_unscaled.p); KtoPars(NfPar, NfPar, fPars_unscaled.p, Ascal.pp, Asci.pp, Bscal.p, fPars.p); } } void qubx_constrained_storage::calc_std(double *InvHessian, double *dPar) { matrix<double> Acombined = prod(Acns.m, Ascal.m); int n = NparR; int m = NfPar; // each element squared: for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) Acombined(i,j) *= Acombined(i,j); // mil confidence: // dx[i] = sqrt( sum_j( A[i,j]**2 * H[j,j] ) ) { * exp(x[i]) } for (int i=0; i<n; ++i) { double sum = 0.0; for (int j=0; j<m; ++j) { sum += Acombined(i,j) * InvHessian[j*m+j]; } dPar[kR_indices.p[i]] = sum; } } extern "C" QUBFAST_API qubx_constrained* qubx_constrained_create(int Npar) { qubx_constrained *obj = new qubx_constrained; obj->Npar = Npar; qubx_constrained_storage *st = new qubx_constrained_storage(obj->Npar, obj->NparR, obj->NfPar, obj->Ncns, obj->NcnsR); obj->version = 1; obj->storage = st; obj->Ain = st->Ain.pp; obj->Bin = st->Bin.p; obj->pars = st->pars.p; obj->fPars = st->fPars.p; return obj; } extern "C" QUBFAST_API void qubx_constrained_free(qubx_constrained *obj) { qubx_constrained_storage *st = (qubx_constrained_storage *) obj->storage; delete st; delete obj; } extern "C" QUBFAST_API int qubx_constrained_setup(qubx_constrained *obj) { qubx_constrained_storage *st = (qubx_constrained_storage *) obj->storage; int rtn = st->setup(); obj->NfPar = st->NfPar; obj->Ncns = st->Ncns; obj->Acns = st->Acns.pp; obj->Ascal = st->Ascal.pp; obj->Bcns = st->Bcns.p; obj->Bscal = st->Bscal.p; return rtn; } extern "C" QUBFAST_API void qubx_constrained_free_to_pars(qubx_constrained *obj) { qubx_constrained_storage *st = (qubx_constrained_storage *) obj->storage; st->free_to_pars(); } extern "C" QUBFAST_API void qubx_constrained_pars_to_free(qubx_constrained *obj) { qubx_constrained_storage *st = (qubx_constrained_storage *) obj->storage; st->pars_to_free(); } extern "C" QUBFAST_API void qubx_constrained_calc_std(qubx_constrained *obj, double *InvHessian, double *dPar) { qubx_constrained_storage *st = (qubx_constrained_storage *) obj->storage; st->calc_std(InvHessian, dPar); } qubx_stim_amps_storage::qubx_stim_amps_storage(qubx_model *m, int Nstim_, int Nsig_, double *stimclasses_, double *sc_frac_) : model(m), Nstim(Nstim_), Nsig(Nsig_), stimclasses(Nstim_*Nsig_), sc_frac(Nstim_), sc_amp(Nstim_, m->Nclass), sc_var(Nstim_, m->Nclass), sc_std(Nstim_, m->Nclass) { memcpy(stimclasses.p, stimclasses_, Nstim*Nsig*sizeof(double)); memcpy(sc_frac.p, sc_frac_, Nstim*sizeof(double)); setup_scs(); cns = qubx_constrained_create((m->IeqFv ? 2 : 0) + 2*m->Nclass); cnst = (qubx_constrained_storage *) cns->storage; model_to_pars(); } qubx_stim_amps_storage::~qubx_stim_amps_storage() { qubx_constrained_free(cns); } void qubx_stim_amps_storage::setup_scs() { int iVoltage = model->IeqFv ? model->iVoltage : 0; if ( iVoltage ) { for (int sc=0; sc<Nstim; ++sc) { double volts = 1e-3 * stimclasses.p[sc * Nsig + iVoltage]; double dV = volts - 1e-3 * model->Vrev; double Ileak = (volts - 1e-3*model->Vleak)*model->Gleak; for (int c=0; c<model->Nclass; ++c) { sc_amp.m(sc,c) = model->amp0 + dV*model->amp[c] + Ileak; sc_var.m(sc,c) = model->var0 + dV*dV*model->var[c]; sc_std.m(sc,c) = sqrt(sc_var.m(sc,c)); } } } else { for (int sc=0; sc<Nstim; ++sc) { memcpy(sc_amp.pp[sc], model->amp, model->Nclass*sizeof(double)); memcpy(sc_var.pp[sc], model->var, model->Nclass*sizeof(double)); for (int c=0; c<model->Nclass; ++c) sc_std.m(sc,c) = (model->var[c] > 0.0) ? sqrt(model->var[c]) : 0.0; } } } void qubx_stim_amps_storage::pars_to_model() { int p=0; for (int i=0; i<model->Nclass; ++i) { model->amp[i] = cns->pars[p++]; model->var[i] = fabs(cns->pars[p++]); } if ( model->IeqFv && model->iVoltage ) { model->amp0 = cns->pars[p++]; model->var0 = fabs(cns->pars[p++]); } } void qubx_stim_amps_storage::model_to_pars() { int p=0; for (int i=0; i<model->Nclass; ++i) { cns->pars[p++] = model->amp[i]; cns->pars[p++] = model->var[i]; } if ( model->IeqFv && model->iVoltage ) { cns->pars[p++] = model->amp0; cns->pars[p++] = model->var0; } } extern "C" QUBFAST_API qubx_stim_amps* qubx_stim_amps_create(qubx_model *m, int Nstim_, int Nsig_, double *stimclasses_, double *sc_frac_) { qubx_stim_amps *obj = new qubx_stim_amps; qubx_stim_amps_storage *st = new qubx_stim_amps_storage(m, Nstim_, Nsig_, stimclasses_, sc_frac_); obj->version = 1; obj->storage = st; obj->model = m; obj->Nstim = st->Nstim; obj->Nsig = st->Nsig; obj->stimclasses = st->stimclasses.p; obj->sc_frac = st->sc_frac.p; obj->cns = st->cns; obj->sc_amp = st->sc_amp.pp; obj->sc_std = st->sc_std.pp; return obj; } extern "C" QUBFAST_API void qubx_stim_amps_free(qubx_stim_amps *obj) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; delete st; delete obj; } extern "C" QUBFAST_API void qubx_stim_amps_setup_scs(qubx_stim_amps *obj) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; st->setup_scs(); } extern "C" QUBFAST_API void qubx_stim_amps_pars_to_model(qubx_stim_amps *obj) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; st->pars_to_model(); } extern "C" QUBFAST_API void qubx_stim_amps_model_to_pars(qubx_stim_amps *obj) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; st->model_to_pars(); } extern "C" QUBFAST_API void qubx_stim_amps_fix_amp0(qubx_stim_amps *obj) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; qubx_constrained *c = obj->cns; for (int j=0; j<c->Npar; ++j) c->Ain[c->Ncns][j] = 0.0; c->Ain[c->Ncns][2*st->model->Nclass] = 1.0; c->Bin[c->Ncns] = st->model->amp0; ++c->Ncns; } extern "C" QUBFAST_API void qubx_stim_amps_fix_var0(qubx_stim_amps *obj) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; qubx_constrained *c = obj->cns; for (int j=0; j<c->Npar; ++j) c->Ain[c->Ncns][j] = 0.0; c->Ain[c->Ncns][2*st->model->Nclass+1] = 1.0; c->Bin[c->Ncns] = st->model->var0; ++c->Ncns; } extern "C" QUBFAST_API void qubx_stim_amps_fix_amp(qubx_stim_amps *obj, int cls) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; qubx_constrained *c = obj->cns; for (int j=0; j<c->Npar; ++j) c->Ain[c->Ncns][j] = 0.0; c->Ain[c->Ncns][2*cls] = 1.0; c->Bin[c->Ncns] = st->model->amp[cls]; ++c->Ncns; } extern "C" QUBFAST_API void qubx_stim_amps_fix_var(qubx_stim_amps *obj, int cls) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; qubx_constrained *c = obj->cns; for (int j=0; j<c->Npar; ++j) c->Ain[c->Ncns][j] = 0.0; c->Ain[c->Ncns][2*cls+1] = 1.0; c->Bin[c->Ncns] = st->model->var[cls]; ++c->Ncns; } extern "C" QUBFAST_API void qubx_stim_amps_calc_std(qubx_stim_amps *obj, double *InvHessian) { qubx_stim_amps_storage *st = (qubx_stim_amps_storage *) obj->storage; std::vector<double> dPar(obj->cns->Npar); qubx_constrained_calc_std(obj->cns, InvHessian, & dPar[0]); int p=0; for (int i=0; i<st->model->Nclass; ++i) { st->model->d_amp[i] = dPar[p++]; st->model->d_var[i] = dPar[p++]; } if ( st->model->IeqFv && st->model->iVoltage ) { st->model->d_amp0 = dPar[p++]; st->model->d_var0 = dPar[p++]; } } qubx_stim_rates_storage::qubx_stim_rates_storage(qubx_model *m, int Nstim_, int Nsig_, double *stimclasses_, double *sc_frac_, double sampling_, eigen_func custom_eigen_) : model(m), custom_eigen(custom_eigen_), Nstim(Nstim_), Nsig(Nsig_), stimclasses(Nstim_*Nsig_), sc_frac(Nstim), sampling(sampling_), QQ(Nstim_), AA(Nstim_), QQ_evec(Nstim_), QQ_einv(Nstim_), QQ_eig(Nstim_), QQ_imag(Nstim_), QQ_p(Nstim_), AA_p(Nstim_), QQ_evec_p(Nstim_), QQ_einv_p(Nstim_), QQ_eig_p(Nstim_), QQ_imag_p(Nstim_), A(m->Nstate, m->Nstate), tmp_diag(m->Nstate, m->Nstate), tmp_m(m->Nstate, m->Nstate) { memcpy(stimclasses.p, stimclasses_, Nstim*Nsig*sizeof(double)); if ( sc_frac_ ) memcpy(sc_frac.p, sc_frac_, Nstim*sizeof(double)); cns = qubx_constrained_create(CountK(m->Nstate)); cnst = (qubx_constrained_storage *) cns->storage; int Ns = model->Nstate; for (int sc=0; sc<Nstim; ++sc) { QQ[sc].resize(Ns,Ns); QQ_p[sc] = QQ[sc].pp; AA[sc].resize(Ns,Ns); AA_p[sc] = AA[sc].pp; QQ_evec[sc].resize(Ns,Ns); QQ_evec_p[sc] = QQ_evec[sc].pp; QQ_einv[sc].resize(Ns,Ns); QQ_einv_p[sc] = QQ_einv[sc].pp; QQ_eig[sc].resize(Ns); QQ_eig_p[sc] = QQ_eig[sc].p; QQ_imag[sc].resize(Ns); QQ_imag_p[sc] = QQ_imag[sc].p; } setup_QQ(); setup_AA(); } qubx_stim_rates_storage::~qubx_stim_rates_storage() { qubx_constrained_free(cns); } void qubx_stim_rates_storage::setup_QQ() { qubx_model_storage *st = (qubx_model_storage *) model->storage; for (int sc=0; sc<Nstim; ++sc) BuildQ_p(model->Nstate, QQ[sc].m, st->K0.m, st->K1.m, st->K2.m, st->L.m, st->V.m, st->P.m, stimclasses.p + sc*Nsig); } void qubx_stim_rates_storage::setup_AA() { qubx_model_storage *st = (qubx_model_storage *) model->storage; bool err_eigen = false; if ( custom_eigen ) { for (int sc=0; sc<Nstim; ++sc) { err_eigen = err_eigen || custom_eigen(model->Nstate, QQ[sc].p, QQ_eig[sc].p, QQ_imag[sc].p, QQ_evec[sc].p, QQ_einv[sc].p); if ( sampling ) expm_eig(QQ_eig[sc].p, QQ_evec[sc].m, QQ_einv[sc].m, sampling, tmp_diag.m, tmp_m.m, AA[sc].m); } } else { int n = model->Nstate; #pragma omp parallel { matrix<double> tmpd(n,n), tmpm(n,n); tmpd.clear(); #pragma omp for for (int sc=0; sc<Nstim; ++sc) { eigen_vals_vecs(QQ[sc].m, QQ_eig[sc].p, QQ_imag[sc].p, QQ_evec[sc].m, QQ_einv[sc].m, st->output); if ( sampling ) expm_eig(QQ_eig[sc].p, QQ_evec[sc].m, QQ_einv[sc].m, sampling, tmpd, tmpm, AA[sc].m); } } } if ( err_eigen ) { st->output << "No convergence in eigen calculations" << endl; } } void qubx_stim_rates_storage::setup_A(int sc, double dt) { setup_A(sc, dt, tmp_diag.m, tmp_m.m, A.m); } void qubx_stim_rates_storage::setup_A(int sc, double dt, matrix<double>& tmp_diag_x, matrix<double>& tmp_m_x, matrix<double>& A_result) { expm_eig(QQ_eig[sc].p, QQ_evec[sc].m, QQ_einv[sc].m, dt, tmp_diag_x, tmp_m_x, A_result); } void qubx_stim_rates_storage::fixRate(int a, int b) { AddFixRateConstraint(model->Nstate, model->K0, model->K1, model->K2, model->L, model->V, model->P, & cns->Ncns, cns->Ain, cns->Bin, a, b); } void qubx_stim_rates_storage::fixExp(int a, int b) { AddFixExpConstraint(model->Nstate, model->K0, model->K1, model->K2, model->L, model->V, model->P, & cns->Ncns, cns->Ain, cns->Bin, a, b); } void qubx_stim_rates_storage::scaleRate(int a, int b, int c, int d) { AddScaleRateConstraint(model->Nstate, model->K0, model->K1, model->K2, model->L, model->V, model->P, & cns->Ncns, cns->Ain, cns->Bin, a, b, c, d); } void qubx_stim_rates_storage::scaleExp(int a, int b, int c, int d) { AddScaleExpConstraint(model->Nstate, model->K0, model->K1, model->K2, model->L, model->V, model->P, & cns->Ncns, cns->Ain, cns->Bin, a, b, c, d); } void qubx_stim_rates_storage::balanceLoop(int Nloop, int *loopstates) { AddLoopBalConstraint(model->Nstate, model->K0, model->K1, model->K2, model->L, model->V, model->P, & cns->Ncns, cns->Ain, cns->Bin, Nloop, loopstates, consoleLog, 0); } void qubx_stim_rates_storage::balanceLoops() { int Nstate = model->Nstate; int Nrate = 0; std::vector<int> edges; for (int a=0; a<Nstate; ++a) { for (int b=a+1; b<Nstate; ++b) { if ( model->K0[a][b] ) { edges.push_back(a); edges.push_back(b); edges.push_back(b); edges.push_back(a); Nrate += 2; } } } std::vector<int> loopsizes(Nrate); std::vector<int> loopstates(Nrate*Nrate); int Nloop = qub_findloops(Nstate, Nrate, & edges[0], & loopsizes[0], & loopstates[0]); for (int i=0; i<Nloop; ++i) balanceLoop(loopsizes[i], & loopstates[i*Nrate]); } int qubx_stim_rates_storage::setup_constraints() { if ( ! cns->NfPar ) AddAutoConstraints(model->Nstate, model->K0, model->K1, model->K2, model->L, model->V, model->P, & cns->Ncns, cns->Ain, cns->Bin); model_to_pars(); return qubx_constrained_setup(cns); } void qubx_stim_rates_storage::pars_to_model() { KtoK012(model->Nstate, cns->pars, model->K0, model->K1, model->K2); setup_QQ(); setup_AA(); } void qubx_stim_rates_storage::model_to_pars() { K012toK(model->Nstate, model->K0, model->K1, model->K2, cns->pars); } extern "C" QUBFAST_API qubx_stim_rates *qubx_stim_rates_create(qubx_model *model, int Nstim_, int Nsig_, double *stimclasses_, double *sc_frac_, double sampling, eigen_func custom_eigen) { qubx_stim_rates *obj = new qubx_stim_rates; qubx_stim_rates_storage *st = new qubx_stim_rates_storage(model, Nstim_, Nsig_, stimclasses_, sc_frac_, sampling, custom_eigen); obj->version = 1; obj->storage = st; obj->model = model; obj->custom_eigen = custom_eigen; obj->Nstim = st->Nstim; obj->Nsig = st->Nsig; obj->stimclasses = st->stimclasses.p; obj->sc_frac = st->sc_frac.p; obj->cns = st->cns; obj->QQ = & st->QQ_p[0]; obj->AA = & st->AA_p[0]; obj->QQ_eig = & st->QQ_eig_p[0]; obj->QQ_imag = & st->QQ_imag_p[0]; obj->QQ_evec = & st->QQ_evec_p[0]; obj->QQ_einv = & st->QQ_einv_p[0]; obj->A = st->A.pp; return obj; } extern "C" QUBFAST_API void qubx_stim_rates_free(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; delete st; delete obj; } extern "C" QUBFAST_API void qubx_stim_rates_setup_QQ(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->setup_QQ(); } extern "C" QUBFAST_API void qubx_stim_rates_setup_AA(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->setup_AA(); } extern "C" QUBFAST_API void qubx_stim_rates_setup_A(qubx_stim_rates *obj, int sc, double dt) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->setup_A(sc, dt); } extern "C" QUBFAST_API void qubx_stim_rates_fix_rate(qubx_stim_rates *obj, int a, int b) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->fixRate(a, b); } extern "C" QUBFAST_API void qubx_stim_rates_fix_exp(qubx_stim_rates *obj, int a, int b) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->fixExp(a, b); } extern "C" QUBFAST_API void qubx_stim_rates_scale_rate(qubx_stim_rates *obj, int a, int b, int c, int d) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->scaleRate(a, b, c, d); } extern "C" QUBFAST_API void qubx_stim_rates_scale_exp(qubx_stim_rates *obj, int a, int b, int c, int d) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->scaleExp(a, b, c, d); } extern "C" QUBFAST_API void qubx_stim_rates_balance_loop(qubx_stim_rates *obj, int Nloop, int *loopstates) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->balanceLoop(Nloop, loopstates); } extern "C" QUBFAST_API void qubx_stim_rates_balance_loops(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->balanceLoops(); } extern "C" QUBFAST_API int qubx_stim_rates_setup_constraints(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; return st->setup_constraints(); } extern "C" QUBFAST_API void qubx_stim_rates_pars_to_model(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->pars_to_model(); } extern "C" QUBFAST_API void qubx_stim_rates_model_to_pars(qubx_stim_rates *obj) { qubx_stim_rates_storage *st = (qubx_stim_rates_storage *) obj->storage; st->model_to_pars(); } extern "C" QUBFAST_API void qubx_stim_rates_calc_std(qubx_stim_rates *obj, double *InvHessian) { std::vector<double> dPar(obj->cns->Npar); qubx_constrained_calc_std(obj->cns, InvHessian, & dPar[0]); dK_to_dK012(obj->model->Nstate, obj->cns->pars, &dPar[0], obj->model->K0, obj->model->d_K0, obj->model->d_K1, obj->model->d_K2); }