β
che `e il suffisso di un unico elemento di π·, `e preceduto dal carattere π con una frequenza π, allora viene scritta nella BWT una sequenza di π lunga π;β
che `e un suffisso proprio di pi`u elementi di π·, allora si utilizza una struttura heap per unire le rispettive inverted-list e scrivere un carattere nella BWT finale ogni volta che viene eseguita lβoperazione di pop dalla struttura heap;β
che coincide con una frase π del dizionario π·, allora i caratteri da scrivere nella BWT vengono ricavati dallβarray π΄ nelle posizioni stabilite dalla inverted-list di π.Utilizzando le informazioni calcolate in Figura 5.4 possiamo applicare la procedura per calcolare ad esempio il carattere antecedente al suffisso π§ = #GATTAC β {π β© π·}, che ha un rango 0 sia in π· che in π, denotiamo quindi π§0= π0= π§. Lβinverted-list di π0contiene solo
lβindice 2, quindi il carattere che precede π§0corrisponde a π΄[2] = A (come calcolato in Figura 5.1).
Ipotizzando invece di calcolare lβordine dei caratteri antecedenti a π§ = TAC β π che `e un suffisso di due elementi di π·, uno con rango 0 in cui `e preceduto da T, π0, e lβaltro con rango 3 in cui `e
preceduto da A, π3. Lβunione delle inverted-list di π0e π3risulta [2, 3] e stabilisce che i caratteri
che precedono il suffisso π§ nella BWT compariranno nellβordine TA.
πβ² 0 1 3 1 4 2 1 3 1 4 2 0 1 4 2 0 1 3 2 0 1 3 1 4 3 1 4 2 0 1 4 2 0 1 3 1 π BWT 1 2 2 0 3 3 4 4 5 1 6 1 inverted-list 0: [2] 1: [5,6] 2: [1] 3: [3] 4: [4] (a) BWT e inverted-list. π π 1 0 2 1 3 3 4 1 5 4 6 2 BWT 2 0 3 4 1 1 π'π 4 2 1 1 0 3 π΄ T A A A T T (b) Array π΄.
Figura 5.4: (a) BWT, inverted-list e (b) arrayπ΄ per π = [0,1,3,1,4,2]. I prefissi sottolineati nella matrice π
'
corrispondono ai suffissi del parser ordinati precedentemente visti in Figura 5.2. Esempio.Gli autori del Prefix-Free Parsing suggeriscono anche la possibilit`a di velocizzare la procedura parallelizzando il lavoro con una tecnica multi-thread (versione del tool ancora in via di sviluppo). La prima fase, in cui vengono calcolati il dizionario π· e il parser π, pu`o essere parallelizzata suddi- videndo lβinput in blocchi di stesse dimensioni e assegnando ciascun blocco ad un thread. Lβultima fase, in cui viene costruita la BWT dal suffix array e dalle inverted-list delle frasi contenute in π·, pu`o essere invece parallelizzata utilizzando un thread principale che scansiona il SA di π· e ogni volta in cui individua un intervallo con gli stessi suffissi assegna ad un altro thread il compito di calcolare e scrivere la porzione di BWT sul disco. Aumentando il numero di thread non avviene per`o un miglioramento sostanziale e le cause possono essere lβaggiornamento del dizionario da parte di tutti i thread durante la fase di parsing, oppure il bottleneck creato dagli accessi su disco causati dalla scrittura della porzione di BWT che ogni thread deve eseguire.
5.3
Prefix-Free Parsing come struttura dati
Assunto di aver gi`a calcolato il parser π e il dizionario π· per una data stringa π [1, π], utilizzano lβim- plementazione descritta nella sezione precedente, si stabilisce adesso che la frase π [π, π] contiene un
CAPITOLO 5. Prefix-Free Parsing 5.3. Prefix-Free Parsing come struttura dati carattere π [π] se π β€ π β€ (π β π€). Cos`Δ± facendo, nonostante due frasi consecutive si sovrappongono di π€ caratteri (vedi Figura 5.3), ogni carattere di π `e contenuto in una sola frase, eccetto per gli ultimi π€ caratteri di π . Per eliminare questa eccezione, si considera che la stringa π sia ciclica e che inizia con una stringa appartenente ad πΈ. Ricordando che un suffisso proprio di una frase π
'
[1, π'
] `e un qualsiasi suffisso π'
[π, π'
] per 2 β€ π β€ π'
, ossia un suo suffisso diverso dalla frase completa. Possiamo quindi (ri)definire π come lβinsieme dei suffissi appropriati di lunghezza almeno π€ per cui vale il Lemma 5.1, in quanto ogni prefisso proprio di una frase lungo almeno π€ termina con una stringa appartenete a πΈ e non presenta al suo interno sottostringhe appartenenti ad πΈ.Nella sezione in cui viene descritta lβimplementazione del Prefix-Free Parsing `e stato appurato che i simboli null, appesi e anteposti per lβelaborazione della stringa in input, possono essere gli stessi. Si considera quindi la stringa π = GATTACAT!GATACAT!GATTAGATA, utilizzata come esempio fino adesso, riscritta come π = GATTACAT#GATACAT#GATTAGATA. Stabilito π€ = 2, ad π viene appesa la stringa ## e viene definito lβinsieme πΈ = {AC,AG,T#,##}. Considerando la stringa ciclica di seguito faremo riferimento a
1 4 8 12 16 20 24 28
π = ...##GATTACAT#GATACAT#GATTAGATA##... con π = 28, e per la quale al termine della procedura di parsing si ottengono
π· = { ##GATTAC, ACAT#, AGATA##, T#GATAC, T#GATAG }
π = [ ##GATTAC, ACAT#, T#GATAC, ACAT#, T#GATAG, AGATA## ] = [ 0, 1, 3, 1, 4, 2 ] Esempio.
Dopo aver memorizzato π e π· con una tecnica in grado sia di compattarli che di permettere un rapido accesso casuale; per supportare query rapide sul Prefix-Free Parsing `e necessario implemen- tare cinque ulteriori strutture dati di seguito definite e denominate con π΅π, π΅BWT, π, π e π. Tali
strutture richiedono uno workspace proporzionale alla somma della lunghezza di π e degli elementi di π·. Se non specificato diversamente, di seguito, lβindice π scorre dalla posizione 1 alla posizione π della struttura dati trattata (1 β€ π β€ π).
π΅π[1, π] `e un bit-vector ciclico in cui, π΅π[π] = 1 se e solo se la π-esima posizione di π corrisponde
al primo carattere di una stringa in πΈ. Essendo π΅π ciclico, lβ1 che identifica il primo carattere della
stringa in πΈ prefisso della prima frase in π `e in posizione (π β π€ + 1).
π΅BWT[1, π] `e un bit-vector ciclico in cui, per ogni suffisso proprio di una frase, lungo almeno π€,
π΅BWT[π] = 1 denota che lβπ-esimo carattere nella BWT `e il primo simbolo a precedere unβoccorrenza
di tale suffisso in π . Non `e necessario costruire la BWT di π per ottenere π΅BWT: si costruisce il SA
e lβarray LCP dellβinsieme π· dopo aver appeso un simbolo terminatore univoco a ciascuno dei suoi elementi; si etichetta ogni suffisso con la frequenza in π della frase che contiene tale suffisso; quindi si scansionano gli array ignorando i suffissi che sono elementi di π· (frasi intere) oppure di lunghezza minore di π€ (terminatori esclusi) e aggregando le frequenze dei suffissi che differiscono solo per i terminatori.
Per la stringa ciclica π = GATTACAT#GATACAT#GATTAGATA## in esempio si ha π΅π = [0000100100001001000001000010] e
π΅BWT= [1111110110111110111110110111].
CAPITOLO 5. Prefix-Free Parsing 5.3. Prefix-Free Parsing come struttura dati π `e una struttura dati tale per cui π[π] fornisce (i) la lunghezza dellβπ-esimo suffisso proprio πΌ di una frase lungo almeno π€; (ii) lβintervallo lessicografico delle frasi inverse che iniziano con πΌ invertito. Le lunghezze vengono memorizzate durante la costruzione di π΅BWT, e gli intervalli
lessicografici invertendo e ordinando le frasi.
La Figura 5.5 riporta la struttura dati π per la stringa di esempio. Per ogni riga si ha il suffisso proprio invertito lungo almeno π€, la sua lunghezza, e infine, lβintervallo lessicografico delle frasi inverse che iniziano con tale suffisso.
1 ## 2 [0] 2 CATAG# 6 [2] 3 CATTAG# 7 [3] 4 GATTAG# 7 [4] 5 ##A 3 [0] 6 CA 2 [2, 3] 7 GA 2 [4] 8 #TA 3 [1] 9 ##ATA 5 [0] 10 CATA 4 [2] 11 CATTA 5 [3] 12 GATTA 5 [4] 13 #TAC 4 [1] 14 ##ATAG 6 [0] 15 CATAG 5 [2] 16 CATTAG 6 [3] 17 GATTAG 6 [4] 18 #T 2 [1] 19 ##AT 4 [0] 20 CAT 3 [2, 3] 21 GAT 3 [4] 22 CATT 4 [3] 23 GATT 4 [4]
Figura 5.5: Struttura datiπ. Esempio.
π `e un wavelet tree(18) calcolato sulla BWT degli identificatori delle frasi in π, in con le fo-
glie sono etichettate da sinistra a destra con lβinsieme degli identificatori delle frasi in ordine co- lessicografico(19). In questo modo, le foglie di π etichettate con gli identificatori delle frasi che
terminano con lo stesso suffisso πΌ sono consecutive.
La Figura 5.6 riporta il wavelet tree π e la struttura con cui viene rappresentato, per la stringa di esempio. Lβalbero `e ruotato per allineare le foglie con le righe corrispondenti della griglia di rappresentazione.
2 1 3 0 4 2 0 3 4 1 1 π·[2] = AGATA## π·[1] = ACAT# π·[3] = T#GATAC π·[0] = ##GATTAC π·[4] = T#GATTAG BWT(π) - gli elementi di π· sono ordinati lessicograficamente
gli elementi di π· sono or dinati co-lessicograficamente 011100 011 π· [2] π· [1] 101 π· [3] 01 π· [0] π· [4] π
Figura 5.6: Struttura datiπ. Esempio.
π `e la permutazione che mappa per ciascuna frase la sua posizione nella BWT di π con la propria posizione in π stesso.
Per lβesempio considerato, essendo π = [0,1,3,1,4,2] e BWT(π) = [2,0,3,4,1,1] risul- ta: π(1) = 6, π(2) = 1, π(3) = 3, π(4) = 5, π(5) = 2, π(6) = 4.
Esempio.
Una volta calcolate le strutture sopra elencate possiamo effettuare query rapide riguardo la longe- st common extension (LCE), il suffix array, il longest common prefix (LCP) e la BWT come descritto in seguito. Innanzitutto, considerando sempre la stringa π [1, π] e lβarray π΅π, dato lβindice 1 β€ π β€ π
(18)Il wavelet tree `e una struttura di dati succinta per memorizzare le stringhe in uno spazio compresso, e che generalizza le
operazioni di rank e select definite su bit-vector o alfabeti arbitrari.
(19)Lβordine co-lessicografico di un insieme di stringhe corrisponde alla sequenza in cui le stringhe risultano ordinate
CAPITOLO 5. Prefix-Free Parsing 5.3. Prefix-Free Parsing come struttura dati possiamo identificare, prima lβindice 1 β€ π β€ | π | della frase ππ contenente il carattere π [π], come
π = π΅π.rank(π) mod | π |, e la posizione di π [π] calcolando il suo offset in ππ. Effettuando infine un
accesso su π per identificare ππe un accesso su π·, possiamo ritornare il carattere π [π].
LCE-query. Dati due indici1 β€ π β€ π e 1 β€ π β€ π, LCE(π, π) ritorna la lunghezza del longest com- mon prefix di π [π, π] e π [π, π]. La lunghezza viene calcolata utilizzando π΅π per identificare le
frasi contenenti π [π] e π [π] e il loro offset nelle rispettive frasi. Si denotano con πΌ e π½ i suffissi delle frasi che iniziano con π [π] e π [π], quindi | πΌ |, | π½ | > π€. Sappiamo che πΌ e π½ non sono prefissi appropriati lβuno dellβaltro, quindi i casi possibili sono due:
1. πΌ[π] β π½[π] per un valore π < min(| πΌ |, | π½ |) e quindi LCE(π, π) = | LCP (πΌ, π½) |;
2. πΌ = π½ e quindi LCP(π, π) = | πΌ | + LCE (π + | πΌ |, π + | πΌ |), dove π [π + | πΌ |, π] e π [π + | πΌ |, π] sono entrambi suffissi di π che iniziano immediatamente dopo una stringa elemento di πΈ. In questo caso `e possibile memorizzare una tabella hash che mappa la posizione di inizio di ciascun suffisso con il suo rango lessicografico di tutti i suffissi, un array LCP dei suffissi e una struttura dati che supporta le Range-Minimum Query (RMQ) sullβarray LCP.
Per la stringa π in esempio la query LCE(4, 12) = 9 in quanto il longest common prefix tra i suffissi
π [4, 28] = TACAT#GATACAT#GATTAGATA## e π [12, 28] = TACAT#GATTAGATA##
`e TACAT#GAT. Secondo la procedura descritta, per il suffisso πΌ = π½ = TAC si ha quindi che LCE(4, 12) = 3 + LCE(7, 15). La Figura 5.7 riporta i suffissi π [π, π] che iniziano dopo un elemento di πΈ, il loro rango lessicografico e lβarray LCP. La tabella hash per lβesempio mappa gli indici ordinati [1, 7, 10, 15, 18, 24] con il rispettivo rango lessicografico [4, 0, 3, 1, 5, 2]. Per stabilire il risultato di LCE(7, 15) si recupera il valore della lunghezza del longest common prefix tra i suffissi di rango 0 e 1, in quanto 7 e 15 sono mappati rispettivamente con 0 e 1, che risulta 6. Quindi alla fine si ottiene che LCE(4, 12) = 3 + LCE(7, 15) = 3 + 6 = 9.
π rango π [π βΆ28] LCP 7 0 AT#GATACAT#GATTAGATA## 0 15 1 AT#GATTAGATA## 6 24 2 ATA## 2 10 3 GATACAT#GATTAGATA## 0 1 4 GATTACAT#GATACAT#GATTAGATA## 3 18 5 GATTAGATA## 5 Figura 5.7 Esempio.
SA-query. Una query SA[π] ritorna la posizione dellβπ-esimo suffisso lessicograficamente minore in π . Dato lβindice π `e possibile utilizzare π = π΅BWT.rank(π) per determinare il rango lessicogra-
fico π (partendo da zero) del suffisso proprio πΌ di una frase lungo almeno π€ che inizia in SA[π]; e π
'
= π β π΅BWT.select(π΅BWT.rank(π)) per ottenere il rango lessicografico π'
(partendo da 0) diπ [SA[π], π] tra i suffissi di π che iniziano con πΌ. DopodichΒ΄e si accede prima ad π per trovare la lunghezza di πΌ e lβintervallo co-lessicografico delle frasi che terminano con πΌ; e poi a π per trovare lβindice π della π-esima frase nellβintervallo co-lessicografico che appare nella BWT(π).
CAPITOLO 5. Prefix-Free Parsing 5.4. Performance del Prefix-Free Parsing