The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
EDI’s statistical model-fitting is implemented once, in C++, under
R/EDI/src/, and exposed twice: to R via Rcpp
(fast_*_cpp exports) and to Python via pybind11
(edi_kernels, same argument names minus the
_cpp suffix, and 0-based instead of 1-based indices — see
below). This page documents the conventions and guarantees (and, just as
importantly, the non-guarantees) that hold across essentially every one
of those backend functions, so individual function docs can link here
rather than repeating “not validated by this function” verbatim on every
page. See vignette("notation-glossary") for symbol meanings
and vignette("reproducibility") for RNG/seed conventions
specifically.
Validation happens in the R6
Design/Inference layer, not in the
_cpp backend it calls. A public method like
InferenceContinOLS$compute_estimate() runs
checkmate assertions (gated by
should_run_asserts()/toggle_asserts()) on its
own arguments before calling into a fast_*_cpp
function — dimension checks, type checks, range checks, NA checks. The
backend function itself then does essentially none of that: it trusts
the shapes and values it receives. This means:
fast_*_cpp/edi_kernels.fast_* function
directly (bypassing the R6 class layer entirely — legitimate
for performance- sensitive code, and the whole point of the Python
bindings) skips the R6 layer’s validation. Malformed
input (wrong dimensions, non-finite values where they’re not expected,
an unsorted or malformed censoring structure) will not be caught with a
clear error message; the most likely outcomes are silently wrong
results, an opaque linear-algebra failure (e.g. a Cholesky/LDLT
decomposition throwing on a non-positive-definite matrix), or, in the
worst case, a segfault. Individual function docs that say “not validated
by this function” are describing exactly this boundary, not an
oversight.fixed_idx/fixed_values (the
optional-parameter-fixing mechanism shared by nearly every iterative
fitting backend) is validated at the C++ level, by
make_fixed_param_spec()
(_helper_functions_core.h): it checks
fixed_idx entries are in-range one-based indices, rejects
duplicates, and requires fixed_values to be finite and the
same length as fixed_idx — throwing
std::invalid_argument (surfaced as an R/Python error)
rather than silently misbehaving. This one mechanism is validated
everywhere specifically because a silently-wrong fixed-parameter index
would corrupt every downstream coefficient, not just one.Eigen::Map<const Eigen::MatrixXd> or
Eigen::Ref<const Eigen::MatrixXd>, \(n\) rows \(\times\) \(p\) columns. Whether an intercept column is
expected to already be included in \(X\) is function-specific, not a
package-wide rule — e.g. Cox regression
(fast_coxph_regression) never takes one (the partial
likelihood has no intercept), while most GLM-style fitters
(fast_ols, fast_logistic_regression,
fast_poisson_regression, …) expect the caller to add one if
wanted; the function’s own parameter doc states which._cpp exports take
Eigen::Map<const Eigen::MatrixXd> directly on an R
REALSXP’s data pointer: it is a genuine zero-copy view, not
a conversion. NumPy’s default array order is row-major (C order);
passing a row-major NumPy array across the pybind11/Eigen boundary to a
parameter typed Eigen::Ref<const Eigen::MatrixXd> can
therefore incur a silent copy (pybind11/Eigen handles the conversion
transparently, but not for free) — pass a Fortran-ordered
(order="F") array if avoiding that copy matters for a hot
path.j_treat, j_T,
fixed_idx) is 1-based on the R/Rcpp side
(matching R’s own 1-based vector/matrix indexing) and 0-based on
the Python-binding side (matching Python/NumPy convention) —
this is a deliberate per-language adaptation, not an inconsistency, and
is stated explicitly on every such parameter. Ordinal-response category
coding (y values \(1,\dots,K\)) is always 1-based in
both languages, since it labels categories rather than array
positions.y has
length \(n\) matching X’s
row count, that weights/fixed_values match
their paired index vector’s length (the one exception being
fixed_idx/fixed_values, checked as above), or
that a group_id/strata vector’s length matches
X. Passing mismatched lengths typically reads out-of-bounds
or silently truncates rather than erroring cleanly.Domain assumptions are almost never checked at the backend level (see “Validation boundary” above) but the numeric kernels themselves do guard against the specific overflow/underflow failure modes their own formulas are prone to:
fast_log1pexp(x) (softplus): for \(|x| \le 37\), uses a numerically stable
atanh-series identity rather than the naive log(1+exp(x)),
which would overflow exp(x) for large positive \(x\) long before the true (finite)
log-sum-exp value does; outside that range it returns the exact
asymptotic value (x itself, or exp(x))
directly.pnorm_fast(x)/fast_log_pnorm(x):
clamped to \([6\times10^{-16},
1-6\times10^{-16}]\) (respectively \([-35.05, -6.6\times10^{-16}]\) on the log
scale) for \(|x| \ge 8\), so an extreme
linear predictor cannot produce an exact \(0\) or \(1\) probability that would later become a
-Inf/NaN when log-transformed or divided by
downstream.logit()/inv_logit()
(other_helpers.R): both clamp their probability
argument/result to \([\texttt{zero\_one\_logit\_clamp},
1-\texttt{zero\_one\_logit\_clamp}]\) (default
.Machine$double.eps) before/after transforming, for the
same reason.EDI_SEPARATION_THRESHOLD
(globals.R, \(10^6\)). A coefficient magnitude
beyond this is treated package-wide as evidence of complete/
quasi-complete separation (the MLE does not exist, or a bootstrap/
likelihood-ratio replicate has diverged) rather than a genuine large
estimate — is_separated_coefficient_magnitude() centralizes
a check that used to be copy-pasted independently across several files.
(One documented exception:
inference_mixin_kk_gee_shared.R’s GEE family uses its own,
deliberately tighter, threshold of \(10^4\) for the same purpose.)fast_erfc(x): uses a Cephes piecewise
rational approximation for \(|x| \le
5.6\) and falls back to the platform libm
erfc beyond that, specifically to preserve accuracy in the
extreme tail outside the range the rational approximation was fit
against.None of these are “input validation” in the sense of rejecting bad input — they are numerical safety nets that keep a function returning a finite, sane value under the extreme arguments its own optimizer or a diverging model fit can legitimately produce, as distinct from the (mostly absent) checking of whether the input made statistical sense in the first place.
Nearly every iterative-fitting backend returns some variant of the
same small set of fields — edi::ResultMap’s
to_rcpp_list()/to_py_dict() converts whatever
fields a given backend .set()s into an R
list() or Python dict, so the
available fields differ by function, but their
meaning, where present, is package-wide:
| Field | Meaning |
|---|---|
converged |
Logical; whether the optimizer’s stopping criterion was met before
maxit was reached. FALSE does not necessarily
mean the returned coefficients are useless — it means the convergence
tolerance was not certified met, so downstream code should decide
whether to trust, retry (e.g. robust_survreg’s
random-restart loop), or reject the fit. |
iterations |
Integer count of optimizer iterations actually taken. |
gradient_norm |
The norm of the score/gradient at the returned parameter vector — a
continuous convergence diagnostic independent of the boolean
converged flag; useful for distinguishing “essentially
converged, tolerance was just slightly too tight” from “genuinely still
moving.” |
neg_loglik / neg_ll /
loglik |
The negative log-likelihood at the fit
(neg_loglik/neg_ll are aliases for the same
quantity — both are populated so callers used to either naming
convention find it) and, where included,
loglik = -neg_loglik computed only when finite
(NA/omitted otherwise) as a convenience so callers don’t
need to negate it themselves. |
vcov, std_err, z_vals |
The parameter covariance matrix, per-parameter standard errors
(sqrt(diag(vcov))), and Wald z-statistics
(coef / std_err) — present only on backends that were
called in variance-computing mode (see estimate_only
below). |
fisher_information / observed_information
/ information |
Three names for the same matrix on backends that
expose it (e.g. fast_ordinal_regression_with_var_cpp,
fast_cpoisson_combined_with_var,
fast_hurdle_negbin_with_var) — populated once, aliased
under all three names, since different call sites in the codebase
historically settled on different names for the identical quantity. |
estimate_only (an argument, not a return field) |
When TRUE, skips computing the
Hessian/Fisher-information-derived quantities above entirely (not just
omitting them from the return value) — a real performance path, used
e.g. inside bootstrap/randomization inner loops that only need a point
estimate per replicate, not a full covariance matrix. |
NA/NaN handlingThere is no single package-wide rule for what a backend does when it
receives NA/NaN in a numeric argument it does
not explicitly document handling for — behavior ranges from “propagates
cleanly to a non-finite result field” to “undefined/reads garbage,” and
is a direct consequence of the “domains are not checked” rule above.
Where a function does have documented
NA/NaN handling, it is because that function’s
job requires it (e.g. sample_mode_cpp() treats
NA — and, for doubles, NaN as a category
distinct from NA — as a first-class value that can itself
be “the mode,” because computing a mode over data that may contain
missingness is exactly the use case that function exists for). Do not
assume a fast_*_cpp fitting backend will produce a clean
NA in its output merely because its input contained one;
check the specific function’s own documentation.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.