diff --git a/Python/waveform.py b/Python/waveform.py
index 1c93946284ec1d979faaa4887b4783f28a2f20fc..e7167d8cc7002d5e7ea62f64724baf9d57ab3cd3 100644
--- a/Python/waveform.py
+++ b/Python/waveform.py
@@ -136,14 +136,51 @@ def create_path_table(wfm: Waveform) -> any:
     # return path_table.astype(int), np.sum(path_table.diagonal().T, axis=0, dtype=int)
 
 
+def stack_left(i_start, i_end, offset, stack_size=0):
+    # calculate first index where the reduced path algorithm is applied
+    #     threshold = 0.01
+    #     cutoff = np.ceil(np.log(threshold) / np.log(1-load_p))
+    #     cutoff = int(cutoff)
+    #     print(cutoff)
+    if stack_size == 0:
+        stack_size = np.floor((i_end - i_start) / 2)
+    stack_last = int(stack_size + i_start) - 1
+    dist_mod = (i_end - i_start - stack_size) / (i_end - i_start)  # max_distance ratio
+    dist_add = offset
+
+    # get a list of moves to pre-generate
+    moves = []
+    max_dist = 0
+    for i in range(i_start, i_end):
+        moves.append([])
+        j_max = i if i < stack_last else stack_last
+        dist = np.ceil((i - i_start) * dist_mod + dist_add)
+        j_min = int(i - dist) if i - dist >= i_start else i_start
+        for j in range(j_min, j_max + 1):
+            moves[i - i_start].append(j)  # add all paths between j_min and j_max
+            if max_dist < abs(j-i):
+                max_dist = abs(j-i)
+    return moves, max_dist
+
+
+def stack_right(i_start, i_end, offset, stack_size=0):
+    moves, max_dist = stack_left(i_start, i_end, offset=offset, stack_size=stack_size)
+    moves.reverse()
+    for i in range(len(moves)):
+        moves[i].reverse()
+        for j in range(len(moves[i])):
+            moves[i][j] = i_end - 1 - moves[i][j] + i_start
+    return moves, max_dist
+
+
 def create_path_table_reduced(
-        wfm: Waveform, target_idx, max_dist=np.inf, save_path=None
+        wfm: Waveform, target_idx, dist_offset=np.inf, save_path=None, partition=False
 ) -> Tuple[Dict[Tuple[int, int], np.ndarray], np.ndarray]:
     """
     create a dim-3 look up table where the table[i,j] contains a sine wave to move tweezer i to tweezer j
     :param save_path: file saving path
     :param target_idx: indices of target pattern
-    :param max_dist: maximum move distance in indices
+    :param dist_offset: maximum move distance in indices
     :param wfm: waveform object already initialized with basic parameters.
     :return: dictionary containing rearrange paths
     """
@@ -153,19 +190,38 @@ def create_path_table_reduced(
     a = wfm.amplitude
     omega_interp = interp1d(w, a, kind='cubic')
 
-    # obtain all move combinations:
-    n = len(wfm.omega)  # total number of tweezers
+    nt = len(wfm.omega)  # total number of tweezers
     moves = []
+    target = np.zeros(nt)
+    target[target_idx] = 1
     dw_max = 0  # longest move, this sets the size of path_table
-    for i in range(n):
-        moves.append([])
-        for j in target_idx:
-            if i < j and True:  # only allow uni-direction moves
-                continue
-            if abs(i - j) <= max_dist:
-                moves[i].append(j)
-                dw = abs(wfm.omega[j] - wfm.omega[i])
-                if dw_max < dw: dw_max = dw
+
+    if not partition:
+        # obtain all move combinations, target based, non-partitioned:
+        for i in range(nt):
+            moves.append([])
+            for j in target_idx:
+                if i < j and True:  # only allow uni-direction moves
+                    continue
+                if abs(i - j) <= dist_offset:
+                    moves[i].append(j)
+                    dw = abs(wfm.omega[j] - wfm.omega[i])
+                    if dw_max < dw: dw_max = dw
+    if partition:
+        offset = dist_offset
+        divide_idx = int(np.floor(np.median(target_idx)))
+        left_size = np.sum(target[:divide_idx], dtype=int)
+        right_size = np.sum(target[divide_idx:], dtype=int)
+        moves_l, dw_max_l = stack_right(0, divide_idx, offset, left_size)  # stack left side to right
+        moves_r, dw_max_r = stack_left(divide_idx, nt, offset, right_size)
+        #     print("stack size, half size, middle:", len(t_idx), left_size, right_size)
+        moves_l.extend(moves_r)
+        moves = moves_l
+        dw_max = dw_max_l if dw_max_l > dw_max_r else dw_max_r
+        dw_max = abs(wfm.omega[dw_max] - wfm.omega[0])
+
+    print(dw_max / 2 / np.pi)
+
     # setup basic variables
     twopi = 2 * np.pi
     vmax = KILO(20) * MEGA(1)  # convert units, 20 kHz/us -> 20e3 * 1e6 Hz/s
@@ -271,13 +327,13 @@ def create_path_table_reduced(
 
 
 def create_path_table_reduced_gpu(
-        wfm: Waveform, target_idx, max_dist=np.inf, save_path=None
+        wfm: Waveform, target_idx, dist_offset=np.inf, save_path=None, partition=False
 ) -> Tuple[Dict[Tuple[int, int], np.ndarray], np.ndarray]:
     """
     create a dim-3 look up table where the table[i,j] contains a sine wave to move tweezer i to tweezer j
     :param save_path: file saving path
     :param target_idx: indices of target pattern
-    :param max_dist: maximum move distance in indices
+    :param dist_offset: maximum move distance in indices
     :param wfm: waveform object already initialized with basic parameters.
     :return: dictionary containing rearrange paths
     """
@@ -289,19 +345,37 @@ def create_path_table_reduced_gpu(
     a = wfm.amplitude
     omega_interp = interp1d(w, a, kind='cubic')
 
-    # obtain all move combinations:
-    n = len(wfm.omega)  # total number of tweezers
+    nt = len(wfm.omega)  # total number of tweezers
     moves = []
+    target = np.zeros(nt)
+    target[target_idx] = 1
     dw_max = 0  # longest move, this sets the size of path_table
-    for i in range(n):
-        moves.append([])
-        for j in target_idx:
-            if i < j and True:  # only allow uni-direction moves
-                continue
-            if abs(i - j) <= max_dist:
-                moves[i].append(j)
-                dw = abs(wfm.omega[j] - wfm.omega[i])
-                if dw_max < dw: dw_max = dw
+
+    if not partition:
+        # obtain all move combinations, target based, non-partitioned:
+        for i in range(nt):
+            moves.append([])
+            for j in target_idx:
+                if i < j and True:  # only allow uni-direction moves
+                    continue
+                if abs(i - j) <= dist_offset:
+                    moves[i].append(j)
+                    dw = abs(wfm.omega[j] - wfm.omega[i])
+                    if dw_max < dw: dw_max = dw
+    if partition:
+        offset = dist_offset
+        divide_idx = int(np.floor(np.median(target_idx)))
+        left_size = np.sum(target[:divide_idx], dtype=int)
+        right_size = np.sum(target[divide_idx:], dtype=int)
+        moves_l, dw_max_l = stack_right(0, divide_idx, offset, left_size)  # stack left side to right
+        moves_r, dw_max_r = stack_left(divide_idx, nt, offset, right_size)
+        #     print("stack size, half size, middle:", len(t_idx), left_size, right_size)
+        moves_l.extend(moves_r)
+        moves = moves_l
+        dw_max = dw_max_l if dw_max_l > dw_max_r else dw_max_r
+        dw_max = abs(wfm.omega[dw_max] - wfm.omega[0])
+
+    print("max dw:", dw_max / 2 / np.pi)
 
     # setup basic variables
     twopi = 2 * np.pi