/* freeiv.dlg 1.0.0 06oct2026 A. Araar (Universite Laval / PEP) Dialog for -freeiv-: instrument-free estimation of a linear structural model. 1.0.0 follows freeiv 1.0.0. The estimators are listed in the order of the command: the interval (bounds, then ols, its upper end), order 3 (qme, the default, then hme), order 4 (gmm, then pgmm), the other maintained models (lsz, lewbel12, copula, rank, oster), all. The routes sce, rre, qbe, rpiv and ape are gone, and with qbe its quantile() box; fweights are gone; the Weights tab gains vce(svy), which takes the weights from svyset and so disables the weight controls when it is ticked. Self-contained on purpose. Version 0.8.0 borrowed -byifin-, -weights_fpa-, -_ifin_pr-, -_weights_pr- and -_level_pr- from the official dialogs and failed to parse with r(198). Only the three includes that every Stata installation carries are used here (_std_large, header, _sp_level), _sp_level is given the _x / _y / _cx macros it positions its spinner with, and the by/if/in and Weights tabs are written out in full. A dialog that borrows anything else breaks the day the command it borrowed from changes. The Estimator options tab follows the estimator: delta() and rmax() belong to Oster, sign() to LSZ; every other route takes none of them, and the default qme takes none at all. onselchangelist(methacts) maps each entry of -methods- to the script that shows the group box that applies and hides the rest. Visibility and emission are kept apart: the scripts hide and show, and PROGRAM command decides what is written from the combobox itself (main.cb_m.iseq(), as esreg.dlg does), so that a dialog filled in by freeiv_examples writes the right options whether or not the scripts ran. The dialog writes the command a user would type, the one the examples of help freeiv list (freeiv_examples fills it in, and OK must run the same command as the example's link in the command window): the estimator's default is the value qme, as the official dialogs give a combobox's default (betareg.dlg: default(logit)), and /hidedefault keeps method(qme) off the command line; the weights are written [pweight=w]. Ticking the two-indicator box hides both boxes. A program cannot read the value of a combobox, so unticking returns the tab to the "no option" state -- which is the state of the default estimator; picking a method again restores its box. The endogenous side is two VARNAME boxes rather than one VARLIST, so "one variable, or two of them" is enforced by the controls. A checkbox drives the two-indicator model: VARNAME and VARLIST carry no event, so a box being filled cannot by itself grey out the estimator list. The checkbox writes nothing to the command line -- the number of variables inside the parentheses is what selects the model. keyword: eclass */ VERSION 16.0 INCLUDE _std_large DEFINE _dlgwd 620 DEFINE _iwd 600 DEFINE _inwd 580 DEFINE _vlx 320 DEFINE _vlwd 290 DEFINE _dlght 420 INCLUDE header HELP hlp1, view("help freeiv") RESET res1 SCRIPT PREINIT BEGIN create BOOLEAN st_none create BOOLEAN st_oster create BOOLEAN st_lsz create BOOLEAN st_all create BOOLEAN st_two END SCRIPT POSTINIT BEGIN script m_none script two_off program wgt_ck program byifin_ck END /* -------------------------------------------------------------- Model -- */ DIALOG main, title("freeiv - Instrument-free estimation") /// tabtitle("Model") BEGIN TEXT tx_dv _lft _top 170 ., /// label("Dependent variable") DEFINE holdy @y VARNAME vn_dv @ _ss @ ., /// ts /// numeric /// label("Dependent variable") TEXT tx_en 200 holdy 410 ., /// label("Endogenous variable") VARNAME vn_en @ _ss @ ., /// ts /// numeric /// label("Endogenous variable") CHECKBOX ck_two _lft _ls _iwd ., /// onclickon(script two_on) /// onclickoff(script two_off) /// label("Two-indicator model: a second endogenous regressor shares the confounder") TEXT tx_en2 _ilft _ss _inwd ., /// label("Second endogenous variable sharing the same confounder") VARNAME vn_en2 _ilft _ss 290 ., /// ts /// numeric /// label("Second endogenous variable") TEXT tx_x _lft _ls _iwd ., /// label("Exogenous controls (optional) - i.sex and ib2.region are accepted") VARLIST vl_x _lft _ss _iwd ., /// fv ts /// allowcat /// numeric /// label("Exogenous controls") TEXT tx_m _lft _ls 260 ., /// label("Estimator (one endogenous regressor only)") COMBOBOX cb_m 280 @ 300 ., /// dropdownlist /// contents(methods) /// values(methodv) /// onselchangelist(methacts) /// option("method") /// default(qme) /// label("Estimator") END SCRIPT two_on BEGIN st_two.settrue program two_ck END SCRIPT two_off BEGIN st_two.setfalse program two_ck END /* show/hide only. Mixing .hide with .disable on one control leaves it visible, so visibility and emission are kept apart: this program hides, and PROGRAM command guards what is written. */ PROGRAM two_ck BEGIN if main.ck_two { call main.tx_en2.enable call main.vn_en2.enable call main.tx_m.hide call main.cb_m.hide } if !main.ck_two { call main.tx_en2.disable call main.vn_en2.disable call main.tx_m.show call main.cb_m.show } program check_method END /* the order of freeiv 1.0.0: the interval, order 3, order 4, the other maintained models, all */ LIST methods BEGIN identified set only ordinary least squares (upper end of the set) quadratic moment estimator (default) higher moments, V1 and V2 symmetric GMM on orders 2, 3 and 4, joint GMM on orders 2, 3 and 4, profiled Lewbel, Schennach and Zhang (2024) Lewbel (2012), heteroskedasticity copula (Park and Gupta 2012) rank control function (Breitung et al. 2024) Oster (2019), coefficient stability all of the above END /* One entry per entry of -methods-, in the same order: bounds ols qme hme gmm pgmm -> no option on that tab lsz -> sign() lewbel12 copula rank -> no option oster -> delta() and rmax() all -> both boxes */ LIST methacts BEGIN script m_none script m_none script m_none script m_none script m_none script m_none script m_lsz script m_none script m_none script m_none script m_oster script m_all END LIST methodv BEGIN bounds ols qme hme gmm pgmm lsz lewbel12 copula rank oster all END /* -------------------------------------------------- Estimator options -- */ DIALOG opts, tabtitle("Estimator options") BEGIN /* Shown when no group box applies. They sit where the first group box sits: never more than one of them is visible at that spot. */ TEXT tx_noopt _lft _top _iwd ., /// label("The selected estimator takes no option on this tab.") TEXT tx_twonote _lft _ss _iwd ., /// label("Only level(), vce(svy) and the header switch apply to it.") GROUPBOX gb_o _lft _top _iwd 115, /// label("Oster (2019): both of these are ASSUMED, not estimated") TEXT tx_d _ilft _ss 300 ., /// label("delta(): selection on unobservables / on observables") EDIT ed_d 340 @ 200 ., /// numonly /// option("delta") /// default(1) /// label("delta") TEXT tx_dn _ilft _ss _inwd ., /// label("1 means the unobservables matter as much as the controls") TEXT tx_r _ilft _ms 300 ., /// label("rmax(): R-squared once the unobservables are included") EDIT ed_r 340 @ 200 ., /// numonly /// option("rmax") /// label("rmax") TEXT tx_rn _ilft _ss _inwd ., /// label("leave it blank for the default, min(1.3 R1, 1)") TEXT tx_gap _lft _ss 10 ., /// label(" ") GROUPBOX gb_s _lft +20 _iwd 90, /// label("Lewbel, Schennach and Zhang (2024)") TEXT tx_s _ilft _ss _inwd ., /// label("Orientation of the solution, as in -trigmm-") RADIO rb_s1 _ilft _ss _inwd ., first /// label("sign(1)") RADIO rb_s2 @ _ss @ ., last /// option("sign(-1)") /// label("sign(-1)") END /* ---------------------------------------------------------- Reporting -- */ DIALOG rep, tabtitle("Reporting") BEGIN DEFINE _x _lft DEFINE _y _top DEFINE _cx _spr INCLUDE _sp_level CHECKBOX ck_nohd _lft _ls _iwd ., /// option("noheader") /// label("Suppress the header") END /* ---------------------------------------------------------- by/if/in -- */ DIALOG byifin, tabtitle("by/if/in") BEGIN CHECKBOX ck_by _lft _top _iwd ., /// onclickon(program byifin_ck) /// onclickoff(program byifin_ck) /// label("Repeat the command for each group defined by") VARLIST vl_by _ilft _ss _inwd ., /// label("by variables") TEXT tx_if _lft _ls 580 ., /// label("Restrict the estimation to the observations satisfying (if)") EDIT ed_if _lft _ss 590 ., /// label("if expression") TEXT tx_in _lft _ls 250 ., /// label("Range of observations (in), for example 1/100") EDIT ed_in _vlx @ 120 ., /// label("in range") END PROGRAM byifin_ck BEGIN if byifin.ck_by { call byifin.vl_by.enable } if !byifin.ck_by { call byifin.vl_by.disable } END /* ------------------------------------------------- Weights and survey -- */ DIALOG wgt, tabtitle("Weights/survey") BEGIN GROUPBOX gb_wgt _lft _top _iwd 90, /// label("Weight type") RADIO rb_wnone _ilft _ss _inwd ., first /// onclickon(program wgt_ck) /// label("No weights") RADIO rb_wpw @ _ss @ ., /// onclickon(program wgt_ck) /// label("Sampling weights (pweight): standard errors in sandwich form") RADIO rb_waw @ _ss @ ., last /// onclickon(program wgt_ck) /// label("Analytic weights (aweight)") TEXT tx_wexp _lft _ls 200 ., /// label("Weight variable or expression") EDIT ed_wexp _vlx @ _vlwd ., /// label("Weight expression") GROUPBOX gb_svy _lft _xls _iwd 70, /// label("Survey data") CHECKBOX ck_svy _ilft _ss _inwd ., /// onclickon(program wgt_ck) /// onclickoff(program wgt_ck) /// option("vce(svy)") /// label("vce(svy): the weights, strata, PSUs and fpc declared by svyset") TEXT tx_svyn _ilft _ss _inwd ., /// label("standard errors linearized over the design, t on its degrees of freedom") END PROGRAM wgt_ck BEGIN if wgt.ck_svy { call wgt.rb_wnone.disable call wgt.rb_wpw.disable call wgt.rb_waw.disable call wgt.tx_wexp.disable call wgt.ed_wexp.disable } if !wgt.ck_svy { call wgt.rb_wnone.enable call wgt.rb_wpw.enable call wgt.rb_waw.enable if wgt.rb_wnone { call wgt.tx_wexp.disable call wgt.ed_wexp.disable } if !wgt.rb_wnone { call wgt.tx_wexp.enable call wgt.ed_wexp.enable } } END /* ---------------------------------------------- the options tab -------- */ /* Each entry of -methacts- runs a SCRIPT that sets one of four BOOLEANs and then calls -check_method-, which reads them. Only a SCRIPT can set a BOOLEAN; a PROGRAM cannot. That indirection is also what lets the two-indicator checkbox restore the right group box when it is unticked: the method's own state is still recorded in the booleans. Each branch states the whole tab, so no BOOLEAN is ever negated, and st_two is tested last and therefore wins. Visibility only: hiding a control does not keep it off the command line, and PROGRAM command decides what is written from the combobox itself. */ SCRIPT m_none BEGIN st_none.settrue st_oster.setfalse st_lsz.setfalse st_all.setfalse program check_method END SCRIPT m_oster BEGIN st_none.setfalse st_oster.settrue st_lsz.setfalse st_all.setfalse program check_method END SCRIPT m_lsz BEGIN st_none.setfalse st_oster.setfalse st_lsz.settrue st_all.setfalse program check_method END SCRIPT m_all BEGIN st_none.setfalse st_oster.setfalse st_lsz.setfalse st_all.settrue program check_method END PROGRAM check_method BEGIN if (st_none) { call opts.gb_o.hide call opts.tx_d.hide call opts.ed_d.hide call opts.tx_dn.hide call opts.tx_r.hide call opts.ed_r.hide call opts.tx_rn.hide call opts.gb_s.hide call opts.tx_s.hide call opts.rb_s1.hide call opts.rb_s2.hide call opts.tx_noopt.show call opts.tx_twonote.hide } if (st_oster) { call opts.gb_o.show call opts.tx_d.show call opts.ed_d.show call opts.tx_dn.show call opts.tx_r.show call opts.ed_r.show call opts.tx_rn.show call opts.gb_s.hide call opts.tx_s.hide call opts.rb_s1.hide call opts.rb_s2.hide call opts.tx_noopt.hide call opts.tx_twonote.hide } if (st_lsz) { call opts.gb_o.hide call opts.tx_d.hide call opts.ed_d.hide call opts.tx_dn.hide call opts.tx_r.hide call opts.ed_r.hide call opts.tx_rn.hide call opts.gb_s.show call opts.tx_s.show call opts.rb_s1.show call opts.rb_s2.show call opts.tx_noopt.hide call opts.tx_twonote.hide } if (st_all) { call opts.gb_o.show call opts.tx_d.show call opts.ed_d.show call opts.tx_dn.show call opts.tx_r.show call opts.ed_r.show call opts.tx_rn.show call opts.gb_s.show call opts.tx_s.show call opts.rb_s1.show call opts.rb_s2.show call opts.tx_noopt.hide call opts.tx_twonote.hide } if (st_two) { call opts.gb_o.hide call opts.tx_d.hide call opts.ed_d.hide call opts.tx_dn.hide call opts.tx_r.hide call opts.ed_r.hide call opts.tx_rn.hide call opts.gb_s.hide call opts.tx_s.hide call opts.rb_s1.hide call opts.rb_s2.hide call opts.tx_noopt.show call opts.tx_twonote.show } END /* ----------------------------------------------------------- command -- */ PROGRAM command BEGIN if byifin.ck_by { require byifin.vl_by put "by " byifin.vl_by ", sort : " } put "freeiv " require main.vn_dv varlist main.vn_dv [main.vl_x] put " (" require main.vn_en if main.ck_two { require main.vn_en2 varlist main.vn_en [main.vn_en2] } if !main.ck_two { varlist main.vn_en } put ")" if byifin.ed_if { put " if " byifin.ed_if } if byifin.ed_in { put " in " byifin.ed_in } /* under vce(svy) the weights are those of svyset: none is written */ if !wgt.ck_svy { if !wgt.rb_wnone { require wgt.ed_wexp put " [" if wgt.rb_wpw { put "pweight" } if wgt.rb_waw { put "aweight" } put "=" wgt.ed_wexp "]" } } beginoptions /* Hiding a control does not keep it off the command line, so the BOOLEANs that decide what is shown decide here too -- the same pattern DASP's ineq.dlg uses to guard an optionarg with has_index8. One flat -if- per state, never nested. */ /* the two-indicator model refuses them all, so its box wins. What is written follows the estimator read from the combobox itself (iseq), not the booleans of the scripts: a dialog filled in from outside -- freeiv_examples, db -- sets the combobox without being sure its script runs. */ if !main.ck_two { optionarg /hidedefault main.cb_m } if (!main.ck_two & (main.cb_m.iseq("oster") | main.cb_m.iseq("all"))) { optionarg /hidedefault opts.ed_d optionarg opts.ed_r } if (!main.ck_two & (main.cb_m.iseq("lsz") | main.cb_m.iseq("all"))) { option opts.rb_s2 } option wgt.ck_svy option rep.ck_nohd optionarg /hidedefault rep.sp_level endoptions END