I am trying to write a function that generates a random walk bound by the unit sphere, resulting in the last location of the random walk given by a vector. In trying to figure out where my function is breaking, I have tried piecing it out like this:
norm_vec <- function(x) sqrt(sum(x^2))
sphere_start_checker <- function() {
repeat {
start_loc <- runif(3, min = -1, max = 1)
if (norm_vec(start_loc) < 1) return(start_loc)
}
}
n <- 10
set.seed(1)
new_loc <- as.matrix(t(sphere_start_checker()))
temp_loc <- as.matrix(t(c(0, 0, 0)))
for(i in n) {
repeat {
temp_loc <- new_loc[i, ] + runif(3, min = -.01, max = .01)
if (norm_vec(temp_loc) < 1) {
return(new_loc <- rbind(new_loc, temp_loc))
}
}
}
new_loc
I get the error: Error in new_loc[i, ] : subscript out of bounds
However, when I manually iterate the code outside of the for loop, everything works fine. I assume this has something to do with R's scoping rules, and I have tried messing with assigning the result of the rbind to the global environment, but that does not work.
Initially, I tried writing the function like this:
randomwalk_sphere <- function(n, radius, stepmax) {
# initialize sphere bounds
sphere_radius <- as.double(radius)
# initialize random starting vector
# while loop that adds a random vector to our start_loc n times, repeating once boundary condition is met
loop <- 0
new_loc <- sphere_start_checker()
while(loop <= n) {
repeat {
temp_loc <- new_loc + runif(3, min = -stepmax, max = stepmax)
if (norm_vec(temp_loc) < sphere_radius) {
return(assign("new_loc", temp_loc, env = .GlobalEnv))
}
}
loop <- loop + 1
}
new_loc
}
but when I do it this way, the while loop does not seem to work and I just get the initial new_loc coordinate from the uniform distribution rather than from a random walk. I realize that using rbind is probably a more correct way of doing this, but I am interested in learning why this approach does not work.
Some thoughts:
while you "fixed" the issue with for (i in n)
, the canonical (more-common) method is typically to keep n
as a single-length count, and use seq_len(n)
or similar within the for
call, such as n <- 10; for (i in seq_len(n))
.
return
in anything other than a formal function(.){..}
declaration is not right; if you want to stop a repeat
, use break
. As demonstrated here, the scope of the re-assignment is not right, so the new_loc
is never really reassigned with all of the iterations' results.
Ultimately, once you stop trying to return
from a non-function, it works.
n <- 10
set.seed(1)
new_loc <- as.matrix(t(sphere_start_checker()))
temp_loc <- as.matrix(t(c(0, 0, 0)))
for (i in seq_len(n)) {
repeat {
temp_loc <- new_loc[i, ] + runif(3, min = -.01, max = .01)
if (norm_vec(temp_loc) < 1) {
new_loc <- rbind(new_loc, temp_loc)
break
}
}
}
new_loc
# [,1] [,2] [,3]
# -0.4689827 -0.2557522 0.1457067
# temp_loc -0.4608185 -0.2617186 0.1536745
# temp_loc -0.4519250 -0.2585026 0.1562568
# temp_loc -0.4606893 -0.2643831 0.1497879
# temp_loc -0.4569488 -0.2667010 0.1551848
# temp_loc -0.4569948 -0.2623487 0.1650229
# temp_loc -0.4593941 -0.2567998 0.1737170
# temp_loc -0.4651513 -0.2537663 0.1662281
# temp_loc -0.4698069 -0.2560440 0.1564959
# temp_loc -0.4721591 -0.2486502 0.1533029
# temp_loc -0.4725175 -0.2466589 0.1531737