Diplomarbeit ”Anpassung von BLAST für Genom-Datenbanken”

Werbung
Diplomarbeit
”Anpassung von BLAST für Genom-Datenbanken”
eingereicht am Institut für Informatik
der Humboldt-Universität zu Berlin
von Matthias Weh
geb. am 5. Januar 1976
in Berlin
Matrikelnummer 134764
Betreuer: Chokri Ben Necib
eingereicht am: 19. Februar 2002
Inhaltsverzeichnis
1 Einleitung
3
2 Biologische Grundlagen
2.1 Typen von Biosequenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Zusammenhang von DNA, RNA und Proteinen . . . . . . . . . . . . . . . .
4
4
6
3 Sequenzvergleiche
3.1 Bedeutung von Sequenzvergleichen . . . . .
3.2 Bewertungsschemata . . . . . . . . . . . . .
3.3 Alignierungen . . . . . . . . . . . . . . . . .
3.4 Algorithmen zur Bestimmung der optimalen
3.5 Approximative Alignierungsalgorithmen . .
. . . . . . .
. . . . . . .
. . . . . . .
Alignierung
. . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
10
10
12
13
4 Analyse des BLAST-Programmcodes
4.1 Das NCBI-Toolkit . . . . . . . . . . . . . .
4.2 Die Module des Programms BLAST . . . .
4.3 Die ”Datenbank”-Schnittstelle von BLAST
4.4 Der Ablauf von blastall . . . . . . . . . . .
4.5 Analyse des multithreading in BLAST . . .
4.6 Die Datenstruktur SeqAlign . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19
19
20
22
28
33
36
5 Verwendete Datenbankkonzepte
5.1 Datenmodellierung der Biosequenzen . . . . . . . . . . . . . . . . . . . . . .
5.2 Anwendungsprogrammierung mit DB2 . . . . . . . . . . . . . . . . . . . . .
5.3 Benutzerdefinierte Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . .
42
42
43
48
6 Anpassung von BLAST
6.1 Implementation der Datenbankschnittstelle von BLAST . . . . . . . . . . .
6.2 BLAST als benutzerdefinierte Funktion . . . . . . . . . . . . . . . . . . . .
54
54
68
7 Ausblick
82
A blastall -Kommandozeilenoptionen
A.1 Genetische Codetabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
84
87
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
INHALTSVERZEICHNIS
B Aufbau der BLAST-Reportdateien
B.1 Das FASTA-Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
B.2 FormatDB -Ausgabedateien . . . . . . . . . . . . . . . . . . . . . . . . . . .
B.3 BLAST-Reportdateien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
88
88
88
91
C UDF-Entwurfsdetails
C.1 Typen von Alignierungen . . .
C.2 DenseDiag-Alignierungen . . .
C.3 DenseSeg-Alignierungen . . . .
C.4 Weitere Anpassungen der UDF
95
95
95
96
97
.
.
.
.
.
.
.
.
.
.
.
.
D Relationales Datenmodell
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
99
Literaturverzeichnis
102
2
Kapitel 1
Einleitung
Seit Ende der 1980er Jahre wird systematisch der Aufbau der gesamten Erbinformation
lebender Organismen experimentell ermittelt und erfasst. Der ”Bauplan” eines jeden Lebewesens kann dabei durch eine Sequenz von Basenpaaren, die DNA, beschrieben werden.
Die funktionale Ausprägung der Erbinformation, die Proteine, sind ebenfalls als Sequenz
von chemischen Bestandteilen, den Aminosäuren, beschreibbar.
Die Auswertung und Interpretation der Sequenzen ist Aufgabe der Bioinformatik. Sie
ist eine sehr junge Forschungsrichtung, die die Disziplinen Molekularbiologie und Informationstechnik zusammenführt. Die Bioinformatik muss mehrere Aufgaben mit Hilfe der
Sequenzanalyse lösen:
• Auswertung von Sequenz-Rohdaten
Die bei der Sequenzierung gewonnenen Rohdaten werden auf ihre Korrektheit überprüft.
• Vorhersage von Genen
Die für Proteine kodierenden Abschnitte der DNA müssen von den weniger relevanten
Abschnitten getrennt werden. Sie sind Voraussetzung für die Proteintranslation.
• Vorhersage der Proteinstruktur und -funktion
Die dreidimensionale Struktur der Proteine determiniert die Wirkungsweise von Proteinen. Diese ist Voraussetzung für das Verständnis biologischer Prozesse.
• Aufklärung der evolutionären Verwandtschaft von Sequenzen
Die Biosequenzen heute analysierter Organismen werden in Beziehung zueinander
gesetzt, um Auskunft über die Verwandtschaft der Organismen zu erhalten.
Zur Bewältigung dieser Aufgaben werden hauptsächlich Algorithmen zum Vergleich und
zur Alignierung von Sequenzen verwendet. Ein populärer Vertreter dieser Methoden ist
das Basic Local Alignment Search Tool (BLAST).
Gegenstand dieser Arbeit ist die Integration von BLAST in ein relationales Datenbanksystem. Relationale Datenbanken bieten für die Genomforschung die Möglichkeit, die
Biosequenzen in ein Modell einzubetten, das verschiedene biologische Informationen in Beziehung setzt. Die Biosequenzen können dann unter verschiedenen Gesichtspunkten mit
Hilfe relationaler Anfragesprachen analysiert werden.
3
Kapitel 2
Biologische Grundlagen
2.1
Typen von Biosequenzen
Gegenstand dieser Diplomarbeit ist die Anpassung des Alignierungsalgorithmus BLAST
zur Anwendung in einem objektrelationalen Datenbanksystem. Dieser Algorithmus stammt
aus dem Bereich der Genanalyse. Deshalb sollen im Folgenden die wichtigsten Begriffe aus
der Genanalyse erläutert werden.
DNA (Deoxyribonucleic Acid, Desoxyribonukleinsäure) und RNA (Ribonucleic Acid,
Ribonukleinsäure) sind das Erbmaterial lebender Materie. Sie bilden das Genom, die Gesamtheit aller in einer Zelle vorhandenen Erbanlagen. Die Vererbung besteht in der Speicherung, Weitergabe, Rekombination und Realisierung der Erbinformation (des genetischen
Material s). Träger der DNA sind die Chromosomen. Auf die Weitergabe der Erbinformation wird in [23] eingegangen.
DNA und RNA sind makromolekulare Nukleinsäuren, die in Form einer Kette — einem
Polynukleotid — aufgebaut sind. Die Bausteine der Ketten sind die Nukleotide, die aus
Zuckern, Basen und Phosphatresten bestehen. Anhand der Basen lassen sich fünf Nukleotide unterscheiden: Adenin, Cytosin, Guanin, Thymin und Uracil. Deren hauptsächliches
Vorkommen kann Tabelle 2.1 entnommen werden. In Abhängigkeit von der Nukleinsäure
sind jeweils vier Nukleotide zu unterscheiden. Der grundlegende Unterschied zwischen DNA
und RNA ist der enthaltene Zucker: im Fall der DNA ist es Desoxyribose, im Fall der RNA
Ribose. Desoxyribose und Ribose kommen nie gleichzeitig im selben Polynukleotid vor.
Die DNA ist als Doppelstrang zweier sich gegenüberliegender Nukleotidketten aufgebaut. In diesem von James D. Watson und Francis Crick 1953 vorgeschlagenen (und
bereits auf seine Richtigkeit überprüften) Strukturmodell bilden die Paare Adenin und
Thymin sowie Cytosin und Guanin Wasserstoffbrücken aus, wobei eine der Basen auf dem
einen Strang, die andere auf dem anderen Strang liegt. Folglich kann aus einer der beiden
Ketten die komplementäre Kette bestimmt werden, was unter anderem für die Replikation
der Erbinformation von Bedeutung ist. Die Ketten der DNA sind in einer rechtsdrehenden
Doppelspirale (Helix ) angeordnet.
Im Gegensatz dazu ist die RNA aus nur einer Kette aufgebaut. Deren Besonderheit
besteht darin, dass die Nukleotide des Strangs untereinander Basenpaare ausbilden können
(Adenin mit Uracil, Cytosin mit Guanin), was die Sekundärstruktur von RNA komplizierter
als die von DNA macht. Der genaue Aufbau der RNA hängt von ihrer Funktion ab und
wird hier nicht weiter vertieft (siehe dazu [24]).
4
KAPITEL 2. BIOLOGISCHE GRUNDLAGEN
Nukleotid
Adenin
Cytosin
Guanin
Thymin
Uracil
Symbol
A
C
G
T
U
Vorkommen
DNA / RNA
DNA / RNA
DNA / RNA
DNA
RNA
Komplement
T/U
G
C
A
A
Tabelle 2.1: Alphabet der Nukleotide
Symbol
A
C
M
G
R
S
V
T
W
Y
H
K
D
B
N/X
Bedeutung
Adenin
Cytosin
A oder C
Guanin
A oder G
C oder G
A oder C oder G
Thymin/Uracil
A oder T
C oder T
A oder C oder T
G oder T
A oder G oder T
C oder G oder T
A oder C oder G oder T
Komplement
T
G
K
C
Y
S
B
A
W
R
D
M
H
V
N/X
Tabelle 2.2: Alphabet der Nukleotide mit Mehrdeutigkeiten. Die komplementären Residuen
ergeben sich, wenn man die komplementären Elementarresiduen verknüpft.
Die Anordnung der Stickstoffbasen auf den Ketten wird als Sequenz bezeichnet. Mit
der Darstellung eines Nukleotids durch den Buchstaben seiner Stickstoffbase kann eine Sequenz als Zeichenkette repräsentiert werden. Damit können DNA- und RNA-Sequenzen
informationstechnisch verarbeitet werden. Zur Vereinheitlichung von RNA und DNA und
zur Darstellung sogenannter ”Mehrdeutigkeitsresiduen” (ambiguity residues) wird das Alphabet aus Tabelle 2.2 verwendet, das von Cornish-Bowden [10] eingeführt wurde. Die vier
eindeutigen Residuen werden im Folgenden als Elementarresiduen bezeichnet, DNA- und
RNA-Sequenzen werden zum Begriff NA-Sequenzen zusammengefasst.
Die dritte Art der hier behandelten Sequenzen sind die Aminosäure- oder Proteinsequenzen. Ein Protein ist ein aus Aminosäuren zusammengesetztes Makromolekül. Proteine
sind die funktionale Realisierung der Erbinformation und werden aus der DNA synthetisiert. Obwohl heute über 100 Aminosäuren bekannt sind, bilden nur 20 von Ihnen den
Bausatz zur Bildung von Proteinen. Zur Darstellung dieser proteinogenen Aminosäuren wird das in [20] eingeführte Alphabet verwendet. Tabelle 2.3 zählt die Aminosäuren
auf. Die Aminosäuren in einem Protein sind, wie die Nukleotide der DNA, als Sequenz
(Polypeptidkette) angeordnet. Die Sequenz bildet die Primärstruktur. Die Sekundär- und
Tertiärstruktur ergibt sich, wenn man die Wechselwirkung der Aminosäuren innerhalb eines Proteins betrachtet. Diese dreidimensionale Struktur ist maßgebend für die Funktion
5
KAPITEL 2. BIOLOGISCHE GRUNDLAGEN
Einbuchstabencode
A
C
D
E
F
G
H
I
K
L
M
N
P
Q
R
S
T
V
W
Y
Dreibuchstabencode
Ala
Cys
Asp
Glu
Phe
Gly
His
Ile
Lys
Leu
Met
Asn
Pro
Gln
Arg
Ser
Thr
Val
Trp
Tyr
Aminosäure
Alanin
Cystein
Asparaginsäure
Glutaminsäure
Phenylalanin
Glycin
Histidin
Isoleucin
Lysin
Leucin
Methionin
Asparagin
Prolin
Glutamin
Arginin
Serin
Threonin
Valin
Tryptophan
Tyrosin
Tabelle 2.3: Alphabet der Aminosäuren
des Proteins und deshalb von besonderem Interesse. Es ist bekannt, dass der dreidimensionale Aufbau durch die Sequenz determiniert ist, allerdings ist bisher nicht geklärt, welche
Information den Prozess der Proteinfaltung (d.h. der Ausbildung der 3D-Struktur) steuert.
2.2
Zusammenhang von DNA, RNA und Proteinen
Die im vorangegangenen Abschnitt behandelten Biosequenzen stehen in einem biologischen
Zusammenhang. Die DNA ist, wie bereits erwähnt, Träger des Erbguts eines Organismus.
Jedem Gen, der kleinsten vererbbaren Einheit auf einem DNA-Molekül, kann eine Peptidkette (also eine Aminosäuresequenz) zugeordnet werden. In Experimenten wurde die
Erkenntnis gewonnen, dass die Gene auf der DNA in einer linearen Sequenz angeordnet
sind. Sie überlappen sich normalerweise nicht, die Ausnahme bildet das Erbgut einiger
Viren. Da ein Gen demnach als Sequenz von Nukleotiden beschreibbar ist, folgt, dass die
Aminosäuresequenz eines Peptids mittels eines eindeutigen Codes aus dem Gen ermittelbar
ist. Dieser wird als genetischer Code (Tabelle 2.4) bezeichnet. Die kleinste Informationseinheit ist dabei eine Gruppe aus drei Basen (Basentriplett), die als Codon bezeichnet
wird. Eine Abbildung von drei aufeinanderfolgenden Nukleotiden auf eine Aminosäure ist
vollständig, da mit drei Nukleotiden 43 = 64 verschiedene Kombinationen möglich sind.
Mit zwei Nukleotiden könnten nicht alle 20 Aminosäuren abgebildet werden (42 = 16).
Das in Abbildung 2.1 dargestellte zentrale Dogma der Molekularbiologie veranschaulicht
den Zusammenhang der hier betrachteten Sequenzen und die Vorgänge, an denen diese
beteiligt sind. Im zentralen Dogma wird der Vorgang der Informationsübertragung nur
von der DNA zum Protein dargestellt, nicht umgekehrt. Es sei hier erwähnt, dass bei
6
KAPITEL 2. BIOLOGISCHE GRUNDLAGEN
1. Position
U (A)
C (G)
A (T)
G (C)
U (A)
Phe
Phe
Leu
Leu
Leu
Leu
Leu
Leu
Ile
Ile
Ile
Met
Val
Val
Val
Val
2. Position
C (G) A (T)
Ser
Tyr
Ser
Tyr
Ser
Stop
Ser
Stop
Pro
His
Pro
His
Pro
Gln
Pro
Gln
Thr
Asn
Thr
Asn
Thr
Lys
Thr
Lys
Ala
Asp
Ala
Asp
Ala
Glu
Ala
Glu
3. Position
G (C)
Cys
Cys
Stop
Trp
Arg
Arg
Arg
Arg
Ser
Ser
Arg
Arg
Gly
Gly
Gly
Gly
U (A)
C (G)
A (T)
G (C)
U (A)
C (G)
A (T)
G (C)
U (A)
C (G)
A (T)
G (C)
U (A)
C (G)
A (T)
G (C)
Tabelle 2.4: Der genetische Code. Die Nukleotidsymbole bezeichnen Residuen der transkribierten mRNA, in Klammern sind die entsprechenden Basen der DNA angegeben.
Abbildung 2.1: Zentrales Dogma der Molekularbiologie
7
KAPITEL 2. BIOLOGISCHE GRUNDLAGEN
bestimmten Viren, den Retroviren, die Synthese der DNA aus der RNA möglich ist.
Die Übersetzung der DNA in Proteine (Proteinbiosynthese), also die Anwendung des
genetischen Codes, erfolgt in zwei Schritten:
1. Transkription
Für die Proteinbiosynthese werden nur Einzelteile des DNA-Strangs benötigt, die
als RNA-Molekül kopiert werden. Dazu wird der DNA-Doppelstrang enzymatisch
getrennt und einer der Stränge komplementär auf die RNA kopiert:
• Adenin in der DNA entspricht Uracil in der RNA
• Cytosin entspricht Guanin
• Guanin entspricht Cytosin
• Thymin entspricht Adenin
Das Produkt dieses ersten Transkriptionsschritts wird in einem zweiten Schritt weiter modifiziert. Die wichtigsten Veränderungen sind die Verkürzung der Sequenz an
einem Ende (untranslatierte Region) sowie die Entfernung nicht kodierender Teilsequenzen aus der RNA. Die nichtkodierenden Sequenzen (Introns) werden aus der
RNA entfernt, die dazwischen liegenden Sequenzen (Exons) werden verbunden (verspleißt). Das Vorkommen nichtkodierender Sequenzen wurde entdeckt, weil bei der
Lokalisierung der Gene in der DNA deren diskontinuierliche Verteilung aufgefallen
war. Das Ergebnis ist die Messenger RNA (mRNA), die zur Translation benötigt
wird.
2. Translation
Der zweite Schritt der Proteinbiosynthese sorgt für die Übersetzung der mRNA in
ein Protein. Mit Hilfe des genetischen Codes (Tabelle 2.4) wird aus den ersten drei
Nukleotiden der Sequenz eine Aminosäure synthetisiert, aus den nächsten drei Nukleotiden die zweite und so fort. Die Translationsprodukte hintereinander liegender
mRNA-Basentripletts liegen im entstehenden Protein ebenfalls hintereinander. Der
Translationsvorgang wird beendet, wenn eines der Stoppcodons gefunden wird. In der
Praxis liegt meist nur ein mRNA-Fragment vor, bei dem das Startcodon nicht mit
Sicherheit bestimmbar ist. Folglich gibt es drei verschiedene Leseraster zum Starten
der Translation (Beginn an den ersten drei Basen), die unterschiedliche Aminosäuresequenzen ergeben.
Zwei Eigenschaften des genetischen Codes sind in diesem Zusammenhang von Bedeutung:
1. Universalität
Der genetische Code ist für fast alle Spezies gleich. Die Ausnahme bilden Organismen
mit sehr kleinen Genomen, die nur wenige Proteine kodieren.
2. Degeneriertheit
Der genetische Code ist nicht eineindeutig, d.h. einer Aminosäure kann meistens kein
kodierendes Basentriplett zugeordnet werden, da fast alle Aminosäuren mindestens
zwei Basentripletts besitzen. Deshalb darf bei einer relativen Unähnlichkeit zweier
DNA-Sequenzen nicht gefolgert werden, dass die kodierten Proteine keine Ähnlichkeit
besitzen.
8
Kapitel 3
Sequenzvergleiche
3.1
Bedeutung von Sequenzvergleichen
Nachdem im letzten Kapitel der Begriff Sequenz im biologischen Kontext geklärt wurde, soll
es hier um die Bewertung von Ähnlichkeiten und Unterschieden von Biosequenzen gehen.
Ziel dieser Betrachtungen ist die Möglichkeit, Beziehungen zwischen den Sequenzen und
den dazugehörigen Organismen abzuleiten:
• strukturelle Beziehungen
Da die 3D-Struktur durch die Primärstruktur (die Sequenz) determiniert ist, ist die
starke Ähnlichkeit von bestimmten Bereichen zweier Sequenzen ein Hinweis auf eine
ähnliche räumliche Struktur der dazugehörigen Proteine.
• funktionale Beziehungen
Wenn sich die 3D-Struktur zweier Proteine in Teilen stark ähnelt, so liegt deren
funktionale Verwandtschaft nahe. In Kombination mit obiger Implikation ist damit
die Funktion eines Proteins aus dessen Sequenz ableitbar, sofern bereits die Funktion
eines Vergleichsproteins auf experimentellem Wege ermittelt wurde.
• evolutionäre Beziehungen
Ein drittes Ziel von Sequenzvergleichen ist der Nachweis der Homologie. Zwei Sequenzen sind homolog, falls sie einen gemeinsamen evolutionären Ursprung, d.h. die
gleiche Sequenz als Vorfahren haben [22]. Homologie kann in zwei Formen auftreten:
1. Orthologie: Die betrachteten Sequenzen haben eine ähnliche Funktion, stammen
aber aus verschiedenen Spezies. Homologe Sequenzen dieser Art zeigen deshalb
die Differenzierung und Verwandtschaft von Spezies an.
2. Paralogie: Die betrachteten Sequenzen haben unterschiedliche, aber verwandte Funktionen innerhalb desselben Organismus. Paraloge Sequenzen entstehen
durch Gen-Duplikation. Sie geben Hinweise auf die Entwicklung des Genoms
einer einzelnen Spezies.
Da die Vorfahren-Sequenz, d.h. der gemeinsame evolutionäre Ursprung der Sequenzen,
oft nicht bekannt ist, wird versucht, mittels Sequenzvergleichen die Homologie nachzuweisen. Ziel ist es, die Ähnlichkeit von Sequenzen bewertbar zu machen. Anhand
9
KAPITEL 3. SEQUENZVERGLEICHE
der Bewertung ist man dann in der Lage, Ähnlichkeiten zu vergleichen. Homologie
wird geschlussfolgert, wenn das Ähnlichkeitsmaß der zu untersuchenden Sequenzen
signifikant höher als das zweier zufälliger Sequenzen ist. Die umgekehrte Implikation
gilt dagegen nicht: bestimmte Sequenzpaare sind zwar homolog, zeigen jedoch keine
signifikante Verwandtschaft auf Sequenzniveau.
3.2
Bewertungsschemata
Sequenzvergleichsalgorithmen verarbeiten Zeichenketten (strings) und berechnen bewertete
Alignierungen. Eine Alignierung (engl.: to align – ausrichten, in Übereinstimmung bringen) zweier Zeichenketten ist eine Ausrichtung der Zeichen des einen strings zu denen des
anderen. Eine solche Anordnung kann numerisch bewertet werden. Bevor auf konkrete
Alignierungsalgorithmen eingegangen wird, führt dieser Abschnitt den Begriff des Bewertungsschemas ein.
Die Berechnung der Ähnlichkeit zweier Sequenzen wird in den hier vorgestellten Algorithmen auf die Substitution einzelner Zeichen reduziert. Die Zuordnung eines Werts s(a, b)
zu einem Zeichenpaar (a, b) kann als Maß für die Ähnlichkeit der beiden Zeichen gelten: je
höher der Wert, desto ähnlicher die Zeichen. s wird als Bewertungs- oder scoring-Schema
(engl.: to score – benoten, Punkte vergeben) bezeichnet. Ist das Alphabet, auf dem die
Bewertung definiert wird, endlich, so kann das Schema als Matrix M dargestellt werden.
Dabei gilt für jedes Matrixelement ma,b :
ma,b = s(a, b)
Die Aufgabe von Alignierungsalgorithmen besteht darin, eine Alignierung mit möglichst
hoher Bewertung zu ermitteln. Deshalb kann es nötig sein, dass einige Zeichen des einen
strings zu Lücken (engl.: gaps oder indels) im anderen string zugeordnet werden, falls dadurch eine hoch bewertete Alignierung gebildet werden kann. Im Alphabet Σ ist deshalb
oft eines der Zeichen ε, ∗ oder - zur Repräsentation einer Lücke enthalten. Folglich müssen
auch Bewertungen der Form s(a, ε) bzw. s(ε, b) Teil des Bewertungsschemas sein. Bewertungsmatrizen werden auch als Substitutionsmatrizen bezeichnet, da die Zuordnung zweier
Zeichen auch als Ersetzung des einen Zeichens durch das andere interpretiert werden kann.
Da Protein- und NA-Sequenzen Zeichenketten auf endlichen Alphabeten sind, werden
zu ihrer Alignierung Bewertungsmatrizen verwendet. Die Bewertungsmatrix hat eine herausragende Bedeutung, weil sie als einziges Element des Alignierungsalgorithmus Wissen
aus der Anwendungsdomäne in den Algorithmus überträgt. Unterschiedliche Anwendungen benötigen dabei verschiedene Bewertungsschemata. Einen Überblick über die wichtigsten Schemata gibt Barton [4]. In der Praxis werden für Proteinsequenzen meistens
die Substitutionsmatrizen der PAM - (point-accepted mutations, [11]) und der BLOSUM Familie (BLOCKS substitution matrix, [13]) verwendet, bei NA-Sequenzen wird häufig nur
zwischen matches (engl.: match – Ebenbild, Gegenstück) und mismatches unterschieden,
d.h. Paaren identischer bzw. nicht-identischer Residuen.
3.3
Alignierungen
Das Ziel von Vergleichsalgorithmen ist die Bestimmung der Ähnlichkeit von Sequenzen. Die
hier betrachteten Verfahren untersuchen nur jeweils zwei Sequenzen, eine Erweiterung auf
10
KAPITEL 3. SEQUENZVERGLEICHE
S1
S2
Score
S 0 (S1 , S2 )
C
D
−4
A
A
+5
B
B
+5
A
C
D
–
B
D
−3 P−4 +5
= 11
B
B
+5
–
D
−3
C
C
+5
Abbildung 3.1: Bewertung einer Beispielalignierung der Zeichenketten CABACDBC und
DABBDBDC. Das Zeichen ”-” steht für eine Lücke.
mehrere gleichzeitig anzuordnende Sequenzen (engl.: multiple alignment) ist aber möglich.
Die Bestimmung der Ähnlichkeit besteht darin, eine möglichst hoch bewertete Alignierung
der Sequenzen, oder Teilen davon, zu erreichen. Die einzelnen Zeichen einer Sequenz behalten nach der Ausrichtung zur anderen Sequenz ihre Reihenfolge. Zeichen der ersten Sequenz
können dabei zu Zeichen der anderen Sequenz oder zu Lücken in dieser Sequenz zugeordnet
werden. Der umgekehrte Fall gilt entsprechend. Abbildung 3.1 stellt eine mögliche Alignierung der Zeichenketten CABACDBC und DABBDBDC dar. Die zugeordneten Paare von Zeichen
werden zur Veranschaulichung nach einem einfachen Schema bewertet: +5 für matches,
−4 für mismatches und −3 für Zeichen-Lücken-Zuordnungen. Der Wert einer Alignierung
ergibt sich als Summe der Werte der Zeichenpaare. Seien
• x und y zwei Zeichenketten,
• xi und yi daraus durch Lückeneinfügen entstandene Sequenzen,
• a[j] das Zeichen an der j-ten Position einer Sequenz a,
• ni die Länge der Alignierung von xi und yi ,
dann berechnet folgende Formel den Wert der Alignierung:
0
S (xi , yi ) =
ni
X
s(xi [j], yi [j])
(3.1)
j=1
Über die Ähnlichkeit der beiden Sequenzen x und y kann erst dann eine Aussage getroffen werden, wenn alle möglichen Alignierungen gebildet und bewertet werden. Die
bestbewertete Alignierung ist dann ein Maß für die Ähnlichkeit der beiden Sequenzen. Der
Wert S(x, y) einer solchen optimalen Alignierung wird deshalb als Maximum über die Werte
aller möglichen Alignierungen (Formel 3.1) definiert:
S(x, y) = max S 0 (xi , yi )
i
(3.2)
Alignierungen können unter verschiedenen Gesichtspunkten klassifiziert werden. Die gebräuchlichste Klassifizierung unterscheidet zwischen globaler und lokaler Alignierung. Obiges Beispiel ist eine globale Alignierung, weil die gesamten Zeichenketten zur Anordnung
herangezogen werden. Lokale Alignierung bedeutet die Anordnung zweier Sequenzausschnitte. Es müssen alle Subsequenzen für eine Alignierung geprüft werden. Die optimale
lokale Alignierung ist unter allen möglichen diejenige mit der höchsten Bewertung.
Eine zweite Klassifikation unterscheidet zwischen lückenbehafteten (gapped ) Alignierungen und solchen ohne Lücken (ungapped ). Obiges Beispiel ist eine lückenbehaftete Alignierung. Es ist einsichtig, dass eine globale Alignierung lückenbehaftet sein muss, weil
11
KAPITEL 3. SEQUENZVERGLEICHE
es sonst nur eine Möglichkeit gäbe, die beiden Sequenzen anzuordnen. Für alle anderen
Kombinationen gibt es entsprechende Algorithmen.
Die Bestimmung globaler Alignierungen dient unter anderem dazu, die evolutionäre
Entwicklung einer Proteinfamilie zu rekonstruieren, wenn bekannt ist, dass beide Sequenzen
zu dieser Familie gehören. Die häufigere Problemstellung ist jedoch die lokale Alignierung.
Sie findet bei der Identifikation von Genen in langen DNA-Sequenzen Anwendung. Auch
Proteine sind aus strukturellen und funktionalen Untereinheiten aufgebaut, deren Position
in der Sequenz nur durch lokale Alignierung bestimmt werden kann, wenn eine Sequenz mit
einer verwandten Funktion zum Sequenzvergleich zur Verfügung steht.
3.4
Algorithmen zur Bestimmung der optimalen Alignierung
Sowohl für die globale als auch die lokale Alignierung existieren Algorithmen, die die optimale Ausrichtung und damit das Maß für die Ähnlichkeit zweier Sequenzen finden. Sie
sollen hier kurz vorgestellt werden, da sie den Ausgangspunkt für die approximativen Algorithmen bilden.
3.4.1
Der Algorithmus von Needleman und Wunsch
Zur optimalen globalen Alignierung zweier Sequenzen wird der Algorithmus von Needleman und Wunsch [25] verwendet. Der Algorithmus ist ein Beispiel für die Technik der
dynamischen Programmierung [5], bei der Teilergebnisse so in ihrer zeitlichen Reihenfolge berechnet werden, dass sie zum benötigten Zeitpunkt vorliegen. Die Berechnung jedes
Teilergebnisses greift auf eine konstante Anzahl bereits berechneter Teilergebnisse zurück.
Ausgenommen sind die Initialwerte, die sich unabhängig von anderen Werten berechnen
lassen. Seien:
• x und y zwei Sequenzen der Längen |x| = n und |y| = m,
• Si,j = S(x[1, i], y[1, j]) der Wert der besten Anordnung der entsprechenden Präfixe
von x und y mit
• x[p1 , p2 ] als Teilsequenz von x, die bei Position p1 beginnt und bei p2 endet; analoges
gilt für y
Die Felder Si,j werden wie folgt berechnet, Sn,m ist dann der Wert der optimalen Alignierung
der Sequenzen x und y:
S0,0 = 0
(3.3)
S0,j = S0,j−1 + s(ε, y[j])
für
1≤j≤m
(3.4)
Si,0 = Si−1,0 + s(x[i], ε)


+ s(ε ,y[j]), 
 Si−1,j
Si−1,j−1 + s(x[i],y[j]),
Si,j = max


Si,j−1
+ s(x[i],ε )
für
1≤i≤n
(3.5)
für
i, j 6= 0
(3.6)
Die Optimalität des Ausdrucks wird induktiv bewiesen. Der Induktionsschritt besteht in
der Überlegung, dass die Alignierung der Sequenzen x[1, i] und y[1, j] auf drei Arten enden
kann:
12
KAPITEL 3. SEQUENZVERGLEICHE
x[1,i]
y[1,j]
Fall 1
. . . x[i]
...ε
Fall 2
. . . x[i]
. . . y[j]
Fall 3
...ε
. . . y[j]
Folglich greift die Berechnung des Zwischenergebnisses Si,j auf die Teilergebnisse Si−1,j ,
Si−1,j−1 und Si,j−1 zurück, und der Algorithmus sorgt dafür, dass diese vor Si,j berechnet
werden.
Zur Ermittlung der optimalen Alignierung aus der S-Matrix muss vom Element Sn,m
der Berechnungsweg des jeweils maximalen Werts zurückgegangen werden. Die Berechnung
eines jeden Elements der Matrix besteht aus konstant vielen Schritten. Da sie aus n · m
Elementen besteht, hat der Algorithmus eine Komplexität von O(n · m). Die Zeilen der
Matrix können als Repräsentanten für die Präfixe der Sequenz x betrachtet werden, die
Spalten als Repräsentanten für die Präfixe von y.
Eine von Gotoh [12] eingeführte und häufig verwendete Variation des Algorithmus unterscheidet zwischen dem Einfügen und der Verlängerung einer Lücke. Der Wert einer
alignierten Lücke ist dabei eine affine Funktion, die von der Länge abhängt. Die Komplexität dieses modifizierten Algorithmus beträgt O(n · m · (n + m)).
3.4.2
Der Algorithmus von Smith und Waterman
Für die lokale Sequenzalignierung existiert ebenfalls ein optimaler Algorithmus, der von
Smith und Waterman [28] entwickelt wurde. Dieser kann vom Needleman-Wunsch-Algorithmus hergeleitet werden. Voraussetzung für diese Modifikation ist ein Bewertungsschema, das Ähnlichkeit positiv und Unähnlichkeit negativ bewertet. Ist ein solches Schema
gegeben, sorgt der Algorithmus dafür, dass die optimale lokale Alignierung nicht mit negativen Werten beginnen oder enden kann. Eine Alignierung ist nicht optimal, wenn noch
positive Werte an einem der beiden Enden zu einem höheren Wert führen würden. Die
Berechnung sogenannter ”affiner Lücken” geschieht, wie bei Needleman-Wunsch, durch die
Modifikation von Gotoh [12].
3.5
Approximative Alignierungsalgorithmen
Der Algorithmus von Smith und Waterman bestimmt die optimale lokale Alignierung zweier Sequenzen. Dieser Eigenschaft steht ein Berechnungsaufwand von O(n2 ) bzw. O(n3 )
gegenüber. Zum Durchsuchen von Sequenz-Datensammlungen ist dieser Aufwand zu groß.
Für diesen mittlerweile sehr häufig vorkommenden Anwendungsfall bedient man sich heuristischer Verfahren zur Approximation des Smith-Waterman-Algorithmus. Durch Heuristiken wird der Lösungsraum, der durch dynamische Programmierung bearbeitet werden
muss, begrenzt und dadurch die Laufzeit verbessert.
Die wichtigsten approximativen Methoden für den paarweisen Sequenzvergleich sind die
Algorithmen der FASTA- und BLAST -Programmpakete. Das von Pearson und Lipman
entwickelte FASTA [26, 27] war der erste wichtige Ansatz zur näherungsweisen Lösung der
optimalen lokalen Alignierung. Schwerpunkt dieser Arbeit ist jedoch BLAST.
3.5.1
BLAST
BLAST (Basic Local Alignment Search Tool ) [1, 2, 30] wurde von Altschul et al. am NCBI
(National Center for Biotechnology Information) entwickelt. Der Algorithmus zeichnet
13
KAPITEL 3. SEQUENZVERGLEICHE
sich gegenüber FASTA durch eine geringere Laufzeit bei gleicher Sensitivität aus. Von dem
ursprünglichen Algorithmus gibt es eine Vielzahl von Erweiterungen, von denen hier einige
vorgestellt werden.
BLAST ist als Web-Applikation beim NCBI unter http://www.ncbi.nlm.nih.gov verfügbar. Dort kann jedes BLAST-Programm mit eigenen Anfragesequenzen gegen eine Vielzahl von Datensammlungen getestet werden.
3.5.2
Grundidee des Algorithmus
BLAST ist für den Vergleich einer Anfragesequenz Anfragesequenz Q mit einer SequenzDatensammlung ausgelegt. Die Grundidee des Algorithmus besteht darin, in den Datensammlungssequenzen (im Folgenden Vergleichssequenzen) nach Teilstücken zu suchen, die
”gute Kandidaten” (Hits) für Alignierungen mit Teilstücken der Anfragesequenz sind. Die
Hits werden dann zu Alignierungen expandiert, die bewertet werden. Vor dem Algorithmus
wird die Anfragesequenz gefiltert, d.h. es werden Regionen geringer Komplexität maskiert.
Für Nukleotidsequenzen wird zur Filterung DUST1 , für Aminosäuresequenzen SEG [29]
verwendet. Der BLAST-Algorithmus selbst führt auf jeder Vergleichssequenz D die folgenden drei Schritte aus:
1. Lokalisierung der Hits.
In der Vergleichssequenz werden Teilwörter der Länge w gesucht, die mit gleich langen
Teilwörtern der Anfragesequenz eine Alignierung mit einem Wert größer T bilden.
Eine derartige Alignierung wird Hit genannt.
2. Expansion eines Hits.
Ein Hit wird zu einer größeren lückenfreien Alignierung expandiert. Dazu wird die
jeweils aktuelle Alignierung schrittweise nach links bzw. rechts um ein Zeichen erweitert. Die Erweiterung wird solange vorangetrieben, bis die entstehende Alignierung
um einen festgelegten Wert X vom erweiterungslokalen Maximum abfällt. X wird als
dropoff -Wert bezeichnet (engl.: to drop off – nachlassen, zurückgehen). Dann stellt
das lokale Maximum das Ergebnis dar und wird mit HSP (High-scoring Segment
Pair ) bezeichnet.
3. Ausgabe der HSPs.
Hat ein HSP einen Wert größer als S, wird er als lokale Alignierung ausgegeben.
BLAST hat damit die Möglichkeit, mehrere lokale Alignierungen zu berechnen und
auszugeben.
Die Schritte beschreiben bereits die von BLAST benutzten Parameter, die unterschiedliche Auswirkungen auf Selektivität und Sensitivität des Algorithmus haben. Sensitivität
ist die Fähigkeit des Algorithmus, tatsächlich verwandte Sequenzen zu finden und hoch zu
bewerten. Selektivität beschreibt die Fähigkeit, nicht verwandte Sequenz niedrig zu bewerten und damit nicht zu betrachten. Folgende BLAST-Parameter dienen der Steuerung des
Algorithmus:
1
Der Algorithmus wurde von Roman L. Tatusov und David J. Lipman am NCBI entwickelt. Zu DUST
gibt es keine Veröffentlichungen.
14
KAPITEL 3. SEQUENZVERGLEICHE
• w ist die Wortlänge eines Hits. Von den BLAST-Autoren empfohlene Werte sind 2
oder 3 für Proteinvergleiche und 11 für DNA-Vergleiche. Eine Erhöhung des Werts
geht mit der Erhöhung der Selektivität einher.
• T ist der Schwellwert für die Entscheidung, welche Alignierung des ersten Schritts
ein Hit ist, also im zweiten Schritt weiterverarbeitet wird. Beim Festlegen dieses
Parameters ist zu beachten, dass ein niedriger Wert mehr Hits produziert und damit
ein höheres Potenzial für erfolgreiche Alignierungen bietet (höhere Sensitivität), dabei
allerdings auch mehr Rechenzeit aufgewendet wird.
• X ist der sogenannte dropoff -Parameter, der bestimmt, ob eine Expansion abgebrochen oder weiterverfolgt wird. Ein höherer Wert geht mit einer Erhöhnung der
Sensitivität einher.
• S entscheidet darüber, ob ein HSP als Ergebnis von BLAST ausgegeben wird. Je niedriger der Wert, desto mehr Ergebnis-Alignierungen (höhere Sensitivität). Heutzutage
wird diese Entscheidung allerdings anhand abgeleiteter Variablen gefällt. Aus dem
hier definierten Wert einer Alignierung, dem nominalen Wert, wird ein normalisierter
Wert berechnet, der die Charakteristika des jeweiligen Bewertungsschemas sowie die
Größe des Suchraums mit einbezieht [21]. Mit Hilfe des normalisierten Werts können
verschiedene BLAST-Suchläufe mit unterschiedlichen Bewertungsschemata untereinander verglichen werden. Aus dem normalisierten Wert wird ein Erwartungswert berechnet, der Auskunft darüber gibt, wieviele Sequenzalignierungen mit dem gleichen
oder einem besseren normalisierten Wert im entsprechenden Suchraum zu erwarten
sind, ohne den Inhalt in Betracht zu ziehen. Je niedriger der Erwartungswert der
Alignierung, desto signifikanter ist die Ähnlichkeit der alignierten Sequenzen. Die
Ausgabe von HSPs wird in den zuletzt entwickelten BLAST-Programmvarianten mit
dem Erwartungswert gesteuert.
Die Lokalisierung selbst besteht aus zwei Teilen:
1. Es werden alle Wörter der Länge w bestimmt, die mit einem Teilwort der Anfragesequenz das T -Kriterium erfüllen. Diese Wörter werden als w-mere (engl.: w-mers)
bezeichnet. Dieser Schritt ist unabhängig von den Vergleichssequenzen und wird deshalb nur einmal vor der gesamten BLAST-Suche durchgeführt. Die Wörter werden
zusammen mit der Teilwort-Position aus der Anfragesequenz in einer Liste gespeichert.
2. Die jeweilige Vergleichssequenz wird nach den Wörtern in der Liste durchsucht.
Im ersten Schritt ist zu beachten, dass alle möglichen Wortkombinationen für jedes der Teilwörter durchsucht werden müssen (bei w = 3 und einer Protein-Anfragesequenz müssen
demzufolge 203 = 8000 Wörter geprüft werden). Der Algorithmus für DNA-Sequenzen ist
dahingehend einfacher, dass der erste Schritt der Hit-Lokalisierung wegfällt. Die Liste der
w-mere besteht dort einfach aus allen Teilwörtern der Anfragesequenz selbst, also genau
|Q| − w + 1 Elementen.
Durch die Beschreibung des Expansionsschritts wird deutlich, dass BLAST ohne Lücken
aligniert. Dieses Vorgehen leistet einen gewissen Beitrag zur Geschwindigkeitssteigerung
15
KAPITEL 3. SEQUENZVERGLEICHE
gegenüber einem rigorosen Algorithmus. Es kann allerdings festgehalten werden, dass der
eigentliche Vorteil von BLAST in der Anwendung zweier Heuristiken liegt:
1. Suche von Hits als Kandidaten
Die Chance einer gut bewerteten Alignierung ist an Stellen höher, an denen bereits
ein Teilwort-Paar mit einem hohen Wert zu finden ist. Anschaulich werden durch den
ersten BLAST-Schritt Elemente des Suchraums vorgeben, durch den eine Alignierung
laufen muss. Dieser Vorteil hat allerdings nur dann Bestand, wenn sich nicht zu viele
Alignierungen unterschiedlicher Hits überlappen, weil dann bestimmte Zeichenpaare
mehrfach untersucht werden. Die Heuristik kann durch den Parameter T gesteuert
werden.
2. Abbruchbedingung für die alignment-Expansion
Die Idee der Abbruchbedingung bei der Expansion ist, dass ein langer Abschnitt
schlecht bewerteter Residuenpaare selten durch einen daran anschließenden Abschnitt
hoher Ähnlichkeit wieder ausgeglichen wird. Deshalb wird die Alignierung abgebrochen. Die Heuristik kann durch den Parameter X gesteuert werden.
3.5.3
Erweiterungen des ursprünglichen Algorithmus
Die im vorigen Abschnitt beschriebene ursprüngliche Variante von BLAST hat einige Unzulänglichkeiten, die zu Erweiterungen des Algorithmus geführt haben:
• Oftmals werden mehrere Hits gefunden, die bei der Expansion die gleichen Residuenpaare bearbeiten und letztlich fast dieselben Alignierungen finden. Diese ”Doppelarbeit” vergrößert den Rechenaufwand ohne positiven Effekt auf das Ergebnis.
• Zur Berechnung vernünftiger Ergebnisse müssen die Parameter so eingestellt werden,
dass der Algorithmus viele Hits findet und jeden von ihnen expandieren muss. Da
der Erweiterungsschritt den größten Teil der Rechenzeit konsumiert, liegt es nahe,
die Zahl der Expansionen ohne Beeinträchtigung der Sensitivität zu reduzieren.
• Der Algorithmus aligniert Sequenzen ohne Lücken. Oftmals könnten signifikantere
Alignierungen gebildet werden, wenn zwei oder mehr herkömmliche Alignierungen
durch Einfügen von Lücken vereinigt würden.
Zur Lösung dieser Probleme wurden zwei Erweiterungen von BLAST entwickelt [2]:
1. Für das Problem der mehrfachen Betrachtung gleicher Teilsequenzpaare und zur Reduktion der Expansionen wird die Zwei-Hit-Methode vorgeschlagen. Sie besteht darin, nur diejenigen Hits zu erweitern, die in ihrer Nachbarschaft einen weiteren, nicht
überlappenden Hit haben. Der Abstand der Hits ist auf beiden Sequenzen gleich und
darf einen festgelegten Wert nicht überschreiten. Um eine vergleichbare Sensitivität
zu erreichen, muss der T -Parameter gegenüber dem Originalalgorithmus verringert
werden. Das führt zwar zu mehr Hits, aber zu weniger Expansionen. Die Methode
dient hauptsächlich der Geschwindigkeitsverbesserung.
2. Zur lückenbehafteten Alignierung wird zunächst der normale Algorithmus ausgeführt.
Nach der Expansion wird der Wert eines HSP mit einem Schwellwert verglichen.
16
KAPITEL 3. SEQUENZVERGLEICHE
Liegt das HSP über der Schwelle, so wird eine lückenbehaftete Alignierung mittels
dynamischer Programmierung angestoßen. Der Unterschied zum Smith-WatermanAlgorithmus ist, dass ein Residuenpaar durch das HSP festgelegt wird, durch das die
Alignierung laufen muss. Damit wird nicht der gesamte Suchraum betrachtet. Außerdem wird auch hier ein dropoff -Wert zum Abbruch der lückenbehafteten Alignierung verwendet. Dieses Gapped BLAST genannte Verfahren dient im Gegensatz zur
Zwei-Hit-Methode nicht dazu, die Geschwindigkeit zu steigern, sondern signifikante
Alignierungen zu detektieren, die dem ursprünglichen BLAST verborgen geblieben
wären.
3.5.4
Programmvarianten von BLAST
BLAST ist sowohl für den Vergleich von sowohl Protein- als auch DNA-Sequenzen geeignet.
Deshalb wurden mehrere Programmvarianten entwickelt:
• blastp
Diese Variante vergleicht eine Protein-Anfragesequenz mit Sequenzen einer Proteindatensammlung. Alle Erweiterungen des Algorithmus sind anwendbar.
• blastn
blastn vergleicht eine DNA-Anfragesequenz mit Sequenzen einer DNA-Datensammlung. Eine DNA-Sequenz besitzt zwei Leserichtungen (strands): die normale Sequenz
sowie die umgekehrte Reihenfolge der Residuen der Komplementärsequenz (vergleiche
Abschnitt 2.1). Es kann eingestellt werden, welche der Leserichtungen der Anfragesequenz analysiert werden sollen. Auch für diese Variante sind alle Erweiterungen
von BLAST anwendbar.
• blastx
Dieses BLAST-Programm vergleicht alle Übersetzungen einer DNA-Anfragesequenz
mit allen Sequenzen einer Proteindatensammlung. Dabei wird die DNA-Sequenz in
alle sechs möglichen Proteinsequenzkodierungen (Leseraster, engl.: reading frames)
übersetzt. Jede dieser Kodierungen stellt eine Protein-Anfragesequenz im oben genannten Sinne dar. Die Anzahl der verschiedenen Leseraster ergibt sich aus der Kombination der drei Übersetzungsraster einer Sequenz bei der Translation (vergleiche
Abschnitt 2.2) mit den beiden Leserichtungen einer DNA-Sequenz. Als zusätzlicher
Parameter für diese BLAST-Variante ist der zu verwendende genetische Code für die
Translation anzugeben. Alle Erweiterungen von BLAST sind anwendbar.
• tblastn
tblastn vergleicht eine Protein-Anfragesequenz mit den Leserastern aller Sequenzen
einer DNA-Datensammlung. Es stellt die Umkehrung von blastx dar. Auch hier
ist der genetische Code für die DNA-Sequenzen anzugeben. Alle Erweiterungen von
BLAST sind anwendbar.
• tblastx
Dieses Programm vergleicht alle Leseraster einer DNA-Anfragesequenz mit den Leserastern aller Sequenzen einer DNA-Datensammlung und fasst damit blastx und
tblastn zusammen. Durch die Leseraster von sowohl Anfrage- als auch Vergleichssequenz sind hier 6 · 6 = 36 BLAST-Durchläufe pro Sequenzpaar durchzuführen.
17
KAPITEL 3. SEQUENZVERGLEICHE
Deshalb wird die Anwendung dieser Variante nur für spezielle Problemstellungen
empfohlen. Für die Anfrage- und die Vergleichssequenz(en) muss jeweils der genetische Code übergeben werden. Die lückenbehaftete Alignierung wird für tblastx nicht
unterstützt.
3.5.5
PSI-BLAST
Altschul et al. [2] haben eine Erweiterung von BLAST entwickelt, die zur Detektion schwacher Homologien entwickelt wurde. Diese PSI-BLAST (Position-Specific Iterated BLAST )
genannte Erweiterung erzeugt eine positionsspezifische Bewertungsmatrix (PSSM, PositionSpecific Scoring-Matrix ) aus einem BLAST-Lauf, um mit deren Hilfe weitere BLAST-Läufe
zu starten und weitere verwandte Sequenzen zu entdecken.
Ausgangspunkt für eine Anwendung von PSI-BLAST ist ein normaler BLAST-Lauf.
Das Ergebnis, eine nach aufsteigendem Erwartungswert geordnete Liste von alignierten Sequenzen, wird zur Bildung einer PSSM herangezogen. Eine solche Matrix unterscheidet sich
von einer herkömmlichen Bewertungsmatrix dadurch, dass die Elemente der Matrix nicht
Bewertungen von Residuenpaaren, sondern Residuum-Position-Paaren sind. Folglich werden darin Werte der Form s(Residuum, Position in der Query Q) gespeichert. Die PSSM
hat die Dimension 20 × |Q|. Zur Bildung der Matrix sei auf [2] verwiesen.
3.5.6
PHI-BLAST
Proteinfamilien werden oft über spezielle Sequenzmuster charakterisiert. Ein Beispiel ist
die PROSITE -Datenbank [3], bei deren Aufbau das Ziel bestand, diejenigen Muster zu
speichern, die für die Funktion einer Proteinfamilie relevant sind. Solche Muster oder Signaturen können als reguläre Ausdrücke notiert werden [6] und stellen damit eine Art Grammatik für Segmente aus den Proteinsequenzen der entsprechenden Familie dar. Hauptziel
ist es, mit einer Signatur möglichst alle Proteine der Familie darstellen zu können und keine
Sequenzen mit völlig anderer Funktion abzubilden.
Eine Weiterentwicklung von BLAST, PHI-BLAST (Pattern-Hit Initiated BLAST ) [30],
basiert auf der Idee solcher Muster (engl.: patterns). Dabei wird dem Programm zusätzlich
zu einer Anfragesequenz ein Muster übergeben, das in dieser Sequenz enthalten sein muss.
Der PHI-BLAST-Algorithmus sucht dann in den Vergleichssequenzen nach dem Muster.
Alle Sequenzen, die das Muster enthalten, werden mit dessen Hilfe zur Anfragesequenz
aligniert. Die Alignierung kann nun als HSP im Sinne von Gapped BLAST betrachtet
werden, bildet also den Ausgangspunkt für eine lückenbehaftete Alignierung.
18
Kapitel 4
Analyse des
BLAST-Programmcodes
Für die Integration von BLAST in ein objektrelationales Datenbanksystem ist es notwendig,
dessen Quelltext zu analysieren. Dabei werden speziell die Module betrachtet, die für
die Integration modifiziert werden müssen. Die Anpassung von BLAST umfasst die in
Abschnitt 3.5.4 vorgestellten Varianten.
4.1
Das NCBI-Toolkit
Die Algorithmen von BLAST werden in mehreren Programmen und Softwarewerkzeugen
benutzt, die zum NCBI Software Development Toolkit gehören. Im Rahmen dieser Diplomarbeit wird die Version 2.1.2 des Toolkits verwendet. Die darin enthaltenen Werkzeuge dienen der Analyse verschiedener biologischer Daten. Sie sind in der Programmiersprache C
geschrieben und basieren auf der Funktionsbibliothek NCBI CoreLib, die es erlaubt, plattformunabhängig Software zu entwickeln. Folgende Funktionsgruppen werden in CoreLib
implementiert:
• Funktionen zum Setzen und Auslesen von Programmparametern und Umgebungsvariablen
• Funktionen für graphische Benutzeroberflächen
• Funktionen zur Verwaltung von Konfigurationsdateien
• Fehlerbehandlungsfunktionen
• Dateisystemfunktionen
• Speicherverwaltungsfunktionen
• Zeichenkettenfunktionen
• Mathematische Funktionen
• Funktionen zur Verwaltung von Prozessen und Threads
19
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
Abbildung 4.1: Die von BLAST verwendeten Dateien
Einige der hier aufgeführten Funktionen bilden lediglich die entsprechenden ANSI-C -Funktionen ab.
Im Rahmen des Toolkits wird ein Datenmodell für biotechnologisch relevante Daten verwendet, das in der Sprache ASN.1 (Abstract Syntax Notation, [19]) spezifiziert ist. ASN.1Dokumente beschreiben konkrete Datensätze und können zum Datenaustausch benutzt
werden. Zum Einlesen und Speichern von ASN.1-Spezifikationen stellt die Toolbox die
Funktionsbibliothek AsnLib zur Verfügung, die aus den Spezifikationen Parse-Bäume erstellt. Mit ihrer Hilfe lassen sich Daten in ASN.1 kodieren und dekodieren. Im Kontext von
BLAST kann die Vergleichssequenzdatei das ASN.1-Format haben, außerdem können die
Ergebnisalignierungen als ASN.1-Datei ausgegeben werden. Im Normalfall wird für die Sequenzdatensammlung das FASTA-Format verwendet, und die Ergebnisse werden in Form
des BLAST-Reports ausgegeben. Deshalb soll hier nicht näher auf ASN.1 eingegangen
werden.
Das Toolkit verwendet eine Setup-Datei namens .ncbirc (Unix) bzw. ncbi.ini (Windows). Sie spezifiziert einen Verzeichnispfad, der unter anderem die BewertungsmatrixDateien beherbergt. Die Setup-Datei muss sich im Arbeitsverzeichnis desjenigen Benutzers
befinden, der ein Werkzeug aus dem Toolkit aufruft.
4.2
Die Module des Programms BLAST
Die BLAST-Varianten blastp, blastn, blastx, tblastn und tblastx sind im Werkzeug blastall
zusammengefasst. blastall ist ein Kommandozeilenprogramm, das mit Hilfe von Aufrufparametern (siehe Anhang A) gesteuert wird. Als Eingabe verwendet das Programm zwei
Dateien, die Anfragesequenzdatei und die Datensammlungs-Datei. blastall produziert als
Ausgabe eine Report-Datei, die die Alignierungen enthält. Abbildung 4.1 stellt das Zusammenspiel der verschiedenen Dateien dar.
Zur Anpassung des Programms wird zunächst auf die Kern-Dateien von BLAST eingegangen:
20
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
• blastall.c
Diese Datei enthält die Main()-Funktion des Programms. Sie wird von der eigentlichen main()-Funktion der CoreLib-Bibliothek aufgerufen, die eine genormte Schnittstelle für verschiedene Programme und deren Parameterverarbeitung darstellt.
• blast.c fasst die Funktionen für den BLAST-Algorithmus zusammen. Dazu gehören
Funktionen zur Initialisierung von BLAST-Datenstrukturen sowie zur Ausführung
der einzelnen Algorithmusschritte.
• blastkar.c
In dieser Datei sind Funktionen zur Bewertung von Alignierungen und zur Berechnung
von Entscheidungsvariablen enthalten. Unter anderem wird damit die Relevanz von
Alignierungen bewertet.
• In blastutl.c befinden sich Hilfsfunktionen zur Reservierung und Freigabe von BLASTDatenstrukturen sowie zur Auswertung der Programmparameter. Die Funktionen
bilden das Gerüst für den BLAST-Algorithmus und werden deshalb nicht nur von
blastall, sondern auch von anderen Werkzeugen verwendet.
• blastool.c umfasst mehrere Funktionsgruppen. Zunächst enthält es Funktionen zum
Setzen von Standardwerten für die Eingabeparameter und für statistische Parameter von BLAST. Das Modul ist außerdem für die Formatierung des BLAST-Reports
verantwortlich.
• gapxdrop.c
Dieses Modul enthält die Implementation für die lückenbehaftete Alignierung und für
deren Darstellung im BLAST-Report.
• Die Funktionen des Moduls lookup.c implementieren den BLAST-Vorverarbeitungsschritt der w-mer-Bildung. Sie erzeugen und verwalten eine Indexstruktur für die
w-mere, mit deren Hilfe die Hits in den Vergleichssequenzen gesucht werden.
• readdb.c
Das Modul readdb ist die ”Datenbank”-Schnittstelle zu den Sequenzdateien. Da es
maßgeblicher Gegenstand der BLAST-Anpassung an eine relationale Datenbank ist,
wird sein Aufbau im folgenden Abschnitt 4.3.3 detailliert erläutert.
• dust.c
Gegenstand dieses Moduls ist der Filteralgorithmus DUST, der für die Filterung der
Nukleotid-Anfragesequenz verwendet wird.
• seg.c
Dieses Modul behandelt den Filteralgorithmus SEG [29], der für die Filterung der
Aminosäure-Anfragesequenz verwendet wird. SEG wurde als eigenständiges Programm entwickelt und ist mittlerweile Bestandteil des Toolkits.
• tofasta.c dient der Bearbeitung von Dateien im FASTA-Format (siehe Anhang B.1).
Die Aufzählung beinhaltet ausschließlich Dateien, die unmittelbar für den Ablauf von
BLAST benötigt werden.
21
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
4.3
4.3.1
Die ”Datenbank”-Schnittstelle von BLAST
BLAST-Datensammlungen
Bei einer BLAST-Sequenzdatensammlung handelt es sich um eine Datei im FASTA-Format,
in der Sequenzen und deren Kennungen gespeichert sind. Damit BLAST diese Datensammlung verwenden kann, wird sie vom Werkzeug FormatDB, das Bestandteil des NCBIToolkits ist, formatiert. Der Dateiname <Datensammlungsname>.nt deutet auf eine Nukleotid-Datensammlung hin, und <Datensammlungsname>.aa steht für eine Aminosäure-Datensammlung. Nach der Formatierung durch FormatDB entstehen aus den FASTA-Dateien
folgende Dateien (als Beispiel dient hier die Datensammlung ecoli.aa):
• ecoli.aa.phr enthält die Kennungen der Vergleichssequenzen. Für Nukleotid-Datensammlungen ist die Dateiendung .nhr.
• ecoli.aa.psq enthält die eigentlichen Sequenzen. Diese sind nicht im ASCII-Format
der ursprünglichen FASTA-Datei abgelegt, sondern werden kodiert gespeichert. Die
Dateiendung für Nukleotid-Datensammlungen ist .nsq.
• In ecoli.aa.pin sind allgemeine Kennwerte der Datensammlung sowie mehrere Indizes abgelegt. Die Indizes verweisen auf die Positionen der Sequenzkennungen und
der Sequenzen in den anderen beiden Dateien. Die Dateiendung für NukleotidDatensammlungen ist .nin.
Die konkreten Dateiformate beschreibt Anhang B.2. Diese Dateien dienen als Eingabe für
blastall (Abbildung 4.1). Dabei gibt es mehrere Möglichkeiten, dem Programm mitzuteilen,
welche Sequenzen für den Vergleich mit einer Anfragesequenz benutzt werden sollen:
1. Auf der Kommandozeile werden mit der blastall -Option -d eine oder mehrere Datensammlungsnamen angegeben. Die Namen entsprechen denen der FASTA-Dateien,
obwohl BLAST nur die daraus formatierten Dateien verwendet. Das Konzept zum
Umgang mit mehreren Datensammlungen besteht darin, alle Sequenzen der übergebenen Datensammlungen zu nummerieren, beginnend beim Wert 0. Die Ordnung der
Sequenzen entspricht der in den Datensammlungsdateien, die Ordnung der Datensammlungsdateien entspricht der Reihenfolge bei der Kommandozeilenoption -d.
2. Zur Erzeugung einer virtuellen Datensammlung kann eine Alias-Datei erzeugt werden,
die mehrere reale Datensammlungen unter einem Namen zusammenfasst. Der Name
der Datei ist <Aliasname>.nal für Nukleotid- bzw. <Aliasname>.pal für Proteindatensammlungen. Die Angabe von <Aliasname> bei der Option -d ist gleichbedeutend
mit der Angabe der darin enthaltenen realen Datensammlungsnamen. Alias-Dateien
können außerdem zwei spezielle Abschnitte enthalten:
(a) Die Angabe einer Liste von Ordnungszahlen (OIDList) schränkt die BLASTSuche auf die Vergleichssequenzen mit den entsprechenden Ordnungszahlen ein.
(b) Die Angabe einer Liste von GenInfo-Kennungen (GI-IDs) schränkt die BLASTSuche auf die Vergleichssequenzen mit den entsprechenden Kennungen ein. Auf
die GI-IDs wird im folgenden Absatz eingegangen.
22
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
3. Mit der Option -l wird eine Datei angegeben, die eine Liste von GenInfo-IDs umfasst
(GI-Datei ). Die BLAST-Suche beschränkt sich dann auf die Vergleichssequenzen mit
den entsprechenden GI-IDs.
Die Angabe mehrerer Datensammlungen kann auf mehreren Ebenen erfolgen, d.h. Realund Alias-Datensammlungen können gemischt bei -d angegeben werden. Außerdem können
Aliasdateien andere Aliasdatensammlungen enthalten.
Die GenInfo-IDs entstammen einer Datenbank am NCBI, der ”ID”-Datenbank. Sie
wird immer dann aktualisiert, wenn beim NCBI eine neue Sequenz von einer der großen
Sequenzdatenbanken (z.B. PIR, SWISSPROT ) registriert wird. Diese Sequenzen besitzen
gemäß einer Nomenklatur eine Kennung, die unter anderem ihre Datenbank-Herkunft angibt. Die GenInfo-Kennungen (GI-IDs) des NCBI dienen dazu, auf alle Sequenzen über eine
einheitliche Nummerierung zuzugreifen. Der Aufbau einer GI-Kennung ist der folgende:
gi|<ID in der ID-Datenbank>
Die Kennung einer Sequenz in einer FASTA-Datei ist meist eine Aneinanderkettung der
GI-ID und der Kennung, die vom ”Erzeuger” der Sequenz (z.B. PIR) vergeben wurde.
4.3.2
Die interne Kodierung von Sequenzen
Sowohl die Anfragesequenz als auch die Vergleichssequenzen werden für die Benutzung im
BLAST-Algorithmus kodiert. Da die Kodierung von Aminosäure- und Nukleotidsequenzen
unterschiedlich ist, werden sie in getrennten Unterabschnitten behandelt.
4.3.2.1
Die Kodierung von Nukleotidsequenzen
Nukleotid-Sequenzen werden in einer FASTA-Datei ebenfalls mit einem Buchstaben (siehe
Tabelle 2.2) pro Nukleotid dargestellt. Für diese Sequenzen werden zwei Kodierungen
verwendet. Die erste Kodierung orientiert sich an der Überlegung, dass fast alle Residuen
in einer Sequenz Elementarresiduen sind. Die meisten Sequenzen enthalten demnach nur
die eindeutigen Nukleotide A, C, G und T. Für solche Sequenzen genügt die NCBI2naKodierung (Tabelle 4.1). Die Kodierung verwendet 2 bit pro Residuum, somit können vier
Residuen in einem Byte kodiert werden. Die Residuen aus der Sequenz werden zuerst in
den signifikantesten Bits gespeichert, d.h. das erste Residuum einer Sequenz wird in den
Bits 7 und 8 des ersten Bytes abgelegt und so fort. Für das Ende der Sequenz werden zwei
Fälle unterschieden:
1. Falls das letzte Byte vollständig belegt wird, bedeutet dies, dass die Länge der ursprünglichen Sequenz ein Vielfaches von vier ist. In diesem Fall wird ein Null-Byte
an die kodierte Sequenz angehängt.
2. Falls das letzte Byte unvollständig belegt wird, bedeutet dies, dass die Länge der ursprünglichen Sequenz nicht durch vier teilbar ist. Weiterhin hat das die Auswirkung,
dass die Bits 1 und 0 des letzten Byte in jedem Fall unbelegt sind. In diesen Bits wird
deshalb die Anzahl der Residuen in dem Byte eingetragen, also der Rest der Division der Länge durch vier (ein Wert zwischen 1 und 3). Damit kann die tatsächliche
23
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
Symbol
A
C
G
T
NCBI2na-Code
0
1
2
3
Name
Adenin
Cytosin
Guanin
Thymin/Uracil
Tabelle 4.1: NCBI2na-Kodierung von Nukleotiden
Symbol
A
C
M
G
R
S
V
T
W
Y
H
K
D
B
N/X
NCBI4na-Code
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BLASTna-Code
15
0
1
6
2
4
9
13
3
8
5
12
7
11
10
14
Tabelle 4.2: Mehrdeutigkeitskodierungen NCBI4na und BLASTna von Nukleotiden
Länge der Sequenz aus diesem Byte und der Länge der kodierten Sequenz determiniert
werden.
Da die meisten Sequenzen nur Elementarresiduen enthalten, können sie mit einem Viertel
des ursprünglichen Speicherbedarfs gespeichert werden. Der BLAST-Algorithmus ist auf
diese Kodierung abgestimmt, benötigt also weniger Zeit als ein vergleichbarer Algorithmus
auf der Basis von ”Ein-Residuum-Bytes”.
Die NCBI2na-Kodierung hat allerdings den Nachteil, dass Mehrdeutigkeitsresiduen
nicht eindeutig dargestellt werden können. Sie werden durch Zufallswerte im Bereich
{0, . . . , 3} repräsentiert. Sequenzen mit Mehrdeutigkeitsresiduen müssen deshalb eine zusätzliche Kodierung erfahren. Bei dieser zweiten Kodierung werden zusammenhängende,
gleichartige Residuen (also Ketten gleicher Buchstaben) gemeinsam in einem 4-Byte-Block
kodiert. Die einzelnen Bits haben folgende Bedeutung:
31 · · · 28
Code
27 · · · 24
Anzahl
23
···
Position in der Sequenz
0
Als Code für die entsprechenden Mehrdeutigkeitsresiduen wird NCBI4na aus Tabelle 4.2
verwendet. Die beiden Sequenzen werden im BLAST-Algorithmus nacheinander verwendet:
Zunächst wird mittels der NCBI2na-Kodierung aligniert, im Fall von Mehrdeutigkeiten
wird die Alignierung mittels NCBI4na und BLASTna neu berechnet (”reevaluiert”).
24
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
Symbol
A
B
C
D
E
F
G
H
I
K
L
M
N
P
Q
R
S
T
V
W
X
Y
Z
U
*
Wert
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Name
Lücke
Alanin
Asparaginsäure oder Asparagin
Cystein
Asparaginsäure
Glutaminsäure
Phenylalanin
Glycin
Histidin
Isoleucin
Lysin
Leucin
Methionin
Asparagin
Prolin
Glutamin
Arginin
Serin
Threonin
Valin
Tryptophan
nicht festgelegtes oder atypisches Residuum
Tyrosin
Glutaminsäure oder Glutamin
Selenocystein1
Ende einer Sequenz
Tabelle 4.3: NCBIstdaa-Kodierung von Aminosäuren
4.3.2.2
Die Kodierung von Proteinsequenzen
Proteinsequenzen sind im FASTA-Format als Buchstabenfolgen gespeichert. Würde man
mittels des dort verwendeten Alphabets (siehe Tabelle 2.3) den Wert eines Residuenpaares
berechnen, müsste man als Indizes für die Substitutionsmatrix die ASCII-Werte der beiden Symbole verwenden. Zur Verkleinerung der Matrix und zur Vereinfachung von deren
Initialisierung wurde die NCBIstdaa-Kodierung (Tabelle 4.3) eingeführt, die jedem Symbol
einen Wert im Bereich {0, . . . , 25} zuordnet. Der Speicherbedarf einer Sequenz bleibt durch
die Kodierung unverändert, da jedes Residuum auch nach der Kodierung ein Byte belegt.
4.3.3
Dateischnittstellenfunktionen
Das Modul readdb wurde bereits als Dateischnittstelle von BLAST identifiziert. In diesem Abschnitt werden vor allem diejenigen Funktionen beleuchtet, die im Rahmen einer
Anpassung an relationale Datenbanken modifiziert werden müssen.
Grundlage des Moduls ist die Datenstruktur ReadDBFILE. Sie speichert alle relevanten Informationen einer BLAST-Datensammlung. Falls mehrere Datensammlungen von
1
Selenocystein ist im ursprünglichen Alphabet nicht enthalten. Es wird in seltenen Fällen aus dem
Basentriplett UGA kodiert, das eigentlich ein Stoppcodon ist [24].
25
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
blastall durchsucht werden, so wird jede von ihnen durch eine eigene ReadDBFILE-Struktur
repräsentiert. Die Strukturen werden als verkettete Liste gespeichert.
In readdb wird das Konzept von Hauptspeicherdateien (memory-mapped files, im Folgenden kurz MMFs) verwendet. Der Zugriff auf Hauptspeicherdateien ist analog zu dem
auf normale Dateien. Intern werden die Dateizugriffsfunktionen allerdings auf Zeigeroperationen im Hauptspeicher abgebildet. In readdb werden folgende Funktionen zum Zugriff
auf MMFs zur Verfügung gestellt
• Öffnen (NlmOpenMFILE()) eines MMF
• Schließen (NlmCloseMFILE()) des MMF
• Lesen (NlmReadMFILE()) von Daten aus dem MMF und gleichzeitiges Verschieben
des Dateizeigers
• Abfragen des Dateizeigers (NlmTellMFile())
• Setzen des Dateizeigers (NlmSeekInMFile())
Das Schreiben in MMFs ist nicht implementiert, da es nicht benötigt wird. Alle drei
Dateien (Sequenz-, Kennungs- und Indexdatei) einer BLAST-Datensammlung werden als
MMF verwendet. Die Sequenz- und die Kennungsdatei werden dabei ausschließlich über
die Funktionen NlmReadMFile() und NlmTellMFile() angesteuert. Die Index-Datei wird
hingegen ausgelesen und die in ihr enthaltenen Kennwerte in ReadDBFILE-Variablen gespeichert, ebenso die Index-Positionen im Hauptspeicher. Im Zusammenhang mit den MMFs
müssen noch die Funktionen
• ReadDBOpenMHdrAndSeqFiles() und
• ReadDBCloseMHdrAndSeqFiles()
erwähnt werden. Diese Funktionen öffnen bzw. schließen die Kennungs- und die Sequenzdatei durch Aufruf obiger MMF-Funktionen.
Im Rahmen einer BLAST-Anpassung sollten die hier aufgeführten Funktionen nicht
aufgerufen werden, da die entsprechenden Dateien durch die relationale Datenbank ersetzt
werden. Die folgenden Funktionen rufen die bisher genannten Funktionen auf oder greifen
auf die Indizes zu. Sie sind deshalb Kandidaten für eine Modifikation im Kontext der
blastall -Anpassung:
• readdb_new_internal() ist die zentrale Initialisierungsfunktion für die ReadDBFILEStruktur. Unter anderem öffnet sie die Datensammlungsdateien und liest Kennwerte
und Indizes aus.
• readdb_attach() dient der Vervielfältigung der ReadDBFILE-Datenstruktur. Unter
anderem werden dabei die Verwaltungsstrukturen für die MMFs kopiert.
• readdb_get_link() erhält als Argument die Ordnungszahl einer Sequenz. Die Funktion hat die Aufgabe, in der verketteten Liste der ReadDBFILE-Strukturen nach demjenigen Element (derjenigen Datensammlung) zu suchen, das die Sequenz mit der übergebenen Ordnungszahl enthält.
26
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
• readdb_destruct_element() gibt Speicherbereiche, die von ReadDBFILE-Elementen
belegt werden, wieder frei. Dabei werden auch die MMFs geschlossen.
• readdb_destruct() führt zunächst ReadDBCloseMHdrAndSeqFiles() aus und ruft
dann für jedes Element der ReadDBFILE-Liste readdb_destruct_element() auf.
• readdb_get_sequence() wird mit der Ordnungszahl einer Sequenz aufgerufen und
gibt die (kodierte) Sequenz und deren unkodierte Länge zurück. Zur Rückgabe der
Sequenz und zur Bestimmung von deren Länge wird auf den Index und das entsprechende MMF zugegriffen. Nukleotidsequenzen werden in der 2bit-Kodierung zurückgegeben.
• readdb_get_sequence_length() erhält als Parameter die Ordnungszahl einer Sequenz. Die Funktion gibt die unkodierte Länge der Sequenz zurück. Dabei wird auf
den Sequenz-Index zugegriffen.
• readdb_get_ambchar() gibt die 4-Byte-Kodierung der Mehrdeutigkeitsresiduen einer
Sequenz zurück. Zur Identifikation der Sequenz wird deren Ordnungszahl übergeben.
Die Funktion wird nur für Nukleotidsequenzen verwendet. Besitzt die Sequenz keine
Mehrdeutigkeitsresiduen, wird NULL zurückgegeben.
• readdb_ambchar_present() wird mit der Ordnungszahl einer Sequenz aufgerufen
und gibt Auskunft darüber, ob eine Sequenz Mehrdeutigkeitsresiduen enthält. In
diesem Fall gibt sie TRUE zurück, im negativen Fall FALSE.
• Die Funktion readdb_get_defline_ex() gibt die Kennung der Sequenz zurück, deren
Ordnungszahl übergeben wird. Dazu wird auf den Kennungsindex zugegriffen.
• readdb_get_header() hat eine ähnliche Funktion wie readdb_get_defline_ex().
Sie wird im Kontext von Alias-Dateien bzw. GI-Dateien (Option -l) verwendet und
ist deshalb für die BLAST-Anpassung unkritisch, da im Rahmen einer relationalen
Datenbank keine Alias-Datensammlungsdateien bzw. GI-Dateien verwendet werden.
• ReadOIDList() und OIDListFree() werden im Fall einer Alias-Datensammlung verwendet. Die Funktionen sind deshalb unkritisch für die BLAST-Anpassung.
• GetGisFromFile() (Modul blast.c) ruft die ReadDBOpen/CloseMHdrAndSeqFiles()Funktionen auf. GetGisFromFile() wird nur im Zusammenhang mit einer GI-Datei
verwendet und ist deshalb bei der Anpassung nicht von Interesse.
• do_the_blast_run() und BLASTSetUpSearchWithReadDbInternal() aus blast.c rufen ebenfalls ReadDBOpen/CloseMHdrAndSeqFiles() auf.
Eine Reihe von readdb-Funktionen liest Kennwerte einer Datensammlung bzw. aller beteiligten Datensammlungen aus den Elementen der ReadDBFILE-Struktur. Diese Funktionen
sind unkritisch, da die entsprechenden Variablen durch readdb_new_internal() gesetzt
werden. Der Vollständigkeit halber werden die Kennwerte-Funktionen im Anhang B.2 aufgelistet.
readdb-Funktionen, die in diesem Abschnitt nicht erwähnt werden, sind bei der Anpassung von blastall unkritisch in Bezug auf den MMF-Zugriff.
27
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
4.4
Der Ablauf von blastall
Die Verwendung der Dateischnittstelle ist in den Ablauf des gesamten Programms eingebettet. Dieser Abschnitt analysiert die wichtigsten Funktionen von blastall und geht
der Fragestellung nach, in welcher Weise die Funktionen die Dateischnittstelle nutzen und
welche Auswirkungen das auf die Anpassung von BLAST hat. Die dafür verwendeten Ablaufschemata einiger Funktionen sind in einem an die Sprache C angelehnten Pseudocode
beschrieben.
4.4.1
Die Main()-Funktion
Die Einstiegsfunktion des Programms blastall ist die Funktion Main(). Sie führt schematisch die folgenden Schritte aus:
1. Lesen der Aufrufparameter, Initialisierung einiger BLAST_OptionsBlk-Elemente
2. Öffnen der Eingabe- (Anfragesequenz) und der Ausgabe-Datei (BLAST-Report)
3. Initialisierung weiterer BLAST_OptionsBlk-Elemente mit Aufrufparameterwerten
4. Lesen und Dekodieren der Anfragesequenz
5. Ausgabe allgemeiner Informationen und Datensammlungskennwerte in die ReportDatei
6. Aufruf der BLAST-Hauptroutine BioseqBlastEngine() (Modul blastutl.c)
7. Ausgabe des BLAST-Reports (siehe Anhang B.3) in die Ausgabedatei
8. Freigabe dynamischer Variablen
Zur Speicherung der Programmparameter sowie davon abgeleiteter Variablen wird eine
Datenstruktur vom Typ BLAST_OptionsBlk verwendet. Sie dient zur Initialisierung der
Funktion BioseqBlastEngine(), die den Einstiegspunkt in die konkrete BLAST-Implementierung bildet.
4.4.2
Die Funktion BioseqBlastEngineByLocEx()
Die Funktion BioseqBlastEngineByLocEx() wird mittels folgender Aufrufkette erreicht:
BioseqBlastEngine()
↓
BioseqBlastEngineByLoc()
↓
BioseqBlastEngineByLocEx()
BioseqBlastEngineByLocEx() hat folgenden schematischen Ablauf:
1. Validierung der BLAST_OptionsBlk-Struktur. BLASTOptionValidateEx() überprüft,
ob die Werte von Programmparametern und davon abgeleitete Werte gültig sind und
zur verwendeten Programmvariante passen.
28
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
BioseqBlastEngineCore(searchBlk, optionsBlk)
{
if (PSI-BLAST)
[...]
[...]
do_the_blast_run(searchBlk);
if (BLASTN && GAPPED) {
[...]
for(i = 0; i < #hits; ++i) {
length = readdb_get_sequence_ex(...,&sequence,...)
seqalign = SumBlastGetGappedAlignmentWithTraceback(searchBlk,i,...,
sequence,length);
[...]
}
[seqaligns zu Liste verketten]
}
else if (GAPPED) {
[...]
for(i = 0; i < #hits; ++i) {
seqalign = BlastGetGapAlgnTbckWithReaddb(search, index, ...);
}
[seqaligns zu Liste verketten]
}
else {
if (PSI-BLAST)
[...]
else seqalign = GetSeqAlignForResultHitList(searchBlk,...);
}
return seqalign;
}
Abbildung 4.2: schematischer Ablauf von BioseqBlastEngineCore()
2. Initialisierung der BlastSearchBlk-Struktur. Diese Datenstruktur wird mit Hilfe der Funktion BLASTSetUpSearchByLocWithReadDbEx() vorbereitet. Zur Initialisierung gehören unter anderem der Aufruf von readdb_new_internal() sowie der
BLAST-Vorberechnungsschritt zur Bestimmung der w-mere. Die w-mer-Bildung wird
für Protein-Anfragesequenzen mit der Funktion BlastFindWords(), für NukleotidAnfragesequenzen mit BlastNtFindWords() durchgeführt.
3. Ausführung des eigentlichen Algorithmus. Die dafür verantwortliche Funktion ist
BioseqBlastEngineCore() (siehe folgender Abschnitt).
Die Datenstruktur BlastSearchBlk umfasst sämtliche Parameter, die für den Algorithmus
benötigt werden. In der Struktur werden auch die Ergebnisse zwischengespeichert.
4.4.3
Die Funktion BioseqBlastEngineCore()
Die Funktion BioseqBlastEngineCore() umfasst die Aufrufe all jener Funktionen, die
den eigentlichen Algorithmus ausführen. Der im Kontext von blastall relevante Teil der
29
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
Funktion ist in Abbildung 4.2 dargestellt. Der Ablauf besteht aus den folgenden Schritten:
1. Die Routine do_the_blast_run() führt den Schritt der lückenfreien Alignierung der
Anfragesequenz mit allen Vergleichssequenzen durch. Die Funktion nutzt die Fähigkeit des multithreading zur parallelen Ausführung des Algorithmus auf mehreren Sequenzen. Der Aufbau von do_the_blast_run() wird im Abschnitt 4.5 im Detail
erläutert.
2. Nach dem Aufruf von do_the_blast_run() sind die HSPs der lückenfreien Alignierungen in der BlastSearchBlk-Struktur abgelegt. Wurde das Programm als Gapped
BLAST gestartet (Programmparameter -g), so wird an dieser Stelle für jedes HSP
eine der folgenden Funktionen zur Bildung einer lückenbehafteten Alignierung aufgerufen:
• SumBlastGetGappedAlignmentWithTraceback() für die Variante blastn
• BlastGetGapAlgnTbckWithReaddb() für alle anderen Programmvarianten
Das Ergebnis beider Funktion ist eine verkettete Liste von SeqAlign-Elementen.
3. Ohne Angabe der -g-Option wird GetSeqAlignForResultHitList() aufgerufen, das
die Zwischenergebnisse aus der BlastSearchBlk-Struktur in eine verkettete Liste von
SeqAlign-Elementen konvertiert.
Die Datenstruktur SeqAlign dient der Speicherung von Alignierungen jeden Typs. Da sie
bei der Anpassung von Bedeutung ist, wird sie im Abschnitt 4.6 detailliert beschrieben.
4.4.4
Die Funktion do blast search()
Zur Funktion do_blast_search() gelangt man mittels folgender Aufrufkette:
BioseqBlastEngineCore()
↓
do_the_blast_run()
↓
do_blast_search()
do_blast_search() wird entweder direkt oder durch den Start eines thread s aufgerufen.
Die Funktion ermittelt lückenfreie Alignierungen der Anfragesequenz mit den Vergleichssequenzen. Zum schematischen Ablauf der Funktion (siehe Abbildung 4.3) gibt es die
folgenden Bemerkungen:
• Die Funktion BlastGetDbChunk() liefert den von der Funktion do_blast_search()
zu bearbeitenden Bereich der Datensammlung. startID ist die Ordnungszahl der
ersten zu bearbeitenden Sequenz, und stopID zeigt hinter die letzte zu bearbeitende
Sequenz. Die Bedeutung dieser Funktion liegt in der Verteilung von Datensammlungschunks (engl.: chunk – Stück, Klotz) für den Fall, dass mehrere threads verwendet
werden. Im Normalfall gibt BlastGetDbChunk() die gesamte Datensammlung als
Bereich zurück.
30
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
do_blast_search(searchBlk)
{
while(BlastGetDbChunk(searchBlk, &startID, &stopID))
{
[...]
for(index = startID; index < stopID; ++index)
{
BLASTPerformSearchWithReadDb(searchBlk, index);
[...]
BlastReapHitListByEvalue(searchBlk);
BlastReevaluateWithAmbiguities(searchBlk, index);
BlastSaveCurrentHitlist(searchBlk);
}
}
[...]
}
Abbildung 4.3: schematischer Ablauf von do blast search()
• Die for-Schleife iteriert über alle Sequenzen im durch startID und stopID festgelegten Bereich. Folglich werden immer zusammenhängende Stücke der Datensammlung
bearbeitet.
• Die Funktion BLASTPerformSearchWithReadDb() liest die Sequenz mit der Ordnungszahl i ein und führt den BLAST-Algorithmus aus.
• BlastReapHitListByEvalue() entfernt Alignierungen zwischen Vergleichs- und Anfragesequenz aus der Ergebnisstruktur, falls deren berechneter Erwartungswert größer
ist als der dem Programm übergebene Schwellwert (Programmparameter -e).
• Die Funktion BlastReevaluateWithAmbiguities() betrachtet noch einmal jene Sequenzen gesondert, die Mehrdeutigkeitsresiduen enthalten, da deren Alignierungen
wegen der Zufallskodierung in NCBI2na möglicherweise fehlerhafte Werte liefern. Die
Funktion ist nur für Nukleotidsequenzen relevant.
• BlastSaveCurrentHitlist() integriert die aus der Sequenz gewonnene Liste von
Alignierungen (hitlist) in die HSP-Liste aller Sequenzen. Die hitlist ist eine Zwischenstruktur und darf nicht mit der Datenstruktur SeqAlign verwechselt werden.
Das Pendant von do_blast_search() ist do_gapped_blast_search(). Diese Funktion
wird auf die gleiche Art und Weise aufgerufen. Sie führt keine lückenbehaftete Alignierung durch (obwohl der Name das suggeriert), sondern bildet lediglich die HSPs für eine
anschließende Ausrichtung mit Lücken.
Die Funktionen do_blast_search() und do_gapped_blast_search() haben einen ähnlichen Aufbau. Deswegen wird letztere nicht gesondert erläutert. In den folgenden Abschnitten werden diese Funktionen unter dem Namen ..._blast_search() gemeinsam
betrachtet, sofern nicht nur eine von ihnen gemeint ist.
31
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
BLASTPerformSearchWithReadDb(searchBlk,seqNum)
{
length = readdb_get_sequence(searchBlk->rdfp, seqNum, &seq);
[...]
BLASTPerformSearch(searchBlk, length, seq);
}
Abbildung 4.4: schematischer Ablauf von BLASTPerformSearchWithReadDb()
BlastReevaluateWithAmbiguities(searchBlk,seqNum)
{
if (searchBlk->prog_number == {BLASTP || BLASTX} )
exit;
if (GAPPED BLAST)
exit;
if (! HITS FOUND)
exit;
if (readdb_ambchar_present(searchBlk->rdfp, seqNum)) == FALSE)
exit;
[...]
bsp = readdb_get_bioseq(searchBlk->rdfp, seqNum);
[Reevaluation...]
}
Abbildung 4.5: schematischer Ablauf von BlastReevaluateWithAmbiguities()
4.4.5
Die Funktion BLASTPerformSearchWithReadDb()
Von ..._blast_search() wird die Funktion BLASTPerformSearchWithReadDb() aufgerufen. Diese liest mit Hilfe von readdb_get_sequence() die Sequenz mit der übergebenen Ordnungszahl und übergibt diese der Funktion BLASTPerformSearch(), die die Sequenz mit der Anfragesequenz aligniert. Das Ergebnis wird in BlastSearchBlk-Variablen
gespeichert. Abbildung 4.4 stellt den schematischen Aufbau der Funktion dar.
4.4.6
Die Funktion BlastReevaluateWithAmbiguities()
BlastReevaluateWithAmbiguities() ist für die Alignierung von Nukleotidsequenzen von
Bedeutung, die Mehrdeutigkeitsresiduen enthalten. In Abschnitt 4.3.2.1 wurde bereits darauf hingewiesen, dass Mehrdeutigkeiten durch die NCBI2na-Kodierung einen Zufallswert
erhalten. Aus diesem Grund werden Sequenzen mit Mehrdeutigkeiten durch die Funktion
BlastReevaluateWithAmbiguities() neu aligniert.
32
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
Abbildung 4.5 zeigt das Ablaufschema von BlastReevaluateWithAmbiguities(). Nach
dem Test auf Mehrdeutigkeitsresiduen in der Sequenz (readdb_ambchar_present()) wird
die Funktion readdb_get_bioseq() ausgeführt, die wiederum zu den folgenden readdbFunktionen verzweigt:
• readdb_get_descriptor()
• readdb_get_sequence()
• readdb_get_ambchar()
Die sich anschließende Reevaluation ruft keine weiteren Funktionen der Dateischnittstelle
auf.
Eine Besonderheit der Reevaluation ist, dass das Nukleotid-Bewertungsschema, das nur
zwischen matches und mismatches unterscheidet, hier nicht angewendet werden kann. Da
ein Mehrdeutigkeitsresiduum für mehrere Elementarresiduen stehen kann, muss eine Substitutionsmatrix verwendet werden. Die Substitutionsmatrix wird während der Initialisierung
aus den Werten für match und mismatch berechnet. Für die Indizierung der Matrix wird die
Kodierung BLASTna (siehe Tabelle 4.2) verwendet. Deshalb stellt das Programm die Konvertierungsfunktionen ncbi4na_to_blastna[] und blastna_to_ncbi4na[] zur Verfügung,
die die beiden Kodierungen NCBI4na und BLASTna ineinander umwandeln. Die Konvertierungsfunktionen sind als Feldvariablen realisiert.
4.5
Analyse des multithreading in BLAST
Das Programm blastall unterstützt sogenanntes multithreading. Das bedeutet, dass mehrere
Instanzen der gleichen Funktion (threads) parallel zueinander abgearbeitet werden können.
Damit kann das Vorhandensein mehrerer Prozessoren auf einer SMP -Maschine (Symmetric
Multiprocessing) ausgenutzt werden. Die Verwendung von mehr als einem Prozessor wird
bei blastall durch den Programmparameter -a eingestellt. Die Anzahl der Prozessoren wird
in diesem Abschnitt mit n bezeichnet.
Grundlage der Parallelisierung ist die Funktion do_the_blast_run(). Die Funktion
führt folgende Schritte aus (siehe Abbildung 4.6):
1. Zur Verwendung gemeinsamer Ressourcen durch die threads werden Mutexe initialisiert (NlmMutexInit()). Auf das Mutex-Konzept wird im folgenden Absatz näher
eingegangen.
2. Zur Initialisierung des multithreading wird die BlastSearchBlk-Struktur n − 1 mal
dupliziert (BlastSearchBlkDuplicate()). Jeder thread arbeitet damit auf seiner
eigenen Struktur. Beim Kopieren der Struktur werden die Zeiger auf die Indizes und
die MMFs mit kopiert. Die Indizes und die MMFs stellen somit geteilte Ressourcen
dar, die durch Mutexe verwaltet werden.
3. Die Funktion NlmThreadCreateEx() erzeugt jeweils einen thread, dem eine der beiden ..._blast_search()-Funktionen und eine BlastSearchBlk-Struktur zugeordnet wird. Die ..._blast_search()-Funktion wird dann im Kontext des threads
mit der BlastSearchBlk-Struktur als Parameter aufgerufen. Der thread ist beendet,
33
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
wenn die ihm zugeordnete Funktion beendet ist. Die threads laufen parallel sowohl
zueinander als auch zur Funktion do_the_blast_run().
4. NlmThreadJoin() ist die Synchronisationsfunktion für die threads. Die Funktion
kehrt erst zu do_the_blast_run() zurück, wenn alle threads beendet sind.
5. Die am Anfang initialisierten Mutex-Variablen werden wieder freigegeben.
In der Aufzählung wurde bereits das Mutex-Konzept erwähnt. Mutexe sind Synchronisationsobjekte. Sie dienen zur Zugriffssteuerung auf Ressourcen, die von mehreren threads
gemeinsam genutzt werden. Der Einsatz von Mutexen verhindert den gleichzeitigen Zugriff
zweier threads auf die gleiche Ressource. Mutexe haben folgende Eigenschaften:
1. Ein Mutex ist nicht an eine Ressource gebunden, sondern wird über Anweisungen an
beliebiger Stelle im Quelltext reserviert (lock ) und freigegeben (unlock ). Die Logik
der Mutex-Reservierung und -Freigabe obliegt dem thread und nicht einem zentralen
Mechanismus.
2. Ein thread erhält eine Mutex-Reservierung, wenn kein anderer thread den Mutex
reserviert hat.
3. Schafft ein thread es, einen Mutex zu reservieren, bleibt dieser solange reserviert, bis
der gleiche thread ihn wieder freigibt. Zwischen den Anweisungen für Reservierung
und Freigabe sollten die Anweisungen zur Bearbeitung der zugriffskritischen Ressource stehen.
4. Versucht thread 1, einen Mutex zu reservieren, der durch thread 2 reserviert ist, so
wartet thread 1 solange mit seiner Ausführung, bis er den Mutex selbst reserviert hat.
Die von BLAST verwendeten Mutexe gehören zum BlastSearchBlk-Element thr_info,
das eine Struktur zur Verwaltung der multithreading-relevanten Daten bereithält. Folgende
Mutexvariablen werden mit thr_info verwaltet:
• db_mutex
Dieser Mutex verwaltet die Zuteilung der Datensammlungsabschnitte. Bei der Beschreibung der Funktion do_blast_search() wurde bereits BlastGetDbChunk() erläutert. Diese Funktion teilt die Datensammlung auf und weist jedem thread einen
Bereich zum Bearbeiten zu. BlastGetDbChunk() führt in einer allen threads gemeinsamen Variable thr_info->db_chunk_last Buch darüber, welcher Datensammlungsbereich als nächstes berechnet werden muss. Der Zugriff auf diese Variable wird mit
dem Mutex gesteuert.
• results_mutex
Die Funktion BlastSaveCurrentHitlist() verwendet diesen Mutex, um die Alignierungen verschiedener Vergleichssequenzen in einer gemeinsamen Datenstruktur
(searchBlk->result_struct) abzulegen.
• ambiguities_mutex
BlastReevaluateWithAmbiguities() benötigt diesen Mutex zum Zugriff auf die
Mehrdeutigkeitskodierungen der Sequenzen.
34
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
do_the_blast_run(searchBlk)
{
[...]
if (NlmThreadsAvailable() && searchBlk->pbp->process_num > 1) {
/*** 1. Fall: mehrere Threads ***/
[...]
/*** INITIALISIERUNG Mutexe ***/
NlmMutexInit(searchBlk->thr_info->db_mutex);
[ ...thr_info->results_mutex, ...thr_info->ambiguities_mutex ]
/*** DUPLIKATION der SearchBlk-Struktur ***/
array = malloc((searchBlk->pbp->process_num)*sizeof(BlastSearchBlkPtr));
array[0] = searchBlk;
for (index=1; index<searchBlk->pbp->process_num; index++)
array[index] = BlastSearchBlkDuplicate(searchBlk);
/*** ERZEUGEN von n Threads ***/
thread_array = malloc((searchBlk->pbp->process_num)*sizeof(TNlmThread));
for (index=0; index<searchBlk->pbp->process_num; index++) {
if (GAPPED BLAST)
thread_array[index] = NlmThreadCreateEx(do_gapped_blast_search,
array[index], [...] );
else
thread_array[index] = NlmThreadCreateEx(do_blast_search,
array[index], [...] );
}
/*** WARTEN, bis alle Threads beendet sind ***/
for (index=0; index<searchBlk->pbp->process_num; index++)
NlmThreadJoin(thread_array[index], [...] );
for (index=1; index<searchBlk->pbp->process_num; index++) {
[SAMMELN statistischer Werte aus den verschiedenen SearchBlk-Variablen...]
array[index] = BlastSearchBlkDestruct(array[index]);
}
free(array); free(thread_array);
NlmMutexDestroy(searchBlk->thr_info->db_mutex);
[ ...thr_info->results_mutex, ...thr_info->ambiguities_mutex ]
}
else /*** 2. Fall: EIN Thread ***/
if (GAPPED BLAST)
do_gapped_blast_search(searchBlk);
else
do_blast_search(searchBlk);
[...]
}
Abbildung 4.6: Schematischer Ablauf von do the blast run(). Die Variable array ist ein
Feld von SearchBlk-Variablen, thread array ist ein Feld von threads.
35
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
typedef struct seqalign {
char
type,
segtype;
short int
dim;
Score
*score;
void
*segs;
struct seqalign *next;
SeqLoc
*bounds;
SeqId
*master;
SeqAlignIndex
*saip;
GatherIndex
idx;
short int
alignID;
}
SeqAlign, *SeqAlignPtr;
Abbildung 4.7: Aufbau der Datenstruktur SeqAlign
4.6
Die Datenstruktur SeqAlign
Im bisherigen Verlauf der Analyse wurde bereits der Datentyp SeqAlign angeführt. Mit
SeqAlign werden verschiedene Alignierungsarten einheitlich dargestellt. Abbildung 4.7
zeigt den Aufbau des Datentyps als C-Struktur. Im Hinblick auf die Anpassung von BLAST
sind die folgenden Elemente von Interesse:
• next
Die Alignierungen der Sequenzen sind in einer verketteten Liste gespeichert. Die
Variable next zeigt auf das nachfolgende Element der Kette.
• type
Man unterscheidet vier Grundtypen von SeqAlign’s, wovon im Rahmen von blastall
zwei von Interesse sind:
1. Im Fall type == 2 (SAT_DIAGS) sind alle Alignierungen einer Vergleichssequenz
in einem einzigen SeqAlign-Element abgelegt. Die Alignierungen sind in einer
Liste gespeichert, die mit dem Element segs beginnt.
2. Der Wert type == 3 (SAT_PARTIAL) steht dafür, dass ein SeqAlign-Element
genau eine Alignierung enthält. Weitere Alignierungen der Vergleichssequenz
findet man in den nachfolgenden SeqAlign-Elementen.
• segtype, segs
Es werden sechs Möglichkeiten zur Speicherung von Alignierungen unterschieden. Im
blastall -Kontext sind davon drei von Interesse. Der Wert der Variablen segtype
bestimmt dabei, von welchem Datentyp das Element segs ist:
1. Im Fall segtype == 1 (SAS_DENDIAG) zeigt das Element segs auf eine Struktur
vom Typ DenseDiag. Dieser Typ von Alignierung wird von den lückenfreien
Varianten von blastn und blastp verwendet.
2. Der Fall segtype == 2 (SAS_DENSEG) wird von den lückenbehafteten Varianten
von blastn und blastp verwendet. segs zeigt dabei auf eine DenseSeg-Variable.
36
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
typedef struct stdseg {
short int
dim;
SeqId
*ids;
SeqLoc
*loc;
Score
*scores;
struct stdseg *next;
}
StdSeg, *StdSegPtr;
Abbildung 4.8: Aufbau der Datenstruktur StdSeg
3. Die BLAST-Translationsvarianten blastx, tblastn und tblastx verwenden den
Fall segtype == 3 (SAS_STD). segs ist hier ein Zeiger auf eine StdSeg-Variable.
• dim
Dieses Element enthält die Anzahl der alignierten Sequenzen. BLAST bildet immer
Alignierungen von jeweils zwei Sequenzen (dim == 2).
• score
Für den Fall type == 3 ist in dieser Variablen die Bewertung der Alignierung gespeichert.
4.6.1
StdSeg-Alignierungen
Die Datenstruktur StdSeg ist unter den oben genannten diejenige mit der größten Komplexität. Deshalb wird sie hier stellvertretend für die anderen beiden Alignierungstypen im
Detail erläutert.
Der Alignierungstyp StdSeg wird von den BLAST-Varianten blastx, tblastn und tblastx
für die Speicherung von sowohl lückenfreien als auch lückenbehafteten Alignierungen verwendet. Der Aufbau von StdSeg kann Abbildung 4.8 entnommen werden. Die Bedeutung
einiger Elemente von StdSeg hängt davon ab, welchen Wert die type-Variable des zugehörigen SeqAlign-Elements hat. Im Fall type == SAT_DIAGS speichert StdSeg eine lückenfreie
Alignierung der beteiligten Sequenzen. Die Elemente haben dann folgende Bedeutung:
• dim
Das Element dim hat die gleiche Bedeutung wie das gleichnamige SeqAlign-Element.
Im Fall von BLAST hat es immer den Wert 2. Die Bedeutung von dim ist unabhängig
vom type-Wert.
• scores
Dieses Element beinhaltet die Bewertung der Alignierung.
• loc
Die Variable ist ein Zeiger auf eine Liste mit zwei Elementen vom Typ SeqLoc. Die
beiden Elemente beschreiben die Alignierung. Die Bedeutung der SeqLoc-Struktur
wird nach der type-Fallunterscheidung im Detail erläutert.
• next
Der next-Zeiger verweist auf die nächste Alignierung der gleichen Sequenz.
37
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
Abbildung 4.9: Die beiden Möglichkeiten, StdSeg-Alignierungen zu speichern. Der obere
Fall stellt die Verknüpfungsstruktur für lückenfreie Alignierungen dar, die untere Abbildung die für lückenbehaftete Alignierungen. Zu beachten ist dabei, an welcher Stelle die
Bewertung der Alignierung jeweils abgelegt ist.
Liegt der Fall type == SAT_PARTIAL vor, so speichert StdSeg nur ein Segment einer lückenbehafteten Alignierung. Dabei ändert sich auch die Bedeutung der Elemente scores, loc
und next:
• Das Element scores wird in diesem Fall nicht benutzt, da die Bewertung der Alignierung im SeqAlign-Element score zu finden ist.
• loc ist auch in diesem Fall eine Liste zweier SeqLoc-Elemente, allerdings speichern
sie hier ein Segment einer lückenbehafteten Alignierung.
• next zeigt auf das nächste Segment der Alignierung.
Für den Fall der lückenbehafteten Alignierung muss der Begriff Segment einer Alignierung
geklärt werden. Ein Segment ist ein zusammenhängender Bereich von Residuenpaaren oder
Residuum-Lücken-Paaren mit genau einer der folgenden Eigenschaften:
1. Beide Sequenzen besitzen ausschließlich Residuen in dem Bereich.
38
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
typedef struct seqint {
long int
from,
to;
char
strand;
SeqId
*id;
IntFuzz
*if_from,
*if_to;
}
SeqInt, PNTR SeqIntPtr;
typedef int (*Nlm_FnPtr)(void);
typedef union dataval {
void
*ptrvalue;
long int *intvalue;
double
*realvalue;
short int *boolvalue;
Nlm_FnPtr funcvalue;
long long *bigintvalue;
}
DataVal, *DataValPtr;
typedef struct seqloc {
char
choice;
char
extended;
DataVal
data;
struct seqloc *next;
}
SeqLoc, PNTR SeqLocPtr;
Abbildung 4.10: Aufbau der Datenstrukturen SeqInt, DataVal und SeqLoc
2. Die Anfragesequenz (Sequenz 0) enthält eine Lücke über den gesamten Bereich.
3. Die Vergleichssequenz (Sequenz 1) enthält eine Lücke über den gesamten Bereich.
Der Unterschied zwischen den beiden type-Fällen wird in Abbildung 4.9 am Beispiel des
Alignierungstyps StdSeg dargestellt.
Zur Speicherung eines Segments bzw. einer lückenfreien Alignierung wird der loc-Zeiger
verwendet. Er zeigt auf das erste von zwei Variablen vom Typ SeqLoc. Der Aufbau der
Datenstruktur ist in Abbildung 4.10 dargestellt. Die Elemente von SeqLoc haben folgende
Bedeutung:
• Mit Hilfe des next-Zeigers verweist das erste Element der Liste auf das zweite Element. Für das zweite Element ist dieser Wert nicht gesetzt. Die beiden Elemente
repräsentieren das Segment bzw. die Alignierung für die Anfrage- und die Vergleichssequenz. Folgende Darstellung soll dies verdeutlichen:
StdSeg.loc → Anfragesequenz-SeqLoc .next → Vergleichssequenz-SeqLoc
• Die Elemente choice und data dienen der Speicherung von Daten dynamischer Da39
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
typedef struct score {
ObjectId
*id;
char
choice;
DataVal
value;
struct score *next;
}
Score, *ScorePtr;
Abbildung 4.11: Aufbau der Datenstruktur Score
tentypen. choice legt dabei fest, welche Art von Daten in data gespeichert werden.
Für die Speicherung von Alignierungen werden zwei choice’s unterschieden:
1. choice == SEQLOC_INT bedeutet, dass data.ptrvalue auf eine Variable vom
Typ SeqInt zeigt.
2. choice == SEQLOC_EMPTY steht dafür, dass die entsprechende Sequenz im aktuellen Segment eine Lücke hat. Die Länge der Lücke muss der anderen Sequenz
entnommen werden. Dieser Fall tritt nur bei lückenbehafteten Alignierungen
auf.
Die SeqInt-Struktur beinhaltet die konkreten Informationen, die eine Sequenz zu einer
Alignierung oder einem Segment beisteuert. Von Interesse sind in diesem Zusammenhang
die folgenden Elemente:
• from und to bezeichnen den Anfangs- und End-Offset der Alignierung in der Sequenz.
• strand bezeichnet die Leserichtung der Sequenz. Bei Proteinsequenzen steht hier
immer eine 0, für Nukleotidsequenzen existieren zwei Leserichtungen. Bei lückenbehafteten Alignierungen müssen nur die Werte der SeqLoc’s der ersten StdSeg-Struktur
betrachtet werden, da die Leserichtung der Sequenzen zwischen den Segmenten nicht
wechselt.
Es sei erwähnt, dass mit Hilfe dieser Struktur die Alignierungen aller BLAST-Varianten
dargestellt werden können. Die Programme blastn und blastp nutzen allerdings die einfacher strukturierten Typen DenseDiag und DenseSeg. Deren Aufbau kann Anhang C.1
entnommen werden.
4.6.2
Die Speicherung von Alignment-Bewertungen
In den Strukturen SeqAlign und StdSeg ist neben den Alignierungen auch deren Bewertung gespeichert. Da eine Alignierung mehrere Bewertungsmaße haben kann, müssen
diese in einer dynamischen Struktur abgelegt werden. Die Elemente score bzw. scores der
oben genannten Strukturen zeigen auf eine verkettete Liste verschiedener Bewertungsmaße,
die jede in einer Score-Datenstruktur abgelegt sind. Deren Aufbau kann Abbildung 4.11
entnommen werden. Die Elemente haben folgende Bedeutung:
• Das Element id ist vom Typ ObjectId, das ein Datenelement str enthält. Dieses
speichert den Namen der Bewertung.
40
KAPITEL 4. ANALYSE DES BLAST-PROGRAMMCODES
• Die Elemente choice und value stehen im gleichen Zusammenhang wie choice und
data in der SeqLoc-Datenstruktur. Im Zusammenhang mit Bewertungen gibt es zwei
sinnvolle choice-Werte. Hat choice den Wert 1, so ist in value der Ganzzahlwert
(long int) gesetzt, im Fall choice == 2 beinhaltet value einen Fließkommawert
(double).
• Der next-Zeiger verweist auf weitere Bewertungsmaße.
41
Kapitel 5
Verwendete Datenbankkonzepte
Ziel dieser Arbeit ist es, BLAST im Rahmen eines relationalen Datenbanksystems zu verwenden. Voraussetzung dafür ist eine relational modellierte Genomdatenbank.
Die Verknüpfung von BLAST mit einer relationalen Datenbank wird mit dem Datenbanksystem DB2 UDB realisiert. DB2 UDB (Universal Database) ist ein objektrelationales Datenbanksystem und wurde vom Unternehmen IBM entwickelt. Die im Kontext dieser
Diplomarbeit verwendete Version des Datenbanksystems ist DB2 Version 7.1 Enterprise
Extended Edition für das Betriebssystem Solaris.
Das Kapitel fasst die Konzepte zusammen, die aus Datenbanksicht für die Anpassung
von BLAST benötigt werden.
5.1
Datenmodellierung der Biosequenzen
Ein relationales Datenbanksystem zeichnet sich dadurch aus, dass die darin zu speichernden
Daten relational modelliert [9] werden müssen. Dieser Abschnitt geht auf den Teil des
Datenmodells ein, der die Biosequenzen beschreibt. Das Modell (Abbildung 5.1) verwendet
dafür die folgenden fünf Relationen:
1. Die Relation BioSequence fasst alle Sequenzen zusammen. Jede in der Datenbank
gespeicherte Sequenz erhält eine Kennung. Die Kennungen der Sequenzen sind im
Attribut BIOSEQID vom Typ BIGINT abgelegt.
2. NASequence fasst alle Nukleotidsequenzen zusammen. Die Relation umfasst die Attribute NASEQID vom Typ BIGINT und SEQTEXT vom Typ LONG VARCHAR. NASEQID
ist zugleich Fremdschlüssel auf das Attribut BIOSEQID aus der Relation BioSequence.
SEQTEXT enthält die Sequenzen als Zeichenketten. Die Zeichenketten verwenden das
Nukleotid-Alphabet [10].
3. Protein umfasst die Aminosäuresequenzen. Die Relation besteht aus den Attributen PROTEINID vom Typ BIGINT und SEQTEXT vom Typ LONG VARCHAR. PROTEINID
ist zugleich Fremdschlüssel auf das Attribut BIOSEQID aus BioSequence. SEQTEXT
enthält die Aminosäuresequenzen als Zeichenketten. Die Zeichenketten verwenden
das Alphabet der Aminosäuren [20].
42
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
Abbildung 5.1: Ausschnitt des Genom-Datenmodells. Die unterstrichenen Attribute sind
Primärschlüssel, die kursiv gesetzten sind Fremdschlüssel.
4. In der Relation DNA sind die DNA-Sequenzen abgelegt. Sie werden über das einzige
Attribut DNASEQID vom Typ BIGINT identifiziert. Dieses ist zugleich Fremdschlüssel
auf das Attribut NASEQID der Relation NASequence.
5. In RNA werden die RNA-Sequenzen modelliert. Das Schlüsselattribut ist RNASEQID
vom Typ BIGINT, das zugleich Fremdschlüssel auf das Attribut NASEQID aus NASequence ist. Zwei weitere Fremdschlüsselattribute bilden die Beziehungen des zentralen Dogmas (vergleiche Abbildung 2.1) ab: DNASEQID ist ein Fremdschlüssel auf das
gleichnamige Attribut der Relation DNA und enthält für ein Tupel die Kennung der
DNA-Sequenz, aus der die RNA-Sequenz erzeugt wurde. PROTEINID ist ein Fremdschlüssel auf das gleichnamige Protein-Attribut und verweist auf die Aminosäuresequenz, in die die RNA-Sequenz übersetzt wurde.
Im Rahmen von BLAST sind die Relationen NASequence und Protein von besonderem
Interesse, da sie die Zeichenketten der Sequenzen enthalten.
Das gesamte Datenmodell der verwendeten Datenbank befindet sich in Anhang D. Dort
ist zu ersehen, welche Verknüpfungen zu anderen biologischen Informationen bestehen.
5.2
Anwendungsprogrammierung mit DB2
Anfragen an relationale Datenbanken erfolgen heutzutage überwiegend mit der Anfragesprache SQL (Structured Query Language). Sie ist aus der Sprache SEQUEL [8] entstanden. SQL ist ein ISO-Standard [17] und wird von vielen Herstellern von Datenbankmanagementsystemen (DBMS en) implementiert. Deshalb sind auf SQL basierende Anwendungen
relativ portabel.
Ursprünglich wurde SQL als interaktive Anfragesprache entwickelt. Die Benutzer kommunizieren dabei direkt mit dem Datenbanksystem, indem sie eine SQL-Anfrage formulieren und die Antwort sofort bekommen, wenn die Anfrage vollständig verarbeitet wurde.
43
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
Bei dieser Benutzung muss nicht extra ein Programm geschrieben werden, vielmehr gibt
es verschiedene Werkzeuge, die interaktiv SQL verarbeiten, wie z.B. der Kommandozeilenprozessor (Command Line Processor, CLP ) von DB2.
Häufig jedoch kommen Datenbanksysteme auch in Bereichen zur Anwendung, in denen
die meisten Anwender kein SQL formulieren können, sondern auf andere Möglichkeiten der
Eingabe zurückgreifen, wie zum Beispiel grafische Formulare und Eingabefelder. Für diese
Zwecke werden von Datenbanksystemen Schnittstellen definiert, um die Programmierung
solcher Anwendungssysteme zu ermöglichen. In den folgenden Unterkapiteln werden zwei
Schnittstellen vorgestellt, die DB2 zur Verfügung stellt: Embedded SQL sowie das Call Level
Interface.
5.2.1
Eingebettetes SQL
Die erste Möglichkeit, SQL in Anwendungsprogrammen zu verarbeiten, besteht in der Einbettung der SQL-Anweisungen in den Quelltext der Anwendung. Diese Möglichkeit kann
in Programmen verwendet werden, die in den ”Wirtssprachen” C, C++, FORTRAN und
COBOL. Programme dieser Art müssen zunächst von einem SQL-Precompiler verarbeitet
werden, der die eingebetteten (embedded ) SQL-Anweisungen durch Aufrufe von Laufzeitroutinen ersetzt. Das Ergebnis sind Programme, die von normalen Compilern übersetzt
werden können.
Im Fall der Sprache C müssen einer eingebetteten SQL-Anweisung die Wörter EXEC SQL
vorangestellt werden, damit der Precompiler die Anweisung erkennt. Um Variablen des Programms innerhalb einer SQL-Anweisung zu benutzen, müssen sie in einem vom Precompiler erkennbaren Teil des Programms deklariert werden (BEGIN/END DECLARE SECTION).
Die Variablen können dann in SQL-Anweisungen benutzt werden, indem ihnen ein Doppelpunkt vorangestellt wird. Zum Auslesen der Tupel einer SELECT-Anweisung wird eine
Positionsmarke (engl.: cursor ) deklariert, die jedes Tupel mittels einer EXEC SQL FETCHAnweisung ausliest. Die Attribute des Tupels werden dann in vorher deklarierte Variablen
geschrieben, die im Anwendungsprogramm weiter verarbeitet werden können. Nach dem
Lesen eines Tupels wird der cursor auf das nächste zu lesende Tupel ”gesetzt”.
Der hier angedeutete Mechanismus erzeugt sogenanntes statisches SQL. Dieses zeichnet sich dadurch aus, dass die SQL-Anweisungen vom Anwendungsentwickler selbst in das
Programm hineingeschrieben werden. Das hat den Vorteil, dass während der PrecompilerPhase der Anfrage-Optimierer des DBMS einen Zugriffsplan in der Datenbank ablegt, so
dass diese Phase der Anfrageoptimierung während der Laufzeit entfällt. Falls das Programm
dynamisch SQL-Anweisungen erzeugen soll, so kann das nicht mehr mit den Mitteln von statischem SQL bewerkstelligt werden. Zu diesem Zweck existieren andere EXEC SQL-Befehle.
Sie können string-Variablen verarbeiten, die die eigentlichen SQL-Befehle enthalten. Diese
Zeichenketten können während der Laufzeit erzeugt werden. Deshalb spricht man hier von
eingebettetem dynamischem SQL.
Eingebettetes SQL ist im Detail in [7] beschrieben. Hier soll nicht näher darauf eingegangen werden, da die BLAST-Anpassung mit dem Call Level Interface realisiert wird.
5.2.2
Das Call Level Interface
Die Verwendung von eingebettetem SQL hat den Nachteil, dass ein Precompiler bemüht
werden muss, bevor das Programm mit dem eigentlichen Compiler erzeugt werden kann.
44
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
Das so genannte Call Level Interface (CLI, engl.; etwa: Schnittstelle auf der Ebene
von Funktionsaufrufen) geht einen anderen Weg. Hierbei werden die SQL-Anweisungen als
Zeichenkettenvariablen abgelegt. Dies kann während der Laufzeit oder bereits durch den
Entwickler passieren. Mittels einer standardisierten Schnittstelle von Funktionsaufrufen
können diese SQL-Anweisungen vorbereitet und ausgeführt werden. Im Folgenden werden die wesentlichen CLI-Funktionen und der schematische Ablauf eines CLI-Programms
vorgestellt.
Ein Vorteil der Benutzung von CLI liegt, wie bereits erwähnt, in der Vermeidung eines
zusätzlichen Precompiler-Aufrufs. Durch die Trennung der SQL-Anweisungen (in Funktionsaufrufen) von den Anweisungen in der eigentlichen Programmiersprache ist auch die
Fehlersuche einfacher. CLI erlaubt es überdies, portable Datenbankanwendungen zu schreiben, da es auf den Standards SQL Call Level Interface (SQL/CLI ) [18] und Open Database
Connectivity (ODBC ) basiert. Ein Nachteil von CLI ist, dass es nur in den Sprachen C
und C++ verwendet werden kann.
5.2.2.1
Handles
Im CLI wird das Konzept von Handles (hier: Kennungen) verwendet. Kennungen sind
C-Variablen, die dazu dienen, Zustände von Objekten zu repräsentieren. Folgende Typen
von Kennungen werden unterschieden:
1. Eine Umgebungskennung beschreibt den Zustand der Anwendung. Zu Beginn eines
Programms wird eine Umgebungsvariable reserviert und am Ende wieder freigegeben.
Ein Programm sollte nur eine einzige Umgebungskennung besitzen.
2. Eine Verbindungskennung repräsentiert die Verbindung des Programms zu einer Datenbank. Diese Kennung wird zum Auf- und Abbau der Datenbankverbindung verwendet. Vor dem Aufbau der Verbindung sollte die Kennung reserviert und nach dem
Abbau wieder freigegeben werden. Eine Anwendung kann Verbindungen zu mehreren
Datenbanken unterhalten; für jede von Ihnen ist eine eigene Kennung notwendig.
3. Anweisungskennungen dienen zur Verwaltung des Status von SQL-Anweisungen. In
einem Programm können mehrere Anweisungskennungen verwendet werden. Mittels
einer solchen Kennung wird eine SQL-Anweisung vorbereitet und ausgeführt.
4. Deskriptorkennungen dienen dazu, bestimmte Informationen über die Rückgabeattribute einer SQL-Anweisung oder die in einer Anweisung verwendeten Parametermarker zu erhalten. Diese Informationen sind zum Teil auch über Anweisungskennungen
zugänglich, weshalb Deskriptorvariablen relativ selten verwendet werden.
Die folgenden beiden Funktionen dienen der Reservierung und Freigabe von Kennungsvariablen:
• SQLAllocHandle() reserviert eine Kennung. Zusätzlich zur Kennung werden noch
ihr Typ und ihre Kontextkennung übergeben. Die Kontextkennungen können Tabelle
5.1 entnommen werden. Die Kontextkennung selbst muss bereits reserviert sein.
• SQLFreeHandle() gibt eine Kennungsvariable wieder frei. Auch hier wird der Typ
der Kennung übergeben.
45
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
Kennung
Umgebung
Verbindung
Anweisung
Deskriptor
Kontextkennung
—
Umgebung
Verbindung
Verbindung
Tabelle 5.1: CLI-Kennungen und ihre Kontexte
5.2.2.2
Die Verbindung mit der Datenbank
Nach der Reservierung von Umgebungs- und Verbindungskennung wird die Verbindung
mit der Datenbank hergestellt. Dazu dient hauptsächlich die Funktion SQLConnect(), der
die Verbindungskennung, der Name der Datenbank, der Benutzername und das BenutzerKennwort übergeben werden. Werden Benutzername und Kennwort freigelassen, so verwendet SQLConnect() die Zugangsdaten desjenigen Benutzers, der das Programm ausführt.
Zum Abbau der Verbindung wird die Funktion SQLDisconnect() benutzt, die als Parameter die Verbindungskennung erhält.
5.2.2.3
Ausführung von SQL-Anweisungen
Vor der Ausführung von SQL-Anweisungen müssen Anweisungshandles mit der Funktion
SQLAllocHandle() reserviert werden. Im Fall von BLAST genügen SELECT-Anweisungen,
deren zurückgegebene Tupel nacheinander ausgelesen und verarbeitet werden. Für diesen
Anwendungsfall wird die folgende Abfolge von Funktionsaufrufen verwendet:
1. SQLPrepare() bereitet eine SQL-Anweisung vor. Die Anweisung wird als Zeichenkette an die Funktion übergeben. Das DBMS erzeugt in der Datenbank einen Zugriffsplan für die Anweisung.
2. SQLBindParameter()
Bei der Ausführung einer SQL-Anweisung ist es möglich, Variablenwerte des Anwendungsprogramms in der Anweisung zu verwenden. Dies geschieht durch Parametermarker, die als Fragezeichen in der SQL-Anweisung repräsentiert werden. Mit
der Funktion SQLBindParameter() werden diesen Markern Variablen des Programms
zugeordnet. Für jeden Marker ist ein Aufruf erforderlich.
3. SQLExecute() führt die SQL-Anweisung aus. Im Fall von Parametermarkern werden
die Werte aus den Programmvariablen gelesen und in die Anweisung eingefügt.
4. SQLBindCol()
Die Ausführung einer SELECT- oder VALUES-Anweisung liefert null oder mehr Ergebnistupel. Für jedes in einer SELECT- oder VALUES-Anweisung angegebene Attribut
muss eine Variable festgelegt werden, die die Attributwerte der Ergebnistupel aufnimmt. Die Zuordnung einer Programmvariablen zu einem Attribut erfolgt mit Hilfe
der Funktion SQLBindCol(). Die Funktion erhält die Anweisungskennung, die Attributnummer (zu deren Identifikation), den Typ der Programmvariablen und deren
Länge (bei Zeichenketten). Außerdem kann eine NULL-Indikator-Variable angegeben
46
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
werden. Sie gibt Auskunft darüber, ob der Attributwert im aktuellen Tupel NULL
ist.
5. SQLFetch() liest das nächste Tupel der Ergebnismenge und schreibt die Werte der
einzelnen Attribute in die Variablen, die mittels SQLBindCol() deklariert wurden.
Intern ist dieses Vorgehen als cursor wie bei eingebettetem SQL implementiert.
An dieser Stelle sei darauf hingewiesen, dass die Funktionsfolge
SQLPrepare()
↓
SQLBindParameter()
↓
SQLExecute()
durch die Folge
SQLBindParameter()
↓
SQLExecDirect()
ersetzt werden kann. Die Zeichenkette mit der SQL-Anweisung wird in diesem Fall an
SQLExecDirect() übergeben. Die erste Funktionsfolge ist dann zu bevorzugen, wenn die
Anweisung mit unterschiedlichen Variablenwerten mehrmals ausgeführt werden soll. Der
Zugriffsplan wird in diesem Fall genau einmal mit SQLPrepare() erzeugt, die Funktionen
SQLBindParameter() und SQLExecute() können danach beliebig oft aufgerufen werden.
Wird die Anweisung nur einmal ausgeführt, sind die beiden Funktionsfolgen gleichberechtigt.
Zur Ausführung und zur Analyse von SQL-Anweisungen existieren noch weitere CLIFunktionen [7, 15], etwa zum Zurücksetzen eines cursor s oder zum Transaktionsmanagement. Die hier vorgestellten Funktionen genügen jedoch, die im Rahmen von BLAST
notwendigen Anpassungen durchzuführen.
5.2.2.4
CLI-Rückgabewerte
Fast alle CLI-Funktionen geben einen Wert zurück, der Auskunft über den Erfolg der
Funktionsausführung gibt. Die folgende Aufzählung beschreibt nur die wichtigsten SQLRückgabewerte:
• SQL_SUCCESS zeigt an, dass die Funktion erfolgreich war.
• SQL_SUCCESS_WITH_INFO zeigt ebenfalls den Erfolg der Funktion an. Die Funktion
liefert aber gleichzeitig eine Warnung oder eine andere Information. Durch den Aufruf
der Funktion SQLGetDiagRec() kann diese Information ausgewertet werden.
• SQL_INVALID_HANDLE deutet auf eine nicht initialisierte Kennung hin.
• SQL_ERROR bedeutet, dass die Funktion nicht erfolgreich ausgeführt wurde. Mittels
SQLGetDiagRec() kann der Grund für den Fehler in Erfahrung gebracht werden.
47
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
• SQL_NO_DATA_FOUND ist ein Rückgabewert von SQLFetch(). Er gibt an, dass der
cursor hinter dem letzten Tupel der Ergebnismenge positioniert ist. Folglich sind
keine weiteren SQLFetch()-Aufrufe zur Verarbeitung der Ergebnisse notwendig.
Das Auslesen der Ergebnismenge einer SELECT- oder VALUES-Anweisung kann in C
so implementiert werden, dass eine Schleife so lange SQLFetch() aufruft und die
Ergebnisse verarbeitet, bis die Funktion den Wert SQL_NO_DATA_FOUND zurückliefert.
5.3
Benutzerdefinierte Funktionen
In SQL-Anweisungen werden häufig Funktionen verwendet. Die Anweisung
SELECT count(attribut2) FROM tabelle1
ruft beispielsweise die Funktion count auf, die die Anzahl der Tupel der tabelle1 -Relation
zurückgibt. DB2 unterscheidet vier Typen von Funktionen [16]:
1. Skalare Funktionen
Skalare Funktionen bilden eine Argumentliste skalarer Werte auf einen skalaren Rückgabewert (auch NULL) ab. Eine solche Funktion kann überall dort verwendet werden, wo ein Ausdruck stehen kann, etwa in der SELECT- oder WHERE-Klausel einer
SQL-Anweisung. Ein Beispiel für eine skalare Funktion ist LN, die den natürlichen
Logarithmus des übergebenen Arguments berechnet.
2. Spaltenfunktionen
Das Argument einer Spaltenfunktion ist eine Menge gleicher skalarer Werte. Betrachtet man eine Relation als Tabelle, so nimmt eine Spaltenfunktion eine Spalte als Ganzes entgegen und berechnet daraus einen skalaren Wert. Spaltenfunktionen können
überall dort verwendet werden, wo ein Ausdruck stehen kann. Ein Beispiel für eine
Spaltenfunktion ist MAX, die den größten Wert der übergebenen Spalte zurückgibt.
3. Zeilenfunktionen
Eine Zeilenfunktion nimmt einen strukturierten Typ als Argument entgegen und liefert ein Tupel von vordefinierten Typen zurück. Zeilenfunktionen dienen lediglich als
Transformationsfunktionen für strukturierte Typen.
4. Tabellenfunktionen
Tabellenfunktionen bilden eine Liste skalarer Werte auf eine Tabelle ab. Diese Funktionen liefern also eine Menge von Tupeln vordefinierter Struktur. Sie können nur
in der FROM-Klausel einer SQL-Anweisung stehen. Die einzige vordefinierte Tabellenfunktion von DB2 ist SQLCACHE_SNAPSHOT.
Neben der Verwendung bereits vordefinierter Funktionen ist es Benutzern von DB2 möglich, selbst Funktionen zu erstellen. Diese werden als benutzerdefinierte Funktionen (engl.:
user-defined functions, UDF ) bezeichnet. UDFs [14] können wie folgt klassifiziert werden:
1. Quellenbasierte Funktionen
DB2 implementiert das Konzept einzigartiger Typen. Diese zeichnen sich dadurch
aus, dass sie von einem vordefinierten Typ mittels einer 1:1-Abbildung abgeleitet
48
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
werden und einen eigenen Namen erhalten. Der Name dient dazu, die Semantik
des Datentyps hervorzuheben. Als Beispiel kann ein Typ WINKEL zur Darstellung
geometrischer Winkel definiert werden, der von DOUBLE abgeleitet ist.
Um Funktionen, die auf Argumenten vom Typ DOUBLE definiert sind, im Kontext
von WINKEL zu verwenden, müssen sie als quellenbasierte Funktionen vom jeweiligen
Original abgeleitet werden. Beispielsweise kann eine Funktion SIN definiert werden,
die Werte vom Typ WINKEL als Argumente entgegennimmt und die gleiche Semantik
hat wie die SIN-Funktion für DOUBLE-Werte.
2. Externe skalare Funktionen
Eine externe skalare Funktion ist eine skalare Funktion, die von einem Benutzer in
einer Programmiersprache geschrieben wurde. Externe skalare Funktionen können in
C, C++ oder Java implementiert werden. Der Programmcode wird dem Datenbanksystem in einer kompilierten Funktionsbibliothek zur Verfügung gestellt. Externe
skalare Funktionen dürfen kein SQL enthalten und können deshalb nicht auf die Datenbank zugreifen.
3. Externe Tabellenfunktionen
Eine externe Tabellenfunktion ist eine Tabellenfunktion, die von einem Benutzer in
einer der Programmiersprachen C, C++ oder Java implementiert ist. Auch hier ist es
nicht erlaubt, SQL in der Funktion zu verwenden. Da eine Tabellenfunktion mehrere
Tupel als Ergebnis liefern kann, wird sie intern mehrmals aufgerufen.
Benutzerdefinierte Funktionen werden genauso verwendet wie die vordefinierten. Es gelten
die gleichen Einschränkungen. Für die Anpassung von BLAST ist die Verwendung einer
externen Tabellenfunktion von besonderem Interesse. Deswegen wird auf deren Aufbau
näher eingegangen.
5.3.1
Deklaration einer externen Tabellenfunktion
Ausgangspunkt für die Behandlung von Tabellenfunktionen ist deren Deklaration. Dies
geschieht mit der SQL-Anweisung CREATE FUNCTION. Sie hat den folgenden Aufbau:
CREATE FUNCTION <Funktionsname> (<Parameterliste>)
RETURNS TABLE (<Liste der Ergebnisattribute>)
EXTERNAL NAME <Dateiname>!<externer Funktionsname>
LANGUAGE <Implementationssprache>
FENCED | NOT FENCED
EXTERNAL ACTION | NO EXTERNAL ACTION
FINAL CALL | NO FINAL CALL
SCRATCHPAD | NO SCRATCHPAD
RETURNS NULL ON NULL INPUT | CALL ON NULL INPUT
NO SQL
DISALLOW PARALLEL
<weitere Optionen>
Die Anweisungsoptionen haben folgende Bedeutung:
• Die Funktion wird in SQL-Anweisungen mit dem <Funktionsname>n aufgerufen.
49
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
• <Parameterliste> enthält die Typen der Funktionsparameter. Die Parameter können optional einen Namen erhalten.
• Die komma-separierte <Liste der Ergebnisattribute> enthält Einträge der Form
<Attributname> <Datentyp>.
Der Attributname kann in der SELECT-Klausel verwendet werden.
• <Dateiname> ist der Name der Funktionsbibliotheksdatei, die den Code der UDF
enthält. Bei der Ausführung der CREATE FUNCTION-Anweisung muss die Datei nicht
vorhanden sein. Die Funktion in der Bibliothek wird erst bei der Ausführung der
UDF in einer SELECT- oder VALUES-Anweisung aufgerufen.
• <externer Funktionsname> ist der Name der UDF in der Funktionsbibliothek.
• Die <Implementationssprache> ist C, JAVA oder OLE. OLE bedeutet, dass die
benutzerdefinierte Funktion eine Methode eines OLE Automationsobjekts ist. Diese
Möglichkeit kann nur in den 32-Bit-Versionen des Betriebssystems Windows verwendet werden.
• Bei der Option FENCED wird die Funktion in einem eigenen Prozess ausgeführt. Das
Gegenstück ist NOT FENCED, bei dem die aufgerufene Funktion im gleichen Prozess
wie das DBMS läuft.
FENCED hat den Vorteil, dass bei schweren Fehlern (Speicherschutz- oder Zugriffsfehlern) die Funktion abnormal beendet wird, ohne das DBMS zu beeinträchtigen.
Ein derartiger Fehler in einer NOT FENCED-Funktion führt im schlimmsten Fall zur
Beendigung des DBMS-Prozesses. Dadurch würden alle Verbindungen zu den vom
DBMS verwalteten Datenbanken beendet. Der Vorteil von NOT FENCED liegt in der
schnelleren Ausführung einer Funktion gegenüber ihrem FENCED-Pendant.
• EXTERNAL ACTION wird angegeben, wenn die Funktion externe Ressourcen verwendet,
wie z.B. das Dateisystem oder Gerätetreiber.
• Der Aufruf einer Tabellenfunktion stellt sich für den Benutzer von SQL so dar, dass
für jede Kombination von Aufrufargumenten die UDF einmal aufgerufen wird. Intern wird die Funktion für das Liefern jedes Ergebnistupels separat aufgerufen. Ist
die Option FINAL CALL gesetzt, unterscheidet die UDF fünf Aufruftypen, im Fall
NO FINAL CALL drei. Die Aufruftypen werden im nächsten Abschnitt detailliert
erläutert.
• Da eine UDF mehrmals aufgerufen wird, kann es nötig sein, bestimmte Variablenwerte
und Datenstrukturen zwischen den verschiedenen Aufrufen zu erhalten. Zu diesem
Zweck wird das scratchpad -Konzept (engl.; Notizzettel) verwendet. Im scratchpad
können Referenzen (Zeiger) auf Speicherbereiche gespeichert werden, die zwischen den
verschiedenen Aufrufen einer UDF konstant bleiben sollen. Die Option SCRATCHPAD
stellt einen solchen Bereich zur Verfügung.
• Oftmals ist es sinnvoll, eine UDF nicht aufzurufen, wenn ihr NULL-Werte übergeben
werden. Dieses Verhalten kann mit der Option RETURNS NULL ON NULL INPUT eingestellt werden. In diesem Fall wird vom DBMS auch NULL zurückgegeben. Dabei
50
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
genügt es, wenn nur eines der übergebenen Argumente NULL ist. Soll die Funktion
in jedem Fall aufgerufen werden, so ist die Option CALL ON NULL INPUT anzugeben.
• Die Optionen NO SQL und DISALLOW PARALLEL sind in jedem Fall anzugeben. NO SQL
bedeutet, dass die Funktion nicht mittels eingebettetem SQL oder CLI auf die Datenbank zugreifen darf. DISALLOW PARALLEL bedeutet, dass der Aufruf der Funktion
nicht parallelisiert werden kann.
5.3.2
Aufbau einer externen Tabellenfunktion
Tabellenfunktionen können in JAVA oder C implementiert werden. In diesem Abschnitt
soll der Aufbau einer C-Tabellenfunktion beschrieben werden. Schematisch sieht der Kopf
einer solchen Funktion wie folgt aus:
void SQL_API_FN <Funktionsname>(
<Eingabeparameter>,
<Rueckgabevariablen>,
<NULL-Indikatoren der Eingabeparameter>,
<NULL-Indikatoren der Rueckgabewerte>,
SQLSTATE,
SQL-Funktionsname,
spezifischer Name,
Fehlermeldungsvariable,
Notizzettel-Variable,
Aufruftyp,
DBInfo-Struktur
)
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
IN
OUT
IN
OUT
OUT
IN
IN
OUT
IN
IN
IN
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
Die Eingabeparametervariablen entsprechen der ”Parameterliste” aus der Funktionsdeklaration. Die Rückgabevariablen entsprechen der ”Liste der Ergebnisattribute”. Jedem Eingabeparameter wird zusätzlich ein NULL-Indikator zur Seite gestellt. Die Rückgabevariablen besitzen ebenfalls NULL-Indikatoren, die von der Funktion gesetzt werden können.
Die SQLSTATE-Variable wird von der Funktion gesetzt und zeigt den Rückgabestatus der
Funktion an. Die Variable ist eine Zeichenkette der Länge 5, die eine Zahl repräsentiert.
Folgende Werte können dabei zurückgegeben werden:
• ”00000” zeigt den Erfolg der Funktion an.
• Werte im Bereich ”38600” bis ”38999” deuten auf Fehler bei der Ausführung hin. Die
Funktion kann dann in der Zeichenkette Fehlermeldungsvariable eine Fehlermeldung eintragen, die von der SQL-Benutzerschnittstelle ausgegeben wird.
• Der Wert ”02000” kann nur bei einem Aufruf vom Typ FETCH zurückgegeben werden
und zeigt an, dass keine Ergebnistupel mehr folgen.
Andere Werte sollten nicht verwendet werden, da sie dem DBMS vorbehalten sind.
Die anderen Parameter haben die folgende Bedeutung:
1. SQL-Funktionsname ist der Name der UDF in SQL.
2. spezifischer Name ist ein Name, den DB2 bei der Funktionsdeklaration vergibt.
51
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
3. Die Notizzettel-Variable ist ein Speicherbereich, dessen Inhalt zwischen den Aufrufen der Funktion bestehen bleibt.
4. Die DBInfo-Struktur enthält einige weiterführende Informationen des Funktionsaufrufs, wie z.B. die aktuelle Datenbank oder die Benutzerkennung.
Die Notizzettel- und die DBInfo-Struktur sind nur vorhanden, falls die entsprechenden
Optionen bei der Deklaration gesetzt wurden.
Eine UDF wird innerhalb einer SQL-Anweisung mehrfach intern aufgerufen. Dabei
werden die folgenden Aufruftypen unterschieden:
1. FIRST
Der FIRST call (erster Aufruf) der Funktion wird durchgeführt, falls die Option
FINAL CALL bei der Deklaration der Funktion gesetzt wurde. Die Funktion wird
nur einmal pro SQL-Anweisung mit dem Typ FIRST aufgerufen. Der Aufruf dient
dazu, Ressourcen und ähnliches zu initialisieren, die für alle weiteren Aufrufe benötigt
werden. Der FIRST-Aufruf sollte kein Ergebnistupel zurückgeben. Ist die NotizzettelVariable vorhanden, wird deren Inhalt vor dem Aufruf mit Nullen initialisiert. Im Fall
eines Fehlers im FIRST-Aufruf werden keine weiteren Aufrufe der Funktion getätigt.
2. OPEN
Der OPEN-Aufruf wird für jede Parameterkombination der UDF einmal aufgerufen.
Er dient dazu, Initialisierungen und Berechnungen für diese Parameterkombination
durchzuführen. Auch bei OPEN sollte kein Ergebnistupel zurückgegeben werden. Ist
ein Notizzettel vorhanden und wurde FINAL CALL nicht deklariert, initialisiert der
OPEN-Aufruf den Notizzettel-Speicherbereich. Falls der OPEN-Aufruf einen Fehler
meldet, wird danach nur der FINAL-Aufruf der Funktion ausgeführt.
3. FETCH
Der FETCH-Aufruf dient der Rückgabe eines Ergebnistupels. Es gibt zwei Möglichkeiten zur Ausführung der Funktion:
(a) Falls intern Ergebnistupel vorliegen, müssen die entsprechenden Rückgabevariablen gesetzt werden. Die Funktion muss dann die Variable SQLSTATE auf den
Wert ”00000” setzen.
(b) Falls keine Ergebnistupel mehr zurückgegeben werden können, muss die Variable SQLSTATE auf den Wert ”02000” gesetzt werden. Damit wird dem DBMS
angezeigt, dass keine weiteren Ergebnistupel folgen.
Im Falle eines Fehlers bei FETCH werden nur noch CLOSE und FINAL aufgerufen
und keine weiteren FETCH’es.
4. CLOSE
Jedem OPEN-Aufruf entspricht ein CLOSE-Aufruf, der Initialisierungen wieder freigibt, die im OPEN-Aufruf vorgenommen wurden. Der CLOSE-Aufruf folgt demjenigen FETCH-Aufruf, der den Wert ”02000” in SQLSTATE zurückgegeben hat, oder
einem fehlerhaften FETCH-Aufruf.
52
KAPITEL 5. VERWENDETE DATENBANKKONZEPTE
5. FINAL
Jedem FIRST-Aufruf entspricht ein FINAL-Aufruf. Hier werden Initialisierungen
wieder zurückgesetzt, die für alle Funktionsaufrufe benötigt wurden. Ein FINALAufruf erfolgt nur, falls FINAL CALL bei der SQL-Deklaration der Funktion gesetzt
wurde.
53
Kapitel 6
Anpassung von BLAST
Dieses Kapitel führt die Erkenntnisse der letzten beiden Kapitel zusammen. In den folgenden Abschnitten wird das Programm blastall in der Form modifiziert, dass es im Kontext
einer relationalen Datenbank eingesetzt werden kann. Zu diesem Zweck werden zwei verschiedene Wege beschritten.
Die erste Anpassungsvariante von BLAST basiert auf der Programmierschnittstelle Call
Level Interface von DB2. Bei dieser Modifikation geht es vor allem darum, Zugriffe auf
blastall -Datensammlungsdateien durch entsprechende SQL-Anweisungen zu ersetzen.
Die zweite Anpassung besteht in der Implementation von blastall als benutzerdefinierte
Funktion. Das Augenmerk bei dieser Umwandlung liegt darauf, den Ablauf des Programms
geeignet auf den einer UDF abzubilden.
6.1
Implementation der Datenbankschnittstelle von BLAST
Bei der Anpassung von blastall mit Hilfe des CLI besteht die Hauptaufgabe in der Modifikation der Datenbankschnittstelle. Dabei werden die Datensammlungsdateien, die die
Vergleichssequenzen enthalten, durch die Anbindung an eine relationale Datenbank ersetzt
(Abbildung 6.1). Die Anfragesequenz und der BLAST-Reports werden auch nach der Anpassung in Dateien abgelegt.
Das modifizierte Programm wird mit db2blast bezeichnet, um es von blastall abzuheben.
6.1.1
Initialisierung der Datenbankschnittstelle
Vor der Nutzung der Datenbankschnittstelle steht deren Initialisierung. Für die grundlegenden Initialisierungen wird die Funktion db2init() implementiert, die die folgenden
Schritte ausführt:
1. Allokation der Umgebungskennung mittels SQLAllocHandle()
2. Allokation der Datenbank-Verbindungskennung mittels SQLAllocHandle()
3. Aufbau der Datenbank-Verbindung mittels SQLConnect()
4. Allokation der Anweisungskennungen für die verwendeten SQL-Anweisungen mittels
SQLAllocHandle()
54
KAPITEL 6. ANPASSUNG VON BLAST
Abbildung 6.1: Integration von BLAST in eine relationale Datenbank. Es ist zu beachten,
dass das Format der Anfragesequenz- und der Reportdatei unverändert bleibt.
Die Kennungen sind globale Variablen, da sie in anderen Funktionen verwendet werden
müssen.
db2init() wird ein Zeichenketten-Parameter übergeben, der den Datenbanknamen
enthält. Legt man den schematischen Ablauf der Main()-Funktion zugrunde (siehe Abschnitt 4.4.1), so wird db2init() vor dem Einlesen der Anfragesequenz-Datei ausgeführt.
Das Gegenstück von db2init() ist db2destruct(). Diese Funktion gibt die Allokationen wieder frei und baut die Datenbankverbindung ab:
1. Freigabe des Sequenz-Speicherbereich
Dieser Speicherbereich wird nicht von db2init() ermittelt, sondern erst in einer
danach aufgerufenen Funktion reserviert.
2. Freigabe der Anweisungskennungen mittels SQLFreeHandle()
3. Abbau der Datenbank-Verbindung mittels SQLDisconnect()
4. Freigabe der Datenbank-Verbindungskennung mittels SQLFreeHandle()
5. Freigabe der Umgebungskennung mittels SQLFreeHandle()
db2destruct() wird am Ende der Main()-Funktion ausgeführt.
Im weiteren Verlauf werden die von BLAST benötigten SQL-Anweisungen vorbereitet
und teilweise ausgeführt. Zu diesem Zweck werden zwei weitere Funktionen implementiert:
• db2prepareStatements() bereitet die SQL-Anweisungen vor (SQLPrepare()). Die
verwendeten SQL-Anweisungen sind abhängig von der BLAST-Variante, da die entsprechenden Sequenzen entweder in der NASequence- (blastn, tblastn, tblastx) oder
der Protein-Relation (blastp, blastx) zu finden sind.
55
KAPITEL 6. ANPASSUNG VON BLAST
• db2executeAndBindConstStatements() führt die Anweisungen aus (SQLExecute()),
die keine Parametermarker enthalten und nicht mehrfach ausgeführt werden. Diesen
Anweisungen werden auch die Ergebnisvariablen zugewiesen (SQLBindCol()). Die
SQLBindCol()-Variablen sind global, weil ihre Werte in BLAST-Funktionen verwendet werden.
Einige der SQL-Anweisungen geben nur ein Ergebnistupel zurück. Dieses wird an
dieser Stelle gleich in die Programmvariablen geschrieben (SQLFetch()).
Über die tatsächlich benötigten SQL-Anweisungen geben die folgenden Abschnitte Auskunft.
Folgt man dem Schema der Main()-Funktion, so werden die beiden eben beschriebenen
Funktionen vor dem Lesen der Anfragesequenz ausgeführt. Ihnen geht der Aufruf der
Funktion BlastGetTypes() voraus. Sie bestimmt anhand der BLAST-Programmvariante,
von welchem Typ Anfragesequenz und Vergleichssequenzen sind.
6.1.2
Initialisierung des Moduls readdb
Die Funktion readdb_new_internal() ist die zentrale Initialisierungsfunktion des Moduls
readdb. Sie führt schematisch die folgenden Schritte aus:
1. Suche nach dem Pfad der Index-Datei der BLAST-Datensammlung
2. Erzeugen der ReadDBFILE-Datenstruktur
3. Öffnen der FormatDB -Dateien
4. Lesen der Kennwerte aus der Index-Datei und Speicherung in ReadDBFILE
5. Reservierung des Speicherbereichs für die zu lesenden Sequenzen
6. Setzen von Zeigern auf die Hauptspeicherdateien (MMF)
7. Suche nach speziellen Index-Dateien (deren Erzeugung bei FormatDB mit angegeben
werden kann)
Die Schritte 1, 3, 6 und 7 werden in der Anpassung nicht verwendet, da die entsprechenden Dateien nicht vorhanden sind. Die Vergleichssequenzen kommen stattdessen aus der
relationalen Datenbank.
Die Kennwerte aus Schritt 4 werden im Folgenden aufgezählt. In Klammern stehen die
Elemente von ReadDBFILE, die die Werte speichern:
• Versionsnummer von FormatDB (formatdb_ver)
• Titel der Datensammlung (title; kann bei FormatDB mit angegeben werden)
• Erstellungszeitpunkt der Datensammlungsdateien durch FormatDB (date)
• Länge der längsten Vergleichssequenz (maxlen)
• Anzahl der Vergleichssequenzen (num_seqs)
56
KAPITEL 6. ANPASSUNG VON BLAST
• Summe über die Längen aller Vergleichssequenzen (totlen)
Um db2blast mit blastall zu vergleichen, müssen beim angepassten Programm entsprechende Kennwerte in ReadDBFILE abgelegt werden. Die Elemente dieser Struktur werden im
Rahmen von db2blast wie folgt belegt:
• formatdb_ver erhält den Wert 3, da das die Versionsnummer des verwendeten FormatDB -Dateiformats ist.
• title wird auf den Wert ”PROTEIN” gesetzt, wenn die Protein-Relation verwendet
wird (Proteinsuche), und auf ”DNA”, wenn die NASequence-Relation verwendet wird.
• date wird im db2blast-Kontext das Erstellungsdatum der jeweiligen Tabelle übergeben. Für den Fall einer Proteinsuche wird dazu folgende SQL-Anweisung ausgeführt:
SELECT create_time FROM syscat.tables WHERE tabname=’PROTEIN’
Nach der Ausführung der Anweisung wird das Ergebnis in die Variable db2date vom
CLI-Typ TIMESTAMP_STRUCT gespeichert. Erst in readdb_new_internal() gelangt
der Wert in das date-Element.
• Die Elemente maxlen, num_seqs und totlen werden gemeinsam ermittelt. Dazu wird
folgende SQL-Anweisung ausgeführt:
SELECT MAX(LENGTH(seqtext)), COUNT(*), SUM(LENGTH(seqtext))
FROM sequences.protein
Die drei Attribute werden beim Holen des Tupels in die globalen Variablen db2maxlen,
db2count und db2totlen gespeichert und gelangen erst in readdb_new_internal()
in die entsprechenden ReadDBFILE-Elemente.
db2blast führt zusätzlich die folgenden neuen ReadDBFILE-Elemente ein, die ebenfalls in
readdb_new_internal() initialisiert werden:
• currSeq enthält die unkodierte, aktuell bearbeitete Sequenz.
• currSeqNum speichert die Kennung der aktuellen Sequenz.
• In seqLength ist die Länge der aktuellen Sequenz abgelegt.
• isAmb hat den Wert TRUE, falls die aktuell bearbeitete Sequenz Mehrdeutigkeitsresiduen beinhaltet.
• Die Mehrdeutigkeitskodierung der aktuellen Sequenz ist in ambchars gespeichert.
Die Bedeutung dieser Elemente wird in den folgenden Abschnitten deutlich, wenn es um
die Modifizierung von bestehenden BLAST-Funktionen geht.
Das Gegenstück von readdb_new_internal() ist die Funktion readdb_destruct().
Sie ruft ReadDBCloseMHdrAndSeqFiles() auf, was im Fall von db2blast unterbunden werden muss. Außerdem wird readdb_destruct_element() für jedes ReadDBFILE-Listenelement aufgerufen. Diese Funktion hat folgenden Aufbau:
57
KAPITEL 6. ANPASSUNG VON BLAST
1. Freigabe der Speicherbereiche der ReadDBFILE-Zeichenketten-Elemente
2. Freigabe der Speicherbereiche für die verschiedenen Indizes auf die MMFs
3. Schließen der MMFs, falls diese noch offen sind
Der erste Schritt wird in db2blast dahingehend erweitert, dass auch die neu hinzugekommenen ReadDBFILE-Elemente freigegeben werden. Die anderen beiden Schritte werden nicht
ausgeführt, da die entsprechenden FormatDB -Dateien nicht existieren.
Die neuen Elemente von ReadDBFILE haben auch Auswirkung auf die ”Initialisierungsfunktion” readdb_attach() (im Kontext von C++ würde man von copy constructor sprechen). Bei der Duplikation der ReadDBFILE-Struktur müssen die neu eingeführten Elemente
ebenfalls initialisiert werden.
6.1.3
Der Zugriff auf die Datenbankschnittstelle durch BLAST
Nach der Initialisierung wird die Datenbankschnittstelle im gesamten Ablauf von blastall
verwendet. Folgende readdb-Funktionen wurden bei der Analyse (Abschnitt 4.3.3) als kritisch bewertet:
• readdb_new_internal()
• readdb_attach()
• readdb_destruct()
• readdb_destruct_element()
• readdb_get_link()
• readdb_get_defline_ex()
• readdb_get_sequence()
• readdb_ambchar_present()
• readdb_get_ambchar()
• readdb_get_sequence_length()
Von diesen Funktionen wurden die ersten vier bereits im letzten Abschnitt modifiziert. Die
restlichen Funktionen haben die Eigenschaft gemeinsam, dass sie auf genau einer Vergleichssequenz operieren. Zur Identifikation der Sequenz wird deren Ordnungszahl übergeben.
readdb_get_link() ist im Vergleich relativ einfach anzupassen: Im Fall von db2blast
gibt es nur eine einzige ReadDBFILE-Struktur. Folglich wird ein Zeiger auf diese zurückgegeben.
Die anderen fünf Funktionen werden im Folgenden als die Menge der kritischen Funktionen bezeichnet. Im Fall von readdb_get_defline_ex() besteht das Problem darin,
dass keine Kennungen wie in blastall ’s FASTA-Dateien zur Verfügung stehen. Die anderen
vier Funktionen müssen ihre Informationen aus dem Attribut SEQTEXT der entsprechenden
Datenbank-Sequenz generieren. Die Anpassung der kritischen Funktion hängt davon ab,
wie der Ordnungszahl-Parameter im db2blast-Kontext interpretiert wird:
58
KAPITEL 6. ANPASSUNG VON BLAST
1. Wird den Funktionen die tatsächliche ID aus der Relation übergeben, könnten die
Funktionen das in die SQL-Anweisung
SELECT seqtext FROM sequences.protein WHERE proteinid=?
übersetzen (am Beispiel der Proteinsuche). Die IDs müssten dazu vorher bekannt
sein. Bei der initialen BLAST-Suche, wie sie in den Funktionen ..._blast_search()
durchgeführt wird, ist dies nicht der Fall.
2. Wird die tatsächliche Ordnungszahl der Sequenz übergeben (etwa in einer Schleife
über alle Sequenzen), müsste das in eine SQL-Anweisung der Art ”Ermittle Informationen zur i-ten Sequenz” transformiert werden. Im Rahmen einer relationalen
Datenbank sind solche Ordnungszahlen allerdings nicht gegeben. Auch die entsprechenden ID-Attribute (NASeqID und ProteinID) bilden keine zusammenhängende
Zahlenreihe, weshalb sie nicht zu Ordnungszahlen uminterpretiert werden können.
Bei den kritischen Funktionen muss also der Zusammenhang untersucht werden, indem
sie aufgerufen werden. Zur Lösung dieses Problems wird der Begriff des Aufrufkontexts
eingeführt. Dabei werden die Aufrufe dieser speziellen readdb-Funktionen zueinander in
Beziehung gesetzt. Eine kritische Funktion kann in drei Aufrufkontexten stehen:
1. ”nächste Sequenz”-Kontext
In einer Schleife über alle Vergleichssequenzen wird die Funktion als erste der kritischen Funktionen aufgerufen. Sie muss also die neue oder nächste Sequenz holen und
diese auswerten. Da die Schleife über alle Vergleichssequenzen iteriert, kann in der
Funktion das nächste Tupel der SQL-Anweisung (Beispiel Proteinsuche)
SELECT proteinid,seqtext FROM sequences.protein
geholt werden (SQLFetch()). Die Sequenz wird ausgewertet. Sie und die ID werden zwischengespeichert.
Bei der Implementation dieses Kontexts ist es wichtig, dass die betroffene Schleife
nach dem Aufruf der kritischen Funktion die ID überall dort verwendet, wo vorher
die Ordnungszahl (die Schleifenvariable) benutzt wurde. Die ID von db2blast ersetzt
die Ordnungszahl von blastall. Dies ist für die anderen Kontexte von Bedeutung.
2. wahlfreier Kontext
Die Funktion wird nach der über alle Sequenzen laufenden Schleife aufgerufen. Da
die Schleife den Ordnungszahlen-Bezug durch einen ID-Bezug ersetzt hat, ist sichergestellt, dass die übergebene Zahl eine ID der Datenbank-Relation ist. Deshalb kann
diese Funktion die SQL-Anweisung
SELECT seqtext FROM sequences.protein WHERE proteinid=?
ausführen und aus der Sequenz die nötigen Informationen generieren. Die Sequenz
wird zwischengespeichert.
59
KAPITEL 6. ANPASSUNG VON BLAST
3. ”aktuelle Sequenz”-Kontext
Die Funktion wird in einem Kontext aufgerufen, in dem bereits eine andere kritische
Funktion auf die gleiche Sequenz zugegriffen hat. Dabei ist es unerheblich, ob die
andere Funktion im wahlfreien oder im ”nächste Sequenz”-Kontext steht. Die andere
Funktion hat bereits die Sequenz aus der Datenbank geholt. Somit kann die aktuelle
Funktion auf die zwischengespeicherte Sequenz zurückgreifen.
Die Analyse von blastall (Abschnitt 4.4) wird nun zur Bestimmung der Aufrufkontexte der
kritischen Funktionen herangezogen:
• readdb_get_sequence()-Aufrufe:
1. BLASTPerformSearchWithReaddb() steht am Anfang der for-Schleife der Funktion ..._blast_search(). → ”nächste Sequenz”-Kontext
2. readdb_get_bioseq_ex() wird von BlastReevaluateWithAmbiguities() aufgerufen, das in der for-Schleife steht. → ”aktuelle Sequenz”-Kontext
3. readdb_get_sequence_ex() wird von BioseqBlastEngineCore() aufgerufen.
Die Schleife über alle Sequenzen ist an dieser Stelle bereits beendet. → wahlfreier
Kontext
4. BlastGetGapAlgnTbckWithReaddb() wird von BioseqBlastEngineCore() nach
Beendigung der Schleife aufgerufen. → wahlfreier Kontext
• readdb_ambchar_present() wird von BlastReevaluateWithAmbiguities() aufgerufen, das bereits dem ”aktuelle Sequenz”-Kontext zugeordnet wurde.
• readdb_get_ambchar()-Aufrufe:
1. readdb_get_bioseq_ex() wurde bereits dem ”aktuelle Sequenz”-Kontext zugeordnet.
2. readdb_get_sequence_ex() ruft zuerst readdb_get_sequence() für dieselbe
Sequenz auf. Damit steht der Aufruf im ”aktuelle Sequenz”-Kontext.
• readdb_get_sequence_length()-Aufrufe:
1. GetSeqAlignForResultHitList() wird von BioseqBlastEngineCore() nach
Beendigung der Schleife aufgerufen. Der Aufruf steht im wahlfreien Kontext.
2. FillInStdSegInfo() wird von GetSeqAlignForResultHitList() aufgerufen.
Deshalb steht auch dieser Aufruf im wahlfreien Kontext.
• Für den Aufruf von readdb_get_defline_ex() ist nur relevant, ob die übergebene Zahl eine gültige ID der Datenbankrelation ist. Im wahlfreien und im ”aktuelle
Sequenz”-Kontext ist die Bedingung erfüllt. Keiner der readdb_get_defline_ex()Aufrufe steht im ”nächste Sequenz”-Kontext, weil nur die ..._blast_search()Funktionen Schleifen über alle Sequenzen enthalten. Dort steht allerdings schon der
readdb_get_sequence()-Aufruf im ”nächste Sequenz”-Kontext. Keine zweite Funktion kann zusätzlich in diesem Kontext stehen.
Nach der Identifikation der Funktionskontexte beschreiben die folgenden Unterabschnitte
die Implementation der kritischen Funktionen.
60
KAPITEL 6. ANPASSUNG VON BLAST
6.1.3.1
Die Modifikation von readdb get sequence()
readdb_get_sequence()-Aufrufe stehen in allen drei Kontexten. Deshalb muss die Implementation zweigeteilt werden:
• Für den ”nächste Sequenz”-Kontext wird die Funktion db2_get_sequence() neu
eingeführt.
• readdb_get_sequence() wird für die Verwendung im wahlfreien und im ”aktuelle
Sequenz”-Kontext angepasst.
Die Funktion db2_get_sequence() holt bei jedem Aufruf das nächste Tupel der SQLAnweisung (Beispiel Proteinsuche)
SELECT proteinid,seqtext FROM sequences.protein .
Die Attributwerte werden in die globalen Variablen db2rawseq (Sequenz) und db2seqNum
(ID) geschrieben.
Die Sequenzen in der Datenbank sind Wörter auf dem Nukleotid- bzw. dem AminosäureAlphabet. Da die blastall -Sequenzen kodiert vorliegen, müssen auch die relationalen Datenbanksequenzen kodiert werden. Dafür ist db2convertSeqToNCBIFormat() verantwortlich. Die Funktion wird von db2blast neu eingeführt. Sie prüft unter anderem auch, ob eine
übergebene Nukleotidsequenz Mehrdeutigkeitsresiduen enthält. Die folgenden ReadDBFILEElement werden beschrieben:
• buffer speichert die kodierte Sequenz.
• currSeq speichert die unkodierte Sequenz.
• In seqLength wird die Länge der Sequenz abgelegt.
• Falls die Sequenz eine Nukleotidsequenz ist und Mehrdeutigkeiten enthält, wird das
Element isAmb auf TRUE gesetzt. Im anderen Fall erhält es den Wert FALSE.
db2_get_sequence() wird von db2BLASTPerformSearch() aufgerufen. Diese Funktion
ist eine Modifikation von BLASTPerformSearchWithReaddb(). Die Funktion hat in den
wesentlichen Punkten den gleichen Aufbau (vergleiche Abbildung 6.2) wie das Original. Sie
unterscheiden sich darin, dass die neue Funktion die von SQLFetch() gewonnene SequenzID an die aufrufende Funktion ..._blast_search() zurückgibt.
Die ursprüngliche Funktion readdb_get_sequence() muss die beiden anderen Kontexte
abdecken. Um den ”aktuelle Sequenz”-Kontext vom wahlfreien zu unterscheiden, ermittelt
ein Test, ob die übergebene ID der zwischengespeicherten entspricht. Ist dies der Fall, liegt
der ”aktuelle Sequenz”-Kontext vor. Dann wird die Sequenz aus dem ReadDBFILE-Element
buffer genommen, die Länge aus seqLength.
Im anderen Fall liegt der wahlfreie Kontext vor. Für diesen wird die SQL-Anweisung
(Beispiel Proteinsuche)
SELECT seqtext FROM sequences.protein WHERE proteinid=?
61
KAPITEL 6. ANPASSUNG VON BLAST
db2BLASTPerformSearch(searchBlk,*pSeqNum)
{
/*** den DB-Mutex setzen, falls Multithreading ***/
NlmMutexLock(searchBlk->thr_info->db_mutex);
db2_retval = <RUECKGABEWERT der letzten SQLFetch()-Operation>
if (db2_retval INDICATES SUCCESS) {
/*** Kopieren der SequenzID in ***/
*pSeqNum = search->rdfp->currSeqNum = db2seqNum;
/*** mit BLASTPerformSearchWithReadDb identischer Teil ***/
length = db2_get_sequence(searchBlk->rdfp,
searchBlk->thr_info->db_mutex, &seq);
[...]
BLASTPerformSearch(searchBlk, length, seq);
}
else {
/*** Mutex bei Fehler freigeben ***/
NlmMutexUnlock(searchBlk->thr_info->db_mutex);
}
/*** RUECKGABEWERT = SQLFetch-RUECKGABE ***/
return db2_retval;
}
Abbildung 6.2: schematischer Ablauf von db2BLASTPerformSearch(). Die Abbildung steht
in Beziehung mit Abbildung 4.4.
benötigt. Die dafür erforderliche Funktionsfolge
SQLBindParameter() → SQLExecute() → SQLBindCol()
sowie der SQLFetch()-Aufruf werden in readdb_get_sequence() ausgeführt. Die Sequenz
wird hier ebenfalls mittels db2convertSeqToNCBIFormat() kodiert. Außerdem werden die
Zwischenergebnisse wie bei db2_get_sequence() in den ReadDBFILE-Elementen gespeichert.
Der Aufbau von db2convertSeqToNCBIFormat() wird hier nicht vertieft. Die Funktion
bildet lediglich die Sequenzkodierung als Programmcode ab (Abschnitt 4.3.2).
6.1.3.2
Die Modifikation von readdb ambchar present()
readdb_ambchar_present() wird ausschließlich im ”aktuelle Sequenz”-Kontext aufgerufen. Die Modifikation der Funktion besteht lediglich darin, den Wert der Variablen isAmb
der ReadDBFILE-Struktur zurückzugeben.
6.1.3.3
Die Modifikation von readdb get ambchar()
readdb_get_ambchar() wird ebenfalls ausschließlich im ”aktuelle Sequenz”-Kontext aufgerufen, kann also auf Zwischenergebnisse zurückgreifen. Falls isAmb aus der ReadDBFILEStruktur auf Mehrdeutigkeiten hinweist, ruft readdb_get_ambchar() die von db2blast neu
62
KAPITEL 6. ANPASSUNG VON BLAST
eingeführte Funktion db2ConstructAmbInfo() auf. Diese liefert zu einer unkodierten Sequenz die Mehrdeutigkeitskodierung (siehe Abschnitt 4.3.2), die im ReadDBFILE-Element
ambChars gespeichert wird.
6.1.3.4
Die Modifikation von readdb get sequence length()
readdb_get_sequence_length()-Aufrufe stehen ausschließlich im wahlfreien Kontext. Die
Funktion verwendet folgende SQL-Anweisung (Beispiel Proteinsuche):
SELECT length(seqtext) FROM sequences.protein WHERE proteinid=?
Die dafür nötige Funktionsfolge
SQLBindParameter() → SQLExecute() → SQLBindCol() → SQLFetch()
wird in readdb_get_sequence_length() ausgeführt. Das Ergebnis wird in der Variablen
db2seqlen gespeichert.
6.1.3.5
Die Modifikation von readdb get defline ex()
Die Funktion gibt in blastall die Kennungszeile einer Sequenz zurück. Im Rahmen von
db2blast muss eine solche Kennungszeile so gut wie möglich simuliert werden, um die Ergebnisse von blastall und db2blast vergleichbar zu machen. Der Vergleich geschieht mit
Hilfe des BLAST-Reports, wo den Vergleichssequenzkennungen die Alignierungen zugeordnet werden. Bei der Konstruktion einer Kennungszeile müssen zwei Dinge beachtet werden:
1. Um db2blast und blastall zu vergleichen, müssen sie auf denselben Vergleichssequenzen
arbeiten. Zu diesem Zweck wird die Datenbankrelation als Datei exportiert und in
das FASTA-Format (Anhang B.1) konvertiert. Zur Identifikation der Sequenzen wird
die spezielle Kennung ”lcl” (die soviel wie ”lokale Sequenz” bedeutet) verwendet.
Die Sequenz mit der ID x (NASeqID bzw. ProteinID) erhält dann folgende FASTAKennung: lcl|x .
2. Während der Formatierung dieser FASTA-Datensammlung durch FormatDB werden
die Kennungen modifiziert. Diese Modifikation schlägt sich in der ID-Datei nieder.
Die Kennung sieht danach wie folgt aus:
gnl|BL_ORD_ID|y lcl|x
y ist hier die Ordnungszahl der Sequenz, beginnend bei 0. Diese Ordnungszahl wird
intern von BLAST verwendet und erscheint nicht im BLAST-Report.
Damit die Kennungen der alignierten Sequenzen eines blastall - und eines db2blast-Lauf vergleichbar sind, wird die Kennung bei db2blast wie folgt konstruiert:
gnl|BL_ORD_ID|x lcl|x
Das x steht in beiden Teilkennungen für die ID in der jeweiligen Datenbankrelation. Die
ID wird der Funktion als Parameter übergeben. Da der gnl-Teil der Kennung im BLASTReport nicht verwendet wird, sind die Reports von blastall und db2blast vergleichbar.
63
KAPITEL 6. ANPASSUNG VON BLAST
do_blast_search(searchBlk)
{
do
{
db2_retval = db2BLASTPerformSearch(searchBlk,&seq_num);
if (db2_retval INDICATES SUCCESS)
{
BlastReapHitListByEvalue(searchBlk);
BlastReevaluateWithAmbiguities(searchBlk, seq_num);
BlastSaveCurrentHitlist(searchBlk);
}
} while (db2_retval INDICATES SUCCESS);
[...]
}
Abbildung 6.3: Schematischer Ablauf von do blast search() im Kontext der db2blastAnpassung. Die Abbildung steht in Beziehung zu Abbildung 4.3.
6.1.4
Anpassungen der BLAST-Hauptroutine
Im vorangegangenen Abschnitt wurde darauf hingewiesen, dass eine über alle Vergleichssequenzen laufende Schleife im Rahmen der Anpassung ebenfalls modifiziert werden muss.
Dies ist notwendig, damit die Aufrufkontexte der kritischen readdb-Funktionen gültig sind.
Die ..._blast_search()-Funktionen enthalten eine solche for-Schleife.
Für db2blast wird die for-Schleife durch eine do-while-Schleife ersetzt. Die Funktionen, die in der Schleife nach dem db2BLASTPerformSearch()-Aufruf folgen, erhalten die ID
der Sequenz anstatt ihrer Ordnungszahl, welche im blastall -Original durch die Schleifenvariable repräsentiert wird. Der Aufbau der modifizierten do_blast_search-Funktion kann
Abbildung 6.3 entnommen werden. Der Wegfall des BlastGetDbChunk()-Funktionsaufrufs
ist deshalb sinnvoll, weil in der Datenbankrelation keine Abschnitte einem einzelnen thread
zugewiesen werden können.
Die restlichen db2blast-Modifikationen rühren daher, dass zwei Funktionen des Moduls
blast.c die MMF-Funktion ReadDBCloseMHdrAndSeqFiles() aufrufen:
• BLASTSetUpSearchWithReadDbInternal()
• do_the_blast_run()
Der Aufruf von ReadDBCloseMHdrAndSeqFiles() kann in beiden Fällen einfach weggelassen
werden, da die entsprechenden Dateien nicht existieren.
6.1.5
Parallelverarbeitung in db2blast
Durch das Entfernen des BlastGetDbChunk()-Aufrufs in den ..._blast_search()-Funktionen ist dem Programm die Verwaltungsstelle des Datenbank-Mutex und damit die Möglichkeit zum multithreading ”genommen”. Dieser Abschnitt analysiert, wie multithreading
im Rahmen von db2blast realisiert werden kann.
db2blast hat, wie das Originalprogramm, eine geteilte Ressource, und zwar die Datenbankschnittstelle. Konkret ist es die folgende SQL-Anweisung:
64
KAPITEL 6. ANPASSUNG VON BLAST
SELECT proteinid,seqtext FROM sequences.protein
Im Fall der Ausführung mehrerer threads wird die Anweisung von den Instanzen der Funktion db2_get_sequence() verwendet. Diese wiederum werden von einer konkreten Instanz
von ..._blast_search() gerufen. Bei jedem SQLFetch()-Aufruf in db2_get_sequence()
werden die gleichen globalen Variablen beschrieben. Der Zugriff auf diese Variablen muss
deshalb mittels eines Mutex serialisiert werden. Der dafür in Frage kommende Mutex ist
thrinfo->db_mutex aus der BlastSearchBlk-Struktur. Dieser kann verwendet werden,
da die ursprüngliche Verwaltungsfunktion BlastGetDbChunk() in db2blast nicht mehr verwendet wird.
Der Mutex wird in db2BLASTPerformSearch() gesetzt. Nach dem SQLFetch()-Aufruf
und dem Kopieren der Ergebnisattribute in thread -lokale Variablen wird der Mutex in
db2_get_sequence() wieder freigegeben. Die thread -lokalen Variablen sind die Elemente
von ReadDBFILE, da jeder thread eine eigene Instanz dieser Struktur besitzt.
Im Parallelbetrieb arbeiten nur die ..._blast_search()-Funktionen. In diesen Funktionen wird nur die obige SQL-Anweisung benötigt. Deshalb sind die anderen SQL-Anweisungen vom multithreading nicht betroffen.
6.1.6
Die praktische Umsetzung von db2blast
Dieser Abschnitt gibt einen kurzen Überblick darüber, wie die in den vorangegangenen
Abschnitten beschriebenen Modifikationen praktisch umgesetzt werden. Ausgangspunkt
sind zwei neue Quelltextdateien, die die neuen Funktionen beherbergen:
1. db2conn.c enthält zunächst die globalen Variablen sowie die Initialisierungs- und Freigaberoutinen:
• db2init()
• db2prepareStatements()
• db2executeAndBindConstStatements()
• db2destruct()
Außerdem sind db2_get_sequence() und db2BLASTPerformSearch() enthalten.
2. db2seqHandl.c enthält von der Datenbankverbindung unabhängige Funktionen zum
Umgang mit Sequenzen:
• db2convertSeqToNCBIFormat()
• db2ConstructAmbInfo()
Die Funktionen in db2seqHandl.c werden, im Gegensatz zu denen in db2conn.c, auch
in der zweiten Anpassung verwendet.
Die Funktionsschnittstellen und die globalen Variablendeklarationen werden durch die Datei
db2conn.h exportiert. Aus diesem Grund muss diese Datei per #include in die Dateien
blast.c und readdb.c eingebunden sein.
Die in den vorangegangenen Abschnitten beschriebenen Modifikationen am BLASTQuelltext werden wie folgt integriert:
65
KAPITEL 6. ANPASSUNG VON BLAST
• Wird neuer Programmcode in den Quelltext eingefügt, so geschieht dies mittels einer
Präprozessor-Definitionsabfrage namens DB2BLAST. Programmcode, der zwischen den
Präprozessor-Direktiven #ifdef DB2BLAST und dem dazugehörigen #endif steht, ist
eine Modifikation von db2blast.
• Wird alter Programmcode im Quelltext durch neuen ersetzt, so wird nach dem neuen
Programmcode ein #else eingefügt. Der neue Programmcode endet vor der #elseDirektive, der alte Programmcode endet vor der #endif-Direktive.
Mit diesem Schema ist es möglich, sowohl das Original-Programm als auch db2blast zu erzeugen. Beim zweiten Fall wird als Compiler-Option die Präprozessor-Definition DB2BLAST
angegeben. Außerdem muss in diesem Fall die Bibliothek db2 mit eingebunden werden,
damit eine CLI-Anwendung erzeugt werden kann.
6.1.7
Vergleich des Laufzeitverhaltens von blastall und db2blast
Zum Abschluss der CLI-Anpassung soll die Laufzeit von blastall und db2blast verglichen
werden. Dazu werden verschiedene Sequenzen (Anfragesequenzen) mit den Sequenzen der
Testdatenbank aligniert.
Der erste Test führt die Programmvariante blastp aus, die die Sequenzen aus der Relation PROTEIN aus dem Datenbankschema (siehe Abbildung 5.1) als Vergleichssequenzen
verwendet. Die PROTEIN-Relation umfasst 261440 Sequenzen. Daraus werden vier Sequenzen verschiedener Länge ausgewählt, die als Anfragesequenzen verwendet werden.
Um die Sequenzen auch im Kontext von blastall verwenden zu können, müssen sie
aus der Relation in eine Datei exportiert und dort in’s FASTA-Format gebracht werden.
Diese Datei wird dann mittels formatdb bearbeitet, damit BLAST die Vergleichssequenzen
verwenden kann.
Für den Vergleich der beiden Programme wurden nur die Optionen -p, -d, -i und -o
(vergleiche Anhang A) verwendet, es wird also standardmäßig eine lückenbehaftete Alignierung durchgeführt. Folgende Tabelle zeigt den Vergleich von blastall und db2blast am
Beispiel eines blastp-Laufs mit einem Prozessor (Längen in Bytes, Laufzeiten in Sekunden):
Länge der Anfragesequenz
3419
2065
778
240
Laufzeit blastall
231
69
33
18
Laufzeit db2blast
279
121
84
73
Der Unterschied zwischen den Laufzeiten von blastall und db2blast kommt dadurch
zustande, dass das modifizierte Programm zum Lesen der Sequenzen auf die relationale
Datenbank zugreift. Außerdem müssen die Sequenzen kodiert werden. Die Laufzeitdifferenz liegt bei den betrachteten Sequenzen zwischen 48 und 55 Sekunden. Die Variabilität
des Werts rührt daher, dass während der lückenbehafteten Nachbearbeitung noch einmal
diejenigen Sequenzen per SQL-Anweisung geholt werden, welche HSPs enthalten. Die Anzahl der betroffenen Sequenzen hängt von der Anfragesequenz ab.
blastall und db2blast haben die Fähigkeit, im multithreading-Betrieb ausgeführt zu werden. In beiden Programmen wird dazu die lückenfreie Alignierung parallel ausgeführt. Die
folgende Tabelle zeigt den Vergleich der Programme unter Verwendung von acht Prozessoren (Längen in Bytes, Laufzeiten in Sekunden):
66
KAPITEL 6. ANPASSUNG VON BLAST
Länge der Anfragesequenz
3419
2065
778
240
Laufzeit blastall
85
10
6
5
Laufzeit db2blast
132
58
57
60
Da der Zugriff auf die Datenbank serialisiert werden muss, ist der Laufzeitunterschied
der beiden Programme fast identisch mit dem im Ein-Prozessor-Fall. Im Mehrprozessorfall
ist der Unterschied etwas geringer. Das kann damit erklärt werden, dass ein Teil der
Berechnungszeit der lückenfreien Alignierung sowie die Kodierung der Sequenzen parallel
zum Datenbankzugriff anderer Sequenzen ausgeführt werden.
Der zweite Test ist ein blastn-Lauf, der vier Anfragesequenzen unterschiedlicher Länge
mit den DNA-Sequenzen der Datenbank vergleicht. Die Sequenzen sind als Tupel in der
Relation NASEQUENCE gespeichert, und über einen Join mit der DNA-Relation werden
die DNA-Sequenzen ermittelt:
SELECT SeqText,NASeqID FROM sequences.nasequence,sequences.dna
WHERE naseqid=dnaseqid;
Die Relation NASEQUENCE enthält 70398 DNA-Sequenzen. Für den Vergleich von blastall
und db2blast müssen auch die DNA-Sequenzen als BLAST-Datensammlung gespeichert und
formatiert werden. Für den blastn-Lauf wurden außer den notwendigen Parametern -p, -d,
-i und -o keine weiteren Optionen gesetzt. Standardmäßig wird also eine lückenbehaftete
Alignierung durchgeführt.
Folgende Tabelle zeigt den Vergleich von blastall und db2blast am Beispiel eines blastpLaufs mit einem Prozessor (Längen in Bytes, Laufzeiten in Sekunden):
Länge der Anfragesequenz
8152
2566
734
162
Laufzeit blastall
3.98
2.61
1.81
1.87
Laufzeit db2blast
39.41
37.94
36.56
36.90
Interpretiert man den Laufzeitunterschied zwischen den beiden Programmen als Zugriffszeit auf die relationale Datenbank, so beträgt die Ausführungszeit aller Datenbankanweisungen und die Kodierung der Sequenzen rund 35 Sekunden. Für die längste Sequenz
ergibt das eine Verzehnfachung der Ausführungszeit, für kürzere Sequenzen liegt der Faktor
noch höher. Die Berechnungszeit des Algorithmus ist deutlich kleiner als bei blastp, was
primär zwei Gründe hat:
1. Die Kodierung der Residuen als 2bit-Werte führt zu einer kompakteren Speicherung
der Nukleotidsequenzen. Da der Algorithmus darauf eingestellt ist, ist auch dessen
Laufzeit entsprechend niedriger.
2. Bei der Alignierung der Proteinsequenzen wird eine Bewertungsmatrix, wie z.B. BLOSUM oder PAM, verwendet. Das erfordert zweidimensionale Indexzugriffe. Im Fall
von Nukleotidsequenz-Vergleichen wird nur zwischen Übereinstimmungen (matches)
und Unterschieden (mismatches) differenziert. Letzteres erfordert nur einfache Vergleichsoperationen, was die Laufzeit ebenfalls günstig beeinflusst.
67
KAPITEL 6. ANPASSUNG VON BLAST
Für blastn wird ebenfalls der Multiprozessorvergleich mit acht Prozessoren durchgeführt.
Folgende Tabelle zeigt die Laufzeiten (in Sekunden, Längen der Sequenzen in Bytes):
Länge der Anfragesequenz
8152
2566
734
162
Laufzeit blastall
0.97
0.62
0.46
0.67
Laufzeit db2blast
26.44
26.22
26.02
26.30
Auffallend ist hier, dass die Laufzeitdifferenz zwischen den beiden Versionen erheblich
kleiner ist als im Einprozessorfall. Der Grund liegt darin, dass nur der Zugriff auf die
Datenbank serialisiert wird (mittels db_mutex), die Kodierung der Sequenzen aber nicht.
Die Kodierung in einem thread läuft also parallel zum Datenbankzugriff in einem anderen
thread. Der Aufwand für die Kodierung der Nukleotidsequenzen beträgt also ca. 10 Sekunden. Es ist plausibel, dass der Kodierungsaufwand für eine Nukleotidsequenz höher ist als
der für eine gleich lange Proteinsequenz, da im ersteren Fall einzelne Bits belegt und bei
Mehrdeutigkeiten zwei Kodierungen durchgeführt werden müssen.
Zusammenfassend lässt sich sagen, dass für die BLAST-Varianten blastp und blastn, die
die Hauptanwendungen darstellen, der Aufwand für den Datenbankzugriff die Laufzeit von
BLAST erheblich erhöht. Im Fall von blastp fällt dieser Aufwand weniger in’s Gewicht, da
der Aufwand für die Kodierung relativ gering ist und der Protein-Algorithmus eine relativ
hohe Laufzeit hat. Bei blastn führen die Datenbankzugriffe zu deutlich mehr Laufzeit, was
an der komplizierteren Kodierung der Nukleotidsequenzen und der niedrigeren Laufzeit des
Algorithmus selbst liegt.
6.2
BLAST als benutzerdefinierte Funktion
Die Umwandlung von blastall zu einer Call Level Interface-Anwendung ermöglicht es einem
Benutzer, BLAST wie bisher als eigenständiges Programm auszuführen. Der Nutzungskontext bleibt gleich. Folgende Einschränkungen müssen dabei gemacht werden:
1. db2blast kann nur verwendet werden, wenn das Programm für die entsprechende Nutzungsumgebung, also die Kombination aus Betriebssystem und Datenbank-Managementsystem, übersetzt worden ist.
2. Die Anwendung von BLAST ist auf ein Datenmodell zugeschnitten. Modifikationen
am Datenmodell müssen sich in Änderungen der im Programm enthaltenen SQLAnweisungen niederschlagen.
3. Die Anwendung des Programms ist auf die Fälle beschränkt, die im Programm vorgesehen sind. Sollen beispielsweise nur die RNA-Sequenzen aus der Datenbank untersucht werden, die zu einem bestimmten Organismus gehören, so müsste der BLASTReport manuell untersucht werden.
Die Verwendung von db2blast schöpft also die Möglichkeiten nicht aus, die SQL-Anfragen
bieten. Aus diesem Grund beschreibt dieser Abschnitt die Umsetzung von BLAST als
benutzerdefinierte Funktion (UDF ). Auf diesem Weg wird BLAST stärker in die Datenbank
68
KAPITEL 6. ANPASSUNG VON BLAST
UDF-Parameter
progType
filterQuery
gappedBlast
expectValue
nuclMismatchScore
nuclMatchScore
matrixName
wordSize
hits_n_passes
queryStrands
gapOpenCost
gapExtCost
dropoffExt
dropoffGapped
dropoffGappedFinal
expandThresh
queryGenCode
subjGenCode
SQL-Datentyp
VARCHAR(10)
INTEGER
INTEGER
DOUBLE
INTEGER
INTEGER
VARCHAR(30)
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
DOUBLE
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
blastall -Option
-p
-F
-g
-e
-q
-r
-M
-W
-P
-S
-G
-E
-y
-X
-Z
-f
-Q
-D
Tabelle 6.1: udfblast-Parameter, die sich direkt aus blastall -Optionen ergeben
integriert. Das entstehende ”Produkt” wird im Folgenden mit udfblast bezeichnet, um es
von blastall und db2blast zu unterscheiden.
Zunächst geht es darum, BLAST als externe Tabellenfunktion zu modellieren. Da
eine externe UDF nicht auf die Datenbank zugreifen darf, müssen alle Parameter beim
Aufruf oder mittels Dateien übergeben werden. Die Funktion wird so konzipiert, dass jeder
(externe) Aufruf der UDF genau eine Vergleichssequenz bearbeitet. Zur Analyse einer
kompletten Relation durch BLAST muss eine entsprechende SQL-Anweisung formuliert
werden. Darauf wird am Ende des Kapitels eingegangen.
Eine Tabellenfunktion eignet sich für die Implementation von BLAST aus dem Grund,
dass eine Vergleichssequenz mit der Anfragesequenz mehrere Alignierungen bilden kann,
die alle gleichartig strukturiert sind. Eine derartige Struktur kann durch die Attribute
(Rückgabespalten) der Funktion abgebildet werden.
6.2.1
Eingabeparameter von udfblast
Ausgangspunkt für die Frage nach den Parametern von udfblast sind die Optionen, die
blastall auf der Kommandozeile übergeben werden. Die im Anhang A zu findende Optionenliste muss möglichst vollständig in udfblast übernommen werden. Es werden lediglich die
Optionen ausgespart, die im Zusammenhang mit udfblast keine Bedeutung haben. Tabelle
6.1 beinhaltet die Parameter, die sich direkt aus blastall -Optionen abbilden lassen.
Die Anfrage- und die Vergleichssequenz werden ebenfalls als Parameter übergeben. Da
die Datenbank die Vergleichssequenzen als LONG VARCHAR speichert, wird dieser Datentyp
auch für die UDF-Parameter verwendet. Die Parameter werden mit querySequence und
subjSequence bezeichnet.
blastall und db2blast verwenden eine Setup-Datei, die im Arbeitsverzeichnis des Benutzers steht. In dieser Datei ist der Verzeichnis-Pfad der Bewertungsmatrix-Datei abgelegt.
69
KAPITEL 6. ANPASSUNG VON BLAST
Die Setup-Datei kann im UDF-Kontext nicht verwendet werden, da die UDF unter einer
Benutzerkennung läuft, die vom DBMS reserviert ist. Um dennoch auf die Matrix-Datei
zugreifen zu können, wird der Verzeichnispfad als weiterer UDF-Parameter übergeben. Er
wird mit matrixFilePath bezeichnet und ist vom Typ VARCHAR(255).
Bei der Analyse von blastall als auch bei db2blast werden einige Kennwerte erwähnt,
die entweder aus der Index-Datei (blastall ) bzw. mittels einer SQL-Anweisung (db2blast)
ermittelt werden:
1. Anzahl der Vergleichssequenzen
2. Summe der Längen aller Vergleichssequenzen
3. Länge der längsten Vergleichssequenz
Die ersten beiden dienen der Berechnung normalisierter Alignierungsbewertungen. Der dritte Wert wird zur Reservierung von Speicherbereichen benötigt, die die Vergleichssequenzen
aufnehmen. Insbesondere die ersten beiden Kennwerte sind notwendig, um die Ergebnisse
von udfblast mit db2blast vergleichbar zu machen. Folglich werden diese drei Kennwerte
ebenfalls als Parameter an die benutzerdefinierte Funktion übergeben. Sie heißen count,
totlen und maxlen (in der gleichen Reihenfolge wie in obiger Aufzählung) und sind vom
SQL-Typ INTEGER.
6.2.2
Rückgabewerte von udfblast
Das Ergebnis eines BLAST-Vergleichs von Anfrage- und Vergleichssequenz ist im allgemeinen Fall eine Liste mehrerer Alignierungen. Jede Alignierung ist dabei auf die gleiche Art
und Weise strukturiert. Die Struktur einer Alignierung wird im Folgenden als Menge von
Attributen mit entsprechenden SQL-Datentypen zu modelliert. Ausgangspunkt ist der im
Anhang B.3 beschriebene BLAST-Report, dessen wichtigste Informationen hier übernommen werden.
Da ein Ergebnistupel der UDF eine Alignierung darstellt, werden nur die einer Alignierung zuordenbaren Informationen ausgewertet. Tabelle 6.2 enthält die Rückgabeattribute
der UDF. Zu den Attributen muss Folgendes angemerkt werden:
1. Bei den Lücken (gaps) werden im Original-BLAST zwei Fälle unterschieden. Wurde
mit blastp oder blastn lückenbehaftet aligniert, so werden die Lücken sowohl in der
Anfrage- als auch der Vergleichssequenz gezählt. Bei den Varianten blastx und tblastn
werden nur die Lücken in der Anfragesequenz gezählt. Dieses Verhalten wird bei
udfblast beibehalten, da die Ergebnisse mit blastall vergleichbar sein sollen.
2. blastn gibt im BLAST-Report die Leserichtung von Anfrage- und Vergleichssequenz
zurück. Bei der UDF werden entsprechende Werte in queryFrame und subjectFrame
zurückgegeben. Der Wert 1 steht dabei für die positive Leserichtung und -1 für die
negative.
3. Die BLAST-Varianten blastx, tblastn und tblastx geben das Leseraster von Anfrageund Vergleichssequenz zurück. Bei der UDF werden die entsprechenden Werte in den
Attributen queryFrame und subjectFrame zurückgegeben.
70
KAPITEL 6. ANPASSUNG VON BLAST
UDF-Attribut
bitScore
score
expectValue
alignLength
queryOffset
subjectOffset
queryLength
subjectLength
queryFrame
SQL-Datentyp
DOUBLE
INTEGER
DOUBLE
INTEGER
INTEGER
INTEGER
INTEGER
INTEGER
SMALLINT
subjectFrame
SMALLINT
identPairs
positivePairs
gaps
queryString
transitString
subjectString
INTEGER
INTEGER
INTEGER
LONG VARCHAR
LONG VARCHAR
LONG VARCHAR
Beschreibung
normalisierter Wert der Alignierung
nominaler Wert der Alignierung
Erwartungswert der Alignmentbewertung
Länge der Alignierung (Residuen)
Startposition der Alignierung in der Anfragesequenz
Startposition der Alignierung in der Vergleichssequenz
Länge der Alignierung in der Anfragesequenz
Länge der Alignierung in der Vergleichssequenz
Leserichtung (blastn) bzw. Leseraster (blastx, tblastx) der
Anfragesequenz
Leserichtung (blastn) bzw. Leseraster (tblastn, tblastx) der
Vergleichssequenz
Anzahl von Paaren identischer Residuen
Anzahl Residuenpaare mit positiver Bewertung
Anzahl Lücken
alignierter Bereich der Anfragesequenz
Ähnlichkeitssequenz der alignierten Sequenzen
alignierter Bereich der Vergleichssequenz
Tabelle 6.2: udfblast-Parameter, die sich direkt aus blastall -Optionen ergeben
4. transitString stellt eine Art ”Vergleichszeichenkette” dar. Aus ihr kann ersehen
werden, welche Residuen der beiden Sequenzen identisch zugeordnet wurden und
welche mit positiver Bewertung.
6.2.3
SQL-Deklaration der UDF
Die letzten beiden Abschnitte haben die Ein- und Ausgabeparameter beschrieben, die zur
Deklaration der benutzerdefinierten Funktion BLAST benötigt werden. Folgende SQLAnweisung deklariert die UDF:
CREATE FUNCTION sequences.blast(<Eingabeparameter>)
RETURNS TABLE (<Rueckgabewerte>)
EXTERNAL NAME ’udfblast!UDF_blast’
LANGUAGE C
PARAMETER STYLE DB2SQL
NOT DETERMINISTIC
FENCED
NULL CALL
NO SQL
EXTERNAL ACTION
SCRATCHPAD
FINAL CALL
DISALLOW PARALLEL
NO DBINFO
Die Eingabeparameter und die Rückgabewerte wurden in den letzten beiden Abschnitten
aufgezählt. Zu den Optionen gibt es folgende Anmerkungen zu machen:
• Die Option NULL CALL wird gesetzt, damit die Funktion auch dann aufgerufen wird,
wenn einige der Parameter NULL sind. Das Design der Funktion ist von der Art, dass
71
KAPITEL 6. ANPASSUNG VON BLAST
einige Parameter NULL sein können und die Funktion trotzdem vernünftige Resultate
liefert.
• EXTERNAL ACTION wird angegeben, weil die Funktion auf die Bewertungsmatrix-Datei
zugreift (matrixName und matrixFilePath).
• Das SCRATCHPAD wird verwendet, um zwischen den verschiedenen Aufruftypen der
UDF bestimmte Informationen zu konservieren.
• FINAL CALL wird in der Implementationsbeschreibung motiviert. Im Wesentlichen
werden die Funktionsaufrufe FIRST und FINAL dazu verwendet, einen BLAST-Lauf
als Ganzen zu verwalten.
6.2.4
Implementation der UDF
Für die Implementation von BLAST als UDF wird zunächst der Ablauf von blastall grob
schematisiert. Das Ablaufschema lässt sich wie folgt beschreiben:
1. Initialisierung
Der Schritt der Initialisierung wird dadurch charakterisiert, dass Datenstrukturen reserviert und initialisiert werden. In diesem Schritt werden keine Aktionen ausgeführt,
die von konkreten Vergleichssequenzen abhängen. Der Schritt der Initialisierung
reicht vom Anfang der Main()-Funktion bis zum Aufruf von ..._blast_search()
in der Funktion do_the_blast_run(). Die ..._blast_search()-Funktionen selbst
können nicht mehr zur Initialisierung gezählt werden, da in ihnen BLAST auf den
Vergleichssequenzen ausgeführt wird.
2. BLAST-Alignmentsuche
Der Schritt der BLAST-Alignmentsuche ist dadurch gekennzeichnet, dass alle Vergleichssequenzen nach lückenfreien Alignierungen durchsucht werden. Der Schritt
umfasst nur die Funktion do_blast_search() bzw. do_gapped_blast_search(). In
diesen wird eine Schleife über alle Sequenzen ausgeführt, in der nach den Alignierungen gesucht wird.
3. Nachbearbeitung
Die Nachbearbeitung konvertiert die in der BlastSearchBlk-Struktur gespeicherten
lückenfreien Alignierungen in eine SeqAlign-Liste. Falls blastall lückenbehaftete Alignierungen bilden soll, werden die lückenfreien HSPs dabei entsprechend erweitert.
Dieser Schritt erstreckt sich über den Teil der Funktion BioseqBlastEngineCore(),
der nach dessen Aufruf von do_the_blast_run() folgt.
4. Ausgabe der Ergebnisse und Freigabe von Datenstrukturen
Dieser Schritt dient dazu, die SeqAlign-Liste für den BLAST-Report aufzubereiten
und diesen auszugeben. Gleichzeitig werden die im Programm reservierten und initialisierten Datenstrukturen wieder freigegeben. Dieser Schritt beginnt nach Beendigung
von BioseqBlastEngineCore() und reicht bis zum Ende der Main()-Funktion.
Zur Implementation dieses Schemas als UDF müssen die einzelnen Schritte den UDFAufruftypen zugeordnet werden. Dabei muss analysiert werden, welche Informationen zwischen den Schritten kommuniziert werden müssen. Die dazu erforderlichen Datenstrukturen
72
KAPITEL 6. ANPASSUNG VON BLAST
Abbildung 6.4: Änderung des Ablaufschemas zur Implementation von BLAST als UDF.
können mittels des schon erwähnten scratchpad s verwaltet werden. Folgender (Abbildung
6.4) Ablauf wird für die UDF vorgeschlagen:
1. Für den FIRST -Aufruf der UDF eignet sich der Initialisierungsschritt, da dieser von
den konkreten Sequenzen unabhängig ist und Initialisierungen für alle Vergleichssequenzen vornimmt. Beim FIRST-Aufruf werden der UDF diejenigen Parameter übergeben, die der ersten Parameter-Kombination in der SQL-Anweisung entsprechen.
Der Aufruf initialisiert die BLAST_OptionsBlk- und die BlastSearchBlk-Struktur.
Beide werden in den nachfolgenden Aufrufen benötigt.
2. Die UDF ist so konzipiert, dass nur jeweils eine Vergleichssequenz übergeben wird.
Soll ein BLAST-Lauf über mehrere Vergleichssequenzen gehen, muss eine entsprechende SQL-Anweisung formuliert werden. Für jede Vergleichssequenz wird dann genau
einmal ein OPEN -Aufruf gestartet. Es bietet sich an, die BLAST-Alignmentsuche
von genau einer Sequenz im OPEN-Aufruf laufen zu lassen. Dafür muss der Inhalt der
Schleife aus ..._blast_search() für die Sequenz ausgeführt werden. Die Nachbearbeitung erfolgt bei blastall nach der Schleife. Dieser Schritt wird ebenfalls in OPEN
ausgeführt. Es muss dafür gesorgt werden, dass er nur auf einer Sequenz läuft. Die
SeqAlign-Liste wird in den nachfolgenden FETCH -Aufrufen ausgewertet.
3. Das Ergebnis der Nachbearbeitung ist die Kette von SeqAlign-Elementen. Im UDFKontext darf diese Kette nur Alignierungen einer einzigen Vergleichssequenz mit der
Anfragesequenz enthalten. Das Traversieren der einzelnen Alignierungen erfolgt im
FETCH -Aufruf. Dabei wertet jeder FETCH-Aufruf eine Alignierung aus, formatiert
diese und gibt die Alignierung als Ergebnistupel zurück. Der folgende FETCH-Aufruf
wertet die nächste Alignierung der gleichen Sequenz aus und so fort. Enthält die
SeqAlign-Liste keine unausgewerteten Elemente mehr, gibt die Funktion den Spezialstatus ”keine weiteren Zeilen” (SQLSTATE ”02000”) zurück. Der blastall -Schritt
”Ausgabe der Ergebnisse” wird hier nicht ausgeführt, da er für die Formatierung des
73
KAPITEL 6. ANPASSUNG VON BLAST
BLAST-Reports konzipiert ist. Stattdessen wird eine eigene SeqAlign-Auswertung
implementiert.
4. Da der Schritt der Nachbearbeitung in der UDF für jede Sequenz einzeln vollzogen
wird, müssen entsprechende Datenstrukturen wieder freigegeben werden. Der nächste
OPEN-Aufruf reserviert die Strukturen erneut. Zur Freigabe dieser Ressourcen bietet
sich der CLOSE -Aufruf an.
5. Die Freigabe von Datenstrukturen ist sinnvollerweise Gegenstand des FINAL-Aufrufs,
da erst hier sicher ist, dass in der aktuellen SQL-Anweisung keine weiteren Aufrufe
mehr kommen, die die Datenstrukturen eventuell noch benötigen könnten.
6.2.4.1
Der FIRST -Aufruf von udfblast
Der FIRST-Aufruf enthält den Initialisierungsteil von BLAST. Die UDF muss so verwendet werden, dass subjSequence (die Vergleichssequenz) der einzige sich ändernde Parameter zwischen zwei OPEN-Aufrufen ist. Die anderen Parameter können bereits beim
FIRST-Aufruf ausgewertet werden. Die meisten von ihnen dürfen einen NULL-Wert übergeben. Ausgenommen sind folgende Parameter (es werden die Bezeichner aus der SQLFunktionsdeklaration verwendet):
• progType — die Programmvariante
• querySequence — die Anfragesequenz
• count — Kennwert für die gesamte BLAST-Suche
• totLen — Kennwert für die gesamte BLAST-Suche
• maxLen — Kennwert für die gesamte BLAST-Suche
• matrixFilePath — Verzeichnispfad zur Bewertungsmatrix-Datei
Wird für einen dieser Parameter NULL übergeben, so gibt die benutzerdefinierte Funktion
einen Fehler in SQLSTATE und eine entsprechende Fehlermeldung zurück. Die Angabe von
NULL für einen der anderen Parameter bedeutet, dass für diesen der jeweilige Standardwert
(default) von udfblast verwendet wird. Dies ist äquivalent mit dem Verhalten von blastall,
wenn der Parameter nicht gesetzt wird.
An die Prüfung der Eingabeparameter schließt sich der Initialisierungsteil von BLAST
an. Um ihn mit wenig Aufwand aus blastall zu übernehmen, werden die zum Initialisierungsteil gehörenden Anweisungen in den FIRST-Code übernommen. Davon sind diejenigen Funktionen betroffen, die nur zum Teil zur Initialisierung beitragen:
1. Main() aus blastall.c
2. BioseqBlastEngine()
3. BioseqBlastEngineByLoc()
4. BioseqBlastEngineByLocEx()
5. BioseqBlastEngineCore()
74
KAPITEL 6. ANPASSUNG VON BLAST
6. do_the_blast_run()
Beim Kopieren des Codes in die UDF müssen einige Änderungen vorgenommen werden:
• Es werden drei globale Variablen db2count, db2maxlen und db2totlen eingeführt,
die die Werte der UDF-Parameter count, maxLen und totLen aufnehmen. Sie werden
von readdb_new_internal() zur Initialisierung der ReadDBFILE-Struktur verwendet.
• Die Ein- und Ausgabedateien (Anfragesequenz und BLAST-Report) müssen hier nicht
geöffnet werden (wie in Main()), da die entsprechenden Informationen über die UDFSchnittstelle ”übertragen” werden.
• Der Test auf die Gültigkeit bestimmter Parameterkombinationen (Main()) kann entfallen, wenn die Parameter nicht Teil der UDF sind.
• Im Fall eines Fehlers in einer der BLAST-Funktionen muss die SQLSTATE-Variable
gesetzt und eine Fehlermeldung formuliert werden.
• Die Einführung des neuen Parameters matrixFilePath zieht ein gleichnamiges Element in der BLAST_OptionsBlk-Struktur nach sich.
• In der Main()-Funktion wird mit Hilfe der Funktion FastaToSeqEntryEx() die Anfragesequenz aus der Datei gelesen und verarbeitet. Dies ist im UDF-Kontext nicht
möglich. Deshalb wird das Programm so modifiziert, dass die Anfragesequenz aus
dem Hauptspeicher gelesen und danach verarbeitet wird.
• Bei bestimmten Fallunterscheidungen in Unterfunktionen von Main() werden nur
die Teile des Programmcodes übernommen, die ausgeführt werden, wenn blastall die
entsprechenden Funktionen aufrufen würde.
• Vom Programmcode von do_the_blast_run() wird nur der Teil übernommen, der
im Ein-thread -Betrieb verwendet würde. Das multithreading von blastall arbeitet
auf der Basis mehrerer Sequenzen. Die Bearbeitung nur einer Sequenz kann nicht
parallelisiert werden. Deshalb ist der multithreading-Programmcode in der UDF nicht
anwendbar.
6.2.4.2
Der OPEN -Aufruf von udfblast
Im OPEN -Aufruf werden die BLAST-Alignmentsuche sowie die Nachbearbeitung vereinigt.
Der Aufruf setzt sich aus folgenden Schritten zusammen:
1. Zunächst wird geprüft, ob subjSequence keinen NULL-Wert übergeben hat.
2. Es wird die neue Funktion calcBlast() aufgerufen, die die BLAST-Alignmentsuche
und die Nachbearbeitung vereinigt. Der Rückgabewert der Funktion ist ein Zeiger auf
die SeqAlign-Liste.
3. Ist die SeqAlign-Liste nicht leer, wird eine udfblast-eigene Nachbearbeitung durchgeführt. Um in den nachfolgenden FETCH-Aufrufen die Alignierungstupel zu erzeugen, müssen die Anfrage- und die Vergleichssequenz vorliegen, und zwar in allen Leserastern. Für die Anfragesequenz existieren Leseraster-Sequenzen in Unterstrukturen der BlastSearchBlk-Struktur. Für die entsprechende Vergleichssequenz
75
KAPITEL 6. ANPASSUNG VON BLAST
calcBlast(searchBlk,optionsBlk,sequence,seqLength)
{
[ angepasster Code aus db2_get_sequence() ]
[ angepasster Code aus db2BlastPerformSearch() ]
if (GAPPED && !BLASTN)
[ angepasster Code aus do_gapped_blast_search() ]
else
[ angepasster Code aus do_blast_search() ]
[ angepasster Code aus BioseqBlastEngineCore,
nach dem do_the_blast_run()-Aufruf ]
}
Abbildung 6.5: Schematisierter Ablauf von calcBlast()
müssen diese gebildet werden. Die übersetzten Sequenzen werden in einem Feldelement namens subjSeqsTransl[] des scratchpad s abgelegt, weil sie in den nachfolgenden FETCH-Aufrufen benötigt werden. Je nach BLAST-Programmvariante wird
die Übersetzung wie folgt durchgeführt:
• Bei blastp und blastx zeigt das 1. Element des subjSeqsTransl[]-Felds auf das
ReadDBFILE-Element buffer, da es die kodierte Sequenz enthält. Bei diesen
Programmvarianten werden keine Übersetzungen der Vergleichssequenz durchgeführt.
• Im Fall von blastn werden die ersten beiden Elemente des subjSeqsTransl[]Felds mit den beiden Leserichtungen der Vergleichssequenz beschrieben. Für
die positive Leserichtung wird der UDF-Parameter subjSequence herangezogen und byteweise in das BLASTna-Format kodiert. Die negative Leserichtung
der Sequenz wird, am hinteren Ende beginnend, mit den komplementären Residuen beschrieben. Dazu wird ein Konvertierungsfeld namens NAcompl_tab[]
verwendet, dass zu jedem BLASTna-Wert (als Feldindex) den BLASTna-Wert
des komplementären Residuums verwendet.
• tblastn und tblastx müssen die Vergleichssequenz in die sechs Leseraster übersetzen. Dazu wird die Funktion BlastTranslateUnambiguousSequence() aus
dem Modul blastutl.c verwendet.
Der schematische Aufbau der Funktion calcBlast() kann Abbildung 6.5 entnommen werden. Dazu seien einige Bemerkungen gemacht:
• Der angepasste db2_get_sequence()-Code unterscheidet sich vom Original im Wesentlichen darin, dass hier die CLI-Funktionsaufrufe und deren Behandlung weggelassen werden. Die Mutex-Funktionen werden ebenfalls nicht aufgerufen.
• Die db2BlastPerformSearch()-Modifikation besteht darin, die Mutex-Funktionsaufrufe und die Prüfung von CLI-Funktionsergebnissen zu entfernen. Außerdem wird die
Funktion db2_get_sequence() nicht aufgerufen, da deren äquivalenter Code bereits
vorher abgearbeitet wurde.
76
KAPITEL 6. ANPASSUNG VON BLAST
UDF_blast([...])
{
[...]
switch(UDF_CALLTYPE) {
[...]
case FETCH:
if (ALIGNMENT AVAILABLE?)
{
switch(SeqAlign.segtype) {
case SAS_DENDIAG:
getScoresFromSeqAlign(...);
getAlignScalarsFromDD(...);
getAuxAlignStringsFromDD(...);
compileResidueStatsAndTransStr(...);
[TRAVERSIEREN der SeqAlign-Kette]
case SAS_DENSEG:
[...]
case SAS_STDSEG:
[...]
}
strcpy(SQLSTATE,"00000");
}
else
strcpy(SQLSTATE,"02000");
break;
[...]
}
}
Abbildung 6.6: Ablauf des FETCH-Aufrufs am Beispiel von DenseDiag-Alignierungen. Für
die anderen Alignierungstypen werden gleichartige Funktionen aufgerufen.
• Aus den Funktionen ..._blast_search() wird nur derjenige Teil des Quelltexts
übernommen, der bei einem Aufruf durch blastall ausgeführt würde. Die Abarbeitung
von db2BLASTPerformSearch() wird aus der Anpassung herausgenommen, da dessen
äquivalenter Code bereits vor der Fallunterscheidung abgearbeitet wurde.
• Der BioseqBlastEngineCore()-Code, der nach dem do_the_blast_run()-Aufruf
ausgeführt wird, ändert sich bei der Anpassung dahingehend, dass die Fälle, die für
blastall nicht relevant gewesen wären, hier ebenfalls wegfallen.
6.2.4.3
Der FETCH -Aufruf von udfblast
Der FETCH-Aufruf dient dazu, die SeqAlign-Liste auszuwerten und bei jedem Aufruf ein
Alignierungstupel zu liefern. Der Aufbau der SeqAlign-Struktur wurde bereits ausführlich
im Abschnitt 4.6 erläutert. Deshalb werden an dieser Stelle lediglich die neu eingeführten
Funktionen und ihr Zusammenspiel im FETCH-Aufruf (Abbildung 6.6) beleuchtet. Die
aufgerufenen Funktionen haben folgende Aufgaben:
• getScoresFromSeqAlign() ist eine vom Alignmenttyp unabhängige Funktion, die
aus einer Score-Struktur die verschiedenen alignment-Bewertungen ausliest. Ergeb77
KAPITEL 6. ANPASSUNG VON BLAST
nis dieses Aufrufs ist die Belegung der UDF-Rückgabewerte score, bitScore und
expectValue.
• getAlignScalarsFromDD() ermittelt die folgenden UDF-Rückgabewerte aus einer
DenseDiag-Alignierung:
1. alignLength
2. queryOffset und subjectOffset
3. queryLength und subjectLength
4. queryFrame und subjectFrame
Für die Alignmenttypen DenseSeg und StdSeg werden entsprechende Funktionen
implementiert (getAlignScalarsFromDS() und getAlignScalarsFromSS()).
• getAuxAlignStringsFromDD() ermittelt für den Typ DenseDiag die Zeichenketten
für Anfrage- und Vergleichssequenz, die an der Alignierung beteiligt sind. Die Ergebnisse sind allerdings noch kodiert, da sie in der nachfolgenden Funktion zur Berechnung weiterer Werte verwendet werden. Für DenseSeg und StdSeg werden entsprechende Funktion implementiert.
• compileResidueStatsAndTransStr() berechnet aus den kodierten Zeichenketten die
folgenden UDF-Rückgabewerte:
1. queryString und subjectString
2. transitString
3. identPairs, positivePairs und gaps
Der Ablauf ist für alle Alignierungstypen gleich mit dem Unterschied, dass die jeweils
zum Datentyp passenden Funktionen aufgerufen werden. Die Traversierung zur nächsten
Alignierung hängt von den Elementen type und segtype des aktuellen SeqAlign-Listenelements ab. Zeiger auf das erste und das aktuelle Element der SeqAlign-Liste sind Teil des
scratchpad s, da sie die Information von einem FETCH-Aufruf zum nächsten weitergeben.
6.2.4.4
Der CLOSE -Aufruf von udfblast
Im CLOSE-Aufruf werden diejenigen Ressourcen wieder freigegeben bzw. zurückgesetzt,
die bei OPEN reserviert und initialisiert wurden. CLOSE besteht aus folgenden Schritten:
1. Rücksetzen des Elements result_struct aus der BlastSearchBlk-Struktur
Während des Ausführung der ..._blast_search()-Funktionen wird diese Variable
zur Zwischenspeicherung von Alignierungen verwendet. Da calcblast() für jede Sequenz neu aufgerufen wird, müssen die Speicherbereiche jedesmal freigegeben werden.
2. Freigabe der Übersetzungen der Vergleichssequenz
Die Feldelemente der Variablen subjSeqsTransl geben ihren Speicherplatz frei.
3. Freigabe aller Elemente der SeqAlign-Liste
Da der Nachbearbeitungsschritt für jede Vergleichssequenz erneut aufgerufen wird,
muss die SeqAlign-Listenstruktur an dieser Stelle freigegeben werden.
78
KAPITEL 6. ANPASSUNG VON BLAST
6.2.4.5
Der FINAL-Aufruf von udfblast
Der FINAL-Aufruf dient der Freigabe von Ressourcen, die in allen Zwischenaufrufen der
UDF verwendet wurden. Im wesentlichen sind dies die Strukturen BlastSearchBlk und
BLAST_OptionsBlk. Nach dem FINAL-Aufruf werden von der UDF oder dem scratchpad
keine Ressourcen mehr verwaltet.
6.2.5
Die konkrete Umsetzung von udfblast
Neben den hier beschriebenen Änderungen zur Anpassung enthält Anhang C.4 noch weitere
Details, die zur Lauffähigkeit der UDF notwendig sind.
Basis der udfblast-Implementation ist die Quelltextdatei udfblast.c, die die neuen Funktionen beherbergt:
• calcBlast()
• getScoresFromSeqAlign()
• compileResidueStatsAndTransStr()
• getAlignScalarsFromDD()
• getAuxAlignStringsFromDD()
• getAlignScalarsFromDS()
• getAuxAlignStringsFromDS()
• getAlignScalarsFromSS()
• getAuxAlignStringsFromSS()
• die UDF UDF_blast()
Die Funktionsschnittstellen und die globalen Variablendeklarationen werden durch die Datei
udfblast.h exportiert. Aus diesem Grund muss diese Datei per #include in die Dateien
blast.c und readdb.c eingebunden sein.
In den vorangegangenen Abschnitten wurden einige Modifikationen an BLAST-Funktionen vorgenommen. Mit dem Programmcode wird wie folgt verfahren:
• Wird neuer Programmcode in den Quelltext eingefügt, so geschieht dies mittels einer
Präprozessor-Definitionsabfrage namens UDFBLAST. Programmcode, welcher zwischen
einer der Präprozessor-Direktiven #ifdef UDFBLAST oder #ifdef DB2BLAST und dem
dazugehörigen #endif steht, gehört zu udfblast.
• Wird alter Programmcode im Quelltext durch neuen ersetzt, so wird nach dem neuen
Programmcode ein #else eingefügt. Der neue Programmcode endet vor der #elseDirektive, der alte Programmcode vor der #endif-Direktive.
Um UDFs verwenden zu können, müssen sie Teil einer dynamischen Funktionsbibliothek
(shared library) sein. Diese wird im Verzeichnis der DB2-Instanz im Unterverzeichnis
/sqllib/function/ abgelegt. Am Beispiel des cc-Compilers von SUN sollen die Optionen
erläutert werden, die zur Erzeugung von udfblast benötigt werden:
79
KAPITEL 6. ANPASSUNG VON BLAST
1. Zur Übersetzung von Quelltextdateien für eine dynamische Bibliothek muss beim ccCompiler die Option -Kpic angegeben werden. Diese sorgt für die Erzeugung von
positionsunabhängigem Code. Damit kann dieser von einer beliebigen Anwendung
dynamisch gebunden und aufgerufen werden. Die Anzahl Funktionen, die mit der
Option -Kpic übersetzt werden kann, ist allerdings begrenzt (in der verwendeten
Umgebung 2048). udfblast enthält insgesamt über 7000 Funktionen. Es genügt,
diejenige Datei mit -Kpic zu übersetzen, die die benutzerdefinierte Funktion enthält,
also udfblast.c.
2. Es müssen zwei Präprozessordefinitionen gesetzt werden:
-DDB2BLAST
-DUDFBLAST
3. Für das Binden der übersetzten Objektdateien zu einer dynamischen Bibliothek muss
die Option -G verwendet werden.
4. Um die Anzahl der globalen Symbole zu reduzieren, wird die Option -M <mapfile>
gesetzt. <mapfile> ist eine Datei, die angibt, welche Funktionen in der globalen
Symboltabelle stehen sollen. Im vorliegenden Fall betrifft dies nur die Funktion
UDF_blast(), die als UDF verwendet werden soll.
5. Es müssen die Bibliotheken db2 und db2apie als Optionen übergeben werden, um
eine UDF-Bibliothek zu erzeugen.
6.2.6
Verwendung von udfblast
In diesem Abschnitt wird ein Beispiel für den Einsatz von udfblast gegeben. Zu beachten ist, dass der einzige variable UDF-Parameter innerhalb der SELECT-Anweisung die
Vergleichssequenz subjSequence sein darf. Desweiteren werden bei der Initialisierung der
UDF einige Kennwerte übergeben, die vorher ermittelt werden müssen.
Folgendes SQL-Skript demonstriert die Anwendung der UDF:
DECLARE GLOBAL TEMPORARY TABLE seq_stats
(count INTEGER, totLen INTEGER, maxLen INTEGER)
NOT LOGGED
IN usertemptabspace
ON COMMIT PRESERVE ROWS
WITH REPLACE;
INSERT INTO session.seq_stats
SELECT COUNT(*), SUM(LENGTH(seqtext)), MAX(LENGTH(seqtext))
FROM sequences.protein;
SELECT
FROM
SeqT.proteinid,
<all attributes from FctT>
sequences.protein as SeqT,
session.seq_stats as StatT,
table( sequences.blast(
’blastp’,
<query sequence>
<further blast options>
80
KAPITEL 6. ANPASSUNG VON BLAST
StatT.count,
StatT.totLen,
StatT.maxLen,
SeqT.seqtext
) ) as FctT;
Dazu müssen einige Bemerkungen gemacht werden:
1. Die Kennwerte der BLAST-Suche werden vor dem ersten Aufruf bestimmt und in
einer temporären Tabelle abgespeichert. Eine solche Tabelle muss in einem benutzerdefinierten, temporären Tabellenbereiche (USER TEMPORARY SPACE ) abgelegt
werden. Dieser wird zweckmäßigerweise bei der Initialisierung der Datenbank angelegt. Das Schema einer solchen Tabelle ist, sofern nicht anders angegeben, session.
2. Die Kennwerte der BLAST-Suche erhält man mittels der Anweisung
SELECT COUNT(*), SUM(LENGTH(seqtext)), MAX(LENGTH(seqtext)) FROM ...
Dabei ist zu beachten, dass genau der gleiche Suchraum benutzt wird wie in der
nachfolgenden SQL-Anweisung mit UDF-Aufruf. Das Ergebnis dieser Anweisung ist
genau ein Tupel, das in der temporären Tabelle abgelegt wird (INSERT INTO).
3. Die SELECT-Anweisung sollte neben allen Attributen der Tabellenfunktion auch die
ID der jeweiligen Vergleichssequenz zurückgeben, damit jede Alignierung einer Sequenz zuordenbar ist.
4. Die Tabellenfunktion wird extern für jede Vergleichssequenz einmal aufgerufen, da die
temporäre Tabelle session.seq_stats nur ein Tupel besitzt. Damit ist die Bedingung erfüllt, dass der Parameter subjSequence der einzig variable während der SQLAnweisung ist. Jeder dieser Aufrufe liefert eine Tabelle von Alignierungen zurück, die
von der Anweisung zu einer gemeinsamen Tabellen vereinigt werden.
5. Die Parameter <query sequence> und <further blast options> müssen konstant
sein und dürfen von keiner der anderen beiden Tabellen in der FROM-Klausel abhängen.
Das Ergebnis der letzten Anweisung ist eine Liste aller Alignierungen der übergebenen
Anfragesequenz mit allen Vergleichssequenzen. Mittels einer WHERE-Bedingung oder einer
verschachtelten SQL-Anweisung könnten weitere Einschränkungen des Suchraums gemacht
werden. Dabei ist zu beachten, dass die gleichen Einschränkungen bei der Ermittlung der
drei Kennwerte count, totLen und maxLen gemacht werden. Ist das SQL-Skript beendet,
wird auch die temporäre Tabelle wieder freigegeben. Die temporäre Tabelle ist sitzungslokal, wird also von anderen Anwendungen oder Skripten nicht ”gesehen”.
81
Kapitel 7
Ausblick
BLAST ist ein approximativer Algorithmus zur Bestimmung von Alignierungen jeweils
zweier Sequenzen. Dazu wird eine Anfragesequenz mit einer Menge von Vergleichssequenzen
aligniert. Die Vergleichssequenzen sind als Datei gespeichert.
Im Rahmen dieser Diplomarbeit wurden zwei Anpassung vorgestellt, die den Algorithmus in ein relationales Datenbanksystem integriert. Die erste Anpassung hat die Integration
mittels der Programmierschnittstelle Call Level Interface implementiert. Folgende Erweiterungen werden für die CLI-Modifikation vorgeschlagen:
1. Im Originalprogramm blastall sind die Sequenzen mit Kennungen gespeichert, die
Auskunft über die Herkunft der Sequenzen gibt. Im Datenmodell der Genomdatenbank (siehe Anhang D) sind ebenfalls Informationen gespeichert, die die Sequenzherkunft festlegen. Diese Informationen könnten durch spezielle SQL-Anweisungen ebenfalls ausgewertet werden, so dass sie im BLAST-Report dargestellt werden können.
2. Im Programm db2blast müssen die Vergleichssequenzen bei jedem Aufruf des Programms kodiert werden, da sie in den Datenbankrelationen ”im Klartext” abgelegt
sind. Um die Kodierung nicht in jedem BLAST-Lauf durchführen zu müssen, bietet
es sich an, die Sequenzen aus den Relationen PROTEIN und NASEQUENCE nur einmal
zu kodieren und die so modifizierten Sequenzen in speziellen Relationen abzulegen.
Für die Kodierung könnte eine externe skalare UDF implementiert werden.
3. Die Laufzeiten von db2blast bestehen zu einem wesentlichen Teil aus der Datenbankzugriffszeit. Zu dessen Minimierung können Konzepte zum Zugriff auf die entsprechenden Relationen verwendet werden, wie z.B. die Partitionierung der Datenbank.
4. db2blast hat keine höhere Flexibilität als blastall. Wie das Original durchsucht auch
db2blast alle Vergleichssequenzen. Dazu werden fest implementierte SQL-Anweisungen verwendet. Das Programm könnte eine höhere Flexibilität erfahren, indem ihm
beispielsweise eine WHERE-Klausel als Parameter übergeben wird. Die WHEREKlausel würde dazu dienen, den Suchraum einzuschränken.
Die zweite Anpassung bestand in der Implementation von blastall als benutzerdefinierte
Funktion. Für die UDF werden folgende Erweiterungen vorgeschlagen:
82
KAPITEL 7. AUSBLICK
1. Das Paradigma der verschiedenen Aufruftypen sorgt dafür, dass bei jedem Aufruf einer UDF die gleichen Parameter übergeben werden. Bei udfblast werden die BLASTParameter nur für den FIRST-Aufruf benötigt. Die OPEN-Aufrufe benötigen lediglich die jeweilige Vergleichssequenzen. Zur Verbesserung der Ausführungsgeschwindigkeit böte sich deshalb die Implementation von BLAST mit Hilfe von drei UDFs
an. Die Funktionen implementieren folgende Schritte von BLAST:
• Die erste Funktion (”init”) führt die Initialisierung von BLAST aus. Diese
Funktion würde den FIRST-Aufruf der ursprünglichen Funktion ersetzen. Sie
benötigt die BLAST-Parameter und liefert eine Referenz auf die Initialisierungsstrukturen zurück.
• Die Referenz der ersten Funktion wird der zweiten Funktion (”calc”) übergeben.
Diese Funktion erhält zusätzlich die jeweilige Vergleichssequenz. Sie implementiert die Schritte OPEN, FETCH und CLOSE der ursprünglichen UDF und hat
die Alignierungen als Rückgabewerte.
• Die dritte Funktion (”destruct”) erhält ebenfalls die Rückgabereferenz der ersten
Funktion und implementiert den FINAL-Schritt der ursprünglichen UDF.
Die Implementation dient dazu, die Laufzeit eines BLAST-Laufs dadurch zu verbessern, dass nicht bei jedem Aufruf die gesamte Menge der Parameter übergeben
wird. Voraussetzung ist, dass die drei Funktionen als NOT FENCED deklariert werden,
damit sie im Betriebssystem-Prozess ausgeführt werden und damit die DatenstrukturReferenz zwischen den Aufrufen gültig bleibt.
2. Einige Parameter der UDF könnten direkter übergeben werden. Folgende Parameter
sind davon betroffen:
• Die Parameter matrixFilePath und matrixName spezifizieren eine Datei, die
die Bewertungsmatrix enthält. Um die UDF enger in das Datenbanksystem
zu integrieren, könnte das Datenmodell dahingehend erweitert werden, dass die
Bewertungsmatrizen in einer speziellen Relation als Large Objects (LOB s) gespeichert werden. Sie können dann auch der UDf als LOBs übergeben werden.
• Die Parameter für die genetischen Codes sind Zahlen, die einen bestimmten Code
repräsentieren (vergleiche Anhang A.1). Für die genetischen Codes existiert
im Datenmodell bereits eine Relation. Deshalb könnten diese direkt an das
Programm übergeben werden.
Die beiden Anpassungen stellen zwei Extreme der Anpassung dar: In einem Fall werden
über einfachste SQL-Anweisungen die Vergleichssequenzen aus der Datenbank entnommen, um sie in einem vollständig externen Programm zu verwenden. Im zweiten Fall wird
das komplette Programm als eine benutzerdefinierte Funktion implementiert, die jeweils
eine Anfrage- mit einer Vergleichssequenz aligniert. Beide Varianten belassen die BLASTSchritte in ihrem funktionalen Zusammenhang. Als Erweiterung wäre es allerdings denkbar, die einzelnen Schritte von BLAST getrennt, d.h. in eigenen UDFs, zu implementieren,
und diese dann über SQL-Anweisungen zu verknüpfen. Dies bietet dem Datenbankmanagementsystem die Möglichkeit, mehr Einfluss auf den den Ablauf auszuüben und diesen
damit aus Datenbanksicht zu optimieren.
83
Anhang A
blastall -Kommandozeilenoptionen
Parameter
-p
Name
Programmvariante
Typ
string
-d
Datenbankname
string
-i
Anfragesequenzdatei
string
-o
BLAST-Report-Datei
string
-e
Erwartungswert
double
-F
T/F
-g
Anfrage filtern
blastn = DUST
andere = SEG
gapped alignments
-q
Nukleotid-mismatch
int
-r
Nukleotid-match
int
-M
Matrix
string
T/F
84
Bedeutung
mögliche Werte sind:
blastp
blastn
blastx
tblastn
tblastx
Name der Datenbankdatei(en) (z.B. ecoli.aa).
Hier können auch Aliasdatenbanken angegeben
werden.
Dateiname der Anfragesequenz
Standard : stdin
Dateiname des BLAST-Reports
Standard : stdout
Für den Nominalwert jedes Sequenzalignments
wird ein Erwartungswert berechnet. Sequenzen,
deren ”beste” Alignierung einen kleineren Erwartungswert hat als der hier angegebene Wert, werden mit ihren Alignierungen im BLAST-Report
ausgegeben.
Standard : 10.0
Soll die Anfragesequenz gefiltert werden?
(T = ja, F = nein)
Standard : T
Sollen lückenbehaftete Alignierungen gebildet
werden? (nicht für tblastx)
(T = ja, F = nein)
Standard : T
Kosten für alignierte, nicht-identische Residuen
(nur blastn)
Standard : -3
Wert für alignierte, identische Residuen (nur
blastn)
Standard : 1
Bewertungsmatrix für Proteinalignierungen
Standard : BLOSUM62
ANHANG A. BLASTALL-KOMMANDOZEILENOPTIONEN
-G
Wert Lücke-Öffnen
int
-E
Wert Lücke-Erweitern
int
-W
Wortgröße
int
-f
Hit-Expansionsschwelle
int
-Q
gen.Code Anfrage
int
-D
gen.Code DB
int
int
-P
-S
Leserichtungen Anfrage
int
-y
dropoff
double
-X
dropoff gapped
int
-Z
dropoff gapped final
int
-U
Kleinbuchstabenfilter
T/F
-z
Effektive DB-Länge
int
85
Kosten, im Alignment eine Lücke zu öffnen (0 bedeutet Standardverhalten). Standard : 0
Kosten, im Alignment eine Lücke zu erweitern (0
bedeutet Standardverhalten). Standard : 0
Vor der Expansion werden Hits dieser Länge in
der Vergleichssequenz gesucht (0 bedeutet Standardverhalten). Standard : 0
Ein w-mer der Länge -W hat mindestens diesen
Wert mit einem Teilwort der Anfragesequenz. (0
bedeutet Standardverhalten)
Standard : 0
Genetischer Code für die Übersetzung der Anfragesequenz (nur blastx und tblastx). Über die
möglichen Werte gibt der nächste Abschnitt Auskunft. Standard : 1
Genetischer Code, der für die Übersetzung jeder
Vergleichssequenz wird (nur tblastn und tblastx).
Die möglichen Werte können dem folgenden Abschnitt entnommen werden.
Standard : 1
0 = 1-Pass, mehrere benachbarte Hits für Expansion benötigt (Standard )
1 = 1-Pass, ein Hit für Expansion genügt
2 = 2-Pass
Leserichtungen der Anfragesequenz, die bearbeitet werden sollen (nur blastn, blastx und tblastx).
1 = positive Leserichtung
2 = negative Leserichtung
3 = beide (Standard )
Wert, um den die aktuelle Erweiterung kleiner
ist als der während dieser Erweiterung gefundene
maximale Wert (0.0 bedeutet Standardverhalten).
Standard : 0.0
wie -y, für angehängte lückenbehaftete Alignierungen (0 bedeutet Standardverhalten).
Standard : 0
wie -y, allerdings während der lückenbehafteten Alignierung (0 bedeutet Standardverhalten).
Standard : 0
Ist diese Option gesetzt (T), dann werden Kleinbuchstaben in der Anfragesequenz als Regionen geringer Komplexität betrachtet und entsprechend vom Vorfilter (SEG) behandelt. Sie können
als keine Hits bilden, werden in der späteren Expansion aber wieder als normale Residuen betrachtet. Standard : F
Gibt die effektive Größe der Datenbank als Anzahl
Residuen an. 0 bedeutet die tatsächliche Größe.
Standard : 0
ANHANG A. BLASTALL-KOMMANDOZEILENOPTIONEN
-Y
Effektive Suchraumgröße
int
-K
culling
int
-I
GI’s im BLAST-Report
string
-l
GI-Datei
string
-m
BLAST-Report-Ausgabe
int
-v
#DB-Seq.-Beschreibung
int
-b
#DB-Seq.-Alignments
int
-T
HTML-Ausgabe
T/F
-J
korrekte Anfragekennung
T/F
-O
SeqAlign-Datei
string
-a
Anzahl Prozessoren
int
86
Gibt die effektive Größe des Suchraums als Anzahl
Residuen an. 0 bedeutet die tatsächliche Größe.
Standard : 0
Anzahl der besten hits einer Region, die als Ergebnis bleiben. Falls mehr als -K HSPs einen Hit in
der gleichen Region haben, werden die ”schlechteren” aus dem Ergebnis genommen. Falls der
Parameter verwendet werden soll, wird 100 empfohlen. 0 bedeutet, daß der Parameter nicht verwendet wird.
Standard : 0 (bedeutet AUS)
Die Kennung einer Sequenz besteht aus mehreren
Komponenten. Eine ist meist die GI-ID. Wenn
diese im BLAST-Report neben den anderen IDs
erscheinen soll, muß hier T übergeben werden,
sonst F. Standard : F
Dieser Parameter zeigt auf eine Datei, in der pro
Zeile eine GI-ID steht. In BLAST werden dann
nur die Vergleichssequenzen mit diesen GI-IDs
verwendet.
Darstellung der Alignierungen im BLAST-Report.
Standard : 0 (paarweise Darstellung)
Anzahl Vergleichssequenzen, für die deren Alignmentbewertung im BLAST-Report ausgegeben
werden. Standard : 500
Anzahl Vergleichssequenzen, für die die konkreten
Alignments im BLAST-Report ausgegeben werden. Standard : 250
Ist die Option gesetzt (T), wird ein BLASTHTML-Report erstellt. Standard : F
Ist die Option gesetzt, so wird die Kennung der
Anfragesequenz, die in der FASTA-Datei steht, als
korrekte Kennung anerkannt. Diese Option ist für
die folgende Option von Bedeutung. Standard : F
Die Alignierungen können ASN.1-kodiert in eine
Datei geschrieben werden. Dazu muß die Option
-J auf ”T” gesetzt sein.
Da BLAST multithreadingfähig ist, kann hier die
Anzahl zu verwendender Prozessoren übergeben
werden. Standard : 1
ANHANG A. BLASTALL-KOMMANDOZEILENOPTIONEN
A.1
Genetische Codetabellen
Folgende Tabelle stellt die verschiedenen genetischen Codes dar. Die Nummer wird bei den
blastall -Optionen -Q und -D angegeben.
Nummer
1
2
3
4
5
6
9
10
11
12
13
14
15
16
21
22
23
Name
Standard
Vertebrate Mitochondrial
Yeast Mitochondrial
Mold Mitochondrial, Protozoan Mitochondrial, Coelenterate Mitochondrial,
Mycoplasma, Spiroplasma
Invertebrate Mitochondrial
Ciliate Nuclear, Dasycladacean Nuclear, Hexamita Nuclear
Echinoderm Mitochondrial
Euplotid Nuclear
Bacterial
Alternative Yeast
Ascidian Mitochondrial
Flatworm Mitochondrial
Blepharisma Macronuclear
Chlorophycean Mitochondrial
Trematode Mitochondrial
TAG-Leu, TCA-Stop
Thraustochytrium mitochondrial code
87
Anhang B
Aufbau der BLAST-Reportdateien
Sowohl im ursprünglichen BLAST -Programm als auch in der modifizierten Version werden
einige Dateiformate verwendet, deren Aufbau Gegenstand dieses Anhangs sein soll.
B.1
Das FASTA-Format
Das FASTA-Dateiformat wurde vom Alignment-Algorithmus FASTA [27] eingeführt und
etabliert. Im BLAST-Kontext wird es von ”Datenbank”-Dateien verwendet und dient
BLAST als Eingabeformat, welches vom Programm FormatDB gelesen und für die Benutzung durch BLAST formatiert wird.
Das FASTA-Format ist ASCII-lesbar und wird zeilenweise bearbeitet. Jeweils zwei aufeinanderfolgende Zeilen spezifizieren eine Biosequenz. In der ersten Zeile steht die Kennung
der Sequenz, in der zweiten die Sequenz selbst. Eine FASTA-Datei hat demnach folgendes
Aussehen:
1. Zeile: >[Kennung von Sequenz 1]
2. Zeile: [Sequenz 1]
3. Zeile: >[Kennung von Sequenz 2]
4. Zeile: [Sequenz 2]
usf.
Die Sequenzen sind Buchstabenfolgen, d.h. es werden die Alphabete der Tabellen 2.2
für Nukleinsäure-Residuen und 2.3 für Aminosäure-Residuen verwendet. Für Nukleinsäuren
ist zu beachten, daß das Zeichen ”X” für das entsprechende Mehrdeutigkeitsresiduum nicht
erlaubt ist, hier muß stattdessen ”N” benutzt werden, ansonsten werden die ”X”e aus den
Sequenzen gelöscht.
B.2
FormatDB-Ausgabedateien
Nach der Formatierung einer FASTA-Datenbank-Datei mit FormatDB entstehen drei Dateien, die die Eingabe für BLAST bilden. Die folgenden Unterabschnitte beschreiben diese
Dateitypen.
88
ANHANG B. AUFBAU DER BLAST-REPORTDATEIEN
Abbildung B.1: Zusammenhang der von FormatDB formatierten Dateien
B.2.1
Die FormatDB-Kennungendatei
In dieser Datei werden die Kennungen der Sequenzen aus der FASTA-Datei abgelegt. Jeder
Eintrag bezieht sich auf eine Sequenz. Sei y die Kennunge der x. Sequenz (wobei x bei 0
beginnt), so ist der Kennungseintrag dieser Sequenz
gnl|BL_ORD_ID|x y.
Die Einträge stehen hintereinander in der Datei, es gibt keine Trennungszeichen und keine
Zeilenumbrüche. Um einen Eintrag zu lesen, muß zuvor auf die entsprechende Index-Datei
zugegriffen werden (siehe dazu Abschnitt B.2.3). Die Reihenfolge der Kennungseinträge in
dieser Datei entspricht der Sequenz-Reihenfolge in der FASTA-Datei.
B.2.2
Die FormatDB-Sequenzdatei
In dieser Datei werden die Sequenzen aus der FASTA-Datei abgelegt. Die Sequenzen sind
allerdings nicht im ASCII-Format abgelegt, sondern werden vor der Speicherung in diese
Datei kodiert. Zur Kodierung sei auf Abschnitt 4.3.1 verwiesen. Die kodierten Sequenzen stehen hintereinander und sind nicht durch spezielle Trennungszeichen oder Zeilenumbrüche separiert. Um einen Eintrag zu lesen, muß zuvor auf die entsprechende Index-Datei
zugegriffen werden (siehe dazu folgenden Abschnitt). Die Reihenfolge der Sequenzeinträge
entspricht der Sequenz-Reihenfolge in der FASTA-Datei.
89
ANHANG B. AUFBAU DER BLAST-REPORTDATEIEN
B.2.3
Die FormatDB-Indexdatei
Diese Datei enthält Datenbankkennwerte sowie Dateizeiger-Indizes auf die Kennungs- und
die Sequenz-Dateien (Abbildung B.1). Wie diese beiden Dateien ist auch die Index-Datei eine Binärdatei. Die folgende Tabelle zählt zunächst die Kennwerte und ihren Speicherbedarf
in der Datei auf. Die Kennwerte stehen in ebendieser Reihenfolge in der Datei.
Länge
4 Bytes
4 Bytes
4
n
4
m
4
4
4
Bytes
Bytes
Bytes
Bytes
Bytes
Bytes
Bytes
Datentyp
Integer
Integer
Integer
String
Integer
String
Integer
Integer
Integer
Beschreibung
FormatDB-Versionsnummer
Art der Sequenzen
(0 = Nukleotid, 1 = Protein)
Länge des Titels (n)
Titel der Datenbank
Länge des Erstellungsdatums (m)
Erstellungszeitpunkt der Datenbank durch FormatDB
Anzahl Sequenzen (k)
Länge der Datenbank (Anzahl Residuen)
Länge der längsten Sequenz
Darauf folgen k + 1 Dateizeigereinträge in die zugehörige Kennungsdatei (im Folgenden
IDZeiger). Die ersten k Einträge zeigen auf die Kennungen der k Sequenzen, der (k + 1)te Eintrag zeigt hinter die Kennung der letzten Sequenz. Die Länge der Kennung der i-ten
Sequenz kann demnach mit der Formel IDZeiger[i + 1] − IDZeiger[i] berechnet werden.
Jeder Zeiger-Eintrag ist ein Integerwert von vier Byte Größe.
Auf die Zeiger in die Kennungsdatei folgen die Dateizeigereinträge in die Sequenz-Datei
(im Folgenden SeqZeiger). Dies sind ebenfalls k + 1 Einträge, welche auf die Sequenzen
selbst zeigen. Der (k + 1)-te Eintrag zeigt hinter die letzte Sequenz. Die Länge der (kodierten) Sequenz kann somit nach der Formel SeqZeiger[i + 1] − SeqZeiger[i] berechnet
werden. Jeder Zeiger-Eintrag ist ein Integerwert von vier Byte Größe.
Falls in der ”Datenbank” Nukleotid-Sequenzen gespeichert sind, folgt ein weiterer Block
von Dateizeigereinträgen, welche ebenfalls in die Sequenz-Datei zeigen (AmbZeiger). Solche Sequenzen können sogenannte Mehrdeutigkeitsresiduen enthalten, also solche, die für
mehrere Elementarresiduen (vergleiche Tabelle 2.2) stehen können. Eine Nukleotid-Sequenz
ist daher zweigeteilt kodiert. Diese Kodierungen sind in Abschnitt 4.3.2 erklärt. Der zweite
Teil, die Mehrdeutigkeitskodierung, wird mit diesen AmbZeigern referenziert. Auch hier
gibt es k + 1 Einträge. Mit der Formel SeqZeiger[i + 1] − AmbZeiger[i] kann die Länge
der Mehrdeutigkeitskodierung der Sequenz i bestimmt werden; ist diese 0, dann besitzt die
Sequenz nur eindeutige Residuen. Jeder Zeiger-Eintrag ist ein Integerwert von vier Byte.
An dieser Stelle sei darauf hingewiesen, daß die Integerwerte in dieser Datei als Big
Endian gespeichert sind. Das bedeutet, daß das höchstwertige Byte zuerst gespeichert ist
und das niedrigstwertige zuletzt. Liest man, wie hier nötig, einen 4-Byte-Wert aus einer
Datei, so wird normalerweise das erste gelesene Byte als niedrigstwertiges und das vierte
Byte als höchstwertiges Byte interpretiert (Little Endian). Folglich muß jeder 4-Byte-Wert
nach dem Lesen byteweise gedreht werden.
90
ANHANG B. AUFBAU DER BLAST-REPORTDATEIEN
B.2.4
readdb-Kennwertefunktionen
Die Dateien der vorangegangenen Unterabschnitte werden von readdb_new_internal()
gelesen. Insbesondere die Indexdatei-Kennwerte dienen der ReadDBFILE-Initialisierung. Die
folgenden readdb-Funktionen liefern die Kennwerte für eine oder mehrere Datenbank(en):
• readdb_get_dblen() – Gesamtlänge aller Sequenzen (Anzahl Residuen)
• readdb_get_totals_ex() – liefert Gesamtlänge aller Sequenzen und deren Anzahl
• readdb_get_totals() – wie readdb_get_totals_ex(), berücksichtigt keine AliasDatenbanken
• readdb_get_num_entries_total() – Anzahl der Sequenzen aller Datenbanken in
der verketteten ReadDBFILE-Liste
• readdb_get_num_entries_total_real() – wie readdb_get_num_entries_total(),
berücksichtigt keine virtuellen Datenbanken
• readdb_get_num_entries() – Anzahl der Sequenzen der Datenbank, welche zur
übergebenen ReadDBFILE-Struktur gehört
• readdb_get_maxlen() – Länge der längsten Sequenz
• readdb_get_filename() – Dateiname der Datenbank
• readdb_get_title() – Titel der Datenbank
• readdb_get_date() – Erstellungsdatum der Datenbank-Datei
• readdb_is_prot() – TRUE, falls Datenbank Proteinsequenzen enthält, FALSE, sonst
• readdb_get_formatdb_version() – Versionsnummer von FormatDB, welches die
Datenbankdatei formatiert hat
B.3
BLAST-Reportdateien
Dieser Abschnitt beschreibt das Aussehen des BLAST-Reports, wenn blastall mit der
Standard-Ausgabeoption -m 0 aufgerufen wird. Ein BLAST-Report (Abbildung B.2) besteht aus drei Arten von Informationen:
1. einer Vergleichssequenz zuordenbare Informationen
2. einer Alignierung zuordenbare Informationen
3. dem BLAST-Lauf zuordenbare Informationen
Die einer Vergleichssequenz zuordenbaren Informationen stellen eine Zusammenfassung der
Alignierungen dar. Jede Sequenz, die relevante Alignierungen gebilet hat, wird in einer
Liste dargestellt. Die Reihenfolge der Sequenzen wird dabei durch den Erwartungswert
von deren jeweils ”bester” Alignierung bestimmt: je kleiner der Erwartungswert, desto
91
ANHANG B. AUFBAU DER BLAST-REPORTDATEIEN
[...]
Query= TestProtein
(8192 letters)
Database: DNA
1407 sequences; 1,551,788 total letters
Score
(bits)
Sequences producing significant alignments:
[... ERSTER TEIL ...]
lcl|480001170
43
E
Value
4e-004
[... ZWEITER TEIL ...]
>lcl|480001170
Length = 3524
Score = 32.1 bits (71), Expect = 0.90
Identities = 20/84 (23%), Positives = 43/84 (50%), Gaps = 7/84 (8%)
Frame = +3
Query: 7521 QQKANEVEQMIRDLEASIARYKEEYAVLISEAQAIKADLAAVEAKV--NRSTA-----LL 7573
++ +N + + I++LE+ +
K +
L++E
+K LA
+ + + S+A
+
Sbjct: 75
KENSNTLSEQIKNLESELNSSKIKNESLLNERNLLKEMLATSRSSILSHNSSAGNIDDKM 254
Query: 7574 KSLSAERERWEKTSETFKNQMSTI 7597
KS+
EK E ++N+M+ I
Sbjct: 255 KSIDESTRELEKNYEVYRNEMTAI 326
[... DRITTER TEIL ...]
Database: DNA
Posted date: 20.1.2002, 18:24:28
Number of letters in database: 1,551,788
Number of sequences in database: 1407
Lambda
0.319
Gapped
Lambda
0.270
K
H
0.135
0.392
K
H
0.0470
0.230
Matrix: BLOSUM62
Gap Penalties: Existence: 11, Extension: 1
Number of Hits to DB: 13760138
[...]
Abbildung B.2: Aufbau der einzelnen Abschnitte des BLAST-Reports am Beispiel eines
lückenbehafteten tblastn-Laufs
92
ANHANG B. AUFBAU DER BLAST-REPORTDATEIEN
weiter oben in der Liste. Jede Zeile der Liste enthält die Kennung einer Sequenz, den
normalisierten Wert und den Erwartungswert von deren ”bester” Alignierung.
Daran schließen sich die Alignierungsinformationen an. Der Liste der Alignierungen
gehen die Zeilen
>x
Length = y
voraus, die die Kennung (x) und die Länge (y) der Vergleichssequenz enthalten. Jede
Alignierung wird dabei durch die folgenden Informationen beschrieben:
1. Der mit ”Score” bezeichnete Wert mit der Einheit ”bits” ist die normalisierte Bewertung der Alignierung dar.
2. Hinter dem ”bits”-Wert steht in Klammern der nominale Wert der Alignierung.
3. ”Expect” ist die Anzahl der zufällig zu erwartenden Alignierungen mit dem angegebenen normalisierten Wert oder einem besseren.
4. Bei ”Identities: x/y” ist x die Anzahl von identischen Residuenpaaren und y die
Länge der Alignierung.
5. Das x in ”Positives: x/y” (wird nicht von blastn verwendet) ist die Anzahl der Residuenpaare, die einen positiven Zuordnungswert haben.
6. Bei ”Gaps: x/y” ist x die Anzahl der Lücken in der Alignment. Dieser Wert steht
nur in Reports von lückenbehafteten BLAST-Läufen. Bei blastn und blastp werden
die Lücken in beiden beteiligten Sequenzen gezählt, bei blastx und tblastn zählen nur
die Lücken in der Anfragesequenz.
7. Die Angabe von ”Frame: x” steht bei blastx für das Leseraster der Anfragesequenz
und bei tblastn für das der Vergleichssequenz.
8. Bei tblastx gibt es Angabe ”Frame: x/y”. Sie steht für die Leseraster der beteiligten Sequenzen. x steht für das Leseraster der Anfragesequenz und y für das der
Vergleichssequenz.
9. Bei blastn wird die Angabe ”Strands: x/y” gemacht. Sie steht für die Leserichtungen
der Sequenzen. x ist die Leserichtung der Anfragesequenz in dieser Alignierung, y
die der Vergleichssequenz. ”Plus” steht für die positive und ”Minus” für die negative
Leserichtung.
10. Eine Alignierung, deren Länge mehr als 60 Zeichen beträgt, wird zur besseren Lesbarkeit im BLAST-Report umgebrochen. Würde man die einzelnen Stücke wieder
zusammenfügen, erhielte man die Alignierung. Die oberste Zeichenkette ist der alignierte Teil der Anfragesequenz, die unterste Zeichenkette der der Vergleichssequenz.
Dazwischen steht eine ”Ähnlichkeitszeichenkette”, die Auskunft über die einzelnen
alignierten Zeichen gibt. Steht in diesem string ein Buchstabe, so ist das Residuum
in den beiden Sequenz identisch, steht hier ein Plus, so wird das Residuenpaar von
der Bewertungsmatrix positiv bewertet. In blastn wird nur zwischen Leerzeichen und
”|” unterschieden. Letzteres steht dort für identische Residuen.
93
ANHANG B. AUFBAU DER BLAST-REPORTDATEIEN
11. Vor und hinter jeder Zeile (maximal 60 Zeichen) der Alignierung stehen Zahlen, die
auf die Position der Alignierung innerhalb der Sequenzen hinweist. Von vorn nach
hinten sind die Zahlen aufsteigend, wenn blastp oder in den anderen Varianten eine
positive Leserichtung vorliegt, und absteigend, falls die Leserichtung (und damit bei
den translatierten Versionen auch das Leseraster) negativ sind.
Nach den Alignierungen folgt der dritte Teil des BLAST-Reports. Dieser gibt einen statistischen Überblick über den gesamten BLAST-Laufs.
94
Anhang C
UDF-Entwurfsdetails
Dieser Anhang behandelt Details, die bei Entwurf und Implementation der benutzerdefinierten Funktion verwendet wurden. Er komplettiert damit sowohl die Analyse als auch
die Umsetzung der UDF-Anpassung.
C.1
Typen von Alignierungen
Im Abschnitt 4.6 wurde der Datentyp StdSeg erläutert, weil er unter den Alignierungstypen
derjenige mit der größten Komplexität ist. Dieser Abschnitt komplettiert lediglich die
Analyse, indem die beiden anderen Alignierungstypen dargestellt werden.
C.2
DenseDiag-Alignierungen
DenseDiag-Elemente sind die einfachste Form, Alignments zu speichern. Die Struktur
hat den in Abbildung C.1 dargestellten Aufbau.
• dim hat die gleiche Bedeutung wie das gleichnamige SeqAlign-Element.
• Das Feld starts besteht aus dim Elementen (hier 2) und enthält die Start-Offsets
der Alignierung der beiden Sequenzen. Der erste Wert bezeichnet den Offset in der
Anfragesequenz, der zweite Wert den in der Datenbank-Vergleichssequenz.
typedef struct dendiag {
short int
dim;
SeqId
*id;
long int
*starts;
long int
len;
char
*strands;
Score
*scores;
struct dendiag *next;
}
DenseDiag, *DenseDiagPtr;
Abbildung C.1: Aufbau der Datenstruktur DenseDiag
95
ANHANG C. UDF-ENTWURFSDETAILS
typedef struct denseg {
short int
dim,
numseg;
SeqId
*ids;
long int
*starts;
long int
*lens;
char
*strands;
Score
*scores;
}
DenseSeg, *DenseSegPtr;
Abbildung C.2: Aufbau der Datenstruktur DenseSeg
• Die Länge der Alignierung ist im Element len gespeichert. Hier genügt für beide
beteiligten Sequenzen ein gemeinsamer Wert, da DenseDiag nur lückenfreie Alignierungen verwendet wird. Folglich haben die beiden Subsequenzen die gleiche Länge in
der Alignierung.
• strands ist wie starts ein Feld mit dim Elementen, das die Leserichtungen der
beiden Sequenzen in Alignierung enthält. Für blastp sind diese Werte 0. Für blastn
sei auf den Abschnitt ist ein Element 1, falls die positive Leserichtung vorliegt, und
2 für die negative Leserichtung.
• Die Variable scores zeigt auf eine verkettete Score-Liste. Der Aufbau von Score
wird im Abschnitt 4.6.2 erläutert.
Die Verwendung von DenseDiag geht bei BLAST immer mit dem Wert type = 2 aus
SeqAlign einher. Deshalb stehen die Alignments einer Sequenz in einer gemeinsamen
DenseDiag-Liste. Der hier vorhandene next-Zeiger zeigt auf die nächste Alignierung der
Vergleichssequenz. Ist keine weitere Alignierung vorhanden, hat next den Wert NULL.
C.3
DenseSeg-Alignierungen
Die Datenstruktur DenseSeg dient der Speicherung lückenbehafteter Alignierungen und
wird von blastn und blastp verwendet. Ihr Aufbau ist in Abbildung C.2 dargestellt. dim
hat die gleiche Bedeutung wie bei DenseDiag. numseg enthält die Anzahl der Segmente der
Alignierung. Zum Begriff des Segments sei auf den Abschnitt 4.6.1 verwiesen. Die anderen
Elemente haben folgende Bedeutung:
• starts ist ein Feld mit 2 · numseg Elementen. starts[2*k] ist der Offset des Segments k in der Anfragesequenz, starts[2*k+1] der Offset in der Vergleichssequenz.
Besteht für eine der beiden Sequenzen das Segment aus einer Lücke, so ist der Wert
des entsprechenden start-Feldelements −1.
• len ist ein Feld von numseg Elementen und enthält die Längen der Segmente.
• strands enthält wie starts ebenfalls 2 · numseg Elemente. Jedes Segment könnte
also pro Sequenz eine eigene Leserichtung besitzen. In der Praxis ist das nicht der
96
ANHANG C. UDF-ENTWURFSDETAILS
Fall. Folglich kann die Leserichtung der beiden Sequenzen aus den Feldelementen
strands[0] und strands[1] ermittelt werden. Zu den Werten dieser Elemente sei
auf den vorigen Abschnitt verwiesen.
• Das Feld scores ist hier nicht von Interesse, da der Alignmenttyp DenseSeg bei
BLAST immer zusammen mit dem SeqAlign-Elementwert type = 3 auftritt und
deshalb der Score in der SeqAlign-Struktur selbst zu finden ist.
Die weiteren Alignierungen der Vergleichssequenz können durch Traversieren der SeqAlignListe erreicht werden.
C.4
Weitere Anpassungen der UDF
Damit UDFBLAST einsetzbar ist, müssen neben den im Abschnitt 6.2.4 beschriebenen
Modifikationen noch weitere Änderungen vorgenommen werden:
1. Einige Funktionen von BLAST sind als static deklariert. Diese sind nur von anderen
Funktionen verwendbar, die im gleichen Modul definiert sind. Durch die Übernahme von BLAST-Code in den FIRST- oder OPEN-Aufruf werden diese Funktionen
nun von ”außerhalb” aufgerufen, und durch ihre Deklaration nicht mehr gefunden.
Deshalb muss für folgende Funktionen die Deklaration geändert werden:
• FastaReadSequenceInternalEx()
• BlastReevaluateWithAmbiguities()
• BLASTResultFreeHsp()
2. Einige der von db2blast eingeführten Präprozessordirektiven der Art #ifdef DB2BLAST
müssen modifiziert werden, da der dort enthaltene Code auf CLI-Typen und -Funktionen zurückgreift. Auch wenn der Code niemals im Rahmen der UDF ausgeführt
würde, darf dieser nicht kompiliert werden, weil die CLI-Typen und -Funktionen nicht
bekannt sind. Die Direktiven ändern sich zu
#if defined(DB2BLAST) && !defined(UDFBLAST)
3. In BLASTSetUpSearchWithReadDbInternal() wird die Variable matrixFilePath von
der BLAST_OptionsBlk-Struktur in eine Unterstruktur von BlastSearchBlk kopiert.
4. Die Funktion BlastSaveCurrentHitlist(), welche im Rahmen der BLAST-Alignierung ausgeführt wird, fügt die Alignierungen einer Vergleichssequenz in das Feld
result_struct->results ein, welches ein Element der BlastSearchBlk-Struktur
ist. In diesem Feld stehen die Alignierungen der bisher betrachteten Vergleichssequenzen. Dieses Feld ist immer nach der Bewertung der Alignments sortiert. Da die
UDF nur eine Vergleichssequenz zu verarbeiten hat, wird die Suche nach dem Ort der
Einsortierung in das Feld nicht ausgeführt, stattdessen wird immer der Index 0 für
das Feld gesetzt.
5. Die Funktion BlastScoreBlkMatFill() wird dazu verwendet, die Matrix-Datei auszulesen. Der Verzeichnispfad, der sich in matrixFilePath befindet, muß vorn an den
Dateinamen angehängt werden.
97
ANHANG C. UDF-ENTWURFSDETAILS
6. Während der Anpassung von db2blast werden einige Funktionen als kritisch betrachtet, die auf die Index-Dateien von blastall zugreifen. Diese Funktionen könnten in
Rahmen von udfblast ebenfalls als kritisch gelten, wenn sie im ”nächste Sequenz”Kontext oder im ”wahlfreien”-Kontext stehen würden. Die einzigen Funktionsaufrufe, die dafür in Frage kommen, sind der von readdb_get_sequence() am Anfang der
Funktionen ..._blast_search() sowie der von readdb_get_sequence_length().
Der erste Aufruf wird ersetzt durch die explizite Sequenzverarbeitung in calcBlast,
und die Verwendung von readdb_get_sequence_length() kann sich nur auf die
aktuelle Sequenz beziehen, da nur diese verarbeitet wird. Die Funktion wird deshalb so modifiziert, dass sie bei udfblast immer den Wert des ReadDBFILE-Elements
seqLength zurückgibt.
98
Anhang D
Relationales Datenmodell
99
Literaturverzeichnis
[1] Altschul, S. F., W. Gish, W. Miller, E. W. Myers und D. J. Lipman: Basic
Local Alignment Search Tool. Journal of Molecular Biology, 215:403–410, 1990.
[2] Altschul, S. F., T. L. Madden, A. A. Schäffer, J. Zhang, Z. Zhang, W. Miller und D. J. Lipman: Gapped BLAST and PSI-BLAST: a new generation of protein
database search programs. Nucleic Acids Research, 25(17):3389–3402, 1997.
[3] Bairoch, A.: The PROSITE dictionary of sites and patterns, its current status.
Nucleic Acids Research, 21(13):3097–3103, Juli 1993.
[4] Barton, G. J.: Protein Sequence Alignment and Database Scanning. erschienen in:
M. J. E. Sternberg (Hrsg.): Protein Structure Prediction — a practical approach, 1997.
[5] Bellman, R. E.: Dynamic Programming. Princeton University Press, Princeton,
N.J., 1957.
[6] Bucher, P. und A. Bairoch: A generalized profile syntax for biomolecular sequence
motifs and its function in automatic sequence interpretation. In: Proceedings of the 2nd
International Conference on Intelligent Systems for Molecular Biology, Seiten 53–61,
Menlo Park, CA, 1994. AAAI Press.
[7] Chamberlin, D. D.: A Complete Guide to DB2 Universal Database. Morgan Kaufmann Publishers, Inc., San Francisco, California, 1998.
[8] Chamberlin, D. D. und R. F. Boyce: SEQUEL: A Structured English Query Language. In: Proceedings of the ACM SIGFIDET Workshop on Data Description, Access,
and Control, Seiten 249–264, Ann Arbor, MI, Mai 1974. ACM.
[9] Codd, E. F.: A Relational Model of Data for Large Shared Data Banks. Communications of the ACM, 13(6):377–387, Juni 1970.
[10] Cornish-Bowden, A.: Nomenclature for incompletely specified bases in nucleic acid
sequences: recommendations 1984. Nucleic Acids Research, 13(9):3021–3030, Mai
1985.
[11] Dayhoff, M. O., R. M. Schwartz und B. C. Orcutt: A Model of Evolutionary
Change in Proteins. Atlas of Protein Sequence and Structure, 5(3):345–352, 1978.
[12] Gotoh, O.: An improved algorithm for matching biological sequences. Journal of
Molecular Biology, 162(3):705–708, 1982.
100
LITERATURVERZEICHNIS
[13] Henikoff, S. und J. G. Henikoff: Amino acid substitution matrices from protein
blocks. In: Proceedings of the National Academy of Sciences of the USA, Band 89,
Seiten 10915–10919, November 1992.
[14] International Business Machines: IBM DB2 Universal Database: Application
Development Guide, 2000.
[15] International Business Machines: IBM DB2 Universal Database: Call Level
Interface Guide And Reference, 2000.
[16] International Business Machines: IBM DB2 Universal Database: SQL Reference, 2000.
[17] International Organization for Standardization: ISO/IEC 9075:1992/: Information Technology — Database Language SQL. International Organization for Standardization, Genf, 1992.
[18] International Organization for Standardization: ISO/IEC 9075-3:1995/:
Information Technology — Database Languages — SQL — Part3: Call Level Interface
(SQL/CLI). International Organization for Standardization, Genf, 1995.
[19] International Organization for Standardization: ISO/IEC 8824-1:1998/:
Information Technology — Abstract Syntax Notation One (ASN.1) — Specification
of basic notation. International Organization for Standardization, Genf, Zweite Auflage, 1998.
[20] IUPAC-IUB Commission On Biochemical Nomenclature: A one-letter notation for amino-acid sequences, tentative rules. Journal of Biological Chemistry,
243:3557–3559, 1968.
[21] Karlin, S. und S. F. Altschul: Methods for assessing the statistical significance of
molecular sequence features by using general scoring schemes. In: Proceedings of the
National Academy of Sciences of the USA, Band 87, Seiten 2264–2268, 1990.
[22] Lackie, J. M. und J. Dow (Herausgeber): The Dictionary of Cell & Molecular
Biology. Academic Press, London, Dritte Auflage, 1999.
[23] Lewin, B.: Genes. Oxford University Press, Oxford, Fünfte Auflage, 1994.
[24] Löffler, G. und P. E. Petrides: Biochemie und Pathobiochemie. Springer-Verlag,
Berlin Heidelberg New York, Fünfte Auflage, 1999.
[25] Needleman, S. B. und C. D. Wunsch: A general method applicable to the search for
similarities in the amino acid sequence of two proteins. Journal of Molecular Biology,
48:443–453, 1970.
[26] Pearson, W. R. und D. J. Lipman: Rapid and sensitive protein similarity searches.
Science, 22:1435–1441, März 1985.
[27] Pearson, W. R. und D. J. Lipman: Improved tools for biological sequence comparison. In: Proceedings of the National Academy of Sciences of the USA, Band 85, Seiten
2444–2448, 1988.
101
LITERATURVERZEICHNIS
[28] Smith, T. F. und M. S. Waterman: Identification of common molecular subsequences. Journal of Molecular Biology, 147:195–197, 1981.
[29] Wootton, J. C. und S. Federhen: Statistics of local complexity in amino acid
sequences and sequence databases. Computers and Chemistry, 17(2):149–163, 1993.
[30] Zhang, Z., A. A. Schäffer, W. Miller, T. L. Madden, D. J. Lipman, E. V.
Koonin und S. F. Altschul: Protein sequence similarity searches using patterns as
seeds. Nucleic Acids Research, 26(17):3986–3990, 1998.
102
Danksagungen
Bei der Anfertigung dieser Arbeit haben mir viele Leute mit Rat und Tat zur Seite gestanden. Zuallererst danke ich Prof. Johann-Christoph Freytag für die Zuweisung des Themas
und wichtige Anregungen und Hinweise. Ferner möchte ich mich bei Chokri Ben Necib für
die Betreuung und die Diskussionen bedanken. Peter Rieger hat mir mit seinen Hinweisen
zu benutzerdefinierten Funktionen viel Zeit und Frust bei der Fehlersuche erspart.
Jeannine Rettschlag danke ich für ihre Einführung in die Biochemie und die Beantwortung diesbezüglicher Fragen. Sebastian Marek hat meine drängenden LATEX-Probleme oft
mit einer Antwort oder einem \usepackage aus der Welt geschafft. Oliver Bierwagen hat
mir ein paar wichtige Tips für die Präsentation gegeben.
Nicht zuletzt möchte ich meinen Eltern für die Hinweise, die Stil und Gesamtbild meiner
Arbeit betreffen, danken.
103
Ich erkläre, diese Diplomarbeit selbständig und nur unter Verwendung der angegebenen Literatur und Hilfsmittel angefertigt zu haben.
Ich bin mit der Auslage der Arbeit in der Bibliothek der HumboldtUniversität zu Berlin einverstanden.
Berlin, den 19. Februar 2002
104
Herunterladen