A Graph a Day

Vor einiger Zeit habe ich @randomGraphs geschrieben: Ein Twitterbot, der einen Zufallsgraphen pro Tag tweetet.

Die meisten Graphtypen, die er darstellen kann stammen aus der NetworkX Bibliothek oder sind reale Netzwerke. Ein paar Proximity Graphs habe ich selbst geschrieben. Die Darstellung und gegebenenfalls das Layout übernimmt Cytoscape oder graph-tool (dessen Autor diesem Bot folgt).

Bei diesem Projekt habe ich exzessiv Gebrauch von Pythons Decorator und Introspection gemacht, sodass man, um einen neuen Graphtyp einzuführen nur eine Methode schreiben muss, die eine Graph-Datenstruktur zurück gibt. Einstellungen, welche Darstellungen erlaubt sind, werden per decorator getätigt und alle Methoden werden per Introspection automatisch zum Pool hinzugefügt, aus dem der Zufallsgenerator zieht.

Eine typische Methode sieht etwa so aus.

@synonym("Barabasi Albert")
@synonym("preferential attachment")
@style(styles_all)
@layout(["kamada-kawai", "force-directed", "sfdp", "fruchterman_reingold", "arf", "radial_tree"])
def generateBarabasiAlbert(self, N=None, m=None, **kwargs):
    if N is None: N = random.randint(4, 400)
    if m is None: m = random.randint(1, 5)

    G = gen.barabasi_albert_graph(N, m)  # gen is networkx Generator
    details = dict(name="Barabási-Albert Graph", N=N, m=m, seed=self.seed,
                   template="{name}, N = {N}, m = {m}")

    return G, details

Und liefert für $N=226, m=1$ und das radial_tree Layout beispielsweise diesen Graph. Die Größe der Knoten wird hier von der Betweenness Centrality bestimmt.

Graph

Die @synonym Decorators ermöglichen die zweite Funktion des Bots, denn er tweetet nicht nur einmal am Tag einen zufälligen Graphen, sondern reagiert auch auf Mentions. Falls in der Mention der Name der Methode oder eines der per @synonym registrierten Worte auftaucht, antwortet er mit einem Bild des entsprechenden Graphen. Dank fuzzywuzzy ist es sogar resistent gegen Tippfehler.

Twitter unterstützt leider keine Vektorgrafiken und wandelt Bilder gerne in stark komprimierte .jpg, was gerade bei diesen Graphen zu störenden Artefakten führt. Dagegen hilft es, wenn ich einen Rand aus transparenten Pixeln dem Bild hinzufüge. Das führt dazu, dass Twitter .jpg nicht als geeignetes Format ansieht und die Bilder im verlustfreien .png ausliefert.

convert -alpha on -channel RGBA -bordercolor "rgba(0,0,0,0)" -border "1x1" input.png output.png

Graph

Der komplette Quellcode ist auf Github.

Labyrinthartiger Zellulärer Automat

Der wohl berühmteste zelluläre Automat ist vermutlich Conway’s Game of Life. Er und nahe Verwandte sind geradezu lächerlich gut untersucht. Das LifeWiki gibt einen ganz guten Überblick. Die Regeln sind einfach: Jede Zelle hat 8 Nachbarn, wenn genau 3 Nachbarn leben, erwacht sie auch zum Leben, bei weniger als 2 oder mehr als 3 stirbt sie (23/3). Wenn man die Regeln des Automaten ändert, kann man mit 12345/3 labyrinthartige Strukturen erzeugen.

Der Code ist als Gist auf GitHub verfügbar.

TSPview

Das Problem des Handlungsreisenden ist es, die kürzeste Rundtour zu planen, sodass man alle Städte besucht. Es ist eines der berühmtesten Optimierungsprobleme und gehört zur Klasse NP-hard.

Es gibt also (bis jetzt) keine effiziente Möglichkeit zur Lösung. Allerdings gibt es Näherungen, untere Schranken und unzählige Heuristiken.

Die einfachsten dieser Heuristiken habe ich in einem kleinen Programm TSPview implementiert, mitsamt Visualisierung. Der Quellcode ist auf GitHub zu finden.

Algorithmen

Hier folgt eine kurze Beschreibung der verwendeten Algorithmen und jeweils ein Bild, welche Lösung die Methode auf einer berühmten Instanz des TSP findet.

42 Hauptstädte in Amerika Das sind 42 Hauptstädte der Vereinigten Staaten von Amerika und Washington, DC (Hawaii und Alaska, sowie einige Staaten an der Ostküste, die das Problem nicht schwieriger machen, fehlen). Dieses Problem war das erste größere, das 1956 beweisbar optimal gelöst wurde.

Nearest Neighbor

Nearest Neighbor Die Nearest Neighbor Heuristik ($\mathcal{O}(N^2)$) startet bei einer zufälligen Stadt und wählt als nächste Stadt immer die Stadt, die am nächsten an der aktuellen Stadt ist und noch nicht besucht wurde.

Greedy

Greedy Diese Heuristik ($\mathcal{O}(N^2 \log N)$) ist ähnlich zu Kruskals Algorithmus für minimal spannende Bäume. Sie nimmt die kürzeste Verbindung zwischen zwei Städten und fügt sie der Tour hinzu, wenn sie in der Tour noch erlaubt ist.

Farthest Insertion

Farthest Insertion Farthest Insertion ($\mathcal{O}(N^3)$) startet bei einer zufälligen Stadt und fügt dann die Stadt, die am weitesten von der aktuellen Tour entfernt ist an der Stelle in die Tour, die dafür sorgt, dass die Tour möglichst kurz bleibt.

Two-Opt

Two-Opt Two-Opt beginnt mit einer beliebigen Tour, die bspw. durch eine der obigen Heuristken erstellt wurde und verbessert sie, indem sie zwei Verbindungen nimmt und die Endpunkte über Kreuz austauscht, wenn die Tour dadurch verbunden bleibt und kürzer wird.

Lineare Programmierung mit „Subtour Elimination Cuts“

Linear Programming Lineare Programmierung (LP) zu erklären, würde diesen Artikel sprengen. Aber diese Methode liefert untere Schranken für die Tourlänge und kann somit benutzt werden, um die Qualität einer heuristischen Lösung abzuschätzen. Falls man die optimale Lösung durch lineare Programmierung findet, erkennt man sie auch sofort als optimal.

Für weitere Details, kann ich auf einen arXiv Artikel von mir verweisen.

Concorde

Optimale Tour Concorde ist der „State of the Art“ Solver für das Problem des Handlungsreisenden und löst problemlos Instanzen mit mehr als 1000 Städten. Intern benutzt es zwar eine Menge Heuristiken, allerdings auch lineare Programmierung, um nachzuweisen, dass die gefundene Lösung optimal ist.

Technische Details

TSPview ist ein Python3 Programm, das zur Darstellung PyQt5 benutzt, das sich per pip3 install PyQt5 einfach installieren lässt.

Darüber hinaus enthält es eine optionale Abhängigkeit zu CPLEX, einem kommerziellen LP solver.

boost::python

Da das Hauptprogramm in Python geschrieben ist, aber der LP-Teil in C++, braucht man eine Möglichkeit der Kommunikation. Glücklicherweise gibt es mit boost::python eine Möglichkeit C++ Klassen in Python als Python-Klassen zu benutzen.

Um beispielsweise die C++ Klasse MyClass, deren Konstruktor einen Integer und eine Python-Liste entgegen nehmen soll, in Python benutzen und myMethod aufrufen zu können, reicht folgender Code:

#include <boost/python.hpp>
namespace py = boost::python;

// implement MyClass

BOOST_PYTHON_MODULE(MyClass)
{
    py::class_<MyClass>("MyClass", py::init<int, py::list>())
        .def("myMethod", &MyClass::myMethod)
    ;
}