Repository navigation
Avoid full sorts in ps_tail() and mcse_quantile(), and add ps_tail_sorted_log_ratios() for loo - #452
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
|
This is how benchmark results would change (along with a 95% confidence interval in relative change) if 16e594c is merged into master:
|
avehtari
left a comment
There was a problem hiding this comment.
exported function might need NEWS.md item
|
@VisruthSK is this waiting on anything else? Also does this include the performance benefit from that loo PR that we closed? |
|
I think I need to implement that after which it's gtg |
7be3b39 to
bdb0066
Compare
|
I pushed two commits and changed the title and description, because the PR changes The first commit changes the tie handling in The second commit uses the same idea in Before and after these commits, the results are identical to master. The description lists the checks. A C routine for the tail selection (#453) gave about the same speed-up in my measurements as the R version (1.43x and 1.50x for continuous data), so the R version seems enough. The previous description said that the warning for fewer than 5 tail draws was removed, but |
|
@VisruthSK @jgabry, this needs a new review since I added two commits |
jgabry
left a comment
There was a problem hiding this comment.
The results look identical to master for ps_tail() and mcse_quantile() in my checks too. I made some review comments. A couple of other things:
-
Do we need to export ps_tail_sorted_log_ratios()? The description says loo#290 uses it, but loo#290 currently calls posterior::ps_tail(), and loo#413 says #290 will use ps_tail() too. Now that ps_tail() only sorts the tail, the remaining extra sort in loo#290 is the full sort.int(lw_i) it does just to warn about a constant tail. ps_tail() already returns k = NA in that case, so loo could drop its own sort and check is.na(smoothed$k). But if loo is going to use it, then can you update loo#290 to call it and I guess check if it's better than calling ps_tail() directly?
-
NEWS still needs an entry. Maybe something like: "ps_tail(), and therefore pareto_smooth(), pareto_khat() and pareto_diags(), and mcse_quantile(), no longer sort all draws (#452)".
The idea is that this is a fast version not doing any checks and branches that |
Makes sense, thanks. I asked Codex to check and selecting the tail in loo and then calling In that case my other comments on it still apply (the Regarding the |
|
Thanks for the review. I pushed four commits:
I repeated your measurement with the tail selection of loo#413 followed by Results are still identical to master in all checks, and |
|
@VisruthSK since this was your PR originally did you want to take a look again before merging? |
|
Looks good to me! |
This PR was made with assistance from Codex and Claude.
Summary
ps_tail()no longer sorts the whole vector to find the tail. The internal.ps_tail_selection()finds the cutoff with a partial sort (sort.int(partial = ...)), takes the values at or above it, including all values that tie with the cutoff, and sorts only those. The radix order is stable, so the selected tail and its order are the same as with a full sort. This affects all functions that callps_tail():pareto_smooth(),pareto_khat()andpareto_diags().ps_tail_sorted_log_ratios()smooths a tail of log ratios that is already sorted. Rely on posterior for pareto smooth tails loo#290 will use it, so that loo selects the tail once and does not sort the log ratios twice (see this comment). If the tail is not smoothed (smooth_draws = FALSEor a non-finitek), it returnsxunchanged, as loo'spsis_smooth_tail()did. It gives an error ifxis not sorted; the check costs less than 1 % of the call.ps_tail()andps_tail_sorted_log_ratios()share the internal.ps_tail_sorted().mcse_quantile()sorted all draws to read two order statistics. It now usessort.int(partial = c(i1, i2)), which gives the same two values.sort.int()does not allowindex.return = TRUEtogether withpartial, which is why the tail selection gets the indices withwhich()andorder()of the few values at or above the cutoff.Speed
Speed-up relative to master, 500 vectors, median of 5 runs, one core (Intel i5-1335U, R 4.6.1):
ps_tail(are_log_weights = TRUE)-Inflog weightsmcse_quantile(probs = c(0.05, 0.5, 0.95))mcse_quantile()gains less becauseess_quantile()takes most of its time.For loo, the tail selection of stan-dev/loo#413 followed by
ps_tail_sorted_log_ratios()is 1.12x (S = 4000) and 1.08x (S = 16 000) faster thanps_tail()on the whole vector, for 1000 observations, median of 7 runs. The log weights andkare identical.Results
ps_tail()(right and left tail, values and log weights),pareto_smooth(tail = "both"),pareto_khat(),pareto_pit()andmcse_quantile()return results identical to master in 63 of 63 checks: 7 vectors (normal, t(1), rounded values with many ties,-Inflog weights, ties at the cutoff, 40 draws, and a constant tail).ps_tail(are_log_weights = TRUE)is also identical for the 3000 vectors of the speed test, andmcse_quantile()for 200 vectors at 3 probabilities.New tests:
ps_tail_sorted_log_ratios()agrees withps_tail(), also for a constant tail.ps_tail_sorted_log_ratios(smooth_draws = FALSE)returnsxunchanged whenexp(x)underflows.ps_tail_sorted_log_ratios()gives an error for a tail that is not sorted..ps_tail_selection()gives the same indices and cutoff as a full sort for 4 vectors with ties and-Infvalues and 3 tail lengths.mcse_quantile()gives the same values as the computation withsort()for 5 probabilities, andNAfor draws withNA.devtools::test()withNOT_CRAN=truepasses all 2290 tests and gives no warnings (R 4.6.1, testthat 3.3.2, Linux x86_64).Copyright and Licensing
By submitting this pull request, the copyright holder is agreeing to license the submitted work under the following licenses: