-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pure Julia implementation of SobolSeq #3
Conversation
|
||
type SobolSeq | ||
sdim::Integer #dimension of sequence being generated | ||
m::Array{Int,2} #array of size (sdim, WORD_SIZE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these should be Uint32
, not Int
; I'm not sure the algorithm will work for 64-bit integers.
Unfortunately, you then have to be very careful about type-stability, because operations on smaller integer types currently yield Int
results in Julia. This may change in 0.4 with JuliaLang/julia#8028
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, we should check the original papers on the 64-bit issue. My worry is that the "seed" data needs to be different for 64-bit integers. If that's not the case, Int
is fine.
Thank you very much for the detailed constructive feedback. I've forced Uint32 everywhere (also ndims for no particular reason), devectorized those assignments and changed the The test still does not pass. Starting for ndims = 4 the results change.
With the pure Julia translation:
The last element is different, but the first 3 elements continue to be equal between the two versions. That makes me think the issue is with generating the initial |
Even worse, for high dimensions the number generator goes out of bound. This starts around dimenions 90:
|
ac = a | ||
m[i,j] = m[i,j-d] | ||
for k = 0:d-1 | ||
m[i,j] $= ((ac & 1) * m[i, j-d+k]) << (d-k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This *
is problematic. Remember that Julia currently promotes the result of every operation to an Int
(which is usually 64-bit), whereas we need 32-bit multiplication here. (Nor is it correct to recast the result after the multiplication.)
I didn't fully understand your original comment and this point goes beyond my knowledge. Is there a workaround you can recommend? Anything I can do or look up from my side? |
Yes, I mean that multiplication will not overflow as expected if you multiply two uint32s. Actually, now that I look back at the original papers, it seems at first glance like it shouldn't be restricted to the 32-bit case. So, the easiest thing to do is probably to change all of your |
Thanks for your comments. How it seems now is that it works up to dimension 3, but starts diverging from your C code results at dimension 4 and goes out of bounds at least at dimension 90. I'm at a loss how to continue. Suggestions? |
@stevengj Now the tests pass (travis fails because it's on v0.3). It also works for There are still some depreciation warnings for array concatenation, shall I change the |
@@ -1,67 +1,93 @@ | |||
module Sobol | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need a using Compat
for Julia 0.3 compatibility
Thanks for keeping at it, and I'm glad you found the bug! It would be very useful to have a performance benchmark against the C implementation as well. |
|
||
#set initial values of m from table | ||
# TODO transpose sobol_minit for faster column access? | ||
m[i, 1:Int(d)] = sobol_minit[i-1, 1:Int(d)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just make d = floor(Int, log2(a))
and then you don't need the subsequent Int(d)
calls.
Thanks for all the comments. I'll try to make it work for v0.3 with Compat, but it might be that operators on Good idea also for a speed comparison, I'll just use the tests. |
The latest changes follow your feedback and now it works on v0.3! |
Don't merge yet! With the latest changes the tests fail on v0.4. Let me check. |
Actually, the tests pass if I run |
(Ptr{Void}, Ptr{Float64}), s.p, x) | ||
x | ||
# Special case N == 0 | ||
next(s::SobolSeq{0}) = Float64[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one seems redundant with next(s::SobolSeq) = next!(s, Array(Float64,ndims(s)))
Oops, nevermind about the tests failing, there was some previous stuff in my workspace. Restarting julia fixed it. Sorry about that. For the benchmark: running the tests (second run) on my macbook pro with julia v0.4, both pure julia I've just added a precompile line. This PR seems finished. |
Yes, it makes sense that they would be the same performance, but it's great to see it confirmed. |
return nothing | ||
end | ||
|
||
s.n += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably += one(s.n)
to avoid promoting, adding, then truncating.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well spotted!
Note that you should also:
|
adaptation after @stevengj feedback: force Uint32; devectorize some assignments; change skip number Uint32 -> Uint tryout make it work for julia v0.4 and UInt32 support 64bit, make it work for general UInt Revert "support 64bit, make it work for general UInt" This reverts commit 94099ad. make sdims a type parameter, allow sdims==0, compatibility with v0.3 and minor changes from comments add recompilation remove special case next for N==0 update README and small incrementer fix
Wow, that was my first rebase and force push, pretty cool :) Did it all arrive well? |
and takes advantage of the [NLopt.jl](https://github.com/JuliaOpt/NLopt.jl) | ||
Julia module to ensure that the NLopt library is installed when you | ||
`Pkg.add("Sobol")` in Julia. | ||
[NLopt library](http:https://ab-initio.mit.edu/nlopt) for nonlinear optimization, it has been ported by [Ken-B](https://github.com/Ken-B) into pure Julia code with the same performance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has been -> was. (Past tense, not present perfect.)
Looking good! |
Pure Julia implementation of SobolSeq
@@ -28,13 +28,9 @@ described in: | |||
**14** (1), pp. 88-100 (1988). | |||
* S. Joe and F. Y. Kuo, *ACM Trans. Math. Soft* **29** (1), 49-57 (2003). | |||
|
|||
Our implementation is in C, and was implemented in 2007 by SGJ as | |||
Originally implementated in C in 2007 as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
implementated -> implemented
Now that I look at it, the function bench(N, n)
s = SobolSeq(N)
x = Array(Float64, N)
skip(s, n)
return @elapsed for j = 1:n
next!(s, x)
end
end |
Okay, looks like the performance is suboptimal for larger
Pure-Julia implementation:
|
Improved the performance somewhat. After f8fbe05, I now get:
which is comparable to the C version. However:
involves a disturbing fraction of gc time. Something is doing an allocation somewhere that it shouldn't. I get the same allocations even if I take out the allocation of function bench(s, x, N, n)
skip(s, n)
return @elapsed for j = 1:n
next!(s, x)
end
end |
Oh, all the allocations are in Better to benchmark without the
which looks good. |
Thanks for merging this and all the careful tweaks afterwards. Especially thanks for the helpful comments, this has been very instructive for me! |
As discussed before my holiday, a pure Julia translation of your code, @stevengj. The motivation came for usage in Optim.jl, which aims to become a pure Julia environment.
The test passed when I last tested it, but today when creating this PR it does not. I tried finding the issue for some time without success.
I noticed that the
skip
does not produce the same result as the C implementation, but a simply looping overnext
does.For many dimensions, some have the same output (usually the first 5 or so) but higher dimensions differ. I'm at a loss where this comes from, but I thought I'd already put it here while I continue investigating, maybe it's something simple you will spot immediately.