Сравнивая большое количество графиков для изоморфизма

Я сравниваю большой набор сетевых диаграмм для изоморфизма, где большинство графиков не должно быть изоморфным (например, 0-20% изоморфны чему-либо в списке, например).

Я пробовал следующий подход.

graphs = [] # A list of networkx graphs
unique = [] # A list of unique graphs

for new in graphs:
    for old in unique:
        if nx.is_isomorphic(new, old[0]):
            break
    else:
        unique.append([new])

Это позволило мне получить гораздо более быстрый сокращенный набор, но я все еще считаю его слишком медленным для идеального использования. Есть ли более быстрый алгоритм для решения этого типа проблемы (сравнение пар транзитивных коммутативных свойств) или способ распространить этот алгоритм на многоядерную настройку (работает на 20-ядерном компьютере).

Я уже фильтрую эти наборы данных на основе количества узлов/ребер, мы можем предположить, что функция nx.is_isomorphic не может быть выполнена быстрее с помощью любых типов фильтрации операций. Я также не могу легко менять инструменты прямо сейчас, поэтому использование скомпилированного пакета не является вариантом.

Дополнительная информация:

Графики, как правило, составляют примерно 16-20 узлов с общим количеством 24-48 ребер, существует много взаимосвязи, поэтому каждый node имеет примерно 8 ребер. Каждое ребро также помечено, но в нем используется только 2-3 типа ребер.

Ответ 1

Как уже упоминалось, если вы хотите остаться в Python + Networkx, вы можете использовать could_be_isomorphic для фильтрации ваших графиков.

Проблема заключается в том, что этот метод ожидает, что 2 графика будут входными, а не миллионными. Если вы сравниваете каждую пару графиков с этим методом, это займет очень много времени.

Рассматривая исходный код could_be_isomorphic, он сравнивает степень, треугольник и количество последовательностей клик для обоих графиков. Если они не равны, графики не могут быть изоморфными.

Вы можете упаковать этот отпечаток в функцию, сортировать свои графики в соответствии с этим отпечатком и группировать их с помощью itertools.groupby. Будет огромное большинство одиноких графиков. Несколько графиков, имеющих одни и те же отпечатки пальцев, можно проверить на изоморфизм.

Использование списка из 100 000 случайных графов:

many_graphs = [nx.fast_gnp_random_graph(random.randint(16, 22), 0.2) for _ in range(100000)]

Было всего 500 отпечатков пальцев, которые были разделены по меньшей мере на 2 графа. Если вы добавите информацию о типах краев, будет еще меньше общих отпечатков пальцев.

Здесь приведен пример с 3000 графиками, каждый из которых имеет от 10 до 14 узлов:

import networkx as nx
from itertools import groupby
import random

many_graphs = [nx.fast_gnp_random_graph(
    random.randint(10, 14), 0.3) for _ in range(3000)]


def graph_fingerprint(g):
    order = g.order()
    d = g.degree()
    t = nx.triangles(g)
    c = nx.number_of_cliques(g) 
    props = [[d[v], t[v], c[v]] for v in d]
    props.sort()
    # TODO: Add count of edge types.
    return(props)


sorted_graphs = sorted(many_graphs, key=graph_fingerprint)

for f, g in groupby(sorted_graphs, key=graph_fingerprint):
    similar_graphs = list(g)
    n = len(similar_graphs)
    if n > 1:
        print("Found %d graphs which could be isomorphic." % n)
        for i in range(n):
            for j in range(i + 1, n):
                g1, g2 = similar_graphs[i], similar_graphs[j]
                if g1 != g2 and nx.is_isomorphic(g1, g2):
                    print(" %s and %s are isomorphic!" %
                          (nx.generate_graph6(g1,header=False), nx.generate_graph6(g2,header=False)))

Он находит 4 изоморфных пары менее чем за 1s:

Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
 Id?OGG_C? and [email protected][email protected]? are isomorphic!
Found 6 graphs which could be isomorphic.
 I?OWcGG?G and [email protected]_ are isomorphic!
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
 I_uI???JG and II??QDNA? are isomorphic!
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
 [email protected] and [email protected]`dS? are isomorphic!
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 3 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.
Found 2 graphs which could be isomorphic.

Вот 2 последних изоморфных графа. "IDOCCY @GG":

введите описание изображения здесь

и "IOGC @\dS?":

введите описание изображения здесь

Вот 2 графика, которые имеют один и тот же отпечаток, но не изоморфны:

введите описание изображения здесь введите описание изображения здесь

Отпечаток пальца может выполняться параллельно. Сортировка и группировка должны выполняться на 1 ЦП, но проверка изоморфизма для каждой группы может выполняться в разных ЦП.

Ответ 2

Можете ли вы использовать nauty (http://users.cecs.anu.edu.au/~bdm/nauty/, доступный в дистрибутивах Linux)? У этого есть алгоритм канонической метки, который быстр и может работать для вашей проблемы. Каноническая маркировка делает изоморфные графики одинаковыми (канонизация). Например, используя формат graph6, выводимый из набора случайных графов, дает следующий счет изоморфных графов

$ cat g6.py
import networkx as nx
for i in range(100000):
    print(nx.generate_graph6(nx.fast_gnp_random_graph(4,0.2),header=False))


$ python g6.py  |nauty-labelg  |sort |uniq -c 
>A labelg
>Z 100000 graphs labelled from stdin to stdout in 0.21 sec.
   4898 C`
    167 C^
     10 C~
  26408 C?
  39392 [email protected]
  19684 CB
   1575 CF
   1608 CJ
   1170 CN
    288 Cr
   4800 CR

Это 11 графиков из 4 узлов -

$ cat atlas.py 
import networkx as nx
for g in  nx.atlas.graph_atlas_g()[8:19]:
     print(nx.generate_graph6(g,header=False))
$ python atlas.py  |nauty-labelg  |sort |uniq -c 
>A labelg
>Z 11 graphs labelled from stdin to stdout in 0.00 sec.
      1 C`
      1 C^
      1 C~
      1 C?
      1 [email protected]
      1 CB
      1 CF
      1 CJ
      1 CN
      1 Cr
      1 CR

Было бы довольно легко распараллеливать этот подход, если он работает слишком медленно.

Ответ 3

Вы можете попробовать свой код на PyPy, который обеспечивает компиляцию точно в срок для чистого кода Python. Для возможного повышения производительности они говорят это...

... сильно зависит от типа выполняемой задачи. Среднее геометрическое значение всех тестов составляет 0,13 или 7,5 раза быстрее, чем CPython

Если ваша рабочая нагрузка связана с процессором (что похоже на это), а процесс Python длительный (так что компиляция JIT может быть выполнена), то повышение производительности может быть значительным. NetworkX - чистый Python (он имеет необязательные зависимости, такие как numpy, но они необходимы для дополнительной функциональности) и, в частности, isomorph. Я пробовал пропустить PyPy 5.7.1 и networkx/algorithms/isomorphism/tests/test_isomorphism.py. В целом набор имеет несколько сбоев:

Ran 2952 tests in 51.311s

FAILED (failures=3, skipped=54)
Test failed: <unittest.runner.TextTestResult run=2952 errors=0 failures=3>

В Python 2.7.12 это:

Ran 2952 tests in 88.915s

OK (skipped=54)