You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Alex Herbert (Jira)" <ji...@apache.org> on 2021/04/09 20:39:00 UTC
[jira] [Commented] (RNG-128) UnitBallSampler
[ https://issues.apache.org/jira/browse/RNG-128?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17318317#comment-17318317 ]
Alex Herbert commented on RNG-128:
----------------------------------
I have constructed a JMH benchmark for sampling with an n-ball of dimension 1 to 5.
Dimension 1 methods to generate a double in the approximate range [-1, 1]:
{code:java}
// Baseline
rng.nextDouble()
// signedDouble:
// Sample uniformly from 2^54 dyadic rationals in [-1, 1).
// Equivalent to nextDouble() then subtracting 0 or 1 using an unused bit.
long bits = rng.nextLong();
((bits >>> 11) * 0x1.0p-53d) - ((bits >>> 10) & 0x1L);
// twoDoubles:
// Sample [-1, 1) excluding -0.0 but also missing the final 1.0 - 2^-53.
rng.nextDouble() + rng.nextDouble() - 1.0
// booleanDouble:
// This will sample (-1, 1) including -0.0 and 0.0
rng.nextBoolean() ? -rng.nextDouble() : rng.nextDouble()
{code}
Dimension 2:
||Method||Description||
|Baseline|An array of rng.nextDouble()|
|Rejection|Sample in a 2D square of length 2 and reject x^2 + y^2 >= 1.0.
This will reject approximately
2^2 / pi = 1.27 square positions per sample|
|DiskPoint|Use the [disk point picking|https://mathworld.wolfram.com/DiskPointPicking.html] method described in Wolfram|
Dimension 3-5:
||Method||Description||
|Baseline|An array of rng.nextDouble()|
|Rejection|Sample in a ND hypercube of length 2 and reject x^2 + y^2 + ... >= 1.0.
This will reject approximately 2^3 / (4pi/3) = 1.91 cube positions per sample for 3D and more as dimensions increase|
|BallPoint|Use the [ball point picking|https://mathworld.wolfram.com/BallPointPicking.html] method described in Wolfram|
For dimension 3 I have included dedicated samplers that computes xyz without using arrays to view the performance gain.
The results are as follows using a XO_RO_SHI_RO_128_PP random source:
|Method|dimension|size|type|Score|Relative|
|create1D| |1|Baseline|8.854|1.00|
|create1D| |1|signedDouble|8.85|1.00|
|create1D| |1|twoDoubles|9.876|1.12|
|create1D| |1|booleanDouble|14.392|1.63|
|create1D| |100|Baseline|762.456|1.00|
|create1D| |100|signedDouble|774.765|1.02|
|create1D| |100|twoDoubles|881.637|1.16|
|create1D| |100|booleanDouble|1503.541|1.97|
|create2D| |1|Baseline|9.876|1.00|
|create2D| |1|Rejection|19.167|1.94|
|create2D| |1|DiskPoint|47.243|4.78|
|create2D| |100|Baseline|869.734|1.00|
|create2D| |100|Rejection|1833.306|2.11|
|create2D| |100|DiskPoint|4753.457|5.47|
|create3D| |1|Baseline|13.585|1.00|
|create3D| |1|Rejection|41.447|3.05|
|create3D| |1|BallPoint|52.409|3.86|
|create3D| |100|Baseline|1158.785|1.00|
|create3D| |100|Rejection|3994.783|3.45|
|create3D| |100|BallPoint|4900.946|4.23|
|createND|3|1|Baseline|17.307|1.00|
|createND|3|1|Rejection|45.327|2.62|
|createND|3|1|BallPoint|60.219|3.48|
|createND|3|100|Baseline|1569.775|1.00|
|createND|3|100|Rejection|4258.297|2.71|
|createND|3|100|BallPoint|5849.117|3.73|
|createND|4|1|Baseline|18.908|1.00|
|createND|4|1|Rejection|83.398|4.41|
|createND|4|1|BallPoint|64.528|3.41|
|createND|4|100|Baseline|1873.559|1.00|
|createND|4|100|Rejection|7979.563|4.26|
|createND|4|100|BallPoint|6585.013|3.51|
|createND|5|1|Baseline|21.459|1.00|
|createND|5|1|Rejection|134.776|6.28|
|createND|5|1|BallPoint|81.294|3.79|
|createND|5|100|Baseline|1986.237|1.00|
|createND|5|100|Rejection|13380.114|6.74|
|createND|5|100|BallPoint|7527.744|3.79|
For 1D the signed double method is almost as fast as the baseline generation of a double in [0, 1).
For 2D the rejection method is 2.6x faster than the disk point picking.
For 3D the dedicated non-array based rejection method is 1.46x faster than the generic 3D ball point picking.
For 4D and 5D the ball point picking is faster than the rejection method.
> UnitBallSampler
> ---------------
>
> Key: RNG-128
> URL: https://issues.apache.org/jira/browse/RNG-128
> Project: Commons RNG
> Issue Type: Improvement
> Components: sampling
> Reporter: Alex Herbert
> Priority: Minor
>
> The *UnitSphereSampler* can generate isotropically distributed coordinates *on the surface* of a hypersphere of a given dimension.
> I propose to create a *UnitBallSampler* to generate isotropically distributed coordinates *within* a hypersphere of a given dimension.
> Wolfram describes a method that will work with any dimension and a method for 2 dimensions:
> [Ball Point Picking|https://mathworld.wolfram.com/BallPointPicking.html]
> [Disk Point Picking|https://mathworld.wolfram.com/DiskPointPicking.html]
> It also suggests that a simple rejection method when sampling inside an n-cube of length 2 may be faster for small dimensions.
> Creating an abstract class with a factory method would allow the best performing sampler to be created for each dimension:
> {code:java}
> public abstract class UnitBallSampler implements
> SharedStateSampler<UnitBallSampler> {
> /**
> * @return a random Cartesian coordinate within the unit n-ball.
> */
> public double[] next() {
> // ...
> }
> /**
> * Create a unit n-ball sampler for the given dimension.
> *
> * @param dimension Space dimension.
> * @param rng Generator for the individual components of the coordinates.
> * A shallow copy will be stored in this instance.
> * @throws IllegalArgumentException If {@code dimension <= 0}
> */
> public static UnitBallSampler of(int dimension,
> UniformRandomProvider rng) {
> // ...
> }
> }
> {code}
> The UnitSphereSampler sample method is named {{nextVector}}. The corresponding method here could be:
> * next
> * nextVector
> * nextCoordinate
> * nextPoint
> * sample
> * ...
--
This message was sent by Atlassian Jira
(v8.3.4#803005)