qub_constraints.cpp.html mathcode2html   
 Source file:   qub_constraints.cpp
 Converted:   Tue May 19 2015 at 15:35:14
 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 "qub_constraints.h"
#include "ublas_matrixutil.h"
#include "callbk_reportstream.h"

//std::ofstream logg;


/*
 What are the optimization parameters?  (some of) the off-diagonal elements of K0 and K1.
 Actually, we use log(K0) to keep them positive.
 Breaking with past practice, we include all the unconnected zeros,
 and put all the K0s before all the K1s.  As we'll see, this is a column vector.
UPDATE: we omit any fixed zero from the final K vector
*/

inline int k_ix_of(int Ns, int a, int b)
{
  return (a * (Ns - 1)) + b - ((b > a) ? 1 : 0);
}

extern "C" QUBFAST_API int CountK(int Ns)
{
  return 3*(Ns*Ns - Ns); // the off-diagonal elements, twice
}

extern "C" QUBFAST_API void K01toK(int Ns, double **K0, double **K1, double *K)
{
  int Noffd = CountK(Ns) / 3;
  int i=0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if ( a != b ) {
	double x = K0[a][b];
	if ( x > 0.0 ) // tricky; 1->0 and 0->0; see KtoK01
	  x = log(x);
	else
	  x = 0.0;
	K[i] = x;
	K[Noffd+i] = K1[a][b];
	++i;
      }
}

extern "C" QUBFAST_API void K012toK(int Ns, double **K0, double **K1, double **K2, double *K)
{
  int Noffd = CountK(Ns) / 3;
  int i=0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if ( a != b ) {
	double x = K0[a][b];
	if ( x > 0.0 ) // tricky; 1->0 and 0->0; see KtoK01
	  x = log(x);
	else
	  x = 0.0;
	K[i] = x;
	K[Noffd+i] = K1[a][b];
	K[2*Noffd+i] = K2[a][b];
	++i;
      }
}

// K alone isn't enough to reconstruct K0 and K1, since 0 could mean log(1), or else unconnected.
// We disambiguate by checking the original K0 for zeros, which mean unconnected.

#define MIN_LOG_K0 -30.0
#define MAX_LOG_K0 30.0

extern "C" QUBFAST_API int KtoK01(int Ns, double *K, double **K0, double **K1)
{
  int Noffd = CountK(Ns) / 3;
  int i = 0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if ( a != b ) {
	if ( K0[a][b] ) {
	  if ( (K[i] > MIN_LOG_K0) && (K[i] < MAX_LOG_K0) )
	    K0[a][b] = exp(K[i]);
	  else
	    return -(i+1);
	}
	K1[a][b] = K[Noffd+i];
	++i;
      }
  return 0;
}

extern "C" QUBFAST_API int KtoK012(int Ns, double *K, double **K0, double **K1, double **K2)
{
  int Noffd = CountK(Ns) / 3;
  int i = 0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if ( a != b ) {
	if ( K0[a][b] ) {
	  if ( (K[i] > MIN_LOG_K0) && (K[i] < MAX_LOG_K0) )
	    K0[a][b] = exp(K[i]);
	  else
	    return -(i+1);
	}
	K1[a][b] = K[Noffd+i];
	K2[a][b] = K[2*Noffd+i];
	++i;
      }
  return 0;
}

extern "C" QUBFAST_API int dK_to_dK01(int Ns, double *K, double *dK, double **K0, double **d_K0, double **d_K1)
{
  int Noffd = CountK(Ns) / 3;
  int i = 0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if ( a != b ) {
	if ( K0[a][b] ) {
	  d_K0[a][b] = dK[i];
	  if ( (K[i] > MIN_LOG_K0) && (K[i] < MAX_LOG_K0) )
	    d_K0[a][b] *= exp(K[i]);
	}
	d_K1[a][b] = dK[Noffd+i];
	++i;
      }
  return 0;
}

extern "C" QUBFAST_API int dK_to_dK012(int Ns, double *K, double *dK, double **K0, double **d_K0, double **d_K1, double **d_K2)
{
  int Noffd = CountK(Ns) / 3;
  int i = 0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if ( a != b ) {
	if ( K0[a][b] ) {
	  d_K0[a][b] = dK[i];
	  if ( (K[i] > MIN_LOG_K0) && (K[i] < MAX_LOG_K0) )
	    d_K0[a][b] *= exp(K[i]);
	}
	d_K1[a][b] = dK[Noffd+i];
	d_K2[a][b] = dK[2*Noffd+i];
	++i;
      }
  return 0;
}

// But some of those K are zero and have to stay zero, and the user may have other constraints.
// We represent the constraints by the system of linear equations (11) and derive the transformations (12, 13).

// Some constraints are more a part of the representation than the model:
//   * where K0 == 0 there's no connection; fix k0 and k1
//   * where V == 0  fix k1

// Ncns_max = upper_bound(Ncns) = Nk [fixed] + 3*Ncns_user
// Ain[Ncns_max][Nk]
// Bin[Ncns_max]
// Ncns = 0 (or how many pre-existing constraints in Ain,Bin)

extern "C" QUBFAST_API void AddAutoConstraints(int Ns, double **K0, QUBFAST_VAR_NOT_USED double ** K1, QUBFAST_VAR_NOT_USED double ** K2,
					       QUBFAST_VAR_NOT_USED int ** L, int **V, int **P,
					       int *Ncns, double **Ain, double *Bin)
// """Returns the constraints (Ain, Bin)   [11] which fix unconnected k0 and unsensitive k1."""
{
  int Nk = CountK(Ns);
  int Noffd = Nk/3;
  int i=0;
  for (int a=0; a<Ns; ++a)
    for (int b=0; b<Ns; ++b)
      if (a != b) {
        if ( ! K0[a][b] ) {
	  for (int j=0; j<Nk; ++j)
	    Ain[*Ncns][j] = 0.0;
	  Ain[*Ncns][i] = 1.0;
	  Bin[*Ncns] = 0.0;
	  V[a][b] = 0;
	  ++*Ncns;
	}
	if ( ! V[a][b] ) {
	  for (int j=0; j<Nk; ++j)
	    Ain[*Ncns][j] = 0.0;
	  Ain[*Ncns][Noffd+i] = 1.0;
	  Bin[*Ncns] = 0.0;
	  ++*Ncns;
	}
	if ( ! P[a][b] ) {
	  for (int j=0; j<Nk; ++j)
	    Ain[*Ncns][j] = 0.0;
	  Ain[*Ncns][2*Noffd+i] = 1.0;
	  Bin[*Ncns] = 0.0;
	  ++*Ncns;
	}
	++i;
      }
}

// The user might have some constraints too:

extern "C" QUBFAST_API void AddFixRateConstraint(int Ns, double **K0, QUBFAST_VAR_NOT_USED double ** K1, QUBFAST_VAR_NOT_USED double ** K2,
						 QUBFAST_VAR_NOT_USED int ** L, QUBFAST_VAR_NOT_USED int ** V, QUBFAST_VAR_NOT_USED int ** P,
						 int *Ncns, double **Ain, double *Bin,
						 int a, int b)
{
  int Nk = CountK(Ns);
  int k_ix = k_ix_of(Ns, a, b);
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;
  Ain[*Ncns][k_ix] = 1.0;
  Bin[*Ncns] = log(K0[a][b]);
  ++*Ncns;
}

extern "C" QUBFAST_API void AddFixExpConstraint(int Ns, QUBFAST_VAR_NOT_USED double ** K0, double **K1, QUBFAST_VAR_NOT_USED double ** K2,
						QUBFAST_VAR_NOT_USED int ** L, QUBFAST_VAR_NOT_USED int ** V, QUBFAST_VAR_NOT_USED int ** P,
						int *Ncns, double **Ain, double *Bin,
						int a, int b)
{
  int Nk = CountK(Ns);
  int k_ix = k_ix_of(Ns, a, b);
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;
  Ain[*Ncns][k_ix + Nk/3] = 1.0;
  Bin[*Ncns] = K1[a][b];
  ++*Ncns;
}

extern "C" QUBFAST_API void AddFixPresConstraint(int Ns, QUBFAST_VAR_NOT_USED double ** K0, QUBFAST_VAR_NOT_USED double **K1, double ** K2,
						 QUBFAST_VAR_NOT_USED int ** L, QUBFAST_VAR_NOT_USED int ** V, QUBFAST_VAR_NOT_USED int ** P,
						 int *Ncns, double **Ain, double *Bin,
						 int a, int b)
{
  int Nk = CountK(Ns);
  int k_ix = k_ix_of(Ns, a, b);
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;
  Ain[*Ncns][k_ix + 2*Nk/3] = 1.0;
  Bin[*Ncns] = K2[a][b];
  ++*Ncns;
}

// we optimize log(k0); this constraint actually means log(k0ab) - log(k0cd) = const

extern "C" QUBFAST_API void AddScaleRateConstraint(int Ns, double **K0, QUBFAST_VAR_NOT_USED double ** K1, QUBFAST_VAR_NOT_USED double ** K2,
						   QUBFAST_VAR_NOT_USED int ** L, QUBFAST_VAR_NOT_USED int ** V, QUBFAST_VAR_NOT_USED int ** P,
						   int *Ncns, double **Ain, double *Bin,
						   int a, int b, int c, int d)
{
  int Nk = CountK(Ns);
  int k_ix1 = k_ix_of(Ns, a, b);
  int k_ix2 = k_ix_of(Ns, c, d);
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;
  Ain[*Ncns][k_ix1] = 1.0;
  Ain[*Ncns][k_ix2] = - 1.0;
  Bin[*Ncns] = log(K0[a][b]) - log(K0[c][d]);
  ++*Ncns;
}

// ratio = (k1ab / k1cd)
// k1ab - ratio * k1cd = 0

extern "C" QUBFAST_API void AddScaleExpConstraint(int Ns, QUBFAST_VAR_NOT_USED double ** K0, double **K1, QUBFAST_VAR_NOT_USED double ** K2,
						  QUBFAST_VAR_NOT_USED int ** L, QUBFAST_VAR_NOT_USED int ** V, QUBFAST_VAR_NOT_USED int ** P,
						  int *Ncns, double **Ain, double *Bin,
						  int a, int b, int c, int d)
{
  int Nk = CountK(Ns);
  int k_ix1 = k_ix_of(Ns, a, b);
  int k_ix2 = k_ix_of(Ns, c, d);
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;
  Ain[*Ncns][k_ix1 + Nk/3] = 1.0;
  Ain[*Ncns][k_ix2 + Nk/3] = - K1[a][b] / K1[c][d];
  Bin[*Ncns] = 0.0;
  ++*Ncns;
}

extern "C" QUBFAST_API void AddScalePresConstraint(int Ns, QUBFAST_VAR_NOT_USED double ** K0, QUBFAST_VAR_NOT_USED double **K1, double ** K2,
						  QUBFAST_VAR_NOT_USED int ** L, QUBFAST_VAR_NOT_USED int ** V, QUBFAST_VAR_NOT_USED int ** P,
						  int *Ncns, double **Ain, double *Bin,
						  int a, int b, int c, int d)
{
  int Nk = CountK(Ns);
  int k_ix1 = k_ix_of(Ns, a, b);
  int k_ix2 = k_ix_of(Ns, c, d);
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;
  Ain[*Ncns][k_ix1 + 2*Nk/3] = 1.0;
  Ain[*Ncns][k_ix2 + 2*Nk/3] = - K2[a][b] / K2[c][d];
  Bin[*Ncns] = 0.0;
  ++*Ncns;
}

// nonzero if there's a problem, reported through report_cb
extern "C" QUBFAST_API int AddLoopBalConstraint(int Ns, QUBFAST_VAR_NOT_USED double ** K0, double **K1, double **K2, int **L, int **V, int **P,
							  int *Ncns, double **Ain, double *Bin,
							  int Nloop, int *loopstates,
							  callbk_reportfun report_cb, void *report_data)
{
  callbk_reportstream output(report_cb, report_data);
  set<int> ligs, volts, press;
  map<int,int> ligsF, ligsB, voltsPlus, voltsMinus, pressPlus, pressMinus;
  
  int Nk = CountK(Ns);
  if ( loopstates[Nloop-1] == loopstates[0] )
    --Nloop; // don't repeat first as last
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;

  for (int i=0; i<Nloop; ++i) {
    int a = loopstates[i];
    int b = loopstates[(i+1)%Nloop];
    int k_ix1 = k_ix_of(Ns, a, b); 
    int k_ix2 = k_ix_of(Ns, b, a);
    
    Ain[*Ncns][k_ix1] = 1.0;
    Ain[*Ncns][k_ix2] = - 1.0;
    
    if ( L[a][b] ) {
      ligs.insert(L[a][b]);
      ++ligsF[L[a][b]];
    }
    if ( L[b][a] ) {
      ligs.insert(L[b][a]);
      ++ligsB[L[b][a]];
    }
    if ( V[a][b] ) {
      volts.insert(V[a][b]);
      if ( K1[a][b] < 0 )
	++voltsMinus[V[a][b]];
      else
	++voltsPlus[V[a][b]];
    }
    if ( V[b][a] ) {
      volts.insert(V[b][a]);
      if ( K1[b][a] < 0 )
	++voltsPlus[V[b][a]];
      else
	++voltsMinus[V[b][a]];
    }
    if ( P[a][b] ) {
      press.insert(P[a][b]);
      if ( K2[a][b] < 0 )
	++pressMinus[P[a][b]];
      else
	++pressPlus[P[a][b]];
    }
    if ( P[b][a] ) {
      press.insert(P[b][a]);
      if ( K2[b][a] < 0 )
	++pressPlus[P[b][a]];
      else
	++pressMinus[P[b][a]];
    }
  }
  Bin[*Ncns] = 0.0;
  
  for (set<int>::iterator ligi = ligs.begin(); ligi != ligs.end(); ++ligi) {
    if ( ligsF[*ligi] != ligsB[*ligi] ) {
      output << "In loop";
      for (int i=0; i<Nloop; ++i)
	output << " " << (loopstates[i]+1);
      output << ": Ligand (signal " << *ligi
	     << ") must influence the same number of rates forward and back." << endl;
      return -1;
    }
  }
  for (set<int>::iterator voli = volts.begin(); voli != volts.end(); ++voli) {
    if ( (! voltsPlus[*voli]) || (! voltsMinus[*voli]) ) {
      output << "In loop";
      for (int i=0; i<Nloop; ++i)
	output << " " << (loopstates[i]+1);
      output << ": Voltage (signal " << *voli
	     << ") must influence at least two rates,"
	     << " and at least one must have opposite sign or direction." << endl;
      return -2;
    }
  }
  for (set<int>::iterator presi = press.begin(); presi != press.end(); ++presi) {
    if ( (! pressPlus[*presi]) || (! pressMinus[*presi]) ) {
      output << "In loop";
      for (int i=0; i<Nloop; ++i)
	output << " " << (loopstates[i]+1);
      output << ": Pressure (signal " << *presi
	     << ") must influence at least two rates,"
	     << " and at least one must have opposite sign or direction." << endl;
      return -2;
    }
  }
  
  ++*Ncns;
  
  if ( volts.size() ) {
    for (int j=0; j<Nk; ++j)
      Ain[*Ncns][j] = 0.0;
    for (int i=0; i<Nloop; ++i) {
      int a = loopstates[i];
      int b = loopstates[(i+1)%Nloop];
      int k_ix1 = k_ix_of(Ns, a, b); 
      int k_ix2 = k_ix_of(Ns, b, a);
      
      Ain[*Ncns][k_ix1 + Nk/3] = 1.0;
      Ain[*Ncns][k_ix2 + Nk/3] = - 1.0;
    }
    Bin[*Ncns] = 0.0;
    ++*Ncns;
  }

  if ( press.size() ) {
    for (int j=0; j<Nk; ++j)
      Ain[*Ncns][j] = 0.0;
    for (int i=0; i<Nloop; ++i) {
      int a = loopstates[i];
      int b = loopstates[(i+1)%Nloop];
      int k_ix1 = k_ix_of(Ns, a, b); 
      int k_ix2 = k_ix_of(Ns, b, a);
      
      Ain[*Ncns][k_ix1 + 2*Nk/3] = 1.0;
      Ain[*Ncns][k_ix2 + 2*Nk/3] = - 1.0;
    }
    Bin[*Ncns] = 0.0;
    ++*Ncns;
  }
  return 0;
}

extern "C" QUBFAST_API int AddLoopImbalConstraint(int Ns, double **K0, double **K1, double **K2, int **L, int **V, int **P,
							    int *Ncns, double **Ain, double *Bin,
							    int Nloop, int *loopstates,
							    callbk_reportfun report_cb, void *report_data)
{
  callbk_reportstream output(report_cb, report_data);
  set<int> ligs, volts, press;
  map<int,int> ligsF, ligsB, voltsPlus, voltsMinus, pressMinus, pressPlus;
  
  int Nk = CountK(Ns);
  if ( loopstates[Nloop-1] == loopstates[0] )
    --Nloop; // don't repeat first as last
  
  for (int j=0; j<Nk; ++j)
    Ain[*Ncns][j] = 0.0;

  double imbal = 0.0;
  for (int i=0; i<Nloop; ++i) {
    int a = loopstates[i];
    int b = loopstates[(i+1)%Nloop];
    int k_ix1 = k_ix_of(Ns, a, b); 
    int k_ix2 = k_ix_of(Ns, b, a);
    
    imbal += log(K0[a][b]) - log(K0[b][a]);
    
    Ain[*Ncns][k_ix1] = 1.0;
    Ain[*Ncns][k_ix2] = - 1.0;
    
    if ( L[a][b] ) {
      ligs.insert(L[a][b]);
      ++ligsF[L[a][b]];
    }
    if ( L[b][a] ) {
      ligs.insert(L[b][a]);
      ++ligsB[L[b][a]];
    }
    if ( V[a][b] ) {
      volts.insert(V[a][b]);
      if ( K1[a][b] < 0 )
	++voltsMinus[V[a][b]];
      else
	++voltsPlus[V[a][b]];
    }
    if ( V[b][a] ) {
      volts.insert(V[b][a]);
      if ( K1[b][a] < 0 )
	++voltsPlus[V[b][a]];
      else
	++voltsMinus[V[b][a]];
    }
    if ( P[a][b] ) {
      press.insert(P[a][b]);
      if ( K2[a][b] < 0 )
	++pressMinus[P[a][b]];
      else
	++pressPlus[P[a][b]];
    }
    if ( P[b][a] ) {
      press.insert(P[b][a]);
      if ( K2[b][a] < 0 )
	++pressPlus[P[b][a]];
      else
	++pressMinus[P[b][a]];
    }
  }
  Bin[*Ncns] = imbal;
  
  for (set<int>::iterator ligi = ligs.begin(); ligi != ligs.end(); ++ligi) {
    if ( ligsF[*ligi] != ligsB[*ligi] ) {
      output << "In loop";
      for (int i=0; i<Nloop; ++i)
	output << " " << (loopstates[i]+1);
      output << ": Ligand (signal " << *ligi
	     << ") must influence the same number of rates forward and back." << endl;
      return -1;
    }
  }
  for (set<int>::iterator voli = volts.begin(); voli != volts.end(); ++voli) {
    if ( (! voltsPlus[*voli]) || (! voltsMinus[*voli]) ) {
      output << "In loop";
      for (int i=0; i<Nloop; ++i)
	output << " " << (loopstates[i]+1);
      output << ": Voltage (signal " << *voli
	     << ") must influence at least two rates,"
	     << " and at least one must have opposite sign or direction." << endl;
      return -2;
    }
  }
  for (set<int>::iterator presi = press.begin(); presi != press.end(); ++presi) {
    if ( (! pressPlus[*presi]) || (! pressMinus[*presi]) ) {
      output << "In loop";
      for (int i=0; i<Nloop; ++i)
	output << " " << (loopstates[i]+1);
      output << ": Pressure (signal " << *presi
	     << ") must influence at least two rates,"
	     << " and at least one must have opposite sign or direction." << endl;
      return -2;
    }
  }
  
  ++*Ncns;
  
  if ( volts.size() ) {
    for (int j=0; j<Nk; ++j)
      Ain[*Ncns][j] = 0.0;
    for (int i=0; i<Nloop; ++i) {
      int a = loopstates[i];
      int b = loopstates[(i+1)%Nloop];
      int k_ix1 = k_ix_of(Ns, a, b); 
      int k_ix2 = k_ix_of(Ns, b, a);
      
      Ain[*Ncns][k_ix1 + Nk/3] = 1.0;
      Ain[*Ncns][k_ix2 + Nk/3] = - 1.0;
    }
    Bin[*Ncns] = 0.0;
    ++*Ncns;
  }

  if ( press.size() ) {
    for (int j=0; j<Nk; ++j)
      Ain[*Ncns][j] = 0.0;
    for (int i=0; i<Nloop; ++i) {
      int a = loopstates[i];
      int b = loopstates[(i+1)%Nloop];
      int k_ix1 = k_ix_of(Ns, a, b); 
      int k_ix2 = k_ix_of(Ns, b, a);
      
      Ain[*Ncns][k_ix1 + 2*Nk/3] = 1.0;
      Ain[*Ncns][k_ix2 + 2*Nk/3] = - 1.0;
    }
    Bin[*Ncns] = 0.0;
    ++*Ncns;
  }
  return 0;
}

/*

To derive the transformation we will use singular value decomposition (svd) which is really well described at http://www.uwlax.edu/faculty/will/svd/index.html

And derive some useful extras:

  • \(W^{-1}\), where \(0^{-1} = 0\)
  • \(A^{-1} = V W^{-1} U^T\)
  • Nsv = number of nonzero singular values

*/

void DeleteConstraint(int Nk, int *Ncns, double **Ain, double *Bin, int ic)
{
  for (int jc=ic+1; jc<*Ncns; ++jc) {
    memcpy(Ain[jc-1], Ain[jc], Nk*sizeof(double));
    Bin[jc-1] = Bin[jc];
  }
  --*Ncns;
}


/*
Now we have K with 2*Nstate*Nstate elements, and a constraint system in which most of which are fixed. We remove the fixed elements to speed up the constraint engine.

K[Kr_indices] = Kr

kR_indices: length Nk constraint_indices: length Ncns */

extern "C" QUBFAST_API void IdentifyFixedParams_p(int Nk, int Ncns, double *K, double *Ain, double *Bin, /*output: */ int *NkR, int *kR_indices, int *NcnsR, int *constraint_indices) { double **Ain_pp = new double*[Ncns]; for (int i=0; i extern "C" QUBFAST_API void IdentifyFixedParams(int Nk, int Ncns, QUBFAST_VAR_NOT_USED double * K, double **Ain, QUBFAST_VAR_NOT_USED double * Bin, /*output: */ int *NkR, int *kR_indices, int *NcnsR, int *constraint_indices) { // a fix constraint has exactly one nonzero A-coefficient std::vector fixed_k, fixed_iC; for (int c=0; c 1 ) break; iCoeff = i; } } if ( nCoeff == 1 ) { // can't eliminate it if other constraints are involved e.g. 2 scaled rates, one of them fixed bool other_constraints_on_k = false; for (int cc=0; cc // Kr: length NkR // AinR: NcnsR x Kr // BinR: length NcnsR extern "C" QUBFAST_API void EliminateFixedParams_p(int Nk, int Ncns, double *K, double *Ain, double *Bin, int NkR, int *kR_indices, int NcnsR, int *constraint_indices, double *Kr, double *AinR, double *BinR) { double **Ain_pp = new double*[Ncns]; for (int i=0; i extern "C" QUBFAST_API void EliminateFixedParams(QUBFAST_VAR_NOT_USED int Nk, QUBFAST_VAR_NOT_USED int Ncns, double *K, double **Ain, double *Bin, int NkR, int *kR_indices, int NcnsR, int *constraint_indices, double *Kr, double **AinR, double *BinR) { for (int i=0; i for (int c=0; c

extern "C" QUBFAST_API void Kr_to_K(double *Kr, int NkR, double *K, int *kR_indices) { for (int i=0; i

#define PRETTY_SMALL 1e-5

/*