Commit b005c4be by Hrishee Shastri

### Merge branch 'refactor-gdc' into 'release'

```Refactor gdc

See merge request eschoute/reversal-sort!3```
parents 5b292738 72aa1518
 ... ... @@ -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 adaptive_tb_sort """ 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(adaptive_tb_sort) 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,15 @@ 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 apply(self, perm): # Perform the given reversal on the given permutation i = self.beg j = self.end perm[i:j+1] = perm[i:j+1][::-1] def __lt__(self, other): return self.beg < other.beg ... ... @@ -42,13 +51,6 @@ class Reversal: return repr(self) # Perform the given reversal on the given permutation def reverse(permutation, reversal): i = reversal.beg j = reversal.end permutation[i:j+1] = permutation[i:j+1][::-1] # Determines if the reversals in the given list are independent, where perm_length is the length of the permutation def independent(rev_list, perm_length): busy = [0]*perm_length ... ... @@ -186,48 +188,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 median(L): return len(L) // 2 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:] # Note that permutations are 0-indexed so we need to shift by one. return [int(x + 1 > median(L)) for x in L] class DCRoute: """Divide and Conquer routing algorithm""" def __init__(self, sorting_alg): """Construct divide and conquer routing algorithm with a sorting algorithm""" self.alg = sorting_alg def routing_divideconquer_tbs(L, i=None, j=None): def route(self, L): """ Sorts the given permutations L using a divide and conquer approach. Returns a list of reversals used to perform the sort. L must contain all elements in [0, len(L)-1] once. """ if len(L) <= 1: return [] T = perm_to_01(L) revs = self.alg(T) for rev in revs: rev.apply(L) m = median(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,26 +6,33 @@ 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 Performs TBS on T, 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 + 1]) middle_revs = tripartite_binary_sort(negatelist(T[part1 + 1: part2 + 1])) revlist += [rev.offset(part1 + 1) for rev in middle_revs] end_revs = tripartite_binary_sort(T[part2 + 1: len(T)]) revlist += [rev.offset(part2 + 1) for rev in end_revs] # Leave T immutable copyT = T.copy() for rev in revlist: rev.apply(copyT) oneind, zerind = zero_one_indices(copyT, 0, len(T)-1) if oneind < zerind: rev = reversal.Reversal(oneind, zerind) reversal.reverse(T, rev) revlist.append(rev) return revlist def negatelist(T): return [int(not x) for x in T] def flipbits(T, i, j): for index in range(i,j+1): T[index] = int(not T[index]) ... ...
 import random from reversal_sort import routing from unittest import TestCase from reversal_sort.routing import DCRoute from reversal_sort.tripartite_binary_sort import tripartite_binary_sort from reversal_sort.adaptive_tbs import adaptive_tb_sort class TestRouting(TestCase): def test_perm_sort(self): def check_route_correctly(self, router, L): L_orig = L.copy() reversals = router.route(L.copy()) for rev in reversals: rev.apply(L) self.assertEqual(sorted(L_orig), L, msg=f"Did not route {L_orig}") def test_route_tbs_simple(self): L = [0, 1, 3, 2] router = DCRoute(tripartite_binary_sort) self.check_route_correctly(router, L) def test_route_tbs_simple2(self): L = [1, 3, 0, 4, 2] router = DCRoute(tripartite_binary_sort) self.check_route_correctly(router, L) def test_route_tbs(self): """ Testing correctness of sorting permutations with TBS as bitstring sorting subroutine. Returns number """ n = 1000 router = DCRoute(tripartite_binary_sort) for ct in range(n): L = list(range(0,random.randint(5, 200))) random.shuffle(L) self.check_route_correctly(router, L) def test_route_atbs(self): """ Testing correctness of sorting permutations with TBS as bitstring sorting subroutine. Returns number """ n = 1000 router = DCRoute(adaptive_tb_sort) for ct in range(n): L = list(range(1,random.randint(5, 200))) L = list(range(0,random.randint(5, 50))) random.shuffle(L) before = L.copy() routing.routing_divideconquer_tbs(L,0,len(L)-1) self.assertEqual(sorted(before), L) \ No newline at end of file self.check_route_correctly(router, L) \ No newline at end of file
 import random from reversal_sort import tripartite_binary_sort from reversal_sort.tripartite_binary_sort import tripartite_binary_sort from unittest import TestCase class TestTBS(TestCase): def test_simple(self): l = [1, 0, 1, 0] sortme = l.copy() reversals = tripartite_binary_sort(sortme) for reversal in reversals: reversal.apply(sortme) self.assertEqual(sortme, sorted(l)) def test_simple2(self): l = [1, 0, 0, 1, 1] sortme = l.copy() reversals = tripartite_binary_sort(sortme) for reversal in reversals: reversal.apply(sortme) self.assertEqual(sortme, sorted(l)) def test_binary_sort(self): """ Testing correctness of sorting bitstrings with TBS. """ for i in range(1000): l = [0]*random.randint(100,200) + [1]*random.randint(100,200) random.shuffle(l) before = l.copy() tripartite_binary_sort.tripartite_binary_sort(l, 0 , len(l)-1) self.assertEqual(sorted(before), l) \ No newline at end of file shuffled = l.copy() random.shuffle(shuffled) shuffled_orig = shuffled.copy() reversals = tripartite_binary_sort(shuffled) for reversal in reversals: reversal.apply(shuffled) self.assertEqual(shuffled, l, msg=f"Did not sort {shuffled_orig}") \ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!