""" CS146 Example with sequence alignment using recursion and memoization """ from functools import wraps import urllib.request # Python has rejected the certificate used for the CS dept. server so we bypass some of those checks import ssl ssl._create_default_https_context = ssl._create_unverified_context # Edge scores MATCH = 1 MISMATCH = -1 GAP = -1 # Record source of incoming best edge DIAG = 0 LEFT = 1 UPWD = 2 TAXI = 3 def countcalls(func): """Decorator to track the number of times a function is called. Provides `calls` attribute.""" countcalls.calls = 0 @wraps(func) def wrapper(*args, **kwargs): initial_calls = countcalls.calls countcalls.calls += 1 result = func(*args, **kwargs) wrapper.calls = countcalls.calls - initial_calls return result return wrapper @countcalls def global_align(lft_seq, top_seq, lft, top): """Perform Needleman-Wunsch global alignment of two sequences Args: lft_seq: String sequence on "left side" of the alignment grid top_seq: String sequence on "top" of the alignment grid lft: Index to align through, i.e., left_seq[lft-1] with top_seq[top-1] top: Index to align through, i.e., left_seq[lft-1] with top_seq[top-1] Returns: Score of global alignment of left_seq[lft-1] with top_seq[top-1] """ if lft == 0 and top == 0: return 0 elif lft == 0: return global_align(lft_seq, top_seq, lft, top-1) + GAP elif top == 0: return global_align(lft_seq, top_seq, lft-1, top) + GAP else: if lft_seq[lft-1] == top_seq[top-1]: diag = MATCH else: diag = MISMATCH return max( global_align(lft_seq, top_seq, lft, top-1) + GAP, global_align(lft_seq, top_seq, lft-1, top) + GAP, global_align(lft_seq, top_seq, lft-1, top-1) + diag ) # Test example # global_align("TGTCC", "TGATC", 5, 5) # print("Number of recursive calls:", global_align.calls) # Report number of recursive calls @countcalls def global_align_memo(lft_seq, top_seq, lft, top, scores): """Perform memoized Needleman-Wunsch global alignment of two sequences Args: lft_seq: String sequence on "left side" of the alignment grid top_seq: String sequence on "top" of the alignment grid lft: Index to align through, i.e., left_seq[lft-1] with top_seq[top-1] top: Index to align through, i.e., left_seq[lft-1] with top_seq[top-1] scores: Dictionary of memoized scores for each (lft, top) pair Returns: Score of global alignment of left_seq[lft-1] with top_seq[top-1] """ #TODO: Have we already solved this problem? Return solution if so. if lft == 0 and top == 0: edge = 0 elif lft == 0: edge = global_align_memo(lft_seq, top_seq, lft, top-1, scores) + GAP elif top == 0: edge = global_align_memo(lft_seq, top_seq, lft-1, top, scores) + GAP else: if lft_seq[lft-1] == top_seq[top-1]: diag = MATCH else: diag = MISMATCH edge = max( global_align_memo(lft_seq, top_seq, lft, top-1, scores) + GAP, global_align_memo(lft_seq, top_seq, lft-1, top, scores) + GAP, global_align_memo(lft_seq, top_seq, lft-1, top-1, scores) + diag ) #TODO: Save solution to this problem for future reference return edge # Test example # scores = {} # global_align_memo("TGTCC", "TGATC", 5, 5, scores) # print("Number of recursive calls:", global_align_memo.calls) # Report number of recursive calls