Project: s41467-024-49163-7 | Function: Calc_Interaction_Energy (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Implements the core biophysical interaction energy calculation between RNase and SLF proteins. Calculates the total interaction energy as the sum of pairwise amino acid interactions, directly implementing the paper's Equation 1.

**KEY TERMS:** {
  "female_al_pool": "RNase protein sequences R_i",
  "male_al_pool": "SLF protein sequences F_j",
  "eij": "interaction energy matrix E(R_i(k)F_j(k))",
  "E_pool_Ri_Fj": "total interaction energies E_tot(R_i,F_j)",
  "sum_eRi_Fj": "sum of pairwise interactions Σ E(R_i(k)F_j(k))"
}

**CONTEXT CODE:**     haplotypes_dict = {}
    num_of_alleles = Model.slf_amount_in_haploid

    for ind in range(Model.num_types_of_haplotypes):
        allele_indices_list = list(range(Model.num_types_of_haplotypes))
        allele_indices_list.remove(ind)  # all SLFs but the i'th, the one that fertilizes the i'th RNase

        alleles = []
        for k in range(num_of_alleles):
            tmp_all = deepcopy(SLF_object[allele_indices_list[k]])
            alleles.append(tmp_all)

        haplotype = Model.Haplotype(RNase_object[ind], alleles,
                    ind, ind, [], [], 0, 0, 'Initiation', 'False',
                                [], [], [], [], [], [])

        haplotype.Haplotype_index_in_pool = ind
        haplotype.RNasesIndices = []
        haplotype.RNasesIndices.append(haplotype.Female_part.IndexInRNasesPool)
        haplotype.SLFsIndices = []
        for k in range(Model.slf_amount_in_haploid):
            haplotype.SLFsIndices.append(haplotype.Male_part[k].IndexInSLFsPool)
        haplotype.PreviousHaplotype = []
        haplotype.BirthIter = 0
        haplotype.BirthReason = 'Initiation'
        haplotype.RNases_len = len(haplotype.RNasesIndices)
        haplotype.SLFs_unique_len = len(np.unique(haplotype.SLFsIndices))
        haplotype.SLFs_len = len(haplotype.SLFsIndices)
        haplotype.SLFsIndices_unique = np.unique(haplotype.SLFsIndices)
        haplotype.RNasesAncestralIndices = haplotype.RNasesIndices
        haplotype.SLFsAncestralIndices = haplotype.SLFsIndices
        haplotypes_dict[ind] = haplotype

    return haplotypes_dict


def is_compatible(E_Ri_Fj_dict, unique_SLFs_indices, unique_RNases_indices):
    # at least one of the SLFs can detoxify the RNase
    is_compatible = False
    for ii_slfs, slf_ind in enumerate(unique_SLFs_indices):
        if E_Ri_Fj_dict[int(unique_RNases_indices),slf_ind] < Model.interaction_thresh:
            is_compatible = True
            break

    return is_compatible

# -----------------------------------------------------------
#         CALCULATE INTERACTIONS
# -----------------------------------------------------------

def Calc_Interaction_Energy(female_al_pool, male_al_pool, eij, seq_len):
    '''
    this function calculates the interaction intensities between all the pairs of alleles in the pool.
    the Interaction between the two alleles is calculated by summing the interaction correspondences along the pair of alleles (i and j)
    The total interaction energy - equals the sum of interactions between the corresponding amino acids of the two protein surfaces

    input  - the Alleles pool of RNnases and SLFs
    output - E_pool_Ri_Fj an array of Interaction Energies between two alleles

    An Allele vector (RNase (R-allele)/ SLF (F-allele)) looks like this:
    Fj = {Fj1, Fj2, ... , FjL}
    Ri = {Ri1, Ri2, ... , RiL}
    L - is the length of the allele- a sequence of amino acids

    note - this function is called by 'initiate_haplotypes' only.
    Further needs of interaction calculations are calculated and saved as re-updating interactions dict
    '''
    raise NotImplementedError("Function has been masked for testing purposes")


def calc_female_fertilization_weights(diploid_count_dict, haplotypes_count_dict, delta_g_r_dict):
    """Calculate fertilization weights for all the diploids in the population based on the frequencies of the haplotypes that are able to fertilize them."""
    raise NotImplementedError("Function has been masked for testing purposes")

def calc_Rg_r_3D(offspring_dict, diploid_count_dict, delta_g_g_dict, cond1_g_g_dict, cond2_g_g_dict):

    # calculate R
    # given a genotype g and a foreign pollen with haploid genotype h / or a self pollen with haploid genotype g1 or g2
    # calculate the probability to produce an offspring with diploid genotype r

    R_g_g_r_dict = {}
    R_g_h_r_dict = {}

    # self fertilization
    for diploid_ind, diploid_i in enumerate(diploid_count_dict):
        diploid_i_lst = list(diploid_i)
        if delta_g_g_dict[diploid_i] == 0.5: # g is half self compatible
            is_C1 = cond1_g_g_dict[diploid_i] == 1
            is_C2 = cond2_g_g_dict[diploid_i] == 1
            if is_C1:
                offspring = [diploid_i[0], diploid_i[0]]
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[0]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.5
                offspring = diploid_i_lst
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[0]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.5
            if is_C2:
                offspring = [diploid_i[1], diploid_i[1]]
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[1]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.5
                offspring = diploid_i_lst
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[1]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.5
        if delta_g_g_dict[diploid_i] == 1: # g is fully self compatible
            if diploid_i[0] == diploid_i[1]: # g is homozygous,
                offspring = diploid_i_lst #the offspring and the parent are identicle
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[0]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 1
            else: # g is heterozygous
                offspring = diploid_i_lst # g is heterozygous the offspring and the parent are identicle
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[0]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.5
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[1]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.5
                offspring = [diploid_i[0], diploid_i[0]] # g is heterozygous, the offspring is homozygous
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[0]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.25
                offspring = [diploid_i[1], diploid_i[1]]
                g_h_r = tuple([item for sublist in [diploid_i_lst, [diploid_i[1]], offspring] for item in sublist])
                R_g_g_r_dict[g_h_r] = 0.25

    # foreign fertilizations
    for offspring_key, parents_values in offspring_dict.items():
        for parent in parents_values:
            haplotype_ind = parent[1]
            diploid_i = parent[0]

            g_h_r = tuple([item for sublist in [diploid_i, [haplotype_ind], list(offspring_key)] for item in sublist])
            if diploid_i[0] == diploid_i[1]:  # homozygosity
                if diploid_i == offspring_key:  # offspring equals diploid # i.e. g = S0S0, h = S0, r = S0S0
                    if diploid_i[0] == haplotype_ind:
                        R_g_h_r_dict[g_h_r] = 1

                else:  # the offspring r and the genotype g are not the same. # i.e. g = S0S0, h = S1, r = S0S1
                    if tuple([diploid_i[0], haplotype_ind]) == offspring_key or tuple(
                            [haplotype_ind, diploid_i[0]]) == offspring_key:
                        R_g_h_r_dict[g_h_r] = 1

            else:  # heterozygosity
                # the offspring r and the genotype g are not the same. # i.e. g = S0S1, h = S2, r = S0S2
                if tuple([diploid_i[0], haplotype_ind]) == offspring_key \
                        or tuple([haplotype_ind, diploid_i[0]]) == offspring_key \
                        or tuple([diploid_i[1], haplotype_ind]) == offspring_key \
                        or tuple([haplotype_ind, diploid_i[1]]) == offspring_key:
                    R_g_h_r_dict[g_h_r] = 0.5

    return R_g_g_r_dict, R_g_h_r_dict

def calc_pai_genotype_self_and_out(E_Ri_Fj_dict, haplotypes_dict, diploid_count_dict, haplotypes_count_dict,
                       delta_g_g_dict, delta_g_r_dict, cond1_g_g_dict, cond2_g_g_dict):


Complete the function implementation replacing "raise NotImplementedError".