Python heapq heappush The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
<p>I'm running into a bug with the <code>heapq</code> library -- the <code>heappush</code> function in particular. The error code (below) gives me no help.</p>

<pre><code>(Pdb) heapq.heappush(priority_queue, (f, depth, child_node_puzzle_state))
*** ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
</code></pre>

<p>Here's the snippet that is causing the problem...</p>

<pre><code>h = compute_heuristic(child_node_puzzle_state, solved_puzzle)
depth = current_node[1] + 1
f = h + depth
heapq.heappush(priority_queue, [f, depth, child_node_puzzle_state])
</code></pre>

<p>I should note that <code>h</code> and <code>depth</code> are <code>int</code>'s and <code>child_node_puzzle_state</code> is a numpy array. Checkout out some of the debugging code...</p>

<pre><code>(Pdb) child_node_puzzle_state
array([[  5.,   4.,  18.,  15.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
         99.],
       [ 99.,  10.,   6.,  14.,  12.,  20.,   0.,   0.,   0.,   0.,  99.,
         99.],
       [ 99.,  99.,  11.,  19.,  17.,  16.,   8.,   0.,   0.,  99.,  99.,
         99.],
       [ 99.,  99.,  99.,   2.,   3.,   0.,   0.,   0.,  99.,  99.,  99.,
         99.],
       [ 99.,  99.,  99.,  99.,   1.,  21.,   0.,  99.,  99.,  99.,  99.,
         99.],
       [ 99.,  99.,  99.,  99.,  99.,   9.,  13.,   7.,   0.,   0.,   0.,
          0.]])
(Pdb) child_node_puzzle_state.dtype
dtype('float64')
(Pdb) p h
3
(Pdb) depth
2
(Pdb) f
5
(Pdb) priority_queue
[(5, 2, array([[  9.,  15.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
         99.],
       [ 99.,  10.,   6.,  14.,   5.,   4.,  18.,   0.,   0.,   0.,  99.,
         99.],
       [ 99.,  99.,  11.,  19.,  17.,  12.,  20.,   8.,   0.,  99.,  99.,
         99.],
       [ 99.,  99.,  99.,  16.,   3.,   0.,   0.,   0.,  99.,  99.,  99.,
         99.],
       [ 99.,  99.,  99.,  99.,   2.,   0.,   0.,  99.,  99.,  99.,  99.,
         99.],
       [ 99.,  99.,  99.,  99.,  99.,   1.,  21.,  13.,   7.,   0.,   0.,


...

(Pdb) len(priority_queue)
9
</code></pre>

<p>Here's what I cannot figure out... if I change one little thing it works -- but it is semantically wrong. Here's is the change ...</p>

<pre><code>h = compute_heuristic(child_node_puzzle_state, solved_puzzle)
depth = current_node[1] + 1
heapq.heappush(priority_queue, (h, depth, child_node_puzzle_state))
</code></pre>

<p>Did you catch the difference? Instead of computing <code>f = h + depth</code> I just use <code>h</code>. And magically it works? </p>

<p>This couldn't be the size, because as I showed in debugging...</p>

<pre><code>(Pdb) len(priority_queue)
9
</code></pre>

<hr>

<p>This really doesn't make sense to me, so I'm going to include more code. First, here is everything needed to compute <code>h</code> there is nothing funky going on, so I really doubt this is the problem. All the functions return integers (although they use numpy arrays)...</p>

<pre><code>def tubes_aligned(puzzle_state):

    current_index = 3 #test for index 3
    blue_tube = puzzle_state[3,:]
    len_of_top_tube = len(blue_tube[blue_tube &lt; 99]) - 3
    correct_index = 6 - len_of_top_tube

    found = False
    distance = 3
    for i in range(3):
        if i == correct_index:
            distance = current_index - i
            found = True

    if not found:
        for i in range(5,2,-1):
            if i == correct_index:
                distance = i - current_index

    return distance

def balls_in_top_half(puzzle_state):

    for i in range(6):
        full_tube = puzzle_state[i,:]
        num_balls = full_tube[full_tube &lt; 99]
        num_balls = len(num_balls[num_balls &gt; 0])
        if (6 - i - num_balls) != 0:
            return 1

    return 0

def balls_in_correct_place(puzzle_state, solved_puzzle):
    if is_solved(puzzle_state, solved_puzzle):
        return 0
    else:
        return 1

def compute_heuristic(puzzle_state, solved_puzzle):
    # print "computing heuristic"
    # heuristic (sum all three):
    #     1. how many tubes is the puzzle state from tubes being aligned -- max is 3
    #     2. is there balls in the top portion? 1 -- yes || 0 -- no
    #     3. are there balls in the wrong place in the bottom half? 1 -- yes || 0 -- no
    part_1 = tubes_aligned(puzzle_state)
    part_2 = balls_in_top_half(puzzle_state)
    part_3 = balls_in_correct_place(puzzle_state, solved_puzzle)
    return part_1 + part_2 + part_3
</code></pre>

<p><code>heapq.heappush</code> will <em>compare</em> an array with other arrays in the heap, if the preceding elements in the tuple you push are otherwise equal.</p>

<p>Here is a pure Python implementation of <code>heappush()</code>:</p>

<pre><code>def heappush(heap, item):
    """Push item onto heap, maintaining the heap invariant."""
    heap.append(item)
    _siftdown(heap, 0, len(heap)-1)

def _siftdown(heap, startpos, pos):
    newitem = heap[pos]
    # Follow the path to the root, moving parents down until finding a place
    # newitem fits.
    while pos &gt; startpos:
        parentpos = (pos - 1) &gt;&gt; 1
        parent = heap[parentpos]
        if newitem &lt; parent:
            heap[pos] = parent
            pos = parentpos
            continue
        break
    heap[pos] = newitem
</code></pre>

<p>The actual implementation will be in C, which is why you get the error without a deeper traceback.</p>

<p>Note the <code>newitem &lt; parent</code> comparison; it is <em>that comparison</em> that is throwing the exception, as numpy <code>array</code> objects would be compared element by element and produce a boolean array with true and false results. If there is a state in your heap where <code>f</code> and <code>depth</code> are equal, that comparison must compare the arrays:</p>

<pre><code>&gt;&gt;&gt; import numpy
&gt;&gt;&gt; t1 = (5, 2, numpy.array([9.,  15.]))
&gt;&gt;&gt; t2 = (5, 2, numpy.array([10.,  15.]))
&gt;&gt;&gt; t1 &lt; t2
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
</code></pre>

<p>For you the problem 'disappeared' when you changed the value in the first position of the tuple, making the first two values unique again compared to what is already in your heap. But it doesn't actually solve the underlying problem.</p>

<p>You can avoid this issue by inserting a unique count (using <a href="https://docs.python.org/3/library/itertools.html#itertools.count" rel="nofollow"><code>itertools.count()</code></a>) before the array:</p>

<pre><code>from itertools import count

# a global
tiebreaker = count()

# each time you push
heapq.heappush(priority_queue, (f, depth, next(tiebreaker), child_node_puzzle_state))
</code></pre>

<p>The counter ensures that the first three elements of your tuples are <em>always</em> unique. It also means that any later addition to the heap that matches an already-present state on the heuristic score <em>and</em> depth are sorted before older ones. You can use <code>count(step=-1)</code> if you want to inverse that relationship.</p>

python numpy heap