Commit adbd4d25 by Eddie Schoute

### Refactor to generic divide & conquer alg

```Now we can construct routing algorithms from various sorting algorithms,
such as TBS and ATBS.```
parent 5b292738
 ... ... @@ -3,7 +3,9 @@ from itertools import permutations from math import factorial import random from reversal_sort import SBR, adaptive_tbs, tripartite_binary_sort from reversal_sort import routing from reversal_sort.tripartite_binary_sort import tripartite_binary_sort from reversal_sort.adaptive_tbs import binary_sort_parallel """ Collects routing time data for the algorithms GDC(TBS), GDC(ATBS), and OES. ... ... @@ -117,7 +119,7 @@ def new_rand_perms_file(alglist, algnames, nlist, count): def main(NUM_PERMS_PER_LENGTH, LENGTH_FROM, LENGTH_TO): algnames = ['OES', 'GDC(TBS)', 'GDC(ATBS)'] algs = [SBR.odd_even_sort, tripartite_binary_sort.GDC_TBS, adaptive_tbs.GDC_ATBS] algs = [routing.odd_even_sort, routing.GDC_TBS, routing.GDC_ATBS] new_rand_perms_file(algs, algnames, list(range(LENGTH_FROM,LENGTH_TO + 1)), NUM_PERMS_PER_LENGTH) ... ... @@ -127,7 +129,8 @@ if __name__ == "__main__": for line in sys.stdin: perm = [int(el) for el in line.split()] # Check if is permutation if set(range(0, len(perm))) != set(perm): if set(range(len(perm))) != set(perm): raise ValueError(f"Given permutation does not contain all elements in [0,{len(perm)-1}].") reversals = tripartite_binary_sort.routing_divideconquer_tbs(perm) router = routing.DCRoute(binary_sort_parallel) reversals = router.route(perm) print(reversals) # TODO: Implement pretty print \ No newline at end of file
 import math from . import reversal def GDC_ATBS(perm): """ Sorts the given permutation by using a version of Tripartite Binary Sort using dynamic programming. Returns the cost of sorting the permutation. """ revs = sequential_permutation_sort_parallelized(perm, 0, len(perm) - 1) return reversal.compress_reversals(revs, len(perm)) / 3 from .reversal import Reversal def beginning_index_of_weight(w, v, i): """ ... ... @@ -64,19 +54,7 @@ def weights_to_cumulative(w): v.append(oddsum) return v def perm_to_01(L, i, j): """ TUrns a permutation L[i:j] into a permutation of 0s and 1s, where L[i] is 0 if it is less than the median and L[i] is 1 if it is greater than the median """ subseq = L[i:j + 1] sorted_subseq = sorted(subseq) median = (j - i) // 2 return L[:i] + [int(sorted_subseq.index(k) > median) for k in subseq] + L[j + 1:] def binary_sort_parallel(b): def adaptive_tb_sort(b): """ Returns the reversals required to sort the given binary sequence using a parallelized version of the sequential binary sort algorithm found in Bender et al.'s paper "Improved Bounds on Sorting by Length-Weighted Reversals". ... ... @@ -95,7 +73,6 @@ def binary_sort_parallel(b): fill_4_parallel(w, v, A, i, j, (i + 1) % 2) return A[1][len(w) - 2][0][1] def fill_4_parallel(w, v, A, i, j, b): """ Fills the matrix A[i,j,b] according to equation (4) in the paper. The parallelizations are made in equation (6). ... ... @@ -105,7 +82,7 @@ def fill_4_parallel(w, v, A, i, j, b): elif j == i + 1 and b == (i + 1) % 2: leftind = beginning_index_of_weight(w, v, i) rightind = end_index_of_weight(w, v, j) r = reversal.Reversal(leftind, rightind) r = Reversal(leftind, rightind) A[i][j][b] = [w[i] + w[j] + 1, [r]] elif j > i + 1 and b == i % 2: A[i][j][b] = A[i + 1][j][b] ... ... @@ -130,23 +107,6 @@ def fill_6_parallel(w, v, A, i, j, b): minval = val leftind = beginning_index_of_weight(w, v, t + 1) - (v[t - ((t - i) % 2)] - v[i] + w[i]) rightind = end_index_of_weight(w, v, k - 1) + (v[j] - v[k + ((j - k) % 2)] + w[k + ((j - k) % 2)]) r = reversal.Reversal(leftind, rightind) r = Reversal(leftind, rightind) minrevs = A[i][t][b][1] + A[t + 1][k - 1][1 - b][1] + A[k][j][b][1] + [r] A[i][j][b] = [minval, minrevs] def sequential_permutation_sort_parallelized(perm, i, j): """ Sorts the given permutation from index i to j (inclusive) by using a parallelized version of the optimal sequential sorting search for 0/1 strings. The method of parallelization is dictated by the version number. Returns a list of reversals used to sort the permutation. """ if i == j: return [] b = perm_to_01(perm, i, j) revs = binary_sort_parallel(b[i:j + 1]) offsetrevs = [reversal.Reversal(i + r.beg, i + r.end) for r in revs] m = (i + j) // 2 reversal.apply_revs(offsetrevs, perm) return offsetrevs + sequential_permutation_sort_parallelized(perm, i, m) + \ sequential_permutation_sort_parallelized(perm, m + 1, j) \ No newline at end of file
 ... ... @@ -32,6 +32,9 @@ class Reversal: def get_length(self): return abs(self.beg - self.end) + 1 def offset(self, by): return Reversal(self.beg + by, self.end + by) def __lt__(self, other): return self.beg < other.beg ... ... @@ -186,48 +189,6 @@ class ReversalCompresser: return self.counter def odd_even_sort(L): """ L: List to be sorted. Since we can reduce the problem of sorting according to a permutation pi to just sorting according to the identity permutation, we just sort normally. Odd even sort is performed in parallel. Each pass is an odd pass or even pass, where we look at every odd/even pair and swap if they are out of order. Each pass takes time 1, since there are n/2 swaps per pass (each taking time 1) done in parallel. sorts in place. Returns the cost """ cost = 0 sortflag = False addflag = False while not sortflag: sortflag = True addflag = False for i in range(1, len(L) - 1, 2): if L[i] > L[i+1]: L[i], L[i+1] = L[i+1], L[i] sortflag = False addflag = True cost += int(addflag) addflag = False for i in range(0, len(L) - 1, 2): if L[i] > L[i+1]: L[i], L[i+1] = L[i+1], L[i] sortflag = False addflag = True cost += int(addflag) return cost def apply_revs(revs, perm): """ Applies reversals in list revs in order to permutation perm, in place ... ...
 from . import reversal, tripartite_binary_sort from . import reversal def GDC_TBS(perm): """ Returns the cost to sort perm using TBS as the binary sorting sequence. (GenericDivideConquer_TripartiteBinarySort) """ revlist = routing_divideconquer_tbs(perm, 0, len(perm) - 1) cost = reversal.compress_reversals(revlist, len(perm)) / 3 return cost def perm_to_01(L, i, j): def perm_to_01(L): """ TUrns a permutation L[i:j] into a permutation of 0s and 1s, where L[i] is 0 if it is less than the median and L[i] is 1 if it is greater than the median """ subseq = L[i:j + 1] sorted_subseq = sorted(subseq) median = (j - i) // 2 return L[:i] + [int(sorted_subseq.index(k) > median) for k in subseq] + L[j + 1:] return [int(x > (len(L)-1) // 2) for x in L] class DCRoute: """Divide and Conquer routing algorithm""" def routing_divideconquer_tbs(L, i=None, j=None): def __init__(self, sorting_alg): """Construct divide and conquer routing algorithm with a sorting algorithm""" self.alg = sorting_alg def route(self, L): """ Sorts the given permutations L from index i to j (inclusive) using a divide and conquer approach. Returns a list of reversals used to perform the sort. """ if len(L) <= 1: return [] T = perm_to_01(L) revs = self.alg(T) m = len(T) // 2 reversal.apply_revs(revs, L) left_revs = self.route(L[:m]) right_perm = [x - m for x in L[m:]] right_revs = [rev.offset(m) for rev in self.route(right_perm)] return revs + left_revs + right_revs def odd_even_sort(L): """ Sorts the given permutations L from index i to j (inclusive) using a divide and conquer approach. Returns a list of reversals used to perform the sort. L: List to be sorted. Since we can reduce the problem of sorting according to a permutation pi to just sorting according to the identity permutation, we just sort normally. Odd even sort is performed in parallel. Each pass is an odd pass or even pass, where we look at every odd/even pair and swap if they are out of order. Each pass takes time 1, since there are n/2 swaps per pass (each taking time 1) done in parallel. sorts in place. Returns the cost """ # Set default params if i == None: i = min(L) if j == None: j = max(L) if i == j: return [] T = perm_to_01(L, i, j) revs = tripartite_binary_sort.tripartite_binary_sort(T, i, j) m = (i + j) // 2 reversal.apply_revs(revs, L) return revs + routing_divideconquer_tbs(L, i, m) + routing_divideconquer_tbs(L, m + 1, j) cost = 0 sortflag = False addflag = False while not sortflag: sortflag = True addflag = False for i in range(1, len(L) - 1, 2): if L[i] > L[i+1]: L[i], L[i+1] = L[i+1], L[i] sortflag = False addflag = True cost += int(addflag) addflag = False for i in range(0, len(L) - 1, 2): if L[i] > L[i+1]: L[i], L[i+1] = L[i+1], L[i] sortflag = False addflag = True cost += int(addflag) return cost
 ... ... @@ -6,20 +6,22 @@ import math from . import reversal def tripartite_binary_sort(T, i, j): def tripartite_binary_sort(T): """ Performs TBS (binary version) on T[i,j] inclusive, returns list of reversals """ if all(T[ind] <= T[ind + 1] for ind in range(i, j)): if all(T[ind] <= T[ind + 1] for ind in range(len(T) - 1)): return [] part1, part2 = (2 * i + j) // 3, (i + 2 * j) // 3 part1, part2 = len(T) // 3, 2 * len(T) // 3 revlist = [] revlist += tripartite_binary_sort(T, i, part1) flipbits(T, part1 + 1, part2) revlist += tripartite_binary_sort(T, part1 + 1, part2) flipbits(T, part1 + 1, part2) revlist += tripartite_binary_sort(T, part2 + 1, j) oneind, zerind = zero_one_indices(T, i, j) revlist += tripartite_binary_sort(T[:part1]) flipbits(T, part1, part2) middle_revs = tripartite_binary_sort(T[part1: part2]) revlist += [rev.offset(part1) for rev in middle_revs] flipbits(T, part1, part2) end_revs = tripartite_binary_sort(T[part2: len(T)]) revlist += [rev.offset(part2) for rev in end_revs] oneind, zerind = zero_one_indices(T, 0, len(T)-1) if oneind < zerind: rev = reversal.Reversal(oneind, zerind) reversal.reverse(T, rev) ... ...
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment