Fix random typo

Fix random typo

mata:
mata set matastrict on
// -------------------------------------------------------------------------------------------------
// Acceleration Schemes
// -------------------------------------------------------------------------------------------------

`Group' function accelerate_none(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer'	iter
	`Group'		resid
	pragma unset resid

	for (iter=1; iter<=S.maxiterations; iter++) {
		(*T)(S, y, resid) // Faster version of "resid = S.T(y)"
		if (check_convergence(S, iter, resid, y)) break
		y = resid
	}
	return(resid)
}
// -------------------------------------------------------------------------------------------------

// Start w/out acceleration, then switch to CG
`Group' function accelerate_hybrid(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer' iter, accel_start
	`Group' resid
	pragma unset resid

	accel_start = 3

	for (iter=1; iter<=accel_start; iter++) {
		(*T)(S, y, resid) // Faster version of "resid = S.T(y)"
		if (check_convergence(S, iter, resid, y)) break
		y = resid
	}

	return(accelerate_cg(S, y, T))
}

// -------------------------------------------------------------------------------------------------
// Memory cost is approx = 4*size(y) (actually 3 since y is already there)
// But we need to add maybe 1 more due to u:*v
// And I also need to check how much does project and T use..
// Double check with a call to memory

// For discussion on the stopping criteria, see the following presentation:
// Arioli & Gratton, "Least-squares problems, normal equations, and stopping criteria for the conjugate gradient method". URL: https://www.stfc.ac.uk/SCD/resources/talks/Arioli-NAday2008.pdf

// Basically, we will use the Hestenes and Stiefel rule

`Group' function accelerate_cg(`Problem' S, `Group' y, `FunctionPointer' T) {
	// BUGBUG iterate the first 6? without acceleration??
	`Integer'	iter, d, Q
	`Group'		r, u, v
	real rowvector alpha, beta, ssr, ssr_old, improvement_potential
	`Matrix' recent_ssr
	pragma unset r
	pragma unset v

	Q = cols(y)

	d = 2 // BUGBUG Set it to 2/3 // Number of recent SSR values to use for convergence criteria (lower=faster & riskier)
	// A discussion on the stopping criteria used is described in
	// http://scicomp.stackexchange.com/questions/582/stopping-criteria-for-iterative-linear-solvers-applied-to-nearly-singular-system/585#585

	improvement_potential = weighted_quadcolsum(S, y, y)
	recent_ssr = J(d, Q, .)

	(*T)(S, y, r, 1)
	ssr = weighted_quadcolsum(S, r, r) // cross(r,r) when cols(y)==1 // BUGBUG maybe diag(quadcross()) is faster?
	u = r

	for (iter=1; iter<=S.maxiterations; iter++) {
		(*T)(S, u, v, 1) // This is the hottest loop in the entire program
		alpha = safe_divide( ssr , weighted_quadcolsum(S, u, v) )
		recent_ssr[1 + mod(iter-1, d), .] = alpha :* ssr
		improvement_potential = improvement_potential - alpha :* ssr
		y = y - alpha :* u
		r = r - alpha :* v
		ssr_old = ssr
		ssr = weighted_quadcolsum(S, r, r)
		beta = safe_divide( ssr , ssr_old) // Fletcher-Reeves formula, but it shouldn't matter in our problem
		u = r + beta :* u
		// Convergence if sum(recent_ssr) > tol^2 * improvement_potential
		if ( check_convergence(S, iter, colsum(recent_ssr), improvement_potential, "hestenes") ) break
	}
	return(y)
}

// -------------------------------------------------------------------------------------------------

`Group' function accelerate_sd(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer'	iter, g
	`Group' proj
	real rowvector t
	pragma unset proj

	for (iter=1; iter<=S.maxiterations; iter++) {
		(*T)(S, y, proj, 1)
		if (check_convergence(S, iter, y-proj, y)) break
		t = safe_divide( weighted_quadcolsum(S, y, proj) , weighted_quadcolsum(S, proj, proj) )
		if (uniform(1,1)<0.1) t = 1 // BUGBUG: Does this help to randomly unstuck an iteration?
		y = y - t :* proj

		if (S.storing_betas) {
			for (g=1; g<=S.G; g++) {
				if (length(S.fes[g].target)>0) {
					S.fes[g].alphas = S.fes[g].alphas + t :* S.fes[g].tmp_alphas
				}
			}
		}
	}
	return(y-proj)
}

// -------------------------------------------------------------------------------------------------
// This is method 3 of Macleod (1986), a vector generalization of the Aitken-Steffensen method
// Also: "when numerically computing the sequence.. stop..  when rounding errors become too
// important in the denominator, where the ^2 operation may cancel too many significant digits"
// Note: Sometimes the iteration gets "stuck"; can we unstuck it with adding randomness
// in the accelerate decision? There should be a better way.. (maybe symmetric kacz instead of standard one?)

`Group' function accelerate_aitken(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer'	iter
	`Group'		resid, y_old, delta_sq
	`Boolean'	accelerate
	real rowvector t
	pragma unset resid

	//S.pause_length = 20
	//S.bad_loop_threshold = 1
	//S.stuck_threshold = 5e-3
	// old_error = oldest_error = bad_loop = acceleration_countdown = 0

	y_old = J(rows(y), cols(y), .)

	for (iter=1; iter<=S.maxiterations; iter++) {

		(*T)(S, y, resid)
		accelerate = iter>=S.accel_start & !mod(iter,S.accel_freq)

		// Accelerate
		if (accelerate) {
			delta_sq = resid - 2 * y + y_old // = (resid - y) - (y - y_old) // Equivalent to D2.resid
			// t is just (d'd2) / (d2'd2)
			t = safe_divide( weighted_quadcolsum(S,  (resid - y) , delta_sq) ,  weighted_quadcolsum(S, delta_sq , delta_sq) )
			resid = resid - t :*  (resid - y)
		}

		// Only check converge on non-accelerated iterations
		// BUGBUG: Do we need to disable the check when accelerating?
		// if (check_convergence(S, iter, accelerate? resid :* .  : resid, y)) break
		if (check_convergence(S, iter, resid, y)) break

		// Experimental: Pause acceleration
		//if (accelerate) {
		//	improvement = max(( (old_error-update_error)/update_error , (oldest_error-update_error)/update_error ))
		//	bad_loop = improvement < stuck_threshold ? bad_loop+1 : 0
		//	// bad_loop, improvement, update_error, old_error, oldest_error
		//	// Tolerate two problems (i.e. 6+2=8 iters) and then try to unstuck
		//	if (bad_loop>bad_loop_threshold) {
		//		bad_loop = 0
		//		if (VERBOSE==3) printf(" Fixed point iteration seems stuck, acceleration paused\n")
		//		acceleration_countdown = pause_length
		//	}
		//	assert(bad_loop<=3)
		//	oldest_error = old_error
		//	old_error = update_error
		//}
		//
		y_old = y // y_old is resid[iter-2]
		y = resid // y is resid[iter-1]
	}
	return(resid)
}

// -------------------------------------------------------------------------------------------------

`Boolean' check_convergence(`Problem' S, `Integer' iter, `Group' y_new, `Group' y_old,| `String' method) {
	`Boolean'	done, is_last_iter
	`Real'		update_error

	// max() ensures that the result when bunching vars is at least as good as when not bunching
	if (args()<5) method = "vectors"

	if (S.G==1 & !S.storing_betas) {
		// Shortcut for trivial case (1 FE)
		update_error = 0
	}
	else if (method=="vectors") {
		update_error = max(mean(reldif(y_new, y_old)))
	}
	else if (method=="hestenes") {
		// If the regressor is perfectly explained by the absvars, we can have SSR very close to zero but negative
		// (so sqrt is missing)
		update_error = max(safe_divide( sqrt(y_new) , editmissing(sqrt(y_old), sqrt(epsilon(1)) ) , sqrt(epsilon(1)) ))
	}
	else {
		exit(error(100))
	}

	done = update_error <= S.tolerance
	is_last_iter = iter==S.maxiterations

	if (done) {
		S.num_iters_last_run = iter
		if (S.verbose==1) printf("{txt} converged in %g iterations last error =%3.1e)\n", iter, update_error)
		if (S.verbose>1) printf("\n{txt} - Converged in %g iterations (last error =%3.1e)\n", iter, update_error)
	}
	else if (is_last_iter) {
		printf("\n{err}convergence not achieved in %g iterations (last error=%e); try increasing maxiter() or decreasing tol().\n", S.maxiterations, update_error)
		exit(430)
	}
	else {
		if ((S.verbose>=2 & S.verbose<=3 & mod(iter,1)==0) | (S.verbose==1 & mod(iter,10)==0)) {
			printf("{txt}.")
			displayflush()
		}
		if ( (S.verbose>=2 & S.verbose<=3 & mod(iter,100)==0) | (S.verbose==1 & mod(iter,1000)==0) ) printf("{txt}%9.1f\n", update_error/S.tolerance)

		if (S.verbose==4 & method!="hestenes") printf("{txt} iter={res}%4.0f{txt}\tupdate_error={res}%-9.6e\n", iter, update_error)
		if (S.verbose==4 & method=="hestenes") printf("{txt} iter={res}%4.0f{txt}\tupdate_error={res}%-9.6e  {txt}norm(ssr)={res}%g\n", iter, update_error, norm(y_new))

		if (S.verbose==5) {
			printf("\n{txt} iter={res}%4.0f{txt}\tupdate_error={res}%-9.6e{txt}\tmethod={res}%s\n", iter, update_error, method)
			"old:"
			y_old
			"new:"
			y_new
		}
	}
	return(done)
}

// -------------------------------------------------------------------------------------------------

`Matrix' weighted_quadcolsum(`Problem' S, `Matrix' x, `Matrix' y) {
	// BUGBUG: colsum or quadcolsum??
		return( quadcolsum(S.weightvar=="" ? (x :* y) : (x :* y :* S.w) ) )
}
end

mata:
mata set matastrict on
// -------------------------------------------------------------------------------------------------
// Acceleration Schemes
// -------------------------------------------------------------------------------------------------

`Group' function accelerate_none(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer'	iter
	`Group'		resid
	pragma unset resid

	for (iter=1; iter<=S.maxiterations; iter++) {
		(*T)(S, y, resid) // Faster version of "resid = S.T(y)"
		if (check_convergence(S, iter, resid, y)) break
		y = resid
	}
	return(resid)
}
// -------------------------------------------------------------------------------------------------

// Start w/out acceleration, then switch to CG
`Group' function accelerate_hybrid(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer' iter, accel_start
	`Group' resid
	pragma unset resid

	accel_start = 3

	for (iter=1; iter<=accel_start; iter++) {
		(*T)(S, y, resid) // Faster version of "resid = S.T(y)"
		if (check_convergence(S, iter, resid, y)) break
		y = resid
	}

	return(accelerate_cg(S, y, T))
}

// -------------------------------------------------------------------------------------------------
// Memory cost is approx = 4*size(y) (actually 3 since y is already there)
// But we need to add maybe 1 more due to u:*v
// And I also need to check how much does project and T use..
// Double check with a call to memory

// For discussion on the stopping criteria, see the following presentation:
// Arioli & Gratton, "Least-squares problems, normal equations, and stopping criteria for the conjugate gradient method". URL: https://www.stfc.ac.uk/SCD/resources/talks/Arioli-NAday2008.pdf

// Basically, we will use the Hestenes and Stiefel rule

`Group' function accelerate_cg(`Problem' S, `Group' y, `FunctionPointer' T) {
	// BUGBUG iterate the first 6? without acceleration??
	`Integer'	iter, d, Q
	`Group'		r, u, v
	real rowvector alpha, beta, ssr, ssr_old, improvement_potential
	`Matrix' recent_ssr
	pragma unset r
	pragma unset v

	Q = cols(y)

	d = 2 // BUGBUG Set it to 2/3 // Number of recent SSR values to use for convergence criteria (lower=faster & riskier)
	// A discussion on the stopping criteria used is described in
	// http://scicomp.stackexchange.com/questions/582/stopping-criteria-for-iterative-linear-solvers-applied-to-nearly-singular-system/585#585

	improvement_potential = weighted_quadcolsum(S, y, y)
	recent_ssr = J(d, Q, .)

	(*T)(S, y, r, 1)
	ssr = weighted_quadcolsum(S, r, r) // cross(r,r) when cols(y)==1 // BUGBUG maybe diag(quadcross()) is faster?
	u = r

	for (iter=1; iter<=S.maxiterations; iter++) {
		(*T)(S, u, v, 1) // This is the hotest loop in the entire program
		alpha = safe_divide( ssr , weighted_quadcolsum(S, u, v) )
		recent_ssr[1 + mod(iter-1, d), .] = alpha :* ssr
		improvement_potential = improvement_potential - alpha :* ssr
		y = y - alpha :* u
		r = r - alpha :* v
		ssr_old = ssr
		ssr = weighted_quadcolsum(S, r, r)
		beta = safe_divide( ssr , ssr_old) // Fletcher-Reeves formula, but it shouldn't matter in our problem
		u = r + beta :* u
		// Convergence if sum(recent_ssr) > tol^2 * improvement_potential
		if ( check_convergence(S, iter, colsum(recent_ssr), improvement_potential, "hestenes") ) break
	}
	return(y)
}

// -------------------------------------------------------------------------------------------------

`Group' function accelerate_sd(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer'	iter, g
	`Group' proj
	real rowvector t
	pragma unset proj

	for (iter=1; iter<=S.maxiterations; iter++) {
		(*T)(S, y, proj, 1)
		if (check_convergence(S, iter, y-proj, y)) break
		t = safe_divide( weighted_quadcolsum(S, y, proj) , weighted_quadcolsum(S, proj, proj) )
		if (uniform(1,1)<0.1) t = 1 // BUGBUG: Does this help to randomly unstuck an iteration?
		y = y - t :* proj

		if (S.storing_betas) {
			for (g=1; g<=S.G; g++) {
				if (length(S.fes[g].target)>0) {
					S.fes[g].alphas = S.fes[g].alphas + t :* S.fes[g].tmp_alphas
				}
			}
		}
	}
	return(y-proj)
}

// -------------------------------------------------------------------------------------------------
// This is method 3 of Macleod (1986), a vector generalization of the Aitken-Steffensen method
// Also: "when numerically computing the sequence.. stop..  when rounding errors become too
// important in the denominator, where the ^2 operation may cancel too many significant digits"
// Note: Sometimes the iteration gets "stuck"; can we unstuck it with adding randomness
// in the accelerate decision? There should be a better way.. (maybe symmetric kacz instead of standard one?)

`Group' function accelerate_aitken(`Problem' S, `Group' y, `FunctionPointer' T) {
	`Integer'	iter
	`Group'		resid, y_old, delta_sq
	`Boolean'	accelerate
	real rowvector t
	pragma unset resid

	//S.pause_length = 20
	//S.bad_loop_threshold = 1
	//S.stuck_threshold = 5e-3
	// old_error = oldest_error = bad_loop = acceleration_countdown = 0

	y_old = J(rows(y), cols(y), .)

	for (iter=1; iter<=S.maxiterations; iter++) {

		(*T)(S, y, resid)
		accelerate = iter>=S.accel_start & !mod(iter,S.accel_freq)

		// Accelerate
		if (accelerate) {
			delta_sq = resid - 2 * y + y_old // = (resid - y) - (y - y_old) // Equivalent to D2.resid
			// t is just (d'd2) / (d2'd2)
			t = safe_divide( weighted_quadcolsum(S,  (resid - y) , delta_sq) ,  weighted_quadcolsum(S, delta_sq , delta_sq) )
			resid = resid - t :*  (resid - y)
		}

		// Only check converge on non-accelerated iterations
		// BUGBUG: Do we need to disable the check when accelerating?
		// if (check_convergence(S, iter, accelerate? resid :* .  : resid, y)) break
		if (check_convergence(S, iter, resid, y)) break

		// Experimental: Pause acceleration
		//if (accelerate) {
		//	improvement = max(( (old_error-update_error)/update_error , (oldest_error-update_error)/update_error ))
		//	bad_loop = improvement < stuck_threshold ? bad_loop+1 : 0
		//	// bad_loop, improvement, update_error, old_error, oldest_error
		//	// Tolerate two problems (i.e. 6+2=8 iters) and then try to unstuck
		//	if (bad_loop>bad_loop_threshold) {
		//		bad_loop = 0
		//		if (VERBOSE==3) printf(" Fixed point iteration seems stuck, acceleration paused\n")
		//		acceleration_countdown = pause_length
		//	}
		//	assert(bad_loop<=3)
		//	oldest_error = old_error
		//	old_error = update_error
		//}
		//
		y_old = y // y_old is resid[iter-2]
		y = resid // y is resid[iter-1]
	}
	return(resid)
}

// -------------------------------------------------------------------------------------------------

`Boolean' check_convergence(`Problem' S, `Integer' iter, `Group' y_new, `Group' y_old,| `String' method) {
	`Boolean'	done, is_last_iter
	`Real'		update_error

	// max() ensures that the result when bunching vars is at least as good as when not bunching
	if (args()<5) method = "vectors"

	if (S.G==1 & !S.storing_betas) {
		// Shortcut for trivial case (1 FE)
		update_error = 0
	}
	else if (method=="vectors") {
		update_error = max(mean(reldif(y_new, y_old)))
	}
	else if (method=="hestenes") {
		// If the regressor is perfectly explained by the absvars, we can have SSR very close to zero but negative
		// (so sqrt is missing)
		update_error = max(safe_divide( sqrt(y_new) , editmissing(sqrt(y_old), sqrt(epsilon(1)) ) , sqrt(epsilon(1)) ))
	}
	else {
		exit(error(100))
	}

	done = update_error <= S.tolerance
	is_last_iter = iter==S.maxiterations

	if (done) {
		S.num_iters_last_run = iter
		if (S.verbose==1) printf("{txt} converged in %g iterations last error =%3.1e)\n", iter, update_error)
		if (S.verbose>1) printf("\n{txt} - Converged in %g iterations (last error =%3.1e)\n", iter, update_error)
	}
	else if (is_last_iter) {
		printf("\n{err}convergence not achieved in %g iterations (last error=%e); try increasing maxiter() or decreasing tol().\n", S.maxiterations, update_error)
		exit(430)
	}
	else {
		if ((S.verbose>=2 & S.verbose<=3 & mod(iter,1)==0) | (S.verbose==1 & mod(iter,10)==0)) {
			printf("{txt}.")
			displayflush()
		}
		if ( (S.verbose>=2 & S.verbose<=3 & mod(iter,100)==0) | (S.verbose==1 & mod(iter,1000)==0) ) printf("{txt}%9.1f\n", update_error/S.tolerance)

		if (S.verbose==4 & method!="hestenes") printf("{txt} iter={res}%4.0f{txt}\tupdate_error={res}%-9.6e\n", iter, update_error)
		if (S.verbose==4 & method=="hestenes") printf("{txt} iter={res}%4.0f{txt}\tupdate_error={res}%-9.6e  {txt}norm(ssr)={res}%g\n", iter, update_error, norm(y_new))

		if (S.verbose==5) {
			printf("\n{txt} iter={res}%4.0f{txt}\tupdate_error={res}%-9.6e{txt}\tmethod={res}%s\n", iter, update_error, method)
			"old:"
			y_old
			"new:"
			y_new
		}
	}
	return(done)
}

// -------------------------------------------------------------------------------------------------

`Matrix' weighted_quadcolsum(`Problem' S, `Matrix' x, `Matrix' y) {
	// BUGBUG: colsum or quadcolsum??
		return( quadcolsum(S.weightvar=="" ? (x :* y) : (x :* y :* S.w) ) )
}
end


iekensity: typos in help file

iekensity: typos in help file

{smcl}
{* DEVELOP}{...}
{hline}
help for {hi:iekdensity}
{hline}

{title:Title}

{phang2}{cmd:iekdensity} {hline 2} This command plots univariate kernel density estimates by treatment assignment.

{* {phang2}For a more descriptive discussion on the intended usage and work flow of this command please see the {browse "https://dimewiki.worldbank.org/wiki/Iekdensity":DIME Wiki}.}

{title:Syntax}

	{phang2} {cmd:iekdensity} {it:yvar} [{help if}] [{help in}] [{help weight}],
		{opt treat:var(treatmentvar)}
			{break}[
			{opt stat(string)}
			{opt statstyle(string)}
			{opt effect}
			{cmdab:control(}{it:{help numlist:numlist}}{cmd:)}
			{cmdab:effectformat(}{it:{help format:%fmt}}{cmd:)}
			{opt abs:orb(varname)}
			{opt reg:ressionoptions(string)}
			{opt kdensity:options(string)}
			{opt color(string)}
			{opt gr:aphoptions(string)}
		]

	{pmore}Where {it:yvar} is a numeric continuous outcome variables, whose distribution is to be plotted by treatment assignment.{p_end}

{marker opts}{...}
{synoptset 24}{...}
{synopthdr:options}
{synoptline}

{pstd}{it:Required options:}{p_end}
	{synopt :{opt treat:var(varname)}}Treatment (dummy or factor) variable.{p_end}

{pstd}{it:Content options:}{p_end}
	{synopt :{opt stat(string)}}Add vertical lines for each treatment group with statistic specified.{p_end}
	{synopt :{opt statstyle(string)}}Specify graphic style of statistic lines.{p_end}
	{synopt :{opt effect}}Add note with treatment effect, containing point estimate, standard error, and p-value.{p_end}
	{synopt :{cmdab:control(}{it:{help numlist:numlist}}{cmd:)}}Specify value of variable for control group.{p_end}
	{synopt :{cmdab:effectformat(}{it:{help format:%fmt}}{cmd:)}}Specify format of point estimate and standard error of the treatment effect.{p_end}

{pstd}{it:Estimation options:}{p_end}
	{synopt :{opt abs:orb(varname)}}Specify fixed effects variable, if any.{p_end}
	{synopt :{opt reg:ressionoptions(string)}}Specify regression options.{p_end}
	{synopt :{opt kdensity:options(string)}}Specify kernel estimation options.{p_end}

{pstd}{it:Graphic options:}{p_end}
	{synopt :{opt color(string)}}Specify colors for each group.{p_end}
	{synopt :{opt gr:aphoptions(string)}}Specify graph options.{p_end}

{synoptline}

{marker desc}
{title:Description}

	{pstd}{cmd:iekdensity} is a command that allows to easily plot the distribution of a variable by treatment group.
	It also allows to include additional information, such as descriptive statistics and treatment effect(s).{p_end}

{marker optslong}
{title:Options}

	{pstd}{it:{ul:{hi:Required options:}}}{p_end}
		{phang}{opt treat:var(varname)} indicates which variable should be used to idenfity the treatment assignment. This can be a dummy variable (0/1) or a factor variable, when there are multiple treatments.{p_end}

	{pstd}{it:{ul:{hi:Content options:}}}{p_end}
		{phang}{opt stat(string)} specifies a descriptive statitistic to be plotted over the kernel density graph. In particular, vertical lines for each treatment group are added. Accepted statistics are: {it:mean}, {it:p1}, {it:p5}, {it:p10}, {it:p25}, {it:p50}, {it:p75}, {it:p90}, {it:p95}, {it:p99}, {it:min} and {it:max}.{p_end}

		{phang}{opt statstyle(string)} specifies the graphic style to be used for the statistic lines. Namely, you will be able to use {opt lpattern()} and {opt lwidth()} options. Colors are instead controlled by option {opt color(string)}.{p_end}

		{phang}{opt effect} Add note with treatment effect, containing point estimate, standard error, and p-value.{p_end}

		{phang}{cmdab:control(}{it:{help numlist:numlist}}{cmd:)} indicates which value the variable {opt treat:var(varname)} takes for the control group. This is usually equal to 0 when the treatment is binary, but may vary when dealing with multiple treatment arms.{p_end}

		{phang}{cmdab:effectformat(}{it:{help format:%fmt}}{cmd:)} specify the format in which treatment effect point estimate and standard error should be displayed in the graph note.{p_end}

	{pstd}{it:{ul:{hi:Estimation options:}}}{p_end}

		{phang}{opt abs:orb(varname)} indicates the fixed effects variable (for example, the experimental strata when the treatment was stratified) to be included in the estimation. This variable must be numerical.{p_end}

		{phang}{opt reg:ressionoptions(string)} indicates other options to be employed for the treatment effect estimations, for example suppress constant term ({opt nocons:tant}) or clustered standard errors ({opt cl:uster(varname)}). All options accepted by {help regress} (or {help areg} when option {opt absorb()} is specified) are accepted.{p_end}

		{phang}{opt kdensity:options(string)} specifies kernel estimation options, such as kernel function and half-width of kernel. The default kernel function is {opt kernel(epanechnikov)}. All options accepted by {help kdensity} are accepted.{p_end}

	{pstd}{it:{ul:{hi:Graphic options:}}}{p_end}

		{phang}{opt color(string)} indicates the colors to be used for each treatment arm. The colors should come in the order of the values in {opt treat:var(varname)}. For instance, if the treatment is binary, you can set the line colors by typing {opt color(color1 color2)}. See {help colorstyle}. {p_end}

		{phang}{opt gr:aphoptions(string)} indicates other options to be applied to the graph, such as additional text and lines, changes axes, titles, and legeng, etc.. See {help twoway_options:twoway_options} for the full set of available options.{p_end}

{marker optslong}
{title:Examples}

	{pstd}All the examples below can be run on the Stata's built in automobile data set, by first running this code:{p_end}

	{pstd}* Open the built in data set{p_end}
	{pstd}{inp:sysuse auto}{p_end}

	{pstd}* Randomly assign time and treatment dummies{p_end}
	{pstd}{inp:gen treatment = (runiform() < .5)}{p_end}

	{pstd} {hi:Example 1.}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)}

	{pstd} This is the most basic way to run this command. This will output a graph with the distributions of of the variable of interests ({it:price} in this case) by treatment assignment.

	{pstd} {hi:Example 2.}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)}

	{pstd} This is an easy way to add descriptive information to the graph. This will output the same graph as above with the addition of two vertical lines for the medians of the control and treatment groups.

		{pstd} {hi:Example 2.1}

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)}

		{pstd} This changes the style of the median vertical lines.

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)}

		{pstd} {hi:Example 2.2}

		{pstd} This sets the colors of the control and treatment lines to different shades of blue.

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)} {inp:color(}eltblue edkblue{inp:)}

		{pstd} {hi:Example 2.3}

		{pstd} This changes some of the graphical options.

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)} {inp:graphoptions(}title(auto distribution) subtitle(By Treatment Assignment) ylab(, angle(horizontal)) graphregion(color(white)) plotregion(color(white)){inp:)}

	{pstd} {hi:Example 3.}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:effect}

	{pstd} This adds a note to the graph, displaying the treatment effect in terms of point estimate, standard error and statistical significance.

		{pstd} {hi:Example 3.1}

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:effect} {inp:effectformat(}%9.0fc{inp:)}

		{pstd} This changes the format of the treatment effect in the note. The point estimate and the standard error now do not include any decimal points.

	{pstd} {hi:Example 4.1}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:effect} {inp:absorb(}{it:foreign}{inp:)}

	{pstd} The treatment effect is now derived from a regression controlling for the variable {it:foreign} fixed effects.

	{pstd} {hi:Example 4.2}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:effect} {inp:absorb(}{it:foreign}{inp:)} {inp:regressionoptions(}{cluster(foreign)}{inp:)}

	{pstd} The treatment effect is now derived from a regression controlling for the variable {it:foreign} fixed effects and clustering standard errors at {it:foreign} level.

	{pstd} {hi:Example 5}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:kdensityoptions(}epan2 bwidth(5){inp:)}

	{pstd} The kernel density is estimated through the alternative Epanechnikov kernel function and half-width of the kernel is specified to be equal to 5.

{title:Acknowledgements}

	{phang}We would like to acknowledge the help in testing and proofreading we received in relation to this command and help file from (in alphabetic order):{p_end}
	{pmore}Luiza Andrade{break}

{title:Authors}

	{phang}All commands in ietoolkit are developed by DIME Analytics at DECIE, The World Bank's unit for Development Impact Evaluations.{p_end}

	{phang}Main author: Matteo Ruzzante, DIME, The World Bank Group.{p_end}

	{phang}Please send bug-reports, suggestions and requests for clarifications
			 writing "ietoolkit iekdensity" in the subject line to:{break}
			 dimeanalytics@worldbank.org{p_end}

	{phang}You can also see the code, make comments to the code, see the version
			 history of the code, and submit additions or edits to the code through
			 the GitHub repository of ietoolkit:{break}
	{browse "https://github.com/worldbank/ietoolkit"}{p_end}

{smcl}
{* DEVELOP}{...}
{hline}
help for {hi:iekdensity}
{hline}

{title:Title}

{phang2}{cmd:iekdensity} {hline 2} This command plots univariate kernel density estimates by treatment assignment.

{* {phang2}For a more descriptive discussion on the intended usage and work flow of this command please see the {browse "https://dimewiki.worldbank.org/wiki/Iekdensity":DIME Wiki}.}

{title:Syntax}

	{phang2} {cmd:iekdensity} {opt:varname} [{help if}] [{help in}] [{help weight}],
		{opt treat:var(varname)}
			{break}[
			{opt stat(string)}
			{opt statstyle(string)}
			{opt effect}
			{cmdab:control(}{it:{help numlist:numlist}}{cmd:)}
			{cmdab:effectformat(}{it:{help format:%fmt}}{cmd:)}
			{opt abs:orb(varname)}
			{opt reg:ressionoptions(string)}
			{opt kdensity:options(string)}
			{opt color(string)}
			{opt gr:aphoptions(string)}
		]

	{pmore}Where {it:varname} is a numeric continuous outcome variables, whose distribution is to be plotted by treatment assignment.{p_end}

{marker opts}{...}
{synoptset 24}{...}
{synopthdr:options}
{synoptline}

{pstd}{it:Required options:}{p_end}
	{synopt :{opt treat:var(varname)}}Treatment (dummy or factor) variable.{p_end}

{pstd}{it:Content options:}{p_end}
	{synopt :{opt stat(string)}}Add vertical lines for each treatment group with statistic specified.{p_end}
	{synopt :{opt statstyle(string)}}Specify graphic style of statistic lines.{p_end}
	{synopt :{opt effect}}Add note with treatment effect, containing point estimate, standard error, and p-value.{p_end}
	{synopt :{cmdab:control(}{it:{help numlist:numlist}}{cmd:)}}Specify value of variable for control group.{p_end}
	{synopt :{cmdab:effectformat(}{it:{help format:%fmt}}{cmd:)}}Specify format of point estimate and standard error of the treatment effect.{p_end}

{pstd}{it:Estimation options:}{p_end}
	{synopt :{opt abs:orb(varname)}}Specify fixed effects variable, if any.{p_end}
	{synopt :{opt reg:ressionoptions(string)}}Specify regression options.{p_end}
	{synopt :{opt kdensity:options(string)}}Specify kernel estimation options.{p_end}

{pstd}{it:Graphic options:}{p_end}
	{synopt :{opt color(string)}}Specify colors for each group.{p_end}
	{synopt :{opt gr:aphoptions(string)}}Specify graph options.{p_end}

{synoptline}

{marker desc}
{title:Description}

	{pstd}{cmd:iekdensity} is a command that allows to easily plot the distribution of a variable by treatment group.
	It also allows to include additional information, such as descriptive statistics and treatment effect(s).{p_end}

{marker optslong}
{title:Options}

	{pstd}{it:{ul:{hi:Required options:}}}{p_end}
		{phang}{opt treat:var(varname)} indicates which variable should be used to idenfity the treatment assignment. This can be a dummy variable (0/1) or a factor variable, when there are multiple treatments.{p_end}

	{pstd}{it:{ul:{hi:Content options:}}}{p_end}
		{phang}{opt stat(string)} specifies a descriptive statitistcs to be plotted over the kernel density graph. In particular, vertical lines for each treatment group are added. Statistics available are sample mean, median, minimum and maximum.{p_end}

		{phang}{opt statstyle(string)}} specifies the graphic style to be used for the statistic lines. Namely, you will be able to use {opt lpattern()} and {opt lwidth()} options. Colors are instead controlled by option {opt color(string)}}.{p_end}

		{phang}{opt effect} Add note with treatment effect, containing point estimate, standard error, and p-value.{p_end}

		{phang}{cmdab:control(}{it:{help numlist:numlist}}{cmd:)} indicates which value the variable {opt treat:var(varname)} takes for the control group. This is usually equal to 0 when the treatment is binary, but may vary when dealing with multiple treatment arms.{p_end}

		{phang}{cmdab:effectformat(}{it:{help format:%fmt}}{cmd:)} specify the format in which treatment effect point estimate and standard error should be displayed in the graph note.{p_end}

	{pstd}{it:{ul:{hi:Estimation options:}}}{p_end}

		{phang}{opt abs:orb(varname)} indicates the fixed effects variable (for example, the experimental strata when the treatment was stratified) to be included in the estimation. This variable must be numerical.{p_end}

		{phang}{opt reg:ressionoptions(string)} indicates other options to be employed for the treatment effect estimations, for example suppress constant term ({opt nocons:tant} or clustered standard errors ({opt cl:uster(varname)}).{p_end}

		{phang}{opt kdensity:options(string)} specifies kernel estimation options, such as kernel function and half-width of kernel. The default kernel function is {opt:kernel(epanechnikov)}.{p_end}

	{pstd}{it:{ul:{hi:Graphic options:}}}{p_end}

		{phang}{opt color(string)} indicates the colors to be used for each treatment arm. The colors should come in the order of the values in {opt treat:var(varname)}. For instance, if the treatment is binary, you can set the line colors by typing {opt color(color1 color2)}. See {help colorstyle:colorstyle}. {p_end}

		{phang}{opt gr:aphoptions(string)} indicates other options to be applied to the graph, such as additional text and lines, changes axes, titles, and legeng, etc.. See {help twoway_options:twoway_options} for the full set of available options.{p_end}

{marker optslong}
{title:Examples}

	{pstd}All the examples below can be run on the Stata's built in automobile data set, by first running this code:{p_end}

	{pstd}* Open the built in data set{p_end}
	{pstd}{inp:sysuse auto}{p_end}

	{pstd}* Randomly assign time and treatment dummies{p_end}
	{pstd}{inp:gen treatment = (runiform() < .5)}{p_end}

	{pstd} {hi:Example 1.}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)}

	{pstd} This is the most basic way to run this command. This will output a graph with the distributions of of the variable of interests ({it:price} in this case) by treatment assignment.

	{pstd} {hi:Example 2.}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)}

	{pstd} This is an easy way to add descriptive information to the graph. This will output the same graph as above with the addition of two vertical lines for the medians of the control and treatment groups.

		{pstd} {hi:Example 2.1}

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)}

		{pstd} This changes the style of the median vertical lines.

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)}

		{pstd} {hi:Example 2.2}

		{pstd} This sets the colors of the control and treatment lines to different shades of blue.

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)} {inp:color(}eltblue edkblue{inp:)}

		{pstd} {hi:Example 2.3}

		{pstd} This changes some of the graphical options.

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:statstyle(}lpattern(dash) lwithd(2){inp:)} {inp:graphoptions(}title(auto distribution) subtitle(By Treatment Assignment) ylab(, angle(horizontal)) graphregion(color(white)) plotregion(color(white)){inp:)}

	{pstd} {hi:Example 3.}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:effect}

	{pstd} This adds a note to the graph, displaying the treatment effect in terms of point estimate, standard error and statistical significance.

		{pstd} {hi:Example 3.1}

		{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:stat(}{it:p50}{inp:)} {inp:effect} {inp:effectformat(}%9.0fc{inp:)}

		{pstd} This changes the format of the treatment effect in the note. The point estimate and the standard error now do not include any decimal points.

	{pstd} {hi:Example 4.1}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:effect} {inp:absorb(}{it:foreign}{inp:)}

	{pstd} The treatment effect is now derived from a regression controlling for the variable {it:foreign} fixed effects.

	{pstd} {hi:Example 4.2}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:effect} {inp:absorb(}{it:foreign}{inp:)} {inp:regressionoptions(}{cluster(foreign)}{inp:)}

	{pstd} The treatment effect is now derived from a regression controlling for the variable {it:foreign} fixed effects and clustering standard errors at {it:foreign} level.

	{pstd} {hi:Example 5}

	{pstd} {inp:iekdensity} {it:auto} , {inp:treatvar(}{it:treatment}{inp:)} {inp:kdensityoptions(}epan2 bwidth(5){inp:)}

	{pstd} The kernel density is estimated through the alternative Epanechnikov kernel function and half-width of the kernel is specified to be equal to 5.

{title:Acknowledgements}

	{phang}We would like to acknowledge the help in testing and proofreading we received in relation to this command and help file from (in alphabetic order):{p_end}
	{pmore}Luiza Andrade{break}

{title:Authors}

	{phang}All commands in ietoolkit are developed by DIME Analytics at DECIE, The World Bank's unit for Development Impact Evaluations.{p_end}

	{phang}Main author: Matteo Ruzzante, DIME, The World Bank Group.{p_end}

	{phang}Please send bug-reports, suggestions and requests for clarifications
			 writing "ietoolkit iekdensity" in the subject line to:{break}
			 dimeanalytics@worldbank.org{p_end}

	{phang}You can also see the code, make comments to the code, see the version
			 history of the code, and submit additions or edits to the code through
			 the GitHub repository of ietoolkit:{break}
	{browse "https://github.com/worldbank/ietoolkit"}{p_end}