# SSCCE version of the core function
def nodes_in_range(src, cell, maxDist):
srcX, srcY, srcZ = src.x, src.y, src.z
maxDistSq = maxDist ** 2
for node in cell:
distSq = (node.x - srcX) ** 2
if distSq > maxDistSq: continue
distSq += (node.y - srcY) ** 2
if distSq > maxDistSq: continue
distSq += (node.z - srcZ) ** 2
if distSq <= maxDistSq:
yield node, distSq ** 0.5 # fast sqrt
from collections import namedtuple
class Node(namedtuple('Node', ('ID', 'x', 'y', 'z'))):
# actual class has assorted other properties
pass
cell = [
Node(1, 0, 0, 0),
Node(2, -2, -3, 4),
Node(3, .1, .2, .3),
Node(4, 2.3, -3.3, -4.5),
Node(5, -2.5, 4.5, 5),
Node(6, 4, 3., 2.),
Node(7, -2.46, 2.46, -2.47),
Node(8, 2.45, -2.46, -2.47),
Node(9, .5, .5, .1),
Node(10, 5, 6, 7),
# In practice, cells have upto 600 entries
]
if __name__ == "__main__":
for node, dist in nodes_in_range(cell[0], cell, 4.2):
print("{:3n} {:5.2f}".format(node.ID, dist))
import numpy
import numpy.linalg
contarry = numpy.ascontiguousarray
float32 = numpy.float32
# The "np_cell" has two arrays: one is the list of nodes and the
# second is a vectorizable array of their positions.
# np_cell[N][1] == numpy array position of np_cell[N][0]
def make_np_cell(cell):
return (
tuple(cell),
contarry([contarry((node.x, node.y, node.z), float32) for node in cell]),
)
# This version fails because norm returns a single value.
def np_nodes_in_range1(srcPos, np_cell, maxDist):
distances = numpy.linalg.norm(np_cell[1] - srcPos)
for (node, dist) in zip(np_cell[0], distances):
if dist <= maxDist:
yield node, dist
# This version fails because
def np_nodes_in_range2(srcPos, np_cell, maxDist):
# this will fail because the distances are wrong
distances = numpy.linalg.norm(np_cell[1] - srcPos, ord=1, axis=1)
for (node, dist) in zip(np_cell[0], distances):
if dist <= maxDist:
yield node, dist
# This version doesn't vectorize and so performs poorly
def np_nodes_in_range3(srcPos, np_cell, maxDist):
norm = numpy.linalg.norm
for (node, pos) in zip(np_cell[0], np_cell[1]):
dist = norm(srcPos - pos)
if dist <= maxDist:
yield node, dist
def np_nodes_in_range4(srcPos, np_cell, maxDist):
# this will fail because the distances are wrong
nodes = np_cell[0]
distances = numpy.linalg.norm(np_cell[1] - srcPos, ord=1, axis=1)
for idx, dist in enumerate(distances):
if dist <= maxDist:
yield nodes[idx], dist
if __name__ == "__main__":
np_cell = make_np_cell(cell)
srcPos = np_cell[1][0] # Position column [1], first node [0]
print("v1 - fails because it gets a single distance")
try:
for node, dist in np_nodes_in_range1(srcPos, np_cell, float32(4.2)):
print("{:3n} {:5.2f}".format(node.ID, dist))
except TypeError:
print("distances was a single value")
print("v2 - gets the wrong distance values")
for node, dist in np_nodes_in_range2(srcPos, np_cell, float32(4.2)):
print("{:3n} {:5.2f}".format(node.ID, dist))
print("v3 - slower")
for node, dist in np_nodes_in_range3(srcPos, np_cell, float32(4.2)):
print("{:3n} {:5.2f}".format(node.ID, dist))
print("v4 - v2 using enumerate")
for node, dist in np_nodes_in_range4(srcPos, np_cell, float32(4.2)):
print("{:3n} {:5.2f}".format(node.ID, dist))
IyBTU0NDRSB2ZXJzaW9uIG9mIHRoZSBjb3JlIGZ1bmN0aW9uCmRlZiBub2Rlc19pbl9yYW5nZShzcmMsIGNlbGwsIG1heERpc3QpOgogICAgc3JjWCwgc3JjWSwgc3JjWiA9IHNyYy54LCBzcmMueSwgc3JjLnoKICAgIG1heERpc3RTcSA9IG1heERpc3QgKiogMgogICAgZm9yIG5vZGUgaW4gY2VsbDoKICAgICAgICBkaXN0U3EgPSAobm9kZS54IC0gc3JjWCkgKiogMgogICAgICAgIGlmIGRpc3RTcSA+IG1heERpc3RTcTogY29udGludWUKICAgICAgICBkaXN0U3EgKz0gKG5vZGUueSAtIHNyY1kpICoqIDIKICAgICAgICBpZiBkaXN0U3EgPiBtYXhEaXN0U3E6IGNvbnRpbnVlCiAgICAgICAgZGlzdFNxICs9IChub2RlLnogLSBzcmNaKSAqKiAyCiAgICAgICAgaWYgZGlzdFNxIDw9IG1heERpc3RTcToKICAgICAgICAgICAgeWllbGQgbm9kZSwgZGlzdFNxICoqIDAuNSAgIyBmYXN0IHNxcnQKCmZyb20gY29sbGVjdGlvbnMgaW1wb3J0IG5hbWVkdHVwbGUKY2xhc3MgTm9kZShuYW1lZHR1cGxlKCdOb2RlJywgKCdJRCcsICd4JywgJ3knLCAneicpKSk6CiAgICAjIGFjdHVhbCBjbGFzcyBoYXMgYXNzb3J0ZWQgb3RoZXIgcHJvcGVydGllcwogICAgcGFzcwoKY2VsbCA9IFsKICAgIE5vZGUoMSwgMCwgMCwgMCksCiAgICBOb2RlKDIsIC0yLCAtMywgNCksCiAgICBOb2RlKDMsIC4xLCAuMiwgLjMpLAogICAgTm9kZSg0LCAyLjMsIC0zLjMsIC00LjUpLAogICAgTm9kZSg1LCAtMi41LCA0LjUsIDUpLAogICAgTm9kZSg2LCA0LCAzLiwgMi4pLAogICAgTm9kZSg3LCAtMi40NiwgMi40NiwgLTIuNDcpLAogICAgTm9kZSg4LCAyLjQ1LCAtMi40NiwgLTIuNDcpLAogICAgTm9kZSg5LCAuNSwgLjUsIC4xKSwKICAgIE5vZGUoMTAsIDUsIDYsIDcpLAogICAgIyBJbiBwcmFjdGljZSwgY2VsbHMgaGF2ZSB1cHRvIDYwMCBlbnRyaWVzCl0KCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBmb3Igbm9kZSwgZGlzdCBpbiBub2Rlc19pbl9yYW5nZShjZWxsWzBdLCBjZWxsLCA0LjIpOgogICAgICAgIHByaW50KCJ7OjNufSB7OjUuMmZ9Ii5mb3JtYXQobm9kZS5JRCwgZGlzdCkpCgppbXBvcnQgbnVtcHkKaW1wb3J0IG51bXB5LmxpbmFsZwpjb250YXJyeSA9IG51bXB5LmFzY29udGlndW91c2FycmF5CmZsb2F0MzIgPSBudW1weS5mbG9hdDMyCgojIFRoZSAibnBfY2VsbCIgaGFzIHR3byBhcnJheXM6IG9uZSBpcyB0aGUgbGlzdCBvZiBub2RlcyBhbmQgdGhlCiMgc2Vjb25kIGlzIGEgdmVjdG9yaXphYmxlIGFycmF5IG9mIHRoZWlyIHBvc2l0aW9ucy4KIyBucF9jZWxsW05dWzFdID09IG51bXB5IGFycmF5IHBvc2l0aW9uIG9mIG5wX2NlbGxbTl1bMF0KCmRlZiBtYWtlX25wX2NlbGwoY2VsbCk6CiAgICByZXR1cm4gKAogICAgICAgIHR1cGxlKGNlbGwpLAogICAgICAgIGNvbnRhcnJ5KFtjb250YXJyeSgobm9kZS54LCBub2RlLnksIG5vZGUueiksIGZsb2F0MzIpIGZvciBub2RlIGluIGNlbGxdKSwKICAgICAgICApCgojIFRoaXMgdmVyc2lvbiBmYWlscyBiZWNhdXNlIG5vcm0gcmV0dXJucyBhIHNpbmdsZSB2YWx1ZS4KZGVmIG5wX25vZGVzX2luX3JhbmdlMShzcmNQb3MsIG5wX2NlbGwsIG1heERpc3QpOgogICAgZGlzdGFuY2VzID0gbnVtcHkubGluYWxnLm5vcm0obnBfY2VsbFsxXSAtIHNyY1BvcykKCiAgICBmb3IgKG5vZGUsIGRpc3QpIGluIHppcChucF9jZWxsWzBdLCBkaXN0YW5jZXMpOgogICAgICAgIGlmIGRpc3QgPD0gbWF4RGlzdDoKICAgICAgICAgICAgeWllbGQgbm9kZSwgZGlzdAoKIyBUaGlzIHZlcnNpb24gZmFpbHMgYmVjYXVzZSAKZGVmIG5wX25vZGVzX2luX3JhbmdlMihzcmNQb3MsIG5wX2NlbGwsIG1heERpc3QpOgogICAgIyB0aGlzIHdpbGwgZmFpbCBiZWNhdXNlIHRoZSBkaXN0YW5jZXMgYXJlIHdyb25nCiAgICBkaXN0YW5jZXMgPSBudW1weS5saW5hbGcubm9ybShucF9jZWxsWzFdIC0gc3JjUG9zLCBvcmQ9MSwgYXhpcz0xKQogICAgZm9yIChub2RlLCBkaXN0KSBpbiB6aXAobnBfY2VsbFswXSwgZGlzdGFuY2VzKToKICAgICAgICBpZiBkaXN0IDw9IG1heERpc3Q6CiAgICAgICAgICAgIHlpZWxkIG5vZGUsIGRpc3QKCiMgVGhpcyB2ZXJzaW9uIGRvZXNuJ3QgdmVjdG9yaXplIGFuZCBzbyBwZXJmb3JtcyBwb29ybHkKZGVmIG5wX25vZGVzX2luX3JhbmdlMyhzcmNQb3MsIG5wX2NlbGwsIG1heERpc3QpOgogICAgbm9ybSA9IG51bXB5LmxpbmFsZy5ub3JtCiAgICBmb3IgKG5vZGUsIHBvcykgaW4gemlwKG5wX2NlbGxbMF0sIG5wX2NlbGxbMV0pOgogICAgICAgIGRpc3QgPSBub3JtKHNyY1BvcyAtIHBvcykKICAgICAgICBpZiBkaXN0IDw9IG1heERpc3Q6CiAgICAgICAgICAgIHlpZWxkIG5vZGUsIGRpc3QKCmRlZiBucF9ub2Rlc19pbl9yYW5nZTQoc3JjUG9zLCBucF9jZWxsLCBtYXhEaXN0KToKICAgICMgdGhpcyB3aWxsIGZhaWwgYmVjYXVzZSB0aGUgZGlzdGFuY2VzIGFyZSB3cm9uZwogICAgbm9kZXMgPSBucF9jZWxsWzBdCiAgICBkaXN0YW5jZXMgPSBudW1weS5saW5hbGcubm9ybShucF9jZWxsWzFdIC0gc3JjUG9zLCBvcmQ9MSwgYXhpcz0xKQogICAgZm9yIGlkeCwgZGlzdCBpbiBlbnVtZXJhdGUoZGlzdGFuY2VzKToKICAgICAgICBpZiBkaXN0IDw9IG1heERpc3Q6CiAgICAgICAgICAgIHlpZWxkIG5vZGVzW2lkeF0sIGRpc3QKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBucF9jZWxsID0gbWFrZV9ucF9jZWxsKGNlbGwpCiAgICBzcmNQb3MgPSBucF9jZWxsWzFdWzBdICAjIFBvc2l0aW9uIGNvbHVtbiBbMV0sIGZpcnN0IG5vZGUgWzBdCiAgICBwcmludCgidjEgLSBmYWlscyBiZWNhdXNlIGl0IGdldHMgYSBzaW5nbGUgZGlzdGFuY2UiKQogICAgdHJ5OgogICAgICAgIGZvciBub2RlLCBkaXN0IGluIG5wX25vZGVzX2luX3JhbmdlMShzcmNQb3MsIG5wX2NlbGwsIGZsb2F0MzIoNC4yKSk6CiAgICAgICAgICAgIHByaW50KCJ7OjNufSB7OjUuMmZ9Ii5mb3JtYXQobm9kZS5JRCwgZGlzdCkpCiAgICBleGNlcHQgVHlwZUVycm9yOgogICAgICAgIHByaW50KCJkaXN0YW5jZXMgd2FzIGEgc2luZ2xlIHZhbHVlIikKCiAgICBwcmludCgidjIgLSBnZXRzIHRoZSB3cm9uZyBkaXN0YW5jZSB2YWx1ZXMiKQogICAgZm9yIG5vZGUsIGRpc3QgaW4gbnBfbm9kZXNfaW5fcmFuZ2UyKHNyY1BvcywgbnBfY2VsbCwgZmxvYXQzMig0LjIpKToKICAgICAgICBwcmludCgiezozbn0gezo1LjJmfSIuZm9ybWF0KG5vZGUuSUQsIGRpc3QpKQoKICAgIHByaW50KCJ2MyAtIHNsb3dlciIpCiAgICBmb3Igbm9kZSwgZGlzdCBpbiBucF9ub2Rlc19pbl9yYW5nZTMoc3JjUG9zLCBucF9jZWxsLCBmbG9hdDMyKDQuMikpOgogICAgICAgIHByaW50KCJ7OjNufSB7OjUuMmZ9Ii5mb3JtYXQobm9kZS5JRCwgZGlzdCkpCgogICAgcHJpbnQoInY0IC0gdjIgdXNpbmcgZW51bWVyYXRlIikKICAgIGZvciBub2RlLCBkaXN0IGluIG5wX25vZGVzX2luX3JhbmdlNChzcmNQb3MsIG5wX2NlbGwsIGZsb2F0MzIoNC4yKSk6CiAgICAgICAgcHJpbnQoIns6M259IHs6NS4yZn0iLmZvcm1hdChub2RlLklELCBkaXN0KSkK