From 179b8b5794ae8a39c64665c6fb28d10b02906827 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 19 Jan 2017 07:18:42 -0800 Subject: [PATCH] [Snippets/interpolatable] Use Hungarian algorithm from munkres or scipy when available Fixes https://github.com/fonttools/fonttools/issues/815 --- Snippets/interpolatable.py | 54 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/Snippets/interpolatable.py b/Snippets/interpolatable.py index 5448e3203..81bafb1c6 100755 --- a/Snippets/interpolatable.py +++ b/Snippets/interpolatable.py @@ -77,36 +77,36 @@ def _matching_cost(G, matching): def min_cost_perfect_bipartite_matching(G): n = len(G) - if n <= 8: - # brute-force - permutations = itertools.permutations(range(n)) - best = list(next(permutations)) - best_cost = _matching_cost(G, best) - for p in permutations: - cost = _matching_cost(G, p) - if cost < best_cost: - best, best_cost = list(p), cost - return best, best_cost - else: + try: + from scipy.optimize import linear_sum_assignment + rows, cols = linear_sum_assignment(G) + # This branch untested + assert rows == list(range(n)) + return cols, _matching_cost(G, cols) + except ImportError: + pass - # Set up current matching and inverse - matching = list(range(n)) # identity matching - matching_cost = _matching_cost(G, matching) - reverse = list(matching) + try: + from munkres import Munkres + cols = [None] * n + for row,col in Munkres().compute(G): + cols[row] = col + return cols, _matching_cost(G, cols) + except ImportError: + pass - return matching, matching_cost - # TODO implement real matching here + if n > 6: + raise Exception("Install Python module 'munkres' or 'scipy >= 0.17.0'") - # Set up cover - cover0 = [max(c for c in row) for row in G] - cover1 = [0] * n - cover_weight = sum(cover0) - - while cover_weight < matching_cost: - break - NotImplemented - - return matching, matching_cost + # Otherwise just brute-force + permutations = itertools.permutations(range(n)) + best = list(next(permutations)) + best_cost = _matching_cost(G, best) + for p in permutations: + cost = _matching_cost(G, p) + if cost < best_cost: + best, best_cost = list(p), cost + return best, best_cost def test(glyphsets, glyphs=None, names=None):