[varLib] Speed up IUP DP when forced set is empty

Drives it into acceptable speed now.  Note that I'm getting better
gvar size after this patch, which I didn't expect.  So, either this
algorithm or the previous one was buggy. :(
This commit is contained in:
Behdad Esfahbod 2017-07-22 18:31:10 -07:00
parent 7fa1ce1ca7
commit 91e4220193

View File

@ -279,17 +279,7 @@ def _iup_contour_optimize_dp(delta, coords, forced={}, tolerance=0):
if j in forced: if j in forced:
break break
# Assemble solution. return chain, costs
solution = set()
i = n - 1
while i is not None:
solution.add(i)
i = chain[i]
assert forced <= solution, (forced, solution)
delta = [delta[i] if i in solution else None for i in range(n)]
return delta, costs[n - 1]
def _rot_list(l, k): def _rot_list(l, k):
"""Rotate list by k items forward. Ie. item at position 0 will be """Rotate list by k items forward. Ie. item at position 0 will be
@ -304,19 +294,6 @@ def _rot_set(s, k, n):
if not k: return s if not k: return s
return {(v + k) % n for v in s} return {(v + k) % n for v in s}
def _iup_contour_optimize_dp_with_offset(delta, coords, offset, forced, tolerance):
n = len(delta)
delta = _rot_list(delta, offset)
coords = _rot_list(coords, offset)
forced = _rot_set(forced, offset, n)
delta, cost = _iup_contour_optimize_dp(delta, coords, forced, tolerance)
delta = _rot_list(delta, -offset)
return delta, cost
def _iup_contour_optimize(delta, coords, tolerance=0.): def _iup_contour_optimize(delta, coords, tolerance=0.):
n = len(delta) n = len(delta)
@ -340,7 +317,7 @@ def _iup_contour_optimize(delta, coords, tolerance=0.):
forced = _iup_contour_bound_forced_set(delta, coords, tolerance) forced = _iup_contour_bound_forced_set(delta, coords, tolerance)
# The _iup_contour_optimize_dp() routine returns the optimal encoding # The _iup_contour_optimize_dp() routine returns the optimal encoding
# solution given the constraint that the last point is always encoded. # solution given the constraint that the last point is always encoded.
# To remove this constraint, we do two different things, depending on # To remove this constraint, we use two different methods, depending on
# whether forced set is non-empty or not: # whether forced set is non-empty or not:
if forced: if forced:
@ -348,21 +325,42 @@ def _iup_contour_optimize(delta, coords, tolerance=0.):
# such that the last point in the list is a forced point. # such that the last point in the list is a forced point.
k = (n-1) - max(forced) k = (n-1) - max(forced)
assert k >= 0 assert k >= 0
delta, _ = _iup_contour_optimize_dp_with_offset(delta, coords, k, forced, tolerance)
delta = _rot_list(delta, k)
coords = _rot_list(coords, k)
forced = _rot_set(forced, k, n)
chain, costs = _iup_contour_optimize_dp(delta, coords, forced, tolerance)
# Assemble solution.
solution = set()
i = n - 1
while i is not None:
solution.add(i)
i = chain[i]
assert forced <= solution, (forced, solution)
delta = [delta[i] if i in solution else None for i in range(n)]
delta = _rot_list(delta, -k)
else: else:
# Brute-force: rotate and retry until all points on the contour are part chain, costs = _iup_contour_optimize_dp(delta+delta, coords+coords, forced, tolerance)
# of at least one solution. The best of those solutions is the optimal # TODO add lookback=n to DP
# solution. best_sol, best_cost = None, n+1
best_delta, best_cost = None, n + 1
tocover = set(range(n)) for start in range(n-1, 2*n-1):
while tocover: # Assemble solution.
k = (n-1) - max(tocover) solution = set()
assert k >= 0 i = start
this_delta, this_cost = _iup_contour_optimize_dp_with_offset(delta, coords, k, forced, tolerance) while i > start - n:
if this_cost < best_cost: solution.add(i % n)
best_delta, best_cost = this_delta, this_cost i = chain[i]
tocover -= {i for i,v in enumerate(this_delta) if v is not None} if i == start - n:
delta = best_delta cost = costs[start] - costs[start - n]
if cost <= best_cost:
best_sol, best_cost = solution, cost
delta = [delta[i] if i in best_sol else None for i in range(n)]
return delta return delta