Repository navigation
Conversation
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.
Summary
This PR runs
kmeans()inget_p_clust()on the coordinates of the reference model draws in their low-dimensional span instead of on the S × n matrixt(eta). The clusters stay the same.varsel()with 50 000 observations, 4 000 draws and 20 clusters gets about 4.5 times faster. For small numbers of observations, the run time does not change.This PR was assisted by Claude.
Overhead
In current master:
get_p_clust()runskmeans()ont(eta), an S × n matrix, so each k-means iteration costs O(S K n) for K clusters. The draws of a GLM-type reference model span at most as many dimensions as the model has coefficients, often far fewer than n. In a profile ofvarsel()for a Gaussian model with n = 10 000, 4 000 draws and 20 clusters,kmeans()took 65 % of the time.Changes
eta_for_kmeans()centers the draws and finds an orthonormal basis Q of their span with a pivotedqr()(rank tolerance 1e-10) of a subset of the draws. It starts with 64 evenly spaced draws and doubles the subset until the subset is rank deficient. It accepts Q only if Q reproduces all draws (relative tolerance 1e-8), and then returns the S × r matrix of coordinates.get_p_clust()callskmeans()on the result ofeta_for_kmeans().The Euclidean distances between the draws, and from a draw to any mean of draws, are the same in the coordinates as in
t(eta).kmeans()takes its initial centers withsample.int(S, K), which gives the same rows for both inputs. So k-means finds the same clustering up to rounding. The subset is deterministic, so the function does not change the state of the random number generator. If the span has more thanmin(n, S) / 4dimensions, if the check fails, or ifetahas missing values,kmeans()getst(eta)as before.Results
Gaussian reference model with P = 50 and 4 000 draws, 20 search clusters; all results identical to master (difference 0, identical rankings):
varsel(), n = 50 000, 20 evaluation clustersvarsel(), n = 10 000, 20 evaluation clustersvarsel(), n = 10 000, 400 evaluation drawsvarsel(), n = 1 000, 20 evaluation clustersFor small n (N = 50, P = 200), the run time does not change. In a local check with rank 21, n = 5 000 and S = 1 000, the clusters were identical and
kmeans()took 0.18 s instead of 2.8 s. The peak memory is about the same as on master:eta_for_kmeans()holds about two n × S matrices for a short time, and on mastert(eta)and the copy thatkmeans()passes to Fortran also take two.Tests
The new
test_eta_for_kmeans.Rchecks that:eta_for_kmeans()returns at most 6 columns, keeps the pairwise distances (tolerance 1e-10), and gives the samekmeans()clusters ast(eta);t(eta);All three tests fail on master (function not found). The full suite passes with
NOT_CRAN=true: 256 tests, 55 812 expectations, 0 failures, the same 4 warnings as master. Tested with this change applied to master 73cf0ee (projpred 2.11.0.9000), R 4.6.1, brms 2.23.0, rstanarm 2.32.2, cmdstanr 0.9.0.9001, CmdStan 2.39.0, on CSC Roihu (Linux, AMD EPYC).