Skip to content

vartype = "simu" is accepted for the kernel estimator but crashes - #29

Open
soodoku wants to merge 2 commits into
xuyiqing:masterfrom
soodoku:reject-simu-for-kernel
Open

vartype = "simu" is accepted for the kernel estimator but crashes#29
soodoku wants to merge 2 commits into
xuyiqing:masterfrom
soodoku:reject-simu-for-kernel

Conversation

@soodoku

@soodoku soodoku commented Aug 2, 2026

Copy link
Copy Markdown

interflex() accepts vartype = "simu" for every estimator (R/interflex.R:273), but R/kernel.R only implements the "bootstrap" (line 2034) and "delta" (line 2410) branches. With estimator = "kernel" and vartype = "simu", neither branch runs, the output objects are never built, and assembly fails with an internal error that gives the caller nothing to act on.

Reproduction

library(interflex)
set.seed(5); n <- 400
X <- rnorm(n); D <- rbinom(n, 1, 0.5)
Y <- 1 + 2*D + 0.5*X + 1.5*D*X + rnorm(n)
dat <- data.frame(Y, D, X)

interflex(estimator = "kernel", Y = "Y", D = "D", X = "X", data = dat,
          Xunif = FALSE, CI = TRUE, vartype = "simu", bw = 1,
          parallel = FALSE, figure = FALSE)

On master:

kernel vartype=delta      ok
kernel vartype=bootstrap  ok
kernel vartype=simu       Error: object 'diff.output.all.list' not found
linear vartype=simu       ok

Unconditional — independent of nsimu, diff.values, bandwidth and sample size.

Worth noting the default is safe: the signature sets vartype = "delta" (line 20), so ordinary calls are unaffected. Two ways in: passing vartype = "simu" explicitly, or passing vartype = NULL, which line 271 turns into "simu".

Change

Reject the combination during validation, where the other estimator/option constraints already live (e.g. the pcse-requires-linear check a few lines below):

if (estimator == "kernel" & vartype == "simu") {
    stop("\"vartype = 'simu'\" is not supported for estimator = \"kernel\"; use \"delta\" or \"bootstrap\".")
}

I have deliberately not tried to implement a simulation branch for the kernel estimator — that is a feature decision, and if you would rather have it than an error, this patch is the wrong change.

Verification

  • kernel + simu now stops with an actionable message; kernel + delta/bootstrap and linear + simu are unaffected.
  • Full suite via cd tests && NOT_CRAN=true Rscript testthat.R: FAIL 1 | SKIP 4 | PASS 344 both before and after. The single failure is test-dml.R:19 ("ml_method = "randomforest" requires the 'ranger' package"), which is a missing optional dependency in my environment, pre-existing on master and unrelated.

Separate observation, not addressed here

vcov.type never reaches the kernel estimator — the string does not occur in R/kernel.R, and interflex() forwards it only to the linear (line 1064) and binning (line 1128) estimators. With the bandwidth held fixed so nothing else varies, kernel standard errors are bit-identical under vcov.type = "robust" and vcov.type = "cluster":

KERNEL fixed bw, robust : 0.20552 0.15232 0.13660 0.15257 0.20801
KERNEL fixed bw, cluster: 0.20552 0.15232 0.13660 0.15257 0.20801   (identical)

On the same cluster-assigned data the linear estimator's clustered SEs are about 4.5x its robust ones, so the choice is not immaterial there.

I am raising this rather than patching it because ?interflex does scope vcov.type to "linear models", so ignoring it for the kernel estimator may well be intended. The part that seems worth a second look either way is that interflex() still requires cl when vcov.type == "cluster" (line 301) even for the kernel estimator, so a user is asked for a clustering variable that is then unused, with no warning. Happy to send a small warning patch if that would be useful.

interflex() validates vartype against c("simu","delta","bootstrap") for every
estimator, but kernel.R implements only the bootstrap and delta branches. With
estimator = "kernel" and vartype = "simu" neither runs, and output assembly
fails with "object 'diff.output.all.list' not found", which tells the caller
nothing.

The default is unaffected: the signature sets vartype = "delta". The two ways in
are passing "simu" explicitly, or passing NULL, which line 271 turns into
"simu".

Rejects the combination during validation, alongside the existing
estimator/option constraints. Full suite unchanged at FAIL 1 | SKIP 4 | PASS 344
(the one failure is a missing optional 'ranger' dependency, pre-existing).
@soodoku

soodoku commented Aug 2, 2026

Copy link
Copy Markdown
Author

Correcting my own closing observation above, having dug further — I was partly wrong and would rather say so than leave it standing.

I wrote that a user is "asked for a clustering variable that is then unused". That is only true under the default vartype = "delta". Under vartype = "bootstrap" the kernel estimator does use cl, and clustered inference works correctly:

                                      SEs at the 10/30/50/70/90th pct of X
delta,     vcov.type = "robust"     : 0.2055 0.1523 0.1366 0.1526 0.2080
delta,     vcov.type = "cluster"    : 0.2055 0.1523 0.1366 0.1526 0.2080
bootstrap, cl = "g"                 : 0.6576 0.6783 0.7007 0.7175 0.7406
bootstrap, no cl                    : 0.2034 0.1525 0.1397 0.1594 0.2147

(cluster-assigned treatment, 40 clusters, bandwidth fixed so nothing else varies)

The block bootstrap lands at ~0.66–0.74, against ~0.71 for the linear estimator's clustered SEs on the same data. So clustered inference for the kernel estimator is available and correct — via vartype = "bootstrap" + cl.

I also traced why vcov.type cannot simply be forwarded: the kernel path's analytic variance is fixed to heteroskedasticity-robust in four separate places — vcov(fe_res, vcov = "hetero") at kernel.R:199 and :271, vcov(iv.reg, type = "H2") at :376, and vcovHC(glm.reg, type = "HC2") at :507. There is no vcov dispatch for the argument to reach, so supporting it would mean threading a variance choice through all four sites and the delta-method assembly. That is a feature request, not a missing argument, and ?interflex already scopes vcov.type to linear models.

So please disregard that paragraph — there is no correctness problem there, and I am not proposing any change for it. The only thing that might still be worth a look is that interflex() requires cl when vcov.type == "cluster" even for estimator = "kernel", where the delta path will not use it; a one-line warning would close that, but it is entirely your call and I have not sent one.

None of this affects the vartype = "simu" fix in this PR, which is unchanged.

My first version rejected the combination unconditionally, which broke a call
pattern that works today. The whole vartype dispatch in kernel.R sits inside
`if (CI)` (bootstrap 2034, delta 2410, closing 2560), and the `if (!CI)` branch
at 2562 builds its output without consulting vartype at all. So
estimator = "kernel", vartype = "simu", CI = FALSE returns a valid interflex
object on master, and the unconditional stop() turned that into an error.

Now only the failing combination is rejected:

  vartype=simu      CI=TRUE  : rejected with a clear message
  vartype=delta     CI=TRUE  : ok
  vartype=bootstrap CI=TRUE  : ok
  vartype=simu      CI=FALSE : ok
  vartype=delta     CI=FALSE : ok
  vartype=bootstrap CI=FALSE : ok

CI is validated at interflex.R:256, before this check at :281.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant