Engineero
Engineero

Reputation: 12928

Generator only populates list with copies of the final item

I am converting a method for creating a Halton sequence into a generator in Python. I am running into the same problem in Python 2.7 and Python 3.3.

When I create a generator object using my generator and call my_object.next() repeatedly, I get the results that I expect. When, however, I call list(my_object) or tuple(my_object), I get the appropriately-sized list/tuple, but each element is just a copy of the final value that the generator outputs. My Python 3.3 code is below:

def halton_gen(dim, num_pts):
    sequence = np.empty(dim)
    sequence.fill(np.nan)
    primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31)  # first 11 prime #s
    log_pts = log(num_pts + 1)
    k = 1

    # Generate the sequence
    while k <= num_pts:
        print("k = {}".format(k))
        for i in range(dim):
            print("i = {}".format(i))
            prime = primes[i]
            num_helper = int(ceil(log_pts / log(prime)))

            kk = k
            sum_ = 0

            for j in range(num_helper):
                sum_ += fmod(kk, prime) * pow(prime, -(j + 1))
                kk = floor(kk / prime)

            sequence[i] = sum_

        print("sequence {}".format(sequence))
        yield sequence
        k += 1

Note that the print statements in the code above give me what I am expecting, but the list/tuple created with list(my_object) or tuple(my_object) is incorrect:

my_object = halton_gen(2, 5)    
tuple(my_object)

k = 1
i = 0
i = 1
sequence [ 0.5         0.33333333]
k = 2
i = 0
i = 1
sequence [ 0.25        0.66666667]
k = 3
i = 0
i = 1
sequence [ 0.75        0.11111111]
k = 4
i = 0
i = 1
sequence [ 0.125       0.44444444]
k = 5
i = 0
i = 1
sequence [ 0.625       0.77777778]

Out[86]:
(array([ 0.625     ,  0.77777778]),
 array([ 0.625     ,  0.77777778]),
 array([ 0.625     ,  0.77777778]),
 array([ 0.625     ,  0.77777778]),
 array([ 0.625     ,  0.77777778]))

I am expecting (dropping the array() specifiers for readability):

([0.5    0.333],
 [0.25   0.667],
 [0.75   0.111],
 [0.125  0.444],
 [0.625  0.778])

How can I get my generator to properly populate my list? Or my list to properly unpack my generator? Whatever the proper way of stating that is.

Upvotes: 0

Views: 63

Answers (1)

merlin2011
merlin2011

Reputation: 75575

Your sequence object is being recycled. One fix is to re-create it every time you yield.

def halton_gen(dim, num_pts):
    primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31)  # first 11 prime #s
    log_pts = log(num_pts + 1)
    k = 1

    # Generate the sequence
    while k <= num_pts:
        sequence = np.empty(dim)
        sequence.fill(np.nan)
        print("k = {}".format(k))
        for i in range(dim):
            print("i = {}".format(i))
            prime = primes[i]
            num_helper = int(ceil(log_pts / log(prime)))

            kk = k
            sum_ = 0

            for j in range(num_helper):
                sum_ += fmod(kk, prime) * pow(prime, -(j + 1))
                kk = floor(kk / prime)

            sequence[i] = sum_

        print("sequence {}".format(sequence))
        yield sequence
        k += 1

Another fix would be as the comment mentions, to use sequence.copy().

    yield sequence.copy()

Upvotes: 2

Related Questions