Commit adbd4d25 authored by Eddie Schoute's avatar Eddie Schoute
Browse files

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