5
5
#
6
6
# ##########################################################
7
7
8
+ using AliasTables
8
9
using Random: Sampler
9
10
10
11
if VERSION < v " 1.3.0-DEV.565"
635
636
direct_sample! (a:: AbstractArray , wv:: AbstractWeights , x:: AbstractArray ) =
636
637
direct_sample! (default_rng (), a, wv, x)
637
638
638
- function make_alias_table! (w:: AbstractVector , wsum,
639
- a:: AbstractVector{Float64} ,
640
- alias:: AbstractVector{Int} )
641
- # Arguments:
642
- #
643
- # w [in]: input weights
644
- # wsum [in]: pre-computed sum(w)
645
- #
646
- # a [out]: acceptance probabilities
647
- # alias [out]: alias table
648
- #
649
- # Note: a and w can be the same array, then that array will be
650
- # overwritten inplace by acceptance probabilities
651
- #
652
- # Returns nothing
653
- #
654
-
655
- n = length (w)
656
- length (a) == length (alias) == n ||
657
- throw (DimensionMismatch (" Inconsistent array lengths." ))
658
-
659
- ac = n / wsum
660
- for i = 1 : n
661
- @inbounds a[i] = w[i] * ac
662
- end
663
-
664
- larges = Vector {Int} (undef, n)
665
- smalls = Vector {Int} (undef, n)
666
- kl = 0 # actual number of larges
667
- ks = 0 # actual number of smalls
668
-
669
- for i = 1 : n
670
- @inbounds ai = a[i]
671
- if ai > 1.0
672
- larges[kl+= 1 ] = i # push to larges
673
- elseif ai < 1.0
674
- smalls[ks+= 1 ] = i # push to smalls
675
- end
676
- end
677
-
678
- while kl > 0 && ks > 0
679
- s = smalls[ks]; ks -= 1 # pop from smalls
680
- l = larges[kl]; kl -= 1 # pop from larges
681
- @inbounds alias[s] = l
682
- @inbounds al = a[l] = (a[l] - 1.0 ) + a[s]
683
- if al > 1.0
684
- larges[kl+= 1 ] = l # push to larges
685
- else
686
- smalls[ks+= 1 ] = l # push to smalls
687
- end
688
- end
689
-
690
- # this loop should be redundant, except for rounding
691
- for i = 1 : ks
692
- @inbounds a[smalls[i]] = 1.0
693
- end
694
- nothing
695
- end
696
-
697
639
"""
698
640
alias_sample!([rng], a::AbstractArray, wv::AbstractWeights, x::AbstractArray)
699
641
@@ -704,29 +646,23 @@ Build an alias table, and sample therefrom.
704
646
Reference: Walker, A. J. "An Efficient Method for Generating Discrete Random Variables
705
647
with General Distributions." *ACM Transactions on Mathematical Software* 3 (3): 253, 1977.
706
648
707
- Noting `k=length(x)` and `n=length(a)`, this algorithm takes ``O(n \\ log n )`` time
708
- for building the alias table, and then ``O(1)`` to draw each sample. It consumes ``2 k`` random numbers.
649
+ Noting `k=length(x)` and `n=length(a)`, this algorithm takes ``O(n)`` time
650
+ for building the alias table, and then ``O(1)`` to draw each sample. It consumes ``k`` random numbers.
709
651
"""
710
652
function alias_sample! (rng:: AbstractRNG , a:: AbstractArray , wv:: AbstractWeights , x:: AbstractArray )
711
653
Base. mightalias (a, x) &&
712
654
throw (ArgumentError (" output array x must not share memory with input array a" ))
713
- Base. mightalias (x, wv) &&
714
- throw (ArgumentError (" output array x must not share memory with weights array wv" ))
715
- 1 == firstindex (a) == firstindex (wv) == firstindex (x) ||
655
+ 1 == firstindex (a) == firstindex (wv) ||
716
656
throw (ArgumentError (" non 1-based arrays are not supported" ))
717
- n = length (a)
718
- length (wv) == n || throw (DimensionMismatch (" Inconsistent lengths." ))
657
+ length (wv) == length (a) || throw (DimensionMismatch (" Inconsistent lengths." ))
719
658
720
659
# create alias table
721
- ap = Vector {Float64} (undef, n)
722
- alias = Vector {Int} (undef, n)
723
- make_alias_table! (wv, sum (wv), ap, alias)
660
+ at = AliasTable (wv)
724
661
725
662
# sampling
726
- s = Sampler (rng, 1 : n)
727
- for i = 1 : length (x)
728
- j = rand (rng, s)
729
- x[i] = rand (rng) < ap[j] ? a[j] : a[alias[j]]
663
+ for i in eachindex (x)
664
+ j = rand (rng, at)
665
+ x[i] = a[j]
730
666
end
731
667
return x
732
668
end
0 commit comments