def available_merges(p, l, mirror=False): """ Return the acceptable merges, i.e. combinations of routes taken from l that aren't impossible due to the quantity restriction """ if mirror: it = combinations(l, 2) else: it = permutations(l, 2) c = [] for merge in it: a, b = merge if a.volume + b.volume < p.capacity: c.append((a, b, merge_routes(a, b))) return sorted(c, key=lambda t: t[0].cost + t[1].cost - t[2].cost, reverse=True)
def available_with_pruning(p, l, cr=False, max_dist=float('inf'), spacing=None, progress_callback=None): """ Return the merges from l that are both possible and appear to be among the better merges for one of their inputs. cr: if true, use combinations from l instead of permutations max_dist: if supplied, short-circuit for distances that are more than this (i.e. avoid doing the work to see what they actually are) progress_callback: if supplied, is called every 10000 cycles with the number of cycles completed. available_with_pruning is the high-performance, bounded-memory version of available_merges. It stores the few (exactly how many is configurable below) best merges for each route; this is suboptimal, because sometimes all the best merges for a route will be invalidated by earlier merges, but it is easy to check and gives us linear space complexity instead of factorial. Using this optimization we can solve problems involving tens or hundreds of thousands of points. """ HIGH, LOW = 20, 10 # Tuned for overall performance. n = 0 actual = 0 sc_radius = 0 sc_d2 = 0 if max_dist is not None: it = MergeGrid(None, l, cr, max_dist, spacing) else: if cr: it = combinations(l, 2) else: it = permutations(l, 2) best = defaultdict(list) key = itemgetter(0) if max_dist is None: max_dist = float('inf') min_dist = -max_dist max_d2 = max_dist ** 2 def insert_merge(mr): nonlocal actual m, saving = mr if a not in best or best[a][0][0] < saving: best[a].append((saving, a, b, m)) if len(best[a]) > HIGH: best[a].sort(key=key, reverse=True) del best[a][LOW:] if b not in best or best[b][0][0] < saving: best[b].append((saving, a, b, m)) if len(best[b]) > HIGH: best[b].sort(key=key, reverse=True) del best[b][LOW:] actual += 1 def radius_check(a, b): nonlocal sc_radius radius = a._post_cost - b._pre_cost if radius > max_dist or radius < min_dist: sc_radius += 1 return False return True last = None for a, b in it: if a is not last: n += 1 last = a if n & 0xff == 0: progress_callback(n) # Fast distance exit if not radius_check(a, b): continue # Don't make routes that are over capacity if a.volume + b.volume > p.capacity: actual += 1 continue mr = merge_routes(a, b, cutoff_d2=max_d2) if mr is None: sc_d2 += 1 continue insert_merge(mr) del a del b del it c = set() for v in best.values(): c.update(v) return len(c), map(itemgetter(slice(1, None)), sorted(c, key=key, reverse=True)), (sc_radius, sc_d2)