Revision 30ab69a5d52df4a5bb576d33e109b840362c0e7b authored by Reza Mohammadi on 14 November 2018, 17:30:12 UTC, committed by cran-robot on 14 November 2018, 17:30:12 UTC
1 parent d69d48c
Raw File
bd_for_ts.cpp
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
//     Copyright (C) 2012-2018 Reza Mohammadi                                                      |
//                                                                                                 |
//     This file is part of BDgraph package.                                                       |
//                                                                                                 |
//     BDgraph 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; see <https://cran.r-project.org/web/licenses/GPL-3>.                   |
//                                                                                                 |
//     Authors contact information:                                                                |
//     Lang Liu:            liulang13@mails.tsinghua.edu.cn                                        |
//     Nicholas Foti:       nfoti@uw.edu                                                           |
//     Alex Tank:           atank18@gmail.com                                                      |
//     Reza Mohammadi:      a.mohammadi@uva.nl                                                     |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |

#include "MyLapack.h"
#include "matrix.h"

extern "C" {

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
// sampling from COMPLEX Wishart distribution
// Ls = t( chol( solve( Ds ) ) )
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
void rcwish_c( double Ls[], Rcomplex *K, int *b, int *p )
{
	int i2p, ip, i, j, dim = *p, bK = *b, n = bK + dim, p2 = 2 * dim, pxn = dim * n, p2xn = 2 * pxn, pxp = dim * dim;
	double alpha = 1.0, beta_0 = 0.0, malpha = -1.0;
	char transT  = 'T', transN = 'N', side = 'L', lower = 'L';

	double *joint = new double[ p2xn ];
	double *X     = new double[ pxn ];
	double *Y     = new double[ pxn ];
	vector<double> r_K( pxp );
	vector<double> i_K( pxp );
	//Rcomplex *csigma = new Rcomplex[ pxp ];
	//Rcomplex *Ind    = new Rcomplex[ pxp ];

	// - -  Sample values in Joint matrix - -
	GetRNGstate();
	for( j = 0; j < n; j++ )
		for( i = 0; i < p2; i++ )
			joint[ j * p2 + i ] = norm_rand();
	PutRNGstate();
	// - - - - - - - - - - - - - - - - - - 

	// dtrmm (SIDE, UPLO, TRANSA, DIAG, M, N, ALPHA, A, LDA, B, LDB)
	// C = Ls %*% joint   I used   joint = Ls %*% joint
	F77_NAME(dtrmm)( &side, &lower, &transN, &transN, &p2, &n, &alpha, Ls, &p2, &joint[0], &p2 );
	for( i = 0; i < n; i++ )
	{
		i2p = i * p2;
		ip = i * dim;
		memcpy( X + ip, &joint[ i2p ], sizeof(double) * dim );
		memcpy( Y + ip, &joint[ i2p + dim ], sizeof(double) * dim );
	}

	// The real part of K_start = X %*% t(X) + Y %*% t(Y)
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &X[0], &dim, &X[0], &dim, &beta_0, &r_K[0], &dim );
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &Y[0], &dim, &Y[0], &dim, &alpha , &r_K[0], &dim );

	// The imaginary part of K_start = Y %*% t(X) - X %*% t(Y)
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &Y[0], &dim, &X[0], &dim, &beta_0, &i_K[0], &dim );
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &malpha, &X[0], &dim, &Y[0], &dim, &alpha, &i_K[0], &dim );

	for( j = 0; j < pxp; j++ )
	{
		K[ j ].r = r_K[ j ];
		K[ j ].i = i_K[ j ];
	}

	//delete[] csigma;
	//delete[] Ind;
	delete[] joint;
	delete[] X;
	delete[] Y;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
// sampling from COMPLEX G-Wishart distribution
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
void rgcwish_c( int G[], double Ls[], Rcomplex *K, int *b, int *p )
{
	int i, j, k, s, ij, ji, i2p, ip, l, size_node_i, info, one = 1, dim = *p, p2 = 2*dim, pxp = dim * dim, bK = *b, n = bK + dim, pxn = dim * n, p2xn = 2 * pxn;
	double temp, threshold = 1e-8, done = 1.0, dmone = -1.0;
	double alpha = 1.0, malpha = -1.0, beta_0 = 0.0;
	char transT  = 'T', transN = 'N', side = 'L', upper = 'U', lower = 'L';
	// STEP 1: sampling from complex wishart distributions
	double *joint = new double[ p2xn ];
	double *X     = new double[ pxn ];
	double *Y     = new double[ pxn ];

	vector<double> r_sigma_start( pxp );
	vector<double> i_sigma_start( pxp );
	Rcomplex *csigma = new Rcomplex[ pxp ];
	Rcomplex *Ind    = new Rcomplex[ pxp ];

	// - -  Sample values in Joint matrix - -
	GetRNGstate();
	for( j = 0; j < n; j++ )
		for( i = 0; i < p2; i++ )
			joint[ j * p2 + i ] = norm_rand();
	PutRNGstate();
	// - - - - - - - - - - - - - - - - - - 

	// dtrmm (SIDE, UPLO, TRANSA, DIAG, M, N, ALPHA, A, LDA, B, LDB)
	// C = Ls %*% joint   I used   joint = Ls %*% joint
	F77_NAME(dtrmm)( &side, &lower, &transN, &transN, &p2, &n, &alpha, Ls, &p2, &joint[0], &p2 );
	for ( i = 0; i < n; i++ )
	{
		i2p = i * p2;
		ip  = i * dim;
		memcpy( X + ip, &joint[ i2p ], sizeof( double ) * dim );
		memcpy( Y + ip, &joint[ i2p + dim ], sizeof( double ) * dim );
	}
	
	// The real part of K_start = X %*% t(X) + Y %*% t(Y)
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &X[0], &dim, &X[0], &dim, &beta_0, &r_sigma_start[0], &dim );
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &Y[0], &dim, &Y[0], &dim, &alpha, &r_sigma_start[0], &dim );

	// The imaginary part of K_start = Y %*% t(X) - X %*% t(Y)
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &Y[0], &dim, &X[0], &dim, &beta_0, &i_sigma_start[0], &dim );
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &malpha, &X[0], &dim, &Y[0], &dim, &alpha, &i_sigma_start[0], &dim );

	for( j = 0; j < dim; j++ )
		for( k = 0; k < dim; k++ )
		{
			csigma[ j * dim + k ].r = r_sigma_start[ j * dim + k ];
			csigma[ j * dim + k ].i = i_sigma_start[ j * dim + k ];
		}

	for( j = 0; j < dim; j++ )
		for ( k = 0; k < dim; k++ )
		{
			Ind[ j * dim + k ].i = 0;
			Ind[ j * dim + k ].r = ( j == k );
		}

	F77_NAME(zpotrf)( &upper, &dim, csigma, &dim, &info );        // Remark: csigma will be changed
	zpotrs( &upper, &dim, &dim, csigma, &dim, Ind, &dim, &info ); // sigma = inv(K)

	for( j = 0; j < pxp; j++ )
	{
		r_sigma_start[ j ] = Ind[ j ].r;
		i_sigma_start[ j ] = Ind[ j ].i;
	}

	vector<double> r_sigma( r_sigma_start );
	vector<double> i_sigma( i_sigma_start );
	vector<double> r_beta_star( dim );
	vector<double> i_beta_star( dim );
	vector<double> r_sigma_i( dim );
	vector<double> i_sigma_i( dim );

	// For dynamic memory used
	vector<double> r_sigma_start_N_i( dim );
	vector<double> r_sigma_start_N_i_2( dim );
	vector<double> i_sigma_start_N_i( dim );
	vector<int> N_i( dim );
	vector<double> r_sigma_N_i( dim );
	vector<double> r_sigma_N_i_2( dim );
	vector<double> i_sigma_N_i( dim );
	vector<double> Inv_R( pxp );
	vector<double> IR( pxp );

	double max_diff = 1.0, r_diff, i_diff;
	while( max_diff > threshold )
	{
		max_diff = 0.0; // The maximum difference between two adjacent sigma

		for( i = 0; i < dim; i++ )
		{
			ip = i * dim;
			// Count size of node
			size_node_i = 0;
			for( j = 0; j < dim; j++ ) size_node_i += G[ j * dim + i ];

			if( size_node_i > 0 )
			{
				l = 0;
				for( j = 0; j < dim; j++ )
				{
					ji = ip + j;
					if( G[ ji ] )
					{
						r_sigma_start_N_i[ l ]   = r_sigma_start[ ji ];
						r_sigma_start_N_i_2[ l ] = r_sigma_start[ ji ];
						i_sigma_start_N_i[ l ]   = i_sigma_start[ ji ];
						N_i[ l++ ] = j;
					}else{
						r_beta_star[ j ] = 0.0;
						i_beta_star[ j ] = 0.0;
					}
				}
				// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

				sub_matrix( &r_sigma[0], &r_sigma_N_i[0], &N_i[0], &size_node_i, &dim );
				sub_matrix( &i_sigma[0], &i_sigma_N_i[0], &N_i[0], &size_node_i, &dim );

				// Inv_R = solve(r_sigma_N_i)
				for( s = 0; s < size_node_i*size_node_i; s++ )
					r_sigma_N_i_2[ s ] = r_sigma_N_i[ s ];
				
				inverse( &r_sigma_N_i_2[0], &Inv_R[0], &size_node_i );
				
				// IR = i_sigma_N_i %*% Inv_R
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &size_node_i, &size_node_i, &alpha, &i_sigma_N_i[0], &size_node_i, &Inv_R[0], &size_node_i, &beta_0, &IR[0], &size_node_i);
				// A = IR %*% i_sigma_N_i + r_sigma_N_i = r_sigma_N_i
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &size_node_i, &size_node_i, &alpha, &IR[0], &size_node_i, &i_sigma_N_i[0], &size_node_i, &alpha, &r_sigma_N_i[0], &size_node_i);
				// B = IR %*% i_sigma_start_N_i + r_sigma_start_N_i	= r_sigma_start_N_i
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &one, &size_node_i, &alpha, &IR[0], &size_node_i, &i_sigma_start_N_i[0], &size_node_i, &alpha, &r_sigma_start_N_i[0], &size_node_i);
				// C = IR %*% r_sigma_start_N_i_2 - i_sigma_start_N_i = i_sigma_start_N_i
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &one, &size_node_i, &alpha, &IR[0], &size_node_i, &r_sigma_start_N_i_2[0], &size_node_i, &dmone, &i_sigma_start_N_i[0], &size_node_i);

				// r_sigma_start_N_i = solve(A) %*% B; i_sigma_start_N_i = solve(A) %*% C
				for( s = 0; s < size_node_i*size_node_i; s++ )
					r_sigma_N_i_2[ s ] = r_sigma_N_i[ s ];
				
				F77_NAME(dposv)( &upper, &size_node_i, &one, &r_sigma_N_i[0], &size_node_i, &r_sigma_start_N_i[0], &size_node_i, &info );
				F77_NAME(dposv)( &upper, &size_node_i, &one, &r_sigma_N_i_2[0], &size_node_i, &i_sigma_start_N_i[0], &size_node_i, &info );

				for( j = 0; j < size_node_i; j++ )
				{
					r_beta_star[ N_i[ j ] ] = r_sigma_start_N_i[ j ];
					i_beta_star[ N_i[ j ] ] = i_sigma_start_N_i[ j ];
				}

				// r_sigma_i = r_sigma %*% r_beta_star + i_sigma %*% i_beta_star
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, &i_sigma[0], &dim, &i_beta_star[0], &dim, &beta_0, &r_sigma_i[0], &dim );
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, &r_sigma[0], &dim, &r_beta_star[0], &dim, &done, &r_sigma_i[0], &dim );

				// i_sigma_i = i_sigma %*% r_beta_star - r_sigma %*% i_beta_star
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, &r_sigma[0], &dim, &i_beta_star[0], &dim, &beta_0, &i_sigma_i[0], &dim );
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, &i_sigma[0], &dim, &r_beta_star[0], &dim, &dmone, &i_sigma_i[0], &dim );

				// Update the first i elements
				for( j = 0; j < i; j++ )
				{
					ij = j * dim + i;
					ji = i * dim + j;
					r_diff = r_sigma[ ij ] - r_sigma_i[ j ];
					i_diff = i_sigma[ ij ] + i_sigma_i[ j ];

					temp = sqrt( r_diff * r_diff + i_diff * i_diff );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ] = r_sigma_i[ j ];
					i_sigma[ ij ] = - i_sigma_i[ j ];
					r_sigma[ ji ] = r_sigma_i[ j ];
					i_sigma[ ji ] = i_sigma_i[ j ];
				}

				for( j = i + 1; j < dim; j++ )
				{
					ij     = j * dim + i;
					ji     = i * dim + j;
					r_diff = r_sigma[ ij ] - r_sigma_i[ j ];
					i_diff = i_sigma[ ij ] + i_sigma_i[ j ];

					temp = sqrt( r_diff * r_diff + i_diff * i_diff );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ] = r_sigma_i[ j ];
					i_sigma[ ij ] = - i_sigma_i[ j ];
					r_sigma[ ji ] = r_sigma_i[ j ];
					i_sigma[ ji ] = i_sigma_i[ j ];
				}
			}else{
				for( j = 0; j < i; j++ )
				{
					ij   = j * dim + i;
					temp = sqrt( r_sigma[ ij ] * r_sigma[ ij ] + i_sigma[ ij ] * i_sigma[ ij ] );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ]     = 0.0;
					i_sigma[ ij ]     = 0.0;
					r_sigma[ ip + j ] = 0.0;
					i_sigma[ ip + j ] = 0.0;
				}

				for( j = i + 1; j < dim; j++ )
				{
					ij   = j * dim + i;
					temp = sqrt( r_sigma[ ij ] * r_sigma[ ij ] + i_sigma[ ij ] * i_sigma[ ij ] );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ]     = 0.0;
					i_sigma[ ij ]     = 0.0;
					r_sigma[ ip + j ] = 0.0;
					i_sigma[ ip + j ] = 0.0;
				}
			}
		}
	}

	memcpy( &r_sigma_start[0], &r_sigma[0], sizeof( double ) * pxp );
	memcpy( &i_sigma_start[0], &i_sigma[0], sizeof( double ) * pxp );

	// K = solve(sigma)
	for( j = 0; j < pxp; j++ )
	{
		csigma[ j ].r = r_sigma_start[ j ];
		csigma[ j ].i = i_sigma_start[ j ];
	}

	for( j = 0; j < dim; j++ )
		for ( k = 0; k < dim; k++ )
		{
			K[ j * dim + k ].i = 0;
			K[ j * dim + k ].r = ( j == k );
		}

	F77_NAME(zpotrf)(&upper, &dim, csigma, &dim, &info);
	zpotrs(&upper, &dim, &dim, csigma, &dim, K, &dim, &info );

	delete[] csigma;
	delete[] Ind;
	delete[] joint;
	delete[] X;
	delete[] Y;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
// rgwish ONLY for inside of MCMC algorithm
// Ls is the cholesky of the covariance matrix of (X;Y) -- sigma = Ls %*% Ls*
// Input (value matters) -- G, size_node, Ls, b_star, p
// Output -- K, r_sigma, i_sigma
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
void rgcwish_sigma( int G[], int size_node[], double Ls[], Rcomplex *K, double r_sigma[], double i_sigma[], Rcomplex *csigma, Rcomplex *Ind, int *b_star, int *p,
					double r_sigma_start[], double i_sigma_start[], double X[], double Y[], double r_beta_star[], double i_beta_star[], double joint[], double r_sigma_i[],
					double i_sigma_i[], vector<double> &r_sigma_start_N_i, vector<double> &r_sigma_start_N_i_2, vector<double> &i_sigma_start_N_i, vector<double> &r_sigma_N_i, vector<double> &r_sigma_N_i_2,
					vector<double> &i_sigma_N_i, vector<int> &N_i, vector<double> &IR, vector<double> &Inv_R, int *iter )
{
	int i, j, k, s, ij, ji, i2p, ip, l, size_node_i, info, one = 1, dim = *p, p2 = 2*dim, pxp = dim * dim, bK = *b_star, n = bK + dim;
	double temp, threshold = 1e-8, done = 1.0, dmone = -1.0;
	double alpha = 1.0, beta_0 = 0.0, malpha = -1.0;
	double max_diff = 1.0, r_diff, i_diff;
	char transT  = 'T', transN = 'N', side = 'L', upper = 'U', lower = 'L';

	// - - STEP 1: sampling from complex wishart distributions - - - - - - - - - - - - - - - - - - |
	// - -  Sample values in Joint matrix - -
	GetRNGstate();
	for( j = 0; j < n; j++ )
		for( i = 0; i < p2; i++ )
			joint[ j * p2 + i ] = norm_rand();
	PutRNGstate();
	// - - - - - - - - - - - - - - - - - - 

	// dtrmm (SIDE, UPLO, TRANSA, DIAG, M, N, ALPHA, A, LDA, B, LDB)
	// C = Ls %*% joint   I used   joint = Ls %*% joint
	F77_NAME(dtrmm)( &side, &lower, &transN, &transN, &p2, &n, &alpha, Ls, &p2, &joint[0], &p2 );
	for( i = 0; i < n; i++ )
	{
		i2p = i * p2;
		ip  = i * dim;
		memcpy( X + ip, &joint[ i2p ],       sizeof(double) * dim );
		memcpy( Y + ip, &joint[ i2p + dim ], sizeof(double) * dim );
	}

	// The real part of K_start = X %*% t(X) + Y %*% t(Y)
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &X[0], &dim, &X[0], &dim, &beta_0, &r_sigma_start[0], &dim );
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &Y[0], &dim, &Y[0], &dim, &alpha, &r_sigma_start[0], &dim );

	// The imaginary part of K_start = Y %*% t(X) - X %*% t(Y)
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &alpha, &Y[0], &dim, &X[0], &dim, &beta_0, &i_sigma_start[0], &dim );
	F77_NAME(dgemm)( &transN, &transT, &dim, &dim, &n, &malpha, &X[0], &dim, &Y[0], &dim, &alpha, &i_sigma_start[0], &dim );

	for( j = 0; j < dim; j++ )
		for( k = 0; k < dim; k++ )
		{
			csigma[ j * dim + k ].r = r_sigma_start[ j * dim + k ];
			csigma[ j * dim + k ].i = i_sigma_start[ j * dim + k ];
		}

	for( j = 0; j < dim; j++ )
		for( k = 0; k < dim; k++ )
		{
			Ind[ j * dim + k ].i = 0;
			Ind[ j * dim + k ].r = ( j == k );
		}

	F77_NAME(zpotrf)(&upper, &dim, csigma, &dim, &info); // Remark: csigma will be changed
	zpotrs(&upper, &dim, &dim, csigma, &dim, Ind, &dim, &info ); //sigma = inv(K)

	for( j = 0; j < pxp; j++ )
	{
		r_sigma_start[ j ] = Ind[ j ].r;
		i_sigma_start[ j ] = Ind[ j ].i;
	}

	memcpy( r_sigma, &r_sigma_start[0], sizeof( double ) * pxp );
	memcpy( i_sigma, &i_sigma_start[0], sizeof( double ) * pxp );

	//vector<double> max(1000);

	while( max_diff > threshold && *iter < 1000 )
	{
		*iter = *iter + 1;
	  //Rprintf("iteration: %d", *iter);
		max_diff = 0.0; // The maximum difference between two adjacent sigma

		for( i = 0; i < dim; i++ )
		{
			ip = i * dim;

			size_node_i = size_node[ i ];
			if( size_node_i > 0 )
			{
				l = 0;
				for( j = 0; j < dim; j++ )
				{
					ji = ip + j;
					if( G[ ji ] )
					{
						r_sigma_start_N_i[ l ]   = r_sigma_start[ ji ];
						r_sigma_start_N_i_2[ l ] = r_sigma_start[ ji ];
						i_sigma_start_N_i[ l ]   = i_sigma_start[ ji ];
						N_i[ l++ ] = j;
					}else{
						r_beta_star[ j ] = 0.0;
						i_beta_star[ j ] = 0.0;
					}
				}
				// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |

				sub_matrix( r_sigma, &r_sigma_N_i[0], &N_i[0], &size_node_i, &dim );
				sub_matrix( i_sigma, &i_sigma_N_i[0], &N_i[0], &size_node_i, &dim );

				// Inv_R = solve(r_sigma_N_i)
				for( s = 0; s < size_node_i * size_node_i; s++ )
					r_sigma_N_i_2[ s ] = r_sigma_N_i[ s ];

				inverse( &r_sigma_N_i_2[0], &Inv_R[0], &size_node_i );
				// IR = i_sigma_N_i %*% Inv_R
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &size_node_i, &size_node_i, &alpha, &i_sigma_N_i[0], &size_node_i, &Inv_R[0], &size_node_i, &beta_0, &IR[0], &size_node_i);
				// A = IR %*% i_sigma_N_i + r_sigma_N_i = r_sigma_N_i
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &size_node_i, &size_node_i, &alpha, &IR[0], &size_node_i, &i_sigma_N_i[0], &size_node_i, &alpha, &r_sigma_N_i[0], &size_node_i);
				// B = IR %*% i_sigma_start_N_i + r_sigma_start_N_i	= r_sigma_start_N_i
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &one, &size_node_i, &alpha, &IR[0], &size_node_i, &i_sigma_start_N_i[0], &size_node_i, &alpha, &r_sigma_start_N_i[0], &size_node_i);
				// C = IR %*% r_sigma_start_N_i_2 - i_sigma_start_N_i = i_sigma_start_N_i
				F77_NAME(dgemm)( &transN, &transN, &size_node_i, &one, &size_node_i, &alpha, &IR[0], &size_node_i, &r_sigma_start_N_i_2[0], &size_node_i, &dmone, &i_sigma_start_N_i[0], &size_node_i);

				// r_sigma_start_N_i = solve(A) %*% B; i_sigma_start_N_i = solve(A) %*% C
				for( s = 0; s < size_node_i*size_node_i; s++ )
				  r_sigma_N_i_2[ s ] = r_sigma_N_i[ s ];

				F77_NAME(dposv)( &upper, &size_node_i, &one, &r_sigma_N_i[0], &size_node_i, &r_sigma_start_N_i[0], &size_node_i, &info );
				F77_NAME(dposv)( &upper, &size_node_i, &one, &r_sigma_N_i_2[0], &size_node_i, &i_sigma_start_N_i[0], &size_node_i, &info );

				for( j = 0; j < size_node_i; j++ )
				{
					r_beta_star[ N_i[ j ] ] = r_sigma_start_N_i[ j ];
					i_beta_star[ N_i[ j ] ] = i_sigma_start_N_i[ j ];
				}

				// r_sigma_i = r_sigma %*% r_beta_star + i_sigma %*% i_beta_star
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, i_sigma, &dim, &i_beta_star[0], &dim, &beta_0, &r_sigma_i[0], &dim );
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, r_sigma, &dim, &r_beta_star[0], &dim, &done, &r_sigma_i[0], &dim );

				// i_sigma_i = i_sigma %*% r_beta_star - r_sigma %*% i_beta_star
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, r_sigma, &dim, &i_beta_star[0], &dim, &beta_0, &i_sigma_i[0], &dim );
				F77_NAME(dgemm)( &transN, &transN, &dim, &one, &dim, &alpha, i_sigma, &dim, &r_beta_star[0], &dim, &dmone, &i_sigma_i[0], &dim );

				// Update the first i elements of sigma[-i,i]
				memcpy( r_sigma + ip, r_sigma_i,  sizeof( double ) * i );
				memcpy( i_sigma + ip, i_sigma_i,  sizeof( double ) * i );

				// Update the first i elements of sigma[i,-i]
				for( j = 0; j < i; j++ )
				{
					ij     = j * dim + i;
					r_diff = r_sigma[ ij ] - r_sigma_i[ j ];
					i_diff = i_sigma[ ij ] + i_sigma_i[ j ];

					temp = sqrt( r_diff * r_diff + i_diff * i_diff );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ] = r_sigma_i[ j ];
					i_sigma[ ij ] = - i_sigma_i[ j ];
				}

				memcpy( r_sigma + ip + i + 1, r_sigma_i + i + 1, sizeof( double ) * ( dim - i - 1 ) );
				memcpy( i_sigma + ip + i + 1, i_sigma_i + i + 1, sizeof( double ) * ( dim - i - 1 ) );

				for( j = i + 1; j < dim; j++ )
				{
					ij   = j * dim + i;
					r_diff = r_sigma[ ij ] - r_sigma_i[ j ];
					i_diff = i_sigma[ ij ] + i_sigma_i[ j ];

					temp = sqrt( r_diff * r_diff + i_diff * i_diff );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ] = r_sigma_i[ j ];
					i_sigma[ ij ] = - i_sigma_i[ j ];
				}
			}else{
				for( j = 0; j < i; j++ )
				{
					ij   = j * dim + i;
					temp = sqrt( r_sigma[ ij ] * r_sigma[ ij ] + i_sigma[ ij ] * i_sigma[ ij ] );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ]     = 0.0;
					i_sigma[ ij ]     = 0.0;
					r_sigma[ ip + j ] = 0.0;
					i_sigma[ ip + j ] = 0.0;
				}

				for( j = i + 1; j < dim; j++ )
				{
					ij = j * dim + i;
					temp = sqrt( r_sigma[ ij ] * r_sigma[ ij ] + i_sigma[ ij ] * i_sigma[ ij ] );
					if( temp > max_diff ) max_diff = temp;

					r_sigma[ ij ]     = 0.0;
					i_sigma[ ij ]     = 0.0;
					r_sigma[ ip + j ] = 0.0;
					i_sigma[ ip + j ] = 0.0;
				}
			}
		}
		//max[*iter - 1] = max_diff;
	}

	// if ( *iter == 1000 )
	// {
	// 	Rprintf( "max_diffs are: " );
	// 	for (k = 980; k < 1000; k++)
	// 	{
	// 		Rprintf("%f, ", max[ k ]);
	// 	}
	// 	Rprintf( "\n" );
	// }

	memcpy( &r_sigma_start[0], r_sigma, sizeof( double ) * pxp );
	memcpy( &i_sigma_start[0], i_sigma, sizeof( double ) * pxp );

	// K = solve(sigma)
	for( j = 0; j < pxp; j++ )
	{
		csigma[ j ].r = r_sigma_start[ j ];
		csigma[ j ].i = i_sigma_start[ j ];
	}

	for( j = 0; j < dim; j++ )
		for( k = 0; k < dim; k++ )
		{
			K[ j * dim + k ].i = 0;
			K[ j * dim + k ].r = ( j == k );
		}

	F77_NAME(zpotrf)( &upper, &dim, csigma, &dim, &info );
	zpotrs( &upper, &dim, &dim, csigma, &dim, K, &dim, &info );
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
/*
 * birth-death MCMC for time series with Gaussian Graphical models
 * for D = I_p
 * it is for Bayesian model averaging
*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
void bdmcmc_for_multi_dim( int *iter, int *burnin, int G[], double g_prior[], double Ls[], double r_K[], double i_K[], int *p,
				int *freq, double r_sigma[], double i_sigma[], double r_K_hat[], double i_K_hat[], double p_links[],
				int *b, int *b_star, double r_Ds[], double i_Ds[], int *print )
{
	int print_c = *print, iteration = *iter, burn_in = *burnin, T = *freq;
	int index_selected_edge, selected_edge_i, selected_edge_j, selected_edge_ij;
	int ip, i, j, ij, t, k, counter = 0, dim = *p, pxp = dim * dim;
	int pxpxT = pxp * T, txpxp, n = dim, max_b_star = 0;
	int qp = dim * ( dim - 1 ) / 2;
	long double sum_rates, max_log, max_rate, sum_weights = 0.0, weight_C;
	long double max_numeric_limits_ld = std::numeric_limits<long double>::max() / 10000;


	for( i = 0; i < T; i++ )
		if( b_star[ i ] > max_b_star )
			max_b_star = b_star[ i ];

	n = n + max_b_star;
	// for a matrix A, A[i,j] -> A[(j-1)*p+(i-1)]
	vector<long double> p_links_Cpp( pxp, 0.0 );
	vector<long double> r_K_hat_Cpp( pxpxT, 0.0 );
	vector<long double> i_K_hat_Cpp( pxpxT, 0.0 );

	// - - allocation for rgcwish_sigma
	Rcomplex *csigma = new Rcomplex[ pxp ];
	Rcomplex *Ind    = new Rcomplex[ pxp ];
	Rcomplex *K      = new Rcomplex[ pxp ];

	vector<double> X( dim * n );
	vector<double> Y( dim * n );
	vector<double> joint( 2 * dim * n );
	vector<double> IR( pxp );
	vector<double> Inv_R( pxp );
	vector<double> r_sigma_start( pxp );
	vector<double> i_sigma_start( pxp );
	vector<double> r_beta_star( dim );
	vector<double> i_beta_star( dim );
	vector<double> r_sigma_i( dim );
	vector<double> i_sigma_i( dim );
	vector<double> r_sigma_start_N_i( dim );     // For dynamic memory used
	vector<double> r_sigma_start_N_i_2( dim );   // For dynamic memory used
	vector<double> i_sigma_start_N_i( dim );     // For dynamic memory used
	vector<double> r_sigma_N_i( pxp );           // For dynamic memory used
	vector<double> r_sigma_N_i_2( pxp );         // For dynamic memory used
	vector<double> i_sigma_N_i( pxp );           // For dynamic memory used
	vector<int> N_i( dim );                      // For dynamic memory used
	// - - - - - - - - - - - - - - 

	// Counting size of nodes
	vector<int> size_node( dim, 0 );      // degrees of vertex
	for( i = 0; i < dim; i++ )
	{
		ip = i * dim;
		for( j = 0; j < dim; j++ ) size_node[ i ] += G[ ip + j ];
	}

	// For finding the index of rates
	//vector<double> log_rates( qp );
	vector<long double> rates( qp );          // the rates of all edges
	vector<long double> log_rates( qp );      // log(rates)
	vector<long double> sub_log_rates( qp );  // log(rates) - max_log
	vector<int> index_row( qp );              // 0,0,1,0,1,2,...
	vector<int> index_col( qp );              // 1,2,2,3,3,3,...
	counter = 0;
	for( j = 1; j < dim; j++ )
		for( i = 0; i < j; i++ )
		{
		    ij = j * dim + i;
		    
		    if( ( g_prior[ ij ] != 0.0 ) or ( g_prior[ ij ] != 1.0 ) )
		    {
		        index_row[ counter ] = i;
		        index_col[ counter ] = j;
		        counter++;
		    }
		}
	int sub_qp = counter;

	vector<double> log_ratio_g_prior( pxp );    // log( p(G) / p(G^-e) )
	for( j = 1; j < dim; j++ )
		for( i = 0; i < j; i++ )
		{
			ij = j * dim + i;
			log_ratio_g_prior[ ij ] = log( static_cast<double>( g_prior[ ij ] / ( 1 - g_prior[ ij ] ) ) );
		}

	GetRNGstate();
	// - - main loop for birth-death MCMC sampling algorithm - - - - - - - - - - - - - - - - - - - |
	bool pri = TRUE;
	for( int i_mcmc = 0; i_mcmc < iteration; i_mcmc++ )
	{
		if( ( i_mcmc + 1 ) % print_c == 0 ) Rprintf( "\n Iteration  %d                 \n", i_mcmc + 1 );

		// - - STEP 1: calculating birth and death rates - - - - - - - - - - - - - - - - - - - - - |
		// Initialize log_rates for each iteration
		for( t = 0; t < qp; t++ )
			log_rates[ t ] = 0.0;

		// if( ( i_mcmc + 1 ) % 100 == 0 ) Rprintf( " Parallel starts  \n");
		for( t = 0; t < T; t++ ) // The loop for the frequencies
		{
			txpxp   = t*pxp;
			rates_cbdmcmc_parallel( &log_rates[0], &log_ratio_g_prior[0], G, &index_row[0], &index_col[0], &sub_qp, &r_Ds[txpxp], &i_Ds[txpxp], &r_sigma[txpxp], &i_sigma[txpxp], &r_K[txpxp], &i_K[txpxp], &b[ t ], &dim );
		}// end of frequency loop
		//if( ( i_mcmc + 1 ) % 100 == 0 ) Rprintf( " Parallel ends  \n");

		//Selecting an edge based on birth and death rates
		max_log = -max_numeric_limits_ld; // Initialize max_log
		for( t = 0; t < qp; t++ ) // Find the maximum log rate
		{
			if ( log_rates[ t ] > max_log )
				max_log = log_rates[ t ];
		}
		
		for( t = 0; t < qp; t++ ) // Substract max_log from each log_rate
		{
		  	sub_log_rates[ t ] = log_rates[ t ] - max_log;
		  	rates[ t ] = exp( sub_log_rates[ t ] );
		  // Debug
		  // 	if( i_mcmc >= 0 )
		  // 	{
				// if (rates[ t ] > 1e-100){
				// 	int trow = index_row[ t ];
				// 	int tcol = index_col[ t ];
				// 	Rprintf( "rates %d: %Le; p_link: %d, ", t, rates[ t ], G[tcol*dim + trow]);
				// }
		  // 	}
		}
		
		//debug if( i_mcmc >= 0 ) Rprintf( "\n" );
		max_rate = exp( max_log );
		if ( !R_FINITE( max_rate ) ) // If max_rate is too large
			max_rate = max_numeric_limits_ld;

		//select_edge2( &rates[0], &index_selected_edge, &sum_rates, &sub_qp );
		select_edge_ts( &rates[0], &index_selected_edge, &sum_rates, &sub_qp );
		selected_edge_i = index_row[ index_selected_edge ];
		selected_edge_j = index_col[ index_selected_edge ];
		sum_rates *= max_rate; // multiply back the max_rate
		//debug Rprintf( " sum_rates  %Le                 \n", sum_rates );

		// If sum_rates = 0, we consider the graph in this state is the true graph
		if( sum_rates == 0 )
		{
			for( t = 0; t < pxpxT; t++ )
			{
				r_K_hat[ t ] = r_K[ t ];
				i_K_hat[ t ] = i_K[ t ];
			}

			for( i = 0; i < pxp; i++ )
				if( G[ i ] ) p_links[ i ] = 1.0;

			//debug Rprintf("sum_rates = 0 \n");

			delete[] csigma;
			delete[] Ind;
			delete[] K;

			return;
		}
		//long double thres = 1e-100; // If the sum_rates is smaller than a threshold, start to save results
		//if ( sum_rates < thres && i_mcmc < burn_in)
			//i_mcmc = burn_in;

		// - - saving result - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
		if( i_mcmc >= burn_in )
		{
			weight_C = 1.0 / sum_rates;
			if ( !R_FINITE( weight_C ) || max_rate > max_numeric_limits_ld )
				weight_C = max_numeric_limits_ld / 100;

			// K_hat_Cpp[ i ] += K[ i ] * weight_C;
			// F77_NAME(daxpy)( &pxpxT, &weight_C, &r_K[0], &one, &r_K_hat_Cpp[0], &one );
			// F77_NAME(daxpy)( &pxpxT, &weight_C, &i_K[0], &one, &i_K_hat_Cpp[0], &one );

			for( t = 0; t < pxpxT; t++ )
			{
			 	r_K_hat_Cpp[ t ] += r_K[ t ] * weight_C;
			 	i_K_hat_Cpp[ t ] += i_K[ t ] * weight_C;
			}

			//#pragma omp parallel for
			for ( i = 0; i < pxp ; i++ )
				if( G[ i ] ) p_links_Cpp[ i ] += weight_C;

			//debug Rprintf( "weight_C: %Le \n", weight_C);
			sum_weights += weight_C;
		} // - - End of saving result - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|

		// Updating G (graph) based on selected edge
		selected_edge_ij      = selected_edge_j * dim + selected_edge_i;
		G[ selected_edge_ij ] = 1 - G[ selected_edge_ij ];
		G[ selected_edge_i * dim + selected_edge_j ] = G[ selected_edge_ij ];

		if( G[ selected_edge_ij ] )
		{
			++size_node[ selected_edge_i ];
			++size_node[ selected_edge_j ];
		}else{
			--size_node[ selected_edge_i ];
			--size_node[ selected_edge_j ];
		}

		// - - STEP 2: Sampling from G-Wishart for new graph - - - - - - - - - - - - - - - - - - - |
		int itera = 0;
		for( t = 0; t < T; t++ )
		{
			txpxp = t*pxp;
			itera = 0;

			rgcwish_sigma(G, &size_node[0], &Ls[4*txpxp], K, &r_sigma[txpxp],
			&i_sigma[txpxp], csigma, Ind, &b_star[ t ], &dim,
			&r_sigma_start[0], &i_sigma_start[0], &X[0], &Y[0],
			&r_beta_star[0], &i_beta_star[0], &joint[0], &r_sigma_i[0],
			&i_sigma_i[0], r_sigma_start_N_i, r_sigma_start_N_i_2,
			i_sigma_start_N_i, r_sigma_N_i, r_sigma_N_i_2, i_sigma_N_i, N_i, IR, Inv_R, &itera);

			for( k = 0; k < pxp; k++ )
			{
				r_K[ k + txpxp ] = K[ k ].r;
				i_K[ k + txpxp ] = K[ k ].i;
			}

			if( itera >= 1000 && pri )
			{
				Rprintf( "Sampling not converge at frequency %d \n", t);
				pri = FALSE;
			}
		}
	} // - - End of MCMC sampling algorithm - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
	PutRNGstate();

	//#pragma omp parallel for
	for( i = 0; i < pxpxT; i++ )
	{
		r_K_hat[ i ] = r_K_hat_Cpp[ i ] / sum_weights;
		i_K_hat[ i ] = i_K_hat_Cpp[ i ] / sum_weights;
	}

	for( i = 0; i < pxp; i++ )
		p_links[ i ] = p_links_Cpp[ i ] / sum_weights;

	delete[] csigma;
	delete[] Ind;
	delete[] K;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
/*
 * birth-death MCMC for time series with Gaussian Graphical models
 * for case D = I_p
 * it is for maximum a posterior probability estimation (MAP)
*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
void bdmcmc_map_for_multi_dim( int *iter, int *burnin, int G[], double Ls[], double r_K[], double i_K[], int *p,
			   int *freq, double r_sigma[], double i_sigma[], int all_graphs[], double all_weights[], double r_K_hat[],
			   double i_K_hat[], char *sample_graphs[], double graph_weights[], int *size_sample_g, int *exit,
			   int *b, int *b_star, double r_Ds[], double i_Ds[] )
{
	int iteration = *iter, burn_in = *burnin, b1 = 0, T = *freq;
	int counterallG = 0;
	string string_g;
	vector<string> sample_graphs_C( iteration - burn_in );

	bool this_one;

	int index_selected_edge, selected_edge_i, selected_edge_j, selected_edge_ij, size_sample_graph = *size_sample_g;
	int row, col, rowCol, i, j, t, k, ij, jj, counter, one = 1, two = 2;
	int dim = *p, pxp = dim * dim, p1 = dim - 1, p1xp1 = p1 * p1, p2 = dim - 2, p2xp2 = p2 * p2, p2x2 = p2 * 2;
	int pxpxT = pxp * T, txpxp, n = dim, max_b_star = 0;
	bool useful;

	double r_Dsjj, r_Dsij, i_Dsij, sum_weights = 0.0, r_sum_diag, r_K022, i_K022, r_a11, r_sigmaj11, i_sigmaj11;
	double mod_Dsjj, mod_a11, coef, r_temp, nu_star, sum_rates, G_prior, common_factor = 1.0;
	double alpha = 1.0, beta_0 = 0.0, dmone = -1.0;
	char transT = 'T', transN = 'N';

	for ( i = 0; i < T; i++ )
	{
		b1 = b1 + b[ i ];
		if (b_star[ i ] > max_b_star)
			max_b_star = b_star[ i ];
	}
	n  = n + max_b_star;
	b1 = b1 / T;

	int qp = dim * ( dim - 1 ) / 2;
	vector<char> char_g( qp );              // char string_g[pp];


	// Counting size of nodes
	int ip;
	vector<int> size_node( dim, 0 );      // degrees of vertex
	for( i = 0; i < dim; i++ )
	{
		ip = i * dim;
		for( j = 0; j < dim; j++ ) size_node[ i ] += G[ ip + j ];
	}

	double log_rate;
	// For finding the index of rates
	//vector<double> log_rates( qp );
	vector<double> rates( qp );      // the rates of all edges
	vector<int> index_rates_row( qp );    // 0,0,1,0,1,2,...
	vector<int> index_rates_col( qp );    // 1,2,2,3,3,3,...
	vector<bool> is_infinite( qp, 0 );
	counter = 0;
	for( j = 1; j < dim; j++ )
		for( i = 0; i < j; i++ )
		{
			index_rates_row[ counter ] = i;
			index_rates_col[ counter ] = j;
			counter++;
		}

	vector<double> r_K121( 4 );
	vector<double> i_K121( 4 );
	vector<double> r_Kj12( p1 );              // K[j, -j]
	vector<double> i_Kj12( p1 );
	vector<double> r_sigmaj12( p1 );          // sigma[-j, j]
	vector<double> i_sigmaj12( p1 );
	vector<double> r_sigmaj22( p1xp1 );       // sigma[-j, -j]
	vector<double> i_sigmaj22( p1xp1 );
	vector<double> r_Kj22_inv( p1xp1 );
	vector<double> i_Kj22_inv( p1xp1 );
	vector<double> i12xi22_j( p1 );
	vector<double> r12xi22_j( p1 );
	vector<double> i12xi22( p2x2 );
	vector<double> r12xi22( p2x2 );
	vector<double> r21xr11( p2x2 );
	vector<double> i21xr11( p2x2 );
	vector<double> r_K12( p2x2 );             // K[e, -e]
	vector<double> i_K12( p2x2 );
	vector<double> r_sigma11( 4 );            // sigma[e, e]
	vector<double> i_sigma11( 4 );
	vector<double> r_sigma12( p2x2 );         // sigma[e, -e]
	vector<double> i_sigma12( p2x2 );
	vector<double> r_sigma22( p2xp2 );        // sigma[-e, -e]
	vector<double> i_sigma22( p2xp2 );
	vector<double> r_sigma11_inv( 4 );
	vector<double> i_sigma11_inv( 4 );
	vector<double> r_sigma2112( p2xp2 );
	vector<double> i_sigma2112( p2xp2 );
	vector<double> r_K22_inv( p2xp2 );
	vector<double> i_K22_inv( p2xp2 );
	vector<double> r_K12xK22_inv( p2x2 );
	vector<double> i_K12xK22_inv( p2x2 );

	// - -  for rgcwish_sigma
	Rcomplex *csigma = new Rcomplex[ pxp ];
	Rcomplex *Ind    = new Rcomplex[ pxp ];
	Rcomplex *K      = new Rcomplex[ pxp ];

	vector<double> X( dim * n );
	vector<double> Y( dim * n );
	vector<double> joint( 2 * dim * n );
	vector<double> IR( pxp );
	vector<double> Inv_R( pxp );
	vector<double> r_sigma_start( pxp );
	vector<double> i_sigma_start( pxp );
	vector<double> r_beta_star( dim );
	vector<double> i_beta_star( dim );
	vector<double> r_sigma_i( dim );
	vector<double> i_sigma_i( dim );
	vector<double> r_sigma_start_N_i( dim );     // For dynamic memory used
	vector<double> r_sigma_start_N_i_2( dim );   // For dynamic memory used
	vector<double> i_sigma_start_N_i( dim );     // For dynamic memory used
	vector<double> r_sigma_N_i( pxp );           // For dynamic memory used
	vector<double> r_sigma_N_i_2( pxp );         // For dynamic memory used
	vector<double> i_sigma_N_i( pxp );           // For dynamic memory used
	vector<int> N_i( dim );                      // For dynamic memory used
	// - - - - - - - - - - - - - - 

	double max_numeric_limits_ld = std::numeric_limits<double>::max() / 10000;

	GetRNGstate();
	// - - main loop for birth-death MCMC sampling algorithm for time series - - - - - - - - - - - |
	for( int i_mcmc = 0; i_mcmc < iteration; i_mcmc++ )
	{
		if( ( i_mcmc + 1 ) % 1000 == 0 ) Rprintf( " Iteration  %d                 \n", i_mcmc + 1 );

		counter = 0;
		useful = FALSE;
		for( j = 1; j < dim; j++ )  // The terms related to G in rate
			for( i = 0; i < j; i++ )
			{
				nu_star = b1 + 0.0;
				for( k = 0; k < dim; k++ )    // nu_star = b + sum( Gf[ , i ] * Gf[ , j ] )
					nu_star += G[ i * dim + k ] * G[ j * dim + k ];

				G_prior = lgammafn( 0.5 * ( nu_star + 1 ) ) - lgammafn( 0.5 * nu_star );
				//log_rates[ counter++ ] = ( G[ j * dim + i ] ) ? G_prior : - G_prior;
				rates[ counter++ ] = ( G[ j * dim + i ] ) ? G_prior : -G_prior;
			}

		for( j = 0; j < qp; j++ )
			is_infinite[ j ] = 0;   // Record if the rate of edge j is infinite

		for( t = 0; t < T; t++ )   // The loop for the frequencies
		{
			txpxp   = t * pxp;
			counter = 0;
			// - - STEP 1: calculating birth and death rates - - - - - - - - - - - - - - - - - - - |
			for( j = 1; j < dim; j++ )
			{
				jj     = j * dim + j + txpxp;
				r_Dsjj = r_Ds[ jj ];
				//i_Dsjj = i_Ds[ jj ];

				r_sigmaj11 = r_sigma[ jj ];        // sigma[j, j]
				i_sigmaj11 = i_sigma[ jj ];
				sub_matrices1( &r_sigma[ txpxp ], &r_sigmaj12[0], &r_sigmaj22[0], &j, &dim );
				Hsub_matrices1( &i_sigma[ txpxp ], &i_sigmaj12[0], &i_sigmaj22[0], &j, &dim );

				// sigma[-j,-j] - ( sigma[-j, j] %*% sigma[j, -j] ) / sigma[j,j]
				for( row = 0; row < p1; row++ )
					for( col = 0; col < p1; col++ )
					{
						rowCol             = col * p1 + row;
						r_Kj22_inv[ rowCol ] = r_sigmaj22[ rowCol ] - ( r_sigmaj11*(r_sigmaj12[row]*r_sigmaj12[col] + i_sigmaj12[row]*i_sigmaj12[col]) + i_sigmaj11*(r_sigmaj12[row]*i_sigmaj12[col] - i_sigmaj12[row]*r_sigmaj12[col]))/(r_sigmaj11*r_sigmaj11 + i_sigmaj11*i_sigmaj11);
						i_Kj22_inv[ rowCol ] = i_sigmaj22[ rowCol ] - ( r_sigmaj11*(r_sigmaj12[row]*i_sigmaj12[col] - i_sigmaj12[row]*r_sigmaj12[col]) - i_sigmaj11*(r_sigmaj12[row]*r_sigmaj12[col] + i_sigmaj12[row]*i_sigmaj12[col]))/(r_sigmaj11*r_sigmaj11 + i_sigmaj11*i_sigmaj11);
					}

				for( i = 0; i < j; i++ )
				{
					ij = j * dim + i;
					if( is_infinite[ counter ] ) // If the rate of edge counter has already been infinite, then we consider the
					{						   // final rate of it as infinite.
						counter++;
						continue;
					}

					ij    += txpxp;
					r_Dsij = r_Ds[ ij ];
					i_Dsij = i_Ds[ ij ];

					// - - For (i,j) = 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
					sub_row_mins( &r_K[txpxp], &r_Kj12[0], &j, &dim );    // K12 = K[j, -j]
					Hsub_row_mins( &i_K[txpxp], &i_Kj12[0], &j, &dim );   // K12 = K[j, -j]
					r_Kj12[ i ] = 0.0;                         // K12[ i ] = 0
					i_Kj12[ i ] = 0.0;                         // K12[ i ] = 0

					// Kj12xK22_inv = Kj12 %*% Kj22_inv
					F77_NAME(dgemm)( &transN, &transN, &one, &p1, &p1, &alpha, &i_Kj12[0], &one, &i_Kj22_inv[0], &p1, &beta_0, &i12xi22_j[0], &one );
					F77_NAME(dgemm)( &transN, &transN, &one, &p1, &p1, &alpha, &r_Kj12[0], &one, &r_Kj22_inv[0], &p1, &dmone, &i12xi22_j[0], &one );
					F77_NAME(dgemm)( &transN, &transN, &one, &p1, &p1, &alpha, &r_Kj12[0], &one, &i_Kj22_inv[0], &p1, &beta_0, &r12xi22_j[0], &one );
					F77_NAME(dgemm)( &transN, &transN, &one, &p1, &p1, &alpha, &i_Kj12[0], &one, &r_Kj22_inv[0], &p1, &alpha, &r12xi22_j[0], &one );
					// K022  <- Kj12 %*% solve( K0[-j, -j] ) %*% t(Kj12) = c
					F77_NAME(dgemm)( &transN, &transN, &one, &one, &p1, &alpha, &r12xi22_j[0], &one, &i_Kj12[0], &p1, &beta_0, &r_K022, &one );
					F77_NAME(dgemm)( &transN, &transN, &one, &one, &p1, &alpha, &i12xi22_j[0], &one, &r_Kj12[0], &p1, &alpha, &r_K022, &one );
					F77_NAME(dgemm)( &transN, &transN, &one, &one, &p1, &alpha, &i12xi22_j[0], &one, &i_Kj12[0], &p1, &beta_0, &i_K022, &one );
					F77_NAME(dgemm)( &transN, &transN, &one, &one, &p1, &alpha, &r12xi22_j[0], &one, &r_Kj12[0], &p1, &dmone, &i_K022, &one );
					
					// - - For (i,j) = 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
					sub_rows_mins( &r_K[txpxp], &r_K12[0], &i, &j, &dim );  // K12 = K[e, -e]
					Hsub_rows_mins( &i_K[txpxp], &i_K12[0], &i, &j, &dim );  // K12 = K[e, -e]

					sub_matrices( &r_sigma[txpxp], &r_sigma11[0], &r_sigma12[0], &r_sigma22[0], &i, &j, &dim ); //r_sigma[e,e], r_sigma[e,-e], r_sigma[-e,-e]
					Hsub_matrices( &i_sigma[txpxp], &i_sigma11[0], &i_sigma12[0], &i_sigma22[0], &i, &j, &dim ); //i_sigma[e,e], i_sigma[e,-e], i_sigma[-e,-e]

					// solve( sigma[e, e] )
					cinverse_2x2( &r_sigma11[0], &i_sigma11[0], &r_sigma11_inv[0], &i_sigma11_inv[0] );

					// sigma21 %*% sigma11_inv = t(sigma12) %*% sigma11_inv
					F77_NAME(dgemm)( &transT, &transN, &p2, &two, &two, &alpha, &r_sigma12[0], &two, &r_sigma11_inv[0], &two, &beta_0, &r21xr11[0], &p2 );
					F77_NAME(dgemm)( &transT, &transN, &p2, &two, &two, &alpha, &i_sigma12[0], &two, &i_sigma11_inv[0], &two, &alpha, &r21xr11[0], &p2 );
					F77_NAME(dgemm)( &transT, &transN, &p2, &two, &two, &alpha, &i_sigma12[0], &two, &r_sigma11_inv[0], &two, &beta_0, &i21xr11[0], &p2 );
					F77_NAME(dgemm)( &transT, &transN, &p2, &two, &two, &alpha, &r_sigma12[0], &two, &i_sigma11_inv[0], &two, &dmone, &i21xr11[0], &p2 );
					// sigma2112 = sigma21xsigma11_inv %*% sigma12 = sigma[-e,e] %*% solve(sigma[e,e]) %*% sigma[e,-e]
					F77_NAME(dgemm)( &transN, &transN, &p2, &p2, &two, &alpha, &i21xr11[0], &p2, &i_sigma12[0], &two, &beta_0, &r_sigma2112[0], &p2 );
					F77_NAME(dgemm)( &transN, &transN, &p2, &p2, &two, &alpha, &r21xr11[0], &p2, &r_sigma12[0], &two, &dmone, &r_sigma2112[0], &p2 );
					F77_NAME(dgemm)( &transN, &transN, &p2, &p2, &two, &alpha, &r21xr11[0], &p2, &i_sigma12[0], &two, &beta_0, &i_sigma2112[0], &p2 );
					F77_NAME(dgemm)( &transN, &transN, &p2, &p2, &two, &alpha, &i21xr11[0], &p2, &r_sigma12[0], &two, &alpha, &i_sigma2112[0], &p2 );


					// solve( K[-e, -e] ) = sigma22 - sigma2112
					for( k = 0; k < p2xp2 ; k++ )
					{
						r_K22_inv[ k ] = r_sigma22[ k ] - r_sigma2112[ k ];
						i_K22_inv[ k ] = i_sigma22[ k ] - i_sigma2112[ k ];
					}

					// K12 %*% K22_inv
					F77_NAME(dgemm)( &transN, &transN, &two, &p2, &p2, &alpha, &i_K12[0], &two, &i_K22_inv[0], &p2, &beta_0, &i12xi22[0], &two );
					F77_NAME(dgemm)( &transN, &transN, &two, &p2, &p2, &alpha, &r_K12[0], &two, &r_K22_inv[0], &p2, &dmone, &i12xi22[0], &two );
					F77_NAME(dgemm)( &transN, &transN, &two, &p2, &p2, &alpha, &r_K12[0], &two, &i_K22_inv[0], &p2, &beta_0, &r12xi22[0], &two );
					F77_NAME(dgemm)( &transN, &transN, &two, &p2, &p2, &alpha, &i_K12[0], &two, &r_K22_inv[0], &p2, &alpha, &r12xi22[0], &two );
					// K121 <- K[e, -e] %*% solve( K[-e, -e] ) %*% t(K[e, -e])
					F77_NAME(dgemm)( &transN, &transT, &two, &two, &p2, &alpha, &r12xi22[0], &two, &i_K12[0], &two, &beta_0, &r_K121[0], &two );
					F77_NAME(dgemm)( &transN, &transT, &two, &two, &p2, &alpha, &i12xi22[0], &two, &r_K12[0], &two, &alpha, &r_K121[0], &two );
					F77_NAME(dgemm)( &transN, &transT, &two, &two, &p2, &alpha, &i12xi22[0], &two, &i_K12[0], &two, &beta_0, &i_K121[0], &two );
					F77_NAME(dgemm)( &transN, &transT, &two, &two, &p2, &alpha, &r12xi22[0], &two, &r_K12[0], &two, &dmone, &i_K121[0], &two );

					// - - Finished (i,j) = 1- - - - - - - - - - - - - - - - - - - - - - - - - - - |

					r_a11      = r_K[ i * dim + i + txpxp ] - r_K121[0]; //k_ii - k_ii^1
					r_sum_diag = r_Dsjj * ( r_K022 - r_K121[3] ) - ( r_Dsij * r_K121[1] - i_Dsij * i_K121[1] ) - ( r_Dsij * r_K121[2] + i_Dsij * i_K121[2] ); //tr(D*(K0-K1))

					mod_Dsjj = fabs( static_cast<double>( r_Dsjj ) );
					mod_a11  = fabs( static_cast<double>( r_a11 ) );
					coef     = ( r_Dsij * r_Dsij + i_Dsij * i_Dsij ) / ( r_Dsjj * r_Dsjj );
					r_temp   = coef * ( r_a11 * r_Dsjj ) + r_sum_diag;

					//rate     = ( G[ij - txpxp] ) ? mod_Dsjj / mod_a11 * exp( -r_temp ) : mod_a11 / mod_Dsjj * exp( r_temp );
					log_rate  = ( G[ ij - txpxp ] )
					          ? log( mod_Dsjj ) - log( mod_a11 ) - r_temp
					          : log( mod_a11 ) - log( mod_Dsjj ) + r_temp;

					log_rate += rates[ counter ];

					rates[counter++] = ( log_rate < 0.0 ) ? exp( log_rate ) : 1.0;

					//if( R_FINITE( rate ) )   // Computer the rate in log space
						//log_rates[counter++] += log( static_cast<double>( rate ) );
					//else
					//{
						//rates[ counter ] = max_numeric_limits_ld;
						//is_infinite[counter++] = true;
					//}
				}
			}
		}// end of frequency loop

		// Selecting an edge based on birth and death rates
		counter = 0;
		for( j = 1; j < dim; j++ )
			for( i = 0; i < j; i++ )
				char_g[ counter++ ] = G[ j * dim + i ] + '0';

		//for( t = 0; t < qp; t++ ) // Exponentialize the log rate
		//{
			//rate = exp( log_rates[ t ] );
			//if( !is_infinite[ t ] && R_FINITE( rate ) )
				//rates[ t ] = rate;
			//else
				//rates[ t ] = max_numeric_limits_ld;
		//}

		//select_edge2( &rates[0], &index_selected_edge, &sum_rates, &qp );
		select_edge( &rates[0], &index_selected_edge, &sum_rates, &qp );
		selected_edge_i = index_rates_row[ index_selected_edge ];
		selected_edge_j = index_rates_col[ index_selected_edge ];

		// - - saving result - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
		if( i_mcmc >= burn_in )
		{
			if( sum_rates == 0 )
			{
				*exit = i_mcmc + 1;
				for( t = 0; t < pxpxT; t++ )
				{
					r_K_hat[ t ] = r_K[ t ];
					i_K_hat[ t ] = i_K[ t ];
				}

				string_g = string( char_g.begin(), char_g.end() );  // The adjacent matrix of G
				all_weights[ counterallG ] = max_numeric_limits_ld;

				this_one = false;
				for ( i = 0; i < size_sample_graph ; i++ )  // Check whether G appeared before
					if( sample_graphs_C[ i ] == string_g )
					{
						graph_weights[ i ] = all_weights[ counterallG ];
						all_graphs[ counterallG ] = i;
						this_one = true;
						break;
					}

				if( !this_one || size_sample_graph == 0 )  // If not, record a new graph
				{
					sample_graphs_C[ size_sample_graph ] = string_g;
					graph_weights[ size_sample_graph ]   = all_weights[ counterallG ];
					all_graphs[ counterallG ]            = size_sample_graph;
					size_sample_graph++;
					*size_sample_g = size_sample_graph;
				}

				for( i = 0; i < ( i_mcmc - burn_in ); i++ )
				{
					sample_graphs_C[ i ].copy( sample_graphs[ i ], qp, 0 );
					sample_graphs[ i ][ qp ] = '\0';
				}

				delete[] csigma;
				delete[] Ind;
				delete[] K;

				return;
			}else{
				if( !useful && sum_rates < 1 ) // Set the first huge sum_rates as the common_factor and multiply it when calculating
				{							   // K_hat_Cpp, p_links_Cpp and sum_weights to avoid overflow
					common_factor = sum_rates;
					useful        = TRUE;
				}

				for( t = 0; t < pxpxT; t++ )
				{
					r_K_hat[ t ] += r_K[ t ] * ( common_factor / sum_rates );
					i_K_hat[ t ] += i_K[ t ] * ( common_factor / sum_rates );
				}

				string_g = string( char_g.begin(), char_g.end() ); // The adjacent matrix of G
				all_weights[counterallG] = 1.0 / sum_rates;

				this_one = false;
				for ( i = 0; i < size_sample_graph ; i++ ) // Check whether G appeared before
					if( sample_graphs_C[ i ] == string_g )
					{
						graph_weights[ i ] += all_weights[ counterallG ];
						all_graphs[ counterallG ] = i;
						this_one = true;
						break;
					}

				if( !this_one || size_sample_graph == 0 )  // If not, record a new graph
				{
					sample_graphs_C[ size_sample_graph ] = string_g;
					graph_weights[ size_sample_graph ]   = all_weights[ counterallG ];
					all_graphs[ counterallG ]            = size_sample_graph;
					size_sample_graph++;
				}

				counterallG++;
				sum_weights += 1.0 * ( common_factor / sum_rates );
			}
		} // End of saving result - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|

		// If sum_rates = 0, we consider the graph in this state is the true graph
		if( sum_rates == 0 )
		{
			*exit = i_mcmc + 1;
			for( t = 0; t < pxpxT; t++ )
			{
				r_K_hat[ t ] = r_K[ t ];
				i_K_hat[ t ] = i_K[ t ];
			}

			delete[] csigma;
			delete[] Ind;
			delete[] K;

			return;
		}

		// Updating G (graph) based on selected edge
		selected_edge_ij    = selected_edge_j * dim + selected_edge_i;
		G[ selected_edge_ij ] = 1 - G[ selected_edge_ij ];
		G[ selected_edge_i * dim + selected_edge_j ] = G[ selected_edge_ij ];

		if( G[ selected_edge_ij ] )
		{
			++size_node[ selected_edge_i ];
			++size_node[ selected_edge_j ];
		}else{
			--size_node[ selected_edge_i ];
			--size_node[ selected_edge_j ];
		}

		// - - STEP 2: Sampling from G-Wishart for new graph - - - - - - - - - - - - - - - - - - - |
		for( t = 0; t < T; t++ )
		{
			txpxp = t * pxp;
			int iter = 0;
			rgcwish_sigma( G, &size_node[0], &Ls[4 * txpxp], K, &r_sigma[txpxp],
				&i_sigma[txpxp], csigma, Ind, &b_star[ t ], &dim,
				&r_sigma_start[0], &i_sigma_start[0], &X[0], &Y[0],
				&r_beta_star[0], &i_beta_star[0], &joint[0], &r_sigma_i[0],
				&i_sigma_i[0], r_sigma_start_N_i, r_sigma_start_N_i_2,
				i_sigma_start_N_i, r_sigma_N_i, r_sigma_N_i_2, i_sigma_N_i, N_i, IR, Inv_R, &iter );

			for( k = 0; k < pxp; k++ )
			{
				r_K[ k + txpxp ] = K[ k ].r;
				i_K[ k + txpxp ] = K[ k ].i;
			}
		}
	} // - - End of MCMC sampling algorithm - - - - - - - - - - - - - - - - - - - - - - - - - - - -|
	PutRNGstate();

	for( i = 0; i < size_sample_graph; i++ )
	{
		sample_graphs_C[ i ].copy( sample_graphs[ i ], qp, 0 );
		sample_graphs[ i ][ qp ] = '\0';
	}

	*size_sample_g = size_sample_graph;

	for( i = 0; i < pxpxT; i++ )
	{
		r_K_hat[ i ] /= sum_weights;
		i_K_hat[ i ] /= sum_weights;
	}

	delete[] csigma;
	delete[] Ind;
	delete[] K;
}

} // End of exturn "C"
back to top