A correctness follow-up to 3.2.0, focused on boot_lucid() and summary().
Bootstrap CIs now align cluster labels. Each bootstrap replicate is an
independent refit whose latent cluster k need not be the point estimate's
cluster k. Replicate coefficient vectors were stacked by name/position anyway,
so any coefficient whose canonical cluster ordering was weakly identified -- a
parallel layer with a near-null cluster-to-outcome effect, an upstream serial
stage ordered by a single omics feature -- could get a bimodal or sign-split
interval spanning orders of magnitude. boot_lucid() now permutes every
replicate's clusters to match the reference fit (by posterior-probability
overlap on the resampled subjects) before extracting coefficients, so each
interval brackets its own point estimate. The point estimate (bootstrap$t0)
is unchanged.
summary() bootstrap CI tables.
(3) E) and binary-outcome ((1) Y) CI tables now
carry an odds-ratio view -- OR, OR_lower, OR_upper = exp() of the
coefficient-scale estimate and interval bounds -- alongside the
coefficient-scale columns. sig is still computed on the coefficient scale
(null = 0).OR
column is shown), the CI table says the columns are coefficients on the
log-odds scale with a bootstrap interval.(3) E CI table now shows the intercept and every covariate (CoG) row,
so it has the same coefficient rows as summary(fit) without a bootstrap --
previously those rows silently vanished when boot.se was supplied. The
covariate row is also now shown in the no-CI (3) E table for the parallel
and serial models, matching the early model.f.binary.early joins the bootstrap CI to the point estimate by row name
rather than by position.Log-likelihood labeling. The "Finished LUCID ... model: penalized
log-likelihood = X" completion message printed the unpenalized observed-data
value; it now says "observed-data log-likelihood". em_control$loglik_trace is
the unpenalized observed-data log-likelihood for every model type (it was the
penalized objective for a penalized early fit), so its last value equals
$likelihood; the penalized objective still drives the internal
convergence/dip check.
This release follows directly on 3.1.0's correctness pass. It narrows the
exported API to what a user actually calls, adds four extractor functions
for a fitted model's output, marks statistically significant bootstrap CI
rows in summary(), fixes several consistency and reliability issues across
model types, and reworks the tutorial vignettes into a polished, user-facing
set built entirely on the public API.
Breaking change. Five numerical-stability helpers
(check_and_stabilize_sigma(), safe_solve(), safe_log_sum_exp(),
check_convergence(), safe_normalize()) and fill_data() are no longer
exported. They were internal EM building blocks that happened to have a
CRAN manual page; their behavior is unchanged, and they remain reachable as
LUCIDus:::fn(...) for anyone who was calling them directly. The bare
plot() S3 generic LUCIDus declared is also removed -- graphics::plot()
already provides it, and plot.early_lucid() etc. still dispatch normally.
New: model-type autodetection. predict_lucid() and boot_lucid() no
longer require lucid_model -- it is now optional and is auto-detected from
class(model). Explicitly passing it still works exactly as before
(including the existing mismatch error when it disagrees with model's
class), so no existing call site is affected; it simply becomes unnecessary
to specify.
New: extractor functions. Four new functions read directly off a fitted model and auto-detect early/parallel/serial themselves, so no branching on model type is needed at the call site:
get_selected_G() -- the selected exposures, as a named logical vector.get_selected_Z() -- the selected omics features, shaped by model type
(a vector, or a list by layer/stage).get_cluster_assignment() -- each subject's hard cluster assignment.get_top_omics_features() -- the top-n most important omics features
per layer/stage, ranked by the same criterion plot_cluster_omic_profile()
uses.New: significance markers in summary(). Every bootstrap CI table
summary() prints when boot.se/se is supplied -- the exposure-to-cluster
table, the cluster-to-outcome table, and the cluster-specific omics-mean
table -- now includes a sig column marking rows whose normal-theory
interval excludes 0 with "*".
Bug fixes. The serial model's returned object is now consistent with
early and parallel: it reports a likelihood and a select field the way
the other two model types always have, a stray unused Rho_G penalty is no
longer silently applied where it has no exposures to act on, and its console
progress messages use the same wording and level of detail the other two
model types use. Several input-validation gaps that only showed up for
specific model types or specific entry points (estimate_lucid(),
tune_lucid(), predict_lucid(), boot_lucid()) are closed, so a bad input
is now reported with a clear message regardless of which model type or
function it went through, rather than surfacing later as a confusing
downstream error.
Reliability. Convergence checking is now handled by a single, shared routine for every model type, and the optimizer's log-likelihood is checked at every iteration to confirm it is actually improving, with a warning if it ever isn't. A handful of tuning and bootstrap code paths that could previously abort an entire multi-candidate run over one bad candidate now skip that candidate and continue, matching how the other paths already behaved.
Internal. Every internal (non-user-facing) function in the package now has real documentation explaining what it does, and source files have been reorganized and renamed so that related functionality lives together and file names describe their contents -- neither change affects behavior, and both are aimed at making the package easier to review and maintain. Several long-unused, dead code paths were also removed.
Vignettes. helix_early_parallel_workflow.Rmd is replaced by two
files, lucid_3models_normal_outcome.Rmd and
lucid_3models_binary_outcome.Rmd, split by outcome family (both still
demonstrate all three model architectures). Every vignette is rewritten as a
user-facing tutorial: custom code that reimplemented feature-selection
extraction, hard cluster assignment, or omics-importance ranking is replaced
by the new extractor functions and lucid_model autodetection, sections that
walked through internal-only functions (fill_data(), the numerical-stability
helpers) are removed, and each major step gains an explanation of what it
does, why it's needed, and how to read its output.
Packaging. The README.md is no longer shipped inside the package
tarball (it remains in the source repository), and the paper-reproduction
vignette (rjournal_paper_examples.Rmd) is no longer part of the package.
Example, vignette and test runtime is trimmed so a full R CMD check
completes well within CRAN's time budget: heavy examples run on small data
subsets with capped EM iterations, vignette bootstrap sizes are reduced, and
essentially the whole model-fitting regression suite is marked
skip_on_cran() -- those tests still run in full locally and in CI; only the
fast oracle, input-validation and tightly-capped smoke tests run on CRAN.
This release is a correctness pass over the estimation, missing-data, prediction, model-selection and inference paths, validated against the statistical definitions in the two LUCID papers:
These change numbers that version 3.0.x produced. Analyses run with 3.0.x may need to be re-run.
Reported log-likelihood was wrong. summary() reported a value that was
not the observed-data log-likelihood of the fitted model. For the worked
example in the R Journal paper (fit1 on simulated_HELIX_data), 3.0.3
reported -6721.801; the correct value at 3.0.3's own maximum-likelihood
estimates is -6575.88. The BIC printed alongside it inherited the error, and
also counted K*M^2 covariance parameters rather than the K*M*(M+1)/2 of
Eq 13. Both are now correct.
Outcome coefficients were mislabelled. res_Gamma$beta held the absolute
cluster means, but summary() printed them as if reference-coded
(cluster1 = 0, cluster2 = <value>), so the second row read as a
between-cluster contrast when it was that cluster's own mean. For the paper's
fit1, the printed 0.504 was cluster 2's mean; the actual contrast between
clusters is 1.044. Outcome parameters are now stored in both forms and
printed as an intercept (cluster 1's level) plus explicit contrasts.
predict_lucid() returned 0-based cluster labels. pred.x was
0, ..., K-1 while Eq 21, summary(), inclusion.p columns and the mu /
beta row names all use 1, ..., K. It now returns 1, ..., K for early,
parallel, serial and g-computation. Code that added 1 to pred.x must drop
that adjustment.
Penalized BIC now implements Eq 18. BICp = -2 logL + (D - DG - DZ) log N
subtracts one parameter per deselected variable. The previous
implementation rebuilt the model dimension from only the selected features,
removing whole mean, covariance and regression blocks, which could select a
different penalty and therefore a different final model. Note that D counts
the multinomial-logit intercepts, making it larger than Eq 13 as printed by
K - 1; the intercepts are genuinely estimated parameters.
K >= 2 was not checked on every path that fits or tunes a model.
lucid() checked it for early, tune_lucid()'s parallel branch checked it,
and estimate_lucid()'s serial branch checked it, but est_lucid() itself
(early and parallel) and tune_lucid()'s own early branch did not, so
calling estimate_lucid()/tune_lucid() directly with K = 1 -- both are
documented as directly callable -- reached deep EM machinery before failing
with an obscure error instead of a clear one. All four paths now share the
same check.
predict_lucid() validated its two branches asymmetrically. Early
checked that model was an early_lucid object; parallel had no analogous
check. Parallel checked that Z had the right number of layers; early
never checked that Z had the right number of columns, so a wrong-width Z
failed inside Estep_early() with a linear-algebra error instead of a
clear one. Both branches now validate both things.
boot_lucid() skipped validation the fitting functions it wraps already
enforce. None of its three branches called the same G/Y/CoG/CoY
completeness check lucid()/estimate_lucid() run, so a missing value
failed confusingly inside boot::boot()'s resampling callback instead of
with the package's normal top-level error; nor did any branch check that
model's class matched the lucid_model argument. Both checks are now
applied uniformly across all three branches.
tune_lucid()'s serial branch could crash on one bad candidate, and had
an unguarded tie-break. Early and parallel wrap each grid candidate in
try(), record NA on failure, and only stop() if every candidate
failed; serial did neither, so one non-converging K (e.g. too many
clusters for the sample size) crashed the whole tuning run, and picking the
best model (which(bic == min(bic)), no tie-break) would error on a
genuine BIC tie the way early/parallel are already written to guard
against. Serial now matches early/parallel's behavior exactly.
Serial Rho_G was applied to a stage's incoming cluster probabilities as
if they were exposures. From stage 2 onward, a serial fit's "G" input is
the previous stage's posterior cluster-membership probabilities, not the
cohort's actual exposures -- there is nothing there for an exposure-selection
penalty to select among. Rho_G was only reset to 0 when that stage's
incoming pseudo-G happened to have fewer than 2 columns, so a stage with 2 or
more forwarded columns (e.g. following a stage with K >= 4) was silently
penalized on a quantity that isn't an exposure. Rho_G is now forced to 0 for
every stage after the first, unconditionally. This changes numeric output for
that specific combination (nonzero Rho_G, a later stage with K >= 4
feeding forward >= 2 non-reference columns); it does not affect the more
common case already covered by the old guard.
A serial fit's likelihood and select were commented-out dead code.
R/EM_all.R's serial results list referenced loglik_update, selectG,
selectZ and Estep_r, none of which are ever assigned in the serial
branch -- copy-paste leftovers from the early/parallel branches, left
commented out. fit$likelihood and fit$select now exist on a serial fit,
matching early and parallel: likelihood is the sum of each stage's own
log-likelihood (matching the existing, previously summary()-only
cal_loglik_serial()), and select is stage 1's own selection (the only
stage whose selectG is a real exposure-selection result, per the fix
above); the complete per-stage record remains at
fit$submodel[[i]]$select and fit$submodel[[i]]$likelihood. The dead #z
= line is removed rather than resurrected: nothing in the package reads
z even where it already exists, on parallel fits.
check_convergence() was exported, documented and tested, but never
called. Early and parallel each ran their own abs(old - new) < tol
convergence check instead. Both now call the shared, tested function.
No log-likelihood trace was ever checked for monotonicity. A decrease
beyond a small, majorization/selection-aware slack now raises a warning()
naming the iteration (or serial stage) and the two values, for all three
model types -- previously nothing would have surfaced a real ascent
violation short of manually inspecting em_control$loglik_trace.
Serial's verbose output mixed "Sub Model" and "Stage" naming for the same concept, and never showed a log-likelihood. The pre-stage banner now says "Stage N/M" consistently, and both the verbose and concise per-stage finish lines report that stage's log-likelihood, matching early/parallel's own per-iteration reporting.
A second round of console-output inconsistencies, found while auditing
the rest of the package for the same pattern. lucid()'s tuned-selection
messages and tune_lucid()'s "Tuning LUCID model" banner printed
unconditionally instead of respecting verbose_tune; predict_lucid()'s
g-computation notice printed unconditionally instead of respecting
verbose, and its verbose argument otherwise did nothing for early/
parallel prediction while serial always printed regardless of it -- early
and parallel prediction now report progress under the same flag serial
already did. predict_lucid()'s serial per-stage message also still said
"Sub Model Number = N", the phrasing already replaced with "Stage N/M"
everywhere else. The three "fit completed" messages ("Success: Early
Integration LUCID converges!" / "Success: LUCID in parallel converges!"
/ "Success: LUCID in Serial Model is constructed!") and estimate_lucid()'s
own two differently-worded serial "Fitting..." banners are now one
consistent pattern each.
A serial fit's inputs were not validated for completeness at the top
level. check_complete_input() (the G/Y/CoG/CoY missing-value guard) was
called from lucid() and from the early path, but not from EM_all.R's
serial orchestration -- a missing value was only caught once stage 1's
nested fit re-validated it, reporting the error as coming from inside a
sub-fit. It is now called once at the top of the serial branch.
predict_lucid() failed whenever lucid_model was not named explicitly.
The model type was resolved with match.arg() only inside the branch tests,
while the unresolved default -- the full vector
c("early", "parallel", "serial") -- was forwarded to the internal worker,
whose own choices are c("early", "parallel"). Every such call died with
'arg' must be of length 1. This is exactly how the R Journal paper documents
prediction in Section 3.5 (predict_lucid(model = fit1, G = ..., Z = ...)),
so that example had never run as printed, in this version or in 3.0.3. The
argument is now resolved once, at entry.
Prediction with sporadically missing omics. Rows containing NA were
passed to a full multivariate Gaussian density. For the early model this
returned a uniform posterior for every affected row; for the parallel model it
raised NA/NaN/Inf in foreign function call. Both now evaluate the correct
marginal density on the observed coordinates, which also makes a fully
unobserved row reduce to the list-wise partition of vbae123 Eq 11.
Parallel penalized exposure model discarded posterior mass. glmnet's
multinomial fit returns one coefficient block per class, and f_GtoX() then
prepended a further zero reference row, producing K + 1 states. The E-step
used only the first K, silently dropping roughly 33% of the probability mass
at K = 2 and 25% at K = 3. Coefficients are now rebased onto a single
reference class and stored as K - 1 rows.
Parallel penalized omics selection never deselected anything. The
indicator OR-ed in colSums(abs(Sigma)) > 0.001, which is always true for a
valid covariance. Selection is now driven by the cluster means, and parallel
penalized estimation uses the same estimator as the early model (L1-penalized
means via the graphical-lasso precision matrix).
Variable selection could abort before returning a model. When exactly one
exposure survived selection, lucid() carried the tuned Rho_G into the
refit, where the lasso requires at least two exposures, and the call failed.
The refit on selected features is now unpenalized; the tuned penalties and the
original selection are retained on the fitted object ($Rho, $selection).
init_impute = "lod" fabricated omics values. Rows with no measured omics
at all were filled with LOD/sqrt(2) and returned in fit$Z, unlike the
"mix" path which restores them. Estimation was unaffected, but the returned
data were not. All initializers now restore fully-missing rows to NA.
Bootstrap t0 was a random refit. The observed-data statistic re-estimated
the model under a random seed, so bootstrap$t0 need not equal the model
passed in and summary(fit, boot.se = ...) could print an estimate column
inconsistent with summary(fit). t0 is now taken from the supplied model.
Failed bootstrap replicates were silent. Replicates returning non-finite
estimates were dropped without notice, quietly reducing the effective R --
most likely with missing data, where a resample can contain too few rows with
complete omics. Failures are now counted and warned about. A small number of
replicates also raises a warning that the limits are unstable (Davison &
Hinkley suggest R >= 200 for normal and R >= 800 for percentile intervals).
Neither suppresses the limits: they are reported as NA only when fewer than
min_valid replicates survive, which defaults to 2 -- the point below which
a standard deviation and an order-statistic interval are undefined rather than
merely imprecise. boot_lucid() gains a min_valid argument for callers who
want a stricter rule.
Cross-layer missingness summary. check_na(..., "parallel") reported the
unweighted mean of per-layer missingness rates rather than the proportion of
missing cells; a fully-missing 1-feature layer beside a complete 9-feature
layer reported 0.50 instead of 0.10.
Bootstrap replicate failures no longer print raw error dumps. The
per-replicate refit was wrapped in capture.output(), which redirects stdout
while try() writes to stderr, so a failed replicate printed a raw
Error in est_lucid(...) trace even though the failure was handled. Two
sibling call sites already passed silent = TRUE; the third now does too.
Recovered failures no longer print raw error dumps. When the penalized
exposure model could not be fitted (for example glmnet reporting
Null probability for class 1 < 1.0e-5 on a near-degenerate cluster), the
code correctly fell back to unpenalized estimation and warned -- but the
underlying try() was not silent, so the raw C++ error text was also printed.
A single document could emit a dozen of these, each looking like a failure
when nothing had actually gone wrong. The fallback warning is unchanged; only
the spurious error text is gone. Affects Mstep_G(), the parallel
Mstep_GtoX(), and the early tuning loop.
Bootstrap confidence-interval tables no longer collapse to a vector when there
is a single exposure or K = 2.
Penalty tuning no longer performs recursive list indexing when several candidate models tie on BIC.
glasso is no longer called when Rho_Z_Cov = 0, where it reduces to the
empirical covariance but emitted a rank warning on every cluster, layer and
iteration (hundreds of identical warnings per fit).
lucid_serial fits now report whether they converged. A serial fit runs
no EM loop of its own, so its em_control carried only the stopping controls
(tol, max_itr, max_tot.itr). A stage that exhausted max_itr was
therefore invisible unless the user inspected every submodel[[i]]$em_control
by hand. em_control now also reports converged (TRUE only when every
stage converged), n_iter (the total across stages), and the per-stage
submodel_converged and submodel_n_iter.
summary() on a serial fit no longer returns duplicate components. The
returned list appended a second copy of BIC and loglik under names it
already used, so it held two entries for each. $BIC resolved to the first,
making the copies unreachable, but str() output looked malformed and
name-based iteration visited them twice. The duplicates are removed; $BIC,
$loglik and the legacy $summary.list alias are unchanged.
Missing or non-finite values outside the omics data are now rejected.
LUCID models missingness in Z; a missing value in G, Y, CoG or CoY
was handled by no code path at all. A single NA in Y did not error --
the fit ran on without converging, and where it did return, the outcome
parameters were badly wrong with nothing in the output to indicate it. These
four inputs are now checked for NA, NaN and infinite values before
fitting, with an error naming the input and the number of offending values.
Z is unaffected and may still be missing.
init_omic.data.model = NULL now works for the parallel model. The
documented automatic-selection value could not be used: R/em.R built the
per-layer geometry names with rep(init_omic.data.model, length(K)), and
rep(NULL, n) is NULL, so the names were lost between initialization and
the M-step. mclust::mstep() then received modelName = NULL and failed,
reaching the user as "LUCID model fails to converge given current tuning
parameters". The selected geometry is now read back from the mclust fit and
reported on init_omic.data.model, one entry per layer. Random
initialization, which has no fit to read, falls back to "EEV" as the early
path does. The same line also used rep() where rep_len() was meant, so a
per-layer vector of names was expanded to length(K)^2 entries.
init_impute and init_par are recorded as resolved values. A serial fit
taking the defaults stored the unevaluated c("mix", "lod") and
c("mclust", "random") -- a record of a choice that was never made, which a
bootstrap refit reading those fields would carry forward.
predict_lucid() now enforces one rule for the omics input. Z is
required for every model type; only g_computation = TRUE relaxes it, because
that mode predicts from the exposure path alone. Previously each model type
validated separately, and serial got it wrong: the check comparing
length(Z) with length(K) ran before the is.null(Z) check, and
length(NULL) is 0, so a forgotten Z was reported as
"Z and K should be two lists of the same length" -- sending the user to
inspect their stage topology rather than the argument they omitted. The
is.null branch was unreachable. All three model types now give the same
message, and it names g_computation so the error states the rule.
A single-stage serial model is now declined at prediction with an
explanation. lucid(..., lucid_model = "serial", K = list(2)) fits, but
predict_lucid() failed on it with object 'pred.y' not found: the stage
loop branches on first / middle / last, and a single stage is simultaneously
first and last, so it takes the first branch and never reaches the assignment
of pred.y. Such a model is a fully equivalent early or parallel model, so
prediction now stops with a message saying so. Known limitation: making
one-stage serial genuinely predictable requires restructuring the stage loop
so its inputs depend on being the first stage and its outputs on being the
last, independently; that is not done here.
plot_cluster_omic_profile() shows what the clusters are. plot() draws
the path structure; summary() prints the cluster means as a table. Neither
answers the question a reader asks first -- which omics features separate the
clusters, and in which direction. The new function draws that, for early,
parallel and serial fits, as a cluster-by-feature heatmap in the style used
for single-cell markers, or as bars. A parallel or serial fit returns one
plot per omics layer, named, so no figure has to hold every layer at once.Features are ranked by an importance measure, "separation" by default: the
spread of the cluster means divided by the typical within-cluster spread. The
simpler alternatives "range" and "sd" use the means alone and so cannot
distinguish a feature that separates the clusters from one that is merely
noisy. Each returned plot carries the data it drew -- feature, cluster, mean,
within-cluster SD and score -- as the attribute "profile_data".
This adds ggplot2, scales and grDevices to Imports.
Convergence diagnostics. Fitted objects carry
em_control$converged, $n_iter, $n_restart and $loglik_trace. Reaching
max_itr without meeting the tolerance previously returned silently as though
it had converged; it now warns and records converged = FALSE.
Multiple random starts. n_starts (default 1) runs independent EM
initializations and keeps the fit with the highest observed-data
log-likelihood, with per-start values in em_control$start_loglik. EM reaches
only a local optimum: in testing at K = 3, five starts on one dataset spanned
23 log-likelihood units, and a single start returned a solution whose reported
exposure-outcome effect was five times too small.
Deterministic cluster ordering for unsupervised stages. Upstream stages of a serial model have no outcome to order by, so their cluster numbering was seed-dependent and the reported transition coefficients were not reproducible across runs on identical data. Such stages are now ordered lexicographically by their omics cluster means.
The imputation step now uses responsibilities recomputed at the updated parameters, restoring the ascent property of the E-M-I cycle.
The reference documentation was reviewed against the code function by function. The substantive corrections:
estimate_lucid() documented one return object where there are three. A
single numbered list described early, parallel and serial alike. It
listed em_control twice with contradictory contents, promised a likelihood
that serial did not yet return at the time (see the Bug fixes entry above --
it does now) and an N that early does not, and omitted missing_summary,
res_Delta and z altogether. The @return is now split into components
common to every fit and components specific to a model type.
lucid() duplicated that list rather than referring to it, and the copy
had drifted. It now cross-references estimate_lucid() and documents the
selection component, which records what the tuned penalties dropped and was
previously undocumented.
tune_lucid() now documents the tuning table's columns, its alignment
with the returned model list, and that its optimum is the penalized fit --
lucid(), not tune_lucid(), performs the unpenalized refit, so estimates
read directly from best_model are shrunk.
fill_data() described the wrong estimator. It is not an average of
per-cluster conditional means; it solves a single precision-weighted system
over the missing coordinates, which reduces to the Gaussian conditional mean
only when K = 1. The weights are also not the supplied p but p
reweighted by each cluster's density at the subject's current values.
Stub descriptions such as "List containing detailed missing data analysis"
were replaced with the actual components, conventions and edge-case behaviour
for summary_lucid(), summary.lucid_parallel(), summary.lucid_serial(),
the print methods, check_na(), analyze_missing_pattern(),
check_imputation_quality(), safe_impute() and the stability utilities.
predict_lucid() now states that pred.x runs 1, ..., K; code written
against the pre-3.1.0 0, ..., K - 1 labels must drop its adjustment.
plot() documents its appearance options and their defaults, and states that
the parallel and serial methods raise an error pending implementation.
gen_ci(), Istep_Z() and summarize_missing_stats() are internal but were
generating public help pages; they are now @noRd.
log_dmvnorm_observed() evaluates Gaussian densities on observed coordinates,
grouping rows by missingness pattern, and is shared by training and prediction.penalized_cluster_block() used by both the early and parallel M-steps.helper-oracle.R) and a
canonical data generator (helper-sim.R) rather than object-shape assertions.tests/testthat.R had test_check() commented out, so R CMD check ran none
of the suite. It is enabled; the suite runs in roughly three minutes.safe_solve() and safe_log_sum_exp() were each defined three times, and
safe_normalize() and check_and_stabilize_sigma() twice, across utils.R,
stability_utils.R and 00_stability_preload.R. The bodies agreed, so
behaviour was unaffected, but the roxygen sat on a copy that collation then
overwrote. Consolidated to one definition each in stability_utils.R.early_estep.R,
which forced a collation order. It is now a call-time wrapper, so no
load-order coupling remains.safe_solve() gains threshold and safe_normalize() gains min_val, both
always supported by the internals but dropped by the exported wrappers.reorder_z() / reorder_lucid() in R/summary.R were dead code -- never
called anywhere, and referencing field names (res_Delta$Delta,
res_Mu_Sigma) that no current class has. Removed."log-likelihood" /
"penalized log-likelihood" instead of "loglike" / "penalized loglike",
matching parallel's own wording; this is a console-output-only change, not a
numeric one.get_ref_cluster(),
reorder_Delta(), reorder_Mu_Sigma(), reorder_Beta() (R/summary.R,
the functions the already-removed reorder_z()/reorder_lucid() used to
call), print.sumlucid_auxi() and print.auxi.serial.scen1/2/3() (S3
methods registered in NAMESPACE for classes never assigned to any object,
so they could never dispatch), parallel_marginal_design(),
make_positive_definite()/is_positive_definite() (superseded by
check_and_stabilize_sigma()/safe_solve()), fill_data_help1()/
fill_data_help2(), initialize_Beta(), and early_estep() (a full
duplicate of Estep_early()) -- all confirmed to have zero call sites
anywhere in the package. check_K(), found dead in the same sweep, was kept
and wired in instead (see Bug fixes); plot.lucid_parallel()'s ~100 lines
of Sankey-plotting code after its unconditional stop() (unreachable) were
also removed, and plot.lucid_serial/plot.lucid_parallel now share the
same trimmed shape.Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.