Repository navigation
Conversation
…++ call and no output capture
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was assisted by Claude.
Summary
This PR builds on #574 (one model matrix for all draws). For non-Gaussian GLM submodels (and Gaussian submodels with a non-identity link),
divmin()still fits each projected draw separately:fit_glm_ridge_callback()callsglm_ridge(), which standardizes the shared model matrix again and runs IRLS in C++ (glm_ridge_c()). Each IRLS iteration and each line-search step calls the R functionpseudo_data()(withfamily$linkinv(),family$mu.eta(),family$variance()andfamily$deviance()), and each draw-wise fit runs inside twocapture.output()calls (infit_glm_ridge_callback()for theRcoutwarnings of the C++ code, and incapt_mssgs_warns()indivmin()). A profile ofcv_varsel(validate_search = TRUE)for a Poisson model showed 32 % of the time inpseudo_data()and about 8 % intextConnection()andsink().This PR makes three changes:
Rcout.glm_ridge()gets the argumentprint_msgs(defaultTRUE, so the output ofglm_elnet()does not change).fit_glm_ridge_callback()usesprint_msgs = FALSEand turns the messages into one warning as before. For this fitter,divmin()collects the messages and warnings with condition handlers (newcapt_conds(), based onwithCallingHandlers()). The other submodel fitters (lme4, mgcv, ordinal, ...) keepcapt_mssgs_warns().PseudoObsNativeinsrc/glmfun.cpp). The link functions are transcribed from R's stats package (make.link(), and for the logit linklogit_linkinv()andlogit_mu_eta()insrc/library/stats/src/family.c), including their thresholds. The variances and deviances are those of the stats families and ofextend_family(), and the code sums the deviance in long double as R'ssum()does. The IRLS loop is the existingglm_ridge()ofsrc/glmfun.cpp; this PR only moves the pseudo-observations behind an interface (PseudoObs) with an R-callback and a native implementation.native_pseudo_codes()selects it only iffamily$linkinv,family$mu.etaandfamily$variancehave the bodies of the stats family with the same name and link (cached per family and link); other families (for exampleStudent_t()or a modified family object) keep the R callback. brms reference models use the stats family objects, so they also get the native code. The internal optionprojpred.glm_native = FALSEswitches this off.glm_ridge_native_multi(), whichdivmin()calls after the Gaussian multi-draw solve of Faster submodel projections: one model matrix and one Gaussian solve for all draws #574, standardizes the shared model matrix once and callsglm_ridge_native_multi_c(), which runs the IRLS fit ofglm_ridge()for each draw from the same starting values. Thesubfitobjects have the same elements as those offit_glm_ridge_callback(), and the per-draw messages go to the samewarn_prj_drawwise()andcheck_conv().divmin()uses the draw-wise path as before for other families and links, an intercept-only submodel, unusable observation weights, and if...contains a formal argument ofglm_ridge()other thanfamily,weights,qa_updates_maxandls_iter_max.Results
The results are the same as on master. In the unit tests, the native pseudo-observations give bit-identical coefficients and weights for 7 family/link pairs on Linux (tested with tolerance 1e-12), and the multi-draw fits equal the draw-wise fits, also for non-converged fits. In the bench below, the rankings, the suggested sizes and the CV ranking proportions are identical to master, and the largest relative difference in the ELPD estimates is 1.9e-16.
Bench: rstanarm reference models, forward search, 1 CPU per run, all versions in the same job on one node (CSC Roihu, AMD EPYC 9965). Models: collinear (Poisson, N 200, P 4, from the projpred case studies), a simulated Bernoulli data set with logit and with probit link (N 400, P 30), and two models for the student data (binomial with 3 trials, N 407, P 26; Poisson, N 382, P 25).
vs=varsel(nterms_max = 15)(collinear: 4),cvt50=cv_varsel(validate_search = TRUE, nloo = 50, nterms_max = 10, refit_prj = TRUE),cvt= the same with full LOO. Times in seconds.The times of
cv_varsel(validate_search = FALSE)change as those ofvarsel().Tests
The test suite passes with
NOT_CRAN=true: 262 tests, 55 921 expectations, 0 failures, 0 errors, the same 4 warnings as master. Tested with this change applied to master 73cf0ee (projpred 2.11.0.9000) together with #574, R 4.6.1, rstanarm 2.32.2, brms 2.23.0, on CSC Roihu (Linux, AMD EPYC).New tests in
tests/testthat/test_glm_ridge.R:glm_ridge()are returned inmsgs, printed only withprint_msgs = TRUE, and turned into a warning byfit_glm_ridge_callback();native_pseudo_codes()returnsNULLforGamma(),poisson("sqrt"), a modifiedlinkinvand with the option set toFALSE;glm_ridge_native_multi()gives the samesubfitobjects and per-draw messages as the draw-wise fitter for binomial (logit, probit), poisson and gaussian (log link), with a factor predictor and observation weights, also withqa_updates_max = 2(non-converged fits); it returnsNULLforGamma()and ifoffsetis given;capt_conds()collects messages and multi-line warnings and passes errors on.The new tests fail on master (the functions and arguments do not exist there).
Overlap with other PRs
divmin(). The second PR to merge needs a rebase. The resolution keeps both changes:capt_fun(soutdmin <- do.call(sdivmin, c(list(...), dot_args))). The GLM multi-draw path of this PR returns before the removal of the observations with weight 0 that Leave the left-out observation out of the fold projections in validated LOO CV #580 adds. Leave the left-out observation out of the fold projections in validated LOO CV #580 removes these observations only for multilevel and additive submodels, and the C++ IRLS gives an observation with weight 0 a working weight of 0, aspseudo_data()does, so the GLM path needs no change.subfitobjects of this PR needterms = shared_x$terms. Keep the data-dependent bases #573 adds this element to the draw-wisesubfitobjects.