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 &amp0_, 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);
}