Schnelle Algorithmen zur Multipatternsuche in der Metagenomik

Werbung
Leibniz Universität Hannover
Schnelle Algorithmen zur Multipatternsuche in
der Metagenomik
Diplomarbeit
vorgelegt von
Jens Neugebauer
am
Institut für Informationssysteme
Fachgebiet Programmiersprachen und Übersetzer
Prof. Dr. R. Parchmann
in Zusammenarbeit mit der
Medizinischen Hochschule Hannover
Klinische Forschergruppe OE6711
Prof. Dr. B. Tümmler, C. Davenport
Gesetzt mit
LATEX 2
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Inhaltsverzeichnis
1. Einführung
5
2. Wichtige Begriffe und Definitionen
8
3. Anforderungen
3.1 Problemgrößen . . . . . . . . . . . . . . . . .
3.2 Ergebnis . . . . . . . . . . . . . . . . . . . . .
3.3 Programmiersprache . . . . . . . . . . . . . .
3.3.1 Messungen zum Speicherbedarf . . . .
3.3.2 Messungen zur Laufzeit (Datei lesen) .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
11
12
12
13
16
4. Algorithmen
4.1 Brute Force / Single Pattern Matching . . . . . . . . . . . .
4.2 Multipattern Karp-Rabin . . . . . . . . . . . . . . . . . . .
4.3 Aho-Corasick . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3.1 Idee . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3.2 Definition . . . . . . . . . . . . . . . . . . . . . . . .
4.3.3 Vorverarbeitung . . . . . . . . . . . . . . . . . . . .
4.3.4 Suchphase . . . . . . . . . . . . . . . . . . . . . . . .
4.3.5 Vermeidung unnötiger Übergänge der Fehlerfunktion
4.3.6 Verbesserung für kleine Alphabete . . . . . . . . . .
4.4 Wu-Manber . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.1 Beschreibung . . . . . . . . . . . . . . . . . . . . . .
4.4.2 Vorverarbeitung . . . . . . . . . . . . . . . . . . . .
4.4.3 Suchphase . . . . . . . . . . . . . . . . . . . . . . . .
4.4.4 Wahl der Blockgröße B . . . . . . . . . . . . . . . .
4.4.5 Verbesserungen . . . . . . . . . . . . . . . . . . . . .
4.5 q-Gramme . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.5.1 Boyer-Moore-Horspool mit q-Grammen . . . . . . .
4.6 Multiple Approximate String Matching with l-Grams . . . .
4.6.1 Vorverarbeitung . . . . . . . . . . . . . . . . . . . .
4.6.2 Suchphase . . . . . . . . . . . . . . . . . . . . . . . .
4.6.3 Anmerkung . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
17
18
20
20
20
21
24
26
27
28
28
32
33
33
34
35
36
39
39
40
42
.
.
.
.
43
45
48
49
51
5. Implementierung
5.1 Messungen: Vergleich der Laufzeiten
5.2 Details . . . . . . . . . . . . . . . . .
5.3 Messungen zur Implementierung . .
5.4 Ergebnisse . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6. Weitere Einsatzgebiete
56
7. Fazit
59
8. Literatur
63
Index
65
3
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
1. Einführung
Diese Diplomarbeit befasst sich mit Algorithmen zur Multipatternsuche in der Metagenomik. Die Metagenomik ist ein Forschungsgebiet der Biologie/Bio-Informatik, deren
Grundfragestellung es ist, welche Mikroorganismen in einem bestimmten Lebensraum
(Biotop) vorkommen und in welcher Anzahl sie dort auftreten.
Eine wichtige Methode der Metagenomik ist dabei die DNA-Sequenzierung, also das Ablesen der Nukleotidfolge der DNA. Die erste Methode zur DNA-Sequenzierung wurde
1975 von Frederick Sanger entwickelt. Nach diesem Verfahren wird die DNA zuerst auf
biochemische Art in kleine Abschnitte zerlegt, von denen dann die Nukleotidfolge bestimmt werden kann. In jeder einzelnen Sequenzierreaktion werden auf Grund technischer
Beschränkungen nur kurze DNA-Abschnitte, sogenannte Reads, von weniger als 1000
Basenpaaren abgelesen. Die Sanger Sequenzierung war bis vor 2 Jahren die dominante
Sequenzierungsmethode.
Die aus der Sequenzierung erhaltenen Reads werden anschließend auf verschiedene Arten
ausgewertet:
Bei klassischen Sequenzierungsprojekten wird versucht die Reads wie ein Puzzle zusammenzusetzen um so die Gesamtsequenz wieder rekonstruieren zu können. Ein bekanntes
Beispiel dafür ist das Human-Genom-Projekt [Wat90].
Abbildung 1: Rekonstruktion einer Gesamtsequenz
Dabei ist es notwendig, dass die DNA nur von einem Organismus kommt.
Außerdem sind bei solchen Projekten möglichst lange Reads sehr wichtig, denn nur so
kann beim Rekonstruieren der Gesamtsequenz eine ausreichende Überlappung der Reads
und damit ein hinreichend verlässliches Ergebnis erreicht werden.
Daher kommen bei klassischen Sequenzierungsprojekten meistens die älteren SangerTechnologien zum Einsatz, welche zwar weniger, dafür aber die benötigten längeren Reads
liefern.
Mit dieser Methode sind bis heute etwa 700 bakterielle Genome und mehr als 20 größere
eukaryotische Genome komplett sequenziert worden.
(Quelle: GOLD - Genomes OnLine Database,
http://www.genomesonline.org/. Stand: Januar 2008.)
5
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Wir werden uns aber mit einer neueren Art der Anwendung von Sequenzierung befassen.
Die Sequenzierung ist durch neue Verfahren in den letzten Jahren um den Faktor 100
billiger geworden. Eine Prognose1 von Hans-Peter Klenk vom DSMZ (Deutsche Sammlung von Mikroorganismen und Zellkulturen GmbH, Braunschweig) sagt voraus, dass die
Kosten für Sequenzierungen bis 2013 um 50% pro Jahr sinken werden.
Abbildung 2: Sequenzierungskosten bis 2013, Prognose der Deutschen Sammlung von
Mikroorganismen und Zellkulturen GmbH. Aus [Kle08].
Moderne Sequenzierverfahren arbeiten miniaturisiert und parallel, benötigen immer weniger Probenmaterial und liefern dabei immer mehr Reads pro Sequenzierung. So können
beispielsweise mit dem Genome Analyzer des US-Genomicsanbieters Illumina/Solexa [Ill]
in einem Durchlauf 40 Millionen Reads mit Leseweiten von bis zu 35 Nukleotiden, also
1Gbp an Sequenzinformation bestimmt werden.
Diese große Menge an Reads sowie die geringen Kosten moderner Verfahren machen es
möglich, Sequenzierung in bisher nicht möglichen Anwendungsbereichen einzusetzen.
So bringt beispielsweise das Sequenzieren metagenomischer Proben, also Proben die viele
verschiedene Arten von Bakterien enthalten, erst dann verwertbare und verlässliche Ergebnisse, wenn man sehr viele Reads hat. Dieses ist vor allem von Vorteil, wenn man sich
mit Bakterien beschäftigt, die im Labor nicht kultivierbar sind. Solche Bakterien müssen
in einer Probe aus der Umwelt direkt untersucht werden, welche naturgemäß verschiedenste Bakterien enthält.
Ein weiterer Vorteil moderner Sequenzierungsverfahren ist, dass sie weniger Probenmaterial benötigen.
Allerdings haben die modernen Sequenzierungsverfahren nicht nur Vorteile. Der größte
Nachteil besteht darin, dass die gelieferten Reads immer kürzer werden.
Diese kurzen Reads sind schwerer einzelnen Bakterien zuzuordnen, da die bestehenden Algorithmen zum Großteil auf die langen Reads der älteren Sanger-Sequenzierungstechniken
ausgelegt sind. So benötigt zum Beispiel der am häufigsten eingesetzte heuristische Algorithmus BLAST“ 2 mindestens Reads einer Länge von 100 Basenpaaren um eine verlässli”
che Zuordnungen treffen zu können.
1
2
6
siehe [Kle08]
Basic Local Alignment Search Tool
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Abbildung 3: Eigenschaften verschiedener DNA-Sequenzierungstechnologien aus [SBR07]
Wir wollen uns daher mit einer neuen Methode der Zuordnung befassen, dem sogenannten Short Oligo Alignment“. Dafür wurden die bekannten Bakteriengenome auf über”
repräsentierte DNA-Sequenzen einer Länge von 8 bis 14 Basenpaaren untersucht. Ein
Auswahlkriterium hierfür ist ein häufiges und regelmäßiges Auftreten im Bakteriengenom, beispielsweise alle 10.000 Basenpaare mindestens einmal (siehe [DWRTed] sowie
[ea]). Diese kurzen überrepräsentierten DNA-Sequenzen nennen wir im Folgenden Marker.
Wir werden daher verschiedene Algorithmen untersuchen, welche möglichst schnell zu allen Reads bestimmen, wie viele Marker eines Bakteriums in ihm auftreten. Das Ziel dabei
ist, Reads in denen mehrere Marker eines Bakteriums vorkommen, direkt dem Genom
dieses Bakteriums zuordnen zu können.
7
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
2. Wichtige Begriffe und Definitionen
Definition
Ein Nukleotid ist ein Molekül, das als Grundbaustein von Nukleinsäuren (DNA und
RNA) fungiert. Es ist aus mehreren Bestandteilen aufgebaut, unter anderem aus einer
der fünf Nukleobasen, nämlich Adenin (A), Cytosin (C), Guanin (G), Thymin (T)
oder Uracil (U). In der DNA werden nur vier dieser Basen (A, C, G, T) verwendet, in
der RNA ist die Nukleobase Thymin (T) gegen Uracil (U) ausgetauscht. Die Nukleotide unterscheiden sich also durch die Base, die jeweils eingebaut ist. Da A, C, G und
T in der DNA vorkommen, werden diese Moleküle auch als DNA-Basen bezeichnet.
Definition
Als Taxon bezeichnet man in der Biologie eine als systematische Einheit erkannte
Gruppe von Lebewesen.
Eine solche Gruppe wäre zum Beispiel ein bestimmter Bakterien-Stamm (bsp.: Pseudomonas aeruginosa PAO1, Pseudomonas putida KT2440, Pseudomonas aeruginosa
PA7), eine Bakterien-Spezies (bsp.: Pseudomonas aeruginosa, Pseudomonas putida),
oder eine ganze Bakterien-Gattung (bsp.: Pseudomonas).
Definition
Ein Read ist ein durch ein Sequenzierverfahren bestimmter Ausschnitt aus dem Genom eines Bakteriums. Da in der Metagenomik die sequenzierten Proben immer aus
einer Gesellschaft verschiedenster Bakterien bestehen, ist eine der grundlegenden Aufgabenstellungen zu bestimmen, zu welchem Taxon ein Read zugeordnet werden kann.
Definition
Als Marker für ein bestimmtes Taxon bezeichnet man eine kurze DNA-Sequenz, welche charkteristisch für dieses Taxon ist.
Definition
Ein Hit ist ein Read, welcher einem Taxon zugeordnet werden konnte.
Definition
Als Basenpaar bezeichnet man zwei Basen der Nukleotide in der DNA oder RNA, die
zueinander komplementär sind und durch Wasserstoffbrückenbindungen zusammengehalten werden. Die Anzahl der Basenpaare wird üblicherweise in bp (Basenpaaren)
oder kbp (Kilo-Basenpaaren) gemessen. 1kbp sind dabei 1000 Basenpaare.
Es gibt vier mögliche Basenpaare: Adenin-Thymin, Thymin-Adenin, Guanin-Cytosin
und Cytosin-Guanin. Ein bp kann also vier verschiedene Werte darstellen und entspricht folglich einer Informationsmenge von 2 Bit.
8
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Definition
In der DNA-Sequenzierung ist ein contig (englisch von contiguous) eine DNA Sequenz,
welche aus einer Menge von sich überlappenden Reads zusammengesetzt wurde. Die
Reads dürfen dabei nur aus einer genetischen Quelle, also von einem Genom, stammen
[Sta79].
Definition
Ein deterministischer endlicher Automat ( deterministic finite automaton) wird
durch ein Tupel (Q, Σ, δ, q0 , F ) beschrieben. Dabei gelten folgende Bedingungen:
•
•
•
•
•
•
Q ist eine endliche Menge von Zuständen
Σ ist eine endliche Menge von Eingabesymbolen.
Q∩Σ=∅
q0 ∈ Q ist der Startzustand
F ⊆ Q ist die Menge der Endzustände
δ : Q × Σ → Q heißt Zustandsübergangsfunktion.
Ein deterministischer endlicher Automat kann durch einen Zustandsgraphen veranschaulicht werden. Dabei werden Zustände als Knoten und Übergänge als Kanten
dargestellt. Eine mit a ∈ Σ markierte Kante vom Knoten u zum Knoten v im Zustandsgraphen des Automaten stellt also δ(u, a) = v dar.
Definition
Ein Baum ist ein gerichteter Graph B = (V, E) mit folgenden Eigenschaften:
• Es gibt genau einen Knoten w ∈ V mit (v, w) ∈
/ E für alle v ∈ V . w heißt Wurzel
von B.
• Für alle v ∈ V , v 6= w existiert genau ein u 6= v mit (u, v) ∈ E.
• Für alle v ∈ V existiert ein Weg von w nach v. Die Länge dieses eindeutigen
Weges heißt Tiefe von v.
Definition
Ein Trie über einem Alphabet Σ ist ein Baum (V, E) mit einer zusätzlichen Markierungsfunktion µ : E → Σ, welche jeder Kante e ∈ E ein Symbol aus Σ zuweist. Es gilt
weiterhin:
(*) Sind (u, v1) ∈ E und (u, v2) ∈ E mit v1 6= v2, dann ist µ(u, v1) 6= µ(u, v2).
Bemerkung
Aus (*) folgt direkt, dass der maximale Verzweigungsgrad eines Knotens in Tries |Σ|
ist.
Bemerkung
Jeder Knoten v im Trie repräsentiert ein Wort ρ(v), welches sich aus der Konkatenation
der Kantenmarkierungen entlang des Weges von der Wurzel zu v ergibt.
Für u ∈ V , v ∈ V mit u 6= v gilt: ρ(u) 6= ρ(v).
9
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Definition
Ein binärer Suchbaum B ist ein Binärbaum für den zusätzlich gilt:
• Jeder Knoten in B enthält einen Schlüssel, für den eine Ordnung definiert ist.
Die Elemente eines binären Suchbaumes müssen also vergleichbar sein.
• Existiert zu einem Knoten b ∈ B ein linker Unterbaum Bl , so ist dieser ebenfalls
ein binärer Suchbaum. Außerdem müssen alle in Bl enthaltenen Schlüssel kleiner
sein als der Schlüssel von b.
• Existiert zu einem Knoten b ∈ B ein rechter Unterbaum Br , so ist dieser ebenfalls
ein binärer Suchbaum. Außerdem müssen alle in Br enthaltenen Schlüssel größer
sein als der Schlüssel von b.
Bemerkung
In binären Suchbäumen benötigt man maximal O(h) Schritte um ein Element zu
finden, wobei h die Höhe des binären Baumes ist.
Im Bestcase ist der binäre Suchbaum ausgeglichen. Dann benötigt man O(log(m))
Schritte um ein Element im Baum zu finden, wobei m die Anzahl der Elemente im
Baum ist. Im Worstcase hingegen entartet der binäre Suchbaum zu einer linearen Liste
und man benötigt O(m) Schritte.
Definition
Ein AVL-Baum ist ein binärer Suchbaum mit der zusätzlichen Eigenschaft:
• Für jeden Knoten im AVL-Baum gilt, dass sich die Höhe des linken Unterbaumes
höchstens um 1 von der Höhe des rechten Unterbaumes unterscheidet.
Da diese Bedingung verhindert, dass der Baum aus der Balance gerät, nennt man
ihn auch ausgeglichen“. Das Suchen in einem AVL-Baum benötigt immer O(log(m))
”
Schritte.
10
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
3. Anforderungen
3.1 Problemgrößen
Gegeben sind etwa 700 verschiedene Bakterien, deren Genome komplett assembliert und
frei zugänglich sind. Die Zahl der sequenzierten Bakteriengenome wächst sehr stark an,
bis 2013 wird mit etwa 105 komplett sequenzierten Genomen gerechnet.
Abbildung 4: Sequenzierte Genome bis 2013, eine Prognose der Deutschen Sammlung
von Mikroorganismen und Zellkulturen GmbH. Aus [Kle08].
Zu jedem dieser Bakterien sind bis zu 1.000 verschiedene Marker gegeben. Ein Marker ist
dabei ein Stück DNA Sequenz der Länge 8 bis 14 Basenpaare, welches im zugehörigen
Bakteriengenom überrepräsentiert3 ist. Die Marker sind eindeutig, jeweils 2 Bakterien haben also keine gemeinsamen Marker.
Des Weiteren ist eine sogenannte metagenomische Probe aus einer Gesellschaft verschiedenster Bakterien gegeben. Eine solche Probe ist eine mehrere 100MB bis einige GB große
Datei im FASTA-Format4 . Diese Datei besteht jeweils aus Reads mit einer Länge von etwa
30 - 800 Basenpaaren.
Abbildung 5: Voraussetzungen
3
4
siehe [DWRTed] sowie [ea]
siehe [Fas]
11
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
3.2 Ergebnis
Das Programm soll als Ergebnis eine Statistik zu den Reads erstellen. In dieser soll zu
jedem Read aufgelistet werden, wie viele zu einen bestimmten Bakterium gehörenden Marker darin enthalten sind. Es interessieren nur Marker die innerhalb eines Reads gefunden
werden. Wird ein Marker gefunden, dessen Anfang in einem Read und dessen Ende im
folgenden Read liegt, so gilt dies nicht als Auftreten des entsprechenden Markers.
Reads in denen keiner der Marker gefunden wurden, sollen verworfen werden.
Abbildung 6: Ergebnis. In grün bzw. blau sind die Stellen markiert, an denen ein Marker
gefunden wurde
3.3 Programmiersprache
An den Anforderungen erkennen wir schon 2 entscheidende Größen, welche bei der Wahl
der Programmiersprache wichtig sind:
Zum einen gibt es sehr viele Marker. Für eine schnelle Suche sollten diese nach Möglichkeit im Arbeitsspeicher liegen. Wenn wir von 105 verschiedenen Bakterien ausgehen und
als Mittelwert pro Bakterium 500 Marker der Länge 10 nehmen, so kommen wir auf
500.000.000 zu speichernde Basenpaare. Als int mit 4 Byte je Basenpaar abgespeichert,
wären das 2.000.000.000 Byte, also rund 2 GB.
Von der Größenordnung passen also alle Marker in den Arbeitsspeicher, sofern die Programmiersprache nicht zuviel Overhead für eine entsprechende Datenstruktur benötigt.
Die zweite wichtige Größe ist die Zeit, welche die Programmiersprache benötigt um eine
sehr große Datei einzulesen. Die Probendatei kann bis zu mehreren GB groß sein und
muss voraussichtlich Zeichen für Zeichen eingelesen werden. Neben der Zeit, welche der
spätere Algorithmus benötigen wird, um alle Auftreten der Marker zu finden, muss also
auch die reine Lesezeit der Probendatei beachtet werden.
12
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
r
r
Alle Messungen wurden auf einem Laptop mit einem Intel
Pentium
M (1,8 Ghz) und
1048 MByte RAM unter Windows XP Professional mit Service Pack 2 durchgeführt.
Verwendet wurde JavaTM Standard Edition 6 (build 1.6.0 05-b13). Als Entwicklungsumr
gebung für C++ wurde Microsoft Visual C++
6.0 benutzt.
3.3.1 Messungen zum Speicherbedarf
Um die passende Programmiersprache und die passende Datenstruktur zu bestimmen,
wurden die folgenden Messungen zum Speicherbedarf für 4 verschiedene Implementierungen gemacht:
• Sprache: Java, Datenstruktur: eigene Klasse
5
• Sprache: Java, Datenstruktur: Vector
• Sprache: Java, Datenstruktur: Collection
• Sprache: Java, Datenstruktur: Array
Bei den Java Implementierungen wurde jeweils der verfügbare Speicher der JVM festgesetzt und dann wurden in einer Endlosschleife Instanzen erzeugt, bis es zu einer OutOfMemoryError-Exception kommt.
In Abbildung 7 sind die Messergebnisse in einem Diagramm dargestellt. Die genauen
Messdaten zu dem Diagramm sind in Tabelle 1 aufgeführt.
5
public class Knoten {
Knoten Nachfolger;
int Wert;
public void addNachfolger(Knoten n) {
Nachfolger=n;
}
public void setWert(int i){
Wert=i;
}
public Knoten getNachfolger(){
return Nachfolger;
}
public int getWert(){
return Wert;
}
}
13
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Abbildung 7: Speichermessung für Java Implementierungen mit verschiedenen
Datenstrukturen.
verfügbarer
Speicher
16MB
24MB
32MB
40MB
48MB
56MB
64MB
72MB
80MB
88MB
96MB
Array
3.860.387
5.790.580
7.730.774
9.660.967
11.591.160
13.541.355
15.471.548
17.411.742
19.341.935
21.282.129
23.212.322
erzeugte Instanzen
Vector
Collection eigene Klasse
655.361
670.206
840.085
1.230.376 1.005.309
1.250.126
1.310.721 1.507.964
1.670.168
1.943.080 1.507.964
2.080.209
2.463.272 2.261.946
2.500.251
2.621.441 2.261.946
2.920.293
2.621.441 3.310.735
3.330.334
2.621.441 3.392.919
3.750.376
3.888.680 3.392.919
4.160.417
4.408.872 3.392.919
4.580.459
4.929.013 4.967.388
5.000.501
Tabelle 1: Speichermessung für Java Implementierungen mit verschiedenen
Datenstrukturen.
Man sieht, dass das Array in etwa dem theoretisch berechneten Wert von 4Byte pro Array Element entspricht. Alle anderen Datenstrukturen in Java weisen jedoch einen recht
deutlichen Overhead auf.
Zum Vergleich wurden die Messungen noch mal für eine Implentation in C++ mit der
Datenstruktur Vector gemacht. Dazu wurde jeweils eine bestimmte Anzahl von int-Zahlen
in einen Vector eingefügt. Anschließend wurde gemessen, wie viel Speicher das Programm
benötigt.
14
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Instanzen
3.860.387
5.790.580
7.730.774
9.660.967
11.591.160
13.541.355
15.471.548
17.411.742
19.341.935
21.282.129
23.212.322
benötigter Speicher
Java Array C++ Vector
16.384KB
17.248KB
24.576KB
33.672KB
32.768KB
33.672KB
40.960KB
66.520KB
49.152KB
66.520KB
57.344KB
66.520KB
65.536KB
66.520KB
73.728KB
132.216KB
81.920KB
132.216KB
90.112KB
132.216KB
98.304KB
132.216KB
Tabelle 2: Speichermessung für eine C++ Implementierungen unter Verwendung eines
Vectors, im Vergleich mit der Java Implementierung unter Verwendung eines Arrays.
An den Ergebnissen kann man erkennen, dass der Vector in C++ kaum Overhead besitzt. Bei 3.860.387, 7.730.774 und 15.471.548 erzeugten Einträgen benötigt er etwa so
viel Speicher wie ein entsprechend gefülltes Array in Java und erreicht damit nahezu den
theoretisch optimalen Wert von 4Byte pro Eintrag.
An den übrigen Werten können wir ein bestimmtes Verhalten des Vectors erkennen. Der
Vector reserviert Speicher für eine bestimmte Anzahl Einträge. Sobald diese Anzahl an
Einträgen in dem Vector abgespeichert wurden, verdoppelt der Vector den reservierten
Platz. Dadurch kommt dieses sprunghafte Verhalten im Speicherbedarf zustande. Die Collection sowie der Vector in Java arbeiten auf dieselbe Art. Auch sie verdoppeln sich, wenn
der reservierte Platz nicht mehr ausreicht.
Allerdings wird jetzt auch eine Schwäche bei den Messungen in Java deutlich:
Das Array wurde jedes Mal neu definiert, also nur um 1 Element vergrößert. Dadurch hat
es natürlich den Speicher bis an die gegebene Grenze ausnutzen können.
Der Vector hingegen verdoppelt seine Größe jedes Mal, wenn er keinen Platz mehr zum
Speichern weiterer Elemente hat. Es kommt also schon zu einem Speicherüberlauf, wenn
die Verdoppelung des Vectors nicht mehr möglich ist. Im Worstcase speichert der Vector
in Java also nur halb so viele Elemente wie eigentlich möglich wären. Noch schlechter wird
die Speicherauslastung, wenn der Vector sich nicht direkt vergrößern kann, sondern erst
einen neuen Speicherbereich reservieren muss, der doppelt so groß wie der aktuelle ist. In
diesem Fall würde der Vector in Java im Worstcase sogar nur ein Drittel der theoretisch
möglichen Elemente speichern können. Gleiches gilt für die Collection.
In C++ beeinflusst dieser Effekt unsere Messungen nicht, da das Programm zur Laufzeit
beliebig viel Speicher nutzen konnte. Der Speicherbedarf wurde erst am Ende gemessen,
also nachdem alle Elemente in den Vector eingetragen waren.
15
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
3.3.2 Messungen zur Laufzeit (Datei lesen)
Es wurde jeweils eine 10MB, eine 101MB sowie eine 728MB große Textdatei eingelesen.
Getestet wurden damit die folgenden 5 Implementierungsvarianten:
• 1) Sprache: Java, Benutze Methode: Filereader
• 2) Sprache: Java, Benutze Methode: Filereader mit festem Buffer (32.768 Zeichen)
• 3) Sprache: Java, Benutze Methode: BufferedInputStream
• 4) Sprache: Java, Benutze Methode: FileInputStream
• 5) Sprache: C++, Benutze Methode: fopen
Bei dem Filereader mit festem Buffer wurden anschließend jeweils die erhaltenen Strings
Zeichen für Zeichen durchlaufen.
Auf eine Implementierung mit einer Readline-Funktion wurde verzichtet, um das Programm später leichter erweiterbar auf weitere Anwendungsbereiche zu machen. So ist es
denkbar, dass das Programm nicht nur für die Analyse kurzer Reads aus Ilumina/Solexa
Sequenzierungsprojekten von 30-100 Basenpaarlänge verwendet werden könnte, sondern
auch auf Daten aus bereits zu größeren Contigs zusammengefassten Reads oder sogar auf
ganze Genome (siehe Kapitel 6: weitere Einsatzgebiete). Im Worstcase könnten solche
Dateien dann keine Zeilenumbrüche mehr enthalten.
Alle Messungen wurden 3 mal durchgeführt, angegeben ist jeweils der Mittelwert.
Dateigröße
in MB
10
101
728
1)
1sec
16 sec
215 sec
Implementierungsvariante
2)
3)
4)
5)
1 sec
40 sec
55 sec
1 sec
3 sec
416 sec
556 sec
2 sec
35 sec 2845 sec 2832 sec 29 sec
Tabelle 3: Lesezeit für eine Datei
16
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4. Algorithmen
Gegeben seien r Pattern, bezeichnet mit p1 , . . . , pr . Die Länge des i-ten Patterns pi sei li ,
l sei die Länge des längsten Patterns. Die summierte Länge aller Pattern bezeichnen wir
r
P
li .
mit m =
i=1
Mit pi [x] bezeichnen wir den x-ten Buchstaben des Patterns pi , das Präfix der Länge q
von pi wird mit pi [1..q] bezeichnet. Π sei die Menge aller Pattern p1 , . . . , pr . T sei der
Eingabetext, die Länge des Eingabetextes sei n.
Σ sei das den Pattern und dem Eingabetext zugrunde liegende Alphabet der Größe |Σ|.
Wir werden in diesem Kapitel mehrere Algorithmen betrachten, mit denen man alle Auftreten der Pattern pi ∈ Π im Eingabetext bestimmen kann.
4.1 Brute Force / Single Pattern Matching
Für das Single Pattern Matching, also das Suchen nach nur einem Pattern, gibt es viele
bewährte Algorithmen (siehe [Par03]). Eine naheliegende Idee wäre, einfach diese Algorithmen für alle pi ∈ Π einzeln anzuwenden. Man hätte so ohne großen Aufwand einen
Algorithmus, welcher unser Problem lösen würde. Allerdings hätten all diese Algorithmen
eine Laufzeit von mindestens O(n · m), was nicht sehr effizient wäre.
Für sehr wenige Pattern (≤ 10) und sehr große Alphabete kann sich das mehrfache Anwenden von Single Pattern Matching dennoch lohnen. Die Vorteile sind ein geringer Implementationsaufwand, sowie die Tatsache, dass einige der Single Pattern Algorithmen
bei großen Alphabeten im Mittel (nicht im Worstcase!) eine sublineare Laufzeit haben.
In unserem Fall haben wir aber extrem viele Pattern (≥ 105 ) und ein sehr kleines Alphabet
(|Σ| = 4). Single Pattern Matching Algorithmen sind für diese Aufgabe also vollkommen
ungeeignet.
17
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.2 Multipattern Karp-Rabin
Dieser Algoritmus ist auf dem klassischen“ Karp-Rabin Algorithmus [KR87] für das
”
Single Pattern Matching aufgebaut. Die Grundidee des Karp-Rabin Algorithmus ist, dass
man vor dem Vergleich der Zeichen des Patterns mit denen einer gleich langen Zeichenkette des Eingabetextes prüft, ob die beiden Zeichenketten ähnlich“ sind (Fingerabdruck).
”
Diese Ähnlichkeit bestimmt man mit einer Hashfunktion h : Σm → N.
Haben zwei Zeichenketten einen unterschiedlichen Hashwert, so folgt daraus, dass sie ungleich sind. Für die Suche bedeutet dies, dass an dieser Stelle im Eingabetext das Pattern
nicht vorkommen kann. Haben dagegen zwei Zeichenketten den selben Hashwert, so kann
es sein, dass die beiden Zeichenketten gleich sind. Dies muss man dann Zeichen für Zeichen
überprüfen.
Damit ist der Karp-Rabin Algorithmus für das Single Pattern Matching auch schon beschrieben. Um einen effizienten Algorithmus zu erhalten, sollte sich der Wert der Hashfunktion für eine Zeichenkette T [i + 1..i + m + 1] leicht aus der vorangegangenen Zeichenkette T [i..i + m] errechnen lassen. Idealerweise gilt für die Hashfunktion also:
i) h(T [i + 1..i + m + 1]) = f (h(T [i..i + m]), T [i + m + 1])
In der Praxis ist die Forderung aber zu stark, daher verwendet man stattdessen Hashfunktionen, welche die folgende abgeschwächte Bedingung erfüllen:
ii) h(T [i + 1..i + m + 1]) = f (h(T [i..i + m]), T [i + m + 1], T [i])
Karp und Rabin haben als Hashfunktion folgende Funktion vorgeschlagen:
h(x1 · · · xm ) =
m
X
xj · bm−j mod q
j=1
wobei xi ∈ Σ, q eine ausreichend große“ Primzahl und b eine passend gewählte“ Basis
”
”
(oft b = |Σ|) ist. Man erkennt leicht, dass die so definierte Hash-Funktion ii) erfüllt, denn:
h(T [i + 1..i + m + 1]) = ((h(T [i..i + m]) − T [i] · bm−1 ) · b + T [i + m + 1])mod q
Lemma
Der Karp-Rabin Algorithmus löst das Single Pattern Matching Problem in O(m · n)
Zeiteinheiten und benötigt dabei O(m) Speicherplatz.6
Muth und Manber [MM96] sowie Gum und Lipton [GL00] haben den klassischen KarpRabin Algorithmus auf das Multi Pattern Matching erweitert, indem Sie ihn mit binären
Suchbäumen kombiniert haben.
In der Vorverarbeitung werden dazu als erstes alle Pattern pi ∈ Π auf dieselbe Länge
gebracht, indem wir jeweils nur die ersten lmin = min {lp1 , · · · , lpr } Zeichen betrachten.
Dann werden für all diese verkürzten Pattern die Hashwerte mit derselben Hashfunktion
h wie in dem klassischen Karp-Rabin berechnet. Diese Hashwerte werden alle in einen
Binären Suchbaum eingetragen.
Die Suchphase läuft dann so ab, dass zuerst der Hashwert von der aktuellen Position im
Eingabetext berechnet wird. Anschließend wird nachgeschaut, ob sich dieser Hashwert im
binären Suchbaum befindet. Ist dies der Fall, so müssen alle Pattern pi ∈ Π, welche diesen
Hashwert besitzen, einzeln gegen den Eingabetext an dieser Position getestet werden. Ist
6
Beim Single Pattern Matching gilt m =
1
P
i=1
18
|pi | = |p1 | = |p|
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
dieser Hashwert nicht in dem Suchbaum vorhanden, kann an der aktuellen Position auch
keins der Pattern vorkommen. Die aktuelle Position kann also um 1 nach rechts verschoben werden.
Lemma
Der Multipattern“ Karp-Rabin Algorithmus löst das Multi Pattern Matching Pro”
blem in O(m · n) Zeiteinheiten und benötigt dabei O(m) Speicherplatz.
Verbessern lässt sich die Laufzeit, indem man anstatt von binären Suchbäumen AVLBäume verwendet.
Eine weitere Verbesserung, das sogenannte Two-Level-Hashing, schlagen Muth und Manber in [MM96] vor. Dabei wird zuerst wie vorher beschrieben der Hashwert für alle Pattern
berechnet und in den Suchbaum eingetragen.
Anschließend wird eine zweite Hashtabelle (auch Bitmap-Table genannt) erstellt, welche
für eine Zahl x den Wert 1 enthält, wenn x in dem vorher berechneten Suchbaum enthalten ist. Alle anderen Werte in der zweiten Hashtabelle sind 0.
In der Suchphase wird nun wie bisher der Hashwert über die aktuelle Position des Eingabetextes berechnet. Anschließend wird erst mal in der zweiten Hashtabelle geschaut, ob
sich dieser Hashwert überhaupt in dem Suchbaum befindet. Dadurch spart man sich im
Fall, dass der Hashwert nicht vorkommt, die O(log(m)) Zugriffe auf den Suchbaum.
19
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.3 Aho-Corasick
4.3.1 Idee
Sei l die Länge des längsten Patterns aus Π. Liest man von links nach rechts über den
Eingabetext, so kann man für alle pi aus Π anhand der letzten l gelesenen Zeichen bestimmen ob pi an der aktuellen Position im Eingabetext vorkommt oder nicht. Alles, was
vor den letzten l Zeichen gelesen wurde, spielt keine Rolle mehr.
Man braucht also nur die l zuletzt gelesenen Zeichen zu betrachten, was wiederum bedeutet, dass man bei einem Alphabet der Größe |Σ| nur |Σ|l verschiedene Möglichkeiten hat.
Da man also nur endlich viele Möglichkeiten ( = Zustände) hat und der Eingabetext
Zeichen für Zeichen eingelesen wird, bietet sich hier die Verwendung eines endlichen deterministischen Automaten an.
4.3.2 Definition
Definition 4.1:
Ein Aho-Corasick Automat wird durch ein Tupel (Q, Σ, g, next, q0 , out) beschrieben. Dabei sind Q die Menge von Zuständen, Σ das Eingabealphabet, g : Q × Σ → Q
die partielle Zustandsübergangsfunktion und q0 ∈ Q der Startzustand jeweils wie beim
DFA definiert.
out (Q) ist die Ausgabefunktion, welche für jeden Zustand aus Q eine Ausgabe erzeugt.
Die Fehlerfunktion next ist definiert als next : Q → Q.
Wenn g(q, b) für q ∈ Q, b ∈ Σ nicht definiert ist, wird die Fehlerfunktion next(q) so
oft angewendet, bis der Aho-Corasick Automat sich wieder in einem Zustand q 0 ∈ Q
befindet für den g(q 0 , b) definiert ist. next ist genau wie g eine partielle Funktion.
Außerdem gilt für den Aho-Corasick Automaten:
1) g(q0 , a) ist für alle a ∈ Σ definiert.
2) Gilt next(q) = q 0 , so muss der kürzeste Weg im Transitionsgraphen des Automaten
von q0 nach q 0 kürzer sein, als der von q0 nach q.
Lemma: Der Aho-Corasick Algorithmus arbeitet in O(n) Schritten.
g(q0 , a) ist definiert für alle a ∈ Σ. Wenn man jetzt k-mal die Übergangsfunktion g
anwendet, so ist der kürzeste Weg im Transitionsgraphen des Automaten von q0 zum
aktuellen Zustand kleiner oder gleich k. Da bei jeder Anwendung der Fehlerfunktion
dieser Weg nach 2) aber um mindestens 1 kürzer werden muss, sind maximal k Anwendungen möglich. Insgesamt kann also die Fehlerfunktion maximal so oft angewendet
werden wie die Übergangsfunktion g.
Pro Zeichen im Eingabetext wird genau einmal die Übergangsfunktion angewendet.
Dies ist gewährleistet, da spätestens wenn man auf q0 zurückgesprungen ist, die Übergangsfunktion für alle Symbole aus Σ definiert ist. Daraus folgt, dass die Anzahl der
Übergänge im Algorithmus kleiner oder gleich 2n sein muss. (Genauer sogar 2n − 1,
weil der letzte Übergang im Algorithmus mit der Übergangsfunktion g gemacht wird
und danach kein Übergang mit der Fehlerfunktion mehr folgen kann.)
20
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.3.3 Vorverarbeitung
Wir konstruieren aus allen Pattern pi ∈ Π einen Trie.
Die Konstruktion erfolgt sukzessiv aus allen Pattern pi ∈ Π. Wir starten mit einem Knoten q0 , welcher die Wurzel des Baumes sei. Der Knoten q0 sei unser aktueller Knoten.
Jetzt betrachten wir den ersten Buchstaben p1 [1] des erste Patterns p1 ∈ Π. Gibt es eine
Kante vom aktuellen Knoten aus, welche mit p1 [1] markiert ist und zu einem Knoten q
führt, so sei der Knoten q unser aktueller Knoten.
Gibt es keine solche Kante, so fügen wir einen neuen Knoten als Nachfolger vom aktuellen
Knoten ein und markieren die neue Kante mit p1 [1]. Der aktuelle Knoten sei dann der
neu eingefügte Knoten.
Entsprechend verfahren wir mit den anderen Buchstaben p1 [2] bis p1 [l1 ] des ersten Patterns.
Nachdem wir das erste Pattern abgearbeitet haben, nehmen wir wieder q0 als unseren
aktuellen Knoten und setzen dann das Verfahren mit p2 fort.
Abbildung 8: Hinzufügen vom Wort TEMP zu einem bestehenden Trie
Nach Konstruktion erhalten wir in O(m) Schritten den gewünschten Trie, wobei
r
P
m :=
|pi | gilt.
i=1
Die Knoten des Tries entsprechen den Zuständen des Automaten, die Kanten ergeben
folgendermaßen die Übergangsfunktion g:
1) g(q, b) := q 0 , falls es eine Kante von q nach q 0 gibt, welche mit b markiert ist.
Da wir für unseren Aho-Corasick Automaten noch gefordert hatten, dass g(q0 , b) für alle
b ∈ Σ definiert sein soll, setzen wir noch:
2) g(q0 , b) := q0 , falls g(q0 , b) nicht in 1) definiert wurde.
21
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Als nächstes konstruieren wir die Fehlerfunktion next : Q → Q.
Die Fehlerfunktion soll immer dann verwendet werden, wenn g(q, b) für q ∈ Q, b ∈ Σ
nicht definiert ist. Ziel ist es, dass wir nur soweit im Trie zurückspringen wie notwendig.
Wir suchen also den Knoten q 0 , welcher das längste Suffix von dem aktuellen Knoten q
repräsentiert und setzen dafür next(q) := q 0 .
Gibt es keinen Knoten im Trie, welcher ein Suffix von q repräsentiert, so setzen wir
next(q) := q0 .
Lemma: Naive Konstruktion der Fehlerfunktion in O(r · l2 )
Wir durchlaufen jeden Knoten im Baum, wobei die Reihenfolge unwichtig ist. Sind die
Knoten in einem Array oder Vector gespeichert, so kann man beispielsweise einfach
das Array bzw. den Vector linear durchlaufen.
q sei dabei der Knoten, bei dem wir uns gerade befinden und lq die Länge des vom
Knoten q repräsentierten Wortes. Wir durchlaufen dann mit dem Suffix q[2..lq ] den
bisher konstruierten DFA (Q, Σ, g, q0 , F )7 . Erreichen wir nach dem Abarbeiten des
Eingabewortes q[2..lq ] einen Zustand qsuf f ix , ohne das wir zwischendurch auf einen
Übergang g(q 0 , b) → q0 und ohne das wir auf einen nicht definierten Übergang treffen,
so setzen wir next(q) = qsuf f ix .
Andernfalls versuchen wir den DFA entsprechend mit den Suffixen q[3..lq ], . . . ,q[lq ..lq ]
zu durchlaufen. Erreichen wir bei keinem der Durchläufe am Ende einen Zustand im
DFA ohne vorher auf einen nicht definierten Übergang oder einen Übergang g(q 0 , b) →
q0 zu treffen, so setzen wir next(q) := q0 .
Dann setzen wir das Verfahren mit dem nächsten Knoten im Durchlauf fort. Insgesamt
kommen wir auf m Knoten und pro Knoten auf maximal l Suffixe. Die Längen der
Suffixe aufaddiert ergibt:
l + (l − 1) + (l − 2) + . . . ≤ l2 , also haben wir eine Gesamtkomplexität von O(r · l2 )
Diese Konstruktion der Fehlerfunktion demonstriert bereits sehr schön die Arbeitsweise
des Aho-Corasick Automaten. Allerdings ist sie nicht optimal, was die Anzahl der benötigten Schritte angeht. Mit etwas mehr Speicherbedarf lässt sich die Fehlerfunktion effizienter
bestimmen, indem man rekursiv vorgeht:
Lemma: Rekursive Konstruktion der Fehlerfunktion in O(m)
Wir durchlaufen jeden Knoten in einem Breitendurchlauf.
Im ersten Schritt setzen wir dabei für alle Knoten der Tiefe 1 die Fehlerfunktion auf
q0 .
Angenommen die Fehlerfunktion sei für alle Knoten mit Tiefe kleiner als t bereits
bestimmt. Wir durchlaufen nun alle Knoten mit Tiefe t − 1 und bestimmen dabei die
Menge Rt als Menge aller Tupel (r, a, s) für die gilt, dass g(r, a) = s, a ∈ Σ, s ∈ Q,
r Knoten der Tiefe t − 1. Nach Konstruktion dieser Tupel gilt also für (r, a, s) ∈ Rt ,
dass s ein Knoten der Tiefe t ist. r ist jeweils der eindeutige Vorgänger von s und a
die zugehörige Kantenmarkierung für die Kante von r nach s.
Da die zugrundeliegende Datenstruktur ein Trie ist folgt weiterhin, dass für jeweils 2
Tupel (r1 , a1 , s1 ) ∈ Rt und (r2 , a2 , s2 ) ∈ Rt gilt: s1 6= s2 . Ebenfalls aus der Eigenschaft
Trie folgt: {s| {r, a, s} ∈ Rt } ist genau die Menge aller Knoten mit Tiefe t.
7
setze dazu F = ∅
22
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Aus Rt bestimmen wir nun die Fehlerfunktion für jeden Knoten der Tiefe t folgendermaßen:
for every (r, a, s) in Rt :
begin
state := next(r)
while (g(state, a) == nicht definiert)
begin
state = next(state)
end
f(s) = state
end
Da g(q0 , b) für alle b ∈ Σ definiert ist, liefert der Algorithmus in jedem Fall ein Ergebnis.
Als letztes müssen wir noch die Endzustände des Automaten bestimmen. Endzustand sind
natürlich alle Knoten des Tries, die eins der Pattern repräsentieren. Im Folgenden werden
wir diese Endzustände als direkte Endzustände bezeichnen. Die direkten Endzustände
können anfangs beim Erstellen des Tries ohne zusätzlichen Zeit und Platzbedarf bestimmt
werden. Der Knoten, welcher beim Hinzufügen des letzten Zeichens eines Patterns erreicht
wird, ist nämlich der direkte Endzustand für das jeweilige Pattern.
Sei q ein direkter Endzustand für das Pattern p. Dann setzen wir für die Ausgabefunktion
out(q) := P attern p gef unden“.
”
Des Weiteren müssen alle Knoten ein Endzustand des Automaten sein, die ein Wort repräsentieren, welches als Suffix eins der Pattern enthält (Im Folgenden als indirekte
Endzustände bezeichnet). Ist beispielsweise Haus ein Pattern, so ist der Knoten q im
Baum, welcher das Wort HolzHaus repräsentiert ein Endzustand des Automaten (für
das Wort Haus).
Wurden die direkten Endzustände bereits vor dem Erstellen der Fehlerfunktion bestimmt,
so können die indirekten Endzustände ohne zusätzlichen Zeit und Platzbedarf beim Erstellen der Fehlerfunktion mitbestimmt werden. Setzt man beim Breitendurchlauf für einen
Knoten q die Fehlerfunktion next(q) := q 0 , so erweitern wir einfach die zugehörige Outputfunktion um den Wert der Outpuntfunktion an der Stelle q 0 , also:
out(q) := out(q) + out(q 0 ).
Lemma: Die Vorverarbeitung des Aho-Corasick Automaten benötigt O(m) Schritte.
Dies folgt jetzt direkt aus unseren bisherigen Ergebnissen. Der DFA lässt sich in O(m)
konstruieren, für die Fehlerfunktion benötigt man ebenfalls O(m) Schritte. Um die
Endzustände zu bestimmen, benötigt man keine weitere Komplexität.
Insgesamt erhält man also eine Komplexität von O(m) .
❏
23
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.3.4 Suchphase
Gegeben ist ein Eingabetext der Länge n, sowie der in der Vorverarbeitung bestimmte
Aho-Corasick Automat (Q, Σ, g, next, q0 , out).
Genau wie bei einem DFA arbeiten wir jetzt Zeichen für Zeichen den Eingabetext ab.
Erreichen wir dabei einen Endzustand, so haben wir eins der Pattern pi ∈ Π gefunden.
Im Gegensatz zu einem DFA ist die Zustandsübergangsfunktion aber nicht vollständig
bestimmt. Es kann also vorkommen, dass wir uns im Zustand q ∈ Q befinden und ein
Zeichen b ∈ Σ einlesen und g(q, b) nicht definiert ist. In diesem Fall wenden wir solange
die Fehlerfunktion next(q) an, bis wir uns in einem Zustand q 0 befinden, für den g(q 0 , b)
definiert ist. Ein solcher Knoten q 0 existiert nach Definition.
Bemerkung:
Für die Fehlerfunktion hatten wir gefordert, wenn next(q) = q 0 gilt, so muss der
kürzeste Weg im Transitionsgraphen des Automaten von q0 nach q’ kürzer sein, als
der von q0 nach q.
Hat ein Knoten q also die Tiefe k im Transitionsgraphen des Automaten, so erreichen
wir spätestens nach (k − 1)-maligen Anwenden der Fehlerfunktion den Wurzelknoten
q0 . Und für q0 gilt wiederum nach Definition, dass g(q0 , a) für alle a ∈ Σ definiert ist.
Zu beachten ist noch, dass wenn man mit der Fehlerfunktion von einem Zustand q des
Automaten zu einem Zustand q 0 übergeht und q 0 ein Endzustand für ein Pattern pi ist, so
war q bereis ein (indirekter) Endzustand für dieses Pattern. Man muss also bei der Implementierung aufpassen, dass man hierbei ein Auftreten für das Pattern pi nicht doppelt
zählt.
24
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Beispiel:
Abbildung 9: Suchphase eines Aho-Corasick Automaten.
In Abbildung 9 ist die Suchphase des Aho-Corasick Automaten grafisch dargestellt. Zu sehen ist ein Aho-Corasick Automat für 2 Pattern ( TET“ und ETT“). Die Fehlerfunktion
”
”
ist in Hellblau eingezeichnet, alle Endzustände sind mit Gelb hinterlegt. Der aktuelle Zustand und der letzte Übergang sind mit Rot markiert. Der Eingabetext lautet TTETT“,
”
das Alphabet ist Σ = {E, T }.
25
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.3.5 Vermeidung unnötiger Übergänge der Fehlerfunktion
Wir betrachten noch mal den Aho-Corasick Automaten aus Abbildung 9, nehmen diesmal
als Eingabetext aber TEE“.
”
Abbildung 10: Suchen von TEE“ in dem AC-Automaten aus Abbildung 9
”
Wir sehen, dass der Aho-Corasick Automat beim Anwenden der Fehlerfunktion next(q2 )
erst in q4 übergeht und dann mit next(q4 ) nach q0 . Im Prinzip hätte aber auch gleich von
q2 nach q0 gesprungen werden können, da von q2 und q4 nur mit einem T“ beschriftete
”
Kanten ausgehen. Wenn wir also in q2 die Fehlerfunktion verwenden müssen, weil g(q2 , b)
nicht definiert ist, so ist klar, dass g(q4 , b) ebenfalls nicht definiert ist.
26
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Wir können also die Fehlerfunktion verbessern, indem wir zusätzlich fordern:
Gilt next(q) = q 0 , so muss es mindestens ein b ∈ Σ geben mit g(q, b) nicht definiert und
g(q 0 , b) = q 00 .
4.3.6 Verbesserung für kleine Alphabete
Ist die Alphabetgröße bekannt, so kann man insbesondere bei kleinen Alphabeten eine
weitere Veränderung am Aho-Corasick Automaten vornehmen. Und zwar definieren wir
dazu folgendermaßen eine neue Fehlerfunktion next0 : (Q, Σ) → Q:
next0 (q, b) = next(q), wenn g(next(q), b) definiert ist.
Andernfalls wenden wir so oft unsere alte Fehlerfunktion next(q) an, bis wir einen Zustand
qnext0 des Aho-Corasick Automaten erreichen, für den g(qnext0 , b) definiert ist und setzen:
next0 (q, b) = qnext0
Wir haben somit für jeden Zustand und jeden Buchstaben eine eigene Fehlerfunktion definiert. Da wir die Fehlerfunktion immer nur dann verwenden, wenn g(q, b) nicht definiert
ist, können wir unsere neue Fehlerfunktion und unsere Zustandsübergangsfunktion zu einer neuen Funktion g 0 verschmelzen:
g 0 (q, b) = g(q, b), falls g(q, b) definiert ist,
g 0 (q, b) = next0 (q, b) sonst.
Wir erhalten somit einen DFA mit vollständig definierter Zustandsübergangsfunktion.
Der Vorteil ist, dass wir die Eingabe somit ohne irgendwelche Fehlerfunktionsaufrufe oder
unnötige Übergänge nur mit der Zustandsübergangsfunktion abarbeiten können. Während
der klassische Aho-Corasick Automat im schlechtesten Fall noch 2n−1 Schritte benötigte,
benötigt dieser vollständige DFA nur n Schritte. Der Nachteil ist allerdings, dass wir für
jeden Zustand q ∈ Q und für jedes Eingabesymbol b ∈ Σ die Zustandsübergangsfunktion
definieren müssen. Dies ist sehr Speicheraufwendig und lohnt daher nur bei recht kleinen
Alphabeten.
27
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.4 Wu-Manber
Bevor wir uns den Algorithmus anschauen, wollen wir uns auf 2 Schreibweisen festlegen:
Sei T = T [1 · · · n] der Eingabetext. An Position i im Eingabetext steht dann das
Zeichen T [i] ∈ Σ. Entsprechend definieren wir auch die Position für ein Teilwort der
Länge l vom Eingabetext:
Sei x = T [i · · · i + l] ein Teilwort der Länge l vom Eingabetext, so kommt x an der
Position i in T vor.
4.4.1 Beschreibung
Mit dem Aho-Corasick Algorithmus haben wir bereits einen Algorithmus kennengelernt,
der das Problem in O(n) löst. Im Worstcase ist dies der optimale Fall. Die Idee des
Wu-Manber Algorithmus ist es, nicht so sehr auf den Worstcase zu achten, sondern den
Algorithmus im Mittel schneller als lineare Laufzeit zu machen. Vorbild dazu ist der BoyerMoore Algorithmus8 , welcher quasi auf das Multi Pattern Matching übertragen wird.
Als erstes bringen wir dazu alle pi ∈ Π auf dieselbe Länge, indem wir jeweils nur die
ersten lmin = min {lp1 , · · · , lpr } Zeichen betrachten. Aus diesen verkürzten Pattern bilden
wir lmin Mengen P1 , · · · , Plmin , wobei die Menge Pk alle Zeichen aus Σ enthält, die in
mindestens einem der pi ∈ Π an k-ter Position vorkommen:
P1 = {p1 [1], · · · , pr [1]} , · · · , Plmin = {p1 [lmin ], · · · , pr [lmin ]}.
Mit diesen Mengen Pi können wir einen Filteralgorithmus für das Multi Pattern Matching
konstruieren. Dazu arbeiten wir den Eingabetext T folgendermaßen von Links nach Rechts
ab:
Unsere aktuelle Position im Eingabetext sei mit x (in den Grafiken durch den kleinen
blauen Pfeil dargestellt) bezeichnet. Wir unterscheiden jetzt 3 Fälle:
1.) Wenn der Buchstabe T [x] in keiner der Mengen Pk vorkommt, so können wir die
nächsten lmin Zeichen des Eingabetextes überspringen.
Abbildung 11: Fall 1: Der Buchstabe T [x], in diesem Fall W“ kommt in keinem der
”
Pi ’s vor. Wir können also 4 Zeichen des Eingabetextes überspringen.
8
Der Boyer-Moore ist ein Algorithmus für das Single Pattern Matching. Siehe dazu [BM77] und [Hor80]
28
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
2.) Wenn der Buchstabe T [x] in der Menge Plmin vorkommt, so kann es sein, dass wir
ein pi ∈ Π gefunden haben. Um dies zu verifizieren müssen jetzt alle pi ’s, die an der
lmin ’ten Position das Zeichen T [x] haben, einzeln getestet werden. Danach verschieben
wir unsere aktuelle Position um 1.
Abbildung 12: Fall 2: Der Buchstabe T [x], in diesem Fall T“ kommt in P4 vor. Wir
”
müssen also testen, ob eines der Pattern pi an dieser Stelle auftritt.
3.) Andernfalls verschieben wir unsere aktuelle Position so, dass das gerade im Eingabetext gelesene Zeichen T [x] mit dem rechtesten Auftreten des Zeichens T [x] in einem
der Pattern übereinstimmt. Wir überspringen also lmin − max {k|T [x] ∈ Pk } Zeik=1,··· ,lmin
chen des Eingabetextes.
Abbildung 13: Fall 3: Der Buchstabe T [x], in diesem Fall S“ kommt in P3 vor. Wir
”
können also 4 - 3 = 1 Zeichen des Eingabetextes überspringen.
Aus diesen 3 Fällen kann man schon 2 wichtige Feststellungen machen:
29
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Lemma 4.1: Der Algorithmus hat eine Laufzeit von O(n · m).
In jedem der drei Fälle wird im Eingabetext um mindestens 1 nach rechts gesprungen.
Im zweiten Fall kommt es zusätzlich noch zu einem Vergleich mit allen Pattern. Die
Laufzeit beträgt also im Worstcase O(n · m).
❏
Lemma: 4.2 Der Algorithmus findet alle Auftreten der Pattern, insbesondere wird kein
Auftreten übersprungen.
Im ersten Fall wird ein Zeichen eingelesen, welches in keinem der Pattern vorkommt.
Danach wird der Eingabetext so verschoben, dass genau dieses Zeichen übersprungen
wird. Dabei ist es unmöglich, ein Auftreten eines Patterns zu überspringen, weil ja
keins der Pattern diesen Buchstaben enthält.
Im zweiten Fall testen wir alle Pattern und verschieben dann um 1. Ein Überlesen ist
also unmöglich.
Im dritten Fall wird das gelesene Zeichen T [x] mit dem rechtesten Auftreten dieses
Zeichens in einem der Pattern in Übereinstimmung gebracht. Alle kleineren Verschiebungen würden keinen Treffer liefern, weil das Zeichen T [x] gegen ein Zeichen ungleich
T [x] ausgerichtet wäre.
❏
Wir haben also einen Algorithmus, welcher unser Problem löst. Allerdings erkennt man
schnell, dass dieser Algorithmus noch sehr ineffizient ist. Insbesondere bei relativ vielen
Pattern würde oft der zweite Fall eintreten und der Algorithmus würde praktisch dem
Brute Force entsprechen.
Daher wollen wir jetzt zwei Verbesserungen betrachten, mit denen dieser Algorithmus
konkurrenzfähig“ wird:
”
Verbesserung 1: Vergrößern“ des Alphabetes
”
Anstatt immer nur ein Zeichen des Eingabetextes zu betrachten, nehmen wir jetzt
ein Lesefenster9 der Länge B. Damit verändern sich die 3 obigen Fälle folgendermaßen:
1.) Kommt der aktuelle Inhalt des Lesefensters in keinem der Pattern vor, so
verschieben wir unser aktuelles Lesefenster um lmin − B + 1 Positionen.
9
in den Grafiken als hellblaues Rechteck dargestellt
30
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Abbildung 14: Fall 1: Der Inhalt des Lesefensters, in diesem Fall WT“
”
kommt in keinem der pi ’s vor. Wir können also das Lesefenster um
lmin − B + 1 = 4 − 2 + 1 = 3 Positionen nach rechts verschieben.
2.) Ist der Inhalt des Lesefensters Suffix von einem der Pattern, so testen wir ob
eins der Pattern an der aktuellen Position im Eingabetext vorkommt. Danach
verschieben wir unsere Lesefenster um eine Position nach Rechts.
Abbildung 15: Fall 2: Der Inhalt des Lesefensters, in diesem Fall AU“ kommt
”
in p3 vor. Wir müssen also testen, ob eins der Pattern pi an dieser Stelle
auftritt.
31
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
3.) Ansonsten verschieben wir das Lesefenster so, dass der gerade gelesene
Block genau mit dem rechtesten Auftreten dieses Blocks in einem der Pattern
übereinstimmt.
Abbildung 16: Fall 3: Der Inhalt des Lesefensters, in diesem Fall TE“ kommt
”
in p1 vor. Wir verschieben unser Lesefenster also um 2 Positionen, um das
TE“ im Eingabetext dem TE“ in p1 gegenüberzustellen.
”
”
Verbesserung 2: Optimierung der Vergleiche im Fall 2
Anstatt im zweiten Fall immer Brute Force gegen alle Pattern zu vergleichen, wollen wir nur die Pattern testen, die auch auf das aktuelle Zeichen enden. Dafür
sortieren wir die Pattern einfach nach den letzten B Zeichen.
Denkbar ist dazu auch die Implementierung eines Suchbaumes, ähnlich wie es beim
Multi Pattern Karp-Rabin (siehe Kapitel 4.2) gemacht wird, sowie der Einsatz der
von Muth und Manber in [MM96] vorgestellten Technik des Two-Level-Hashing.
Besonders effizient ist eine solche Implementierung, wenn bereits eine Ordnung
>“ auf Σ gegeben ist. Dies ist Beispielsweise der Fall, wenn die Zeichen des Ein”
gabetextes im Algorithmus als Zahlen gespeichert werden (ASCII-Werte).
4.4.2 Vorverarbeitung
Im ersten Schritt bringen wir alle pi ∈ Π auf dieselbe Länge, indem wir jeweils nur die
ersten lmin = min {lp1 , · · · , lpr } Zeichen betrachten. Die verkürzten Pattern bezeichnen
wir mit p0i . Anschließend sortieren wir die p0i ’s nach den letzten B Zeichen. Dies benötigt
O(B · r · log(r)) Schritte.
Nun bestimmen wir die Funktion Shif t : |Σ|B → {0, · · · , lmin }, welche uns für die aktuellen B Zeichen angeben soll, um wie viele wir unser Lesefenster nach rechts verschieben
können. Dazu setzen wir zuerst Shif t(x) = lmin − B + 1 für alle x ∈ ΣB . Dies ist die
Verschiebung für den Fall 1.
Anschließend durchlaufen wir nacheinander sämtliche Pattern p0i ∈ Π0 , und bestimmen
dabei für jedes Pattern alle Teilworte der Länge B. Sei t ein solches Teilwort und sei pos
die Position des Teilwortes in dem aktuellen Pattern. Dann setzen wir:
32
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Shif t(x) = min(Shif t(x), lmin − B + 1 − pos).
Kommt das Teilwort x genau am Ende des aktuellen Patterns vor, so ist pos = lmin −B+1,.
Für dieses Teilwort wird die Shif t-Funktion also 0 (Entspricht Fall 2).
Die Konstruktion der Shif t-Funktion geschieht in O(m).
4.4.3 Suchphase
Die Suchphase ist jetzt eigentlich ganz einfach:
Sei x der aktuelle Inhalt unseres Lesefensters. Solange Shif t(x) > 0 gilt, schieben wir
unser Lesefenster um x Positionen nach rechts.
Gilt Shif t(x) = 0, so testen wir alle p0i s ∈ Π deren zugehöriges p0i ∈ Π0 auf x endet, ob sie
an der aktuellen Position vorkommen. Dazu verwenden wir einfach den Brute Force oder
einen der bekannten Single Pattern Matching Algorithmen.
4.4.4 Wahl der Blockgröße B
Bei der Wahl der Blockgröße B spielen neben der begrenzenden Größe durch den verfügbaren Speicher 2 Faktoren eine Rolle:
Je größer B ist, desto mehr verschiedene Blöcke der Größe B gibt es. Damit steigt die
Wahrscheinlichkeit, dass ein Block x in keinem der Pattern am Ende vorkommt und damit wird man im Mittel öfter einen Wert Shif t(x) > 0 haben. Wir können also öfter
verschieben.
Wenn ein Block aber nicht in einem Pattern vorkommt, dann können wir die nächsten
lmin − B + 1 Positionen überspringen. Das bedeutet allerdings, dass wir umso weniger
Positionen pro Verschiebung überspringen können je größer B ist.
Sun Wu und Udi Manber schlagen in [WM94] als optimalen Wert für B daher log|Σ| 2m ,
r
P
m=
|pi | vor.
i=1
Lemma:
Pr
Für B = log|Σ| 2m , m =
i=1 |pi | ist die Wahrscheinlichkeit, dass eine zufällige
Zeichenkette der Länge B zu einem Shift größer 0 führt mindestens 12 .
Beweis:
Es gibt |Σ|B = |Σ|dlog|Σ| 2me ≥ |Σ|log|Σ| 2m = 2m verschiedene Worte der Länge B.
m ist definiert als die Summe aller Patternlängen und repräsentiert somit alles, was in
die Berechnung der Shiftfunktion eingeht. Folglich führen mindestens 2m − m Worte
zu einem Shiftwert größer 0.
Anmerkung:
Was Wu und Manber dabei allerdings nicht bedacht haben, ist die Tatsache, dass das
so berechnete B bei sehr vielen Pattern größer sein kann als lmin !
Das macht natürlich keinen Sinn, weil man ein Wort der Länge lmin nicht in Worte der
Länge lmin + 1 zerlegen kann. Die Grenze, bis zu der eine Berechnung der Blockgröße
B nach der von Wu und Manbers vorgeschlagenen Formel sinnvoll berechnet werden
können, kann man leicht erkennen:
Wenn man mehr als die Hälfte aller Worte der Länge lmin als Pattern verwendet, so ist
ein zufällig gewähltes Wort der Länge lmin mit einer Wahrscheinlichkeit größer als 21
33
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
in der Menge der Pattern und somit muss der Wert der Shift-Funktion nach Definition
0 sein.
Man kann dieses Problem technisch umgehen, indem man fordert, dass B maximal lmin
groß sein darf. Allerdings gilt dann das Lemma wonach die Wahrscheinlichkeit eines
Shifts wenigstens 12 ist nicht mehr. Daher sollte man sich überlegen, ob bei derartig
großen Mengen an Pattern der Einsatz einer Filterfunktion generell noch sinnvoll ist
(siehe dazu auch: Kapitel 5).
4.4.5 Verbesserungen
Bei großen Alphabeten ist der Algorithmus sehr Speicheraufwendig, weil wir bisher den
Wert der Shif t-Funktion für alle |Σ|B abgespeichert haben.
Wir haben allerdings schon anhand von Lemma 4.1 und Lemma 4.2 gesehen, dass der
Algorithmus, egal wo sich das Lesefenster befindet, kein Auftreten der Pattern überliest.
Insbesondere bedeutet dies, dass wenn man das Lesefenster um weniger als Shif t(x) Positionen, also weniger als eigentlich möglich wäre, verschiebt, der Algorithmus trotzdem
alle Auftreten der Pattern findet.
Wir können also auch eine Hashfunktion (Hash (x) : |Σ|B → XHash ) verwenden, um
x ∈ |Σ|B auf einen kleineren Wertebereich abzubilden. Dann können wir eine neue, kleinere Shiftfunktion definieren:
Shif t0 (y) : XHash → N.
Wichtig ist, dass die neue Shiftfunktion folgende Bedingung erfüllt:
Shif t0 (Hash (x)) ≤ Shif t (x) für alle x ∈ |Σ|B .
Dies lässt sich am einfachsten sicherstellen, indem man für
Shif t0 (y) = min {Shif t(x)|Hash(x) = y} wählt.
x∈|Σ|B
Eine weitere Verbesserung des Algorithmus sei noch kurz angesprochen, welche insbesondere bei Sprachen sinnvoll ist. Und zwar ist es bei Sprachen häufig so, dass gewisse
Endungen sehr oft vorkommen (bspw. die Endung ing“ im Englischen oder lich“ im
”
”
Deutschen). Daher ist es in solchen Fällen sinnvoll, die Pattern neben der Sortierung der
verkürzten Pattern außerdem noch nach den ersten10 Buchstaben zu sortieren. Dadurch
lässt sich im Fall 2 die Anzahl der nötigen Vergleiche deutlich reduzieren, weil man nur
noch die Pattern gegen den Eingabetext prüfen muss, welche auch ein passendes Präfix“
”
haben.
In unserer konkreten Problemstellung sehen wir die Verteilung der DNA-Basen als komplett zufällig an, daher ist eine solche Erweiterung nicht notwendig.11
10
Es empfiehlt sich die ersten B Buchstaben zu verwenden, weil man dann die oben definierte Hashfunktion
auch für die Präfixe wiederverwenden kann.
11
Wer dennoch mehr wissen möchte, dem empfehle ich [WM94]
34
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.5 q-Gramme
Viele Algorithmen für das String Matching, insbesondere Varianten des Boyer-Moore Algorithmus, haben eine gute Performance für große Alphabete. In dem vorangegangen
Kapitel haben wir dies auch beim Wu-Manber Algorithmus gesehen. Um die Performance
dabei zu Verbessern, haben wir das Alphabet scheinbar vergrößert, indem wir mehrere
Buchstaben sozusagen zu einem zusammengefasst haben. Diesen Trick wollen wir jetzt
formalisieren und auf weitere Algorithmen anwenden.
Definition 4.2:
Ein q-Gramm ist ein Wort der Länge q.
Sei w = w[1..m] ein Wort der Länge m ≥ q. Dann ist {w[1 .. q], w[2 .. q + 1], · · · , w[m − q .. q]}
eine Zerlegung von w in überlappende q-Gramme.
n
hl
m
io
Entsprechend ist w [1 .. q] , w [q + 1 .. 2 · q] , · · · , w m
−
1
q
..
q
eine Zerlegung
q
hl
m
i
von w in fortlaufende q-Gramme. Dabei ist zu beachten, dass w m
−
1
q
..
q
q
hl
m
i
m
eventuell nicht die Länge q hat. In diesem Fall ergänzen wir w q − 1 q .. q um
hl
m
i
−
1
q
..
q
/ Σ zu einem q-Gramm.
q − w m
Zeichen $ ∈
q
Beispiel: w = KATZE“
”
Zerlegung von w in überlappende q-Gramme mit q = 2:
KA, AT, TZ, ZE
Zerlegung von w in fortlaufende q-Gramme mit q = 2:
KA, TZ, E$
Salmela, Tarhio und Kytöjoki haben 2006 in [STK06] gezeigt, wie man mittels q-Grammen
verschiedene12 Single Pattern Matching Algorithmen effizient für Multi Pattern Matching
einsetzen kann. Die Idee dabei ist zuerst ein verallgemeinertes Pattern aus q-Grammen zu
erstellen, welches alle original Pattern enthält. Anschließend verwendet man einen Single
Pattern Algorithmus um dieses verallgemeinerte Pattern im Eingabetext zu suchen.
Die ursprünglichen Single Pattern Algorithmen arbeiten so als Filterfunktion für das Multi
Pattern Matching. Die Arbeitsweise der so gewonnenen neuen Multipattern Algorithmen
lässt sich also in 3 Schritte unterteilen:
1) Vorverarbeitung der Pattern zu einem verallgemeinerten q-Gramm Pattern.
2) Suche nach diesem verallgemeinerten Pattern mit einem Single Pattern Matching
Algorithmus. Der Single Pattern Matching Algorithmus wird so modifiziert, dass er
über einem durch Bildung von q-Grammen vergrößerten Alphabet sucht.
3) Überprüfen von den in Schritt 2 gefundenen Kandidaten mit einem (anderen) Single
Pattern Matching Algorithmus, ob ein Auftreten eines Patterns vorliegt.
12
konkret gezeigt wird dies in dem Paper für Boyer-Moore-Horspool, Shift-Or und Backward Nondeterministic DAWG Matching (BNDM)
35
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Anstatt in Schritt 3 mit einem Single Pattern Matching Algorithmus alle Pattern zu
überprüfen, schlagen L.Salmela et al. vor, dies mit einem modifizierten Karp-Rabin Algorithmus zu machen (vergleiche auch Kapitel 4.3). In der Vorverarbeitungsphase wird
dazu für jedes Pattern ein Hashwert (Fingerprint) berechnet und die Pattern in einer nach
den Hashwerten sortierten Liste gespeichert. Somit wird Schritt 3 noch mal in 2 Schritte
aufgespaltet:
3a) Überprüfen, ob zu den in Schritt 2 gefundenen Kandidaten ein passender Hashwert
abgespeichert ist.
3b) Bestimmung der zu diesem Hashwert passenden Pattern (Aufwand: O(m · log(m)))
und anschließend vergleich dieser Pattern gegen den Eingabetext.
4.5.1 Boyer-Moore-Horspool mit q-Grammen
Im folgenden Kapitel wollen wir jetzt obige Konstruktion eines Multi Pattern Algorithmus aus einem Single Pattern Algorithmus durch den Einsatz von q-Grammen am Beispiel
Boyer-Moore-Horspool betrachten.
Es gibt verschiedene Varianten des Boyer-Moore Algorithmus. Die Grundidee ist immer,
dass der Vergleich der Pattern gegen den Eingabetext von rechts nach links gemacht wird.
Bei großen Alphabeten ist die Chance recht groß, dass es früh zu einer Nichtübereinstimmung kommt. In dem Fall einer Nichtübereinstimmung wird dann mittels Match- und
Occurrence Heuristik das Pattern bis zur nächsten sinnvollen Ausrichtung über den Eingabetext geschoben (siehe [BM77] und [Par03]).
Wir wollen die von Hoorspool [Hor80] vorgeschlagene Variante des Boyer-Moore Algorithmus betrachten. Sie eignet sich besonders für große Alphabete und benutzt nur die
Occurrence-Heuristik. Der Boyer-Moore-Horspool Algorithmus arbeitet folgendermaßen:
In der Vorverarbeitungsphase wird die Bad Character Funktion“ B(x) für das Pattern
”
p = [1..m] berechnet. Sie gibt an, um wie viel das Pattern bei einer Nichtübereinstimmung verschoben werden kann:
Abbildung 17: Verschiebung bei Nichtübereinstimmung
Die Bad Character Funktion“ ist definiert als Abstand des letzten Auftretens von x
”
in p[1..m − 1] zum Ende des Patterns: B(x) = min {h|p[m − h] = x, h ≥ 1}.
Kommt x nicht in p[1..m − 1] vor, so setzten wir B(x) = m.
36
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Abbildung 18: Kommt das a nicht im Pattern vor, so können wir das Pattern bis zu
dem ersten Zeichen nach a im Eingabetext verschieben.
In der Suchphase positionieren wir das Pattern über dem Eingabetext und vergleichen
zuerst das letzte Zeichen vom Pattern p[m] gegen den Eingabetext. Kommt es dabei
zu einer Nichtübereinstimmung, so verschieben wir das Pattern gegen den Eingabetext
um B(p[m]) Positionen nach Rechts. Andernfalls überprüfen wir die restlichen Zeichen
des Patterns gegen den Eingabetext. Bei einer Nichtübereinstimmung verschieben wir
ebenfalls das Pattern um B(p[m]) Positionen nach rechts.
Um diesen Algorithmus jetzt für das Multi Pattern Matching zu verwenden, benötigen
wir die Vorraussetzung, dass alle Pattern dieselbe Länge haben. Dies erreichen wir, indem
wir (wie schon beim Wu-Manber Algorithmus) nur die ersten lmin Zeichen jedes Patterns
verwenden, mit lmin = min {lp1 , · · · , lpr }.
Die Pattern werden anschließend in überlappende q-Gramme zerlegt. Aus diesen u =
lmin − q + 1 q-Grammen pro Pattern werden nun u Tabellen erstellt, wobei die erste
Tabelle alle q-Gramme enthält, die an erster Position in einem der Pattern auftauchen.
Die zweite Tabelle enthält alle q-Gramme, die in einem der Pattern an erster oder an
zweiter Position auftreten. Die dritte Tabelle alle q-Gramme, die an erster, zweiter oder
dritter Stelle in einem der Pattern auftreten, usw. Abbildung 19 zeigt die 2-Gramm
”
Tabellen“ für die Pattern HAUS, HAND, MIAU“.
”
1. Tabelle
HA
MI
2. Tabelle
HA
MI
AU
AN
IA
3. Tabelle
HA
MI
AU
AN
IA
US
ND
Abbildung 19: 2-Gramm Tabellen“ für die Pattern HAUS, HAND, MIAU“
”
”
Diese Tabellen kann man nun folgendermaßen als Filteralgorithmus verwenden:
Wir betrachten jeweils m Zeichen des Eingabetextes, sozusagen als Sliding Window. Wir
vergleichen nun, ob das rechteste q-Gramm im Sliding Window in der u-ten Tabelle vorkommt. Ist dies nicht der Fall, so kommt dieses q-Gramm in keinem der Pattern an
irgendeiner Position vor, wir können also die nächsten u Positionen überspringen.
Andernfalls schauen wir nach, ob das 2t-rechteste q-Gramm in der u − 1-ten Tabelle vorkommt. Ist dies nicht der Fall, so kommt das q-Gramm in keinem der Pattern an Position
1, · · · , u − 1 vor, wir können also um u − 1 Positionen nach rechts verschieben.
Sind wir bei der ersten Tabelle angelangt, und kommt das linkeste q-Gramm im Sliding
Window in dieser Tabelle vor, so haben wir einen Kandidaten für ein mögliches Auftreten
eines Patterns gefunden. Wie in Schritt 3 beschrieben, wendet man nun einen Single Pattern Algorithmus an, um zu überprüfen, ob an dieser Stelle wirklich ein Auftreten eines
37
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
der Pattern vorliegt.
Beispiel:
Eingabetext = DAUSIMIAUH
Pattern={HAUS, HAND, MIAU}
q = 2, die zugehörigen 2-Gramm Tabellen sind in Abbildung 19 dargestellt
Abbildung 20: Suchphase des Boyer-Moore-Horspool mit q-Grammen
38
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.6 Multiple Approximate String Matching with l-Grams
Dieser 2004 von Fredriksson und Navarro in [FN04] vorgeschlagene Algorithmus verwendet die im vorherigen Kapitel definierten fortlaufenden q-Gramme. Er wurde konstruiert,
um das Multiple Approximate String Matching Problem zu lösen, also die approximative
Suche nach mehreren Pattern. Da die exakte Suche nach mehreren Pattern ein Sonderfall
(k = 0) der approximativen Suche von mehreren Pattern mit maximal k Unterschieden
ist, lässt sich der Algorithmus auch für unser Problem verwenden.
Zuerst wollen wir formal definieren, was k Unterschiede sind:
Definition:
Ein Tupel (r, s) ∈ Σ0 × Σ0 mit r 6= s heißt einfache Edit-Operation, Σ0 := Σ ∪ {}
Es gibt drei Arten von einfachen Edit-Operationen:
1) gilt r = so heißt (r, s) Einfügung.
2) gilt s = so heißt (r, s) Löschung.
3) Andernfalls heißt (r, s) Substitution.
Man schreibt auch r → s für die einfache Edit-Operation (r, s).
Definition:
2 Worte a ∈ Σ∗ , b ∈ Σ∗ haben maximal k Unterschiede, wenn a mit maximal k
einfachen Edit-Operationen in b überführt werden kann.
a und b haben genau k Unterschiede wenn a mit maximal k einfachen Edit-Operationen
in b überführt werden kann und es kein n < k gibt, so dass a mit maximal n einfachen
Edit-Operationen in b überführt werden kann.
Schreibweise: #U nterschiede (a, b) = k
Beispiel:
a = HUNDE
b = HAND
Σ = {A, D, E, H, N, U }
a kann mit 2 einfachen Edit-Operationen in b überführt werden:
(U,A)
HUNDE → HANDE
(E,)
HANDE → HAND
4.6.1 Vorverarbeitung
Sei q ein fester Wert. Als erstes Zerlegen wir alle Pattern in fortlaufende q-Gramme und
bilden die Menge Gram, welche alle dieser dabei entstehenden q-Gramme enthält.
Anschließend berechnen wir die Funktion D : Σq → N für jedes mögliche q-Gramm.
Die Funktion D wird auch Distanz-Tabelle genannt. D(g), g ∈ Σq soll dabei die minimale Anzahl an Unterschieden des q-Gramm g zu einem beliebigen q-Gramm aus Gram sein:
D(g) := min {#U nterschiede (g, h)|h ∈ Gram}
39
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.6.2 Suchphase
Es sei m = min {|p1 |, · · · , |pr |} die kleinste gemeinsame Länge aller Pattern.
Wir betrachten ein Lesefenster der Länge m − k über dem Eingabetext. Für jede Position i + 1, · · · , i + m − k des Lesefensters lesen wir sukzessiv fortlaufende q-Gramme ein,
beginnend vom Ende des Lesefensters.
Sei also S1 = T [i + m − k − q + 1..i + m − k] das rechteste q-Gramm im Fenster,
S2 = T [i + m − k − 2q + 1..i + m − k − q] das zweitrechteste,... usw bis Sv = T [i +
m − k − v · q + 1..i + m − k − (v − 1) · q], dem q-Gramm am Anfang des Lesefensters.
Jedes Auftreten eines Patterns, welches am Anfang des Lesefensters beginnt, muss alle
diese q-Gramme vollständig enthalten. Insbesondere bedeutet das, dass diese q-Gramme
S1 , · · · , Sv bis auf k Ausnahmen in Gram enthalten sein müssen. Noch genauer, die Summe der
P Abweichungen zu q-Grammen aus Gram darf k nicht überschreiten, es gilt also:
k ≤ vt=1 D(St ).
Diese Eigenschaft nutzen wir jetzt als Filteralgorithmus aus: Wir lesen sukzessiv fortlaufende q-Gramme beginnend
vom Ende des Lesefensers ein und berechnen die Summe der
P
Abweichungen Mu = ut=1 D(St ). Wird die Summe irgendwann größer als k, dann kann
kein Pattern im Eingabetext vorkommen, denn die Funktion D stellt die minimal notwendige Anzahl an Abweichungen zu beliebigen q-Grammen innerhalb aller Pattern da.
Sobald Mu also größer als k wird, können wir das Lesefenster auf die erste Position, welche
nicht alle der eingelesenen q-Gramme enthält, verschieben. In dem Fall also auf Position
i + m − k − u · q + 2.
Erreicht man beim Einlesen der q-Gramme den Anfang des Lesefensters ohne das Mu
größer als k wird, so hat man einen Kandidaten für das Auftreten eines Patterns gefunden und muss nun mit einem anderen Algorithmus verifizieren, ob wirklich ein Auftreten
eines Patterns vorliegt.
Im Fall k = 0 kann man dies mit einem der bekannten Single Pattern Algorithmen für jedes Pattern einzeln prüfen. Alternativ bietet sich auch, wie es beim Boyer-Moore-Horspool
mit q-Grammen vorgeschlagen wurde, die Verwendung des Karp-Rabin Algorithmus an.
Für k > 0 benötigt man zum Verifizieren eines Treffers einen Algorithmus für das approximative Pattern Matching. Fredriksson und Navarro verwenden dafür den Myers Algorithmus [Mye99] bzw. den Algorithmus von Sellers [Par04].
Beispiel:
p1 = AABC,
p2 = BBAA,
p3 = BCBB
Σ = {A, B, C}
T = CACCBAABCBAC,
k = 0, also keine Approximative Suche!
Vorverarbeitung:
g
Auftreten in Pattern
mit D(g) Unterschieden
AA
0
p1 : AABC
AB
0
p1 : AABC
AC
1
p1 : AABC
BA
0
p2 : BBAA
BB
0
p3 : BCBB
BC
0
p1 : AABC
CA
1
p3 : BCBB
CB
0
p3 : BCBB
CC
1
p3 : BCBB
Tabelle 4: Distanz-Tabelle D für 2-Gramme
40
D(g)
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Suchphase:
Abbildung 21: Suchphase des Algorithmus von Fredriksson und Navarro
41
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
4.6.3 Anmerkung
In dem Beispiel können wir schon erkennen, dass der Algorithmus Multiple Approxi”
mate String Matching with l-Grams“ für kleine Alphabete in der Distanz-Tabelle D nur
sehr wenige Einträge ungleich 0 hat, welche überhaupt zu einem vorzeitigen Verschieben
während der Suchphase führen könnten. In Übereinstimmung mit dieser Beobachtung berichten Salmela, Tariho und Kytöjki in [STK06], dass die Verwendung von fortlaufenden
q-Grammen für das Multi Pattern Matching generell deutlich schlechter ist, als die Verwendung von überlappenden q-Grammen.
Wie der Name Multiple Approximate String Matching with l-Grams“ allerdings schon
”
verrät, wurde dieser Algorithmus nicht für das einfache Multi-Pattern-Matching entworfen, sondern für die approximative Suche. Welche Art von q-Grammen für die approximative Zeichenkettensuche besser geeignet wäre, ist eine andere interessante Fragestellung,
welcher wir an dieser Stelle aber nicht weiter nachgehen werden.
42
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
5. Implementierung
Für die Implementierung stellen sich zwei grundlegende Fragen:
1) In welcher Programmiersprache soll das Programm geschrieben werden?
2) Welcher Algorithmus soll verwendet werden?
Die Frage der Programmiersprache haben wir schon in Kapitel 3.3 betrachtet. Zur Diskussion standen im Wesentlichen drei Varianten:
a) sauberes, objektorientiertes Java
b) dirty“ Java, also teilweise prozedurale Programmierung sowie Verwendung von Arrays
”
anstatt höheren“ Objekten
”
c) C++
Variante a) ist dabei deutlich langsamer und ineffizienter, was die Speicherausnutzung
angeht. Dies wird aus den Messungen in Kapitel 3.3 deutlich.
Da es bereits einige Programme zu verwandten metagenomischen Problemen gibt und diese alle in Java geschrieben sind und da die MHH bereits Erfahrungen mit Java gemacht
hat, fiel die Entscheidung auf die Variante b) obwohl eine Implementierung in C++ nach
den Messergebnissen wahrscheinlich geringfügig effizienter gewesen wäre.
Um zu Entscheiden, welcher Algorithmus am besten für dieses Problem geeignet ist, wollen
wir die Vor- und Nachteile der einzelnen Algorithmen gegenüberstellen:
Laufzeit (Worstcase):
Vorverarbeitung
Suchphase
Speicherbedarf
Filteralgorithmus
AC
WM
BMq
MASM
MKR
O(m)
O(n)
O(m)
Nein
O(m · log(m))
O(m · n)
O(|Σ|B )
Ja
O(m)
O(m · n)
O(m)
Ja
O(m)
O(m · n)
O(|Σ|l )
Ja
O(m)
O(m · n)
O(m)
Ja
Tabelle 5: Übersicht über die verschiedenen Algorithmen.
In Tabelle 5 werden folgende Abkürzungen verwendet:
Abkürzung
AC
WM
BMq
MASM
MKR
Volle Bezeichnung
Aho Corasick
Wu-Manber
Boyer-Moore mit q-Grammen
Multiple Approximate String Matching with l-Grams
Multipattern Karp-Rabin
Von der Laufzeit im Worstcase her schneidet der Aho Corasick am besten ab. Allerdings
wissen wir bereits aus der Theorie, dass der Algorithmus von Wu und Manber, Multiple Approximate String Matching with l-Grams sowie der Boyer-Moore Algorithmus mit
q-Grammen im Mittel schnellere Laufzeiten als im Worstcase haben. Ähnlich wie der
Boyer-Moore-Algorithmus für das Single Pattern Matching sind nämlich alle drei Algorithmen in der Lage, gewisse Positionen ohne Vergleich zu überspringen. Der Multipattern
43
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Karp-Rabin kann zwar keine Positionen überspringen, im Bestcase arbeitet auch er aber
in O(n). Der Aho Corasick hingegen arbeitet sowohl im Worstcase als auch im Bestcase
immer in O(n).
Beim Betrachten des Speicherbedarfes fällt auf, dass dieser bei dem Algorithmus von Wu
und Manber nicht von der Anzahl der Pattern abhängt, sondern nur von der gegebenen Alphabetgröße |Σ| sowie der frei wählbaren Blockgröße B. Dies ist eine sehr positive
Eigenschaft, allerdings muss man auch hier wieder relativieren, da dies nur der Speicherbedarf für die Filterfunktion ist. Findet man mit der Filterfunktion einen Kandidaten
für einen möglichen Treffer, muss anschließend für jedes Pattern einzeln getestet werden,
ob ein Auftreten vorliegt. Für diesen anschließenden Vergleich ist es zwar nicht nötig,
dass sich alle Pattern gleichzeitig im Speicher befinden, in der Praxis wird dies aber aus
Performancegründen vermutlich dennoch der Fall sein. Dasselbe gilt für den Algorithmus
Multiple Approximate String Matching with l-Grams“, hier hängt der Speicherbedarf
”
ebenfalls nicht von der Anzahl der Pattern ab, sondern von der gegebenen Alphabetgröße
|Σ| sowie der frei wählbaren Größe q der q-Gramme.
Betrachten wir also den letzten Wert in der Tabelle, also die Eigenschaft Filteralgorithmus zu sein. Wir haben bei unserem Problem ein kleines Alphabet |Σ| = 4 gegeben, sowie
extrem viele Pattern13 . Filteralgorithmen scheinen dafür auf den ersten Blick vermutlich
ungeeignet zu sein.
Um das genauer zu quantifizieren, wollen wir den Filterwert für eine perfekte Filterfunktion14 bestimmen. Der Filterwert sei definiert als Anzahl der zu filternden Worte geteilt
durch die Anzahl der möglicher Worte.
Es gibt 414 = 268.435.456 verschiedene Worte der Länge 14 in dem zugrundeliegenden Alphabet. Wenn wir davon ausgehen, dass wir 105 = 100.000 verschiedene Pattern suchen,
dann würde eine perfekte Filterfunktion einen Filterwert von gerundet 0.00037 haben.
Wenn wir also zufällig Worte der Länge 14 wählen würden, würde unsere Filterfunktion
99, 963% aussortieren. Für eine Filterfunktion wäre dies ein extrem guter Wert.
Alle vorgestellen Filteralgorithmen haben allerdings die für das Filtern verwendete Patternlänge auf lmin = {l1 , · · · , lm } reduziert. In diesem Fall müssen wir von einer gemeinsamen Mindestlänge von lmin = 8 der Pattern ausgehen (siehe Kapitel 3.1).
Im zugrundeliegenden Alphabet gibt es aber nur 48 = 65.536 verschiedene Worte der
Länge 8. Bei 105 = 100.000 verschiedenen Pattern hätten wir daher mehr Pattern als es
überhaupt Worte gibt.
Stochastisch entspricht die Belegung der 100.000 Pattern auf die 65.535 verschiedenen
Worte der Länge 8 dem Verteilen von k ununterscheidbaren Kugeln auf n Fächer mit
Mehrfachbesetzung. Betrachten wir dies als Binomialverteilung mit Wahrscheinlichkeit n1
und Gegenwahrscheinlichkeit n−1
n , so beträgt die Wahrscheinlichkeit dafür, dass ein Fach
m-fach besetzt ist:
m 1
n
1
n − 1 k−m
·
Bin k,
({m}) =
·
n
k
n
n
13
aktuelle Markersätze bestehen aus etwa 2 · 104 Markern. Bis Ende des Jahres muss bereits mit
Markersätzen von 105 Markern gerechnet werden
14
Eine perfekte Filterfunktion sei in diesem Zusammenhang eine Filterfunktion, welche keine False”
Positive“’s liefert.
44
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Konkret interessiert uns die Wahrscheinlichkeit dafür, dass ein Fach leer bleibt. Denn
übertragen auf unsere Filterfunktion bedeutet ein leeres Fach, dass es zu diesem Wort
der Länge 8 kein Pattern gibt welches gesucht wird, und folglich die Filterfunktion diesen
Wert ohne weitere Vergleich aussortieren (=
ˆ kein Treffer) kann. In unserem Fall ist diese
Wahrscheinlichkeit:
Bin 100.000,
1
65.536
0 65.536
1
65.535 100.000
·
·
0
65.536
65.536
100.000
65.535
=
65.536
≈ 0, 2174
({0}) =
Im Mittel werden also 21, 74% der Fächer leer bleiben. Bei der Filterfunktion würde dies
bedeuten, dass bei zufälligen Worten der Länge 8 nur 21, 74% der Worte aussortiert werden würden. Dies entspricht einem Filterwert von gerundet nur 0, 78.
Berücksichtigt man nunnoch, dass die Anzahl der Pattern in den nächsten Jahren noch
deutlich höher liegen wird und dass dies für eine Filterfunktion ohne False-Positives gerechnet ist, so erkennt man schnell, dass für die gegebenen Problemgrößen Filterfunktionen,
welche auf der kleinsten gemeinsamen Patternlänge basieren, ungeeignet sind.
Da bis auf den Aho Corasick Algorithmus alle vorgestellen Algorithmen auf derartigen
Filterfunktionen basierten folgt alleine aus der Theorie, dass der beste Algorithmus für
dieses Problem der Aho Corasick ist.
5.1 Messungen: Vergleich der Laufzeiten
Nach dem theoretischen Teil folgen nun ein paar Messungen zu Implementierungen von einigen der vorgestellten Algorithmen. Alle Messungen wurden auf einem Laptop mit einem
r
r
Intel
Pentium
M (1,8 Ghz) und 1048 MByte RAM unter Windows XP Professional
mit Service Pack 2 durchgeführt. Verwendet wurde die JavaTM Standard Edition 6 (build
1.6.0 05-b13).
Für die Messungen wurden folgende Algorithmen implementiert:
• Aho Corasick
• vollständiger“ Aho Corasick nach Kapitel 4.3.6
”
• Wu Manber
• Brute Force
In erster Linie interessant ist dabei der Vergleich des Aho Corasick Algorithmus mit dem
vollständigen“ Aho Corasick. Die anderen Algorithmen kommen bei den gegebenen Ein”
gabegrößen und nach der theoretischen Betrachtung für dieses spezielle Problem sowieso
nicht mehr in Frage. Als Vergleichswert habe ich aber dennoch den Algorithmus von Wu
und Manber (Als Referenzwert für die Filteralgorithmen) sowie den Brute Force (als Kontrollgröße) implementiert.
45
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Für den ersten Testlauf wurde ein Markersatz von 33.373 Markern verwendet. Als Probedatei wurde eine 3,09MB große Fasta-Datei genommen, welche 30.000 Reads mit Längen
zwischen 17 und 44 Basenpaaren enthält. Die Fasta-Datei wurde künstlich erzeugt.
Ergebnis mit dem Aho Corasick Algorithmus:
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 30000
Gesamtzahl Treffer: 465 (1,55%)
———————————————–
Benötige Zeit: 6s
davon Vorverarbeitung: 4s
davon Suche: 2s
Ergebnis mit dem vollständigen“ Aho Corasick Algorithmus:
”
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 30000
Gesamtzahl Treffer: 465 (1,55%)
———————————————–
Benötige Zeit: 9s
davon Vorverarbeitung: 6s
davon Suche: 3s
Ergebnis mit Brute Force:
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 30000
Gesamtzahl Treffer: 465 (1,55%)
———————————————–
Benötige Zeit: 926s
Ergebnis mit dem Algorithmus von Wu und Manber für B = 5:
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 30000
Gesamtzahl Treffer: 465 (1,55%)
———————————————–
Benötige Zeit: 1276s
davon Vorverarbeitung: 1120s
davon Suche: 156s
Als erstes fällt auf, dass der Algorithmus von Wu und Manber langsamer arbeitet als der
Brute Force. Der Grund dafür ist, dass die Vorverarbeitung sehr viel Zeit beansprucht. Da
bei diesem Durchlauf sehr viele Marker im Verhältnis zu recht wenigen und sehr kurzen
Reads gegeben waren, fällt dies natürlich besonders auf.
Andererseits muss auch erwähnt werden, dass die Vorverarbeitung sich bestimmt auch
wesentlich eleganter und effizienter implementieren lässt, als es für diese Messung der Fall
46
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
war.
Der Unterschied zwischen dem Aho Corasick Algorithmus und dem vollständigen“ Aho
”
Corasick ist minimal. In der Vorverarbeitungsphase ist der Aho Corasick erwartungsgemäß etwas (2 Sekunden) schneller, weil für den vollständige“ Aho Corasick zusätzlich
”
die Funktion next0 (q, b) für alle q ∈ Q und alle b ∈ Σ berechnet werden muss. Dass der Aho
Corasick auch in der Suchphase minimal (1 Sekunde) schneller ist, überrascht ein bisschen.
Für den zweiten Testlauf wurde wieder der Markersatz mit 33.373 Markern verwendet.
Als Probe-Datei wurde diesmal eine 165MB große Fasta-Datei genommen, welche 138.453
Reads unterschiedlicher Längen enthielt. Die Probe stammt übrigens aus [oCI].
Ergebnis mit dem Aho Corasick Algorithmus:
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 138453
Gesamtzahl Treffer: 0 (0%)
———————————————–
Benötige Zeit: 645s
davon Vorverarbeitung: 4s
davon Suche: 641s
Ergebnis mit dem vollständigen“ Aho Corasick Algorithmus:
”
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 138453
Gesamtzahl Treffer: 0 (0%)
———————————————–
Benötige Zeit: 635s
davon Vorverarbeitung: 6s
davon Suche: 629s
Ergebnis mit Brute Force:
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 138453
Gesamtzahl Treffer: 0 (0%)
———————————————–
Benötige Zeit: 143723s
Ergebnis mit dem Algorithmus von Wu und Manber für B = 5:
———————————————–
Gelesene Marker: 33373
Gelesene Reads: 138453
Gesamtzahl Treffer: 0 (0%)
———————————————–
Benötige Zeit: 31420s
davon Vorverarbeitung: 1120s
davon Suche: 30300s
47
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Bei der größeren Probe ist der vollständige“ Aho Corasick jetzt minimal schneller als der
”
Standard Aho Corasick. Der höhere Aufwand bei der Vorverarbeitung scheint sich also
auszuzahlen.
Auch bei dem Wu-Manber zahlt sich die Vorverarbeitung gegenüber dem Brute Force
jetzt deutlich aus. Wie aus der Theorie zu erwarten war, schneidet der Algorithmus von
Wu und Manber allerdings wesentlich schlechter ab als die Aho Corasick Varianten.
5.2 Details
Als Algorithmus für unsere konkrete Aufgabenstellung wurde also folglich die in Kapitel
4.2.6 vorgestellte Variante des Aho Corasick Algorithmus ausgewählt. Dies war die Verbesserung für kleine Alphabete, bei der die next und g Funktion zu einer neuen Funktion
verschmolzen werden.
Ein weiterer wichtiger Punkt für die Implementierung ist die Verwendung von Arrays.
Wie wir in Kapitel 3.3.1. gesehen haben, ist es bei Arrays sehr Platz- und Zeitaufwendig
diese zu vergrößern. Idealerweise sollten also alle Arrays von Anfang an mit der richtigen
Größe initialisiert werden. Um dies für eine Implementierung des Aho Corasick Algorithmus erreichen zu können, muss man beim Initialisieren wissen, wie viele Knoten der
zugehörige Trie haben wird. Wir wollen dies abschätzen, indem wir als ersten Schritt der
Vorverarbeitungsphase alle Pattern einlesen, und dabei folgende Mengen konstruieren:
P ati := {p | p ∈ Π, |p| ≥ i}
Aus diesen Mengen kann man exakt berechnen, wie viele Knoten der Trie im Aho Corasick Automaten haben wird. Allerdings stehen wir weiterhin vor demselben Problem, weil
wir für die Konstruktion dieser Mengen die Elemente wieder in einem Array speichern
müssten ohne vorher zu wissen, wie viele Elemente diese Mengen haben werden.
Als Näherung wollen wir daher nicht die Mengen P ati , sondern die entsprechend definierten Multimengen15 P atmi := {p | p ∈ Π, |p| ≥ i}b betrachten. Die Anzahl der Elemente
dieser Multimengen lässt sich durch einfaches Zählen ermitteln, da man aufgrund der Multimengeneigenschaft nicht kontrollieren muss, ob Elemente doppelt vorkommen. Somit ist
ein Speichern der Elemente für das Errechnen der Anzahl nicht notwendig.
Wir wissen bereits aus der Aufgabenstellung, dass nur Pattern mit einer Länge von maximal 14 Basenpaaren vorkommen. Die (|P atm1 |, · · · , |P atm14 |) können also in O(m) mit
O(1) Speicherbedarf berechnet werden.
Da der maximale Verzweigungsgrad im Trie |Σ| = 4 beträgt, gilt, dass die maximale Anzahl Knoten der Tiefe i im Trie 4i ist. Wir können also die Gesamtanzahl Knoten im Trie
des Aho Corasick folgendermaßen abschätzen:
AnzahlKnoten := 1;
for (i := 1; i ≤ 14; i + +)
begin
AnzahlKnoten + = min(|P atmi |, 4i );
end
15
Der Unterschied einer normalen Menge zu einer Multimenge besteht darin, dass Elemente in einer
Multimenge mehrfach vorkommen können. Wir bezeichnen Multimengen mit einem Index b (englisch
Bag“) um sie von normalen Mengen unterscheiden zu können.
”
48
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
5.3 Messungen zur Implementierung
Nachdem wir uns nun für den Ansatz vollständiger“ Aho Corasick entschieden haben,
”
wollen wir einige Messungen zum konkreten Speicherbedarf und zur Laufzeit unserer Implementierung machen. Die Messungen wurden alle mit der Final Version der Implementierung gemacht, daher weichen die Laufzeiten etwas von denen in Kapitel 5.2 ab.
r
r
Alle Messungen wurden wieder auf einem Laptop mit einem Intel
Pentium
M (1,8
Ghz) und 1048 MByte RAM unter Windows XP Professional mit Service Pack 2 durchgeführt. Verwendet wurde JavaTM Standard Edition 6 (build 1.6.0 05-b13).
Gemessen wurde die Laufzeit in Abhängigkeit von der Größe der Probedatei. Als Marker
wurde für alle Messungen ein Markersatz von 1734 Markern verwendet. Als Probedatei
wurde eine selbsterzeugte Fasta-Datei verwendet. Um keinen zu großen Overhead durch
den Fastaheader zu haben, bestand die Fasta-Datei nur aus einem Read. Der Read war
eine zufällige Folge von Zeichen aus dem Alphabet Σ = {A, C, G, T }.
Dateigröße
10 MB
20 MB
30 MB
40 MB
50 MB
60 MB
70 MB
80 MB
90 MB
100 MB
Laufzeit
1 Sekunden
2 Sekunden
4 Sekunden
5 Sekunden
6 Sekunden
8 Sekunden
9 Sekunden
11 Sekunden
12 Sekunden
14 Sekunden
Tabelle 6: Laufzeiten
In Abbildung 22 ist das Ganze grafisch dargestellt. Man kann erkennen, dass sich die
tatsächlich gemessene Laufzeit linear zur größe der Probendatei verhält.
49
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Abbildung 22: Laufzeit
Um den Speicherbedarf zu bestimmen wurde folgende Messung durchgeführt. Gemessen
wurde dabei, wie viele Marker bei festgesetzten Arbeitsspeicher in einem Durchlauf mit
der Implementierung gesucht werden können. Die Marker wurden zufällig erzeugt und
hatten alle eine Länge von 14 Basenpaaren.
verfügbarer Speicher
4MB
8MB
32MB
64MB
96MB
128MB
160MB
192MB
224MB
256MB
Marker
23.000 Marker
30.000 Marker
135.000 Marker
275.000 Marker
436.000 Marker
598.000 Marker
759.000 Marker
920.000 Marker
1.088.000 Marker
1.282.000 Marker
Tabelle 7: Speicherbedarf
In Abbildung 23 ist sind die Werte aus Tabelle 7 wieder grafisch dargestellt. Man kann
gut erkennen, dass sich der Speicherbedarf ebenfalls linear zur Anzahl der Marker verhält.
Abbildung 23: Speicherbedarf
50
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
5.4 Ergebnisse
Um den ganzen Ansatz des Short Oligo Alignments“ zu testen, sowie die Arbeitsweise
”
und Geschwindigkeit des Programms in der Praxis zu erproben, wurden im Folgenden einige Tests gemacht. Verwendet wurde dazu jeweils ein Markersatz mit 19.835 eindeutigen
Markern zu insgesamt 207 verschiedenen Bakterien.
Im ersten Testlauf wurden die Genome von 3 Bakterien zu Grunde gelegt und daraus
künstlich eine metagenomische Probe erstellt. Folgende Bakterien wurden dafür ausgewählt:
1. Mycobacterium ulcerans Agy99 (NC 00861116 )
2. Enterobacter sakazakii ATCCBAA-894 (NC 009778)
3. Pseudomonas entomophila L48 (NC 008027)
Die verwendete Probendatei war insgesamt 52,3 MB groß und enthielt 300.000 Reads mit
Längen zwischen 80 und 130 Basenpaaren.
Ergebnis des Programms:
NC 008611 (390 markers) :2904 hits (7,45 hits/marker)
NC 008027 (121 markers) :1705 hits (14,09 hits/marker)
NC 007760 (237 markers) :1627 hits (6,86 hits/marker)
NC 009079 (218 markers) :427 hits (1,96 hits/marker)
NC 005835 (400 markers) :402 hits (1 hits/marker)
NC 006834 (293 markers) :304 hits (1,04 hits/marker)
NC 009778 (24 markers) :285 hits (11,88 hits/marker)
NC 009675 (139 markers) :264 hits (1,9 hits/marker)
NC 007604 (104 markers) :243 hits (2,34 hits/marker)
NC 002929 (500 markers) :218 hits (0,44 hits/marker)
NC 008513 (210 markers) :212 hits (1,01 hits/marker)
NC 008786 (500 markers) :212 hits (0,42 hits/marker)
NC 002607 (35 markers) :196 hits (5,6 hits/marker)
NC 000911 (79 markers) :184 hits (2,33 hits/marker)
NC 009767 (500 markers) :145 hits (0,29 hits/marker)
NC 007606 (500 markers) :142 hits (0,28 hits/marker)
NC 009484 (26 markers) :140 hits (5,38 hits/marker)
NC 007204 (500 markers) :122 hits (0,24 hits/marker)
NC 008340 (313 markers) :112 hits (0,36 hits/marker)
NC 002947 (54 markers) :110 hits (2,04 hits/marker)
NC 009523 (175 markers) :104 hits (0,59 hits/marker)
NC 002945 (10 markers) :97 hits (9,7 hits/marker)
NC 010175 (274 markers) :82 hits (0,3 hits/marker)
NC 007613 (338 markers) :68 hits (0,2 hits/marker)
NC 007626 (24 markers) :66 hits (2,75 hits/marker)
NC 004344 (281 markers) :64 hits (0,23 hits/marker)
NC 009456 (226 markers) :58 hits (0,26 hits/marker)
NC 005126 (500 markers) :57 hits (0,11 hits/marker)
NC 002978 (470 markers) :55 hits (0,12 hits/marker)
16
NC XXXXXX ist eine Bezeichnung vom NCBI [CfBI]. NC steht dabei für komplettes Genom“, die
”
Zahl XXXXXX ist eine Identifikationsnummer
51
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
NC 002771 (424 markers) :48 hits (0,11 hits/marker)
NC 003116 (500 markers) :47 hits (0,09 hits/marker)
...
——————————————————–
Markers read: 19835
Unique markers: 19835 (100%)
Reads read: 300000
Total hits: 12075 (4,02%)
Parameters
Min. difference to other hits (per read): 2
Max. hits to second most common taxon: 10
Allowed maximum number of other taxa: 100
Der Durchlauf dauerte im Mittel 4 Sekunden pro 10MB. Insgesamt wurden inklusive
Vorverarbeitung und anschließender Sortierung nach Anzahl der Treffer 24 Sekunden
benötigt.
Sehr gut zu erkennen ist, dass die enthaltenen Bakterien alle unter den ersten 7 Bakterien
mit den meisten Treffern liegen. Des Weiteren kann man erkennen, dass bei den enthaltenen Bakterien die Anzahl der Treffer pro Marker höher ist als bei den nicht enthaltenen.
Richtig eindeutig ist das Ergebnis dennoch nicht, NC 007760 mit 1627 Treffern und 6,86
Treffern pro Marker würde in diesem Fall wohl ebenfalls als enthalten“ gewertet werden.
”
Enterobacter sakazakii ATCCBAA-894 (NC 009778) hingegen taucht etwas weiter hinten
in der Statistik auf, einfach weil es verhältnismässig wenige Marker für dieses Bakterium
gibt. Die Anzahl Treffer pro Marker ist hingegen ähnlich hoch wie bei den anderen beiden
enthaltenen Bakterien.
Das Ganze könnte man noch etwas optimieren, indem man neben der Statistik über die
Hits auch noch eine Statistik zu den Markern erstellt. Bei einem erneuten Durchlauf wurde zusätzlich bestimmt, wie viele der Marker gefunden wurden und wie viele nicht. Der
Durchlauf dauerte damit im Mittel 7 Sekunden pro 10MB. Inklusive Vorverarbeitung und
anschließender Sortierung brauchte der gesamte Durchlauf 43 Sekunden. Das Ergebnis
sah diesmal folgendermaßen aus:
NC 008611 (390 markers) :2904 hits (7,45 hits/marker)
markers found: 390 markers not found: 0
NC 008027 (121 markers) :1705 hits (14,09 hits/marker)
markers found: 121 markers not found: 0
NC 007760 (237 markers) :1627 hits (6,86 hits/marker)
markers found: 235 markers not found: 2
NC 009079 (218 markers) :427 hits (1,96 hits/marker)
markers found: 173 markers not found: 45
NC 005835 (400 markers) :402 hits (1 hits/marker)
markers found: 241 markers not found: 159
NC 006834 (293 markers) :304 hits (1,04 hits/marker)
markers found: 224 markers not found: 69
NC 009778 (24 markers) :285 hits (11,88 hits/marker)
markers found: 24 markers not found: 0
NC 009675 (139 markers) :264 hits (1,9 hits/marker)
52
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
markers found: 120 markers not found: 19
NC 007604 (104 markers) :243 hits (2,34 hits/marker)
markers found: 96 markers not found: 8
NC 002929 (500 markers) :218 hits (0,44 hits/marker)
markers found: 212 markers not found: 288
Die Verbesserung ist deutlich zu erkennen: Alle Bakterien die vorher noch als FalsePositives auftraten, haben Marker welche nicht gefunden wurden. Da die Marker so definiert wurden, dass sie im Genom mindestens alle 10.000 Basenpaare einmal vorkommen,
ist davon auszugehen, dass alle Marker gefunden werden sollten, wenn das Genom enthalten ist und mit einer ausreichenden Abdeckung sequenziert wurde.
Als Blindprobe lassen wir das Programm mit demselben Markersatz gegen eine Datei
Zufall.fas“ laufen. Diese Datei enthält 300.000 Reads der Länge 80, wobei jeder Read
”
eine zufällige Folge von Zeichen aus dem Alphabet Σ = {A, C, G, T } darstellt. Insgesamt
ist diese Datei 27,1 MB groß. Das Ergebnis sah folgendermaßen aus:
NC 005835 (400 markers) :109 hits (0,27 hits/marker)
markers found: 112 markers not found: 288
NC 003116 (500 markers) :69 hits (0,14 hits/marker)
markers found: 91 markers not found: 409
NC 005364 (500 markers) :61 hits (0,12 hits/marker)
markers found: 62 markers not found: 438
NC 007204 (500 markers) :52 hits (0,1 hits/marker)
markers found: 102 markers not found: 398
NC 010296 (291 markers) :48 hits (0,16 hits/marker)
markers found: 70 markers not found: 221
NC 009488 (500 markers) :48 hits (0,1 hits/marker)
markers found: 73 markers not found: 427
NC 007109 (371 markers) :47 hits (0,13 hits/marker)
markers found: 60 markers not found: 311
NC 002978 (470 markers) :47 hits (0,1 hits/marker)
markers found: 89 markers not found: 381
NC 002929 (500 markers) :38 hits (0,08 hits/marker)
markers found: 67 markers not found: 433
NC 008340 (313 markers) :37 hits (0,12 hits/marker)
markers found: 53 markers not found: 260
NC 007606 (500 markers) :37 hits (0,07 hits/marker)
markers found: 68 markers not found: 432
NC 005126 (500 markers) :34 hits (0,07 hits/marker)
markers found: 77 markers not found: 423
NC 008786 (500 markers) :34 hits (0,07 hits/marker)
markers found: 58 markers not found: 442
NC 009767 (500 markers) :33 hits (0,07 hits/marker)
markers found: 73 markers not found: 427
...
——————————————————–
Markers read: 19835
53
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Unique markers: 19835 (100%)
Reads read: 300000
Total hits: 1818 (0,61%)
Parameters
Min. difference to other hits (per read): 2
Max. hits to second most common taxon: 10
Allowed maximum number of other taxa: 100
Der Durchlauf dauerte diesmal im Mittel 6 Sekunden pro 10MB, insgesamt wurden inklusive Vorverarbeitung und anschließender Sortierung nach der Anzahl der Treffer 21
Sekunden benötigt.
Wie zu erwarten war, ist ein gewisses Hintergrundrauschen“ zu erkennen. Es gibt aber
”
kein Bakterium von dem alle Marker gefunden wurden. Auch von der Anzahl der Hits
gibt es keine großen Unterschiede zwischend den unterschiedlichen Bakterien.
Als letztes wollen wir den obigen Markersatz noch mal an einer echten metagenomischen
Probe testen. Verwendet haben wir diesmal wieder einen Markersatz aus 19.835 Markern
zu insgesamt 207 verschiedenen Bakterien. Die Probedatei ist 150MB groß und besteht aus
121.590 Reads mit Längen von jeweils etwa 800 - 1200 Basenpaaren. Die Probe wurde im
Rahmen vom Global Ocean Sampling Project“ 17 des J. Craig Venter Institute (JCVI)
”
dem Golf von Mexiko entnommen. Die Sequenzierten Daten werden vom Community
Cyberinfrastructure for Advanced Marine Microbial Ecology Research and Analysis (CAMERA) zur Verfügung gestellt18 .
NC 004344 (281 markers) :4191 hits (14,91 hits/marker)
markers found: 281 markers not found: 0
NC 009714 (238 markers) :2088 hits (8,77 hits/marker)
markers found: 238 markers not found: 0
NC 008513 (210 markers) :2061 hits (9,81 hits/marker)
markers found: 210 markers not found: 0
NC 009635 (234 markers) :1709 hits (7,3 hits/marker)
markers found: 218 markers not found: 16
NC 007716 (273 markers) :1040 hits (3,81 hits/marker)
markers found: 270 markers not found: 3
NC 008599 (155 markers) :772 hits (4,98 hits/marker)
markers found: 143 markers not found: 12
NC 004432 (178 markers) :469 hits (2,63 hits/marker)
markers found: 176 markers not found: 2
NC 004829 (221 markers) :384 hits (1,74 hits/marker)
markers found: 166 markers not found: 55
NC 004342 (178 markers) :281 hits (1,58 hits/marker)
markers found: 129 markers not found: 49
NC 009883 (207 markers) :258 hits (1,25 hits/marker)
markers found: 107 markers not found: 100
NC 005364 (500 markers) :250 hits (0,5 hits/marker)
17
18
siehe [SGD+ 07] und [VRHea04]
siehe [fAMMERC]
54
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
markers found: 342 markers not found: 158
NC 007797 (500 markers) :221 hits (0,44 hits/marker)
markers found: 283 markers not found: 217
NC 007294 (299 markers) :221 hits (0,74 hits/marker)
markers found: 209 markers not found: 90
NC 002528 (50 markers) :199 hits (3,98 hits/marker)
markers found: 48 markers not found: 2
NC 009488 (500 markers) :167 hits (0,33 hits/marker)
markers found: 285 markers not found: 215
NC 002771 (424 markers) :167 hits (0,39 hits/marker)
markers found: 294 markers not found: 130
NC 005835 (400 markers) :135 hits (0,34 hits/marker)
markers found: 194 markers not found: 206
NC 006570 (75 markers) :117 hits (1,56 hits/marker)
markers found: 66 markers not found: 9
NC 003116 (500 markers) :113 hits (0,23 hits/marker)
markers found: 221 markers not found: 279
NC 002978 (470 markers) :107 hits (0,23 hits/marker)
markers found: 164 markers not found: 306
NC 010296 (291 markers) :104 hits (0,36 hits/marker)
markers found: 142 markers not found: 149
...
——————————————————–
Markers read: 19835
Unique markers: 19835 (100%)
Reads read: 121590
Total hits: 18430 (15,16%)
Parameters
Min. difference to other hits (per read): 3
Max. hits to second most common taxon: 10
Allowed maximum number of other taxa: 100
Der Durchlauf dauerte diesmal im Mittel 6 Sekunden pro 10MB, insgesamt wurden inklusive Vorverarbeitung und anschließender Sortierung nach der Anzahl der Treffer 111
Sekunden benötigt.
Ähnlich wie bei dem künstlich erzeugten Metagenom scheint aber wieder eine kleine Gruppe von Bakterien sehr viele Hits zu haben. Einen Kontrollwert welche Bakterien jetzt
wirklich in der Probe waren und welche nicht, gab es auf Stammesebene (NC-Nummer)
leider nicht. Daher ist es an dieser Stelle schwer, eine Aussage zum biologischen Wert
dieser Ergebnisse zu machen.
Wichtig für uns soll an dieser Stelle sein, dass der Ansatz funktioniert und das die Implementierung in sehr kurzer Zeit Ergebnisse liefert.
55
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
6. Weitere Einsatzgebiete
Neben der Analyse von metagenomischen Proben gibt es noch weitere Einsatzgebiete,
bei denen die vorgestellten Algorithmen und das für dieses konkrete Problem entwickelte Programm verwendet werden können. So sind beispielsweise Resequenzierungsprojekte
ein mögliches weiteres Einsatzgebiet.
Bei der Resequenzierung geht es darum, dass bereits bekannte und sequenzierte Bakterien
noch mal neu sequenziert werden. Der Grund für eine erneute Sequenzierung ist, dass oftmals die Genome zwar bekannt sind, wenn man genauer hinschaut, diese aber trotzdem
noch einige unbekannte Stellen enthalten.
Ein anderer Grund dafür ist, dass sich die Bakterien auch im Laufe der Zeit verändern.
Es entstehen spontane Mutationen, manchmal bauen die Bakterien auch fremde DNA in
ihre eigene mit ein.
Untersucht man die Bedeutung der einzelnen Gene im Bakterium ist es sinnvoll, dies zu
Resequenzieren um sicher zu gehen, dass das Bakterium auch das Genom besitzt von
dem man ausgeht. Es kann nämlich vorkommen, dass ein Labor Bakterien von einem
Referenzstamm besitzt, die ursprünglich das bekannte Genom besessen haben. Mit der
Zeit können sich aber durch das immer wieder neue Vermehren der Bakterien im Labor
kleine Veränderungen in das Genom eingeschlichen haben, so dass dies nicht mehr dem
des Referenzstamm entspricht.
Anstatt nun mit den aufwendigeren, teureren und älteren Sanger Sequenzierungsmethoden
längere Reads zu produzieren und daraus das Genom noch mal komplett zusammenzusetzen kann man auch hier die moderneren Sequenzierungsmethoden einsetzen. Dazu nimmt
man das früher sequenzierte Genom als Vorlage und sortiert erst mal alle Reads raus, die
sich im früheren Genom wiederfinden lassen.
Die übriggebliebenden Reads sind dann die Lücken bzw. die neue DNA. Mit diesen Reads
kann dann anschließend weiter gearbeitet werden.
Es gibt 2 Varianten, wie man die bisherige Implementierung im Aufgabenfeld der Resequenzierung anwenden kann:
1. Man verwendet die kompletten Reads aus dem Resequenzierungsprojekt anstatt
der Marker als Pattern und anstatt einer metagenomische Probe verwendet man
als Eingabetext das ursprüngliche Genom, welches resequenziert werden soll. Man
sucht also die kompletten Reads in dem ursprünglichen Genom um damit die bereits
bekannte Information aus den Reads herauszufiltern.
Der Vorteil dieser Methode ist, dass man die bisherige Implementierung im Wesentlichen nur an einer Stelle abändern muss, nämlich dass auch Pattern mit einer
größeren Länge als 14 gesucht werden können.
Der Nachteil ist natürlich, dass je länger der Reads ist, die Wahrscheinlichkeit größer
wird, dass der Read einen oder mehrere Sequenzierungsfehler enthält und sich somit
nicht im ursprünglichen Genom wiederfinden lässt.
Daher würde es sich für diese Variante anbieten, nicht den Aho Corasick Algorithmus zu verwenden, sondern stattdessen einen approximativen Multi Pattern Algorithmus wie beispielsweise den in Kapitel 4.6 vorgestellten Multiple Approximate
String Matching with l-Grams von Fredriksson und Navarro.
Will man dennoch exakt suchen, so sind für dieses Problem ebenfalls die vorgestellten Filteralgorithmen sinnvoll, weil hier als Pattern die Reads verwendet werden.
56
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Die Reads haben zwei angenehme Eigenschaften, welche für die Anwendung von
Filteralgorithmen hilfreich sind:
• Sie haben alle dieselbe Länge
• Sie sind deutlich länger als die beim Short Oligo Alignment verwendeten Marker
2. Man zerlegt jeden Read in mehrere überlappende q-Gramme und verwendet diese
dann als Marker für den zugehörigen Read. Anschließend zerlegt man das ursprüngliche Genom künstlich in Reads.
Jetzt kann man mit den vorgestellten Algorithmen alle Auftreten der Marker in
den künstlich erzeugten Reads suchen. Dabei ist dann allerdings nicht das Ziel,
dem künstlich erzeugten Read eindeutig einen Read aus der Resequenzierung zuzuordnen, sondern es sollen die Reads der Resequenzierung den künstlichen Reads
zugeordnet werden. Die bisherige Implementierung müsste also so abgeändert werden, dass Mehrfachzuordnungen möglich sind.
Nach dem Suchlauf würden dann in einem zweiten Schritt alle Reads der Resequenzierung herausgesucht, welche keinem der künstlichen Reads zugeordnet werden
konnten. Diese Reads enthalten die durch die Resequenzierung gewonnenen neuen
Informationen gegenüber dem ursprünglichen Genom.
Zu beachten ist, dass die Marker in diesem Fall nicht mehr eindeutig sind. Die bisherige Implementierung müsste also auch dafür angepasst werden.
Der Vorteil dieser Methode ist, dass kurze Marker verwendet werden und man sich
somit auf die exakte Suche beschränken kann.
Tatsächlich angewendet wurde der zweite Ansatz. In dem konkreten Fall waren die Reads
der Resequenzierung bereits in 2 Dateien aufgeteilt. Die erste Datei enthielt alle Reads, die
mittels eines unbekannten Algorithmus19 dem original Genom zugeordnet werden konnten.
Die zweite Datei enthielt 267.155 Reads, welche laut GATC20 nicht im original Genom zu
finden waren.
Bei genauerem Betrachten der Reads fiel auf, dass ein bestimmter Typ von Sequenzierungsfehlern scheinbar öfter vorkam. Und zwar kamen in den Reads immer wieder Sequenzen von nur einem Basenpaar vor, der Anfang und das Ende des Reads ließen sich
jedoch im Genom wiederfinden. Es ist also anzunehmen, dass dies ein technischer Fehler
bei der Sequenzierung ist.
Abbildung 24: Sequenzierungsfehler in einem Read.
Blau ist das original Genom, schwarz der Read.
Approximative Algorithmen können schlecht mit solchen blockweise auftretenden Fehlern
umgehen, da jede Abweichung als neuer Fehler gezählt wird. Das Short Oligo Alignment
jedoch findet am Anfang und am Ende des Reads Sequenzen, die sich dem original Genom
zuordnen lassen. Daher wurde der zweite Ansatz gewählt.
19
Die Resequenzierung wurde vom GATC durchgeführt. Welche Algorithmen für die Zuordnung der Reads
verwendet wurden, wird vom GATC nicht bekannt gegeben.
20
[Kob]
57
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Gegeben waren insgesamt 267.155 Reads mit jeweils einer Länge von 34 Basenpaaren.
Durch einen vorgeschalteten Algorithmus wurden erst mal 50.737 Reads als Trash“ 21
”
aussortiert.
Von den verbliebenden 216.418 Reads konnten mit diesem Ansatz 82.293 dem original
Genom zugeordnet werden. 114.611 Reads ließen sich weiterhin nicht zuordnen. Diese
Reads enthalten vermutlich die von dem original Genom abweichende DNA.
21
Aussortiert wurden im Wesentlichen alle Reads, die zu viele N’s also nichtidentifizierte Basen enthielten
58
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
7. Fazit
Die Metagenomik ist ein sehr aktuelles Forschungsgebiet, welche große Herausforderungen
an die Bioinformatik stellt. Durch die neuen Sequenziertechniken und die dadurch immer
geringer werdenden Kosten wird der Einsatz von Sequenzierungsprojekten in immer mehr
Aufgabenbereichen möglich. Die Vorteile sind gewaltig, die anfallende Datenmenge allerdings auch.
Bisher gibt es nur sehr wenige Programme, die dafür geeignet sind und alle haben ihre Probleme. Die erfolgversprechensten Ansätze bisher benötigen beispielsweise eine sehr
rechenintensive Vorverarbeitung, welche meist nur auf High-Performance Rechenclustern
ausgeführt werden können (z.B. MEGAN, siehe [HAQS07]).
Daher ist es wichtig, Programme zu entwickeln, die mit diesen Datenmengen umgehen
können. Geeignete Algorithmen aus dem Bereich der Zeichenkettensuche gibt es genug.
Die Herausforderung besteht einerseits darin, den effizientesten Algorithmus für die jeweilige Problemstellung zu finden und ihn auf das konkrete Problem anzupassen und
andererseits darin, den Algorithmus dann auch effizient zu implementieren.
Gerade die vielen vorgegebenen Datenstrukturen einer Programmiersprache, sowie ein
Programmieren unter strikten Paradigmen wie zum Beispiel strenger Objektorientierung,
können dazu führen, dass effiziente Algorithmen nur durch die Implementierung langsam
werden.
In dem konkreten Fall dieser Diplomarbeit wurde für die Untersuchung einer metagenomischen Probe mittels Short Oligo Alignment mit dem Aho Corasick Algorithmus eine
sehr effiziente Lösung gefunden und implementiert. Aktuelle Datensätze können in einigen
Minuten damit durchsucht werden.
Ein schnelles Suchprogramm, wie es im Rahmen dieser Arbeit entwickelt wurde, ist jedoch nur der Anfang. Das Hauptproblem bei der Analyse von metagenomischen Proben
mittels Short Oligo Alignment stellen zur Zeit die Markersätze, sowie die daraus resultierende Interpretation der erhaltenen Ergebnisse dar. So sind die bisherigen Markersätze im
Hinblick auf die Anzahl der Marker pro Bakterium alle sehr unterschiedlich. Für einige
Bakterien gibt es sehr viele Marker, für andere hingegen fast gar keine. Auch die Qualität
der einzelnen Marker scheint sich sehr stark zu unterscheiden. So sind einige Marker wirklich für nur ein Bakterium charakteristisch, während andere scheinbar in verschiedensten
Genomen recht oft vorkommen. Aufgrund der Tatsache, dass es aber noch sehr viele nicht
sequenzierte oder sogar unbekannte Bakterien gibt, ist es sehr schwierig zu bestimmen,
wie charakteristisch“ ein bestimmter Marker ist.
”
Zur Zeit werden verschiedene Ansätze effizientere Markersätze zu erhalten von Dr. Oleg
N. Reva 22 untersucht und ausgetestet.
Dennoch scheint der Ansatz des Short Oligo Alignments sehr erfolgversprechend zu sein.
In Abbildung 25 ist die Anzahl der Treffer für die jeweils 13 am häufigsten in den Proben
vorkommenden Bakterien für die drei Durchläufe aus Kapitel 5.4 grafisch dargestellt.
Während in der Probe aus den zufälligen Reads die Verteilung der Hits auf die einzelnen
Bakterien ebenfalls zufällig ist, erkennt man bei der künstlich erzeugten sowie der echten
metagenomischen Proben deutliche Unterschiede in der Anzahl der Hits.
22
Dep. of Biochemistry, Bioinformatics and Computational Biology Unit, University of Pretoria
59
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Abbildung 25: Verteilung der häufigsten Treffer bei verschiedenen Proben
Auch haben wir an der aus drei verschiedenen Genomen künstlich erzeugten metagenomischen Probe in Kapitel 5.4 gesehen, dass die Bakterien, deren Genome in der Probe
vorhanden waren, unter den ersten Treffern auftauchen. Die Idee des Short Oligo Alignments scheint also zu funktionieren.
Andererseits sehen wir bereits an unseren Ergebnissen sehr starke Streuungen, so dass es
schwierig ist, eine Grenze zu ziehen und festzulegen, welches der Bakterien noch in der
Probe enthalten war und welches nicht mehr. Auch ist es zur Zeit noch nötig, für jede
Probe den Parameter neu zu bestimmen, ab wie vielen gefunden Markern in einem Read
dieser als Hit gewertet werden soll.
Die Hauptursache für dieses Problem dürfte, wie erwähnt, in den verwendeten Markersätzen
liegen. Mögliche Lösungsansätze für das Problem der schlechten Markersätze wären:
• Das Erstellen von zusätzlichen Statistiken über die einzelnen Marker. In Kapitel 5.4
haben wir bereits gesehen, dass allein die Information, wie viele der Marker gefunden und wie viele nicht gefunden wurden, eine interessante zusätzliche Information
darstellt. Diese Idee könnte man noch weiter vertiefen und Statistiken über die Anzahl, wie oft die Marker insgesamt gefunden werden, erstellen. Aus der Verteilung
der Marker könnte man so gegebenenfalls auch Informationen zu den in der Probe
enthaltenen Bakterien bekommen.
• Eine andere Idee wäre, generell etwas längere Marker zu verwenden. Der Vorteil
wäre, dass längere Marker meistens charakteristischer für ein Bakterium sind und
man somit weniger False-Positives erhält. Außerdem würde bei der Verwendung
längerer Marker gegebenenfalls der Einsatz von Filterfunktionen wieder Sinn machen, so dass man gerade für kleinere Markersätze Algorithmen verwenden könnte,
die im Mittel sogar schneller als in O(n) laufen würden.
Auf der anderen Seite gibt es aber auch Probleme bei der Verwendung längerer
60
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
Reads. Zum einen wird es schwieriger Marker zu finden, da es wenige längere DNASequenzen gibt, die regelmäßig in einem Genom auftreten. Man müsste also Marker
verwenden, die nur alle 30.000 oder alle 50.000 Basenpaare im Genom auftreten.
Zum anderen steigt die Wahrscheinlichkeit, dass je länger ein Marker ist, er aufgrund eines Sequenzierungsfehlers nicht gefunden wird. Bei den kurzen Reads sinkt
außerdem noch die Wahrscheinlichkeit, je größer ein Marker ist, dass dieser komplett in einem Read liegt. Eine mögliche Lösung hierzu wäre ein approximativer
Suchalgorithmus, wie das in Kapitel 4.6 vorgestellte Multiple Approximate String
”
Matching with l-Grams“ von Fredriksson und Navarro.
61
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
8. Literatur
Literatur
[AC75]
Alfred V. Aho and Margaret J. Corasick. Efficient String Matching: An Aid
to Bibliographic Search. Commun. ACM, 18(6):333–340, 1975.
[AOSiA97]
Kazuaki Ando, Makoto Okada, Masami Shishibori, and Jun ichi Aoe. Efficient Multi-Attribute Pattern Matching Using the Extended Aho-Corasick
Method. IEEE International Conference, 4:3936–3941, 1997.
[BM77]
Robert S. Boyer and J. Strother Moore. A Fast String Searching Algorithm.
Communications of the ACM, 20(10):762–772, 1977.
[BYN97]
R. A. Baeza-Yates and G. Navarro. Multiple Approximate String Matching.
In F. K. H. A. Dehne, A. Rau-Chaplin, J.-R. Sack, and R. Tamassia, editors,
Proceedings of the5th Workshop on Algorithms and Data Structures, number
1272, pages 174–184, Halifax, Nova Scotia, Canada, 1997. Springer-Verlag,
Berlin.
[CfBI]
National
Center
for
http://www.ncbi.nlm.nih.gov/.
[CM94]
William I. Chang and Thomas G. Marr. Approximate String Matching and
Local Similarity. In CPM ’94: Proceedings of the 5th Annual Symposium
on Combinatorial Pattern Matching, pages 259–273, London, UK, 1994.
Springer-Verlag.
[Dat]
Genomes OnLine Database. http://www.genomesonline.org/.
[DWRTed]
Colin F. Davenport, Lutz Wiehlmann, Oleg N. Reva, and Burkhard Tümmler. Visualisation of pseudomonas genomic structure by abundant 8-14mer
oligonucleotides, Environmental Microbiology, submitted.
[ea]
C.
Davenport
et
al.
OligoCounter.
http://webhost1.mhhannover.de/davenport/oligocounter/docs oc params.html.
Biotechnology
Information.
[fAMMERC] Community Cyberinfrastructure for Advanced Marine Microbial Ecology Research and Analysis (CAMERA). http://camera.calit2.net/.
[Fas]
NCBI Fastaformat. http://www.ncbi.nlm.nih.gov/blast/fasta.shtml.
[FN03]
K. Fredriksson and G. Navarro. Average-optimal multiple approximate
string matching, 2003.
[FN04]
Kimmo Fredriksson and Gonzalo Navarro. Improved Single and Multiple
Approximate String Matching. Combinatorial Pattern Matching (CPM’04),
pages 457–471, 2004.
[Fou04]
J. Craig Venter Science Foundation. Global Ocean Sampling Expedition,
2004.
[GL00]
Ben Gum and Richard Lipton. Cheaper by the Dozen: Batched Algorithms.
Proceedings of the 1st SIAM International Conference on Data Mining,
2000.
63
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
[HAQS07]
Daniel H. Huson, Alexander F. Auch, Ji Qi, and Stephan C. Schuster. MEGAN analysis of metagenomic data. Genome Research, 17:377–386, 2007.
[Hor80]
R. Nigel Horspool. Practical Fast Searching in Strings. SOFTWAREPRACTICE AND EXPERIENCE, 10:501–506, 1980.
[Ill]
Illumina/Solexa.
illumina
sequencing
http://www.illumina.com/pages.ilmn?id=203.
[Kle08]
Hans-Peter Klenk. Molecular Taxonomy of Bacteria: Sequencing of Type
Strains. Technical report, 03.03.2008.
[Kob]
GATC Biotech Koblenz. http://www.gatc-biotech.com.
[KR87]
Richard M. Karp and Michael O. Rabin. Efficient randomized patternmatching algorithms.
IBM Journal of Research and Development,
31(2):249–260, 1987.
[KST03]
Jari Kytöjoki, Leena Salmela, and Jorma Tarhio. Tuning String Matching
for Huge Pattern Sets. Combinatorial Pattern Matching: 14th Annual Symposium, CPM 2003, 2676:1017ff, 2003.
[MGK02]
Jan Mrázek, Lisa H. Gaynon, and Samuel Karlin. Frequent oligonucleotide motifs in genomes of three streptococci. Nucleic Acids Research,
30(19):4216–4221, 2002.
[MM96]
R. Muth and U. Manber. Approximate Multiple String Search. In D. S.
Hirschberg and E. W. Myers, editors, Proceedings of the7th Annual Symposium on Combinatorial Pattern Matching, number 1075, pages 75–86, Laguna
Beach, CA, 1996. Springer-Verlag, Berlin.
[Mye99]
Gene Myers. A fast bit-vector algorithm for approximate string matching
based on dynamic programming. Journal of the ACM, 46(3):395–415, 1999.
[Nav97]
G. Navarro. Multiple Approximate String Matching by Counting. In
R. Baeza-Yates, editor, Proceedings of the 4th South American Workshop
on String Processing, pages 95–111, Valparaiso, Chile, 1997. Carleton University Press.
[Nie05]
Janne Nieminen. Effcient implementation of Unicode string pattern matching automata in Java. Report A/2005/2, 2005.
[oCI]
University of California and DOE Joint Genome Institute.
Drainage Metagenome,
technology,
Acid Mine
http://web.camera.calit2.net/cameraweb/gwt/org.jcvi.camera.web.gwt.download.BrowseProjectsPage/BrowseProjectsPage.oa
[Par03]
R. Parchmann. Vorlesung Zeichenketten, 2003.
[Par04]
R. Parchmann. Vorlesung Approximative Zeichenketten, 2004.
[SBR07]
Kerstin Stangier, Christopher Bauser, and Johannes Regenbogen. Next
Generation DNA-Sequenzierung. LABORWELT, 8(3), 2007.
[SGD+ 07]
Yooseph S, Sutton G, Rusch DB, Halpern AL, and Williamson SJ et al. The
Sorcerer II Global Ocean Sampling Expedition: Expanding the Universe of
Protein Families. PLoS Biology, 5(3), March 2007.
64
.
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
[Sta79]
R. Staden. A strategy of DNA sequencing employing computer programs.
Nucleic Acids Research, 6(7):2601–2610, 1979.
[STK06]
Leena Salmela, Jorma Tarhio, and Jari Kytöjoki. Multipattern String
Matching with q-grams. Journal of Experimental Algorithmics, 11(Article No.1.1):1–19, 2006.
[VRHea04]
J. Craig Venter, Karin Remington, John F. Heidelberg, and Aaron L. Halpern et al. Environmental genome shotgun sequencing of the Sargasso Sea.
Science Express, 304(5667):66–74, 2004.
[Wat90]
James D. Watson. Human Genome Project. U.S. National Institutes of
Health, started in 1990.
[WM94]
S. Wu and U. Manber. A fast algorithm for multi-pattern searching. Technical Report TR-94-17, 1994.
[WUO+ 02]
Christian Weinel, David W. Ussery, Hakan Ohlsson, Thomas SicheritzPonten, Claudia Kiewitz, and Burkhard Tümmler. Comparative Genomics
of Pseudomonas aeruginosa PAO1 and Pseudomonas putida KT2440: Orthologs, Codon Usage, Repetitive Extragenic Palindromic Elements, and
Oligonucleotide Motif Signatures. Genome Letters, 1(4):175–187, 2002.
65
Schnelle Algorithmen zur Multipatternsuche in der Metagenomik
66
Index
Aho Corasick Algorithmus, 45–48
Aho-Corasick Algorithmus, 28, 43
Aho-Corasick Automat, 20, 23, 25
ASCII-Werte, 32
AVL-Baum, 10, 19
Multi Pattern Matching, 19, 28, 35, 37,
42
Multimenge, 48
Multiple Approximate String Matching,
39, 42, 43, 56
Bad Character Funktion, 36
Basenpaar, 8
Basenpaaren, 8
Baum, 9
binärer Suchbaum, 10, 18
BLAST, 6
Booyer-Moore Algorithmus, 28
Boyer-Moore Algorithmus, 35, 36
Boyer-Moore Algorithmus mit q-Grammen,
36, 43
Boyer-Moore-Horspool, 36, 40
Brute Force, 17, 30, 32, 46, 47
Nukleotid, 8
contig, 9, 16
deterministischer endlicher Automat, 9,
20, 24, 27
Distanz-Tabelle, 39, 42
DNA-Basen, 34
DNA-Sequenzierung, 5, 9
Edit-Operationen, 39
Einfügung, 39
Outpuntfunktion, 23
Pattern, 17
q-Gramm, 35–37, 39, 42, 57
Read, 8, 12
Resequenzierung, 56
Sanger, 5
Sanger Sequenzierung, 5, 56
Sequenzierungsfehler, 57
Short Oligo Alignment, 7, 57, 59
Single Pattern Matching, 17, 33, 35
Substitution, 39
Suffix, 22, 23, 31
Taxon, 8
Trie, 9, 21, 48
Two-Level-Hashing, 19, 32
Wu-Manber Algorithmus, 28, 35, 43, 46,
47
Fehlerfunktion, 20, 22–26
Filteralgorithmus, 28, 37, 40, 57
Filterfunktion, 34, 35, 44, 45
Filterwert, 44
Fredriksson und Navarro, 39, 40
Hashfunktion, 18, 34
Hashwerten, 36
Hit, 8
Illumina/Solexa, 6
Karp-Rabin Algorithmus, 18, 36, 40
Löschung, 39
Marker, 8, 11
Markersätze, 59
Metagenomik, 5, 8, 59
metagenomische Probe, 11, 56
67
Hiermit versichere ich, dass ich diese Arbeit selbständig verfasst habe und keine anderen
als die angegebenen Quellen und Hilfsmittel benutzt habe.
Hannover, den 29. Juli 2008
(Jens Neugebauer)
Herunterladen