Analyzing pair by pair DNA sequences with for loops in Python
I notice something small, well you know the corner cases. You know that a DNA string will yield a score of that is the same as length. Obviously ^^. So don't check those cases. 

You make 4 iterations in your code. While only one is interesting. 

A big improvement in runtime could be to change:

def build_matrix(sequences, N):
    # in : list of lists 
    # out : matrix of scores between each pair of sequences    
    matrix = np.zeros((N, N))
    for i in range(N):
        for j in range(i, N):
            matrix[i][j] = calculate_distance(sequences[i], sequences[j])       
    return matrix


to this:

def build_matrix(sequences, N):
    matrix = np.zeros((N, N))
    for i in range(N):
        for j in range(i+1, N):
            matrix[i][j] = calculate_distance(sequences[i], sequences[j])
    return matrix




If you could load your data as:

def get_test_arr():
    parts = ['0', 'A', 'T', 'C', 'G']
    return ["".join(
        sorted([random.choice(parts) for _ in range(2)]))
            for _ in range(5*10**5)]


you could make them np arrays as:

a = np.array(get_test_arr(), dtype=object)
b = np.array(get_test_arr(), dtype=object)


and have your distance calculation as:

def calculate_distance1(genom1, genom2):
    score = 0
    for g1, g2 in zip(genom1, genom2):
        if (g1[0] in g2) or (g1[1] in g2):
            score += 2 if g1 == g2 else 1
    return score




In the problem you've provided each induvidual pairs order does not seem to matter, so load them as such. If you do that, you don't have to sort the in the calculation function.

If you don't, you'll resort them later. Again and again. Them more samples you use, there more time is spent doing this.

Times:



Your calculation function

0.575332 s for one loop in 100 iterations.

The my solution 

0.10583 s for one loop in 100 iterations.