*! mixRRM_gf0 1.1.0 10May2022
*! [aut & dev] Ziyue Zhu & Álvaro A. Gutiérrez-Vargas


mata: mata set matastrict off

/*=========================================================*/
/*=================== RRM FUNCTION ========================*/
/*=========================================================*/

mata:
real matrix RRM_log(real matrix    x_n,
                    real rowvector b,
                    real scalar    mu,
                    real scalar    gamma)
{
	real scalar i, j, r_m
	real matrix regret_n

	regret_n = J(rows(x_n), cols(x_n), 0)
	for(i = 1; i <= rows(x_n); ++i) {
		for(j = 1; j <= rows(x_n); ++j) {
			if (i != j) {
				r_i = ln(gamma :+ exp((b :/ mu) :* (x_n[j, .] :- x_n[i, .])))
				regret_n[i, . ] = regret_n[i, . ] :+ mu*r_i
			}
		}
	}
	return(regret_n)
}
end

/*=============================================================*/
/*=================== MIXED RRM FUNCTION ======================*/
/*=============================================================*/

version 12
mata:
void mixRRM_gf0(transmorphic scalar MM,
                real scalar todo,
                real rowvector b,
                real colvector lnfj, /* <- a colvector with all the individual loglikelihood constributions */
                S,
                H)
 {

 	/*--------------------------*/
	/*--- External Variables ---*/
	/*--------------------------*/

	external mixr_T     // observations in the (long) data  [N x J x T]
	external mixr_CSID  // choice sets ID
	external mixr_IND   // individual ID
	external mixr_nrep
	external mixr_np
	external mixr_kfix
	external mixr_krnd
	external mixr_krln
	external mixr_burn
	external mixr_corr
	external mixr_user

	nrep = mixr_nrep
	np   = mixr_np
	kfix = mixr_kfix
	krnd = mixr_krnd
	krln = mixr_krln
	burn = mixr_burn
	corr = mixr_corr
	user = mixr_user
	ID_IND = mixr_IND

	/*-----------------------------*/
	/*--- Variables Declaration ---*/
	/*-----------------------------*/

	real matrix subject     // general info about individuals
	real matrix N_subject   // intermediate step to extract info about num panels

	/* Loop scalar variables */
	real scalar n           // for individual
	real scalar t           // for choice set
	real scalar i           // alternative i
	real scalar j           // alternative j
	real scalar m           // index
	real scalar n_rows      // scalar for num rows
	real scalar n_cols      // scalar for num columns

	/* Matrix explanatory and explained variable */
	real matrix Y           // explained variable
	real matrix X           // covariates

	/* Sub matrices per individual */
	real matrix x_n         // sub matrix covariates individual n
	real matrix y_n         // sub matrix   choice   individual n

	cons_demanded = st_global("cons_demanded")

	/*--------------------------*/
	/*--- Variables Creation ---*/
	/*--------------------------*/

	mixr_Y = moptimize_util_depvar(MM, 1)       // recovering the explained variable
	mixr_X = moptimize_init_eq_indepvars(MM, 1) // recovering the explanatory variable

	if (cons_demanded=="YES"){
		ASC = moptimize_init_eq_indepvars(MM, 3) // 3rd equation estimates ASC
		id_ASC_eq = moptimize_util_eq_indices(MM, 3)
		b_ASC = b[|id_ASC_eq|]
	}

	/*------------------------------------------------*/
	/*--- Parse Parameter vector (Random vs Fixed) ---*/
	/*------------------------------------------------*/

	B = b' // the program works with parameters (b) in column vector format (B = b')

	kall = kfix + krnd // total Number of Parameters (kall) = fixed (kfix) + random (krand)
	if (kfix > 0) {
		MFIX = B[|1, 1\kfix, 1|]
		MFIX = MFIX :* J(kfix, nrep, 1)
	}

	/* Recover mean and SD */
	MRND = B[|(kfix+1), 1\kall, 1|] // recovering the mean of the random parameters (MRND, "M" for \mu)
	SRND = diag(B[|(kall+1), 1\(kfix+2*krnd), 1|]) // independet distributions only take the diagonal

	/*------------------------------*/
	/*--- Compute log-likelihood ---*/
	/*------------------------------*/

	/* Set up panel information */
	subject = panelsetup(ID_IND, 1) // input is [N x 1] vector; output is [N_subject x 2] matrix
	N_subject = panelstats(subject)[1] // # of panel units (subjects), identified by number of rows in "panel"; given this 1 we recover the Num. of panels

	st_view(panvar = ., ., st_global("group_mata")) // panel information for ASC
	subject_ASC = panelsetup(panvar, 1)
	npanels = panelstats(subject_ASC)[1]

	moptimize_init_by(MM, ID_IND) // log-likelihood is computed at subject level

	if (user == 1) external mixr_USERDRAWS

	/* Object P */
	P = J(np, 1, 0) // will store individual contributions to the LL of each individual

	m = 1
	/* Loop over individuals (npanels) */
	for(n = 1; n <= N_subject; n++) {

		if (user == 1) {
			ERR = invnormal(mixr_USERDRAWS[|1, (1+nrep*(n-1))\krnd, (nrep*n)|])
		}
		else {/* Regular (non-scrambled) Halton integration */
			ERR = invnormal(halton(nrep, krnd, (1+burn+nrep*(n-1)))')
		}

		/* Modify parameters to construct the [beta = mu + sigma * draw] structure. */
		if (kfix > 0) BETA = MFIX \ (MRND :+ (SRND*ERR))
		else BETA = MRND :+ (SRND*ERR) // get all random variables

		/* Log-normal distribution */
		if (krln > 0) {
			if ((kall-krln) > 0) {
				BETA = BETA[|1, 1\(kall-krln), nrep|]\exp(BETA[|(kall-krln+1), 1\kall, nrep|])
			}
			else {
				BETA = exp(BETA)
			}
		}

		/* Object R */
		R = J(1, nrep, 1) // will store the product of sequences of choices made by the same individual

		/* Looping over choice situations (t) of individual (n) */
		t = 1
		nc = mixr_T[m, 1] // number of choice sets

		for(t = 1; t <= nc; t++) {

			/* Parse the matrix X and Y at the level of choice situation (t) */
			YMAT = mixr_Y[|m, 1\(m+mixr_CSID[m,1]-1), cols(mixr_Y)|]
			XMAT = mixr_X[|m, 1\(m+mixr_CSID[m,1]-1), cols(mixr_X)|]

			/* Shape of block individual n */
			n_rows = rows(XMAT) // number of faced alternatives
			n_cols = cols(XMAT) // number of covariates

			ER_rep = J(n_rows, nrep, 0) // store the regret of each draws

			if (cons_demanded == "YES") {
				asc = panelsubmatrix(ASC, 1, subject_ASC)
				ASC_prod = asc*b_ASC'
			}

			for(rr = 1; rr <= nrep; rr++) {
				regret_draw_r = RRM_log(XMAT, BETA[.,rr]', 1, 1) // regret

				if (cons_demanded == "YES") {
					ER_rep[ ., rr] =rowsum(regret_draw_r :+ ASC_prod) // with ASC condition
				}
				else if (cons_demanded == "NO") {
					ER_rep[ ., rr] =rowsum(regret_draw_r)
				}
			}

			ER = exp(-ER_rep) // the negative of the regret
			ER = (ER :/ colsum(ER, 1))

			R = R :* colsum(YMAT :* ER, 1) // product of repeated choices from the same individual

			m = m + mixr_CSID[m, 1] // move the index for the next choice situation
		}

		P[n, 1] = mean(R', 1) // average all the probabilities over all the draws
	}

	/* The vector containing the LL of every individual is simply the log of the probability. */
	lnfj = ln(P)
}
end



/*=====================================================================*/
/*================== MIXED RRM PREDICT FUNCTION =======================*/
/*=====================================================================*/

mata:
real matrix pbb_pred(real matrix X, real matrix ASC, real colvector panvar)
{

	/*--------------------------*/
	/*--- External Variables ---*/
	/*--------------------------*/

	external mixrpre_X
	external mixrpre_T
	external mixrpre_CSID
	external mixrpre_np
	external b_hat
	external b_all
	external ASC_hat
	external ASC
	external mixr_IND

	np = mixrpre_np
	ID_IND = mixr_IND
	command = st_local("cmd")
	nrep = strtoreal(st_local("nrep"))
	kfix = st_numscalar("e(kfix)")
	krnd = st_numscalar("e(krnd)")
	krln = st_numscalar("e(krln)")
	burn = strtoreal(st_local("burn"))
	user = st_numscalar("e(userdraws)")
	cons_demanded = st_global("cons_demanded")
	proba = st_local("proba")

	/*------------------------------*/
	/*--- Parse Parameter vector ---*/
	/*------------------------------*/

	B_hat = b_all'

	kall = kfix + krnd

	if (kfix > 0) {
		MFIX_hat = B_hat[|1, 1\kfix, 1|]
		MFIX_hat = MFIX_hat :* J(kfix, nrep, 1)
	}

	MRND_hat = B_hat[|(kfix+1), 1\kall, 1|]
	SRND_hat = diag(B_hat[|(kall+1), 1\(kfix+2*krnd), 1|])

	/*--------------------------*/
	/*--- Compute Prediction ---*/
	/*--------------------------*/

	/* Set up panel information */
	subject = panelsetup(ID_IND, 1)
	N_subject = panelstats(subject)[1]

	/* Panel information for ASC */
	subject_ASC = panelsetup(panvar, 1)
	npanels = panelstats(subject_ASC)[1]

	if (user == 1) external mixr_USERDRAWS

	m = 1
	for(n = 1; n <= N_subject; n++) {

		if (user == 1) {
			ERR = invnormal(mixr_USERDRAWS[|1, (1+nrep*(n-1))\krnd, (nrep*n)|])
		}
		else {
			ERR = invnormal(halton(nrep, krnd, (1+burn+nrep*(n-1)))')
		}

		if (kfix > 0) BETA_hat = MFIX_hat \ (MRND_hat :+ (SRND_hat*ERR))
		else BETA_hat = MRND_hat :+ (SRND_hat*ERR)

		if (krln > 0) {
			if ((kall-krln) > 0) {
				BETA_hat = BETA_hat[|1, 1\(kall-krln), nrep|] \ exp(BETA_hat[|(kall-krln+1), 1\kall, nrep|])
			}
			else {
				BETA_hat = exp(BETA_hat)
			}
		}

		t = 1
		nc = mixrpre_T[m, 1]

		for(t = 1; t <= nc; t++) {

			XMAT = mixrpre_X[|m, 1\(m+mixrpre_CSID[m, 1]-1), cols(mixrpre_X)|]

			n_rows = rows(XMAT)
			n_cols = cols(XMAT)

			if (cons_demanded == "YES") {
				asc = panelsubmatrix(ASC, 1, subject_ASC)
				ASC_prod = asc*ASC_hat'
			}

			ER_rep_hat = J(n_rows, nrep, 0)
			for(rr = 1; rr <= nrep; rr++) {
				regret_draw_hat = RRM_log(XMAT, BETA_hat[.,rr]', 1, 1)

				if (cons_demanded == "YES") {
					ER_rep_hat[., rr] = rowsum(regret_draw_hat :+ ASC_prod)
				}
				else if (cons_demanded == "NO") {
					ER_rep_hat[., rr] =rowsum(regret_draw_hat)
				}
			}

			ER_hat = exp(-ER_rep_hat)
			p_hat_i = (ER_hat :/ colsum(ER_hat, 1))

			N_cs = mixrpre_CSID[1]
			/* Object output */
			p_out_cs = J(N_cs, 1, 0)
			regret_out_cs = J(N_cs, 1, 0)

			for(aa = 1; aa <= N_cs; aa++) {
				p_out_cs[aa, 1] = mean(p_hat_i[aa,.]', 1)
				regret_out_cs[aa, 1] = mean(ER_rep_hat[aa,.]', 1)
			}

			m = m + mixrpre_CSID[m, 1]

			/* Probability prediction: proba option [default] */
			if (proba != "") {
				if (t == 1) p_hat_cs = p_out_cs
				else	    p_hat_cs = p_hat_cs \ p_out_cs
			}
			else {
				/* Linear prediction: xb option */
				if (t == 1) regret_hat_cs = regret_out_cs
				else	    regret_hat_cs = regret_hat_cs \ regret_out_cs
			}
		}
		/* Append individuals */
		if (proba != "") {
			if (n == 1) p_hat = p_hat_cs
			else	    p_hat = p_hat \ p_hat_cs
		}
		else {
			if (n == 1) regret_hat = regret_hat_cs
			else	    regret_hat = regret_hat \ regret_hat_cs
		}
	}
	/* Output prediction */
	if (proba != "") return(p_hat)
	else return(regret_hat)
}
end



/*=====================================================================*/
/*================== INDIVIDUAL LEVEL PARAMETERS ======================*/
/*=====================================================================*/

version 9.2
mata:
function mixr_beta(string scalar B_s, real colvector panvar)
{

	/*--------------------------*/
	/*--- External Variables ---*/
	/*--------------------------*/

	external mixrbeta_X
	external mixrbeta_Y
	external mixrbeta_T
	external mixrbeta_CSID
	external mixrbeta_PB
	external mixrbeta_np
	external mixrbeta_ID
	external b_beta
	external b_all
	external ASC_beta
	external ASC
	external panvar

	np = mixrbeta_np
	ID_IND = mixrbeta_ID
	command = st_local("cmd")
	nrep = strtoreal(st_local("nrep"))
	kfix = st_numscalar("e(kfix)")
	krnd = st_numscalar("e(krnd)")
	krln = st_numscalar("e(krln)")
	burn = strtoreal(st_local("burn"))
	user = st_numscalar("e(userdraws)")
	cons_demanded = st_global("cons_demanded")

	/*------------------------------*/
	/*--- Parse Parameter vector ---*/
	/*------------------------------*/

	B_beta = b_all'

	kall = kfix + krnd

	if (kfix > 0) {
		MFIX_beta = B_beta[|1, 1\kfix, 1|]
		MFIX_beta = MFIX_beta :* J(kfix, nrep, 1)
	}

	MRND_beta = B_beta[|(kfix+1), 1\kall, 1|]
	SRND_beta = diag(B_beta[|(kall+1), 1\(kfix+2*krnd), 1|])

	/*--------------------------*/
	/*--- Compute Prediction ---*/
	/*--------------------------*/

	/* Set up panel information */
	subject = panelsetup(ID_IND, 1)
	N_subject = panelstats(subject)[1]

	/* Panel information for ASC */
	subject_ASC = panelsetup(panvar, 1)
	npanels = panelstats(subject_ASC)[1]

	if (user == 1) external mixr_USERDRAWS

	/* Object P */
	P = J(np, 1, 0)

	m = 1
	/* Loop over individuals (npanels) */
	for(n = 1; n <= N_subject; n++) {

		if (user == 1) {
			ERR = invnormal(mixr_USERDRAWS[|1, (1+nrep*(n-1))\krnd, (nrep*n)|])
		}
		else {
			/* Regular (non-scrambled) Halton integration */
			ERR = invnormal(halton(nrep, krnd, (1+burn+nrep*(n-1)))')
		}

		/* Modify parameters to construct the [beta = mu + sigma * draw] structure. */
		if (kfix > 0) BETA_new = MFIX_beta \ (MRND_beta :+ (SRND_beta*ERR))
		else BETA_new = MRND_beta :+ (SRND_beta*ERR) // get all random variables

		/* Log-normal distribution */
		if (krln > 0) {
			if ((kall-krln) > 0) {
				BETA_new = BETA_new[|1, 1\(kall-krln), nrep|] \ exp(BETA_new[|(kall-krln+1), 1\kall, nrep|])
			}
			else {
				BETA_new = exp(BETA_new)
			}
		}

		/* Object R */
		R = J(1, nrep, 1)

		/* Looping over choice situations (t) of individual (n) */
		t = 1
		nc = mixrbeta_T[m, 1] // number of choice sets

		for(t = 1; t <= nc; t++) {

			/* Parse the matrix X and Y at the level of choice situation (t) */
			YMAT = mixrbeta_Y[|m, 1\(m+mixrbeta_CSID[m,1]-1), cols(mixrbeta_Y)|]
			XMAT = mixrbeta_X[|m, 1\(m+mixrbeta_CSID[m,1]-1), cols(mixrbeta_X)|]

			/* Shape of block individual n */
			n_rows=rows(XMAT)
			n_cols=cols(XMAT)

			if (cons_demanded == "YES") {
				asc = panelsubmatrix(ASC, 1, subject_ASC)
				ASC_prod = asc*ASC_beta'
			}

			ER_rep_beta = J(n_rows, nrep, 0)
			for(rr = 1; rr <= nrep; rr++) {
				regret_draw_beta = RRM_log(XMAT, BETA_new[., rr]', 1, 1)

				if (cons_demanded == "YES") {

					ER_rep_beta[., rr] =rowsum(regret_draw_beta :+ ASC_prod)

				}
				else if (cons_demanded == "NO") {
					ER_rep_beta[ .,rr] =rowsum(regret_draw_beta)
				}
			}

			ER_beta = exp(-ER_rep_beta)
			ER_beta = (ER_beta :/ colsum(ER_beta, 1))

			R = R :* colsum(YMAT :* ER_beta, 1)
			m = m + mixrbeta_CSID[m, 1]
		}
		P[n, 1] = mean(R', 1)
		mixrbeta_PB[n, .] = mean((R :* BETA_new)', 1) / P[n, 1]
	}
}
end

exit