Beispiel #1
0
def estimate(method, vb_iter, vb_tol, modelName, seed, drawsType, nDraws,
             deleteDraws, paramFixMu_inits, paramFixSi_inits, paramRndMu_inits,
             paramRndSi_inits, zetaMu_inits, zetaSi_inits, cK, dK_inits, rK,
             omega, psi_inits, nu, mu0Fix, Sigma0FixInv, mu0Rnd, Sigma0RndInv,
             indID, obsID, chosen, xFix, xRnd):

    np.random.seed(seed)

    ###
    #Prepare data
    ###

    nFix = xFix.shape[1]
    nRnd = xRnd.shape[1]

    full = method[1] in ['delta', 'mji']
    xList = [xFix, xRnd]
    (xList, nInd, nObs, nRow, chosenIdx, nonChosenIdx, rowsPerInd, rowsPerObs,
     map_obs_to_ind, map_avail_to_obs) = prepareData(xList, indID, obsID,
                                                     chosen, full)
    xFix, xRnd = xList[0], xList[1]

    nRow = map_avail_to_obs.shape[0]
    obsPerInd = np.ones((nObs, ), dtype='int64') @ map_obs_to_ind
    map_ind_to_avail = (map_avail_to_obs @ map_obs_to_ind).T
    map_avail = map_avail_to_obs @ map_avail_to_obs.T
    slObsInd = map_obs_to_ind @ np.arange(nInd)

    cumRowsPerObs = np.cumsum(rowsPerObs)
    slObsRow = [
        slice(l, u)
        for l, u in zip(np.concatenate((np.array([0]),
                                        cumRowsPerObs[:-1])), cumRowsPerObs)
    ]

    chFixIdx = None
    chFixIdxDiag = None
    if nFix:
        chFixIdx = np.triu_indices(nFix)
        chFixIdx = chFixIdx[1], chFixIdx[0]
        chFixIdxDiagAux = np.ones((nFix, nFix), dtype='int64')
        chFixIdxDiagAux[chFixIdx] = np.arange((nFix * (nFix + 1) / 2))
        chFixIdxDiag = np.diag(chFixIdxDiagAux)

    chRndIdx = None
    chRndIdxDiag = None
    if nRnd:
        chRndIdx = np.triu_indices(nRnd)
        chRndIdx = chRndIdx[1], chRndIdx[0]
        chRndIdxDiagAux = np.ones((nRnd, nRnd), dtype='int64')
        chRndIdxDiagAux[chRndIdx] = np.arange((nRnd * (nRnd + 1) / 2))
        chRndIdxDiag = np.diag(chRndIdxDiagAux)

    slIndObs = None
    slIndRow = None
    cumObsPerInd = np.cumsum(obsPerInd)
    slIndObs = [
        slice(l, u)
        for l, u in zip(np.concatenate((np.array([0]),
                                        cumObsPerInd[:-1])), cumObsPerInd)
    ]
    cumRowsPerInd = np.cumsum(rowsPerInd)
    slIndRow = [
        slice(l, u)
        for l, u in zip(np.concatenate((np.array([0]),
                                        cumRowsPerInd[:-1])), cumRowsPerInd)
    ]

    ###
    #Generate draws
    ###

    drawsFix = None
    drawsRnd = None
    if method[1] == "qmc":
        if nFix: _, drawsFix = makeNormalDraws(nDraws, nFix, drawsType)
        if nRnd: drawsRnd, _ = makeNormalDraws(nDraws, nRnd, drawsType, nInd)

    ###
    #Coordinate Ascent
    ###

    if method[0] == "qn" and (method[1] in ["qmc", "delta"]) and nRnd == 0:
        vb_iter = 1

    paramFixMu = np.zeros((0, ))
    paramFixCh = np.zeros((0, 0))
    paramFixSi = np.zeros((0, 0))
    if nFix:
        paramFixMu = paramFixMu_inits.copy()
        paramFixCh = np.linalg.cholesky(paramFixSi_inits).copy()
        paramFixSi = paramFixSi_inits.copy()

    paramRndMu = None
    paramRndCh = None
    paramRndSi = None
    zetaMu = np.zeros((0, ))
    zetaSi = None
    dK = np.zeros((0, ))
    psi = np.zeros((0, 0))
    psiInv = None
    if nRnd:
        paramRndMu = paramRndMu_inits.copy()
        paramRndCh = np.linalg.cholesky(paramRndSi_inits).copy()
        paramRndSi = paramRndSi_inits.copy()
        zetaMu = zetaMu_inits.copy()
        zetaSi = zetaSi_inits.copy()
        dK = dK_inits.copy()
        psi = psi_inits.copy()
        psiInv = np.linalg.inv(psi).copy()

    mjiAux = None
    if method[1] == "mji":
        numer = np.ones((nRow, ))
        denom = map_avail @ numer
        mjiAux = np.array(numer / denom).reshape((nRow, 1))

    tic = time.time()
    (paramFixMu, paramFixCh, paramFixSi, paramRndMu, paramRndCh, paramRndSi,
     zetaMu, zetaSi, psi, dK, mjiAux, iters, parChange) = coordinateAscent(
         method, vb_iter, vb_tol, paramFixMu, paramFixCh, paramFixSi,
         paramRndMu, paramRndCh, paramRndSi, mjiAux, xFix, nFix, chFixIdx,
         chFixIdxDiag, xRnd, nRnd, chRndIdx, chRndIdxDiag, drawsFix, drawsRnd,
         nDraws, nInd, nObs, nRow, obsPerInd, rowsPerInd, rowsPerObs,
         map_obs_to_ind, map_avail_to_obs, map_ind_to_avail, map_avail,
         slIndObs, slIndRow, slObsInd, slObsRow, mu0Fix, Sigma0FixInv, mu0Rnd,
         Sigma0RndInv, zetaMu, zetaSi, psi, psiInv, omega, nu, cK, dK, rK)
    toc = time.time() - tic

    print(' ')
    print('Computation time [s]: ' + str(toc))

    ###
    #Delete draws
    ###

    if deleteDraws:
        drawsFix = None
        drawsRnd = None

    ###
    #Save results
    ###

    results = {
        'modelName': modelName,
        'seed': seed,
        'estimation_time': toc,
        'iters': iters,
        'termTol': parChange,
        'drawsType': drawsType,
        'nDraws': nDraws,
        'drawsFix': drawsFix,
        'drawsRnd': drawsRnd,
        'paramFixMu': paramFixMu,
        'paramFixCh': paramFixCh,
        'paramFixSi': paramFixSi,
        'paramRndMu': paramRndMu,
        'paramRndCh': paramRndCh,
        'paramRndSi': paramRndSi,
        'zetaMu': zetaMu,
        'zetaSi': zetaSi,
        'psi': psi,
        'omega': omega,
        'dK': dK,
        'mjiAux': mjiAux
    }

    return results
def estimate(drawsType, nDraws, nTakes, seed, modelName, deleteDraws,
             simCondInd, nSim, paramFix_inits, paramRndUc_mu_inits,
             paramRndUc_sd_inits, paramRndCo_mu_inits, paramRndCo_ch_inits,
             indID, obsID, altID, chosen, xFix, xRndUc, xRndCo, xFix_trans,
             xRndUc_trans, xRndCo_trans):

    np.random.seed(seed)

    ###
    #Prepare data
    ###

    nFix = xFix.shape[1]
    nRndUc = xRndUc.shape[1]
    nRndCo = xRndCo.shape[1]
    nRnd = nRndUc + nRndCo

    if nRnd > 0:
        nDrawsMem, mod = divmod(nDraws, nTakes)
        assert mod == 0, "nDraws is not multiple of nTakes!"
    else:
        nDraws, nDrawsMem, nTakes = 1, 1, 1

    xFix_transBool = np.sum(xFix_trans) > 0
    xRndUc_transBool = np.sum(xRndUc_trans) > 0
    xRndCo_transBool = np.sum(xRndCo_trans) > 0

    xList = [xFix, xRndUc, xRndCo]
    (xList, nInd, nObs, nRow, chosenIdx, nonChosenIdx, rowsPerInd, rowsPerObs,
     map_obs_to_ind, map_avail_to_obs) = prepareData(xList, indID, obsID,
                                                     chosen)
    xFix, xRndUc, xRndCo = xList[0], xList[1], xList[2]

    sim_xFix, sim_xRndUc, sim_xRndCo = np.tile(xFix, (nDrawsMem, 1)), np.tile(
        xRndUc, (nDrawsMem, 1)), np.tile(xRndCo, (nDrawsMem, 1))
    sim_rowsPerInd = np.tile(rowsPerInd, (nDrawsMem, ))
    sim_map_obs_to_ind = scipy.sparse.kron(scipy.sparse.eye(nDrawsMem),
                                           map_obs_to_ind)
    sim_map_avail_to_obs = scipy.sparse.kron(scipy.sparse.eye(nDrawsMem),
                                             map_avail_to_obs)
    sim_map_draws_to_ind = scipy.sparse.hstack(
        [scipy.sparse.eye(nInd) for i in np.arange(nDrawsMem)])
    sim_map_ind_to_avail = (sim_map_avail_to_obs @ sim_map_obs_to_ind).T

    chIdx = None
    if nRndCo:
        chIdx = np.triu_indices(nRndCo)
        chIdx = chIdx[1], chIdx[0]

    ###
    #Generate draws
    ###

    drawsUc = None
    drawsCo = None
    if nRndUc: _, drawsUc = makeNormalDraws(nDraws, nRndUc, drawsType, nInd)
    if nRndCo: _, drawsCo = makeNormalDraws(nDraws, nRndCo, drawsType, nInd)

    ###
    #Optimise
    ###

    paramRndCo_chVec_inits = np.ndarray.flatten(paramRndCo_ch_inits[chIdx])
    inits = np.concatenate(
        (paramFix_inits, paramRndUc_mu_inits, paramRndUc_sd_inits,
         paramRndCo_mu_inits, paramRndCo_chVec_inits),
        axis=0)

    tic = time.time()
    resOpt = sp.optimize.minimize(
        fun=objectiveMxl,
        x0=inits,
        args=(sim_xFix, xFix_transBool, xFix_trans, nFix, sim_xRndUc,
              xRndUc_transBool, xRndUc_trans, nRndUc, sim_xRndCo,
              xRndCo_transBool, xRndCo_trans, nRndCo, chIdx, drawsUc, drawsCo,
              nDraws, nDrawsMem, nTakes, nInd, sim_rowsPerInd,
              sim_map_obs_to_ind, sim_map_avail_to_obs, sim_map_ind_to_avail,
              sim_map_draws_to_ind),
        method='BFGS',
        jac=True,
        options={'disp': True})
    toc = time.time() - tic

    print(' ')
    print('Computation time [s]: ' + str(toc))

    ###
    #Process output
    ###

    logLik = -resOpt['fun']
    est = resOpt['x']
    iHess = resOpt['hess_inv']
    se = np.sqrt(np.diag(iHess))
    zVal = est / se
    pVal = 2 * scipy.stats.norm.cdf(-np.absolute(zVal))

    u = 0
    if nFix > 0:
        l = u
        u += nFix
        lu = slice(l, u)
        print(' ')
        print('Fixed parameters:')
        paramFix_est, paramFix_se, paramFix_zVal, paramFix_pVal, pd_paramFix = processOutput(
            est, se, zVal, pVal, lu)
    else:
        paramFix_est, paramFix_se, paramFix_zVal, paramFix_pVal, pd_paramFix = None, None, None, None, None

    if nRndUc > 0:
        l = u
        u += nRndUc
        lu = slice(l, u)
        print(' ')
        print('Uncorrelated random parameters (means):')
        paramRndUc_mu_est, paramRndUc_mu_se, paramRndUc_mu_zVal, paramRndUc_mu_pVal, pd_paramRndUc_mu = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u += nRndUc
        lu = slice(l, u)
        print(' ')
        print('Uncorrelated random parameters (standard deviations):')
        paramRndUc_sd_est, paramRndUc_sd_se, paramRndUc_sd_zVal, paramRndUc_sd_pVal, pd_paramRndUc_sd = processOutput(
            est, se, zVal, pVal, lu)
    else:
        paramRndUc_mu_est, paramRndUc_mu_se, paramRndUc_mu_zVal, paramRndUc_mu_pVal, pd_paramRndUc_mu = None, None, None, None, None
        paramRndUc_sd_est, paramRndUc_sd_se, paramRndUc_sd_zVal, paramRndUc_sd_pVal, pd_paramRndUc_sd = None, None, None, None, None

    if nRndCo > 0:
        l = u
        u += nRndCo
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters (means):')
        paramRndCo_mu_est, paramRndCo_mu_se, paramRndCo_mu_zVal, paramRndCo_mu_pVal, pd_paramRndCo_mu = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u = est.shape[0]
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters (Cholesky):')
        paramRndCo_ch_est_vec, paramRndCo_ch_se_vec, paramRndCo_ch_zVal_vec, paramRndCo_ch_pVal_vec, pd_paramRndCo_ch = processOutput(
            est, se, zVal, pVal, lu)

        print(' ')
        print('Correlated random parameters (Cholesky, est.):')
        paramRndCo_ch_est = np.zeros((nRndCo, nRndCo))
        paramRndCo_ch_est[chIdx] = paramRndCo_ch_est_vec
        print(pd.DataFrame(paramRndCo_ch_est))

        print(' ')
        print('Correlated random parameters (Cholesky, std. err.):')
        paramRndCo_ch_se = np.zeros((nRndCo, nRndCo))
        paramRndCo_ch_se[chIdx] = paramRndCo_ch_se_vec
        print(pd.DataFrame(paramRndCo_ch_se))

        print(' ')
        print('Correlated random parameters (Cholesky, p-val.):')
        paramRndCo_ch_pVal = np.zeros((nRndCo, nRndCo))
        paramRndCo_ch_pVal[chIdx] = paramRndCo_ch_pVal_vec
        print(pd.DataFrame(paramRndCo_ch_pVal))
    else:
        paramRndCo_mu_est, paramRndCo_mu_se, paramRndCo_mu_zVal, paramRndCo_mu_pVal, pd_paramRndCo_mu = None, None, None, None, None
        paramRndCo_ch_est, paramRndCo_ch_se, paramRndCo_ch_pVal, pd_paramRndCo_ch = None, None, None, None

    print(' ')
    print('Log-likelihood: ' + str(logLik))
    print(' ')

    if nRnd:
        print('QMC method: ' + drawsType)
        print('Number of simulation draws: ' + str(nDraws))

    ###
    #Conditional expectation of individual-specific parameters
    ###

    if simCondInd and nRnd > 0:
        paramRndUc_ind, paramRndCo_ind = condExpInd(
            paramFix_est, paramRndUc_mu_est, paramRndUc_sd_est,
            paramRndCo_mu_est, paramRndCo_ch_est, xFix, xFix_transBool,
            xFix_trans, nFix, xRndUc, xRndUc_transBool, xRndUc_trans, nRndUc,
            xRndCo, xRndCo_transBool, xRndCo_trans, nRndCo, nInd, rowsPerInd,
            map_obs_to_ind, map_avail_to_obs, nSim)
    else:
        paramRndUc_ind, paramRndCo_ind = None, None

    ###
    #Delete draws
    ###

    if deleteDraws:
        drawsUc = None
        drawsCo = None

    ###
    #Save results
    ###

    results = {
        'modelName': modelName,
        'seed': seed,
        'estimation_time': toc,
        'drawsType': drawsType,
        'nDraws': nDraws,
        'nSim': nSim,
        'drawsUc': drawsUc,
        'drawsCo': drawsCo,
        'logLik': logLik,
        'est': est,
        'iHess': iHess,
        'paramFix_est': paramFix_est,
        'paramFix_se': paramFix_se,
        'paramFix_zVal': paramFix_zVal,
        'paramFix_pVal': paramFix_pVal,
        'pd_paramFix': pd_paramFix,
        'paramRndUc_mu_est': paramRndUc_mu_est,
        'paramRndUc_mu_se': paramRndUc_mu_se,
        'paramRndUc_mu_zVal': paramRndUc_mu_zVal,
        'paramRndUc_mu_pVal': paramRndUc_mu_pVal,
        'pd_paramRndUc_mu': pd_paramRndUc_mu,
        'paramRndUc_sd_est': paramRndUc_sd_est,
        'paramRndUc_sd_se': paramRndUc_sd_se,
        'paramRndUc_sd_zVal': paramRndUc_sd_zVal,
        'paramRndUc_sd_pVal': paramRndUc_sd_pVal,
        'pd_paramRndUc_sd': pd_paramRndUc_sd,
        'paramRndCo_mu_est': paramRndCo_mu_est,
        'paramRndCo_mu_se': paramRndCo_mu_se,
        'paramRndCo_mu_zVal': paramRndCo_mu_zVal,
        'paramRndCo_mu_pVal': paramRndCo_mu_pVal,
        'pd_paramRndCo_mu': pd_paramRndCo_mu,
        'paramRndCo_ch_est': paramRndCo_ch_est,
        'paramRndCo_ch_se': paramRndCo_ch_se,
        'paramRndCo_ch_pVal': paramRndCo_ch_pVal,
        'pd_paramRndCo_ch': pd_paramRndCo_ch,
        'paramRndUc_ind': paramRndUc_ind,
        'paramRndCo_ind': paramRndCo_ind,
        'resOpt': resOpt
    }

    return results
Beispiel #3
0
def estimate(mcmc_nChain, mcmc_iterBurn, mcmc_iterSample, mcmc_thin,
             mcmc_iterMem, mcmc_disp, seed, simLogLik, simLogLikDrawsType,
             simDraws, rho, modelName, deleteDraws, A, nu, diagCov, zeta_inits,
             OmegaB_inits, OmegaW_inits, indID, obsID, altID, chosen, xRnd,
             xRnd_trans):
    ###
    #Prepare data
    ###

    nRnd = xRnd.shape[1]

    xRnd_transBool = np.sum(xRnd_trans) > 0

    xList = [xRnd]
    (xList, nInd, nObs, nRow, chosenIdx, nonChosenIdx, rowsPerInd, rowsPerObs,
     map_obs_to_ind, map_avail_to_obs) = prepareData(xList, indID, obsID,
                                                     chosen)
    xRnd = xList[0]

    obsPerInd = np.ones((nObs, ), dtype='int64') @ map_obs_to_ind

    assert np.unique(obsPerInd).shape[0] == 1, \
    "Number of choice tasks must be the same for all decision makers!"

    ###
    #Posterior sampling
    ###

    mcmc_iter = mcmc_iterBurn + mcmc_iterSample
    mcmc_iterSampleThin = floor(mcmc_iterSample / mcmc_thin)
    mcmc_iterMemThin = floor(mcmc_iterMem / mcmc_thin)

    A = A * np.ones((nRnd, ))
    invASq = A**(-2)

    zeta = zeta_inits
    OmegaB = OmegaB_inits
    OmegaW = OmegaW_inits

    tic = time.time()

    Parallel(n_jobs=mcmc_nChain)(delayed(mcmcChain)(
        c, seed, mcmc_iter, mcmc_iterBurn, mcmc_iterSampleThin,
        mcmc_iterMemThin, mcmc_thin, mcmc_disp, rho, modelName, zeta, OmegaB,
        OmegaW, invASq, nu, diagCov, xRnd, xRnd_transBool, xRnd_trans, nRnd,
        nInd, nObs, obsPerInd, rowsPerObs, map_obs_to_ind, map_avail_to_obs)
                                 for c in range(mcmc_nChain))

    toc = time.time() - tic

    print(' ')
    print('Computation time [s]: ' + str(toc))

    ###
    #Posterior analysis
    ###

    postMean_zeta, pdTabPostAna_zeta = postAna('zeta', nRnd, 1, mcmc_nChain,
                                               mcmc_iterSampleThin, modelName)
    print(' ')
    print('Random parameters (means):')
    print(pdTabPostAna_zeta)

    postMean_sdB, pdTabPostAna_sdB = postAna('sdB', nRnd, 1, mcmc_nChain,
                                             mcmc_iterSampleThin, modelName)
    print(' ')
    print('Random parameters (standard deviations; between):')
    print(pdTabPostAna_sdB)

    postMean_OmegaB, pdTabPostAna_OmegaB = postAna('OmegaB', nRnd, nRnd,
                                                   mcmc_nChain,
                                                   mcmc_iterSampleThin,
                                                   modelName)
    print(' ')
    print('Random parameters (covariance matrix; between):')
    print(pdTabPostAna_OmegaB)

    postMean_CorrB, pdTabPostAna_CorrB = postAna('CorrB', nRnd, nRnd,
                                                 mcmc_nChain,
                                                 mcmc_iterSampleThin,
                                                 modelName)
    print(' ')
    print('Random parameters (correlation matrix; between):')
    print(pdTabPostAna_CorrB)

    postMean_sdW, pdTabPostAna_sdW = postAna('sdW', nRnd, 1, mcmc_nChain,
                                             mcmc_iterSampleThin, modelName)
    print(' ')
    print('Random parameters (standard deviations; within):')
    print(pdTabPostAna_sdW)

    postMean_OmegaW, pdTabPostAna_OmegaW = postAna('OmegaW', nRnd, nRnd,
                                                   mcmc_nChain,
                                                   mcmc_iterSampleThin,
                                                   modelName)
    print(' ')
    print('Random parameters (covariance matrix; within):')
    print(pdTabPostAna_OmegaW)

    postMean_CorrW, pdTabPostAna_CorrW = postAna('CorrW', nRnd, nRnd,
                                                 mcmc_nChain,
                                                 mcmc_iterSampleThin,
                                                 modelName)
    print(' ')
    print('Random parameters (correlation matrix; within):')
    print(pdTabPostAna_CorrW)

    postMean_paramRndB, pdTabPostAna_paramRndB = postAna(
        'paramRndB', nInd, nRnd, mcmc_nChain, mcmc_iterSampleThin, modelName)

    ###
    #Simulate log-likelihood at posterior means
    ###

    if simLogLik:
        print(' ')
        np.random.seed(seed)

        postMean_chOmegaB = np.linalg.cholesky(postMean_OmegaB)
        postMean_chOmegaW = np.linalg.cholesky(postMean_OmegaW)

        _, drawsB = makeNormalDraws(simDraws, nRnd, simLogLikDrawsType, nInd)
        drawsB = np.array(drawsB).reshape((simDraws, nInd, nRnd))
        _, drawsW = makeNormalDraws(simDraws, nRnd, simLogLikDrawsType, nObs)
        drawsW = np.array(drawsW).reshape((simDraws, nObs, nRnd))

        pIndSim = 0
        for i in np.arange(simDraws):
            paramRndB = postMean_zeta + (
                postMean_chOmegaB @ drawsB[i, :, :].T).T
            paramRndB_perObs = np.repeat(paramRndB, obsPerInd, axis=0)

            pObsSim = 0
            for j in np.arange(simDraws):
                paramRndW = paramRndB_perObs + (
                    postMean_chOmegaW @ drawsW[j, :, :].T).T
                pChosen, _ = probMxl(paramRndW, xRnd, xRnd_transBool,
                                     xRnd_trans, rowsPerObs, map_avail_to_obs)
                pObsSim += pChosen
            pObsSim /= simDraws
            pIndSim += np.exp(np.log(pObsSim) @ map_obs_to_ind)

            if ((i + 1) % 50) == 0:
                print('Log-likelihood simulation (inter-ind. draws): ' +
                      str(i + 1))
            sys.stdout.flush()

        pIndSim /= simDraws

        logLik = np.sum(np.log(pIndSim))
        print(' ')
        print('Log-likelihood (simulated at posterior means): ' + str(logLik))
    else:
        logLik = None

    ###
    #Delete draws
    ###

    if deleteDraws:
        for c in range(mcmc_nChain):
            os.remove(modelName + '_draws_chain' + str(c + 1) + '.hdf5')

    ###
    #Save results
    ###

    results = {
        'modelName': modelName,
        'seed': seed,
        'estimation_time': toc,
        'logLik': logLik,
        'postMean_paramRndB': postMean_paramRndB,
        'pdTabPostAna_paramRndB': pdTabPostAna_paramRndB,
        'postMean_zeta': postMean_zeta,
        'pdTabPostAna_zeta': pdTabPostAna_zeta,
        'postMean_sdB': postMean_sdB,
        'pdTabPostAna_sdB': pdTabPostAna_sdB,
        'postMean_OmegaB': postMean_OmegaB,
        'pdTabPostAna_OmegaB': pdTabPostAna_OmegaB,
        'postMean_CorrB': postMean_CorrB,
        'pdTabPostAna_CorrB': pdTabPostAna_CorrB,
        'postMean_sdW': postMean_sdW,
        'pdTabPostAna_sdW': pdTabPostAna_sdW,
        'postMean_OmegaW': postMean_OmegaW,
        'pdTabPostAna_OmegaW': pdTabPostAna_OmegaW,
        'postMean_CorrW': postMean_CorrW,
        'pdTabPostAna_CorrW': pdTabPostAna_CorrW,
    }

    return results
def estimate(
        svb, vb,
        vb_iter, vb_tol, modelName, seed,
        local_iter, local_tol,
        svb_eta, svb_kappa,
        K, N_K, T_K,
        drawsType, nDrawsB, nDrawsW_m,
        paramRndMuB_inits, paramRndSiB_inits,
        paramRndMuW_inits, paramRndSiW_inits,
        zetaMu_inits, zetaSi_inits,
        cK, rK,
        omegaB, psiB_inits, dK_B_inits,
        omegaW, psiW_inits, dK_W_inits,
        mu0Rnd, Sigma0RndInv, nu, A,
        indID, obsID, chosen,
        xRnd, intra):
    
    assert svb or vb, "Method not supported!"
    
    np.random.seed(seed)
    
    if intra:
        local_iter = 1
    
    ###
    #Prepare data
    ###
    
    (xRnd,
     nInd, nObs, obsPerInd, nAlt, nRnd,
     chosenIdx, nonChosenIdx) = prepareData_vb(xRnd, indID, obsID, chosen)
    
    chRndIdx = np.triu_indices(nRnd); chRndIdx = chRndIdx[1], chRndIdx[0];
    chRndIdxDiagAux = np.ones((nRnd, nRnd), dtype = 'int64')
    chRndIdxDiagAux[chRndIdx] = np.arange((nRnd * (nRnd + 1) / 2))
    chRndIdxDiag = np.diag(chRndIdxDiagAux)
    
    tic = time.time()
    
    ###
    #SVB/VB
    ###
    
    #Initialise
    zetaMu = zetaMu_inits.copy()
    zetaSi = zetaSi_inits.copy()
    psiB = psiB_inits.copy()
    psiBInv = np.linalg.inv(psiB)
    dK_B = dK_B_inits.copy()
    psiW = psiW_inits.copy()
    psiWInv = np.linalg.inv(psiW)
    dK_W = dK_W_inits.copy()
    
    if svb:
        
        ### 
        #Generate draws
        ###
        
        if not intra: 
            nDrawsW_m = 1
        nDrawsW = nDrawsB * nDrawsW_m
        
        drawsB = makeNormalDraws(nDrawsB, nRnd, drawsType, N_K)[0]
        if intra:
            drawsW = makeNormalDraws(nDrawsW, nRnd, drawsType, N_K * T_K)[0]
            drawsW = drawsW.reshape((N_K, T_K, nDrawsW, nRnd))
        else:
            drawsW = np.zeros((N_K, T_K, nDrawsW, nRnd))
        
        ### 
        #Coordinate Ascent
        ###
    
        (zetaMu, zetaSi, 
         psiB, dK_B, 
         psiW, dK_W,
         iters, parChange) = coordinateAscent_svb(
                 vb_iter, vb_tol,
                 svb_eta, svb_kappa,
                 local_iter, local_tol,
                 zetaMu, zetaSi,
                 psiB, psiBInv, omegaB, dK_B,
                 psiW, psiWInv, omegaW, dK_W,
                 nu, cK, rK,
                 mu0Rnd, Sigma0RndInv,
                 xRnd, nRnd, chRndIdx, chRndIdxDiag, intra,
                 nInd, obsPerInd, nAlt,
                 K[0], N_K, T_K,
                 drawsB, nDrawsB,
                 drawsW, nDrawsW, nDrawsW_m)    
    
    if vb:   
        ### 
        #Generate draws
        ###
        
        if not intra: 
            nDrawsW_m = 1
        nDrawsW = nDrawsB * nDrawsW_m
        
        drawsB = makeNormalDraws(nDrawsB, nRnd, drawsType, nInd)[0]
        if intra:
            drawsW = makeNormalDraws(nDrawsW, nRnd, drawsType, nObs)[0]
            drawsW = drawsW.reshape((nInd, obsPerInd, nDrawsW, nRnd))
        else:
            drawsW = np.zeros((nInd, obsPerInd, nDrawsW, nRnd))
        
        ### 
        #Coordinate Ascent
        ###
        
        paramRndMuB = paramRndMuB_inits.copy()
        paramRndSiB = paramRndSiB_inits.copy()
        paramRndChB = np.linalg.cholesky(paramRndSiB)
        paramRndMuW = paramRndMuW_inits.copy().reshape((nInd, obsPerInd, nRnd))
        paramRndSiW = paramRndSiW_inits.copy().reshape((nInd, obsPerInd, nRnd, nRnd))
        paramRndChW = np.linalg.cholesky(paramRndSiW)
    
        (paramRndMuW, paramRndSiW,
         paramRndMuB, paramRndSiB,
         zetaMu, zetaSi, 
         psiB, dK_B, 
         psiW, dK_W,
         iters, parChange) = coordinateAscent_vb(
                 vb_iter, vb_tol,
                 local_iter, local_tol,
                 paramRndMuB, paramRndChB, paramRndSiB,
                 paramRndMuW, paramRndChW, paramRndSiW,
                 zetaMu, zetaSi,
                 psiB, psiBInv, omegaB, dK_B,
                 psiW, psiWInv, omegaW, dK_W,
                 nu, cK, rK,
                 mu0Rnd, Sigma0RndInv,
                 xRnd, nRnd, chRndIdx, chRndIdxDiag, intra,
                 nInd, obsPerInd, nAlt,
                 K[1], int(nInd / K[1]),
                 drawsB, nDrawsB,
                 drawsW, nDrawsW, nDrawsW_m)
    
    ###
    #Estimation time
    ###    
    
    toc = time.time() - tic
    
    print(' ')
    print('Computation time [s]: ' + str(toc))
    
    ###
    #Save results
    ###
    
    results = {'modelName': modelName,
               'estimation_time': toc, 'iters': iters, 'termTol': parChange,
               'paramRndMuB': paramRndMuB, 'paramRndSiB': paramRndSiB,
               'paramRndMuW': paramRndMuW, 'paramRndSiW': paramRndSiW,
               'zetaMu': zetaMu, 'zetaSi': zetaSi, 
               'psiB': psiB, 'dK_B': dK_B, 'omegaB': omegaB,
               'psiW': psiW, 'dK_W': dK_W, 'omegaW': omegaW,
               }
        
    return results
def estimate(drawsType, nDrawsB, nTakesB, nDrawsW, nTakesW, K, seed, modelName,
             deleteDraws, paramFix_inits, paramRndUc_mu_inits,
             paramRndUc_sd_inits, paramRndCo_mu_inits, paramRndCo_ch_inits,
             paramRnd2Uc_mu_inits, paramRnd2Uc_sdB_inits,
             paramRnd2Uc_sdW_inits, paramRnd2Co_mu_inits,
             paramRnd2Co_chB_inits, paramRnd2Co_chW_inits, indID, obsID, altID,
             chosen, xFix, xRndUc, xRndCo, xRnd2Uc, xRnd2Co, xFix_trans,
             xRndUc_trans, xRndCo_trans, xRnd2Uc_trans, xRnd2Co_trans):

    np.random.seed(seed)

    ###
    #Prepare data
    ###

    nFix = xFix.shape[1]
    nRndUc = xRndUc.shape[1]
    nRndCo = xRndCo.shape[1]
    nRnd = nRndUc + nRndCo
    nRnd2Uc = xRnd2Uc.shape[1]
    nRnd2Co = xRnd2Co.shape[1]
    nRnd2 = nRnd2Uc + nRnd2Co

    if nRnd == 0 and nRnd2 == 0:
        nDrawsB = 1
        nDrawsBMem = 1
        nTakesB = 1

        nDrawsW = 1
        nDrawsWMem = 1
        nTakesW = 1
    if nRnd > 0 and nRnd2 == 0:
        K = 1
        nDrawsBMem, mod = divmod(nDrawsB, nTakesB)
        assert mod == 0, "nDrawsB is not multiple of nTakesB!"
        nDrawsW, nDrawsWMem, nTakesW = 1, 1, 1
    if nRnd2 > 0:
        #If there are random parameters with intra-individual heterogeneity,
        #nDrawsBMem is necessarily one
        nDrawsBMem = 1
        nTakesB = int(nDrawsB / K)
        nDrawsWMem, mod = divmod(nDrawsW, nTakesW)
        assert mod == 0, "nDrawsW is not multiple of nTakesW!"

    nDrawsMem = nDrawsBMem * nDrawsWMem

    xFix_transBool = np.sum(xFix_trans) > 0
    xRndUc_transBool = np.sum(xRndUc_trans) > 0
    xRndCo_transBool = np.sum(xRndCo_trans) > 0
    xRnd2Uc_transBool = np.sum(xRnd2Uc_trans) > 0
    xRnd2Co_transBool = np.sum(xRnd2Co_trans) > 0

    xList = [xFix, xRndUc, xRndCo, xRnd2Uc, xRnd2Co]
    (xList, nInd, nObs, nRow, chosenIdx, nonChosenIdx, rowsPerInd, rowsPerObs,
     map_obs_to_ind, map_avail_to_obs) = prepareData(xList, indID, obsID,
                                                     chosen)
    xFix, xRndUc, xRndCo, xRnd2Uc, xRnd2Co = xList[0], xList[1], xList[
        2], xList[3], xList[4]

    sim_xFix = np.tile(xFix, (nDrawsMem, 1))
    sim_xRndUc = np.tile(xRndUc, (nDrawsMem, 1))
    sim_xRndCo = np.tile(xRndCo, (nDrawsMem, 1))
    sim_xRnd2Uc = np.tile(xRnd2Uc, (nDrawsMem, 1))
    sim_xRnd2Co = np.tile(xRnd2Co, (nDrawsMem, 1))  #Good

    obsPerInd = np.array(map_obs_to_ind.sum(axis=0)).reshape((-1, ))
    simB_obsPerInd = np.tile(obsPerInd, (nDrawsBMem, ))
    simB_rowsPerInd = np.tile(rowsPerInd, (nDrawsBMem, ))
    simW_rowsPerObs = np.tile(rowsPerObs, (nDrawsWMem, ))  #Good

    simB_map_obs_to_ind = scipy.sparse.kron(scipy.sparse.eye(nDrawsBMem),
                                            map_obs_to_ind)
    simW_map_avail_to_obs = scipy.sparse.kron(scipy.sparse.eye(nDrawsMem),
                                              map_avail_to_obs)
    simB_map_draws_to_ind = scipy.sparse.hstack(
        [scipy.sparse.eye(nInd) for i in np.arange(nDrawsBMem)])

    map_obs_to_obs = scipy.sparse.eye(nObs)
    map_aux = scipy.sparse.csr_matrix(np.ones((nDrawsWMem, 1)), dtype='int64')
    simW_map_drawsW_to_obs = scipy.sparse.kron(map_aux, map_obs_to_obs)
    simW_map_drawsB_to_obs = scipy.sparse.kron(np.eye(nDrawsBMem),
                                               simW_map_drawsW_to_obs).T

    chIdx = None
    if nRndCo:
        chIdx = np.triu_indices(nRndCo)
        chIdx = chIdx[1], chIdx[0]

    ch2Idx = None
    if nRnd2Co:
        ch2Idx = np.triu_indices(nRnd2Co)
        ch2Idx = ch2Idx[1], ch2Idx[0]

    ###
    #Generate draws
    ###

    drawsUc = np.zeros((K, 1, 1, 1))
    drawsCo = np.zeros((K, 1, 1, 1))
    drawsUcB = np.zeros((K, 1, 1, 1))
    drawsUcW = np.zeros((K, 1, 1, 1))
    drawsCoB = np.zeros((K, 1, 1, 1))
    drawsCoW = np.zeros((K, 1, 1, 1))
    if nRndUc:
        _, drawsUc = makeNormalDraws(nDrawsB, nRndUc, drawsType, nInd)
        drawsUc = drawsUc.reshape(
            (K, nTakesB, int(nInd * nDrawsB / K / nTakesB), nRndCo))
    if nRndCo:
        _, drawsCo = makeNormalDraws(nDrawsB, nRndCo, drawsType, nInd)
        drawsCo = drawsCo.reshape(
            (K, nTakesB, int(nInd * nDrawsB / K / nTakesB), nRndCo))
    if nRnd2Uc:
        _, drawsUcB = makeNormalDraws(nDrawsB, nRnd2Uc, drawsType, nInd)
        _, drawsUcW = makeNormalDraws(nDrawsW, nRnd2Uc, drawsType, nObs)
        drawsUcB = drawsUcB.reshape(
            (K, nTakesB, int(nInd * nDrawsB / K / nTakesB), nRnd2Uc))
        drawsUcW = drawsUcW.reshape(
            (nTakesW, int(nObs * nDrawsW / nTakesW), nRnd2Uc))
    if nRnd2Co:
        _, drawsCoB = makeNormalDraws(nDrawsB, nRnd2Co, drawsType, nInd)
        _, drawsCoW = makeNormalDraws(nDrawsW, nRnd2Co, drawsType, nObs)
        drawsCoB = drawsCoB.reshape(
            (K, nTakesB, int(nInd * nDrawsB / K / nTakesB), nRnd2Co))
        drawsCoW = drawsCoW.reshape(
            (nTakesW, int(nObs * nDrawsW / nTakesW), nRnd2Co))

    ###
    #Optimise
    ###

    paramRndCo_chVec_inits = np.ndarray.flatten(paramRndCo_ch_inits[chIdx])
    paramRnd2Co_chBVec_inits = np.ndarray.flatten(
        paramRnd2Co_chB_inits[ch2Idx])
    paramRnd2Co_chWVec_inits = np.ndarray.flatten(
        paramRnd2Co_chW_inits[ch2Idx])
    inits = np.concatenate(
        (paramFix_inits, paramRndUc_mu_inits, paramRndUc_sd_inits,
         paramRndCo_mu_inits, paramRndCo_chVec_inits, paramRnd2Uc_mu_inits,
         paramRnd2Uc_sdB_inits, paramRnd2Uc_sdW_inits, paramRnd2Co_mu_inits,
         paramRnd2Co_chBVec_inits, paramRnd2Co_chWVec_inits),
        axis=0)

    tic = time.time()
    algo = 'L-BFGS-B'
    resOpt = sp.optimize.minimize(
        fun=objectiveMxl,
        x0=inits,
        args=(False, K, sim_xFix, xFix_transBool, xFix_trans, nFix, sim_xRndUc,
              xRndUc_transBool, xRndUc_trans, nRndUc, sim_xRndCo,
              xRndCo_transBool, xRndCo_trans, nRndCo, chIdx, sim_xRnd2Uc,
              xRnd2Uc_transBool, xRnd2Uc_trans, nRnd2Uc, sim_xRnd2Co,
              xRnd2Co_transBool, xRnd2Co_trans, nRnd2Co, ch2Idx, drawsUc,
              drawsCo, nDrawsMem, drawsUcB, drawsCoB, nDrawsB, nDrawsBMem,
              nTakesB, drawsUcW, drawsCoW, nDrawsW, nDrawsWMem, nTakesW, nInd,
              nObs, simB_rowsPerInd, simB_obsPerInd, simW_rowsPerObs,
              simB_map_obs_to_ind, simB_map_draws_to_ind,
              simW_map_avail_to_obs, simW_map_drawsB_to_obs),
        method=algo,
        jac=True,
        options={'disp': True})
    toc = time.time() - tic

    print(' ')
    print('Computation time [s]: ' + str(toc))

    ###
    #Process output
    ###

    logLik = -resOpt['fun']
    est = resOpt['x']

    if algo == 'BFGS':
        iHess = resOpt['hess_inv']
    if algo == 'L-BFGS-B':
        ll, gr, bhhh = objectiveMxl(
            est, True, K, sim_xFix, xFix_transBool, xFix_trans, nFix,
            sim_xRndUc, xRndUc_transBool, xRndUc_trans, nRndUc, sim_xRndCo,
            xRndCo_transBool, xRndCo_trans, nRndCo, chIdx, sim_xRnd2Uc,
            xRnd2Uc_transBool, xRnd2Uc_trans, nRnd2Uc, sim_xRnd2Co,
            xRnd2Co_transBool, xRnd2Co_trans, nRnd2Co, ch2Idx, drawsUc,
            drawsCo, nDrawsMem, drawsUcB, drawsCoB, nDrawsB, nDrawsBMem,
            nTakesB, drawsUcW, drawsCoW, nDrawsW, nDrawsWMem, nTakesW, nInd,
            nObs, simB_rowsPerInd, simB_obsPerInd, simW_rowsPerObs,
            simB_map_obs_to_ind, simB_map_draws_to_ind, simW_map_avail_to_obs,
            simW_map_drawsB_to_obs)
        iHess = np.linalg.inv(bhhh)

    se = np.sqrt(np.diag(iHess))
    zVal = est / se
    pVal = 2 * scipy.stats.norm.cdf(-np.absolute(zVal))

    u = 0
    if nFix > 0:
        l = u
        u += nFix
        lu = slice(l, u)
        print(' ')
        print('Fixed parameters:')
        paramFix_est, paramFix_se, paramFix_zVal, paramFix_pVal, pd_paramFix = processOutput(
            est, se, zVal, pVal, lu)
    else:
        paramFix_est, paramFix_se, paramFix_zVal, paramFix_pVal, pd_paramFix = None, None, None, None, None

    if nRndUc > 0:
        l = u
        u += nRndUc
        lu = slice(l, u)
        print(' ')
        print('Uncorrelated random parameters (means):')
        paramRndUc_mu_est, paramRndUc_mu_se, paramRndUc_mu_zVal, paramRndUc_mu_pVal, pd_paramRndUc_mu = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u += nRndUc
        lu = slice(l, u)
        print(' ')
        print('Uncorrelated random parameters (standard deviations):')
        paramRndUc_sd_est, paramRndUc_sd_se, paramRndUc_sd_zVal, paramRndUc_sd_pVal, pd_paramRndUc_sd = processOutput(
            est, se, zVal, pVal, lu)
    else:
        paramRndUc_mu_est, paramRndUc_mu_se, paramRndUc_mu_zVal, paramRndUc_mu_pVal, pd_paramRndUc_mu = None, None, None, None, None
        paramRndUc_sd_est, paramRndUc_sd_se, paramRndUc_sd_zVal, paramRndUc_sd_pVal, pd_paramRndUc_sd = None, None, None, None, None

    if nRndCo > 0:
        l = u
        u += nRndCo
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters (means):')
        paramRndCo_mu_est, paramRndCo_mu_se, paramRndCo_mu_zVal, paramRndCo_mu_pVal, pd_paramRndCo_mu = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u += int((nRndCo * (nRndCo + 1)) / 2)
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters (Cholesky):')
        paramRndCo_ch_est_vec, paramRndCo_ch_se_vec, paramRndCo_ch_zVal_vec, paramRndCo_ch_pVal_vec, pd_paramRndCo_ch = processOutput(
            est, se, zVal, pVal, lu)

        print(' ')
        print('Correlated random parameters (Cholesky, est.):')
        paramRndCo_ch_est = np.zeros((nRndCo, nRndCo))
        paramRndCo_ch_est[chIdx] = paramRndCo_ch_est_vec
        print(pd.DataFrame(paramRndCo_ch_est))

        print(' ')
        print('Correlated random parameters (Cholesky, std. err.):')
        paramRndCo_ch_se = np.zeros((nRndCo, nRndCo))
        paramRndCo_ch_se[chIdx] = paramRndCo_ch_se_vec
        print(pd.DataFrame(paramRndCo_ch_se))

        print(' ')
        print('Correlated random parameters (Cholesky, p-val.):')
        paramRndCo_ch_pVal = np.zeros((nRndCo, nRndCo))
        paramRndCo_ch_pVal[chIdx] = paramRndCo_ch_pVal_vec
        print(pd.DataFrame(paramRndCo_ch_pVal))
    else:
        paramRndCo_mu_est, paramRndCo_mu_se, paramRndCo_mu_zVal, paramRndCo_mu_pVal, pd_paramRndCo_mu = None, None, None, None, None
        paramRndCo_ch_est, paramRndCo_ch_se, paramRndCo_ch_pVal, pd_paramRndCo_ch = None, None, None, None

    if nRnd2Uc > 0:
        l = u
        u += nRnd2Uc
        lu = slice(l, u)
        print(' ')
        print('Uncorrelated random parameters - intra (means):')
        paramRnd2Uc_mu_est, paramRnd2Uc_mu_se, paramRnd2Uc_mu_zVal, paramRnd2Uc_mu_pVal, pd_paramRnd2Uc_mu = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u += nRnd2Uc
        lu = slice(l, u)
        print(' ')
        print(
            'Uncorrelated random parameters - intra-between (standard deviations):'
        )
        paramRnd2Uc_sdB_est, paramRnd2Uc_sdB_se, paramRnd2Uc_sdB_zVal, paramRnd2Uc_sdB_pVal, pd_paramRnd2Uc_sdB = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u += nRnd2Uc
        lu = slice(l, u)
        print(' ')
        print(
            'Uncorrelated random parameters - intra-within (standard deviations):'
        )
        paramRnd2Uc_sdW_est, paramRnd2Uc_sdW_se, paramRnd2Uc_sdW_zVal, paramRnd2Uc_sdW_pVal, pd_paramRnd2Uc_sdW = processOutput(
            est, se, zVal, pVal, lu)
    else:
        paramRnd2Uc_mu_est, paramRnd2Uc_mu_se, paramRnd2Uc_mu_zVal, paramRnd2Uc_mu_pVal, pd_paramRnd2Uc_mu = None, None, None, None, None
        paramRnd2Uc_sdB_est, paramRnd2Uc_sdB_se, paramRnd2Uc_sdB_zVal, paramRnd2Uc_sdB_pVal, pd_paramRnd2Uc_sdB = None, None, None, None, None
        paramRnd2Uc_sdW_est, paramRnd2Uc_sdW_se, paramRnd2Uc_sdW_zVal, paramRnd2Uc_sdW_pVal, pd_paramRnd2Uc_sdW = None, None, None, None, None

    if nRnd2Co > 0:
        l = u
        u += nRnd2Co
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters - intra (means):')
        paramRnd2Co_mu_est, paramRnd2Co_mu_se, paramRnd2Co_mu_zVal, paramRnd2Co_mu_pVal, pd_paramRnd2Co_mu = processOutput(
            est, se, zVal, pVal, lu)

        l = u
        u += int((nRnd2Co * (nRnd2Co + 1)) / 2)
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters - intra-between (Cholesky):')
        paramRnd2Co_chB_est_vec, paramRnd2Co_chB_se_vec, paramRnd2Co_chB_zVal_vec, paramRnd2Co_chB_pVal_vec, pd_paramRnd2Co_chB = processOutput(
            est, se, zVal, pVal, lu)

        print(' ')
        print('Correlated random parameters - intra-between (Cholesky, est.):')
        paramRnd2Co_chB_est = np.zeros((nRnd2Co, nRnd2Co))
        paramRnd2Co_chB_est[ch2Idx] = paramRnd2Co_chB_est_vec
        print(pd.DataFrame(paramRnd2Co_chB_est))

        print(' ')
        print(
            'Correlated random parameters - intra-between (Cholesky, std. err.):'
        )
        paramRnd2Co_chB_se = np.zeros((nRnd2Co, nRnd2Co))
        paramRnd2Co_chB_se[ch2Idx] = paramRnd2Co_chB_se_vec
        print(pd.DataFrame(paramRnd2Co_chB_se))

        print(' ')
        print(
            'Correlated random parameters - intra-between (Cholesky, p-val.):')
        paramRnd2Co_chB_pVal = np.zeros((nRnd2Co, nRnd2Co))
        paramRnd2Co_chB_pVal[ch2Idx] = paramRnd2Co_chB_pVal_vec
        print(pd.DataFrame(paramRnd2Co_chB_pVal))

        l = u
        u += int((nRnd2Co * (nRnd2Co + 1)) / 2)
        lu = slice(l, u)
        print(' ')
        print('Correlated random parameters - intra-within (Cholesky):')
        paramRnd2Co_chW_est_vec, paramRnd2Co_chW_se_vec, paramRnd2Co_chW_zVal_vec, paramRnd2Co_chW_pVal_vec, pd_paramRnd2Co_chW = processOutput(
            est, se, zVal, pVal, lu)

        print(' ')
        print('Correlated random parameters - intra-within (Cholesky, est.):')
        paramRnd2Co_chW_est = np.zeros((nRnd2Co, nRnd2Co))
        paramRnd2Co_chW_est[ch2Idx] = paramRnd2Co_chW_est_vec
        print(pd.DataFrame(paramRnd2Co_chW_est))

        print(' ')
        print(
            'Correlated random parameters - intra-within (Cholesky, std. err.):'
        )
        paramRnd2Co_chW_se = np.zeros((nRnd2Co, nRnd2Co))
        paramRnd2Co_chW_se[ch2Idx] = paramRnd2Co_chW_se_vec
        print(pd.DataFrame(paramRnd2Co_chW_se))

        print(' ')
        print(
            'Correlated random parameters - intra-within (Cholesky, p-val.):')
        paramRnd2Co_chW_pVal = np.zeros((nRnd2Co, nRnd2Co))
        paramRnd2Co_chW_pVal[ch2Idx] = paramRnd2Co_chW_pVal_vec
        print(pd.DataFrame(paramRnd2Co_chW_pVal))
    else:
        paramRnd2Co_mu_est, paramRnd2Co_mu_se, paramRnd2Co_mu_zVal, paramRnd2Co_mu_pVal, pd_paramRnd2Co_mu = None, None, None, None, None
        paramRnd2Co_chB_est, paramRnd2Co_chB_se, paramRnd2Co_chB_pVal, pd_paramRnd2Co_chB = None, None, None, None
        paramRnd2Co_chW_est, paramRnd2Co_chW_se, paramRnd2Co_chW_pVal, pd_paramRnd2Co_chW = None, None, None, None

    print(' ')
    print('Log-likelihood: ' + str(logLik))
    print(' ')

    if nRnd:
        print('QMC method: ' + drawsType)
        print('Number of simulation draws: ' + str(nDrawsB) + ' (inter); ' +
              str(nDrawsW) + ' (intra)')

    ###
    #Delete draws
    ###

    if deleteDraws:
        drawsUc = None
        drawsCo = None
        drawsUcB = None
        drawsCoB = None
        drawsUcW = None
        drawsCoW = None

    ###
    #Save results
    ###

    results = {
        'modelName': modelName,
        'seed': seed,
        'estimation_time': toc,
        'drawsType': drawsType,
        'nDraws_inter': nDrawsB,
        'nDraws_intra': nDrawsW,
        'drawsUc': drawsUc,
        'drawsCo': drawsCo,
        'logLik': logLik,
        'est': est,
        'iHess': iHess,
        'paramFix_est': paramFix_est,
        'paramFix_se': paramFix_se,
        'paramFix_zVal': paramFix_zVal,
        'paramFix_pVal': paramFix_pVal,
        'pd_paramFix': pd_paramFix,
        'paramRndUc_mu_est': paramRndUc_mu_est,
        'paramRndUc_mu_se': paramRndUc_mu_se,
        'paramRndUc_mu_zVal': paramRndUc_mu_zVal,
        'paramRndUc_mu_pVal': paramRndUc_mu_pVal,
        'pd_paramRndUc_mu': pd_paramRndUc_mu,
        'paramRndUc_sd_est': paramRndUc_sd_est,
        'paramRndUc_sd_se': paramRndUc_sd_se,
        'paramRndUc_sd_zVal': paramRndUc_sd_zVal,
        'paramRndUc_sd_pVal': paramRndUc_sd_pVal,
        'pd_paramRndUc_sd': pd_paramRndUc_sd,
        'paramRndCo_mu_est': paramRndCo_mu_est,
        'paramRndCo_mu_se': paramRndCo_mu_se,
        'paramRndCo_mu_zVal': paramRndCo_mu_zVal,
        'paramRndCo_mu_pVal': paramRndCo_mu_pVal,
        'pd_paramRndCo_mu': pd_paramRndCo_mu,
        'paramRndCo_ch_est': paramRndCo_ch_est,
        'paramRndCo_ch_se': paramRndCo_ch_se,
        'paramRndCo_ch_pVal': paramRndCo_ch_pVal,
        'pd_paramRndCo_ch': pd_paramRndCo_ch,
        'paramRnd2Uc_mu_est': paramRnd2Uc_mu_est,
        'paramRnd2Uc_mu_se': paramRnd2Uc_mu_se,
        'paramRnd2Uc_mu_zVal': paramRnd2Uc_mu_zVal,
        'paramRnd2Uc_mu_pVal': paramRnd2Uc_mu_pVal,
        'pd_paramRnd2Uc_mu': pd_paramRnd2Uc_mu,
        'paramRnd2Uc_sdB_est': paramRnd2Uc_sdB_est,
        'paramRnd2Uc_sdB_se': paramRnd2Uc_sdB_se,
        'paramRnd2Uc_sdB_zVal': paramRnd2Uc_sdB_zVal,
        'paramRnd2Uc_sdB_pVal': paramRnd2Uc_sdB_pVal,
        'pd_paramRnd2Uc_sdB': pd_paramRnd2Uc_sdB,
        'paramRnd2Uc_sdW_est': paramRnd2Uc_sdW_est,
        'paramRnd2Uc_sdW_se': paramRnd2Uc_sdW_se,
        'paramRnd2Uc_sdW_zVal': paramRnd2Uc_sdW_zVal,
        'paramRnd2Uc_sdW_pVal': paramRnd2Uc_sdW_pVal,
        'pd_paramRnd2Uc_sdW': pd_paramRnd2Uc_sdW,
        'paramRnd2Co_mu_est': paramRnd2Co_mu_est,
        'paramRnd2Co_mu_se': paramRnd2Co_mu_se,
        'paramRnd2Co_mu_zVal': paramRnd2Co_mu_zVal,
        'paramRnd2Co_mu_pVal': paramRnd2Co_mu_pVal,
        'pd_paramRnd2Co_mu': pd_paramRnd2Co_mu,
        'paramRnd2Co_chB_est': paramRnd2Co_chB_est,
        'paramRnd2Co_chB_se': paramRnd2Co_chB_se,
        'paramRnd2Co_chB_pVal': paramRnd2Co_chB_pVal,
        'pd_paramRnd2Co_chB': pd_paramRnd2Co_chB,
        'paramRnd2Co_chW_est': paramRnd2Co_chW_est,
        'paramRnd2Co_chW_se': paramRnd2Co_chW_se,
        'paramRnd2Co_chW_pVal': paramRnd2Co_chW_pVal,
        'pd_paramRnd2Co_chW': pd_paramRnd2Co_chW,
        'resOpt': resOpt
    }

    return results