*! version 1.0.1 20feb2024 I I Bolotov program define tmpinvi, eclass byable(recall) version 16.0 /* The program implements an iterated (multistep) Transaction Matrix (TM)- specific LPLS estimator for linear programming with the help of the Moore-Penrose inverse (pseudoinverse), calculated using singular value decomposition (SVD). Estimation using 2x2 to 50x50 contiguous submatrices, repeated with compensatory slack variables until NRMSE is minimized in a given number of iterations, is followed by an F-test from linear regression/t-test of mean NRMSE from a pre-simulated distribution (Monte-Carlo, 50,000 iterations with matrices consisting of normal random variates). The result is adjusted for extreme values to match the RHS via shares of estimated row/column sums if the corresponding option is specified. Author: Ilya Bolotov, MBA, Ph.D. Date: 30 September 2022 */ tempname t F v s title rspec cspec // check for third-party packages from SSC cap which moremata.hlp if _rc { di as err "installing {helpb moremata} (dependency)" ssc install moremata } cap which tmpinv if _rc { di as err "installing {helpb tmpinv} (dependency)" ssc install tmpinv } // syntax syntax /// anything [if] [in] [, /// Values(string) Slackvars(string) ZERODiagonal /// SUBMatrix(int 2) /// Lowerbound(numlist max=1 miss) Upperbound(numlist max=1 miss) /// Round(real 8e-307) PENalization /// STEPNumber(int 2) * /// ] // adjust and preprocess options if "`lowerbound'" == "" loc lowerbound = . if "`upperbound'" == "" loc upperbound = . // perform the pinv estimation via SVD mata: printf("\n----+--- 1 ---+--- 2 ---+--- 3 ---+--- 4 ---+--- 5\n"); /// f = (f=trunc(floatround(log10(`stepnumber')))) + /// trunc(floatround(f / 3)) + 2 sca `t' = clock(c(current_date) + " " + c(current_time), "DMYhms") forv i = 0/`stepnumber' { /* break if converged */ if `i' >= 1 { mata: printf("."); displayflush(); /// if (! mod(`i', 50)) printf("%"+strofreal(f)+".0fc\n", `i');; if `i' == 1 mata: r2 = J( 1,0,.); /// v = J(length(st_matrix("r(solution)")),0,.) mata: r2 = r2,st_numscalar("r(r2_c)"); /// if ("`zerodiagonal'" != "") {; /// x = st_matrix("r(solution)"); _diag(x, 0); /// st_matrix("r(solution)", x); }; /// v = v ,colshape(st_matrix("r(solution)"), 1); /// if ((r2[1] + r2[cols(r2)]) < . & r2[cols(r2)] <= r2[1] & /// `i' > 1 | `i' == `stepnumber') {; /// st_matrix("r(solution)", /// colshape(v[.,selectindex(r2 :== max(r2))[1]], /// cols(st_matrix("r(solution)")))); /// st_local("`F'", "True"); }; /// st_matrix("`v'", colshape(st_matrix("r(solution)"), 1)); /// if ("`penalization'" != "" & /// missing(st_matrix("r(solution)"))) {; /// st_matrix("`s'", ((rowmissing(st_matrix("r(solution)")) \ /// J(max((rows(st_matrix("r(solution)")), /// cols(st_matrix("r(solution)")))) /// - rows(st_matrix("r(solution)")), /// 1,.)), /// (colmissing(st_matrix("r(solution)"))'\ /// J(max((rows(st_matrix("r(solution)")), /// cols(st_matrix("r(solution)")))) /// - cols(st_matrix("r(solution)")), /// 1,.)))); /// st_local("slackvars", "`s'"); }; loc values `v' } qui tmpinv `anything' `if' `in', v(`values') s(`slackvars') /// `=cond(! `i',"`zerodiagonal'","")' /// subm(`submatrix') `options' **** if `lowerbound' < . { mata: st_matrix("r(solution)", mm_cond( /// round(st_matrix("r(solution)"), `round') :< /// J(rows(st_matrix("r(solution)")), 1, `lowerbound'), ., /// st_matrix("r(solution)"))) } if `upperbound' < . { mata: st_matrix("r(solution)", mm_cond( /// round(st_matrix("r(solution)"), `round') :> /// J(rows(st_matrix("r(solution)")), 1, `upperbound'), ., /// st_matrix("r(solution)"))) } if `i' >= 1 & "``F''" != "" continue, break } sca `t' = clock(c(current_date) + " " + c(current_time), "DMYhms") - `t' mata: mata drop f r2 v x // print output if r(r2_c) < . { di _n as txt /// _n "Converged in `=round(`t'/1000, 0.001)'s, solution stored" /// _c " in{stata ret li: r(solution)}" _n } else { di _n as err /// _n "Not converged, solution stored" /// _c " in{stata ret li: r(solution)}" _n } cap confirm mat r(tests) if ! _rc { loc `title' = cond(`submatrix' <= 2, "F-test, linear regression:", /// "t-tests of mean NRMSE in submatrices, MC sample (50,000):") loc `rspec' = "& - `= "& " * rowsof(r(tests))'" loc `cspec' = "& %2.0f & %2.0f | %9.0g | " + cond(`submatrix' <= 2, /// "%12.8f | %8.4f & %10.2g & %5.0f & %5.0f & %5.4f &", /// "%12.8f | %8.4f & %8.0g & %5.4f & %5.4f & %5.4f &") di as res _n "``title''" matlist r(tests), names(col) rspec(``rspec'') cspec(``cspec'') } cap confirm mat r(nrmse_dist) if ! _rc & "`distribution'" != "" { di as res _n "percentiles, MC sample (50,000):" loc `rspec' = "& - `= "& " * rowsof(r(nrmse_dist))'" loc `cspec' = "`= "& %6.2f " * 10'&" matlist r(nrmse_dist), names(col) rspec(``rspec'') cspec(``cspec'') } end