Created
March 29, 2023 14:34
-
-
Save darius/0d0c6820af4e4a9f70b96a71a9a644b5 to your computer and use it in GitHub Desktop.
This file contains 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
withal@gmail.com | |
Binary search is not helpful here because, in the original nested loop, the total count of all the inner loop steps is going to be just n. But maybe we can simplify that code still: try rewriting it by fusing the cdf-computing loop with the (incf index) loop. Do you see what I'm getting at? | |
I see what you're getting at now. You want to compute the cdf and perform the resampling step in a single loop, effectively removing the need for the inner loop. Here's a version of the function that combines the two steps: | |
lisp | |
Copy code | |
(defun systematic-resample/normalized (weights) | |
(loop with n = (length weights) | |
with sum-weights = 0d0 | |
with index = 0 | |
with u = (random (/ 1d0 n)) | |
for weight in weights | |
do (progn | |
(incf sum-weights weight) | |
(when (> u sum-weights) | |
(incf index) | |
(setf u (+ u (/ 1d0 n))))) | |
collect index)) | |
In this version of the function, we removed the inner loop and combined the cdf computation with the resampling step. We maintain a running sum of the weights (sum-weights), and update index as we iterate through the weights. This should make the code more efficient and easier to understand. | |
withal@gmail.com | |
Try writing the loop as a `reduce`? | |
Sure, here's a version of the systematic resampling function using reduce instead of loop. Note that we need to use reduce with the :accumulate keyword argument so we can build the list of indices as we go: | |
lisp | |
Copy code | |
(defun systematic-resample/normalized (weights) | |
(reduce (lambda (state weight) | |
(destructuring-bind (sum-weights index u indices) state | |
(incf sum-weights weight) | |
(when (> u sum-weights) | |
(incf index) | |
(setf u (+ u (/ 1d0 (length weights))))) | |
(values sum-weights index u (cons index indices)))) | |
weights | |
:initial-value (list 0d0 0 (random (/ 1d0 (length weights))) '()) | |
:accumulate t)) | |
In this version, we use reduce with a lambda function that takes the current state as its first argument and the next weight as its second argument. The state is a list consisting of sum-weights, index, u, and the list of indices indices. We update the state in the lambda function and return the new state. Note that the indices will be in reverse order due to using cons to accumulate them. If you want them in the original order, you can use (reverse indices) as the final output. | |
withal@gmail.com | |
Oh, well, that turns out less clear in Lisp. Do you have any other ideas to code this elegantly? | |
Here's an alternative version using the loop macro, which should be more readable: | |
lisp | |
Copy code | |
(defun systematic-resample/normalized (weights) | |
(let ((n (length weights)) | |
(sum-weights 0d0) | |
(index 0) | |
(u (random (/ 1d0 n))) | |
(indices '())) | |
(loop for weight in weights | |
do (progn | |
(incf sum-weights weight) | |
(while (> u sum-weights) | |
(push index indices) | |
(setf u (+ u (/ 1d0 n))) | |
(incf index)))) | |
(nreverse indices))) | |
In this version, we first initialize the variables sum-weights, index, u, and indices outside the loop. Then we use a single loop to iterate through the weights. Inside the loop, we update sum-weights, and then we use a while loop to handle the condition when u is greater than sum-weights. This allows us to avoid using nested loops and makes the code easier to read. | |
Note that the list of indices is built in reverse order using push, so we need to use nreverse at the end to get the indices in the original order. | |
withal@gmail.com | |
One more idea to try: use the `series` package. | |
Using the series package, you can express the systematic resampling algorithm in a more functional style. First, you need to load the series package: | |
lisp | |
Copy code | |
(require 'series) | |
(use-package :series) | |
Then, you can rewrite the systematic-resample/normalized function as follows: | |
lisp | |
Copy code | |
(defun systematic-resample/normalized (weights) | |
(let* ((n (length weights)) | |
(cdf (collect-sum (scan weights))) | |
(u-start (random (/ 1d0 n))) | |
(us (collect (scan-ivectors n :initial-value u-start :generator #'(lambda (i u) (+ u (/ 1d0 n))))))) | |
(collect-nth (scan cdf) | |
:test #'(lambda (cdf-element u) (> u cdf-element)) | |
:input t | |
:allow-other-keys t | |
:series us))) | |
This implementation uses scan, collect, and collect-sum functions from the series package to create a more functional and concise implementation. | |
We compute the cumulative distribution function cdf by summing the weights with the collect-sum function. | |
We generate the uniform random numbers us by using scan-ivectors with an initial value u-start and a generator function that adds (/ 1d0 n) to the previous value. | |
Finally, we use collect-nth to resample the indices based on the cdf and us. The :test function checks if u is greater than the current cdf-element. | |
This implementation avoids using nested loops and mutable state, making the code more elegant and easier to understand. | |
withal@gmail.com | |
Thanks. I'm unfamiliar with most of `series`, so that was interesting to see. | |
You're welcome! The series package is a powerful library for Common Lisp that provides a functional and concise way to manipulate sequences and streams of data. It is particularly useful for expressing complex algorithms and data transformations in a more declarative and composable manner. | |
If you're interested in learning more about series, I recommend checking out the official documentation: | |
Series Reference Manual | |
Series User's Manual | |
These resources provide a comprehensive overview of the library, its functions, and how to use it effectively in your programs. | |
If you have any more questions or need further clarification, feel free to ask! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment