************** Start of program

capture program drop calibrate
*! calibrate V2.1 JCD'Souza 2Feb2011
program define calibrate, rclass
version 10.1
syntax  , MARginals(varlist) POPTot(string) ENTrywt(string) EXITwt(string) ///
 [PRInt(string) GRAPHs(string) QVar(string) METHod(string)  ///
 SAMPvars(varlist) outc(string) TOLerance(real 0.000001) maxit(int 15) UBound(real 5) LBound(real 0.2)]



************** Check the input is correctly defined


* The marginals and entrywt should be numeric
confirm numeric variable `marginals' `entrywt'


* Check poptot is a matrix and print, graphs, and method  take correct values
capture confirm matrix `poptot'
if _rc!=0 {
	display in red  "Matrix required for poptot"
	exit _rc
}

if "`print'" != "" & "`print'" != "all" & "`print'" != "final" & "`print'" != "none" {
	display in red  "Options for print are all, final or none"
	exit
}


if "`graphs'" != "" & "`graphs'" != "all" & "`graphs'" != "final" & "`graphs'" != "none" {
	display in red  "Options for graphs are all, final or none"
	exit
}


if "`method'" != "" & "`method'" != "linear" & "`method'" != "logistic" & ///
"`method'" != "nrSS" & "`method'" != "nr2A" & "`method'" != "nr2B" & "`method'" != "nr2C" &"`method'" != "blinear" {
	display in red  "Options for method are linear, logistic, blinear, nrSS, nr2A, nr2B or nr2C "
	exit
}

if "`method'" == "nrSS" | "`method'" == "nr2A" | "`method'" == "nr2B" | "`method'" == "nr2C" {
	if "`sampvars'"=="" | "`outc'"=="" {
		display in red  "Nonresponse methods require the options sampvars and outc"
		exit
	}
	confirm numeric variable `sampvars'
	capture assert `outc'==1 | `outc'== 0
	if _rc!=0{
		display in red  "`outc' must be binary"
		exit
	}
}



* If qvar is present it should be a numerical variable. Otherwise set it to 1.
if "`qvar'" != "" {
	confirm numeric variable `qvar'
	}
	else {
		tempvar qvar
		qui: gen double `qvar'=1
}


* If method = blinear then ubound must be >1 and lbound must be <1
if "`method'" == "blinear" & (`lbound'>=1 | `ubound'<=1) {
	display in red "lbound must be < 1 and ubound must be > 1"
	exit
}


* Check that the entry weight is non-negative
capture assert (`entrywt'>=0 )
if _rc!=0{
	display in red  "Note: `entrywt' is not a non-negative variable"
	exit
}




************** Set up variables

tempname M delta sampest selest est Phat entdeff exitdeff sclmin sclmax sclsd result InvM det nrows ncols
tempvar entryq tempwt

qui: gen double `tempwt'=`entrywt'


* Count the number of star variables (using Sarndal's terminology)
local nstar: word count `marginals'

* Check that the dimensions of the matrix are consistent with the number of marginals

scalar `nrows'=rowsof(`poptot')
scalar `ncols'=colsof(`poptot')

if (`nstar'!=`ncols' | `nrows'!=1) {
	display in red  "`poptot' is the wrong size"
	exit _rc
}







************** Method = linear
if ("`method'" == "") | ("`method'" =="linear") {
	qui: gen double `entryq'=`entrywt'*`qvar'
	qui: matrix accum `M'=`marginals' [iweight=`entryq'], noconstant   // Make the matrix

	qui: matrix vecaccum `Phat'=`entrywt' `marginals', noconstant    // P-hat is the matrix of estimates
	matrix `InvM'=invsym(`M')
	scalar `det'=det(`InvM')
	if `det'==0 {
		display in red "Matrix is singular -- check results carefully"
	}


	matrix `delta'=(`InvM')'*(`poptot' - `Phat')' 		// Change in estimate

	local i=1
	foreach x of local marginals {
		qui: replace `tempwt'=`tempwt'+`qvar'*`entrywt'*`delta'[`i',1]*`x'
	local i=`i'+1
	}						// Calculates new weight variables
}







************** Method = blinear
if "`method'" =="blinear" {
	tempvar wt_cur wt_old qvar2 iterate change
	qui: gen `qvar2'=`qvar'

	qui: gen double `wt_old'=`entrywt'				// Weight at start of iteration
	qui: gen double `wt_cur'=`entrywt'				// Current weight

	scalar `iterate'=1			// Number of the iteration
	scalar `change'=0



	local i=1
	foreach x of local marginals {
		qui: summ `x' [iweight=`entrywt']
		if abs(`poptot'[1,`i'])>1 {
			scalar `change'=max(`change',abs((r(sum)-`poptot'[1,`i'])/`poptot'[1,`i']))
		}
		else {
			scalar `change'=max(`change',abs(r(sum)))
		}
	local i=`i'+1
	}

if "`print'"=="all" {
	display in white "Original tolerance is " `change'
}


	while (`iterate'<=`maxit' & `change'>`tolerance') {
		qui: replace `wt_old'=`wt_cur'

		calibrate, entrywt(`wt_old') exitwt(`wt_cur') poptot(`poptot') marginals(`marginals') qvar(`qvar2') print(none)
		qui: replace `qvar2'=0 if (`wt_cur'< `entrywt'*`lbound' | `wt_cur'> `entrywt'*`ubound')
		qui: replace `wt_cur'=`entrywt'*`ubound' if `wt_cur'> `entrywt'*`ubound'
 		qui: replace `wt_cur'=`entrywt'*`lbound' if `wt_cur'< `entrywt'*`lbound'

		scalar `change'=0
		local i=1
		foreach x of local marginals {
			qui: summ `x' [iweight=`wt_cur']
			if abs(`poptot'[1,`i'])>1 {
				scalar `change'=max(`change',abs((r(sum)-`poptot'[1,`i'])/`poptot'[1,`i']))
			}
			else {
				scalar `change'=max(`change',abs(r(sum)))
			}
		local i=`i'+1
		}



		* Display result of the iteration
		if "`print'"=="all" {
			display  in white "Iteration = " `iterate' " Tolerance = " `change'
			local j=1
			display in white "Variable" _column(20) "Pop total" _column(35) "Previous iteration" _column(55) "Current iteration"
			foreach x of local marginals {
				egen `sampest'=total(`wt_cur'*`x')
				egen `selest'=total(`wt_old'*`x')
				scalar `est' = `poptot'[1,`j']
				display as result "`x'" _column(20) as result `est' _column(35) as result `selest' _column(55) as result `sampest'
				drop `sampest' `selest'
				local j=`j'+1
			}
		}
	scalar `iterate'=`iterate'+1
}

* This ends the iteration

if `change'>`tolerance' {
	display in red "Program has not converged after `maxit' iterations"
}

qui: replace `tempwt'=`wt_cur'
}





************** Non-response methods

if "`method'" == "nrSS" | "`method'" == "nr2A" |"`method'" == "nr2B" |"`method'" == "nr2C"  {
	tempname sow rsow Pmoon PstarC poptotA poptotB delta
	tempvar entrywtA  exitwtA

	confirm numeric variable `sampvars'
	matrix vecaccum `Pmoon' = `entrywt' `sampvars', noconstant	// Matrix of moon totals
	matrix vecaccum `PstarC' = `entrywt' `marginals', noconstant	// Matrix of star totals restricted to sample
	matrix `poptotA' = `poptot', `Pmoon'			// Matrix of star and moon totals
	matrix `poptotB' = `PstarC', `Pmoon'			// Matrix of star and moon totals with star restricted to the sample
	egen `sow'=total(`entrywt')		// sum of selection weights
	egen `rsow'=total(`outc'*`entrywt')	// sum of selection weights of responders
	qui: gen double `entrywtA'=`entrywt'*`outc'*`sow'/`rsow'	// Scales entrywt of responders, non-responders put to 0
}



************** Method = nrSS
if ("`method'" == "nrSS") {
	qui: calibrate , entrywt(`entrywtA') exitwt(`exitwtA') marginals(`marginals' `sampvars') poptot(`poptotA') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	matrix `delta'=r(Bhat)
}



************** Method = nr2A
if ("`method'" == "nr2A") {
	qui: calibrate , entrywt(`entrywtA') exitwt(`exitwtA') marginals(`sampvars') poptot(`Pmoon') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	qui: replace `tempwt'=0 if `tempwt'<0
	qui: calibrate , entrywt(`tempwt') exitwt(`exitwtA') marginals(`marginals' `sampvars') poptot(`poptotA') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	matrix `delta'=r(Bhat)
}





************** Method = nr2B
if ("`method'" == "nr2B") {
	qui: calibrate , entrywt(`entrywtA') exitwt(`exitwtA') marginals(`sampvars') poptot(`Pmoon') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	qui: replace `tempwt'=0 if `tempwt'<0
	qui: calibrate , entrywt(`tempwt') exitwt(`exitwtA') marginals(`marginals') poptot(`poptot') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	matrix `delta'=r(Bhat)
}



************** Method = nr2C
if ("`method'" == "nr2C") {
	qui: calibrate , entrywt(`entrywtA') exitwt(`exitwtA') marginals(`marginals' `sampvars') poptot(`poptotB') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	qui: replace `tempwt'=0 if `tempwt'<0
	qui: calibrate , entrywt(`tempwt') exitwt(`exitwtA') marginals(`marginals') poptot(`poptot') method(linear) qvar(`qvar')
	qui: replace `tempwt'=`exitwtA'
	matrix `delta'=r(Bhat)
}






************** Method = logistic
if "`method'" =="logistic" {
	tempname iterate change flambda min max delta2
	tempvar wt_cur wt_old  entryq lnwt_cur

	qui: gen double `wt_old'=`entrywt'				// Weight at start of iteration
	qui: gen double `wt_cur'=`entrywt'				// Current weight
	qui: gen double `lnwt_cur'=ln(`wt_cur')			// Log of curreent wt
	qui: gen double `entryq'=`entrywt'*`qvar'
	matrix `delta'=J(`ncols',1,0)

* Begin the iteration


scalar `iterate'=1			// Number of the iteration

scalar `change'=0

local i=1
foreach x of local marginals {
	qui: summ `x' [iweight=`entrywt']
	if abs(`poptot'[1,`i'])>1 {
		scalar `change'=max(`change',abs((r(sum)-`poptot'[1,`i'])/`poptot'[1,`i']))
	}
	else {
		scalar `change'=max(`change',abs(r(sum)))
	}
local i=`i'+1
}

if "`print'"=="all" {
	display in white "Original tolerance is " `change'
}

while (`iterate'<=`maxit' & `change'>`tolerance') {
	qui: replace `wt_old'=`wt_cur'
	qui: replace `entryq'=`wt_cur'*`qvar'
	qui: matrix accum `M'=`marginals' [iweight=`entryq'], noconstant
			// M= Matrix used in Newton-Raphson
	matrix vecaccum `Phat'=`wt_cur' `marginals', noconstant    // P-hat is the matrix of estimates
	matrix `flambda'= `Phat' - `poptot'	// flambda = estimate minus calibration total
	matrix `InvM'=invsym(`M')
	scalar `det'=det(`InvM')
	if `det'==0 {
		display in red "Matrix is singular -- check results carefully"
	}

	matrix `delta2'=-(`InvM')*`flambda'' 		// Change in estimate
	matrix `delta'=`delta'+`delta2'

	qui: replace `lnwt_cur'=ln(`wt_cur')
	local i=1
	foreach x of local marginals {
		qui: replace `lnwt_cur'=`lnwt_cur'+(`qvar'*`delta2'[`i',1]*`x')
	local i=`i'+1
	}
	qui: replace `wt_cur'=exp(`lnwt_cur')

	scalar `change'=0
	local i=1
	foreach x of local marginals {
		qui: summ `x' [iweight=`wt_cur']
		if abs(`poptot'[1,`i'])>1 {
			scalar `change'=max(`change',abs((r(sum)-`poptot'[1,`i'])/`poptot'[1,`i']))
		}
		else {
			scalar `change'=max(`change',abs(r(sum)))
		}
	local i=`i'+1
	}


	* Display result of the iteration
	if "`print'"=="all" {
		display  in white "Iteration = " `iterate' " Tolerance = " `change'
		local j=1
		display in white "Variable" _column(20) "Pop total" _column(35) "Previous iteration" _column(55) "Current iteration"
		foreach x of local marginals {
			egen `sampest'=total(`wt_cur'*`x')
			egen `selest'=total(`wt_old'*`x')
			scalar `est' = `poptot'[1,`j']
			display as result "`x'" _column(20) as result `est' _column(35) as result `selest' _column(55) as result `sampest'
			drop `sampest' `selest'
			local j=`j'+1
		}
	}
	scalar `iterate'=`iterate'+1
}
* This ends the iteration


if `change'>`tolerance' {
	display in red "Program has not converged after `maxit' iterations"
}

qui: replace `tempwt'=`wt_cur'
}







************* Making the final weight, with any warnings

* Now call tempwt exitwt

capture confirm new variable `exitwt'
if _rc!=0 {
	qui: replace `exitwt'=`tempwt'
}
else {
	qui: gen `exitwt'=`tempwt'
}

if "`method'" == "nrSS" | "`method'" == "nr2A" |"`method'" == "nr2B" |"`method'" == "nr2C"  {
	qui: replace `exitwt'=. if `outc'==0
}

* Count the number of negative weights and give a warning
tempname negwt
qui: count if `exitwt'<0
scalar `negwt'=r(N)
if `negwt'>=1 {
	display in red "Warning: " `negwt' " negative weight(s) are present"
}







*********** Printing


if "`print'" == "all" | "`print'"=="final" {
	di _newline
	di in smcl in gr "Totals"
	if "`method'" == "linear" | "`method'"=="" | "`method'"=="logistic" {
		local j=1
		di in smcl in gr "{hline 18}{c TT}{hline 80}"
		di in smcl in gr "Variable" _col(19) %1s "{c |}" _column(20) "Pop total" _column(35) "Weighted (entrywt)" _column(55) "Weighted (exitwt)" _column(75) "R"
		di in smcl in gr "{hline 18}{c +}{hline 80}"
		foreach x of local marginals {
			egen `sampest'=total(`exitwt'*`x')
			egen `selest'=total(`entrywt'*`x')
			scalar `est' = `poptot'[1,`j']
			scalar `result' = `delta'[`j',1]
			di in smcl in gr "`x'" _column(19) "{c |}" in ye `est' _column(35)  `selest' _column(55)  `sampest' _column(75)  `result'
			drop `sampest' `selest'
			local j=`j'+1
		}
		di in smcl in gr "{hline 18}{c BT}{hline 80}"
	}
	else if "`method'" == "blinear" {
		local j=1
		di in smcl in gr "{hline 18}{c TT}{hline 80}"
		di in smcl in gr "Variable" _col(19) %1s "{c |}" _column(20) "Pop total" _column(35) "Weighted (entrywt)" _column(55) "Weighted (exitwt)"
		di in smcl in gr "{hline 18}{c +}{hline 80}"
		foreach x of local marginals {
			egen `sampest'=total(`exitwt'*`x')
			egen `selest'=total(`entrywt'*`x')
			scalar `est' = `poptot'[1,`j']
			di in smcl in gr "`x'" _column(19) "{c |}" _column(20) in ye  `est' _column(35) `selest' _column(55)  `sampest'
			drop `sampest' `selest'
			local j=`j'+1
		}
		di in smcl in gr "{hline 18}{c BT}{hline 80}"
	}
	else  {
		local j=1
		di in smcl in gr "{hline 18}{c TT}{hline 80}"
		di in smcl in gr "Variable" _col(19) %1s "{c |}" _column(20) "Pop total" _column(35) "Weighted (entrywt)" _column(55) "Weighted (exitwt)" _column(75) "R"
		di in smcl in gr "{hline 18}{c +}{hline 80}"
		foreach x of local marginals {
			egen `sampest'=total(`exitwt'*`x')
			egen `selest'=total(`entrywt'*`x')
			scalar `est' = `poptot'[1,`j']
			scalar `result' = `delta'[`j',1]
			di in smcl in gr "`x'" _column(19) "{c |}" _column(20) in ye  `est' _column(35)  `selest' _column(55)  `sampest' _column(75) as result `result'
			drop `sampest' `selest'
			local j=`j'+1
		}
		local j=`nstar'+1
		di in smcl in gr "{hline 18}{c TT}{hline 80}"
		foreach x of local sampvars {
			egen `sampest'=total(`exitwt'*`x')
			egen `selest'=total(`entrywt'*`x')
			scalar `est' = `poptot'[1,`j']
			scalar `result' = `delta'[`j',1]
			di in smcl in gr "`x'" _col(19) %1s "{c |}" _column(20) in ye  `est' _column(35)  `selest' _column(55)  `sampest' _column(75) as result `result'
			drop `sampest' `selest'
			local j=`j'+1
		}
		di in smcl in gr "{hline 18}{c BT}{hline 80}"
	}
}







********** Graphs **********************


if "`graphs'" == "final" {
	graph twoway hist `exitwt', ysc(off)  title("Exit weights")
}

if "`graphs'" == "all" {
	tempname scatterplot combine final gph
	graph twoway hist `exitwt',  ysc(off) title("Exit weights") name(`final') nodraw
	graph twoway scatter `exitwt' `entrywt',  ylabel(,ang(0)) title("Exit weight v Entry weight") name(`scatterplot') nodraw
	if "`method'" == "logistic" {
		tempvar lnratio
		qui: gen double `lnratio'=`exitwt'/`entrywt'
		label var `lnratio' "ln(ratio)"
		graph twoway  hist `lnratio',  ysc(off) title("Log of the ratio: exit weight to entry weight") name(`gph') nodraw
	}
	else {
		tempvar ratio
		qui: gen double `ratio'=`exitwt'/`entrywt'
		label var `ratio' "Ratio"
		graph twoway  hist `ratio',  ysc(off) title("Ratio of exit weight to entry weight") name(`gph') nodraw
	}
	graph combine `final' `scatterplot' `gph'
}





********** Return list  **********************

qui: summ `entrywt'
scalar `entdeff'=1+r(Var)/(r(mean)^2)
return scalar entdeff=`entdeff'



if "`method'" == "nrSS" | "`method'" == "nr2A" |"`method'" == "nr2B" |"`method'" == "nr2C"  {
	preserve
	qui: keep if `outc'==1
		qui: summ `exitwt'
		return scalar mean=r(mean)
		return scalar N=r(N)
		return scalar min=r(min)
		return scalar max=r(max)
		scalar `exitdeff'=1+r(Var)/(r(mean)^2)
		scalar `sclmax'=r(max)/r(mean)
		scalar `sclmin'=r(min)/r(mean)
		scalar `sclsd'=r(sd)/r(mean)
		return scalar exitdeff=`exitdeff'
		return scalar sclmin=`sclmin'
		return scalar sclmax=`sclmax'
		return scalar sclsd=`sclsd'
		return matrix Bhat=`delta'
	restore
}
else {
	qui: summ `exitwt'
	return scalar mean=r(mean)
	return scalar N=r(N)
	return scalar min=r(min)
	return scalar max=r(max)
	scalar `exitdeff'=1+r(Var)/(r(mean)^2)
	scalar `sclmax'=r(max)/r(mean)
	scalar `sclmin'=r(min)/r(mean)
	scalar `sclsd'=r(sd)/r(mean)
	return scalar exitdeff=`exitdeff'
	return scalar sclmin=`sclmin'
	return scalar sclmax=`sclmax'
	return scalar sclsd=`sclsd'
	if ("`method'" != "blinear" ) {
		return matrix Bhat=`delta'
	}
}

end