 Optimize GHPython random 2D points component for faster population?

Hi,

I’ve ported the Population method from the Vector GHa lib, mentioned by @DavidRutten in this thread to GHPython. Now I’m looking for advice, on how to optimise the code in order to make it run faster, since I need it to produce about 10 000 to 20 000 points (tuples with 3 coordinates) for me.

Here are the current speeds of the Python component:

Population Time Percentage
10 6 ms 28 %
100 853 ms 85 %
1000 1.3 min 100 %
10 000 60 min >100 %

This is what the code looks like! The most interesting section to optimise might be the populate() method of the Population class.

import Rhino.Geometry as rg
import random
import math
import sys

class Population:
"""A population of random points within a region.

To use:
>>> ppl = Population(10, (25,25))
>>> ppl.populate()

>>> ppl.get_coordinates()
[(x0, y0, z0), (x1, y1, y2), ..., (x10, y10, z10)]

>>> ppl.get_points()
[Rhino.Geometry.Point3d, Rhino.Geometry.Point3d, ..., Rhino.Geometry.Point3d]
"""

def __init__(self, count, region):
self.count = int(count)
self.region = region
self.population = []

def distance_to(self, ptA, ptB):
"""Calculate the distance between two 3-dimensional points.

Args:
ptA: 3-dimensional point A (Rhino.Geometry.Point3d, tuple or list)
ptB: 3-dimensional point B (Rhino.Geometry.Point3d, tuple or list)
Returns:
The distance between point A and point B.
"""
dist = math.sqrt((ptA -  ptB)**2 + (ptA -  ptB)**2 + (ptA -  ptB)**2)
return dist

def next_point(self, domain):
"""Constructs a random 2-dimensional point coordinate.

Args:
domain: Tuple of 2 maximum values in x and y.
Returns:
Returns a tuple of random 2D point coordinates.
"""
maxX = domain
minX = -maxX
maxY = domain
minY = -maxY
return (random.uniform(minX, maxX), random.uniform(minY, maxY), 0)

def populate(self):
"""Populates a 2-dimensional region with random 2-dimensional points."""
distance_threshold = sys.maxint

for i in range(self.count):
if (len(self.population) == 0): # first point
pt = self.next_point(self.region)
self.population.append(pt)

else: # other points
attempts = int(max(50, math.sqrt(i)))
max_dist = -sys.maxint
max_pt = 0

for k in range(attempts):
if (k > 100) and (max_dist > distance_threshold * 0.9):
break
elif (k > 50) and (max_dist > distance_threshold * 0.92):
break
elif (k > 25) and (max_dist > distance_threshold * 0.95):
break

pt = self.next_point(self.region)
dist = sorted(self.distance_to(pt, prev_pt) for prev_pt in self.population)
if dist == sys.maxint:
break
if dist > max_dist:
max_dist = dist
max_pt = pt
if (max_dist > distance_threshold * 1.25):
break

distance_threshold = max_dist
self.population.append(max_pt)

def get_coordinates(self):
"""Returns a list of random 2-dimensional point coordinates witihn a region."""
return self.population

def get_points(self):
"""Returns a list of Rhino.Geometry.Point3d witin a redion."""
pts = [rg.Point3d(c, c, c) for c in self.population]
return pts

# Variables
count = 10 # number of random points to produce
domain = (5.0, 5.0) # max. extends of the region in x and y

# Initialise class
ppl = Population(count, domain)
ppl.populate()

# Output
a = ppl.get_points()

(To test the script, just copy and paste the python code into a GHPython component and run it.)

Any advice is welcome and appreciated! import rhinoscriptsyntax as rs
rs.EnableRedraw(False)
...code here...
rs.EnableRedraw(True)

Could you refactor the code to use generators instead of creating a massive list of points? You could keep a list of guid if you need to keep track of them

1 Like

Thanks @Dancergraham!

Could you explain a little, what you mean by generators? Would keeping a list of GUIDs be less expensive than keeping a list op tuples with 3 float values each?

Generators let you generate objects one by one rather than storing them all in memory. They are defined much like functions but use yield rather than return:

2 Likes

No saving in using the guid rather than the tuple

import rhinoscriptsyntax as rs
import sys
import Rhino.Geometry as rg

ptcoords = (1000.,1000.,1000.)