Кроссовер правильной краевой рекомбинации для сборки ДНК

Я пытался реализовать генетический алгоритм для сборки фрагментов ДНК в одну последовательность, учитывая только спектр. Мой единственный оператор — Edge Recombination, и я был уверен, что этого достаточно, чтобы получить довольно хороший результат.

Но... я не могу побить 80% (% оптимальной оценки), а экземпляры с 500 фрагментами могут занять до 2 часов (алгоритм останавливается, если за 100 итераций нет улучшения). Я даже реализую это правильно? Я нигде не нашел, что операторы кроссовера должны выбирать элементы, которые лучше соответствуют (перекрытие фрагментов - в большинстве статей это просто случайный выбор), но я реализовал это так, что он выбирает лучший подходящий элемент, и если их много - случайный. Без выбора лучшего он не набирает даже 40%.

Должен ли я реализовать больше кроссоверов? Или это просто не так работает... Или я что-то пропустил? На данный момент я в отчаянии, жду 24 часа для 40 экземпляров, которые простая эвристика (альпинист) может сделать менее чем за 5 секунд...

Вот код (перестановка - это перетасованный спектр - список строк)

    def crossover(g1, g2, arr, ind):  # edge recombination operator
    neigh_list = {}  # adjacency list
    length = len(g1.permutation)  # expected length of a child
    for i, base in enumerate(g1.permutation):  # create nodes
        neigh_list[base] = {g1.permutation[i - 1], g1.permutation[(i + 1) % length]}
    for i, base in enumerate(g2.permutation):  # add neighbours to each node
        neigh_list[base].add(g2.permutation[i - 1])
        neigh_list[base].add(g2.permutation[(i + 1) % length])

    # a starting point of a child is a starting point of one of the parents
    neigh_chosen = [g1.permutation[0], g2.permutation[0]][random.randint(0, 1)]
    child = [neigh_chosen]

    while len(child) < length:  # run until child has desired length
        min_length = 5  # each list has lower length than 5
        min_neigh_list = []
        for k in neigh_list:  # for every node
            if neigh_chosen in neigh_list[k]:  # remove a chosen fragment from the node
                neigh_list[k].remove(neigh_chosen)
        for k in neigh_list[neigh_chosen]:  # if a node is a neighbour of previously chosen
            if len(neigh_list[k]) < min_length:  # remember nodes with the fewest neighbours
                min_length = len(neigh_list[k])
                min_neigh_list = [k]
            elif len(neigh_list[k]) == min_length:
                min_neigh_list.append(k)
        del neigh_list[neigh_chosen]  # delete list of the chosen node
        if len(min_neigh_list) > 0:  # if the chosen node has any neighbours
            # get the best match out of neighbours as next
            max_overlap = overlap(neigh_chosen, max(min_neigh_list, key=lambda x: overlap(neigh_chosen, x)))
            possibilities = list(filter(lambda x: overlap(neigh_chosen, x) == max_overlap, min_neigh_list))
            neigh_chosen = possibilities[random.randint(0, len(possibilities) - 1)]
        else:
            # get the best match out of every node as next
            max_overlap = overlap(neigh_chosen, max(neigh_list, key=lambda x: overlap(neigh_chosen, x)))
            possibilities = list(filter(lambda x: overlap(neigh_chosen, x) == max_overlap, neigh_list))
            neigh_chosen = possibilities[random.randint(0, len(possibilities) - 1)]
        child.append(neigh_chosen)  # add the node to the solution
    arr[ind] = Gene(child)

person Adrian Dąbek    schedule 05.06.2017    source источник
comment
Я думаю, вам будет сложно получить ответ здесь (или где-либо еще), поскольку это довольно специализированный вопрос. Мне интересно, что вы делаете (я не утверждаю, что могу ответить), но одна вещь, которая могла бы мне помочь, это если бы вы придерживались PEP8, чтобы разбить этот гигантский блок кода. Однако даже в этом случае, я думаю, потребуются некоторые реальные усилия, чтобы пройти через эвристику.   -  person roganjosh    schedule 05.06.2017
comment
Вы можете получить хорошие ответы на новом сайте обмена стеками биоинформатики: area51.stackexchange.com/proposals/109245/bioinformatics   -  person nbryans    schedule 05.06.2017


Ответы (1)


Хорошо, мне удалось решить эту проблему, выполнив довольно простой трюк. Проблема заключалась в том, чтобы понять, что на самом деле делает этот оператор и где его использовать. Чтобы это помогло решить проблему сборки ДНК, просто замените

    for k in neigh_list[neigh_chosen]:  # if a node is a neighbour of previously chosen
        if len(neigh_list[k]) < min_length:  # remember nodes with the fewest neighbours
            min_length = len(neigh_list[k])
            min_neigh_list = [k]
        elif len(neigh_list[k]) == min_length:
            min_neigh_list.append(k)
    del neigh_list[neigh_chosen]

с

    min_neigh_list = neigh_list[neight_chosen]
    del neigh_list[neigh_chosen]

Причина, по которой это работает, заключается в том, что мы ищем не фрагмент с минимальным количеством соседей (это может быть важно в другой задаче, связанной с графом!), а сосед с лучшим совпадением!

Это работает как шарм после этого простого изменения :)

person Adrian Dąbek    schedule 07.06.2017
comment
просто интересно, это все еще занимает 2 часа? - person mitoRibo; 08.06.2017
comment
Прямо сейчас он дает 95+% баллов и занимает от 5 до 240 секунд на экземпляр задачи (от 300 фрагментов до 700 фрагментов). - person Adrian Dąbek; 09.06.2017