252
INPE-13052-TDI/1021 ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM MODELOS MARKOVIANOS OCULTOS (HMMs) José Olimpio Ferreira Tese de Doutorado do Curso de Pós-Graduação em Computação Aplicada, orientada pelo Dr. Solon Venâncio de Carvalho, aprovada em 29 de março de 2004. INPE São José dos Campos 2005

ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Embed Size (px)

Citation preview

Page 1: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

INPE-13052-TDI/1021

ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM MODELOS MARKOVIANOS OCULTOS (HMMs)

José Olimpio Ferreira

Tese de Doutorado do Curso de Pós-Graduação em Computação Aplicada, orientada pelo Dr. Solon Venâncio de Carvalho, aprovada em 29 de março de 2004.

INPE São José dos Campos

2005

Page 2: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

519.8 FERREIRA, J. O. Algoritmos de programação dinâmica usados em modelos markovianos ocultos (HMMs) / J. O. Ferreira. – São José dos Campos: INPE, 2004. 250p. – (INPE-13052-TDI/1021). 1.Processos estocásticos. 2.Modelos markovianos. 3.Progrramação dinâmica. 4.Modelos Markovianos Ocultos (HMMs). 5.Biologia computacional. 6.Bioinformática. I.Título.

Page 3: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 4: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 5: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O engajamento na ciência (empregando a palavra na acepção geral alemã Wissenschaft) não reside na discordância sobre fatos verificados, mas sobre sua

escolha e combinação, e sobre o que se pode inferir a partir dos mesmos.

ERIC HOBSBAWN (1917-), historiador, em “Sobre Historia”.

E para que executar projetos, já que o projeto é, nele mesmo, um prazer suficiente?

CHARLES BAUDELAIRE (1821-1867), poeta francês, em “Pequenos poemas em prosa”.

Page 6: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 7: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Dedico carinhosamente esta dissertação a meus pais MANOEL e ELIZENA ( )

a meus filhos VITOR, VINICIUS e NATÁLIA a minha esposa MARIA das GRAÇAS

e àqueles que me incentivaram e apoiaram.

Page 8: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 9: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

AGRADECIMENTOS

Agradeço a todas pessoas que me ajudaram a vencer mais essa etapa da vida. Agradeço à Fundação de Aperfeiçoamento de Pessoal de Nível Superior - CAPES, pelo auxilio financeiro. Agradeço so Instituto Nacional de Pesquisas Espaciais - INPE pela oportunidade de estudos e utilização de suas instalações. Agradeço aos professores do INPE pelo conhecimento compartilhado. Agradeço à Universidade Católica de Goiás – UCG - por ter proporcionado as condições físicas e financeiras para que eu pudesse cursar o Doutorado em Computação Aplicada no Instituto Nacional de Pesquisas Espaciais – INPE. Agradeço ao meu orientador Dr. Solon Venâncio de Carvalho pela sua imensa paciência e valioso incentivo na realização desta pesquisa e na confecção deste relatório. Agradeço ao apoio de minha esposa Maria das Graças, de minha filha Natália e dos meus filhos Vitor e Vinicius que conviveram durante esse período com a minha ausência, com as minhas angústias, ansiedades e o meu nervosismo. Agradeço a meus pais por sempre acreditarem na importância do estudo. Agradeço, também, aos amigos e colegas que me apoiaram e incentivaram a fazer esse curso de Doutorado em Computação Aplicada, e que me aconselharam nos momentos mais delicados.

Page 10: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 11: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

RESUMO

Esta tese trata dos algoritmos de programação dinâmica que são usados nos Modelos Markovianos Ocultos (HMMs), perfis-HMMs, aplicados no estudo de seqüências biológicas no campo da Bioinformática. O foco é a investigação de técnicas (métodos ou paradigmas) de economia de espaço que proporcionem a melhor economia de tempo, e que sejam adequadas para a utilização no cálculo de medidas de interesse dos HMMs. Explorou-se a hipótese da utilização da estratégia de checkpoints em conjunto com o princípio D&C como solução consistente para o problema de complexidade de espaço versus complexidade de tempo dos algoritmos de programação dinâmica utilizados no cálculo de medidas de interesse em perfis-HMMs. Propõe-se um algoritmo denominado algoritmo de programação dinâmica com L-níveis de checkpoints bidimensionais que pode ser usado em conjunto com procedimentos de retrocedimento parcial ou completo, sobre a matriz de programação dinâmica. A versão com retrocedimento parcial desse algoritmo proposto, denominada algoritmo de Viterbi com L-níveis de checkpoints bidimensionais, com partição fixa de memória e retrocedimento restrito, foi superior em desempenho, tanto na análise teórica, quanto nos testes de desempenho a posteriori, ao algoritmo de Viterbi com L-níveis de checkpoints por diagonais, com partição móvel de memória e retrocedimento restrito, considerado o estado da arte entre esses algoritmos de programação dinâmica com a técnica de checkpoints. O desempenho desse algoritmo proposto foi superior, nos testes a posteriori, inclusive ao próprio algoritmo de Viterbi básico. Na simulação dos requerimentos de memória, verificou-se que os requerimentos de memória desse algoritmo proposto são uma fração decrescente dos requerimentos de memória do algorimo de Viterbi básico, para instâncias crescentes do problema, indicando um comportamento assintótico dos requerimentos de memória muito favorável para a computação de instâncias do problema que são intratáveis pelo algoritmo de Viterbi básico.

Page 12: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 13: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

DYNAMIC PROGRAMMING ALGORITHMS USED IN HIDDEN MARKOV MODELS (HMMs)

ABSTRACT

This thesis is about dynamic programming algorithms used in Hidden Markov Models (HMMs) -HMM profiles - applied to biological sequence studies in bioinformatics. Its investigation focuses on space economy techniques (methods or paradigms), that provide best time-saving solutions, which would be adequate for use in calculating HMM interest measures. The checkpoints paradigm use hypothesis together with the D&C strategy, as a consistent solution for the problem of space versus time complexity in dynamic programming algorithms which are used in the calculation of measures of interest to the HMM profiles, is explored. An algorithm denominated dynamic programming algorithm with L-levels of bidimensional checkpoints which can be used together with partial or complete traceback procedures through to the dynamic programming matrix is proposed. The version with partial traceback denominated the Viterbi algorithm with L-levels of bidimensional checkpoints with fixed memory partition and restricted traceback was superior in performance to the Viterbi algorithm with L-levels of diagonals checkpoints with mobile memory partition and restricted traceback, not only in the theoretical analysis but also in the tests a posteriori. The latter is considered state of the art among the dynamic programming algorithms with the checkpoints technique. In the tests a posteriori, the proposed algorithm has been proved to be superior in performance even to the basic Viterbi algorithm. In the memory requirement simulation, the memory requirements of the new algorithms have been shown to be a decreasing fraction of those of the Viterbi basic algorithm for growing instances of the problem, indicating an asymptotic behavior of memory requeriments, very favorable to the computation of instances of the problem which are intractable by the basic Viterbi algorithm.

Page 14: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 15: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

SUMÁRIO

Pág.

LISTA DE FIGURAS ....................................................................................................17

LISTA DE TABELAS ...................................................................................................19

LISTA DE SÍMBOLOS .................................................................................................21

LISTA DE SIGLAS E ABREVIATURAS ....................................................................25

CAPÍTULO 1 INTRODUÇÃO...................................................................................27

1.1 Introdução .......................................................................................................27

1.2 O Problema......................................................................................................38

1.3 Objetivos e Hipóteses ......................................................................................40

1.4 Justificativa......................................................................................................41

CAPÍTULO 2 CONCEITOS BÁSICOS.....................................................................47

2.1 Introdução .......................................................................................................47

2.2 Modelos Markovianos Ocultos (HMMs) ........................................................47

2.2.1 Definições .......................................................................................................47

2.2.2 Os Três Problemas Básicos dos HMMs ..........................................................49

2.2.2.1 Soluções para o Problema 1 ............................................................................49

2.2.2.2 Soluções para o Problema 2 ............................................................................51

2.2.2.3 Soluções para o Problema 3 ............................................................................55

2.2.3 Modelo de Atribuição de Escores e Problemas Numéricos ......... ..................57

2.2.4 Perfis-HMMs para Seqüências Biológicas ......................................................60

2.3 Medidas de Complexidade de Algoritmos .....................................................68

2.3.1 Definição de Algoritmo...................................................................................68

Page 16: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

2.3.2 Análise de Desempenho de Algoritmos e Complexidade de Problemas ........70

2.3.2.1 Análise Assintótica ..........................................................................................75

2.4 Algoritmos de Programação Dinâmica............................................................78

2.4.1 Estratégia de Programação Dinâmica..............................................................78

2.4.2 Métodos de Programação Dinâmica e HMMS................................................79

CAPÍTULO 3 ALGORITMOS DE PROGRAMAÇÃO DINÂMICA COM

CHECKPOINTS USADOS EM PERFIS-HMMS...............................83

3.1 Introdução .......................................................................................................83

3.2 Revisão da Literatura.......................................................................................84

3.3 Algoritmos de Programação Dinâmica com Checkpoints..............................102

3.3.1 Algoritmo A: 2-Níveis com Partição Fixa.....................................................105

3.3.2 Algoritmo B: 2-Níveis com Partição Móvel .................................................106

3.3.3 Algoritmo C: 3-Níveis com Partição Móvel .................................................108

3.3.4 Algoritmo D: L-Níveis com Partição Móvel .................................................109

3.3.5 Algoritmo E: Viterbi com L-Níveis de Checkpoints por Diagonais com Retrocedimento Restrito ................................................................................112

3.3.6 Disposição Ótima de Checkpoints por Linhas (Colunas) e o Algoritmo Melhorado .....................................................................................................118

3.3.6.1 Algoritmo Melhorado....................................................................................119

3.3.6.2 Algoritmo Ótimo ...........................................................................................120

3.3.7 Disposição Ótima de Checkpoints por Diagonais e o Algoritmo de Viterbi Melhorado .....................................................................................................121

CAPÍTULO 4 ALGORITMOS PROPOSTOS .........................................................123

4.1 Introdução .....................................................................................................123

4.2 Algoritmos de L-Níveis de Checkpoints Bidimensionais..............................124

Page 17: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

4.2.1 Algoritmo F: 2-Níveis de Checkpoints Bidimensionais e Partição Fixa de Memória ....................................................................................................130

4.2.2 Algoritmo G: 3-Níveis de Checkpoints Bidimensionais e Partição Fixa de Memória ....................................................................................................132

4.2.3 Algoritmo H: L-Níveis de Checkpoints Bidimensionais e Partição Fixa de Memória ....................................................................................................135

4.2.4 Algoritmo J: Viterbi com L-Níveis de Checkpoints Bidimensionais e Partição Fixa de Memória............................................................................................136

CAPÍTULO 5 EXPERIMENTAÇÃO E ANÁLISE DOS RESULTADOS.............145

5.1 Introdução .....................................................................................................145

5.2 Metodologia de Experimentação...................................................................145

5.3 Implementação dos Algoritmos e Realização dos Experimentos .................150

5.4 Apresentação e Análise dos Resultados da Experimentação.........................161

CAPÍTULO 6 CONCLUSÕES E COMENTÁRIOS................................................185

REFERÊNCIAS BIBLIOGRÁFICAS .........................................................................201

BIBLIOGRAFIA COMPLEMENTAR …...................................................................207

APÊNDICE 1 - ALGORITMO DE VITERBI BÁSICO .............................................209

APÊNDICE 2 - ALGORITMO DE VITERBI COM DE 2-NÍVEIS CHECKPOINTS POR LINHAS E PARTICIONAMENTO MÓVEL DE MEMÓRIA.217

APÊNDICE 3 - ALGORITMO DE VITERBI COM 2-NÍVEIS DE CHECKPOINTS POR DIAGONAIS, COM PARTICIONAMENTO MÓVEL DE MEMÓRIA E RETROCEDIMENTO RESTRITO...........................227

APÊNDICE 4 - ALGORITMO DE VITERBI COM 2-NÍVEIS DE CHECKPOINTS BIDIMENSIONAIS, PARTICIONAMENTO FIXO DE MEMÓRIA E RETROCEDIMENTO RESTRITO ..................................................239

GLOSSÁRIO ...............................................................................................................249

Page 18: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 19: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

LISTA DE FIGURAS

2.1 - HMM discreto com três estados e 5 símbolos de observação................................ 49

2.2 - Topologia de um perfil-HMM com 4 módulos padrões (Hughey e Krogh, 1996).62

2.3 – Taxa de crescimento do tempo de computação de funções típicas. ...................... 78

4.1 - Particionamento A: L-níveis de checkpoints completos e particionamento fixo de memória. .............................................................................................................. 125

4.2 - Particionamento B: L-níveis de checkpoints completos e particionamento fixo de memória. .............................................................................................................. 125

4.3 - Particionamento C: L-níveis de checkpoints completos e particionamento móvel de memória. .............................................................................................................. 126

4.4 - Particionamento D: L-níveis de checkpoints parciais e particionamento fixo de memória. .............................................................................................................. 126

4.5 - Particionamento E: L-níveis de checkpoints parciais e particionamento móvel de memória. .............................................................................................................. 127

4.6 - Particionamento F: L-níveis de checkpoints parciais e particionamento fixo de memória. .............................................................................................................. 127

4.7 - Particionamento G: L-níveis de checkpoints parciais e particionamento móvel de memória. .............................................................................................................. 128

5.1 - Distribuição do tamanho das seqüências de proteínas – adaptada de figura do Swiss-Prot group (2004)...................................................................................... 146

5.2 - Modelo Nulo, R, totalmente probabilístico. ......................................................... 153

5.3 – Tempos de CPU dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 2000 nós.............................................................. 168

5.4 – Tempos totais de execução dos algoritmos de Viterbi para alinhamentos de seqüências de aminoácidos a um HMM com 2000 nós....................................... 169

5.5 – Tempos de CPU dos algoritmos de Viterbi para alinhamentos de seqüências de aminoácidos a um HMM com 2000 nós (eixos logarítmicos)............................. 170

5.6 – Tempos totais de execução dos algoritmos de Viterbi para alinhamentos de seqüências de aminoácidos a um HMM com 2000 nós (eixos logarítmicos). .... 171

5.7 – Tempos de CPU dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 2000 nós.............................................................. 172

Page 20: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

5.8 – Tempos totais de execução dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 2000 nós......................................................... 173

5.9 – Tempos de CPU para efetuar 4 execuções de computações de programação dinâmica de seqüências de aminoácidos contra um HMM com 500 nós – extraído da Figura 3 de Tarnas e Hughey (1998). ............................................................. 174

5.10 – Tempos totais de execução para efetuar 4 execuções de computações de programação dinâmica de seqüências de aminoácidos contra um HMM com 500 nós – extraída da Figura 3 de Tarnas e Hughey (1998)....................................... 175

5.11 – Tempos de CPU dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 368 nós................................................................ 177

5.12 – Tempos totais de execução dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 368 nós......................................... 178

Page 21: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

LISTA DE TABELAS

2.1 - Taxas de Crescimento Típicas................................................................................77

3.1 - Resumo do Desempenho de Alguns Algoritmos de Espaço Linear para o Problema LCS........................................................................................................................90

5.1 - Códigos dos 20 Aminoácidos, com Uma e Três Letras. ......................................152

5.2 - Freqüências Dayhoff de Ocorrências de Aminoácidos do Banco de Dados Swissprot 34 ........................................................................................................153

5.3 - Resultados das Estimativas dos Requerimentos de Memória para os Algoritmos de Viterbi (Valores em Mb).....................................................................................163

5.4 - Resultados das Estimativas Normalizadas dos Requerimentos de Memória para os Algoritmos de Viterbi (Básico = 1). ...................................................................164

5.5 - Resultados dos Tempos de CPU e dos Tempos Totais de Execução (Wall Time) do Primeiro Experimento para os Algoritmos de Viterbi (Tempos em Segundos). .165

5.6 - Resultados dos Tempos de CPU e dos Tempos Totais de Execução (Wall Time) do Segundo Experimento para os Algoritmos de Viterbi (Tempos em Segundos). .167

5.7 - Resultados dos Tempos de CPU e dos Tempos Totais de Execução (Wall Time) do Terceiro Experimento para os Algoritmos de Viterbi (Tempos em Segundos). .176

Page 22: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM
Page 23: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

LISTA DE SÍMBOLOS

A - Matriz de probabilidades de transição de estados

A - Matriz de estimativas das probabilidades de transição de estados

(Aij) - Número de vezes que cada transição ocorre no conjunto de observações de treinamento

aij - Probabilidade de transição do estado i para o estado j

ija - Estimativa da probabilidade de transição do estado i para o estado j

bt(i) - Variável backward, definida como a probabilidade de um certo sufixo até um determinado estado, dado o modelo λ

( )nRC , - Quantidade mínima de computações de linhas da matriz de PD ou o custo de computar n linhas da matriz de PD com R linhas de memória

c - Distância entre dois checkpoints sucessivos

Dj - O j-ésimo estado D (delete)

d - Número de pares de pontos concordantes + indels, ou valor do escore

E - Matriz de probabilidades de emissão de símbolos

E - Matriz de estimativas das probabilidades de emissão de símbolos

(Ei(k)) - Número de vezes que cada emissão ocorre no conjunto de observações de treinamento

ie - Estimativa da probabilidade de emissão do símbolo k no estado j

ej(k) - Probabilidade de emissão do símbolo k no estado j

)(tF Dj - Escore forward em log-odds dos caminhos terminando com Oi sendo

emitido pelo estado Dj )(tF I

j - Escore forward em log-odds dos caminhos terminando com Oi sendo emitido pelo estado Ij

)(tF Mj - Escore forward em log-odds dos caminhos terminando com Oi sendo

emitido pelo estado Mj ft(i) - Variável forward, definida como a probabilidade de um certo prefixo até

um determinado estado, dado o modelo λG(vk|O) - Probabilidade a posteriori do símbolo vk vir de um estado num conjunto

especificado Ij - O j-ésimo estado I (insert)

Page 24: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

K - Número de símbolos de um alfabeto discreto e finito

L - Número de níveis de checkpoints

l - Operação elementar

Mj - O j-ésimo estado M (match)

m - Comprimento de uma dada string ou seqüência de observações

N - Número de estados

n - Comprimento do HMM ou de uma dada string ou seqüência de observações

O - Seqüência de observações

O(n) - Notação big-O para comparação do crescimento assintótico de funções

o(n) - Notação little-o para comparação do crescimento assintótico de funções

P(O/λ) - Probabilidade de ocorrência de uma certa seqüência de observações O dado o modelo λ

p - Tamanho da LCS

Q - Seqüência ou caminho de Estados de um HMM

Q* - Caminho de estados mais provável

qt - Processo estocástico que indica o estado do modelo no instante t

q - Número de pontos concordantes dominantes

R - Número de linhas (colunas ou diagonais) da matriz de PD que podem ser armazenadas como checkpoints

Rbd - Linhas de memória de checkpoints bidimensionais

Rcp - Linhas de memória R destinadas a armazenar os checkpoints

Rtb - Linhas de memória R destinadas ao cálculo de seções da matriz de PD

( )RrL - Número máximo de linhas da matriz de PD, que podem ser computadas com o algoritmo de L-níveis

r - Número de pares de pontos concordantes

Page 25: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

S - Conjunto de estados de um HMM

Si - i-ésimo estado do HMM

( )nSL - Espaço necessário para armazenar os checkpoints de nível L

s - Tamanho do alfabeto

T - Número de épocas ou tempo

( )nTL - Estimativa do tempo total gasto pelo algoritmo com L-níveis de checkpoints

( )tTBXj - Vetor para guardar o argumento que maximiza V , , )(tXj MIDX ,,∈

para t e jt - Instante de tempo (época)

V - Conjunto de símbolos que constitui um alfabeto discreto e finito

)(tV Dj - Escore Viterbi em log-odds do melhor caminho terminando com Oi

sendo emitido pelo estado Dj )(tV I

j - Escore Viterbi em log-odds do melhor caminho terminando com Oi sendo emitido pelo estado Ij

)(tV Mj - Escore Viterbi em log-odds do melhor caminho terminando com Oi

sendo emitido pelo estado Mj vi - i-ésimo símbolo de um conjunto de símbolos que constitui um alfabeto

discreto e finito vt(i) - Variável Viterbi, definida como a mais alta pontuação (mais alta

probabilidade) ao longo de um caminho até o instante t w - Tamanho das palavras da máquina (8, 16, 32, ou 64 bits)

γt(i) - Probabilidade a posteriori do estado Si no instante t,

λ - Parâmetros do modelo, também, designa o próprio modelo

λ - Estimativa dos parâmetros do modelo

π - Vetor de probabilidades para o estado inicial de um HMM

π - Vetor de estimativas das probabilidades para o estado inicial de um HMM

πi - Probabilidade para o estado inicial i de um HMM

iπ - Estimativa da probabilidade para o estado inicial i de um HMM

Page 26: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

ψt(j) - Vetor para guardar, para cada t e j, o argumento que maximiza vt(i)

))(( nfΩ - Notação big-Ω para comparação do crescimento assintótico de funções

))(( nfω - Notação little-ω para comparação do crescimento assintótico de funções

))(( nfΘ

- Notação big-Θ para comparação do crescimento assintótico de funções

Σ - Alfabeto composto pelos símbolos dos aminoácidos (proteínas)

Σd - Alfabeto degenerado para as proteínas

Page 27: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

LISTA DE SIGLAS E ABREVIATURAS

ADN - Ácidos desoxirribonucléicos

ANSI - American National Standards Institute

API - Interface de programação da aplicação (application program interface)

ARN - Ácidos ribonucléicos

ARNm - Ácidos ribonucléicos mensageiros

bp - Pares de bases

CPU - Unidade de processamento central

D&C - Dividir e conquistar

ECG - Eletrocardiograma

GCC - Coleção de compiladores da GNU

GNU - a recursive acronym for "GNU's Not Unix"

HMMpro - Pacote de propósito geral para simulação e modelagem de HMMs para a análise de seqüências biológicas

KDE - ambiente gráfico para estações Linux e Unix

MLE - Estimativa de máxima verossimilhança

HMM - Modelo markoviano oculto

L2 - Nível 2

LCS - Maior subseqüência comum

LL - log likelihood

Mb - Mega bytes

mb - Milhões de pares de bases

MEM - Memória

Page 28: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

ML - Máxima verossimilhança

pb - Pares de bases

PC - Computador pessoal

PD - Programação dinâmica

Pfam - Banco de dados de famílias de proteinas, de alinhamentos e de HMMs (Protein families database of alignments and HMMs)

RAM - Memória de acesso aleatório (Randon Memory Access)

SAM - Sistema de modelagem e alinhamento de seqüências (Sequence Alignment and Modeling Systems)

Page 29: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

CAPÍTULO 1

INTRODUÇÃO

1.1 Introdução

Esse trabalho trata de algoritmos de programação dinâmica que são usados em

Bioinformática (ou Biologia Computacional), mais precisamente nos Modelos

Markovianos Ocultos (HMMs) com topologias lineares, por exemplo nos perfis-HMMs.

O foco é a investigação de técnicas (métodos ou paradigmas) de economia de espaço,

que proporcionem a melhor economia de tempo e, que sejam adequadas para a

utilização no cálculo de medidas de interesse dos HMMs, mas que sejam, também,

adequadas no processo de treinamento destes modelos.

Nos organismos ou seres vivos são encontradas duas famílias de macromoléculas que

são especialmente estudadas, devido a sua importância no ciclo de vida destes

organismos ou seres: as proteínas e os ácidos nucléicos (ácidos ribonucléicos - RNA - e

ácidos desoxirribonucléicos - DNA). Essas macromoléculas são por conveniência

tratadas como seqüências biológicas, que são strings de um alfabeto finito de resíduos.

A estrutura primária das proteínas são vistas como seqüências (polímeros, cadeias)

compostas, geralmente, pelo encadeamento dos 20 aminoácidos. Os ácidos nucléicos

são vistos como seqüências compostas, geralmente, pelo encadeamento de 4

nucleotídeos.

A principal função das moléculas ou seqüências de DNA é armazenar e transmitir a

informação genética ou hereditária. Uma molécula de DNA contém vários genes, que

são as unidades funcionais e físicas básicas da hereditariedade. Cada gene é uma

seqüência específica de nucleotídeos que contém a informação necessária para a síntese

de uma determinada proteína. As moléculas de DNA estão organizadas em estruturas

que são denominadas cromossomos e o conjunto de todos os cromossomos de um ser

vivo constitui o seu genoma (ou genótipo), que em princípio codifica quase toda a

informação de sua biologia. Cada cromossomo é constituído por proteínas e DNA em

partes aproximadamente iguais. Estima-se que o genoma humano contenha 3 bilhões de

27

Page 30: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

pares de bases (bp) agrupados em cerca de 30.000 a 40.000 genes e, que cada

cromossomo contenha entre 50 e 250 milhões de bp (mb). O genoma de outros

organismos vivos como o rato tem cerca de 3 bilhões de pares de bases e as bactérias

cerca de 600 mb.

As moléculas ou seqüências de RNA intervêm no processo de síntese das proteínas,

através do RNA mensageiro - RNAm – e do RNA transportador - RNAt. Já as proteínas

são elementos constituintes fundamentais das células e dos diversos tecidos dos seres

vivos, além de participarem de diversos mecanismos celulares como: catálise de reações

bioquímicas, transporte de outras moléculas, transmissão de mensagens, etc..

O estudo dos genes, da expressão gênica, da síntese protéica e das próprias proteínas

provê informações sobre o crescimento, a comunicação e a organização celular,

guiando-nos na elucidação e aprendizado do complexo funcionamento dos seres vivos.

Os biólogos estão interessados na função molecular que leva ao fenótipo – o conjunto

de características de um indivíduo, determinado pelo seu genótipo (ou genoma) e pelo

ambiente. Eles estão interessados no fluxo de informação entre o genótipo (ou genoma)

e o fenótipo. Esse processo principal que transforma a informação contida no DNA

genômico para os aspectos funcionais do organismo é conhecido em biologia como o

dogma central da genética molecular. Resumidamente diz-se que, com a intervenção de

determinados mecanismos celulares, do DNA fabrica-se RNA e deste RNA fabrica-se

proteína.

1) Existe um processo de desencadeamento, tratamento e manipulação da

informação contida na seqüência genômica antes que essa produza

concretamente um efeito biológico. São considerados cinco passos básicos no

processo de síntese protéica, nesse processo de transformação da informação

contida na seqüência genômica em função. Esses passos são:

2) Transcrição da informação contida na seqüência de DNA e síntese do(s)

fragmento(s) de pré-RNAm;

28

Page 31: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

3) Splicing - Remoção dos Introns do pré-RNAm e junção dos diversos segmentos

Exons produzindo o RNAm. Esse processo está presente somente nos eucariotes

– organismos de mais alta ordem, como os humanos;

4) Tradução do RNAm em proteína;

5) Dobramento da proteína recém-sintetizada;

6) Função final da proteína produzida.

Essa manipulação da informação só é bem entendida para o processo de tradução. Para

os processos de transcrição e splicing existe um entendimento parcial do processo bio-

molecular que produz essa manipulação. Já para os processos de dobramento da

proteína e função final da proteína produzida existe um entendimento precário, que está

muito distante do ideal. Desta forma, dentre os vários desafios ou apostas da biologia

nos dias atuais é de interesse destacar os seguintes: a elucidação e compreensão da

informação genética, a cura através da modificação da informação genética, a

elucidação da expressão gênica e a compreensão e identificação das funções das

proteínas.

Esses desafios da biologia estão intimamente relacionados com diversos projetos do

genoma de espécies animais e vegetais. O objetivo primário dos Projetos do Genoma

Humano e de outros seres vivos é fazer uma série de mapas (diagramas descritivos) de

cada cromossomo com resoluções crescentemente mais finas. O processo de

mapeamento consiste de:

1) Dividir os cromossomos em fragmentos menores que possam ser multiplicados e

caracterizados (descritos);

2) Mapeamento - ordená-los para corresponder às suas localizações respectivas nos

cromossomos;

3) Determinar a seqüência de bases de cada um dos fragmentos de DNA

ordenados.

29

Page 32: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O objetivo final da pesquisa do genoma é encontrar todos os genes na seqüência de

DNA e desenvolver ferramentas para usar essa informação seja no estudo da biologia

humana e medicina, seja no estudo de animais e plantas de interesse agropecuário, seja

no interesse ecológico ou de preservação de espécies ameaçadas.

Para o sucesso destes projetos de elucidação do genoma de determinadas espécies é

necessário um investimento continuado em ferramentas analíticas e em banco de dados

novos e correntes. As ferramentas analíticas e os bancos de dados correntes precisam ser

constantemente adaptados para atender a evolução das necessidades da comunidade

científica. Além disso são necessárias novas ferramentas analíticas e bancos de dados

novos para as atividades que se encontram em expansão – expressão gênica e análise

funcional das proteínas, armazenamento e análise de dados de variação de uma dada

seqüência, e modelagem de redes biológicas complexas e de interações. Esses novos

métodos e tecnologias que estão revolucionando a pesquisa biológica, foram criados

para responder aos desafios explícitos ou gerados no seio destes projetos. Os métodos

experimentais diretos, que formam o ferramental clássico de pesquisa biológica, talvez

fossem capazes de resolver os problemas apresentados por esses projetos, mas a custos

impraticáveis e tempos inaceitavelmente longos, além dos problemas éticos que são

colocados pela sua utilização principalmente em seres humanos.

Desta forma, têm sido desenvolvidas novas técnicas de sequenciamento (sequencing) e

métodos de investigação por analogia para responder as desafios de redução de custo e

de tempo, e de superação do problema ético. Esses métodos permitem tirar os princípios

gerais a partir da comparação das seqüências entre si; por exemplo, determinar a função

de uma proteína a partir de sua estrutura química (primária) ou física (forma).

Entretanto esses métodos e técnicas são essencialmente modelos matemáticos que

necessitam ser implementados através de algoritmos que possam ser executados em

computadores, devido à sua natureza iterativa e/ou à grande quantidade de

processamento exigido.

Esses novos métodos e técnicas têm gerado uma quantidade crescente de informações

sobre seqüências biológicas - a produção de dados do genoma cresce diariamente -, o

30

Page 33: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

que reforça a necessidade do uso de modelos matemáticos e de métodos computacionais

de análise para tratar essas informações. Mas impõe, também, a necessidade de

investigação desses e de novos modelos matemáticos capazes de analisar grande

quantidades de dados, bem como a investigação de técnicas computacionais que dêem

suporte a esses métodos.

Dentre os novos desafios que estão sendo visualizados pelos projetos do genoma

humano e de outros organismos vivos, que possivelmente serão o foco de explorações

biológicas nesse século e nos vindouros, se destaca o uso desse enorme estoque de

dados para explorar como o DNA, as proteínas e o ambiente se interagem para criarem

sistemas de vida dinâmicos e complexos. Essas explorações englobam os seguintes

estudos:

• Transcriptoma – refere-se ao genoma funcional ou genoma expresso. Análise em

grande escala de RNAm para determinar quando, onde e sob que condições

ocorrem a expressão dos genes;

• Proteoma – o estudo da função e da expressão das proteínas e a geração de

estruturas 3-D de uma ou mais proteínas de cada família de proteínas;

• Sequenciamento – uso de métodos laboratoriais e computacionais para a

aquisição da estrutura dita primária de uma dada seqüência biológica;

• Estudos knockout – são métodos experimentais que pretendem entender a função

de seqüências de DNA e descobrir quais as proteínas que elas codificam. Os

pesquisadores têm como estratégia a inativação de genes em organismos vivos e

o monitoramento de quaisquer mudanças que poderiam revelar a função de

genes específicos;

• Genoma comparativo – análise comparativa (side by side) de padrões de

seqüências de DNA humanos e de organismos bem estudados, denominados

modelos. Esse tipo de investigação tem revelado ser uma das mais poderosas

31

Page 34: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

estratégias para a identificação de genes humanos e interpretação de suas

funções.

Uma nova disciplina ou campo de pesquisa tem se estruturado no seio da comunidade

científica internacional, para fazer face a esses desafios, como um esforço conjunto de

cientistas de várias áreas de pesquisa incluindo: Biologia, Computação, Matemática,

Tecnologia da Informação, Química, Física e Engenharia. Bioinformática ou Biologia

Computacional são os termos forjados para esse novo campo para gerenciar e analisar

os dados de seqüências biológicas, com os objetivos principais de modelagem e de

entendimento de sistemas viventes ou biológicos. Desta forma a Bioinformática ligou

duas ciências que vêm crescendo de forma exponencial nos últimos 20 anos, Biologia e

Computação.

Nesse ponto é importante salientar que existe um choque de culturas entre estes

cientistas com formação essencialmente numérica ou quantitativa (matemáticos, físicos,

engenheiros e cientistas da computação) e os biólogos. Os cientistas do campo numérico

ou quantitativo estão mais familiarizados com uma ciência com regras bem definidas e

com uma fundamentação matemática. Os biólogos tratam com uma ciência que tem

poucas regras e muitas exceções. Mas este choque tem provocado uma troca de

experiência positiva entre os dois campos de pesquisa.

Os termos Biologia Computacional e Bioinformática têm sido empregados, com

predominância do primeiro, a cada vez que se fala das mais recentes conquistas feitas

nas áreas relacionadas ao genoma, ao proteoma e à expressão de proteínas. Embora

nunca tenha existido uma definição única e precisa para os termos Biologia

Computacional e Bioinformática, esses termos têm sido utilizados para se referir às

questões científicas e às questões de tecnologia e infra-estrutura computacional,

respectivamente. Biologia Computacional tem se referido a atividades concentradas na

construção de algoritmos para tratar problemas com relevância biológica, ao estudo de

sistemas moleculares naturais e artificiais, bem como aos novos paradigmas de

computação baseada em DNA. Bioinformática tradicionalmente tem se referido às

atividades concentradas na construção e no uso de ferramentas computacionais para a

32

Page 35: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

análise de dados biológicos disponíveis, geralmente provenientes dos diversos projetos

de genomas, a fim de elucidar processos biológicos complexos.

Entretanto, é importante salientar que essa distinção serve principalmente para realçar a

área de concentração principal do estudo e, que, na prática, os dois termos são usados

indistintamente. Mas o que mais se popularizou foi Bioinformática, que hoje é

empregado indistintamente para ambas as designações, pois ele tem sido usado para

designar tanto o que tradicionalmente se conhece por Bioinformática quanto o que

historicamente era conhecido como Biologia Computacional. O uso do termo

Bioinformática, no seu sentido mais amplo, parece cada vez mais convergir para a

designação de uma agregação de tecnologias.

De um ponto de vista computacional as atividades inerentes à Bioinformática vão desde

a construção de ferramentas para problemas biológicos específicos, utilizando teoria

algorítmica, ao trabalho experimental, onde um laboratório com tubos de testes e

microscópios são substituídos por um computador, com grande capacidade

computacional e de armazenamento, e um disco rígido cheio de ferramentas

computacionais projetadas para analisar grandes quantidades de dados biológicos para

provar ou descartar uma dada hipótese. Dentre os desafios que a Bioinformática tenta

dar respostas, atualmente, podem ser arrolados os seguintes questões ou problemas:

1) Filogenia – Trata-se da construção de árvores filogenéticas baseadas em

observações nos organismos existentes para modelar a evolução das espécies. Os

organismos que têm sido estudados até o momento apresentam similaridades

suficientemente fortes nos mecanismos moleculares, que induz a suposição da

existência de um ancestral comum para todos os organismos na Terra. Duas

seqüências que compartilham um ancestral comum são ditas homólogas. Essas

árvores podem servir como guias na comparação (alinhamento) de seqüências,

permitindo medidas de similaridades biologicamente mais significativas.

2) Sequenciamento (sequencing) – Trata do uso de técnicas laboratoriais e de

técnicas computacionais para a aquisição da estrutura dita primária de uma dada

seqüência, geralmente de DNA.

33

Page 36: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

3) Determinação da função e da estrutura tridimensional da proteína – Trata-se do

uso de métodos e técnicas computacionais para determinar a função e a estrutura

tridimensional das proteínas. Até o momento, não é possível deduzir a forma

tridimensional de uma dada proteína somente a partir de sua cadeia de

aminoácidos. A estrutura tridimensional fornece muito mais informações sobre a

função da proteína que o simples encadeamento linear de aminoácidos (estrutura

primária das proteínas). Parece ser necessário levar em conta tanto a estrutura

secundária, terciária e quaternária da proteína, quanto a sua filogenia.

4) Comparação e alinhamento de seqüências – Trata-se de técnicas e métodos de

análise de seqüências por analogia, em que os princípios gerais são tirados a

partir da comparação das seqüências entre si. Estes métodos, que são baseados

nos conceitos de homologia e similaridade, permitem:

• Estimar a diferença ou similaridade entre duas seqüências;

• Construir um alinhamento para mostrar graficamente as diferenças e

similaridades das seqüências, arranjando lado-a-lado em filas os

elementos concordantes, os discordantes e as lacunas;

• Verificar o grau de similaridade de uma dada seqüência a um certo

conjunto de seqüências;

• Criar um modelo representativo de um dado conjunto de seqüências

(família, domínio, etc.);

• Determinar a função de uma proteína (ou de uma outra dada estrutura, ou

de uma família de proteínas) a partir da similaridade de suas estruturas

químicas e/ou físicas;

• Buscar num banco de dados por seqüências pertencentes a uma dada

família ou domínio, ou buscar por seqüências que sejam similares a um

dado conjunto de seqüências.

34

Page 37: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Os organismos vivos evoluem através de gerações por mutação e seleção natural.

Dizemos que duas estruturas (proteínas ou ácidos nucléicos), aqui representadas por sua

seqüência de resíduos, são homólogas se elas descendem de um ancestral comum. Desta

forma a evolução de seqüências biológicas acumula adições, remoções e substituições

de resíduos. Então a avaliação da similaridade entre duas seqüências biológicas inicia-se

pela determinação de um alinhamento plausível entre elas e passa pela avaliação de sua

significância (ou relevância) biológica.

A abordagem de programação dinâmica (PD) é atualmente o método mais popular para

comparação e alinhamento rigoroso de seqüências de ácidos nucléicos e proteínas. Esse

método fornece o resultado da comparação e encontra um alinhamento entre duas

seqüências com o mais alto escore possível para vários esquemas de atribuição de

escores úteis, incluindo funções afins de atribuição de escores com lacunas (affine gap

scores). Praticamente todos os métodos de comparação de seqüências biológicas

encontram o melhor alinhamento entre duas strings sob um determinado esquema de

atribuição de escores. A relevância (ou significância) biológica de um alinhamento está

intimamente relacionada ao esquema de atribuição de escores adotado. Além da

utilização da PD em métodos de comparação e/ou alinhamento de seqüências

biológicas, a abordagem da PD tem sido utilizada em um grande número de outros

problemas computacionais em Bioinformática.

A abordagem de PD constitui o corpo principal de praticamente todos os algoritmos e

métodos utilizados para a comparação de seqüências biológicas. Os algoritmos que

empregam a abordagem de PD são usados em Bioinformática para alinhamento de pares

de seqüências ou de múltiplas seqüências entre si (perfil), para alinhamento de uma ou

múltiplas seqüências a um perfil-HMM ou para treinamento de um perfil-HMM. Esses

algoritmos podem ser utilizados, com diversos critérios de otimização (funções de

custo), para encontrar um alinhamento ótimo (de custo mínimo ou de score máximo) ou

calcular outras medidas de interesse. Em biologia molecular as funções de custo típicas

incluem, em ordem crescente de complexidade:

1) custos simples, onde cada custo de mutação é uma constante;

35

Page 38: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

2) custos lineares de lacunas (gaps) e custos lineares de lacunas afins, onde

inclusões e remoções são orçadas através de uma função linear;

3) custos lineares por partes de lacunas e custos côncavos de lacunas.

A qualidade de um algoritmo é, tradicionalmente, medida através de metodologia

algorítmica padrão em termos de recursos que esse requer para resolver o problema,

principalmente tempo e espaço. Entretanto, tratando-se de algoritmos para a resolução

de problemas com relevância biológica, a qualidade destes deve ser avaliada através da

combinação de seu tempo de execução, de seu requerimento de espaço e da relevância

biológica das respostas que esse produz. Desta forma a construção de bons algoritmos

em Bioinformática envolve um intercâmbio entre a modelagem biológica e a construção

do algoritmo até que um balanço razoável entre os requerimentos de espaço, de tempo e

de relevância biológica seja alcançado.

Os HMMs são modelos estocásticos sofisticados e flexíveis utilizados como uma

ferramenta para o estudo e análise de sinais. Esses modelos muitas vezes são referidos

na literatura como fontes de informações Markovianas (sources Markov) ou como

funções probabilísticas de cadeias de Markov. Os HMMs fornecem uma estrutura geral

para a análise estatística de uma grande variedade de modelos de sinais. Dentre as

diversas aplicações dos HMMs destacam-se: reconhecimento ou processamento de fala,

veja, por exemplo, Rabiner (1989); processamento de sinais; processamento de imagens

e visão por computador (computer vision); reconhecimento de texto e manuscritos;

acústica (reconhecimento de ruídos e sons), veja, por exemplo, Couvreur et al. (1998);

climatologia (padrões atmosféricos e precipitações); controle; comunicações;

reconhecimento de sinais de ECG (eletrocardiograma), veja, por exemplo, Koski

(1996); discriminação entre surtos de corrente e falha interna de corrente em

transformadores de potência, veja, por exemplo, Ma e Shi (2000); biologia

computacional – comparação, reconhecimento, discriminação e busca na modelagem de

seqüências nucléicas (DNAs ou RNAs) e proteínas (genoma), veja, por exemplo,

Hughey e Krogh (1996), Kehagias (1996), Karplus et al. (1998), Koski (1999) e Cuesta

36

Page 39: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

et al. (2003). Para uma boa lista bibliográfica abrangendo essas diversas aplicações veja

Cappé (2001).

Os HMMs têm um grande potencial para modelagem de seqüências de nucleotídeos ou

de aminoácidos em biologia molecular. A idéia básica é construir um modelo que

descreva um conjunto de seqüências e, então, usá-lo para comparar uma seqüência

contra o modelo, para encontrar seqüências relacionadas, ou para examinar o modelo e

determinar propriedades das seqüências. Ou seja, responder a questões como ‘Quais

seqüências num banco de dados se ajustam a um modelo?’ ou ‘Quão bem elas

ajustam?’ ou ‘Qual de um conjunto de modelos melhor descreve um conjunto de

seqüências?' (Durbin et al., 1998).

A modelagem utilizando os HMMs apresenta vantagens significativas:

• Proporcionam a construção de um modelo probabilístico explícito para um

determinado conjunto de seqüências biológicas.

• Os parâmetros são específicos para cada posição no modelo, enquanto outros

modelos de comparação de seqüências usam estatísticas gerais para a

probabilidade de ocorrência dos símbolos.

Entretanto a modelagem utilizando os HMMs apresenta algumas desvantagens:

• O número de parâmetros do modelo depende do tamanho das seqüências

pertencentes ao conjunto que está sendo modelado. Como as seqüências

biológicas freqüentemente chegam a dezenas de milhares de resíduos, o número

de parâmetros do HMM pode tornar-se suficientemente grande para que o

processo de estimação destes parâmetros se torne impraticável na maioria dos

sistemas computacionais uni-processados (PCs, WorkStations) que se encontram

ativos nos laboratórios de pesquisa.

• A modelagem de um problema complexo (difícil) de otimização usando HMMs,

freqüêntemente, produz um modelo muito complexo.

37

Page 40: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Os algoritmos de PD são utilizados na solução dos três problemas básicos na

modelagem usando HMMs:

• Determinação da probabilidade de ocorrência de uma certa seqüência de

observações O dado o modelo λ, P(O/λ);

• Determinação da seqüência de estados Q que melhor elucida a seqüência de

observações O;

• Treinamento do modelo, que nada mais é do que o processo iterativo de

determinação dos parâmetros do modelo para que a P(O/λ) seja maximizada.

Esse trabalho foi organizado da seguinte forma. Nas demais seções deste capítulo são

apresentadas: a formalização do problema, os objetivos e hipóteses e uma justificativa

deste estudo. No capítulo 2 são apresentados as definições e os conceitos básicos da

teoria dos HMMs, dos HMMs lineares – mais precisamente os perfis-HMMs que têm

sido usados na análise de seqüências biológicas –, da teoria de análise de complexidade

de algoritmos e dos algoritmos de programação dinâmica. No capítulo 3 é feita uma

revisão da literatura sobre algoritmos de PD com economia de espaço, seguida de uma

conceituação básica dos algoritmos de PD que fazem uso do paradigma de checkpoints.

No capítulo 4 apresenta-se os algoritmos alternativos propostos nesse trabalho. No

capítulo 5 apresenta-se a metodologia de experimentação, os resultados da

experimentação e faz-se a análise destes resultados. No capítulo 6 apresenta-se as

conclusões e comentários.

1.2 O Problema

O sujeito deste trabalho é constituído dos HMMs (lineares ou perfis) utilizados no

estudo de seqüências biológicas no campo da Bioinformática. O objeto deste trabalho é

constituído do estudo de algoritmos de programação dinâmica, sob a ótica da

complexidade de espaço e de tempo, que possam ser utilizados nos HMMs para o

cálculo de P(O/λ), para decifrar (ou revelar) o caminho de estados Q que melhor elucida

38

Page 41: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

a produção da seqüência de observações O pelo modelo λ e para o treinamento do

modelo λ.

A complexidade de espaço e de tempo dos algoritmos de PD são, respectivamente, de

ordem O(n2) bytes e O(n3) operações, considerando-se que as strings e o HMM são de

comprimentos similares, digamos ~n, ou tomando-se o comprimento maior. Com

relação à complexidade de tempo, uma outra interpretação usada por Grice et al. (1997),

Tarnas e Hughey (1998) e Wheeler e Hughey (2000), considera um tempo unitário para

efetuar os cálculos de uma célula de PD. Sob essa ótica a complexidade de tempo seria

de ordem O(n2) unidades de tempo. Nesse trabalho foi adotada essa ótica alternativa

para expressar a complexidade de tempo, principalmente para facilitar as comparações

com os resultados obtidos em Tarnas e Hughey (1998).

Uma seqüência biológica típica pode conter de centenas a dezenas de milhares de

símbolos de um dado alfabeto – com pelo menos 4 símbolos para seqüências de DNAs

(ou RNAs) e 20 símbolos para seqüências de aminoácidos. Os algoritmos padrões de

programação dinâmica utilizados para computar medidas de interesse, requerem muito

espaço de armazenamento (memória de trabalho ou RAM) e gastam muito tempo de

execução (tempo de CPU e/ou wall time), limitando, portanto, o comprimento máximo

de seqüências biológicas que podem ser analisadas nas máquinas seriais

comercializadas atualmente (PCs e Workstations).

O espaço requerido para armazenar a matriz completa de PD pode exceder a capacidade

física da máquina, mesmo desconsiderando os requerimentos de espaço para o Sistema

Operacional e o próprio programa que executa o algoritmo de PD. Desta forma, o tempo

de execução pode ser agravado pela ineficiência no uso do sistema de memória. A

execução destes algoritmos de PD em máquinas seriais pode até mesmo se tornar

impraticável para seqüências não muito longas (na casa de dezenas de milhares).

Existem técnicas usualmente referidas como métodos de espaço linear, que alinham um

par de seqüências, ou uma seqüência a um HMM, usando memória limitada. Alguns

destes algoritmos fazem uso do princípio dividir e conquistar (D&C) e requerem espaço

39

Page 42: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

da ordem de ( nmO )+ ao invés de ( )nmO × , ao preço de dobrar o tempo de execução

(Hirschberg, 1975). Há outros, que são denominados algoritmos com -níveis de

checkpoints e fazem uso de uma técnica que armazena linhas, colunas ou diagonais da

matriz de PD (checkpoints) para que a computação de seções da matriz de PD possa ser

refeita sem a necessidade de re-iniciar a computação desde a primeira iteração, que, em

geral, requerem espaço da ordem de

L

( )m×nLO , ao preço de aumentar o tempo de

execução por um fator de para alinhar um par de seqüências (Grice et al., 1997),

(Tarnas e Hughey, 1998) e (Wheeler e Hughey, 2000). Existem outros, como o

algoritmo de Powell, que recorrem a esses dois princípios (Powell et al., 1999).

L

No processo de treinamento de HMMs lineares (ou perfis) é necessário recuperar toda a

matriz de PD, o que descarta o uso de técnicas ou estratégias tradicionais que empregam

o princípio D&C, bem como o uso da estratégia adotada em Powell et al. (1999) para a

redução do espaço de armazenamento nos algoritmos de PD.

Os algoritmos que adotam a técnica ou estratégia de checkpoints possibilitam economia

de espaço (memória) e podem ser usados para a redução da complexidade de espaço nos

HMMs lineares (ou perfis). Apesar do potencial de uso destes algoritmos em HMMs

lineares (ou perfis), esses não têm sido, ainda, completamente investigados.

1.3 Objetivos e Hipóteses

Esse trabalho tem o propósito de fazer um estudo dos algoritmos de PD que recorrem à

estratégia de checkpoints como técnica de economia de espaço, que formam o núcleo do

treinamento e da computação das medidas de interesse nos HMMs e, que são utilizados

em máquinas seriais. Pretende-se analisar as soluções para o problema da complexidade

de espaço levando-se em consideração o impacto destas soluções no desempenho destes

algoritmos.

Tem-se como objetivos a proposição de algoritmos novos através da exploração de

alterações nesses algoritmos de PD com checkpoints que iguale ou reduza os

requerimentos de espaço e melhore o desempenho em relação ao estado da arte destes

algoritmos, ou pelo menos não provoque aumentos consideráveis nos requerimentos de

40

Page 43: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

tempo de execução destes algoritmos. Mais precisamente, a proposição de algoritmos

novos baseados na exploração da hipótese de utilização da estratégia de checkpoints em

conjunto com o princípio D&C como solução consistente para o problema de

complexidade de espaço versus complexidade de tempo dos algoritmos de PD utilizados

no cálculo de medidas de interesse em HMMs lineares (ou perfis).

Essa hipótese da utilização da estratégia (ou paradigma) de checkpoints em conjunto

com o princípio de D&C nos algoritmos de PD já foi explorada, ainda que de forma não

explícita, utilizando-se checkpoints por diagonais com retrocedimento restrito. Mas

parece ser mais promissora utilizando-se explicitamente o principio D&C em conjunto

com o paradigma de checkpoints em duas dimensões da matriz de PD, por linhas e por

colunas, simultaneamente.

1.4 Justificativa

Os melhoramentos introduzidos no algoritmo de PD básico proporcionam

requerimentos de memória da ordem de O(n) ou ( )nn ×O , que em termos práticos

apresentam pouca ou nenhuma queda no desempenho quando comparados ao algoritmo

de PD padrão. Apesar destes melhoramentos, os requerimentos de espaço em conjunto

com os requerimentos de tempo continuam sendo um gargalo na avaliação de

seqüências de aminoácidos ou de nucleotídeos em biologia molecular, pois as

seqüências, freqüentemente, podem atingir a casa dos milhões e a memória disponível é

sempre limitada.

É bom lembrar que os recursos de memória são sempre limitados e constituem um

gargalo nos sistemas computacionais. O desenvolvimento tecnológico e a conseqüente

redução de custos e aumento da capacidade dos recursos computacionais, possibilitam a

superação de limitações com relação a solução computacional de instâncias de

determinados problemas, mas, por outro lado, possibilitam o tratamento computacional

de outros problemas, novas fronteiras, que estavam fora do alcance do poder

computacional imediatamente anterior, possivelmente tornando esses novos recursos

computacionais novamente limitados. Adicionalmente, deve ser considerado, também,

41

Page 44: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

que nas máquinas atuais o acesso: à memória cache requer 1 ciclo de clock, à memória

principal de 10 a 20 ciclos, e ao disco rígido cerca de um milhão de ciclos (Tarnas e

Hughey, 1998). Desta forma execuções com requerimentos de memória maiores do que

o tamanho da memória principal, podem tornar o custo de paginação da memória virtual

muito alto, tornando a computação, efetivamente, inviável.

Esses fatos por si só já são bastante significativos para realçar a necessidade de

investimentos em pesquisas para melhorar o desempenho destes algoritmos ou buscar

outros algoritmos alternativos. Desta forma, o estudo se concentra, a princípio, nos

algoritmos que adotam a estratégia de checkpoints com requerimentos reduzidos de

memória.

Os algoritmos alternativos baseados na estratégia D&C e no algoritmo rápido de

Ukkonen apresentados em Powell et al. (1999), que requerem espaço linear sem causar

queda de desempenho nos algoritmos de PD, podem ser acelerados aumentando os

recursos de memória. Entretanto eles somente são apropriados para determinar melhor o

alinhamento entre seqüências, que requer apenas o retrocedimento parcial sobre a matriz

de PD, mas não são apropriados para utilização no treinamento de HMMs ou qualquer

uso em conjunto com o algoritmo forward-backward requerendo todos os caminhos,

que exigem procedimentos de retrocedimento completo sobre a matriz de PD. Isto acaba

se tornando uma grande desvantagem quando o interesse reside justamente em

desenvolver um pacote de ferramentas de modelagem de HMMs, pois não poderíamos

ter um algoritmo base que fosse o núcleo de todo o processamento de PD.

Deve-se considerar, também, que as funções de custo freqüentemente usadas nos

HMMs em biologia molecular não geram matrizes de PD com diagonais não-

decrescentes, pois essas funções usam atribuição de escores em log-probabilidades (log-

odds scoring), impedindo a utilização de algoritmos que exploram essa característica

para acelerar o desempenho computacional. Muitas vezes, ao invés de se usar os escores

em log-probabilidades (log-odds score) relativos ao modelo aleatório, o logaritmo da

probabilidade da seqüência dado o modelo é usado diretamente, que é denominado

42

Page 45: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

escore LL (de log likelihood score), entretanto o LL score é fortemente dependente do

tamanho das seqüências, apresentando uma dependência não linear.

O método de PD com checkpoints apresenta alguns méritos: a sua forte independência

do algoritmo subjacente; a redução do requerimento de espaço da ordem de O para (nm)

( )L nmO ; e a possibilidade de se aumentar o desempenho em troca de um aumento na

constante de espaço. Além disso eles são a única alternativa de economia de espaço

apropriada para o treinamento de HMMs ou para qualquer outro uso em conjunto com o

algoritmo forward-backward requerendo todos os caminhos.

Dessa família de algoritmos de PD com checkpoints, aquele por diagonais com o

processo de retrocedimento restrito, que alia a técnica (ou paradigma) de checkpoints

com o princípio D&C, é o que, segundo Tarnas e Hughey (1998), apresentou melhor

desempenho no alinhamento de seqüências. Entretanto foi observado, com base nos

experimentos realizados e na experiência anterior com esses algoritmos, que a

complexidade das condições de contorno, adicionadas quando se processa diagonais,

não proporcionou um retorno equivalente no ganho de performance. Desta forma, com

base nos resultados obtidos eles planejaram reimplementar o loop interno do pacote

SAM (Hughey, 2003) com checkpoints por linhas. É importante salientar que a

complexidade das condições de contorno foi agravada pelo uso da técnica de

retrocedimento restrito. Com base nestas conclusões o grupo de pesquisadores

responsáveis pelo pacote SAM buscou alternativas para melhorar o desempenho destes

algoritmos com checkpoints por linhas.

Wheeler e Hughey (2000) apresentaram um algoritmo melhorado de (1,2)-níveis de

checkpoints por linhas que, assintoticamente, efetua até 50% menos cálculos quando

comparado ao original com checkpoints por linhas. Entretanto na prática os resultados

mostraram que o algoritmo melhorado, na avaliação de todos os caminhos, é 3 a 12%

mais rápido que o de 2-níveis original, independente do tamanho do HMM. É

importante lembrar que o algoritmo de PD de 2-níveis original, com checkpoints por

linhas, tem requerimentos de memória da ordem de ( )nmO , ao custo de uma queda de

desempenho por um fator de 2 com relação ao algoritmo de PD padrão. Nesse ponto

43

Page 46: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

deve ser salientado que esses melhoramentos relatados por Wheeler e Hughey (2000)

podem ser utilizados, quando for conveniente, juntamente com outros melhoramentos

nos diversos algoritmos membros desta família de algoritmos com checkpoints.

Deve-se salientar também que, talvez, o mais importante a ser considerado nessas

técnicas de uso de espaço de armazenamento restrito seja o comportamento assintótico

da redução dos requerimentos de memória, pois elas são, a princípio, úteis justamente

para seqüências longas, ou seja, quando os requerimentos de memória crescem com o

tamanho destas seqüências. Desta forma, a redução dos requerimentos de memória da

ordem de O(mn) para ( )nmO e para O(m) possuem um comportamentos assintóticos

bastante semelhante, para m e n suficientemente grandes.

Considerando-se, portanto, o tradeoff entre a redução dos requerimentos de espaço e a

performance dos algoritmos de PD de L-níveis com checkpoints e o comportamento

assintótico com relação à complexidade de tempo, da ordem de O(mn), do algoritmo de

PD de 2-níveis com checkpoints por colunas ou linhas, quando determinando um

alinhamento entre duas seqüências ou calculando o melhor caminho simples de estados

através de um HMM, verifica-se que ainda há necessidade de investimento em pesquisa

para explorar alternativas que melhorem o desempenho destes algoritmos.

Portanto, ainda existe necessidade de se explorar alterações nessa família de algoritmos

de PD que usam checkpoints e reavaliação, que tiram vantagem do uso de espaço linear

através da armazenagem de checkpoints, mas ao custo de uma queda no desempenho,

devido à reavaliação, quando comparados com o algoritmo PD tradicional. É necessário

explorar novos algoritmos que mantenham a complexidade de espaço linear e cause um

impacto menor na queda de desempenho.

Esse trabalho contribui para o campo de Bioinformática investigando e propondo novos

algoritmos alternativos que utilizam a estratégia (ou paradigma) de checkpoints em

conjunto com o princípio D&C como solução para o problema de complexidade de

espaço dos algoritmos de PD usados nos HMMs, levando-se em consideração o

44

Page 47: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

aumento dos requerimentos de tempo para execução, provocado pela necessidade de

reavaliação de partes da matriz de PD.

Até onde se tem conhecimento não existe nenhuma investigação relatada na literatura

pertinente, que trate da hipótese levantada nesse trabalho.

45

Page 48: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

46

Page 49: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

CAPÍTULO 2

CONCEITOS BÁSICOS

2.1 Introdução

Nesse capítulo é feita uma revisão da teoria básica de complexidade de algoritmos e da

classe de HMMs. Essa revisão é focada principalmente nos tópicos de interesse deste

trabalho, deixando de abordar aqueles que não são de interesse direto para a pesquisa

em foco. Na segunda seção são apresentados os conceitos fundamentais dos HMMs e as

definições de interesse para a Bioinformática. Na terceira seção são apresentados alguns

conceitos fundamentais de complexidade de algoritmos. Na quarta seção são

apresentados conceitos e descrições dos algoritmos de programação dinâmica.

2.2 Modelos Markovianos Ocultos (HMMs)

Os conceitos e definições apresentados nessa seção estão fundamentados principalmente

nos tutoriais de Rabiner (1989) e de Grate et al. (1999), no manual de Durbin et al.

(1998), no curso de Koski (1999), nos manuais de usuário dos pacotes HMMER (Eddy,

2003) e SAM (Hughey et al, 2003) e nos artigos de Krogh et al (1993), de Haussler et al

(1993), de Hughey e Krogh (1996), de Barret et al. (1997), de Eddy (1998), de Birney

(2001) e de Sato et al. (2003). Na seção 2.2.1 é apresentada uma definição formal para

os HMMs. Na seção 2.2.2 são abordados os três problemas fundamentais dos HMMs e

as soluções apresentadas para esses problemas. Na seção 2.2.3 é esboçada a classe de

HMMs lineares que têm sido estudados em biologia molecular

2.2.1 Definições

Os HMMs estendem o conceito de modelos markovianos para incluir o caso onde a

observação (situação observável) é uma função probabilística do estado. Um HMM

refere-se a um processo estocástico cujas transições entre estados obedecem a uma

cadeia de Markov não observável (daí o nome escondido ou oculto) e que a cada estado

visitado emite (ou produz) um símbolo observável (uma observação) conforme uma

distribuição de probabilidades (estado discreto) ou uma função densidade (estado

47

Page 50: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

contínuo). Os HMMs consistem de cinco blocos (ou elementos) básicos que são

definidos formalmente, como:

1) Um conjunto finito de N estados, designado por S = S1, S2, ..., SN, no qual

define-se um processo estocástico qt que indica o estado do modelo no instante t

. ( )L,3,2,1,0=t

2) Um alfabeto discreto e finito correspondente a K símbolos distintos de

observações (ou de emissões), designado por V = v1, v2, ..., vK. Os símbolos

de observações correspondem à saída física do sistema que está sendo modelado.

3) Uma distribuição de probabilidades de transição de estados denotada por A =

aij, onde aij = P [qt+1 = Sj | qt = Si], 1 i, j N.

4) Uma distribuição de probabilidade de observação (ou emissão) do símbolo vk

quando o processo atinge o estado j, designada por E = ej(k) , onde ej(k)

= P [ Ot = vk | qt = Si ], 1 j N e 1 k K.

5) Uma distribuição de probabilidade para o estado inicial π = πi , onde πi

= P [q1 = Si] e 1 ≤i ≤ N.

Desta forma uma especificação completa de um HMM requer a especificação dos

parâmetros N e K, a especificação dos símbolos de observação (ou emissão) e a

especificação das três distribuições de probabilidade: A, E e π. Mas, por conveniência

usa-se uma notação compacta designada por λ = (A, E, π) para indicar o conjunto de

parâmetros do modelo. Um exemplo de um HMM discreto com 3 estados e 5 símbolos

de observação é mostrado na Figura 2.1.

48

Page 51: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

FIGURA 2.1 - HMM discreto com três estados e 5 símbolos de observação.

2.2.2 Os Três Problemas Básicos dos HMMs

Segundo Rabiner (1989), para que os HMMs sejam úteis em aplicações do mundo real é

necessário apresentar soluções para os três problemas básicos de interesse formulados a

seguir.

“Problema 1 : Dada uma seqüência de observações O = O1 O2 … OT, e um modelo λ =

(A, E, π), como podemos computar eficientemente P(O|λ), isto é, a probabilidade de

ocorrência da seqüência de observações O, dado o modelo λ?“ .

“Problema 2 : Dado uma seqüência de observações O = O1 O2 … OT, e um modelo λ =

(A, E, π), como podemos escolher uma seqüência de estados Q = q1 q2 … qT que seja

ótima segundo algum critério significativo, isto é, que melhor elucida as observações?”.

“Problema 3 : Como podemos ajustar os parâmetros do modelo λ = (A, E, π) para que

seja maximizada a P(O|λ)?“.

2.2.2.1 Soluções para o Problema 1

Uma dada seqüência de observações O pode ser gerada por muitos caminhos diferentes

através dos estados de um HMM; ou seja, por muitas seqüências distintas e possíveis de

estados S do modelo λ. Portanto a probabilidade de ocorrência de uma certa seqüência

de observações O é a soma das probabilidades sobre todos os caminhos distintos e

possíveis de estados através do modelo λ, ou seja:

49

Page 52: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( ) ( ) ( ) ( )

( ) ( )∏∑

∑∑

=−

=

===

T

ttqqq

allQqq

allQallQ

OeaOe

QPQOPQOPOP

ttt2

1 111

|,||,|

π

λλλλ

(2.1)

Para se fazer uma análise de pior caso do esforço computacional requerido para

computar a equação (2.1), é considerado um HMM onde qualquer estado pode ser

alcançado dele mesmo e de qualquer outro estado. Pode ser observado com certa

facilidade, que são requeridas (2T -1)N T multiplicações e N T -1 adições, sendo,

portanto, requeridas (2T -1)N T + N T -1 = (2T)N T -1 operações. Isto significa que o

número de operações cresce exponencialmente quando o tamanho da seqüência cresce,

de onde se conclui que o cálculo direto de P(O|λ) através da equação (2.1) é

computacionalmente inviável, exceto para valores muito pequenos de N e T.

No entanto, o cálculo de P(O|λ) definido na equação (2.1) pode ser realizado de forma

muito mais rápida usando um algoritmo de programação dinâmica como o forward ou o

backward, que requer a realização de 2 operações.

Desde que T , pode-se considerar sem perda de generalidade que são requeridas

( 122 222 −≤++− TNNNTNTN )N≥3223 22 TTT ≤− operações, ou seja, uma complexidade de tempo dominada por uma

equação polinomial de grau 3.

Nesse sentido, define-se a variável forward ft(i) como a probabilidade de um certo

prefixo até um determinado estado, dado o modelo λ, ou seja, ft(i) = P(O1…Ot, qt=Si|λ).

A variável forward ft(i) pode ser resolvida recursivamente da seguinte forma:

algoritmo forward

1) Inicialização: f ( ) ( ) NiOei ii ≤≤= 1 ,11 π .

2) Recursão: f . ( ) ( ) ( ) NjT-t aifOejN

iijttjt ≤≤≤≤= ∑

=++ 1 ,11 ,

111

3) Encerramento: P . ( ) (∑=

=N

iT ifO

1| λ )

50

Page 53: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Como tanto o algoritmo forward como o algoritmo backward serão requeridos na

solução dos problemas 2 e 3, define-se, a seguir, o algoritmo backward que

alternativamente pode ser utilizado para a avaliação de P(O|λ). Nesse sentido, seja a

variável backward bt(i) definida como a probabilidade de um certo sufixo até um

determinado estado, dado o modelo λ, ou seja bt(i) = P(Ot+1 Ot+2 … OT|qt=Si, λ). A

variável backward bt(i) pode ser resolvida recursivamente da seguinte forma:

algoritmo backward

1) Inicialização: b ( ) NiiT ≤≤= 1 ,1 .

2) Recursão: b ( ) ( ) ( ) NiT-T- tjbOeaiN

jttjijt ≤≤== ∑

=++ 1 ,1,,2,1 ,

111 K

3) Encerramento: P ( ) ( ) (∑=

=N

iii OeibO

111| πλ )

)

2.2.2.2 Soluções para o Problema 2

Se a seqüência de estados Q, que melhor elucida uma determinada seqüência de

observações O, é conhecida, então a probabilidade conjunta de O e Q pode ser

facilmente computada, dado o modelo λ, simplesmente avaliando a seguinte expressão.

( ) ( ) (∏=

−=

T

ttqqqqq OeaOeQOP

ttt2

1 111|, πλ (2.2)

Entretanto, o caminho de estados Q freqüentemente não é conhecido e, desta forma, a

equação (2.2) não pode ser utilizada.

Embora não seja possível dizer em qual estado o sistema modelado por um HMM

esteja, simplesmente olhando para o símbolo observado, muitas vezes o interesse reside

justamente na seqüência de estados subjacentes que melhor elucida uma dada seqüência

de observações.

51

Page 54: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O problema de encontrar uma seqüência de estados ‘ótima’ associada com uma dada

seqüência de observações é freqüentemente referido na literatura como decifragem ou

decodificação. Existem várias vias possíveis para resolver o problema de decifragem, a

dificuldade reside justamente no significado de seqüência de estados ótima, pois

existem vários critérios de otimalidade.

Na verdade, em situações práticas, normalmente, recorre-se a um critério de otimalidade

para resolver esse problema de uma maneira mais adequada à aplicação, pois existem

vários critérios de otimalidade razoáveis que podem ser impostos. Desta forma, a

escolha de um critério é fortemente influenciada pela aplicação, ou seja, pelo uso

pretendido para a seqüência de estados em questão.

Embora outros critérios de otimalidade sejam razoáveis para algumas aplicações, os

dois critérios de otimalidade mais populares na decifragem de HMMs são:

1) maximizar o número esperado de estados individuais ‘ótimos’, escolhendo o

mais provável estado a cada época t;

2) encontrar a melhor seqüência, simples, existente de estados, ou seja, encontrar o

caminho mais provável.

Para implementar a solução baseada no primeiro critério, o de maximizar o número

esperado de estados individuais ótimos, define-se a variável γt(i) como a probabilidade a

posteriori do estado Si no instante t, dado a seqüência de observações O e o modelo λ:

( ) ( )λγ ,| OSqPi itt == (2.3)

A equação (2.3) pode ser expressa em termos das variáveis forward-backward, ou seja,

por ft(i) = P(O1 O2 … Ot, qt=Si|λ) e bt(i) = P(Ot+1 Ot+2 … OT|qt = Si, λ).

( ) ( ) ( )( )

( ) ( )( ) ( )∑

=

== N

jtt

ttttt

jbjf

ibifOP

ibifi

1

| λγ

(2.4)

52

Page 55: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Pode ser observado de (2.4) que o denominador P(O|λ) é um fator de normalização, que

faz com que γt(i) seja considerada uma medida de probabilidade, ou seja, ∑ . ( ) 11

=−

N

it iγ

Desta forma, γt(i) pode ser usado para avaliar individualmente o estado qt mais provável

a cada instante t, através da equação

( )[ ] Ttiq tNi

t ≤≤=≤≤

1 ,maxarg1

γ . (2.5)

Podem existir problemas com a seqüência de estados resultante da avaliação da equação

(2.5), pois essa é determinada a cada instante sem considerar a probabilidade de

ocorrência da seqüência de estados, ou seja, se o HMM em consideração tem

probabilidade de transição aij = 0 para algum i e j, então a seqüência de estados ‘ótima’

pode não ser uma seqüência de estados válida.

Segundo Rabiner (1989), uma segunda, e talvez mais importante, abordagem de

decifragem baseada nesse critério surge quando estamos interessados em alguma

propriedade derivada da seqüência de estados e não na própria seqüência de estado

‘ótima’. Pode-se estar interessado na probabilidade a posteriori de um certo símbolo vk

vir de um determinado subconjunto de estados. Assumindo-se uma função g(Si) definida

nos estados S, pode-se obter as probabilidades à posteriori de um certo símbolo vk vir de

um determinado subconjunto destes estados como

( ) ( ) (∑=

==N

iiitk SgOSqPOvG

1,|| λ ) . (2.6)

Um caso especial, importante, desta abordagem é quando g(Si) assume o valor 1 para

um subconjunto de estados e 0 para os demais estados. Nesse caso G(vk|O), equação

(2.6), é a probabilidade a posteriori do símbolo vk vir de um estado no conjunto

especificado.

Para implementar a solução baseada no segundo critério, o de encontrar a melhor

seqüência simples de estados, ou seja, encontrar o caminho mais provável, deve-se

53

Page 56: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

maximizar P(Q|O,λ) ou, de forma equivalente, maximizar P(Q,O|λ). Desta forma tem-

se:

( )λ|,maxarg* OQPQQ

= . (2.7)

Para encontrar esse caminho Q* = q1* q2* ... qN*, com a mais alta probabilidade, para

uma certa seqüência de observações O, dado o modelo λ, recorre-se a uma técnica

formal baseada nos métodos de programação dinâmica, denominada algoritmo de

Viterbi. Nesse sentido define-se uma quantidade vt(i) que é a mais alta pontuação (mais

alta probabilidade) ao longo de um caminho até o instante t, referente às t primeiras

observações, terminando no estado Si.

( ) ( )λ|,max 2121121

ttqqqt OOOiqqqPivt

LLL

==−

(2.8)

Como o interesse reside, justamente, no melhor caminho através dos estados, então é

necessário um vetor ψt(j) para armazenar, para cada t e j, o argumento que maximiza

(2.8).

algoritmo de Viterbi.

1) Inicialização: ( ) ( )( )

=

≤≤=0

1 ,

1

11

iNiOeiv ii

ψπ

2) Recursão: ( ) ( )[ ] ( )( ) ( )[ ]

≤≤≤≤=

=

−≤≤

−≤≤ TtNjaivjOeaivjv

ijtNi

t

tjijtNit2 ,1, maxarg

,max

11

11ψ

3) Encerramento: ( )[ ]

( )[ ]

=

=

≤≤

≤≤

ivq

ivP

TNi

T

TNi

1

*1

*

maxarg

max

4) Recuperação do caminho: ( ) 1,,2,1 ,*11

* K−−== ++ TTtqq ttt ψ

54

Page 57: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

2.2.2.3 Soluções para o Problema 3

A resolução do problema 3 consiste em determinar um método de ajuste (estimativa)

dos parâmetros do modelo λ = (A, E, π) que maximize a probabilidade da seqüência de

observações O para cada seqüência do conjunto de treinamento. Dado um conjunto de

seqüências (amostras) que se deseje modelar, usualmente referido como seqüências de

treinamento, para as quais pretende-se que o modelo ajuste bem, o processo de

treinamento consiste em estimar: a distribuição das observações para cada estado; todas

as probabilidades de transição entre estados; e as probabilidades iniciais para cada

estado.

Se os caminhos de estados são conhecidos para todas as seqüências de treinamento, os

parâmetros do modelo podem ser estimados computando-se seus valores esperados,

contando-se o número de vezes que cada transição (Aij) ou emissão (Ei(k)) ocorre no

conjunto de observações de treinamento e dividindo pela soma de todas as transições

ou de todas as emissões. Desta forma, as estimativas de máxima verossimilhança (ML)

para aij e ej(k) são dados por

( )( )∑∑

==

li

ii

lil

ijij

lEkEe

AA

a e . (2.9)

Quando os caminhos são desconhecidos não existe uma equação fechada para estimar

diretamente os valores dos parâmetros, de forma que, encontrar o melhor modelo λ

constitui um problema de otimização, ou seja, escolhe-se λ = (A, E, π) tal que a P(O|λ)

das seqüências do conjunto de treinamento sejam localmente maximizadas. Qualquer

um dos algoritmos padrões para otimização de funções contínuas pode ser usado,

entretanto, um método iterativo que tem uma interpretação probabilística natural, o

algoritmo de Baum-Welch (Durbin et al., 1998) é usado para escolher os parâmetros do

modelo λ.

Para descrever o procedimento de Baum-Welch para re-estimação (atualização e

melhoramento iterativos) dos parâmetros do HMM é necessário definir as fórmulas para

55

Page 58: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

a re-estimação de A, E e π. Essas fórmulas são dadas através das equações em (2.9) e

(2.10), sendo que as equações em (2.10) são definidas em termos das variáveis forward-

backward, a seguir, onde L é o número de seqüências de treinamento.

( ) ( )( )

( ) ( ) ( ) ( )

( ) ( ) ( ) ( ) NjjbjfOP

kE

NjijbOeaifOP

A

NiOP

ibifT

T

vOt

lt

lt

L

llj

T

t

lt

ltjij

lt

L

llij

L

ll

ll

i

kt

≤≤=

≤≤=

≤≤=

∑∑

∑∑

===

=++

=

=

1 ,|

1

,1 ,|

1

1 ,|

1

11

111

1

1

11

λ

λ

λπ

(2.10)

algoritmo de Baum-Welch

1) Inicialização: Leitura das seqüências de treinamento e adoção de uma estimativa

inicial para os parâmetros λ, geralmente tomando valores aleatórios para A, E e

π.

2) Re-estimação: O modelo corrente λ = (A, E, π) é usado para estimar, através das

equações em (2.9) e (2.10), os novos parâmetros do modelo ( )πλ ,, EA= .

3) Encerramento: O critério de parada, previamente definido, é verificado.

• Se foi atingido então faz-se λλ = e pára.

• Se não foi atingido faz-se λλ = e repete os passos 2 e 3.

O critério de parada é definido com base nas asserções dadas a seguir.

• “ou o modelo corrente λ define um ponto crítico da função de verossimilhança e,

nesse caso λλ = ;

• ou o modelo λ é mais provável que o modelo λ, no sentido que

( ) ( )λλ || OPOP > , ou seja, foi encontrado um novo modelo λ do qual as

56

Page 59: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

seqüências de observações são mais prováveis de terem sido produzidas”

(Rabiner, 1989).

É importante salientar que o resultado final deste procedimento de re-estimação é uma

estimativa de máxima verossimilhança (MLE) do HMM, que o algoritmo forward-

backward, que é utilizado nas fórmulas em (2.10), somente leva a máximos locais e, que

a maioria dos problemas de interesse possuem superfície de otimização muito

complexa, com muitos máximos locais.

Uma abordagem alternativa largamente usada é denominada treinamento Viterbi. Nessa

abordagem os mais prováveis caminhos para as seqüências de treinamento são

derivados utilizando o algoritmo de Viterbi, e esses caminhos são usados no processo de

re-estimação dos parâmetros através das fórmulas em (2.9), para o caso de caminhos

conhecidos. O processo é iterado novamente quando novos valores de parâmetros são

obtidos. Nesse caso o algoritmo converge, precisamente, porque a atribuição de

caminhos é um processo discreto, e pode-se iterar até que nenhum dos caminhos mude

mais. Mas esse processo não maximiza a verdadeira verossimilhança, P(O|λ),

considerada como uma função dos parâmetros do modelo λ, ao contrário, ele encontra o

valor de λ que maximiza a contribuição dos mais prováveis caminhos, para todas as

seqüências de treinamento, para a verossimilhança. Talvez, por esse motivo a sua

performance não seja tão boa quanto a do algoritmo Baum-Welch, contudo é uma boa

opção quando o uso principal do HMM é produzir decifragens através de alinhamentos

Viterbi (Durbin et.al., 1998).

2.2.3 Modelo de Atribuição de Escores e Problemas Numéricos

A comparação de seqüências biológicas tem o propósito de evidenciar se elas têm

divergido de um ancestral comum por um processo de mutação e seleção natural. Nesse

processo a seleção natural tem o papel de filtragem das mutações, de forma que algumas

classes (tipos ou espécies) de mudanças possam ser vistas mais que outras. Os processos

mutacionais básicos freqüentemente considerados são substituições, adições e remoções

de resíduos numa dada seqüência. Inserções e remoções são referidas conjuntamente

como lacunas (gaps).

57

Page 60: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

A suposição de que mutações ocorram independentemente em diferentes locais numa

seqüência, que trata uma lacuna de tamanho arbitrário como uma simples mutação,

parece ser uma aproximação razoável para seqüências de proteínas e de DNAs, apesar

de ser conhecido que as iterações entre resíduos têm papel decisivo na determinação

estrutural de DNAs e de proteínas. Contudo, essa suposição parece não ser apropriada

para RNAs, pois sabe-se que o pareamento de bases introduz dependências de longo

alcance nas estruturas de RNAs e, que muitos RNAs conservam mais as interações de

pareamento de base que produzem a sua estrutura secundária, do que a sua seqüência

primária de bases. Mas é bom lembrar que, apesar de ser possível levar em conta essas

dependências, isto ocasionaria complexidades computacionais significativas. Nos

alinhamentos de seqüências biológicas espera-se que identidades e substituições

conservadoras sejam observadas mais freqüentemente que mudanças aleatórias e, que

substituições não conservadoras sejam menos freqüentes que mudanças aleatórias.

Desta forma identidades e substituições conservadoras contribuem com termos de

escores positivos e as substituições não conservadoras contribuem com termos de

escores negativos.

Essa suposição de independência permite a construção e utilização de um sistema

aditivo de atribuição de escores, o que reduz bastante a complexidade computacional da

comparação de seqüências. Num tal esquema aditivo de atribuição de escores, o escore

total atribuído a um alinhamento é a soma dos termos para cada par alinhado de

resíduos mais os termos para cada lacuna (gap).

Além de encontrar um alinhamento ótimo é necessário avaliar a significância (ou

relevância) de seu escore, avaliar se o alinhamento é biologicamente significante

(relevante), fornecendo evidência para uma homologia, ou se é simplesmente o melhor

alinhamento entre duas seqüências não relacionadas. Para efetuar essa avaliação existem

basicamente duas abordagens, uma abordagem de estilo Bayesiano, baseada na

comparação de modelos diferentes e uma abordagem estatística tradicional baseada no

cálculo da probabilidade de um escore de concordância (match score) ser maior que o

valor observado, assumindo um determinado modelo nulo – o das seqüências

subjacentes não serem relacionadas. Para maiores detalhes veja Durbin et.al. (1998).

58

Page 61: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Nesse trabalho foi adotada a abordagem de tempero Bayesiano, pois o que se quer é a

probabilidade das seqüências serem relacionadas, dado o modelo alternativo M - o

modelo match - em oposição à probabilidade de não serem relacionadas, dado o

modelo aleatório R. O modelo aleatório R modela a probabilidade das seqüências

subjacentes serem não relacionadas. O modelo alternativo M, o modelo match modela a

probabilidade das seqüências em comparação serem relacionadas ou de uma seqüência

ser relacionada ao modelo M. A razão entre a probabilidade das seqüências serem

relacionadas e a probabilidade de não serem relacionadas é denominada taxa de

verossimilhança ou de disparidade (odds ratio). Para chegar a um sistema de atribuição

de escores positivos toma-se o logaritmo desta taxa, que é conhecido como taxa log-

odds. Para maiores detalhes veja Durbin et.al. (1998).

Na modelagem utilizando perfis-HMMs é construído um modelo probabilístico

explícito para um determinado conjunto de seqüências biológicas, onde os parâmetros

são específicos para cada posição no modelo e cada posição corresponde a uma coluna

num alinhamento múltiplo, logo para cada posição no modelo tem-se uma matriz de

probabilidades de emissão de símbolos associada. A solução adotada é a transformação

destas probabilidades de emissão de símbolos, dado o modelo alternativo M, o modelo

match, em escores log-odds relativos ao modelo aleatório R.

Na utilização destes algoritmos de PD em perfis-HMMs surgem preocupações com

relação à existência de problemas numéricos, pois mesmo nos mais modernos

processadores com suporte a operações de ponto flutuante poderia haver muitos

problemas numéricos quando computando os produtórios (multiplicando muitas

probabilidades) nos algoritmos de Viterbi, forward e backward, para modelos de

tamanhos realistas em muitas aplicações. Isto acontece porque os cálculos de ft(i), bt(i)

e vt(i) envolvem múltiplos termos menores do que 1 e maiores do que zero e, portanto,

ft(i), bt(i) e vt(i) tendem exponencialmente para zero, quando t torna-se muito grande.

Por exemplo, na modelagem de seqüências genômicas, para DNA, pode-se ter 100.000

bases ou mais. Supondo que as probabilidades de transição e emissão sejam tipicamente

da ordem de 0,1 (um décimo), a probabilidade do caminho Viterbi poderia ser da ordem

de 10 -100.000. Normalmente se recorre a duas diferentes formas de tratar esse problema.

59

Page 62: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Uma abordagem seria recorrer à transformação logarítmica de todas as probabilidades,

ou seja, no lugar das probabilidades sempre seria usado o logaritmo das probabilidades

e os produtórios seriam transformados desta maneira em somatórios. O uso de

transformações logarítmicas é mais freqüente e conveniente no cálculo das medidas de

desempenho através de um perfil-HMM já treinado. Essa transformação é,

freqüentemente, usada em conjunto com um sistema de atribuição de escores

denominado log-odds scoring.

Outra abordagem seria o scaling de probabilidades. Nesse caso, as variáveis de Viterbi,

forward e backward seriam re-escaladas (rescaled), de tal forma que elas permaneçam

dentro de um intervalo numérico gerenciável. Uma escolha conveniente seria definir

uma variável de scaling para fazer o somatório de todas as variáveis (Viterbi ou forward

ou backward) escaladas (scaled), num dado instante, igual a 1. Para maiores detalhes

ver (Rabiner, 1989) e (Durbin et.al., 1998). Essa abordagem é conveniente para ser

usada com o algoritmo de Baum-Whelch no treinamento de perfis-HMMs.

2.2.4 Perfis-HMMs para Seqüências Biológicas

Os HMMs podem assumir diversas topologias, sendo que na forma topológica mais

geral são permitidas transições de um estado para qualquer um dos outros estados,

assumindo uma topologia com estados totalmente interconectados.

Na modelagem de HMMs, é possível iniciar o treinamento do HMM a partir desta

topologia mais geral e deixar que convirja para um modelo ótimo, mas geralmente essa

estratégia tem se mostrado infrutífera devido à convergência para mínimos locais e ao

número excessivo de parâmetros a serem considerados.

Na prática HMMs, bem sucedidos, são construídos com base no conhecimento sobre o

problema sendo investigado, decidindo cuidadosamente quais e quantos estados o

modelo deve ter e quais transições serão permitidas entre esses estados. Portanto a

modelagem de HMMs é mais uma arte do que uma técnica e está fortemente ligada ao

problema, ou seja, às propriedades do sinal que está sendo modelado. Desta forma, será

60

Page 63: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

tratada, nos parágrafos seguintes, da modelagem de seqüências biológicas através de

HMMs.

De maneira informal pode-se dizer que as proteínas são construídas de um alfabeto de

20 pequenas moléculas conhecidas como aminoácidos. Dentro de uma proteína a

ligação entre dois aminoácidos é conhecida como uma ligação peptídica. Uma

seqüência linear de aminoácidos numa cadeia polipeptídica é conhecida como a

estrutura primária da proteína, podendo ser formadas 20n cadeias polipeptídicas, com n

aminoácidos. Entretanto, dentro destas cadeias polipeptídicas certas seções podem ser

torcidas em forma de espirais e/ou entrelaçadas em forma de chapas, laços e hélices.

Essas formas são conhecidas como a estrutura secundária das proteínas e são formadas

por pontes de hidrogênio entre dois aminoácidos não adjacentes na cadeia polipeptídica.

Embora a cadeia linear de aminoácidos seja uma visão simplificada da estrutura de uma

molécula de proteína, relações entre proteínas podem ser inferidas somente comparando

suas estruturas primárias. É sob está ótica, e desconsiderando qualquer dependência

entre os aminoácidos da cadeia, que foram propostos os HMMs lineares para

modelagem de seqüências biológicas. Entretanto, as dependências entre os aminoácidos

dentro de uma proteína tem motivado pesquisas em outras classes de modelos

estatísticos que possam capturá-las, tais como modelos híbridos de redes neurais e

HMMs, redes Bayesianas dinâmicas, HMMs fatoriais, árvores de Boltzmann e campos

aleatórios markovianos ocultos (Durbin et al., 1998).

Os HMMs comumente utilizados para a modelagem de seqüências biológicas são do

tipo com transições somente da esquerda para a direita (left-right) e são referidos, no

campo da Bioinformática, como HMMs lineares e/ou perfis-HMMs, devido ao arranjo

dos estados em forma linear. Esse arranjo linear dos estados de um perfil-HMM foi

motivado por uma técnica ordinária de modelagem biológica denominada “perfil”, que

alinha seqüências a uma seqüência de consensu. Desta maneira um HMM linear para

uma seqüência de aminoácidos (proteínas) ou de nucleotídeos (RNAs e DNAs) é um

conjunto de posições que correspondem aproximadamente a colunas num alinhamento

de múltiplas seqüências.

61

Page 64: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Nos perfis-HMMs utilizados na modelagem de seqüências biológicas existem três tipos

de estados: match (de concordância), insert (de inserção ou de adição) e delete (de

remoção). Os estados denominados match correspondem à letras da seqüência de

consensu. Os estados denominados insert correspondem à posições entre as letras da

seqüência de consensu onde lacunas (gaps) podem ser abertas e letras inseridas. Os

estados denominados delete permitem saltar uma coluna sem emitir (gastar) nenhuma

letra. A topologia básica do modelo é mostrada na Figura 2.2. Os estados denominados

match, insert e delete são representados por retângulos, trapézios e círculos,

respectivamente, e estão agrupados em módulos (blocos ou nós) correspondendo a uma

posição na seqüência de consensu. A cada linha conectando dois estados existe uma

probabilidade de transição associada. É importante ressaltar a existência de dois

módulos especiais, um modelando o início e outro o final do modelo. Outras topologias

podem ser consideradas, mas por razões de eficiência a topologia mostrada na Figura

2.2 normalmente é utilizada. O tamanho do modelo refere-se ao número de nós no

HMM (com exceção dos módulos especiais) ou, equivalentemente, ao número de

estados denominados match no HMM. Esse modelo apresenta transições entre os

estados insert e delete, pois apesar delas, usualmente, serem improváveis, a ausência

delas pode trazer problemas na fase de construção do modelo (treinamento).

FIGURA 2.2 - Topologia de um perfil-HMM com 4 módulos padrões. FONTE: Hughey e Krogh (1996).

Os perfis-HMMs são ferramentas poderosas e confiáveis para a modelagem de uma

família de seqüências biológicas não-alinhadas de aminoácidos ou de nucleotídeos, ou

para modelar domínios ou fragmentos especiais (motifs) comuns a um conjunto de

seqüências biológicas não-alinhadas. O perfil-HMM resultante da modelagem (treinado)

62

Page 65: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

pode, então, ser usado para comparação de uma seqüência ao HMM, para busca ou para

alinhamento múltiplo. A seguir são dados versões log-odds dos algoritmos forward e

Viterbi desenhados especialmente para perfis-HMMs.

Nessas versões log-odds dos algoritmos forward e Viterbi desenhados especialmente

para perfis-HMMs, usualmente, assume-se que a distribuição das probabilidades de

emissão dos estados Ij é a mesma distribuição do modelo nulo usado com escores em

log-odds. Nesse caso, essas probabilidades se anulam produzindo um escore nulo:

01log)(

log ==t

j

O

tI

q

Oe.

As versões log-odds dos algoritmos forward e Viterbi apresentadas nessa seção são

baseadas numa arquitetura básica para perfis-HMMs, conforme a Figura 2.2,

apresentam transições entre os estados insert e os estados delete, mas elas podem ser

suprimidas se não estão presentes nos modelos construídos, por serem bastante

improváveis.

Seja o escore em log-odds do melhor caminho concordando (matching) a

subseqüência O

)(tV Mj

1O2O3...Oi ao sub-modelo até o bloco j, terminando com Oi sendo

emitido pelo estado Mj. Similarmente, seja V o escore do melhor caminho

terminando com O

)(tIj

i sendo emitido pelo estado Ij. Seja, de forma análoga, V o escore

para o melhor caminho terminando no estado D

)(tDj

j. O algoritmo de Viterbi para perfis-

HMMs usando log-odds é dado a seguir.

algoritmo de Viterbi para perfis-HMMs usando log-odds

1) Inicialização:

( ) 000 =MV

2) Recursão:

63

Page 66: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

+

+

+

=

+−

+−

+−

+=

+−

+−

+−

+=

log)(log)(log)(

max)(

log)1(log)1(log)1(

max)(

log)(

log)1(log)1(log)1(

max)(

log)(

1

1

1

1

1

1

1

1

1

1

1

1

jj

jj

jj

jj

jj

jj

t

j

jj

jj

jj

t

j

DDDj

DIIj

DMMj

Dj

IDDj

IIIj

IMMj

O

tIIj

MDDj

MIIj

MMMj

O

tMMj

atVatVatV

tV

atVatVatV

qOe

tV

atVatVatV

qOe

tV

3) Encerramento:

+++

=+

+

+

+

+ log)(log)(log)(

max)1(

1

1

1

1

NN

NN

NN

MDD

N

MII

N

MMM

NM

N

aTVaTVaTV

TV

De forma análoga às equações de recorrência para o algoritmo de Viterbi com escores

em log-odds, sejam as variáveis para os escores parciais das taxas

totais em log-odds correspondendo a V . O algoritmo forward para

perfis-HMMs usando log-odds é dado a seguir. Embora pareça complicado, a operação

)( e )(),( tFtFtF Ij

Mj

Dj

e )(),( tVt Mj

Dj )(tV I

j

( )yx ee +log pode ser efetuada eficientemente, numa implementação prática, para uma

precisão adequada (aceitável) através de interpolação e de uma tabela de consulta

(lookup).

algoritmo forward para perfis-HMMs usando log-odds

1) Inicialização:

( ) 000 =MF

2) Recursão:

64

Page 67: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( )( ) ( )( ) ( )( )

( )( )

( )( ) ( )( ) ( )( )

( )( )( ) ( )( ) ( )( ) ( )( )[ ]

++=

−+

+−+−+=

−+

+−+−+=

−−−

−−

−−−

−−

tFatFatFatF

tFa

tFatFa

q

OetF

tFa

tFatFa

q

OetF

DjDD

IjDI

MjDM

Dj

DjID

IjII

MjIM

O

tIIj

DjMD

IjMI

MjMM

O

tMMj

jjjjjj

jj

jjjj

t

j

jj

jjjj

t

j

111

1

11

expexpexplog

1exp

1exp1exploglog

1exp

1exp1exploglog

111

1

11

3) Encerramento:

( ) ( )( ) ( )( ) ( )( )[ ]TFaTFaTFaTF DNMD

INMI

MNMM

MN NNNNNN

expexpexplog11111 +++

++=++

O processo de modelagem usando perfis-HMMs é essencialmente a construção de um

modelo que represente a seqüência de consensu para uma determinada família (ou

conjunto) de seqüências, pois não há nenhum interesse em construir um modelo para

representar qualquer seqüência em particular. Assim a estrutura apresentada na Figura

2.2 é usada como um arcabouço inicial no treinamento de perfis-HMMs, determinando-

se, então, as probabilidades de transição e de emissão de símbolos de tal forma que elas

capturem informação específica sobre cada posição num múltiplo alinhamento da

família ou conjunto de seqüências.

Desta forma, o processo de treinamento de um HMM consiste tipicamente em, dado um

modelo inicial, fazer várias computações completas da matriz de PD em cada seqüência

do conjunto de treinamento e, então, re-estimar os parâmetros do modelo. Esse processo

é repetido várias vezes até que o modelo convirja para uma representação estatística da

família de seqüências representativa da amostra ou conjunto de treinamento. O

treinamento de perfis-HMMs de seqüências não-alinhadas é feito com o algoritmo de

Baum-Welch (forward-backward), usando todos os caminhos de estados, ou

alternativamente com o algoritmo de Viterbi, usando os caminhos de estados mais

prováveis. A seguir são dados os algoritmos forward e backward usados para o

treinamento de perfis-HMMs e as equações para re-estimação dos parâmetros dos

65

Page 68: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

perfis-HMMs. O algoritmo de Viterbi é praticamente semelhante ao algoritmo forward,

só que esse maximiza a cada passo ao invés de somar sobre todos os caminhos.

algoritmo forward usado no treinamento de perfis-HMMs

1) Inicialização:

( ) 100

=Mf

2) Recursão:

( ) ( ) ( ) ( ) ( )[ ]( ) ( ) ( ) ( ) ( )[ ]( ) ( ) ( ) ( )

++=

−+−+−=

−+−+−=

−−−−−−

−−−−−−

jjjjjjjjjj

jjjjjjjjjjj

jjjjjjjjjjj

DDDDIIDMMD

IDDIIIIMMtII

MDDMIIMMMtMM

atfatfatftf

atfatfatfOetf

atfatfatfOetf

111111

111111

111

111

3) Encerramento:

( ) ( ) ( ) ( )1111

1++++

++=+NNNNNNNNNN MDDMIIMMMM aTfaTfaTfTf

algoritmo backward usado no treinamento de perfis-HMMs

1) Inicialização:

( )( )

( )( )

=

=

=

=+

+

+

+

+

1

1

1

111

NNN

NNN

NNN

N

MDD

MII

MMM

M

aTb

aTb

aTb

Tb

2) Recursão:

( ) ( ) ( ) ( ) ( ) ( )( ) ( ) ( ) ( ) ( ) ( )( ) ( ) ( ) ( ) ( ) ( )

++++=

++++=

++++=

+++++

+++++

+++++

++

++

++

11111

11111

11111

11

11

11

11

11

11

jjjjjjjjjjjj

jjjjjjjjjjjj

jjjjjjjjjjjj

DDDtIIDItMMDMD

DIDtIIIItMMIMI

DMDtIIMItMMMMM

atbOeatbOeatbtb

atbOeatbOeatbtb

atbOeatbOeatbtb

equações de re-estimação usadas no treinamento Baum-Welch para perfis-HMMs

66

Page 69: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( )∑∑

∑∑

∑∑

∑∑

∑∑

==

=+

=

=+

=

===

===

+++

++++

=

+=

+=

=

=

T

t

lDDX

lX

L

llDX

T

t

lI

ltIIX

lX

L

llIX

T

t

lM

ltMMX

lX

L

llMX

T

vOt

lI

lI

L

llI

T

vOt

lM

lM

L

llM

tbatfOP

A

tbOeatfOP

A

tbOeatfOP

A

tbtfOP

kE

tbtfOP

kE

jjjjjj

jjjjjjj

jjjjjjj

kt

jjj

kt

jjj

11

11

1

11

1

11

11

111

1111

|1

1|

1

1|

1

|1

|1

λ

λ

λ

λ

λ

(2.11)

Uma vantagem significativa de se usar HMMs na modelagem de seqüências biológicas

deve-se a eles poderem ser automaticamente treinados ou estimados de seqüências não-

alinhadas. Entretanto em aplicações práticas na modelagem de seqüências biológicas

surgem vários problemas que precisam ser contornados. Esses problemas são

enumerados a seguir.

O algoritmo de Baum-Welch utilizado no treinamento é uma técnica de otimização hill-

climbing (subir encosta) que freqüentemente encontra soluções sub-ótimas, resultando

em modelos inferiores. As heurísticas utilizadas para contornar esse problema

aumentam o tempo de computação, que pode ser considerável se for levado em conta

que seqüências biológicas podem conter de centenas a milhões de letras.

É necessário recorrer a uma heurística para determinar o tamanho do modelo, pois o que

se quer é modelar a estrutura destas seqüências, que freqüentemente não estão

alinhadas. Essa heurística consiste de um processo iterativo de cirurgia (surgery) e re-

estimação do modelo, que se repete até a sua estabilização. Isto acresce carga

computacional ao treinamento do HMM.

Freqüentemente os dados de treinamento não constituem uma amostra representativa da

família de seqüências que está sendo modelada, ou por existirem poucos dados ou por

estes dados serem viciados - por conterem um grande número de seqüências homólogas

muito próximas e/ou de duplicatas exatas de seqüências. Esse não é somente um

67

Page 70: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

problema teórico onde essa classe de dados distorcidos conflita com a suposição de

independência entre os dados, pois estes dados viciados podem restringir as capacidades

do modelo treinado. O modelo resultante pode ter pouca capacidade de generalização -

com desempenho muito pobre nas atividades de busca e discriminação em bancos de

dados - e/ou pode ter dificuldade de distinguir posições mais conservadas de posições

menos conservadas.

2.3 Medidas de Complexidade de Algoritmos

Essa seção trata de algoritmos e do desempenho destes; das medidas de eficiência e de

complexidade. Na seção 2.3.1 apresenta-se uma definição de algoritmo. Na seção 2.3.2

são tratadas a análise de desempenho de algoritmos e a complexidade de problemas.

2.3.1 Definição de Algoritmo

O termo algoritmo é proveniente da palavra árabe alkharizmi, que por sua vez é

originária do nome do matemático persa do século IX Abu Ja’far Mohammed ibn Musa

al Khowarizmi (Knuth, 1973).

Kowalski (1979) entende que algoritmos convencionais são compostos por dois

elementos. O primeiro é o elemento lógico ou modelo, que consiste do conhecimento

sobre o problema em estudo. O segundo é o elemento responsável pelo controle, que

consiste da metodologia de aplicação do conhecimento para resolver o problema.

Um algoritmo pode ser visto como uma receita matemática para resolver um dado

problema ou uma determinada classe de problemas. Essa receita deve ser precisa,

correta e detalhada como uma construção teórica no campo da matemática. Um

algoritmo descreve um padrão de comportamento, expresso através de um conjunto

finito de ações ou instruções, que podem ser seguidas (realizadas, efetuadas,

executadas) para se obter a solução de uma determinada classe de problemas.

Segundo Toscani e Veloso (2001) algoritmos são o cerne da computação: um programa

codifica um algoritmo de modo a ser executatado em um computador, resolvendo assim

um problema. Um algoritmo é um procedimento, consistindo de um conjunto de regras

68

Page 71: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

não ambíguas, as quais especificam, para cada entrada, uma seqüência finita de

operações, terminando com uma saída correspondente. Na verdade, um algoritmo

resolve um problema quando, para qualquer entrada, produz uma resposta correta, se

forem concedidos tempo e memória suficientes para sua execução. Entretanto, o fato de

um algoritmo resolver (teoricamente) um problema não significa que seja aceitável na

prática. Os recursos de espaço e de tempo requeridos têm grande importância em casos

práticos, ou seja, na prática é fundamental que um programa produza a solução com

dispêndio de tempo e de memória razoáveis, o que ressalta a importância da análise de

complexidade, dos métodos de projeto e da análise de algoritmos.

Para Resende e Stolfi (1994) o grau de detalhe e formalismo de um dado algoritmo

depende do “receptor” almejado. Se o receptor é um programador, um engenheiro ou

um cientista usa-se uma linguagem semi-formal, semelhantes àquelas usadas em

construções lógicas ou matemáticas, e omite-se, geralmente, todos os detalhes que

podem ser facilmente supridos pelo receptor em questão. Caso o receptor seja um

computador, todos os detalhes devem ser especificados através de uma representação

(notação) que possa ser interpretada e seguida mecanicamente pelo computador. Os

algoritmos que podem ser avaliados por computadores são, freqüentemente,

denominados programas.

Segundo Resende e Stolfi (1994) um algoritmo deve conter os seguintes elementos:

1) Enunciado – o enunciado descreve os dados a serem fornecidos (entrada) ao

algoritmo, os resultados calculados (saída) por esse e a relação entre dados e

resultados.

2) Modelo Computacional – o modelo computacional consiste na definição das

operações “elementares” permitidas e do custo de cada uma delas. O modelo

computacional permite computar a eficiência (ou ineficiência) de cada solução.

Essas operações “elementares” são operações que o executor do algoritmo

supostamente saiba executá-las; no caso do computador são aquelas que podem

ser efetuadas com um número fixo de instruções básicas. Para mais detalhes

sobre modelos computacionais veja Leeuwen e Wiedermann (2000).

69

Page 72: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

3) Seqüência de operações – é uma especificação da seqüência de operações que

devem ser efetuadas. Essa seqüência de operações é dependente dos dados de

entrada, que podem variar infinitamente. Desta maneira a notação (os comandos

e as estruturas de operação) adotada precisa descrever de maneira finita esta

infinidade de seqüências.

4) Prova de correção – um algoritmo precisa satisfazer às seguintes condições, para

que possa ser usado com confiança.

• As operações previstas precisam ser sempre válidas e definidas,

quaisquer que sejam os dados fornecidos ao algoritmo.

• O algoritmo sempre termina após efetuar um número finito de operações

previstas.

• A saída do algoritmo precisa satisfazer as condições estabelecidas no

enunciado deste.

5) Análise de desempenho – é necessário efetuar uma análise de desempenho para

a comparação dos algoritmos diferentes, desenhados para resolver um dado

problema, o que permite fazer uma escolha apropriada do algoritmo para a

resolução do problema.

2.3.2 Análise de Desempenho de Algoritmos e Complexidade de Problemas

Algoritmos podem ser analisados ou comparados sob vários pontos de vista tais como:

flexibilidade, robustez, facilidade de uso, tratamento de dados incorretos, eficiência,

complexidade, custo. Entretanto, dentre esses pontos de vista, o do custo ou esforço

computacional do algoritmo tem sido, freqüentemente, adotado devido à facilidade de

quantificação e, portanto, de ser estudado cientificamente. Esse custo ou esforço

computacional do algoritmo, freqüentemente, é denominado de eficiência ou

complexidade do algoritmo.

70

Page 73: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Para Toscani e Veloso (2001) o crescente avanço tecnológico, permitindo a criação de

máquinas cada vez mais rápidas, pode, ingenuamente, parecer ofuscar a importância da

complexidade de tempo de um algoritmo. Um raciocínio semelhante pode ser feito com

relação à importância da complexidade de espaço. Entretanto, acontece exatamente o

inverso, as máquinas, tornando-se cada vez mais rápidas e mais poderosas em termos de

recursos de memória, passam a poder resolver problemas maiores, e é a complexida de

tempo e de espaço do algoritmo que determina o novo tamanho máximo de problema

resolvível. Para um algoritmo eficiente, qualquer melhoria, na velocidade de execução

das operações básicas e nos recursos de memória, é sentida, e o conjunto de problemas

resolvíveis por ele aumenta sensivelmente. Esse impacto é menor no caso de algoritmos

menos eficientes.

Em Kearney et al. (1986) a complexidade é entendida como uma medida dos recursos

gastos por um sistema em sua interação com uma peça de software para executar uma

dada tarefa. Se o sistema é um computador então complexidade é definida pelo tempo

de execução e pelos requerimentos de armazenagem (memória) necessários para

efetuar a computação. Geralmente, essas medidas são baseadas no código do programa,

suprimindo-se comentários e atributos estilísticos, e dependem do tamanho do

programa, da estrutura de controle ou da natureza do módulo de interfaces. Entretanto,

ele observa, que existem fatores externos ao programa (ou cógigo do programa) que

contribuem para a complexidade do programa (ou algoritmo).

A análise da eficiência ou complexidade de algoritmos pode ser feita através da

realização de estimativas a priori e/ou através da realização de testes a posteriori. Essas

duas metodologias de análise do esforço computacional são igualmente importantes e

não são necessariamente contrapostas, mas complementares. A análise a priori de

algoritmos é feita através de medidas comparativas teóricas de ordem de complexidade,

que são utilizadas para comparar algoritmos cujos esforços computacionais diferem por

ordens de magnitude. Já a análise a posteriori é utilizada para comparar algoritmos

semelhantes em conceito através de medidas empíricas, realizadas a posteriori, do

desempenho de versões dos algoritmos já implementadas e compiladas para uma

determinada arquitetura.

71

Page 74: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Segundo Toscani e Veloso (2001) a análise da complexidade de um algoritmo é

realizada, usualmente, de maneira muito particular, o que é compreensível, já que a

complexidade é uma medida que tem parâmetros bem particulares do algoritmo. Apesar

disso, alguns conceitos são gerais, no sentido de que só dependem da estrutura do

algoritmo. Existem vários critérios de avaliação de um algoritmo como: quantidade de

trabalho requerido, quantidade de espaço requerido, simplicidade, exatidão de resposta e

otimalidade. Várias também são as medidas de complexidade: por exemplo, tempo e

espaço requeridos por um algoritmo, relacionadas à velocidade e à quantidade de

memória, respectivamente.

Nesse trabalho a análise de desempenho de algoritmos é tratada do ponto de vista do

custo ou esforço computacional, ou seja, do uso de recursos requeridos por um

algoritmo. Recursos típicos de interesse primário incluem entre outros: memória, tempo

computacional, banda de comunicação e portas lógicas. Entretanto, segundo Resende e

Stolfi (1994), na maioria das vezes, os recursos mais importantes a serem analisados são

o tempo gasto na execução do algoritmo e/ou o espaço de armazenamento ocupado

pelas variáveis internas. É bom lembrar que, tipicamente, a análise do desempenho de

algoritmos e da complexidade de problemas é feita em função do tamanho da entrada do

problema.

Segundo Resende e Stolfi (1994) o tempo de execução, T, de um programa (algoritmo)

é afetado por vários fatores, tais como o modelo computacional, o computador real

usado, o algoritmo adotado, os detalhes da codificação do algoritmo, o compilador

(tradução para a linguagem de máquina do computador) e a entrada do algoritmo. Dessa

forma, o tempo exato de execução, T, geralmente não pode ser determinado a partir da

descrição semi-formal do algoritmo. Entretanto, pode-se obter uma estimativa do tempo

de execução T a partir da contagem do número de operações elementares, l, efetuadas

pelo mesmo. Considerando que cada operação elementar, l, pode ser traduzida num

número fixo de instruções de máquina, tem-se que essa estimativa é aproximadamente

proporcional ao tempo de execução T, pois existem constantes a1, b1, a2, b2 tais que

. Observa-se que devido ao fato destas constantes (a2211 blaTbla +≤≤+ 1, b1)

dependerem da máquina e do programador, não é muito relevante obter o número exato

72

Page 75: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

de operações l, nem considerar todas as operações elementares previstas no algoritmo,

basta considerar apenas aquelas mais relevantes para o cálculo do custo e a maneira

como o número de operações elementares l varia com o tamanho do problema. Assim,

segundo Resende e Stolfi (1994) o desempenho de um algoritmo é expresso através de

uma função cujo parâmetro é o tamanho da entrada deste.

Segundo Toscani e Veloso (2001) para medir a quantidade de trabalho realizada por um

algoritmo, é escolhida uma operação, chamada operação fundamental, e então é contado

o número de execuções dessa operação na execução do algoritmo. A operação escolhida

como fundamental deve ser tal que a contagem do número de vezes que ela é executada

expresse a quantidade de trabalho do algoritmo, dispensando outras medidas. Às vezes,

é necessário mais do que uma operação fundamental, inclusive com pesos diferentes.

Algumas conclusões sobre o espaço utilizado podem ser tiradas, examinando-se o

algoritmo. Um programa requer uma área para guardar suas instruções, suas constantes,

suas variáveis e os dados. Pode também utilizar uma área de trabalho para manipular os

dados e guardar informações para levar adiante a computação. Se os dados têm uma

representação natural, como uma matriz, são considerados para análise de espaço

somente o espaço extra, além do espaço utilizado para para guardar as instruções do

programa e os dados. Porém, se os dados podem ser representados de várias formas, por

exemplo como um grafo, então o espaço requerido para guardá-los também deve ser

levado em conta.

Denomina-se cota superior (da complexidade) de um problema a qualquer função que

expresse o esforço computacional (eficiência) de um algoritmo que o resolve, em

função do tamanho da entrada. Sendo que, freqüentemente, o objetivo de um projetista

de algoritmos é o de obter a menor cota superior possível (Resende e Stolfi, 1994).

Para Toscani e Veloso (2001) a complexidade também pode ser vista como uma

propriedade do problema, o que significa dar uma medida independente do tratamento

dado ao problema, independente do caminho percorrido na busca da solução, portanto

independente do algoritmo. A complexidade de um problema é a do melhor algoritmo

imaginável (conhecido ou não) que possa resovê-lo. Dessa forma, segundo Resende e

73

Page 76: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Stolfi (1994), denomina-se cota inferior (da complexidade) ou complexidade assintótica

de um problema a qualquer função que expresse o número mínimo de operações que

precisa ser realizado para qualquer solução do problema, em função do tamanho da

entrada. Sendo que, freqüentemente, o objetivo de um analista da complexidade de

problemas é o de obter a maior cota inferior possível.

A cota superior (da complexidade) de um problema ou complexidade assintótica de um

algoritmo pode ser obtida através da análise do pior caso do tempo de execução de um

dado algoritmo, que é o maior tempo de execução do algoritmo para qualquer entrada

de tamanho n. A análise da complexidade assintótica de um algoritmo, geralmente, é

reduzida à análise do pior caso do tempo de execução, devido às seguintes

considerações:

• O pior caso do tempo de execução de um algoritmo fornece uma estimativa

razoável do limite superior do tempo de execução para qualquer entrada, o que

propociona confiança ou garantia de que o algoritmo nunca vai gastar mais

tempo para a execução do que esse tempo de pior caso.

• Quase sempre é necessário fazer uma estimativa sobre o tempo de execução do

algoritmo e espera-se que este nunca tenha um desempenho pior.

• Para alguns algoritmos o pior caso de desempenho ocorre com muita freqüência.

Em determinados casos o interesse recai sobre a análise do caso médio (ou esperado) do

tempo de execução de um algoritmo, pois um algoritmo pode ter um comportamento

médio bastante moderado em termos de uso de recursos computacionais, apesar do

comportamento de pior caso ser bastante oneroso computacionalmente. Mas está análise

do caso médio, freqüentemente, é difícil de ser efetuada, pois nem sempre está claro o

que constitui uma entrada “média” para um dado problema ou qual a distribuição de

probabilidades das entradas.

74

Page 77: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

2.3.2.1 Análise Assintótica

Freqüentemente o comportamento do crescimento de uma dada função fica mascarado

por constantes multiplicativas e outros termos de menor grau. Adicionalmente uma

função pode apresentar um comportamento para entradas de tamanhos limitados, que

não corresponde à tendência de seu comportamento para entradas de tamanho crescente.

Desta forma, tanto a comparação da eficiência de algoritmos para resolver um dado

problema, quanto a comparação da complexidade de diferentes problemas, assim como

a determinação da complexidade de um dado problema requerem a comparação do

crescimento assintótico de funções, se faz necessário o estabelecimento de uma notação

mais formal e concisa.

Sejam f e g: N → N funções positivas. Se o limite do quociente )()( nfng quando n

tende ao infinito é definido, pode-se classificar f e g quanto a seus crescimentos

assintóticos, com base no valor deste limite (zero, positivo finito, infinito). Deve ser

observado que as funções g(n) e f(n) são destituídas de constantes multiplicativas bem

como de termos de menor grau.

Notação big-O: Diz-se que um algoritmo tem ordem de complexidade

se existem constantes, c e n))(()( nfOng = 0, tais que 0 )()( nfcng ⋅≤≤ , para ,

ou seja, se

0nn ≥

∞<)()(

nn

∞→ fg

nlim . Essa definição diz que f(n) domina assintoticamente g(n), ou

seja, a taxa de crescimento de g(n) é menor que ou igual à taxa de crescimento de f(n).

Isto significa que f(n) é um limite superior para g(n).

Notação big-Ω: se existem duas constantes, c e n))(()( nfng Ω=

0n≥

0, tais que

, para n , ou seja, se 0)()( ≥⋅≥ nfcng 0)()(lim >

∞→ nfng

n. Essa definição assegura que

f(n) seja um limite inferior para g(n), ou seja, a taxa de crescimento de g(n) é maior que

ou igual à taxa de crescimento de f(n).

75

Page 78: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Notação big-Θ: se, e somente se, ))(()( nfng Θ= ))(()( nfOng = e , ou

seja, existem três constantes, c

))(()( nfng Ω=

0)(2 ≥⋅≥1, c2 e n0, tais que )()(1 ≥⋅ nfcngnfc , para

. Essa definição diz que a taxa de crescimento de g(n) é igual à taxa de

crescimento de f(n).

0nn ≥

Notação little-o: se ))(()( nfong = 0)()(lim =

∞→ nfng

n, ou seja, se e

. Para qualquer constante c > 0 existe uma constante n

))(()( nfOng =

))(()( nfng Θ≠

()(0 nfcng ⋅<≤

0 tal que

, para . Essa definição diz que a taxa de crescimento de g(n) é

menor que a taxa de crescimento de f(n), enquanto que a definição big-O permite que a

taxa de crescimento de g(n) seja igual à taxa de crescimento de f(n).

) 0nn ≥

Notação little-ω: ))(()( nfng ω= se ∞=∞→ )(

)(limnfng

n

)()(

, ou seja, se para qualquer constante

c > 0 existe uma constante n0 tal que 0≥⋅> nfcng , para n . Essa definição diz

que a taxa de crescimento de g(n) é maior que a taxa de crescimento de f(n), enquanto

que a definição big-Ω permite que a taxa de crescimento de g(n) seja igual à taxa de

crescimento de f(n).

0n≥

Para provar a ordem de complexidade de uma determinada função g(n) ou comparar a

ordem de complexidade de várias funções e para provar a complexidade de um

determinado problema ou comparar a complexidade de diversos problemas, usualmente,

não se aplica estas definições formalmente, mas uma coleção de resultados e regras

conhecidos, que são enumerados a seguir. Isto significa que a prova ou a determinação

de que a suposição seja correta, geralmente, é uma tarefa muito simples e não deveria

envolver Álgebra ou Cálculo.

Primeiro. Com base nas definições de complexidade assintótica pode ser observado

que:

• se e somente se ))(()( nfOng = ))(()( ngnf Ω=

76

Page 79: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

• se e somente se ))(()( nfng Ω= ))(()( ngOnf =

• se e somente se ))(()( nfong = ( ) ( )( )ngnf ω=

• ))(()( nfng ω= se e somente se ))(()( ngonf =

• se e somente se ))(()( nfng Θ= ))(()( ngnf Θ=

Segundo: Se h1(n) = O(f(n)) e h2(n) = O(g(n)), então

• h1(n) + h2(n) = max (O(f(n)), O(g(n))),

• h1(n) * h2(n) = O(f(n) * g(n)),

Terceiro: Se p(x) é um polinômio de grau n, então p(x) = . )( nxΘ

Quarto: logk n = O(n) para qualquer constante k. Isto implica que logaritmos crescem

muito lentamente.

Quinto: A Tabela 2.1 contém as principais funções dispostas por ordem crescente de

complexidade assintótica, big-O. O esboço gráfico da maioria delas é apresentado na

Figura 2.3.

TABELA 2.1 – Taxas de Crescimento Típicas.

Função Nome

c Constante

logn Logarítmica

log2n Logarítmica-quadrada

n Linear

n log n Linearítmica

n2 Quadrática

n3 Cúbica

2n Exponencial

77

Page 80: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

FIGURA 2.3 – Taxa de crescimento do tempo de computação de funções típicas.

2.4 Algoritmos de Programação Dinâmica

Essa seção trata do método PD que é muito utilizado em projetos de algoritmos. Faz a

conceituação e a descrição do método de PD e trata de sua aplicação na comparação de

seqüências biológicas através dos HMMs. A Seção 2.4.1 trata da conceituação e

descrição do método de PD. Seção 2.4.1 trata da aplicação desse método de PD aos

HMMs.

2.4.1 Estratégia de Programação Dinâmica

Segundo Toscani e Veloso (2001) a PD é um metodo iterativo, ascendente, que costuma

ser aplicado a determinados problemas de otimização. O método de PD opera,

decompondo um problema em subproblemas mínimos, solucionando os subproblemas,

guardando os resultados parciais, combinando subproblemas menores e sub-resultados

para obter e resolver problemas maiores, até recompor e resolver o problema original.

A PD é aplicável aos problemas de otimização com a seguinte propriedade de

otimalidade: “uma solução não ótima de um subproblema não pode ser subsolução de

solução ótima do problema”. Isto significa que só soluções ótimas dos subproblemas

podem compor uma solução ótima do problema. Em conseqüência, na procura de uma

78

Page 81: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

solução ótima, as soluções não ótimas de subproblemas podem ser descartadas, e é esse

o processo que torna os algoritmos desenvolvidos por PD mais eficientes que os

algoritmos diretos. Além dissso, os subproblemas são resolvidos uma única vez, e suas

soluções são guardadas para serem usadas tantas vezes quantas forem necessárias

(Toscani e Veloso, 2001).

A programação dinâmica é conveniente para resolver problemas de otimização com

uma estrutura iterativa de decisões seqüenciais que apresentem as seguintes

características:

1) Possuem subestruturas ótimas. Uma solução ótima para uma instância do

problema contém uma solução ótima para suas sub-instâncias;

2) Podem ser divididos em subproblemas, que correspondem a essas intâncias ou

sub-instâncias;

3) Os subproblemas são sobrepostos (dependentes). Permitindo que as mesmas

sub-instâncias sejam referidas repetidamente durante a recursão.

2.4.2 Métodos de Programação Dinâmica e HMMS

A abordagem algorítmica empregando PD em Bioinformática apresenta os seguintes

componentes:

1) Uma relação de recorrência para definir o valor de uma solução ótima para

determinados prefixos das seqüências, usando soluções prévias de prefixos

menores;

2) Uma computação tabular (em forma matricial) para computar o valor de uma

solução ótima;

3) Uma rotina ou procedimento de retrocedimento (traceback) sobre a matriz de

PD para emitir (ou recuperar) uma solução ótima.

79

Page 82: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

A comparação de seqüências biológicas usando PD é organizada comparando primeiro

subseqüências menores e armazenando os custos destas comparações numa tabela para

serem usados nas próximas comparações de subseqüências maiores, até que sejam

comparadas as seqüências inteiras. A entrada final da tabela armazena o resultado ou

estimativa da comparação global.

A abordagem de PD constitui o corpo principal de praticamente todos os algoritmos e

métodos utilizados para a comparação de seqüências biológicas. Dentre estes algoritmos

e modelos podem ser relacionados os seguintes:

• O algoritmo de Needleman-Wunsch - produz um alinhamento global ótimo entre

duas seqüências biológicas, permitindo lacunas (gaps). Esse algoritmo constrói

progressivamente um alinhamento ótimo usando soluções prévias de

alinhamentos ótimos de subseqüências menores (Needleeman e Wunsch, 1970) e

(Gotoh, 1982).

• O algoritmo de Smith-Waterman - produz um alinhamento local ótimo entre

duas seqüências biológicas. A versão original é devida a (Smith e Waterman,

1981) e a eficiente versão affine gap scores que é normalmente usada é devida a

Gotoh (1982).

• Os HMMs lineares e os Perfis-HMMs.

Os algoritmos que empregam a abordagem de PD são usados em Bioinformática para

alinhamento de pares de seqüências ou de múltiplas seqüências entre si (perfil), para

alinhamento de uma ou múltiplas seqüências a um perfil-HMM ou para treinamento de

um perfil-HMM. Esses algoritmos podem ser utilizados, com diversos critérios de

otimização (funções de custo), para encontrar um alinhamento ótimo (de custo mínimo

ou de score máximo) ou calcular outras medidas de interesse. Em biologia molecular as

funções de custo típicas incluem, em ordem crescente de complexidade:

1) custos simples, onde cada custo de mutação é uma constante;

80

Page 83: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

2) custos lineares de lacunas (gaps) e custos lineares de lacunas afins, onde

inclusões e remoções são orçadas através de uma função linear;

3) custos lineares por partes de lacunas e custos côncavos de lacunas.

Muitas vezes o algoritmo desenvolvido por métodos de PD reduz drasticamente a

complexidade, quando comparado com a complexidade do algoritmo direto, ou seja,

para um algoritmo direto de complexidade exponencial às vezes pode se construir um

algoritmo de PD com complexidade polinomial. Outras vezes o algoritmo de PD tem a

mesma complexidade do algoritmo direto mas com coeficientes de ordem mais baixos

(Toscani e Veloso, 2001).

Tomando-se como base a metodologia padrão de análise de desempenho de algoritmos,

tem-se que a complexidade de espaço e de tempo dos algoritmos de PD usados para a

computação de medidas de interesse nos HMMs são, respectivamente, de ordem O(n2)

bytes e O(n3) operações, considerando-se que as strings e o HMM são de comprimentos

similares, digamos ~n, ou tomando-se o comprimento da maior string. Mas,

alternativamente, pode-se considerar a um tempo unitário para efetuar os cálculos de

uma célula de PD e, então, sob essa ótica a complexidade de tempo seria de ordem

O(n2) unidades de tempo. Nesse trabalho foi adotada essa ótica alternativa para

expressar a complexidade de tempo, principalmente para facilitar as comparações com

os resultados obtidos em Tarnas e Hughey (1998).

81

Page 84: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

82

Page 85: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

CAPÍTULO 3

ALGORITMOS DE PROGRAMAÇÃO DINÂMICA COM CHECKPOINTS

USADOS EM PERFIS-HMMS

3.1 Introdução

Comparação e alinhamento de seqüências são procedimentos computacionalmente

dispendiosos, comumente utilizados em bioinformática, que freqüentemente recorrem

aos já clássicos métodos de PD com requerimentos de memória de trabalho da ordem de

O(n2). Esses algoritmos podem se beneficiar de técnicas ou métodos de economia de

espaço (memória de trabalho). Essa economia de espaço, geralmente, é obtida em troca

de uma queda de desempenho no tempo de execução do algoritmo. Os HMMs lineares e

mais precisamente os perfis-HMMs, que são os modelos alvos deste estudo, utilizam

métodos de PD para o cálculo de medidas de desempenho e para o treinamento (cálculo

dos parâmetros) do modelo.

Nesse capítulo é apresentada uma família de métodos, que fazem uso do método de PD

e do paradigma (ou estratégia) de checkpoints para comparação e alinhamento de

seqüências, com economia de espaço, que podem ser usados com o algoritmo de Viterbi

na computação do melhor caminho, com o algoritmo forward (ou backward) na

computação da ( )λ|OP e com o algoritmo de Baum-Welch (forward-backward) no

treinamento de HMMs, especialmente nos perfis-HMMs. Esses métodos são

convenientes para implementação tanto serial quanto paralela. Numa implementação

serial eles requerem uso de memória da ordem de O( L nm ) com um fator de queda de

desempenho proporcional a L, onde m é o comprimento da maior seqüência, n o

comprimento da menor seqüência (ou do modelo perfil-HMM) e L é um inteiro

arbitrário indicando o nível do algoritmo, que pode ser determinado considerando-se a

quantidade de memória disponível e os comprimentos m e n. Numa implementação

paralela, com processadores paralelos conectados linearmente ou em malha, com O(m)

elementos de processamento, eles requerem espaço da ordem de O( L n ) por elemento

83

Page 86: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

de processamento e têm complexidade de tempo da ordem de O(mn) com um fator de

queda de desempenho proporcional a L.

Os algoritmos de PD com checkpoints são fundamentalmente idênticos ao algoritmo de

PD básico ou padrão. A principal diferença é que os algoritmos de PD com checkpoints

armazenam, na primeira fase de computação para frente, apenas parte da matriz de PD;

armazenam apenas algumas linhas (colunas ou diagonais) convenientemente

selecionadas da matriz de PD, que são denominadas checkpoints. As seções ausentes,

aquelas entre as linhas (colunas ou diagonais) de checkpoints, são re-computadas

quando necessário durante a fase de retrocedimento.

As demais seções deste capítulo tratam dos algoritmos de PD com economia de espaço,

em particular, da conceituação básica dos algoritmos de PD com checkpoints. Na

segunda seção é apresentada uma revisão da literatura referente aos algoritmos de PD

com economia de espaço atualmente utilizados em Bioinformática, especialmente na

classe de perfis-HMMs. Essa revisão tem o objetivo de situar o problema e analisar o

estado da arte destes algoritmos e é centrada, principalmente, nos tópicos de interesse

deste trabalho. A terceira seção trata do estado da arte dos algoritmos de programação

dinâmica que fazem uso do paradigma de checkpoints.

3.2 Revisão da Literatura

Myers (1991) em um relatório técnico, faz uma revisão da técnica de programação

dinâmica usada em algoritmos de comparação de seqüências em Biologia Molecular.

Segundo ele o paradigma de programação dinâmica, que é um paradigma

computacional de grande e variada aplicabilidade, já tinha se tornado, nesta época, uma

técnica clássica e bem estabelecida em Biologia Computacional. Ele examina variações

do problema básico, que são motivadas, especialmente, por aplicações em Biologia

Computacional, entretanto ele não trata da aplicação destes algoritmos de PD aos

HMMs.

Segundo Myers (1991) a comparação de bio-seqüências possibilita aos Biólogos

moleculares verificar, a partir de similaridades encontradas entre essas, se as

84

Page 87: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

características físicas ou funcionais de uma das seqüências são, também, verdadeiras

para a sua análoga. Tais comparações, geralmente, constituem-se no alinhamento das

seqüências, ou de seções destas, de tal forma que as similaridades entre elas sejam

evidenciadas. Essas comparações podem ser efetuadas do ponto de vista de

maximização da similaridade entre as seqüências ou de minimização da distância entre

elas. Na verdade, esses dois problemas não são contraditórios mas duais do ponto de

vista de modelos de otimização. A maximização da similaridade pode ser visto, de

forma intuitiva, como a busca por alinhamentos que possuem poucas lacunas e cujos

símbolos alinhados sejam idênticos ou tenham funções muito similares. Na

minimização da distância entre as seqüências o problema é encarado do ponto de vista

da transformação da seqüência A na seqüência B através de um número mínimo de

operações “evolucionárias”. Nessa visão, um símbolo alinhado de A é substituído pelo

seu complementar em B, um símbolo não alinhado em A é removido e um símbolo não

alinhado em B é adicionado. Adicionalmente, a noção de otimização requer algum

critério de otimização ou de atribuição de escores e tradicionalmente é fornecida uma

função, freqüentemente associada com a plausibilidade biológica do alinhamento, que

atribui um valor real a cada possível par alinhado (Myers, 1991).

O problema de maximização da similaridade pode ser formalizado, de maneira intuitiva

e esclarecedora, através de um grafo de edição, um grafo direcionado com arestas

rotuladas, que no jargão de ciência da computação é um autômato finito. A cada aresta

rotulada deste grafo de edição pode ser associado um peso ou custo, de forma que a

soma dos pesos das arestas rotuladas de um caminho seja exatamente a soma dos

escores dos pares alinhados no correspondente alinhamento. Desta forma o problema se

reduz ao de computar o caminho de peso-mínimo através do grafo de edição, desde o nó

de origem até o nó de encerramento (sink). No jargão de ciência da computação, a

comparação de seqüências tem sido reduzido ao problema de caminhos mínimos sobre

um grafo com estrutura bastante regular (para mais detalhes veja Myers (1991)).

Segundo Myers (1991) os problemas de comparação de seqüências surgiram mais cedo

no campo de Ciência da Computação do que no campo da Biologia Molecular, numa

formulação para instâncias mais simples do problema. O problema de encontrar a maior

85

Page 88: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

subseqüência comum – LCS – e seu dual, o problema de determinar o script de edição

mínimo, surgiram no contexto de aplicações tais como comparar o conteúdo de arquivos

de computadores e corrigir a ortografia de palavras. Aqui é importante notar que estas

aplicaçõs de Ciência da Computação constituem instâncias do problema que requerem

apenas funções de custos, subjacentes, de natureza bastante simples, com custos

discretos e inteiros, o que permite um desempenho algorítmico muito bom. Esse

desempenho algorítmico não pode ser obtido para as aplicações de Biologia Molecular,

pois essas requerem funções de custos de natureza bem mais complexas.

Nesse formalismo de PD, se o interesse reside somente no custo de um alinhamento

ótimo, então é necessário guardar somente a linha anterior da matriz de PD para

computar a linha corrente, graças à natureza das dependências de dados da recorrência

de PD, requerendo espaço da ordem de ( )( )nmO ,min . Mas se é necessário recuperar o

alinhamento ótimo, o caminho de estados que produziu tal alinhamento, então é

necessário guardar toda a matriz de PD, requerendo espaço da ordem de O .

Entretanto, apesar da complexidade deste problema de PD ser polinomial de grau 2,

quando computando o alinhamento de seqüências muito grandes, o que é muito

freqüente quando alinhando bio-seqüências, o requerimento de espaço pode ser um fator

limitante, mais do que o requerimento de tempo, pois pode-se estar fora da memória

disponível no computador real. Desta forma, a construção de algoritmos com

requerimentos de espaço reduzido é muito importante para a comparação de bio-

seqüências (Myers, 1991).

( )mn

Usando esse algoritmo como um sub-procedimento que produz somente o custo de um

alinhamento ótimo, Hirschberg (1975) apresenta um algoritmo D&C que determina o

alinhamento ótimo, requerendo espaço da ordem de ( )nmO + . Entretanto, esse

algoritmo é apresentado num contexto mais simples, na determinação da LCS entre

duas seqüências. Mas, Myers e Miller (1988) mostraram que esse algoritmo D&C de

Hirschberg pode ser aplicado a muitos algoritmos de comparação de seqüências, que

admitem versões de espaço linear para computar somente o custo do alinhamento ótimo

entre duas seqüências.

86

Page 89: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O algoritmo apresentado por Hirschberg (1975) adota a estratégia de D&C para

computar a LCS entre duas seqüências, e requer somente espaço linear, ao custo de uma

queda de desempenho dada por um fator de 2 em relação ao tempo quadrático do

algoritmo de PD padrão. Dadas duas strings A e B, o algoritmo de Hirschberg divide,

por exemplo, a string B ao meio - quer dizer para a coluna média da matriz de PD - e

encontra em que lugar nessa coluna média o alinhamento ótimo cruza. Restando, desta

forma, dois problemas menores de alinhamento, correspondente às duas regiões da

matriz de PD divididas pela coluna média e pela linha que contém o ponto de

cruzamento do alinhamento ótimo na coluna média. Esses dois problemas menores são,

então, resolvidos recursivamente da mesma forma. Os pontos de cruzamento do

alinhamento ótimo na coluna média destes sucessivos particionamentos são

determinados executando-se o algoritmo de PD para frente na primeira metade da

matriz de PD, ou nas sucessivas seções desta matriz de PD, e em ordem reversa na outra

metade.

Hunt e Szymanski (1977) apresentaram um algoritmo para o problema LCS entre duas

seqüências com complexidade de tempo da ordem de ( )nrO loglog , onde r é o número

de pontos concordantes entre as duas seqüências. Entretanto, o parâmetro r pode ser de

tamanho mn, quando as seqüências são proximamente relacionadas, ou seja, é

potencialmente de complexidade ( )mnΩ . Desta forma, apesar de não poder melhorar a

complexidade deste algoritmo em termos dos parâmetros de entrada m e n, ele é

bastante efetivo para aquelas aplicações que são bastante esparsas com relação ao

parâmetro r, como aquelas de comparação de arquivos de computadores. Entretanto,

Hunt e Szymanski (1977) não apresentaram nenhuma consideração, quanto à

possibilidade de estender essa técnica para que fosse utilizada com outras funções de

custo, que possibilitassem comparações mais efetivas, ou para que fosse utilizada em

aplicações mais complexas.

Em (Myers, 1986) foi apresentado um algoritmo para o problema do script de edição

mínimo, que apresenta complexidade de tempo da ordem de ( )dnO , onde d é número

de indels (número de adições e de remoções) e de substituições no alinhamento original.

87

Page 90: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Esse algoritmo é dito ser sensível à saída, pois ele depende do parâmetro d, que

caracteriza o valor do escore da comparação das duas seqüências. Pode ser verificado

que esse algoritmo apresenta um comportamento de pior caso para seqüências

totalmente divergentes, com d sendo da ordem de ( )nmO + , e um comportamento de

melhor caso para seqüências totalmente similares, com d sendo da ordem de , onde

k é um inteiro. Podendo, desta forma, não apresentar queda na complexidade de tempo

em termos dos parâmentros m e n. Apesar disso, esse algoritmo é muito simples e sua

complexidade de tempo esperada é da ordem de

( )kO

( )2dn +O . Adicionalmente, Myers

(1986) apresentou uma variação deste algoritmo com complexidade de tempo, para o

pior caso de execução, da ordem de ( )2log dnn +O , usando como sub-procedimentos

complexas árvores de sufixos e algoritmos que requerem tempo constante para a

determinação do ancestral comum mínimo.

O algoritmo de Myers (1986) e o de Hunt e Szymanski (1977) não podem ser

estendidos, a princípio, para utilização com outras funções de custo mais complexas,

que possibilitem comparações mais efetivas, ou para utilização em aplicações mais

complexas. Constituindo, portanto, para o problema mais geral de comparação de

seqüências, em alguns resultados algorítmicos pontencialmente úteis.

Segundo Myers e Miller (1988) o método apresentado por Hirschberg (1975) para a

computação da LCS é uma abordagem mais geral, que pode ser aplicada a qualquer

algoritmo de comparação de seqüências cuja versão para determinar somente o custo do

alinhamento ótimo requer apenas espaço linear. Sendo, portanto, superior, teoricamente

e em aplicações práticas, às recentes estratégias propostas para economia de espaço e,

merecendo maior visibilidade no seio da comunidade científica. Desta forma eles

desenvolveram, a partir das idéias de Hirschberg, uma versão de espaço linear do

algoritmo apresentado por Gotoh (1982), que admite funções de custo afim –

penalidades afim de lacunas (affine gap penalities).

O algoritmo desenvolvido por Gotoh (1982) possui complexidades de tempo e espaço

da ordem de para comparação de duas seqüências, produzindo como saída tanto (mnO )

88

Page 91: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

o custo mínimo quanto o alinhamento ótimo dos símbolos das duas seqüências. Esse

algoritmo admite uma implementação com espaço linear, se somente o custo do

alinhamento ótimo é requerido. Desta forma usando esse procedimento para determinar

somente o custo de um alinhamento ótimo e o método apresentado por Hirschberg

(1975), Myers e Miller (1988) desenvolveram a versão linear do algoritmo de Gotoh

(1982) ou, alternativamente, estenderam o algoritmo D&C de Hirschberg para admitir

uma função de custo afim – com penalidades de lacunas dadas por uma função afim.

Rick (2000) apresenta uma versão de espaço linear para o algoritmo desenvolvido por

Rick (1995) para determinar o comprimento da LCS e produzir como saída a própria

LCS. A complexidade de tempo destes algoritmos é da ordem de

((( pnppmnsO )))−+ ,min , onde s é o tamanho do alfabeto e p é o tamanho da LCS.

Rick (1995) observa que os algoritmos desenvolvidos até aquela época para o problema

da LCS não apresentam ganho de desempenho com relação a complexidade de tempo

da ordem de da abordagem de PD, mas alguns destes apresentam performance

bem melhor para certas classes de seqüências. Esses algoritmos freqüentemente são

sensíveis a parâmetros de saída, tais como p, d e s. Alguns são convenientes para tratar

classes de pares de seqüências onde a LCS é muito grande, em média, outros para

pequenas LCS, em média. Mas essa decisão nem sempre é facil, pois freqüentemente se

conhece pouco da natureza das seqüências a serem comparadas ou a classe de

seqüências é heterogênea. Além disso, quando as seqüências de uma classe têm

aproximadamente o mesmo tamanho e são formadas por símbolos de um alfabeto

pequeno, tomados de maneira mais ou menos regular, o tamanho da LCS pode variar

entre

(mnO )

m31 e m3

2 , em média, dependendo dos tamanhos do alfabeto (no caso de

tamanhos: 4, 12, 14). Essa é uma situação típica da análise de bio-seqüências em

Biologia Molecular e não têm sido dada atenção específica a esses problemas. O

algoritmo proposto por Rick (1995 e 2000) apresenta boa peformance para todas essas

classes de problemas, tanto no nível teórico quanto prático, sendo, portanto, pouco

sensível aos parâmetros de saída . Veja na Tabela 3.1, adaptada de Rick (2000), um

resumo do desempenho destes algoritmos de espaço linear para o problema da LCS.

89

Page 92: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Crochemore et al (2003) apresentaram dois algoritmos para determinar o tamanho e o

próprio alinhamento da LCS. Esses algoritmos são um melhoramento, baseado em

paralelismo de bits, dos algoritmos de Hirschberg (1975), baseado na técnica D&C, e de

Hunt e Szymanski (1977), baseado na computação eficiente de todos os pontos

concordantes dominantes (contornos). O paralelismo de bits já tem sido bastante usado

para encontrar o tamanho, p, da LCS, mas eles estendem o uso desta idéia para a

recuperação da LCS. Os algoritmos que recorrem a esse paralelismo ao nível de palavra,

que “empacotam” bits numa palavra de computador, são denominados algoritmos de

vetores de bit (bit-vector algorithms).

TABELA 3.1- Resumo do Desempenho de Alguns Algoritmos de Espaço Linear para o Problema LCS.

Ano AUTOR TEMPO FQD PARADIGMA

1975 Hirschberg ( )mnO 2 PD

1986 Myers ( )( )pnnO − 2 Caminhos Mínimos

1990 Wu et al ( )( )pmnO − 2 Caminhos Mínimos

2000 Goeman et al ( )( )pnpmmpmO −+log,min [5,25; log m] Contornos

2000 Rick ( )( )pnppmO −,min 2 Contornos

FQD: Fator de queda de desempenho para o pior caso de desempenho do algoritmo. FONTE: Adaptada de RICK (2000).

O algoritmo de Hunt e Szymanski (1977) tem complexidade de espaço da ordem de

, com pior caso de complexidade de tempo da ordem de ( )+ nrO nnO loglog2 . É

importante salientar que nesta mesma época Hirschberg (1977) também apresentou um

algoritmo baseado nessa técnica de contornos com complexidade de tempo da ordem de

, com pior caso ( )pnO nO . Esses algoritmos são eficientes para aplicações onde r é

pequeno, ou seja, eles são dependentes do parâmetro r.

( )

( )2

Talvez o grande ganho dos algoritmos de vetores de bits (bit-vector algorithms)

apresentados por Crochemore et al (2003) seja a baixa sensibilidade aos parâmetros de

entrada, s e w, ou aos parâmetros de saída, p e r. Eles asseguram que esses

melhoramentos são de interesse prático e superam os algoritmos básicos em

90

Page 93: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

desempenho. O algoritmo de Hirschberg (1975) melhorado com a adição do paralelismo

de bits mantém os requerimentos de espaço ao nível linear e obtém um ganho de

desempenho ao nível da complexidade de tempo, que agora é da ordem de ( wmn )O . Já o

algoritmo de Hunt e Szymanski (1977) mantém a complexidade linear de espaço e

obtém um pequeno ganho de desempenho com relação ao tempo de execução,

apresentando agora complexidade da ordem de ( )( )nnq logO + , pois rq , mas com

um pior caso da mesma ordem do algoritmo original, que é de

( )nlogn log2O .

Grice et al. (1995 e 1997) apresentaram uma família de métodos, denominados

algoritmos de L-níveis com checkpoints, para computar alinhamentos de seqüências,

que são convenientes tanto para implementação serial quanto paralela. Numa

implementação serial eles requerem uso de memória da ordem de O( L nm ) com um

desempenho da ordem de O( ), com um fator de queda de desempenho de L. Numa

implementação paralela, com O(m) processadores paralelos conectados linearmente ou

em malha, eles requerem espaço da ordem de O(

mn

L n ) por elemento de processamento

com um fator de queda de desempenho de L.

Esses algoritmos são fundamentalmente idênticos aos algoritmos de PD padrões e

podem ser usados com as diversas funções de custo, que freqüentemente são usadas na

biologia computacional. Eles podem ser utilizados para o treinamento de perfis-HMMs,

através do algoritmo de Baum-Welch (forward-backward). Na determinação do melhor

alinhamento entre duas seqüências, um dos membros desta família de algoritmos, o de

L-níveis com checkpoints por diagonais com retrocedimento restrito, supera em

desempenho, numa análise comparativa a posteriori, o algoritmo de Hirschberg baseado

na estratégia D&C e iguala o requerimento de espaço linear deste.

Os algoritmos de PD de L-níveis com checkpoints caracterizam-se por calcularem a

matriz de PD completa, por exemplo, numa execução do algoritmo forward, mas

somente armazenando determinadas linhas, colunas ou diagonais da matriz de PD a

intervalos regulares (checkpoints), para que numa fase de retrocedimento as seções entre

dois destes checkpoints possam ser recalculadas sem a necessidade de recalcular a

91

Page 94: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

matriz de PD desde o início. Isto permite reduzir o requerimento de memória da ordem

de O(nm) para O( L nm ). O algoritmo de PD padrão é considerado o algoritmo de 1-

nível com checkpoints.

Em Grice et al. (1997) foram implementados o algoritmo de 2-níveis com checkpoints

por linhas, o de 2-níveis com checkpoints por diagonais, o algoritmo D&C de

Hirschberg e o algoritmo básico de PD. Os algoritmos de 2-níveis com checkpoints

foram implementados com e sem retrocedimento restrito. Os experimentos seriais foram

efetuados numa estação, não-carregada, Sun UltraSparc, modelo 140 com 64 Mb de

memória, e os experimentos paralelos num computador paralelo Maspar MP-2204 com

4096 elementos de processamento e 64 Kb de memória por elemento de processamento.

Os experimentos seriais para encontrar o melhor alinhamento entre duas seqüências

mostraram que a queda de desempenho em tempo total de execução são proximamente

relacionados para seqüências de até 4.000 resíduos. Mas para seqüências maiores os

algoritmos com economia de espaço mostraram-se muito superiores em desempenho.

Os algoritmos de 2-níveis com checkpoints, sem retrocedimento restrito, apresentaram

uma queda de desempenho dada por um fator ~2 em relação ao algoritmo básico para

seqüências de até 4.000 resíduos e uma aceleração no desempenho por um fator de 10

ou mais, para seqüências entre 4.000 e 7.000 resíduos.

Observa-se que o algoritmo de 2-níveis, com checkpoints por diagonais e

retrocedimento restrito, mostrou-se mais rápido em relação ao tempo total de execução

que o algoritmo básico para seqüências maiores do 1.000 resíduos, e sempre foi mais

rápido que os demais algoritmos para todos os comprimentos de seqüências.

Nesses experimentos a queda de desempenho em relação ao tempo de CPU foram

proximamente relacionadas para todos os tamanhos de seqüências no intervalo

considerado. Somente o algoritmo de 2-níveis com checkpoints por diagonais e

retrocedimento restrito apresentou desempenho superior ao algoritmo de PD básico para

todas as seqüencias maiores do que 500 resíduos, no intervalo considerado. Todos os

outros algoritmos quando comparados ao algoritmo de PD básico apresentaram o

92

Page 95: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

melhor desempenho para seqüências com 7.000 resíduos, mas mesmo assim com uma

queda no desempenho. Os 2 algoritmos com 2-níveis de checkpoints sem

retrocedimento restrito apresentaram uma queda no desempenho por um fator de no

mínimo 1,5. O D&C apresentou uma queda no desempenho por um fator ~1,25. O

algoritmo com 2-níveis de checkpoints por linhas e retrocedimento restrito apresentou

uma queda no desempenho por um fator ~1,1.

Tarnas e Hughey (1998) discutiram as implementações e performance dos algoritmos de

alinhamento de seqüências com uso de espaço reduzido baseado em checkpoints,

apresentados em Grice et al. (1995 e 1997), e a inclusão da versão por diagonais destes

algoritmos de 2-níveis de checkpoints no pacote de modelagem com HMMs

denominado SAM (Sequence Alignment and Modeling) do Departamento de Engenharia

de Computação da Escola de Engenharia Jack Baskin da Universidade da Califórnia em

Santa Cruz. Implementaram o código e avaliaram a sua performance numa estação DEC

Alpha 255 com um clock de 233 MHz, 96 Mb de memória principal e um cache L2 de

1 Mb. Efetuaram vários experimentos usando uma quantidade de memória variável,

com o propósito de verificar o nível de eficiência quando o tamanho do problema cresce

além do tamanho do cache L2, e para verificar o nível de eficiência do algoritmo de PD

padrão quando o tamanho do problema cresce além do tamanho da memória principal.

Mas, segundo eles, essa verificação não pôde ser efetuada pois o código é

computacionalmente limitado devido à manipulação de log-probabilidades. Nesses

experimentos foram calculados os tempos de CPU e os tempos totais de execução para

os algoritmos de Viterbi e forward-backward padrões e para os de 2-níveis com

checkpoints por diagonais.

Foram realizados dois experimentos. No primeiro experimento foi considerado um

HMM com 500 nós e seqüências de proteínas com comprimentos variando de dezenas

a 10.000 resíduos. O desempenho do algoritmo forward-backward com checkpoint é

cerca de 10% mais lento que o algoritmo padrão. Já o algoritmo de Viterbi padrão e o

com checkpoint tiveram desempenho semelhante até o início de paginação. Pode ser

notado, comparando-se os gráficos que sumarizam os tempos de CPU e os tempos de

execução, que para seqüências acima de 8.000 resíduos os algoritmos padrões causam

93

Page 96: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

paginação excessiva na estação de trabalho com 96 Mb de memória, o que torna

inviável a obtenção de resultados para seqüências acima deste tamanho. No segundo

experimento foi considerado um HMM com 2.000 nós e seqüências de RNAs com

comprimentos variando de dezenas a 10.000 resíduos. Os resultados foram similares ao

do primeiro experimento, mas a degradação da memória virtual teve início para

seqüências de cerca de 2.000 resíduos, devido ao tamanho do modelo, o que torna

inviável a obtenção de resultados para seqüências acima deste tamanho.

Da análise dos resultados destes experimentos pôde ser observado que o algoritmo de 2-

níveis com checkpoints apesar de efetuar muito mais computação que o algoritmo

padrão, tem uma queda de desempenho muito pequena, de somente 10%, em relação ao

algoritmo básico. Isto pode ser devido a utilização eficiente do sistema de memória,

particularmente no uso da cache.

Tarnas e Hughey (1998) observaram, com base nesses experimentos e na experiência

anterior com os algoritmos PD de 2-níveis com checkpoints por diagonais, que a

complexidade das condições de contorno adicionadas quando processando diagonais

não proporcionou um retorno equivalente no ganho de performance. E, com base nos

resultados destes experimentos, eles planejaram reimplementar o loop interno do pacote

SAM com checkpoints por linhas. É importante salientar que a complexidade das

condições de contorno foi agravada pelo uso da técnica de retrocedimento restrito. Eles

observaram, também, que o algoritmo de PD de 2-níveis com checkpoints tem

requerimento de memória, O( nm ), maior do que o da abordagem D&C de

Hirschberg, O( ), mas possui três vantagens que faz dele uma forte alternativa a

abordagem D&C de Hirschberg. Primeiro, ele pode ser usado para efetuar cálculos

forward-backward. Segundo, a codificação dele é mais simples que a da abordagem

D&C, principalmente para checkpoints por linhas ou colunas. Por último, a abordagem

D&C, que é completamente recursiva, parece ter um custo constante extra (overhead)

maior do que aquele da abordagem com checkpoints, que não é totalmente recursiva.

nm +

Além disso, eles relataram que a computação da matriz de PD ao longo das diagonais

remove todas as dependências de dados do loop interno do algoritmo de PD, liberando o

94

Page 97: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

compilador para realizar uma otimização mais efetiva e uma reorganização do código

dentro do loop interno. Isto torna essa família de algoritmos de 2-níveis com

checkpoints por diagonais apropriada para implementação em códigos para

processamento paralelo e processamento vetorial.

Powell et al. (1999) aplicaram a técnica de checkpoints ao algoritmo de Hirschberg,

apresentado acima, e ao algoritmo rápido de Ukkonen, na versão apresentada por

(Myers, 1986), para obter um alinhamento entre duas strings. A alternativa ao

algoritmo de Hirschberg, com a técnica de checkpoints, pode ser aplicada a muitas

outras funções de custo, além da função de custo simples ou distância de edição básica.

Essa alternativa mantém o requerimento de espaço linear do algoritmo original de

Hirschberg com uma queda de desempenho por um fator constante de cerca de 2,5.

Entretanto não foi apresentada nenhuma comparação com o algoritmo de PD básico.

Uma versão do algoritmo de Ukkonen apresentada por Myers (1986) é cerca de 42%

mais rápido que o algoritmo básico de PD, quando efetuando o alinhamento de duas

seqüências. Esse algoritmo possui uma complexidade de tempo média da ordem de

O( ), onde d é o custo de edição entre duas strings (seqüências). Se for necessário

determinar somente o custo de edição, o espaço requerido é da ordem de O(d), mas se

for necessário também o alinhamento, o espaço requerido é da ordem de O(d

2dn +

2). A

alternativa apresentada em Powell et al. (1999), com a técnica de checkpoints, reduz o

requerimento de espaço do algoritmo original de Ukkonen para O(d), mesmo quando

produzindo um alinhamento, com uma queda de desempenho por um fator constante de

cerca de 3,5. Entretanto não foi apresentada nenhuma comparação com o algoritmo de

PD básico e nem com o algoritmo de Hirschberg com checkpoints.

O algoritmo de Ukkonen (Myers, 1986), quando aplicado ao alinhamento de duas

seqüências, acelera o desempenho do algoritmo de PD básico, com base em

determinados características da matriz de PD: nem todas as entradas da matriz de PD

são necessárias; as diagonais são monotônicas e não-decrescentes; e somente o ponto

final de uma série de combinações (matches) é importante. E com base nesses fatos ou

características uma matriz alternativa U é construída, onde cada entrada U[ab, d] = i

95

Page 98: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

contém a distância máxima, que pode ser obtida, ao longo da seqüência A, na diagonal

com um custo jiab −= [ ]jiPDd ,= . Uma linha da matriz U corresponde a uma

diagonal da matriz de PD e uma coluna de U corresponde a um contorno (contour) de

custo fixo na matriz PD.

Os algoritmos alternativos apresentados em Powell et al. (1999) podem ser acelerados

aumentando-se o uso de memória (usando-se mais checkpoints) e são apropriados para

determinar o melhor alinhamento entre duas seqüências. É importante salientar que os

dados usados como amostras nos experimentos efetuados por eles, foram gerados

aleatoriamente com um alfabeto composto de 26 símbolos. Primeiro a string A foi

gerada aleatoriamente e, então, a string B foi gerada de A com probabilidades fixas de

0,2 para mudança, 0,1 para inserção e 0,1 para remoção (foram permitidas discordâncias

para o mesmo caractere).

Wheeler e Hughey (2000) exploraram o paradigma de checkpoints, especialmente a

disposição dos checkpoints com a intenção de melhorar o desempenho dos algoritmos

de PD de L-níveis com checkpoints. Eles analisaram e procuraram determinar a

disposição ótima dos checkpoints na matriz de PD que produzisse uma melhor

performance nos algoritmos de PD de L-níveis com checkpoints. Nesse sentido eles

consideraram os custos das diferentes estratégias de disposição dos checkpoints, que

podem ser determinados em termos do número de células de PD calculadas. Eles

observaram que até onde os requerimentos de espaço são suficientemente baixos, para

evitar o risco de troca excessiva de páginas entre a memória virtual e o disco, o cálculo

de células de PD domina a computação e, portanto, a diferença entre esses esquemas de

disposição dos checkpoints na matriz de PD. Isto se deve ao fato que esses algoritmos

de PD com checkpoints caminham de maneira regular através de sucessivas células da

matriz de PD e, desta forma segundo eles, o uso amplo e eficiente da memória cache é

feito através de longas linhas, sendo assim a diferença entre ajustar na memória

principal e ajustar na cache pode ser insignificante.

96

Page 99: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O algoritmo de PD de L-níveis com checkpoints pode calcular uma matriz de PD com

um número máximo de linhas dado por: , desde que

, e para n

−+== ∑

=− L

LRirRr

R

iLL

1)()(

11

( ) RRr =1 )(RrL= o algoritmo utiliza todo o espaço reservado de memória, R,

com uma disposição ótima dos checkpoints. Entretanto, para não é

possível calcular a matriz de PD com o algoritmo de nível L-1 e há um desperdício de

espaço reservado e perda de performance quando se usa um algoritmo de nível L. Essa

perda de performance por um fator de

)(RrL)(1 nRrL <<−

( )1~ −LL se deve principalmente a uma má

disposição dos checkpoints.

Wheeler e Hughey (2000) apresentaram para r )()(1 RrnR LL <<− um algoritmo

denominado algoritmo de ( -níveis com checkpoints, que pode melhorar muito a

situação de perda de performance. Entretanto os algoritmos de

)LL ,1−

( )L,1L − -níveis com

checkpoints não são ótimos. O algoritmo ótimo transita livremente entre níveis para

fazer uso ótimo da memória disponível para qualquer subproblema. Para estudar a

disposição ótima dos checkpoints Wheeler e Hughey (2000) utilizaram um algoritmo de

programação dinâmica de ordem O(n3).

Assintoticamente, o algoritmo de L-níveis calcula Ln linhas, o algoritmo de ( )-

níveis calcula pelo menos

LL ,1−

( ) 11 +− nL linhas e o algoritmo ótimo, de ( ) -

níveis, calcula pelo menos ( )

K,2, −L1, −LL

( ) 12 −− L nL1 −− nL linhas. Na prática, para valores de pior

caso, um dado R e , o algoritmo de 150≥n ( )L,1L − -níveis é cerca de 10% mais lento

que o algoritmo ótimo.

A abordagem de Wheeler e Hughey (2000) para melhorar a performance do algoritmo

de PD com checkpoints por diagonais foi similar àquela usada no algoritmo com

checkpoints por linhas. Foi utilizada programação dinâmica para encontrar o algoritmo

com checkpoints ótimo e, então, tentou-se descobrir um algoritmo mais simples, mas

ainda útil para uso geral. No primeiro conjunto de experimentos, foram realizados o

alinhamento de pares de seqüências. Quando as seqüências eram aproximadamente

97

Page 100: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

iguais em tamanho, o algoritmo melhorado, com checkpoints por diagonais, efetuou um

pouco menos cálculos de células de PD que o algoritmo básico D&C, com duas linhas

de memória cada. Contudo, para seqüências de tamanhos diferentes, o algoritmo D&C

foi claramente superior. Mas, quando mais que duas linhas de memória estão

disponíveis, o algoritmo com checkpoints por diagonais explora melhor esses recursos

extra, e desempenha significativamente melhor que o D&C. Já o algoritmo de Powell

efetua até 2,5 % menos cálculos de células que o algoritmo melhorado com checkpoints

por diagonais, mesmo considerando uma variação de recursos extra de memória.

O pacote de modelagem HMM denominado SAM foi modificado para o segundo

conjunto de experimentos relatados em Wheeler e Hughey (2000), o algoritmo de PD de

2-níveis com checkpoints por linhas foi substituído pelo algoritmo de (1,2)-níveis com

checkpoints por linhas. O pacote modificado foi testado contra o original numa estação

Sun UltraSpark I com 64 MB de memória. Esses algoritmos permitiram a avaliação do

melhor caminho simples de estados para uma seqüência de 80.000 resíduos contra um

HMM com 80.000 nós, mas a avaliação de todos os caminhos não foi possível para

esses tamanhos de seqüências e HMMs. Eles realizaram experimentos, variando os

tamanhos da memória de PD numa faixa de até 60 Mb. Na avaliação de todos os

caminhos o algoritmo melhorado foi 3 a 12% mais rápido, independente do tamanho do

HMM, e o melhoramento típico foi cerca de 10% quando alocados 10 a 20 Mb de

memória para o problema. Para um HMM com 5.000 nós, os dois algoritmos iniciaram

paginação para uma alocação de 60 Mb de memória, com uma queda de desempenho

por um fator de 1,9.

Wheeler e Hughey (2000) apresentaram as seguintes conclusões: quando é requerido

somente o melhor caminho de estados, o algoritmo de Powell é uma escolha apropriada

como uma alternativa ao algoritmo básico D&C e ao algoritmo com checkpoints por

diagonais; mas quando é necessário avaliar todos os caminhos, a única escolha são os

algoritmos com checkpoints.

A seguir são apresentados os resultados obtidos em três teses relacionadas com o tema

em estudo, devidas a Comet (1998), a Pedersen (2000) e a Birney (2000). Essas teses

98

Page 101: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

tratam de algoritmos em Biologia Computacional para a análise e comparação de

seqüências biológicas. Esses estudos, também, recorrem a métodos e técnicas de

programação dinâmica mas focalizam outros aspectos destes algoritmos, diferentes

daqueles que são objeto deste estudo. Essa diferença de foco ficará clara nas descrições

destes trabalhos realizadas a seguir.

Comet (1998) em sua tese de doutorado trata dos algoritmos de comparação de

seqüências biológicas, no campo de aplicação da biologia molecular. Ele desenvolve um

estudo para expressar os algoritmos de programação dinâmica, usados para alinhamento

global e local de seqüências biológicas, através da álgebra (max, +), o que possibilita

relacionar o problema com problemas de outros campos de aplicação e lançar mão de

experiência e conhecimentos acumulados no estudo destes problemas, nessas áreas.

Essa abordagem permite construir um autômato, que permite a busca em um banco de

dados em tempos lineares, embora a dimensão deste autômato seja muito grande.

Entretanto, mesmo que a construção deste autômato seja inviável por causa de sua

dimensão, segundo ele, essa construção permite a ligação entre o método de

alinhamento de seqüências usando programação dinâmica e os métodos de combinação

de padrões (pattern-matching) com erros.

Nesse estudo é proposto um algoritmo (ou método) de alinhamento, que leva em conta

outras informações além daquelas relativas à estrutura primária da seqüência biológica,

com o propósito de melhorar os alinhamentos obtidos por programação dinâmica,

obtendo alinhamentos que são biologicamente mais significativos. No algoritmo são

considerados os padrões contidos no banco de dados PROSITE, favorecendo um

alinhamento se ele coloca em correspondência as ocorrências do mesmo padrão

presente nas duas seqüências. Adicionalmente, é feito um estudo da significância do

escore de um alinhamento e propõe-se o Z-escore como um bom guia estatístico para a

avaliação da significância biológica de um alinhamento, pois esse parece ser um pouco

mais confiável que o próprio escore.

O trabalho de pesquisa desenvolvido por Pedersen (2000) e relatado em sua tese de

doutorado se refere à construção de algoritmos que tratam problemas da área biológica e

99

Page 102: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

apresenta soluções com relevância biológica. Numa primeira parte ele faz uma revisão

do campo de biologia computacional focando na análise e predição de estruturas de

seqüências biológicas. No capítulo 2 ele revisa métodos para comparação de seqüências

biológicas, concentrando-se na comparação de duas seqüências biológicas para

determinar sua proximidade ou distância evolucionária, e em métodos para comparação

de famílias inteiras de seqüências biológicas, o que envolve o problema mais abstrato de

comparar dois HMMs. No capítulo 3 ele revisa métodos para encontrar regularidades

em strings, que podem ser aplicados na análise de seqüências (strings) biológicas,

concentrando-se nos métodos para encontrar repetições em cadeia (tandem repeats),

pares máximos e sub-strings quase-periódicas máximas. No capítulo 4 ele revisa

métodos para predição de estruturas biológicas, levando em conta elementos na

predição total da estrutura tridimensional da biomolécula a partir de sua descrição

enquanto uma seqüência (string) biológica, concentrando-se em métodos para a

predição da estrutura secundária de seqüências de RNA e da estrutura terciária de

proteínas.

A segunda parte da tese de Pedersen (2000) consiste da reimpressão de 6 artigos já

publicados em congressos ou jornais especializados ou através de relatórios técnicos,

que apresentam os resultados do trabalho de pesquisa feito durante a sua participação no

programa de doutoramento em Ciência da Computação na Faculdade de Ciência da

Universidade de Aarhus, na Dinamarca. Esses artigos apresentam resultados no campo

de biologia computacional concentrando-se nos resultados obtidos na análise e predição

de estruturas de seqüências biológicas, referentes àqueles problemas revisados na

primeira parte da tese. Não estenderemos a revisão do trabalho de pesquisa de Pedersen

(2000), para abranger os resultados apresentados nesses artigos, por estarem fora do

foco principal deste trabalho.

A tese de Birney (2000) está voltada para a construção de ferramentas ou de recursos de

programação,. Ele trata, principalmente, da construção de um kit de ferramentas de

programação que fazem uso de PD para analisar seqüências biológicas e extrair

informação útil. Esse kit de ferramentas é denominado Dynamite. Na construção de

cada ferramenta ele primeiro trata da teoria e da experiência acumulada na utilização

100

Page 103: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

destas e depois das questões programáticas que asseguram a eficiência e a construção de

aplicações úteis. Para as ferramentas que produzem resultados biológicos, ele apresenta

uma avaliação de sua eficácia. Ele também apresenta uma discussão do sucesso ou falha

de cada ferramenta construída.

Um kit de ferramentas como o Dynamite, proposto por Birney (2000), permite que o

pesquisador do campo da Bioinformática se concentre mais nos problemas biológicos

reais e na sua modelagem do que na implementação destes modelos, pois a

implementação dos métodos no campo da PD, na maioria das vezes, requer muito

tempo para a programação e depuração do código. Ele escolheu construir o seu kit de

ferramentas baseado em um compilador ao invés de baseado em bibliotecas de código

reutilizável, pois essa abordagem permite migrar para uma descrição de mais alto nível

do problema de PD. Essa abordagem permite, também, maior flexibilidade, separando o

compilador, as bibliotecas de rotinas e de suporte e o código de controle. Uma vantagem

adicional de se usar uma linguagem compilada é que essa permite tirar vantagem de

características específicas do hardware que será utilizado, podendo ser independente do

hardware se baseado em padrão ANSI.

As diferentes implementações possibilitadas pelo Kit Dynamite fornecem processos de

cálculo que são baseados na mesma definição de máquina de estado finito (autômato).

Além do que o Dynamite analisa completamente a definição da máquina de estado

finito numa definição que totalmente descreve a máquina, de forma que arquiteturas

bastante complicadas de máquinas alvo podem ser consideradas. Esse Kit proposto por

(Birney, 2000) fornece as seguintes implementações:

• Escore do alinhamento de duas seqüências (strings) através do algoritmo

forward ou do algoritmo de Viterbi, ambos com requerimento de espaço linear;

• Alinhamento Viterbi de duas seqüências com requerimento de espaço quadrático

e linear;

101

Page 104: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

• Alinhamento recursivo de duas seqüências com requerimento de espaço linear,

que faz uso da técnica D&C introduzida na comparação de seqüências por

Hirschberg (1975);

• Busca serial ou paralela (através de pthreads) em banco de dados de seqüências;

• Geração de código conveniente para hardware especializado, tal como para a

OneModel API (OneModel XL/G port).

Outro foco importante da tese de Birney (2000) está na avaliação da eficácia das

ferramentas construídas com o Kit Dynamite, que produzam resultados com

significância biológica. Para tanto ele se concentra no algoritmo GeneWise, que

compara informações de proteínas diretamente com a seqüência de DNA genotípica

(genômica), com a predição de gene ocorrendo simultaneamente à comparação de

proteínas. A eficácia do algoritmo GeneWise foi testada na análise de famílias de

proteínas (perfis-HMMs) do banco de dados Pfam, na comparação de genes da minhoca

contra o banco de dados PfamI, na comparação de genes do cromossomo humano nº 22

e na densidade de codificação de Humanos x C.elegans.

3.3 Algoritmos de Programação Dinâmica com Checkpoints

Até onde tem conhecimento o paradigma de checkpoints foi apresentado, para uso em

conjunto com o algoritmo de PD padrão em implementações paralelas, por Grice et al.

(1995). Ele foi considerado para implementações seriais por Grice et al. (1997),

estendido para uso com perfis-HMMs por Tanas e Hughey (1998) e melhorado por

Wheeler e Hughey (2000). Desta forma toda essa seção baseia-se nos artigos de: Grice

et al. (1995), Grice et al. (1997), Tarnas e Hughey (1998) e Wheeler e Hughey (2000).

Os algoritmos de PD baseados nesse paradigma são fundamentalmente idênticos aos

algoritmos de PD padrões e podem ser usados com as diversas funções de custo, que

freqüentemente são usadas na biologia computacional.

Antes de prosseguir na descrição e definição dos algoritmos baseados no paradigma de

checkpoints é necessário definir alguns parâmetros. Já foi definido anteriormente, que m

102

Page 105: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

é o tamanho da maior seqüência e n é o tamanho da menor seqüência (ou do perfil-

HMM), mas para simplificar a discussão pode ser assumido sem perda de generalidade

que m = n. Seja mMEMR = o número de linhas (colunas ou diagonais) da matriz de

PD que podem ser armazenadas, onde MEM é o número de células da matriz de PD que

podem ser armazenadas na memória disponível.

O algoritmo de PD padrão pode computar, usando como memória somente R = 2 linhas

da matriz de PD, o escore de um alinhamento ótimo ou a medida P(O|λ) – a

probabilidade de ocorrência da seqüência de observações O, dado o modelo λ –,

possuindo nesse caso complexidade de espaço da ordem de ( )nO sem nenhuma perda

de desempenho. Mas, quando é necessário utilizar um procedimento de retrocedimento

parcial para recuperar o alinhamento ótimo ou um retrocedimento completo – sobre

todos os caminhos ou alinhamentos – o algoritmo de PD básico tem complexidade de

espaço da ordem de ( ) ( )2nOmn =O , com a suposição de que nm = . O algoritmo de PD

com retrocedimento parcial precisa calcular pelo menos nmmn ++ células da matriz de

PD e tem complexidades de tempo da ordem de ( ) ( )2mn nO=O , enquanto que o

algoritmo com retrocedimento completo precisa calcular pelo menos células e

tem complexidade de tempo da ordem de

mnmn +

( ) ( )2nOmnO = .

Quando é necessário executar um procedimento de retrocedimento, a abordagem de PD

padrão cria uma matriz de PD, uma tabela m x n, para armazenar valores na memória.

Mas, infelizmente, na maioria das vezes essa tabela não pode ser ajustada

completamente na memória do PC ou da workstation, quando o modelo e os tamanhos

das seqüências são grandes. A solução é usar um método que requeira menos espaço nas

computações da matriz de PD.

Essa limitação do uso computacional do algoritmo de PD básico, quando associado a

um procedimento de retrocedimento, devido à complexidade de espaço da ordem de

O(n2) pode ser superada tirando vantagem do fato que não é necessário salvar o

resultado de todas as comparações para efetuar o retrocedimento. Ela pode ser superada

com checkpoints e re-computação; é necessário salvar somente informação do estado da

103

Page 106: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

computação (checkpoints) que seja suficiente para re-computar eficientemente as

comparações quando elas forem requeridas. Esses checkpoints de informação do estado

da computação incluem todos os custos totais requeridos para recalcular futuras linhas,

colunas ou diagonais das seções entre dois checkpoints consecutivos. Essas linhas,

colunas ou diagonais de checkpoints podem ser salvos de forma regular e apropriada

para que o retrocedimento através da matriz de PD possa ser efetuado em espaço

limitado, ou seja, sem a necessidade de armazenar em memória toda a matriz de PD.

Esses algoritmos são denominados algoritmos de PD de L-níveis de checkpoints e

caracterizam-se por calcularem a matriz de PD completa, por exemplo, numa execução

do algoritmo forward, mas somente armazenando determinadas linhas, colunas ou

diagonais da matriz de PD a intervalos convenientes e regulares (checkpoints), para que

numa fase de retrocedimento, as seções entre dois destes checkpoints possam ser

recalculadas sem a necessidade de recalcular a matriz de PD desde o início. Isto permite

reduzir assintoticamente o requerimento de memória da ordem de ( )nmO para O( L nm )

ao preço de uma queda de desempenho por um fator de 1≤0 , < kkL , onde L é um

inteiro tal que . nL ≤<0

Quando a memória disponível mMEMR = (linhas) for insuficiente para armazenar a

matriz de PD completa, para que se possa executar um procedimento de retrocedimento

sobre a matriz, a memória pode ser dividida em dois segmentos: um segmento

denominado Rcp para armazenar os checkpoints e um segmento denominado Rtb usado

para o cálculo de seções da matriz de PD na fase de computação para frente (forward) e

na fase de retrocedimento. Frequentemente, a memória disponível R (linhas) pode ser

otimizada fazendo com que 2RRtbcpR == linhas, mas às vezes essa divisão pode ser

móvel ou ter outra configuração que seja mais conveniente.

O algoritmo de PD padrão é considerado o algoritmo de 1-nível com checkpoints, já

descrito nesse trabalho. A seguir será feita uma descrição dos algoritmos de níveis mais

altos, iniciando com o de 2-níveis de checkpoints, que é o mais fácil de ser descrito.

Para facilidade e simplicidade de tratamento e raciocínio é assumido em quase todo o

104

Page 107: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

restante desta seção 3.3, que os checkpoints são por linhas. As mesmas análises e

tratamentos podem ser simplesmente transpostos para checkpoints por colunas e por

diagonais.

3.3.1 Algoritmo A: 2-Níveis com Partição Fixa

Para o algoritmo com 2-níveis de checkpoints com partição fixa de memória o uso de R

linhas de memória disponível pode ser otimizado fazendo-se 2RRR tbcp == .

Como o procedimento de retrocedimento só pode ser iniciado após o cálculo do último

valor (última célula) da matriz de PD, o valor da célula PDn,m, então os resultados da

computação da primeira seção da matriz de PD, Rtb, podem ser descartados, com

exceção da última linha, pois eles podem ser recalculados das condições iniciais. A

última linha precisa ser armazenada (salva) como um checkpoint. As próximas Rtb

linhas são computadas (calculadas) e a última linha deste segmento deve ser salva como

um outro checkpoint, descartando-se as demais linhas, pois elas podem ser re-

computadas do primeiro checkpoint salvo. Esse processo é repetido até que a última

linha da matriz de PD seja calculada. A última seção da matriz de PD, a que contém a

última linha, não é descartada, ela é usada para iniciar o processo de retrocedimento

sobre a matriz.

O processo de retrocedimento inicia-se na última seção da matriz de PD que foi

computada e salva. Os resultados da seção anterior são, então, re-computados a partir do

penúltimo checkpoint salvo e o procedimento de retrocedimento é, então, executado

nessa seção. Re-computa-se, então, os resultados da seção anterior a partir do

checkpoint prévio e executa-se o procedimento de retrocedimento nessa seção. Esse

processo é repetido até que a primeira seção da matriz de PD seja alcançada e o

procedimento de retrocedimento executado nessa primeira seção, completando-se,

portanto, o retrocedimento sobre a matriz de PD.

A análise da performance deste algoritmo é simples de ser feita. Em primeiro lugar,

considerando a suposição assumida de que se nm = , então o maior número de linhas

que o algoritmo de 2-níveis com partição fixa pode calcular com R linhas é

105

Page 108: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( )1+= cptb RRn linhas. Entretanto supondo o particionamento ótimo 2RRR tbcp == ,

então o número de linhas da matriz de PD que pode ser computado com R checkpoints é

dado por ( )24

122

12 RRRRRR cptb +=

+=+=n . Desta forma, tem-se que

nRn 2≤⇒RRn24

2

≤⇒≥ , de onde pode-se concluir que o algoritmo de 2-níveis

com partição fixa tem complexidade de espaço da ordem de ( )nnO .

n2

Com relação à complexidade de tempo do algoritmo de 2-níveis com partição fixa tem-

se que são calculadas n2 células da matriz de PD numa execução forward do algoritmo

de PD, mais nn2− células no processo de retrocedimento, mais 2n ou n2 células,

dependendo se o retrocedimento é parcial ou completo. Desta forma tem-se que para o

retrocedimento parcial são computadas nnnnnnnn 22222 222 +−=+−+n células

da matriz de PD, contra do algoritmo básico, e para o retrocedimento completo nn 22 +

nnnnnn 32 222 =+−+ nn 22 − células, contra 2 do algoritmo básico. Portanto,

considerando que se gaste um tempo unitário para calcular cada célula da matriz de PD,

tem-se que o algoritmo de 2-níveis com partição fixa tem complexidade de tempo da

ordem de

2n

( )2nO com queda de desempenho de nnn 22 − , ou seja, da ordem de ( )2nO .

O fator de queda de desempenho é dado por 10 ,2 ≤< kk .

3.3.2 Algoritmo B: 2-Níveis com Partição Móvel

O algoritmo de 2-níveis com partição fixa de memória não usa de forma eficiente as R

linhas de memória disponível. A divisão entre memória de armazenagem de

checkpoints, Rcp, e memória de trabalho, Rtb, pode ser móvel. Na primeira fase de

computação da matriz de PD para frente as linhas de memória Rcp que não estão

armazenando checkpoints podem ser usadas para o cálculo de seções da matriz de PD,

como memória de trabalho, Rtb. Então, quando é necessária mais memória Rcp a divisão

move na outra direção aumentando a memória Rcp e diminuindo a memória Rtb. Na fase

de retrocedimento o processo de movimento de memória é invertido; a divisão de

106

Page 109: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

memória retrocede diminuindo a memória Rcp após os checkpoints terem sido usados e

aumentando a memória Rtb.

Numa primeira etapa, o algoritmo de 2-níveis com partição móvel de memória calcula

as primeiras R linhas da matriz de PD, mas somente a R-ésima linha é salva e as demais

são descartadas. Numa segunda etapa o algoritmo calcula as próximas R-1 linhas da

matriz de PD, mas somente a (R-1)-ésima linha é salva e as demais são descartadas.

Esse procedimento é repetido até que a linha final seja calculada e armazenada, e a fase

de retrocedimento pode ser, então, iniciada. Durante a fase de retrocedimento seções da

matriz de PD são recalculadas para frente, desde o checkpoint prévio. O retrocedimento

continua através de cada seção até que o checkpoint para aquela seção seja alcançado e

as informações desejadas são, então, armazenadas. Remove-se a seção atual da memória

e a próxima seção é, então, recalculada usando agora a nova quantidade de memória

disponível.

A análise de desempenho deste algoritmo é análoga ao algoritmo de 2-níveis com

partição fixa de memória. O algoritmo de 2-níveis com partição móvel de memória pode

computar com R linhas de memória disponível, uma matriz de PD de dimensões ,

onde o número n de linhas é dado por (Grice, 1997):

nn×

( )22

1 2

1

RRRRinR

i

+=

+== ∑

=

(3.1)

Da equação (3.1) tem-se que: nnRR 222

2

≤≤⇒≥n . Logo pode-se concluir que o

algoritmo de 2-níveis com partição móvel tem complexidade de espaço da ordem de

( )nnO , entretanto faz uso mais efetivo de sua memória disponível R.

Com relação à complexidade de tempo do algoritmo de 2-níveis com partição móvel,

tem-se que: são calculadas n2 células da matriz de PD numa execução forward do

algoritmo de PD, mais nnn −2 células no processo de retrocedimento, mais 2n ou n2

células, dependendo se o retrocedimento é parcial ou completo. Desta forma tem-se que

107

Page 110: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

para o retrocedimento parcial são computadas nnnnnnnnn 222 222 +−=+−+

células da matriz de PD, contra do algoritmo básico, e para o retrocedimento

completo

nn 22 +

nnnnnn =+−+ 222 nn −23 células, contra do algoritmo básico.

Portanto, considerando que se gaste um tempo unitário para calcular cada célula da

matriz de PD, tem-se que o algoritmo de 2-níveis com partição móvel tem

complexidade de tempo da ordem de

22n

( )2nO com queda de desempenho de nnn −2 ,

ou seja, da ordem de ( )2nO . O fator de queda de desempenho é dado por . 10 ≤< k ,2k

)(6

2 R +

1ii

R

i∑

=

3.3.3 Algoritmo C: 3-Níveis com Partição Móvel

Uma versão 3-níveis do algoritmo B pode reduzir o uso de memória ainda mais, ou seja,

com a mesma quantidade de memória usada pelo algoritmo B pode-se calcular matrizes

de PD ainda maiores com uma versão de 3-níveis de checkpoints com partição móvel de

memória. Esse algoritmo é um algoritmo de 3-níveis de checkpoints: computação e

armazenagem de checkpoints de nível-3; computação e armazenagem provisória de

checkpoints de nível-2 (algoritmo B); e a computação e armazenagem provisória de

checkpoints de nível-1 (algoritmo de PD básico). O algoritmo C chama inicialmente o

algoritmo B para calcular um número de linhas suficiente para encher a memória

disponível R e armazena a última linha como o primeiro checkpoint de nível-3. Então,

de forma similar ao algoritmo B, após armazenar um checkpoint de nível-3, o algoritmo

B é chamado para calcular tantas linhas quanto forem possíveis antes do próximo

checkpoint de nível-3 ser armazenado.

Quando o algoritmo C está enchendo a memória de checkpoints, ele chama o algoritmo

B com uma variação de espaços de R até 1. Desta forma o número máximo de linhas

que o algoritmo C pode computar com R linhas de checkpoints é dado por:

( ) ( ) ( )1121

21

1

RRiinR

i

+=+=

+= ∑

=

(3.2)

108

Page 111: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Da equação (3.2) tem-se que: 333

366

nnRR≤≤⇒≥n . Logo, pode-se concluir que o

algoritmo de 3-níveis com partição móvel tem complexidade de espaço da ordem de

( )3 nnO , fazendo uso mais efetivo de sua memória disponível R.

Quanto à complexidade de tempo do algoritmo de 3-níveis com partição móvel, tem-se

que: são calculadas n2 células da matriz de PD numa execução forward do algoritmo de

PD, mais 322 nnn − células no processo de retrocedimento, e mais 2n ou n2 células,

dependendo se o retrocedimento é parcial ou completo. Desta forma tem-se que para o

retrocedimento parcial são computadas nnnnnnnnn 2322 32322 +−=+−+ células

da matriz de PD, contra do algoritmo básico, e para o retrocedimento completo nn 22 +

32 nnn −2322 42 nnnnn =+−+ células, contra 2 do algoritmo básico. Portanto,

considerando que se gaste um tempo unitário para calcular cada célula da matriz de PD,

tem-se que o algoritmo de 3-níveis com partição móvel tem complexidade de tempo da

ordem de

2n

( )2nO com queda de desempenho de 32 n−2 nn , ou seja, da ordem de ( )2nO .

O fator de queda de desempenho é dado por 3 10 , ≤< kk .

3.3.4 Algoritmo D: L-Níveis com Partição Móvel

Nessa seção considera-se um algoritmo de L-níveis com particionamento de memória

móvel, ou seja, um algoritmo com nível de recursão arbitrário e particionamento móvel

de memória. O número máximo de linhas, ( )RrL , que podem ser computadas com o

algoritmo de L-níveis com partição móvel de memória para uma determinada

quantidade de memória disponível de R checkpoints é dado por:

( ) (∑=

−=R

iLL irRr

11 ) . (3.3)

Assumindo que r , essa recorrência pode ser resolvido por: ( ) RR =1

109

Page 112: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( ) ( )( )

( ) ( ) ( )( ) 12...1

1...21!!1!11

×××−××+××−+×−+

=−

−+=

−+=

LLRRLRLR

LRLR

LLR

RrL . (3.4)

Da equação (3.4) pode-se derivar, de forma geral, a seguinte desigualdade:

( ) L

LL

L LR

LRRr ≥≥

!. Logo pode-se concluir que o número de linhas que podem ser

calculadas pelo algoritmo de L-níveis com partição móvel de memória é da ordem de

( )LL LRO linhas, com complexidade assintótica de tempo da ordem de O . Com a

suposição de que e tomando o número de checkpoints

( mrL )nm = nrL = , tem-se que a

complexidade assintótica de tempo é da ordem de ( )2nO e a de espaço é da ordem de

( )L nnO . A queda de desempenho, que se paga em troca da redução do espaço de

memória é dado por . 1≤< k0 ,kL

Mas se for considerado valores pequenos para L, tem-se da equação (3.4), geralmente,

que: ( ) L

LL

L LR

LRRr ≥≥

!. Produzindo um comportamento assintótico de tempo ( )2nO

com uma queda de desempenho proporcional a L e um comportamento assintótico de

requerimento de espaço ( )L nnO .

De forma semelhante pode-se derivar da equação (3.4), para R pequeno, a seguinte

desigualdade: ( ) R

RR

L RL

RLRr ≥≥

!. Ou seja, para R pequeno tem-se, geralmente, que:

( ) ( )RRL RLORr = . Logo, existe um algoritmo com requerimento de espaço O e

com uma penalidade na performance de

(Rn)

( )R nO .

Assim, a equação (3.4) para fornece um quadro de pontos de desenho de projeto

no tradeoff tempo-espaço. Embora seja possível explorar várias alternativas no tradeoff

espaço-tempo, geralmente a melhor escolha é usar toda a memória disponível e

encontrar o nível mais baixo de checkpoints que pode ser usado para retroceder sobre

uma matriz de PD de dimensões n

( )RrL

nnm ×=× , ou seja, que acomoda o tamanho da

maior seqüência, de tamanho n. Desta forma, o nível ótimo é dado por:

110

Page 113: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

−+≤=

iiR

niL1

:min . (3.5)

Usando um método de busca exponencial, L pode ser encontrado em tempo

, para maiores detalhes veja em (Wheeler e Hughey, 2000). ( nO loglog )

Para um dos extremos tem-se com L = 1 o algoritmo de PD básico, que requer R = n

linhas de checkpoints para computar e armazenar a matriz de PD. Esse algoritmo de PD

padrão tem complexidade assintótica de espaço da ordem de ( )2nO e de tempo da

ordem de ( )2nO .

No outro extremo tem-se L = n, que requer um número mínimo de checkpoints (R = 2

linhas) para computar durante a fase de retrocedimento n seções da matriz de PD,

repetindo a computação para frente (forward) da primeira célula da matriz de PD, a

célula PD0,0, até cada uma das linhas. Quer dizer, uma matriz de dimensões arbitrárias

com linhas pode ser computada com apenas 2 linhas de memória e, em

seguida pode ser efetuado um procedimento de retrocedimento sobre a matriz com essas

2 linhas de memória. Assim, tem-se células na fase para frente mais

células na fase de retrocedimento parcial ou na fase de retrocedimento

completo, produzindo um total de ou células, respectivamente. Esse

algoritmo de PD com n-níveis de checkpoints tem, portanto, complexidade assintótica

de espaço da ordem de e complexidade de tempo limitada por

( ) 12 += nrn

mn2

n2+

nmn 22 +22 nmn +

232 nn +n2 3

( )nO ( ) ( )3nO=2mnO ,

com um coeficiente igual a 2. Essa instância pode ser tomada para a estimativa de pior

caso.

Se forem usados níveis de checkpoints, o algoritmo terá complexidade de tempo nblog

( )2nO , com queda de desempenho dada por um fator de e complexidade de

espaço .

nblog~

( )nnO blog

111

Page 114: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

3.3.5 Algoritmo E: Viterbi com L-Níveis de Checkpoints por Diagonais com

Retrocedimento Restrito

Se o interesse reside somente no melhor caminho através da matriz de PD a

performance do algoritmo de PD com checkpoints e partição móvel de memória pode

ser melhorada assumindo checkpoints por diagonais e aplicando a esse o principio

D&C, pois o melhor caminho passa, somente, através de uma região limitada da matriz

de PD entre duas diagonais quaisquer. Esse algoritmo gasta menos tempo e memória

para computar o melhor caminho e é referido na literatura como algoritmo de PD de L-

níveis por diagonais com retrocedimento restrito. O retrocedimento restrito também

pode ser considerado nas versões por linhas e por colunas.

O uso de checkpoints por diagonais, ao invés de por linhas ou colunas, acarreta duas

ineficiências com relação ao requerimento de espaço, a saber. A primeira refere-se à

necessidade de um número maior de posições de checkpoints, pois existem nm +

diagonais, ao invés de m linhas ou n colunas. A segunda refere-se à quantidade de

informações do estado da computação que devem ser armazenados como checkpoints,

pois a computação de uma diagonal requer valores das duas diagonais anteriores, logo

para cada checkpoint devem ser armazenadas 2 diagonais da matriz de PD. Mesmo

assim, esse algoritmo mantém o comportamento assintótico de espaço e de tempo da

versão por linha (Grice et al., 1997).

O tempo para calcular o melhor caminho através da matriz de PD é a soma do tempo na

primeira fase de computações para frente, na computação da matriz de PD, mais o

tempo gasto na segunda fase de computações para traz, no retrocedimento. Usando-se, o

paradigma de checkpoints, com checkpoints por diagonais, gasta-se a princípio um

tempo n2 na primeira fase. Mas, na segunda fase pode-se economizar tempo de re-

computação aplicando o princípio D&C, pois não é necessário re-computar toda a

subseção entre dois checkpoints consecutivos.

Supondo-se em primeiro lugar que exista uma relação um a um entre os pontos de uma

grade ou treliça e as células da matriz de PD, em segundo lugar que o espaçamento ou

112

Page 115: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

distância entre dois checkpoints, por diagonais, sucessivos seja c, e finalmente, em

terceiro lugar se o melhor caminho passa através do ponto ( )ji, pertencente ao

checkpoint k, na diagonal jidk += , então o melhor caminho precisa alcançar a

diagonal entre os pontos cjidk −+=−1 ( )cji −, e ( )jci ,− .

Desta forma, é necessário re-computar, usando o algoritmo de nível L-1, somente uma

região triangular limitada por , ( )ji, ( )cji −, e ( )jci ,− , entre os checkpoints dk e dk-1,

contendo 22c células da matriz de PD. Assim, seguindo o desenvolvimento

apresentado em Grice et al. (1997), então existem LLL nnnm 22 ≤=+ regiões

contendo 22c células da matriz de PD, que necessitam ser re-computadas. É necessário

observar que no algoritmo com partição fixa de memória, LL

Ln

nnc

1

22 −

== para todas as

regiões, e no com partição móvel de memória, c varia de 1 até LL

n1−

, sucessivamente,

para cada região. Assumindo-se, então, partição fixa de memória e considerando que

para um nível arbitrário L , existem L n2 checkpoints com um espaçamento entre eles

de LL

n1−

, então na fase de retrocedimento são re-computadas L n2 seções de dimensões

×

−−L

LL

L

nn11

21

L

da matriz de PD usando o algoritmo de nível L-1. Logo, a estimativa do

tempo total, T , gasto pelo algoritmo com L-níveis de checkpoints por diagonais e

retrocedimento restrito para calcular o melhor caminho pode ser determinado em termos

de complexidade assintótica conforme mostra a desigualdade (3.6), dada a seguir.

( )n

( )

+≤

+≤

−L

L

LLL

L

LL

L nTnnnTnnnT1

12

1

12

212 (3.6)

A partir da equação (3.6) pode-se mostrar por indução que o tempo total, T , gasto

pelo algoritmo com L-níveis de checkpoints por diagonais e retrocedimento restrito para

( )nL

113

Page 116: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

calcular o melhor caminho tem complexidade assintótica ( )2nO , quando ,

onde . Veja a prova nas equações a seguir.

nL log≤

nn bloglog =

( )( ) ( )

( )

( )( )

( )

log

log

2

2

b

T

nT

n

T

nO

n

n

3

nblog

( ) ( )( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( )( ) ( ) ( )

( ) ( ) ( )( ) ( )

( ) ( )2log

12

1log2

1loglogn2

log

221log

52

452

5

24

23

424

222

22

32

222

12

2

21

21

bn

quemostrar se-deve , que se-Supondo

log1log

57

58

59

54

46

47

43

35

32

32

31

32

21

21

21

nOnT

nnbnTnnTnnT

nOnOnT

OnTnnnnnnTnnnT

nOnTnnnnnTnnnT

nOnnnnnnnnnTnnnT

nTnnnnnnTnnnTnOnTnnT

n

nnn

n

nn

=∴

+≤

+=

+≤

==

=∴++++=+≤

=∴+++=+≤

=++=

++=+≤

=∴+=×+=+≤=∴=

−−

−−

M

3

(3.7)

Pode-se perceber da equação em (3.6) que, numa análise a priori, assintoticamente não

existe penalidade de tempo, para níveis de checkpoints L ≤ , quando calculando o

melhor caminho, usando retrocedimento restrito. Entretanto essa análise descarta os

coeficientes multiplicativos e quando se deseja comparar algoritmos da mesma ordem

de complexidade é necessário elucidar a dimensão desta penalidade, levando-se em

consideração esses coeficientes. Desta forma pode-se estabelecer um fator de queda de

desempenho com base nesses coeficientes.

Pode ser deduzido das 5 primeiras equações em (3.7) que a desigualdade em (3.6) tem

equação de recorrência, dada a seguir em (3.8), composta de L fatores no lado direito,

que incluem necessariamente o primeiro e o último fator.

( ) nnnnnnT LL

LL

LL

L +++++≤+−− 222122 L (3.8)

Fazendo em (3.8) e considerando logaritmos de base 2, bastante usados em

biologia computacional, e logaritmos de base 10 e 100, obtém-se as desigualdades em

(3.9), dada a seguir.

nL blog=

114

Page 117: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( )( ) ( ) ( ) ( ) ( )

( )

( )

( ) nnnnnnnnT

nnnnnnnnT

nnnnnnnnT

nnnnnnnnT

LLn

LLn

LLn

nnb

nbnb

nbnb

nbnb

nbnb

nb

b

+++++++≤

+++++++≤

+++++++≤

+++++++≤

−−

−−

−−

++−−−

2

2

3

2

3

2

2

222

log

2

2

3

2

3

2

2

222

log

2

2

3

2

3

2

2

222

log

2log

100100100100100

:se- tem100, base de logaritmos se-doConsideran1010101010

:se- tem10, base de logaritmos se-doConsideran22222

:se- tem2, base de logaritmos se-doConsideran

100

10

2

log2log

log3log

log3log2

log2log2

log1log2

L

L

L

L

(3.9)

Se for considerado em (3.9) somente a equação para logaritmos de base 2, pode ser

mostrado que a soma dos coeficientes dos termos de segundo grau, n , tende a 2

quando n cresce. De onde se pode concluir que a queda de desempenho para o nível

, quando calculando o melhor caminho com o algoritmo de PD com L-níveis

de checkpoints por diagonais e retrocedimento restrito, tem penalidade de tempo dada

por um fator ~2 vezes.

2

nL 2log=

Se for considerado em (3.9) somente a equação para logaritmos de base 10, pode ser

mostrado que a soma dos coeficientes dos termos de segundo grau, , tende a

1,111111... quando n cresce. De onde se pode concluir que a queda de desempenho para

o nível , quando calculando o melhor caminho com o algoritmo de PD com

L-níveis de checkpoints por diagonais e retrocedimento restrito, tem penalidade de

tempo dada por um fator ~1,111111... vezes.

2n

nL 10log=

Se for considerado em (3.9) somente a equação para logaritmos de base 100, pode ser

mostrado que a soma dos coeficientes dos termos de segundo grau, , tende a

1,0111111... quando n cresce. De onde se pode concluir que a queda de desempenho

para o nível , quando calculando o melhor caminho com o algoritmo de PD

com L-níveis de checkpoints por diagonais e retrocedimento restrito, tem penalidade de

tempo dada por um fator ~1,0111111... vezes.

2n

nL 100log=

Para seqüências de tamanhos arbitrários, especialmente para n grande, pode ser

observado, com relação ao nível de checkpoint , que nL bnbloglog =

115

Page 118: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

nnn LLL210100 logloglog <<

Rlog

, o que implica em requerimentos de memória para esses níveis

de checkpoints . Por exemplo, para n , tem-se nnn RR210100 loglog >> 000.10=

( ) ( ) ( )1442log2

10100 loglog =< nL=<= nn LL e ( ) ( ) ( )7200210100 loglog =25 log>=>= nnn RR

2log=

n

( )nblog

R

L

L blog≤

.

nL 2log=

,log≤ nL b ,100 com ≥b

Desta forma pode ser notado das equações em (3.7), (3.8) e (3.9) e das observações

feitas nos parágrafos anteriores que os níveis de checkpoints dados por

constituem instâncias do problema que podem ser uma estimativa razoável para o

comportamento assintótico de pior caso destes algoritmos de PD com L-níveis de

checkpoints por diagonais e retrocedimento restrito dados por . Pois

fornece níveis de checkpoints muito altos contra requisições de memória

muito baixas. Por outro lado, se forem considerados níveis de checkpoints dados por

algoritmos de bases grandes (≥100), podem ser obtidas instâncias do problema que

constituem uma estimativa razoável do comportamento assintótico de melhor caso, pois

quando obtém-se níveis de checkpoints muito baixos contra

requisições de memória muito altas.

n

Isto está de acordo com o comportamento desta classe de algoritmos com checkpoints,

que é: aumentando-se a memória e diminuindo-se o nível provoca-se uma aceleração no

desempenho destes algoritmos com checkpoints; pode-se trocar espaço por ganho de

desempenho e vice-versa. Pode ser notado das equações em (3.7) e (3.9) que a queda de

desempenho aumenta proporcionalmente ao nível L (diretamente) e à quantidade de

memória R (inversamente).

Do exposto nos parágrafos anteriores pode-se concluir que o fator de queda de

desempenho pode ser de até 2 vezes, para o algoritmo de checkpoints por diagonais com

retrocedimento restrito (D&C), quando o número de níveis for da ordem O e

desde que haja memória suficiente para ser utilizada para esse nível e tamanho da matriz

de PD. O que se não contraria, pelo menos ameniza ou coloca em cheque a conclusão

relatada em Grice et al. (1997, p. 51), também baseada na equação (3.6) através de uma

prova por indução, que não está documentada, mas ao que tudo indica semelhante

116

Page 119: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

àquela em (3.7): “Thus when looking for the single best path, there is no time penalty

with the checkpoint method when the number of levels is ( )nO log ”. Deve ser ressaltado

que está conclusão foi assumida nos trabalhos posteriores relatados por Tarnas e

Hughey (1998) e Wheeler e Hughey (2000), que integram o estado da arte com respeito

a essa família de algoritmos de PD com L-níveis de checkpoints.

) 2n=

)

O algoritmo com L-níveis de checkpoints por diagonais com retrocedimento restrito

para calcular o melhor caminho apresenta comportamento de requerimento de espaço

semelhante aos demais membros desta família de algoritmos. Seja o espaço

necessário para armazenar checkpoints de nível L suficientes para que se faça o

retrocedimento sobre a matriz de PD contendo aproximadamente 2n diagonais. O

requerimento de espaço do algoritmo de nível 1é

( )nSL

(1 nS . O algoritmo de nível 2

necessita de espaço para nn8 locações de memória e espaço para computar um bloco

nn × usando o nível imediatamente mais baixo: ( ( )n =nS = 82

∴ ( ) ( )nnOnS =2 . O algoritmo de nível 3 necessita de espaço para 3 nn12 locações de

memória e espaço para computar um bloco 3 n3 usando o nível imediatamente

mais baixo:

n ×

( ) ( )32

33 12 nSnnnS += ∴ ( ) ( )3

3 nnOnS = . Isto pode ser escrito como

uma recorrência dada pela desigualdade a seguir.

nnnSnn ++ 81

( ) ( )LL

nSnLnnS LL

L

1

14−

−+≤ (3.10)

Da recorrência dada pela desigualdade (3.10) pode ser mostrado por indução para

que

1≥L

( ) ( )LL nnOnS = .

Apesar de tudo, o algoritmo de L-níveis para calcular o melhor caminho apresenta

redução no requerimento de espaço quando se toma nL blog= níveis. Nesse caso tem-

se que ( ) ( )nnOn bn loglog =S e não, necessariamente, de O(n) como o apresentado em

Grice et al. (1997).

117

Page 120: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Segundo Grice et al. (1997), esse algoritmo de PD com nL log= níveis de checkpoints

por diagonais, combinado com o princípio D&C, iguala a performance assintótica do

algoritmo de Hirschberg, de requerimento de espaço de ( )nO e de tempo de execução

de ( )2nO , para calcular o melhor caminho através da matriz de PD. Mas como foi visto

nessa seção os requerimentos de espaço são maiores, são da ordem de O . ( )nn blog

3.3.6 Disposição Ótima de Checkpoints por Linhas (Colunas) e o Algoritmo

Melhorado

O algoritmo de PD de L-níveis com checkpoints pode calcular uma matriz de PD com

um número máximo de linhas dado por: , desde que

. Para

−+== ∑

=− L

LRirRr

R

iLL

1)()(

11

( ) RRr =1 )(Rrn L= o algoritmo utiliza todo o espaço reservado de memória, R,

com uma disposição ótima dos checkpoints. Entretanto, para não é

possível calcular a matriz de PD com o algoritmo de nível L-1 e há um desperdício de

espaço reservado e perda de performance quando se usa um algoritmo de nível L. A

maior ineficiência do algoritmo original de nível L acontece quando

)(RrL

(1

)(1 nRrL <<−

1) +=n −rL R .

Essa descontinuidade ou perda de performance por um fator de (~ −LL )1 se deve

principalmente a uma má disposição dos checkpoints.

A solução proposta por Wheeler e Hughey (2000) explora a disposição ou localização

dos checkpoints como a solução para essa perda de performance por um fator de

( 1~ −LL ) . Os algoritmos de PD padrão e aqueles que fazem uso do paradigma de

checkpoints usam de forma eficiente a memória cache, pois eles caminham de maneira

regular através de sucessivas células da matriz de PD de tal forma, que os elementos

das linhas de cache sejam usados de maneira regular e sucessiva. Portanto a diferença

entre ajustar na memória e ajustar na cache pode ser insignificante.

Logo, o custo de estratégias diferentes de disposição dos checkpoints pode ser dado em

termos do número de células da matriz de PD que necessitam ser computadas ou re-

computadas. A computação de células da matriz de PD domina as diferenças entre os

118

Page 121: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

custos de computação para as diferentes estratégias de disposição de checkpoints, desde

que o algoritmo tenha requerimentos de memória suficientemente baixos para evitar o

risco de swap da memória virtual para o disco.

O número de re-computações para cada nível i de checkpoints decresce a medida que i

cresce de 1 até L. Desta forma, pode ser estabelecido que a quantidade de computações

de linhas é dada por (3.11), adicionando-se 1 para corrigir o termo (para maiores

detalhes veja Wheeler e Hughey (2000)).

1=i

( ) ( )

+

−+++=

−+×+= ∑

= 1111

21

1 LLR

RLL

iiR

iRtL

iL (3.11)

3.3.6.1 Algoritmo Melhorado

Wheeler e Hughey (2000) apresentaram para r )()(1 RrnR LL <<− um algoritmo

denominado algoritmo melhorado, com ( )LL ,1− -níveis de checkpoints, que pode

melhorar muito a situação de perda de performance. Esse algoritmo melhorado executa

o algoritmo de ( 1)−L -níveis até que as primeiras ρ linhas da memória reservada para

checkpoints sejam preenchidas, tal que as linhas restantes de checkpoints sejam

suficientes para computar o restante da matriz de PD, usando o algoritmo de L-níveis.

De forma equivalente usa-se o algoritmo de ( )1−L -níveis para as primeiras ρ linhas de

memória tal que e o algoritmo de L-níveis para as últimas ( )ρrL−1 ( ) nρRrL >−+ ρ−R

linhas de memória.

A quantidade de computações de linhas efetuadas pelo algoritmo melhorado, de

-níveis de checkpoints, é dada por (3.12). O ρ ótimo pode ser encontrado em

tempo da ordem de O

( LL ,1− ))( nR loglog × , usando-se um algoritmo de busca com

complexidade exponencial (Wheeler e Hughey, 2000).

( ) ( ) ( )ρρ −+= −− RttRt LLLL 1,1 (3.12)

119

Page 122: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

3.3.6.2 Algoritmo Ótimo

Entretanto os algoritmos melhorados, com ( )LL ,1− -níveis de checkpoints não são

ótimos. O algoritmo ótimo transita livremente entre níveis para fazer uso ótimo da

memória disponível para qualquer subproblema. Para estudar a disposição ótima dos

checkpoints Wheeler e Hughey (2000) utilizaram um algoritmo de programação

dinâmica de ordem O(n3).

Dadas as seqüências m e n, respectivamente, a maior e a menor seqüência, e a

quantidade de memória disponível R. Seja ( )nRC , a quantidade mínima de

computações de linhas. Então, para Rn ≤ tem-se que ( ) nnRC =, linhas e para os

demais casos, quando , a quantidade mínima de computação de linhas, , é

dado pela equação em (3.12), onde, o último termo i é o custo de computar até a linha i,

o termo C é o custo de retroceder da linha i até o início da seqüência e, o termo

é o custo de computar o restante da seqüência, após a linha i.

Rn > ( nRC , )

( ) 1, −iR

)in −,(RC −1

( ) ( ) ( ) iiRCinRCnRCni

+−+−−=≤<

1,,1min,0

(3.12)

Para a quantidade mínima de computação de linhas é dada pela equação em

(3.11),

( )Rrn L=

( ) ( )RtL=nRC , , ou seja, ( ) ( )LnOnRC =, linhas. Para pode-

se estabelecer, também usando a equação em (3.11), que

)()(1 RrnRr LL <<−

( ) ( )RtnR L 1, +C < , novamente

tem-se que ( ) ( ) ( )nO

( )n

LnO =

( ) OLnO =

nR =,

)( )n =1

C

( ) (LOn +=

linhas. Pode-se concluir, desta forma, que

linhas. RC ,

Considerando que, assintoticamente, o algoritmo melhorado com ( )LL ,1− -níveis de

checkpoints computa pelo menos ( )[ ]11 +− nL linhas e, que o algoritmo ótimo obtém

uma melhoria de no mínimo 1−L n linhas iniciais com relação ao algoritmo melhorado,

usando no lugar do algoritmo de nível L-1 pelo menos o algoritmo de nível 1. Desta

forma, assintoticamente, o algoritmo ótimo com ( )K,2−,1, − LLL -níveis de

checkpoints, computa pelo menos ( ) ( )[ ]12 −− L n1 −− LnL linhas.

120

Page 123: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Portanto, a razão da performance entre o algoritmo melhorado e o algoritmo ótimo é

pelo menos de ( )( ) ( ) 121

1−−−−

−L nLnL

nL , ou seja, é de ordem ( )1O . A razão da performance

entre o algoritmo original de L-níveis e o algoritmo ótimo, considerando que o

algoritmo de L-níveis computa Ln linhas, é pelo menos de ( ) ( ) 121 −−−− L nLnL

Ln , ou

seja, é de ordem

−1LLO .

3.3.7 Disposição Ótima de Checkpoints por Diagonais e o Algoritmo de Viterbi

Melhorado

A abordagem adotada por Wheeler e Hughey (2000) para melhorar a performance do

algoritmo com L-níveis de checkpoints por diagonais é similar àquela adotada para

checkpoints por linhas (colunas). Recorre-se à técnica de PD para determinar a

disposição ótima de checkpoints e tenta-se descobrir um algoritmo melhorado, mais

simples para uso geral.

Segundo Wheeler e Hughey (2000) para estudar a disposição ótima dos checkpoints por

diagonais é necessário um algoritmo de programação dinâmica de ordem O(n4). Apesar

de não apresentarem nada de concreto a respeito da modelagem do esquema de

disposição dos checkpoints, proporcionado (fornecido) por esse algoritmo, Wheeler e

Hughey (2000) relatam que essa modelagem é extremamente difícil e complicada. Mas

eles relatam que simplificaram o problema, considerando somente regiões triangulares

da matriz de PD, convenientes, para melhorar o algoritmo de Viterbi com checkpoints

por diagonais através dos resultados fornecidos e, produzindo um problema de PD de

O(n3). Entretanto novamente não apresentaram nem os resultados de experimentos ou

qualquer coisa de concreto a respeito desta modelagem.

Wheeler e Hughey (2000) relatam que o algoritmo melhorado com checkpoints por

diagonais, obtido da análise da disposição de checkpoints, tem desempenho pior que o

de seus competidores, o algoritmo de Powell (Powell et al., 1999). Entretanto eles não

121

Page 124: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

descrevem esse algoritmo melhorado e sugerem que esse pode ser melhorado adotando

uma estratégia já adotada por Powell et al. (1999): mantêm-se duas variáveis adicionais

para guardar a linha da qual cada caminho parte na coluna zero e guardar em qual linha

cada caminho entra na última coluna. Durante a fase de retrocedimento pode-se omitir a

computação da porção final da matriz de PD, aquela compreendida entre a última linha

e a linha onde o melhor caminho deixou a última coluna da matriz, bem como a porção

inicial da matriz de PD, aquela compreendida entre a linha onde o melhor caminho

atingiu a primeira coluna e a primeira linha da matriz.

122

Page 125: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

CAPÍTULO 4

ALGORITMOS PROPOSTOS

4.1 Introdução

No Capítulo 3 foram apresentados membros de uma família de métodos, que fazem uso

do método de PD e do paradigma (ou estratégia) de checkpoints, para comparação e

alinhamento de seqüências com economia de espaço – com necessidade reduzida de uso

de memória de trabalho – que podem ser usados com os algoritmos forward e backward

para determinar a P(O/λ), com o algoritmo de Baum-Welch (forward-backward) no

treinamento de perfis-HMMs e com o algoritmo de Viterbi para determinar o melhor

caminho. Essa família de métodos possibilita que o retrocedimento através da matriz de

PD possa ser efetuado, sem a necessidade de armazenar em memória toda a matriz de

PD, recorrendo-se ao uso de registro de informações do estado da computação a

intervalos regulares e apropriados, checkpoints, e de re-computação de seções da matriz

de PD. Eles requerem espaço da ordem de O( L nm ), permitindo uma economia de

espaço da ordem de ( )L n

,

O , na maioria das vezes, em troca de uma queda de

desempenho por um fator 10 ≤< kkL .

Nesse Capítulo são propostos algoritmos alternativos, denominados algoritmos com

checkpoints bidimensionais, que fazem uso da estratégia de checkpoints em conjunto

com o princípio D&C, através da disposição dos checkpoints em duas dimensões. A

disposição dos checkpoints em duas dimensões possibilita a utilização do princípio

D&C de maneira mais explicita e efetiva nesses algoritmos, sem a desvantagem da

recursividade presente no algoritmo de D&C de Hirschberg. Em contraposição ao

algoritmo de Hirschberg que é totalmente recursivo, os membros destes algoritmos

alternativos, propostos nesse trabalho, não recorrem a processos recursivos. Esses

algoritmos alternativos numa implementação serial mantêm o requerimento de memória

da ordem de O( L nm ) dos algoritmos que adotam o paradigma de checkpoints,

apresentados no Capítulo 3, com queda de desempenho por um fator proporcional a L.

123

Page 126: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Um dos membros desta família alternativa de algoritmos apropriado para determinar o

melhor caminho, denominado algoritmo de Viterbi com L-níveis de checkpoints

bidimensionais – por linhas e colunas simultaneamente – com retrocedimento restrito e

particionamento fixo de memória, apresenta uma performance superior àquela

apresentada pelo algoritmo de Viterbi com checkpoints por diagonais de L-níveis com

retrocedimento restrito e particionamento móvel de memória, numa análise de

desempenho a priori. Ele pode computar matrizes de PD maiores do que esse algoritmo

com checkpoints por diagonal e retrocedimento restrito, descrito no Capítulo 3, com

menor requerimento de memória, em troca de uma queda de desempenho, que em uma

análise a priori é menor e em uma análise posteriori é, também, suposta ser menor.

Esse algoritmo alternativo de Viterbi com checkpoints bidimensionais possibilita um

particionamento explicito da matriz de PD, o que significa condições de contorno bem

mais simples do que aquelas do algoritmo com checkpoints por diagonais. Desta forma

espera-se que a codificação deste algoritmo seja bem mais simples e que ele tenha um

melhor desempenho a posteriori.

Na Seção 4.2 são apresentados os membros da família de algoritmos de PD com L-

níveis de checkpoints que adotam uma disposição bidimensional destes checkpoints.

4.2 Algoritmos de L-Níveis de Checkpoints Bidimensionais

Nos algoritmos alternativos, propostos, denominados algoritmos com checkpoints

bidimensionais, foi adotada uma disposição dos checkpoints em duas dimensões, o que

possibilita a utilização do princípio D&C de maneira mais explicita e efetiva. Há várias

possibilidades de disposição dos checkpoints bidimensionais, nas duas dimensões da

matriz de PD. Eles podem ser dispostos nas duas dimensões na forma: linha × coluna;

diagonal × diagonal (ou anti-diagonal); linha × diagonal (ou anti-diagonal) e; coluna ×

diagonal (ou anti-diagonal). Em cada uma destas possibilidades pode-se imaginar várias

estratégias diferentes e promissoras. Nesse trabalho será explorado a disposição

bidimensional na forma de linha × coluna, pois essa evidencia, conforme pode ser

observado a seguir, ser a mais atraente e apresenta condições de contorno mais simples

para a programação do modelo.

124

Page 127: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Se for considerado apenas a disposição linha × coluna, pode-se dispor os checkpoints

conforme as figuras 4.1, 4.2, 4.3, 4.4, 4.5, 4.6 e 4.7. Pode-se imaginar algo semelhante

para as disposições diagonal × diagonal, linha × diagonal e coluna × diagonal. As 5

possibilidades apresentadas podem ser classificadas dentro de dois grupos. O primeiro

grupo refere-se às figuras 4.1, 4.2 e 4.3 e, o segundo grupo às figuras 4.4, 4.5, 4.6 e 4.7.

1 2 3 4 1 2 3 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 4 1 2 3 4 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 2 3 4 1 2 3 4 3 3 1 3 3 2 3 3 3 3 3 3 3 3 3 1 2 3 4 1 2 3 4 4 4 1 4 4 2 4 4 3 4 4 4 4 4 4 1 2 3 4 1 2 3 4 1 2 3 4

FIGURA 4.1 - Particionamento A: L-níveis de checkpoints completos e particionamento fixo de memória.

1 2 3 4 5 1 2 3 4 51 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 1 2 3 4 52 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 2 3 4 5 1 2 3 4 53 3 1 3 3 2 3 3 3 3 3 3 3 3 3 1 2 3 4 5 1 2 3 4 54 4 1 4 4 2 4 4 3 4 4 4 4 4 4 1 2 3 4 5 1 2 3 4 55 5 1 5 5 2 5 5 3 5 5 4 5 5 5

FIGURA 4.2 - Particionamento B: L-níveis de checkpoints completos e particionamento fixo de memória.

No primeiro grupo a matriz de PD é particionada em subseções por linhas × colunas

completas de checkpoints, criando partições mais homogêneas. Os particionamentos

bidimensionais das figuras 4.1 e 4.2 serão denominados particionamentos A e B – de L-

125

Page 128: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

níveis de checkpoints bidimensionais completos de linhas por colunas e partição fixa de

memória. O particionamento bidimensional da figura 4.3 será denominado

particionamento C – de L-níveis de checkpoints bidimensionais completos de linhas por

colunas e partição móvel de memória.

1 2 3 4 5 1 2 3 4 5 1 2 3 4 5 1 2 3 4 51 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 4 5 1 2 3 4 5 1 2 3 4 52 2 2 2 1 2 2 2 2 2 2 2 2 2 2 1 2 3 4 5 1 2 3 4 53 3 3 3 1 3 3 3 2 3 3 3 3 3 3 1 2 3 4 54 4 4 4 1 4 4 4 2 4 4 3 4 4 45 5 5 5 1 5 5 5 2 5 5 3 5 4 5

FIGURA 4.3 - Particionamento C: L-níveis de checkpoints completos e particionamento móvel de memória.

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 2 1 2 2 2 2 2 2 2 2 2 2 2 2 1 2 3 1 2 3 1 2 3 3 3 3 3 3 3 3 3 1 2 3 4 1 2 3 4 1 2 3 4 4 4 4 4 4 1 2 3 4 1 2 3 4 1 2 3 4 5

FIGURA 4.4 - Particionamento D: L-níveis de checkpoints parciais e particionamento fixo de memória.

No segundo grupo a matriz de PD é particionada em subseções por linhas × colunas

parciais de checkpoints, criando partições menos homogêneas, mas requerendo menos

espaço. Os particionamentos bidimensionais das figuras 4.4 e 4.6 serão denominados

particionamentos D e F – de L-níveis de checkpoints bidimensionais parciais de linhas

126

Page 129: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

por colunas e partição fixa de memória – e os particionamentos bidimensionais das

figuras 4.5 e 4.7 serão denominados particionamentos E e G – de L-níveis de

checkpoints bidimensionais parciais de linhas por colunas e partição móvel de memória.

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 2 1 2 1 2 2 2 2 2 2 2 2 2 2 1 2 3 1 2 3 1 2 3 3 3 3 3 3 1 2 3 4 1 2 3 4 4 4 1 2 3 4 5

FIGURA 4.5 - Particionamento E: L-níveis de checkpoints parciais e particionamento móvel de memória.

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 1 2 1 2 1 2 3 3 3 3 3 3 3 1 2 3 1 2 3 1 2 3 4 4 4 4 1 2 3 4 1 2 3 4 1 2 3 4 5

FIGURA 4.6 - Particionamento F: L-níveis de checkpoints parciais e particionamento fixo de memória.

A princípio a escolha será pelos algoritmos do primeiro grupo, pois apesar de

requererem mais espaço que os do segundo grupo, possuem menos condições de

contorno a serem consideradas na programação do modelo e possibilitam uma queda de

desempenho menor quando comparados aos algoritmos de PD básicos. Por outro lado

os algoritmos do segundo grupo possibilitam a computação de matrizes de PD maiores,

127

Page 130: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

usando praticamente a metade do espaço, do que aqueles algoritmos do primeiro grupo

podem computar usando o dobro de espaço.

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 2 1 2 1 2 3 3 3 3 1 2 3 1 2 3 4 4 1 2 3 4 5

FIGURA 4.7 - Particionamento G: L-níveis de checkpoints parciais e particionamento móvel de memória.

Os algoritmos do primeiro grupo requerem metade do espaço requerido pelo algoritmo

de Viterbi com L-níveis de checkpoints por diagonais com retrocedimento restrito e

particionamento móvel de memória e podem computar matrizes de dimensões duas

vezes maiores, para m , pois numa matriz n= nn × existem aproximadamente

diagonais. nnn 2=+

Outra questão a ser considerada é com relação ao particionamento da memória

disponível R entre memória para armazenar checkpoints e memória de trabalho. Os

algoritmos do primeiro grupo com L-níveis de checkpoints completos bidimensionais e

particionamento fixo de memória podem computar matrizes de PD até duas vezes

maiores do que os algoritmos com L-níveis de checkpoints completos bidimensionais e

particionamento móvel de memória. Adicionalmente, eles não apresentam o problema

de perda de performance devido a má disposição dos checkpoints apresentado pelos

algoritmos com L-níveis de checkpoints completos (bidimensionais ou unidimensionais)

e particionamento móvel de memória e, portanto não há necessidade do uso do

algoritmo melhorado de ( ) níveis,1 −− LL ou outro algoritmo melhorado.

128

Page 131: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

A memória disponível MEM pode ser particionada de maneira análoga àquela do

algoritmo de PD com L-níveis de checkpoints unidimensionais e partição fixa de

memória. Ela pode ser dividida em dois segmentos: um segmento denominado Rcp para

armazenar os checkpoints e um segmento denominado Rtb usado para o cálculo de

seções da matriz de PD. A memória de trabalho, Rtb, sempre será de tamanho suficiente

para comportar no mínimo duas linhas da matriz de PD e a memória de checkpoints

deverá ter no mínimo L n linhas, sendo que é conveniente que sejam em número par.

A memória de trabalho precisa ter no mínimo duas linhas, , pois é necessário

manter a linha da matriz de PD que é anterior àquela que está sendo computada, pois as

recorrências do algoritmo de PD precisam dos valores contidos em células da linha

anterior. Por motivos que ficarão explícitos nos parágrafos seguintes, as linhas de

memória de trabalho R

2≥tbR

tb devem ser suficientes para, na fase de retrocedimento,

armazenar os cálculos ou os checkpoints de nível L-1 da ultima seção da matriz de PD,

aquela localizada no canto inferior direito da matriz e que contém a célula PDn,m,

delimitada pelos checkpoints bidimensionais. Portanto, às vezes, pode ser necessário

mais do que duas linhas de memória de trabalho.

Desta forma se 2=tbR linhas, então 22 −≥ Lcp nR linhas, e se linhas então 4=tbR

4−≥ Lcp nR linhas. Deste ponto em diante, supondo m = n, será assumido que 2=tbR

linhas e 22 −L n=cpR linhas, sendo que as linhas de memória Rcp serão divididas

igualmente em memória de checkpoints verticais (linhas) e horizontais (colunas), que

serão indistintamente chamadas de Rbd, ou seja, 12

22

−−

==L

cpbd

nRR 2

= L n .

Nessa família de algoritmos de PD com checkpoints de L-níveis bidimensionais, nos

membros com partição fixa de memória, os checkpoints em ambas as dimensões são

igualmente espaçados, ou seja, dado Lbd nR = linhas ou colunas e supondo , tem-

se que

nm =

Lbd n

nR

nc =+

=1

.

129

Page 132: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

É importante lembrar, que linhas e que nesses algoritmos de PD com L-níveis

de checkpoints bidimensionais o procedimento de retrocedimento, também, só pode ser

iniciado após o cálculo do último valor (última célula) da matriz de PD, a célula PD

2=tbR

n,m.

4.2.1 Algoritmo F: 2-Níveis De Checkpoints Bidimensionais e Partição Fixa de

Memória

Para o algoritmo com 2-níveis de checkpoints bidimensionais e partição fixa de

memória o uso de R linhas de memória disponível pode ser otimizada fazendo-se

e 2=tbR 12

222

2−=

−=

−= nnRRbd . Entretanto . 962 ≥⇒≥⇒≥ nRRbd

Supondo , tem-se, então, que a distância entre dois checkpoints sucessivos é nm =

nn

=1

cc ,3,2

. Desta forma na primeira fase de computação da matriz de PD

para frente (forward) usam-se as duas linhas da memória de trabalho, , para

computar todas as linhas da matriz e salvam-se somente os valores das células

correspondentes às linhas ou colunas de checkpoints, a saber: as células pertencentes às

linhas 1 e as células pertencentes às colunas 1 .

2=tbR

cbdcRc bd,, K Rccc ,,3,2, K

nR

ncbd

=+

=

O processo de retrocedimento inicia-se na última seção da matriz de PD que contém a

célula PDn,n, aquela compreendida entre os checkpoints - localizados na linha

e na coluna - e a última linha e coluna da matriz. Na fase de retrocedimento

são usadas apenas as linhas da memória de trabalho,

cRbd

2

cRbd cRbd

=tbR , para re-computar todas as

linhas de cada seção, armazenar todos os resultados necessários e então executar o

procedimento de retrocedimento nessa seção.

Se o retrocedimento for completo, então, após o procedimento sobre a última seção, re-

computa-se os resultados da seção à esquerda a partir dos checkpoints (horizontal)

e (vertical) e executa-se o procedimento de retrocedimento nessa seção.

Repete-se esse processo até que a primeira coluna seja alcançada. Em seguida, computa-

cRbd

1−cRbd

130

Page 133: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

se as seções compreendidas entre os checkpoints horizontais e e executa-

se o procedimento de retrocedimento nessas seções, iniciando-se naquela seção

compreendida entre o checkpoint vertical e a última coluna terminando na seção

compreendida entre o checkpoint vertical c e a primeira coluna. Esse processo é

repetido até que a primeira seção da matriz de PD seja alcançada e o procedimento de

retrocedimento seja executado nessa primeira seção, completando-se, portanto, o

retrocedimento sobre a matriz de PD.

cRbd 1−cRbd

cRbd

R4

2

Se o retrocedimento for parcial, então, após o procedimento sobre a última seção, re-

computa-se os resultados da seção adjacente à linha ou coluna de checkpoint que o

alinhamento ótimo alcançou no procedimento de retrocedimento sobre a seção anterior

e, então, executa-se o procedimento de retrocedimento sobre essa seção. Procede-se de

maneira análoga para todas as seções adjacentes até que a primeira seção seja atingida,

aquela localizada no canto superior esquerdo, e seja executado o procedimento de

retrocedimento sobre ela, completando-se, assim, o retrocedimento parcial sobre a

matriz de PD.

Considerando a suposição já assumida de que m n= , então o maior número de linhas

que o algoritmo de 2-níveis com partição fixa pode calcular com R linhas é

( ) ( )42

122

2 RRRRrn bdL =

=+== linhas.

A análise da performance deste algoritmo é simples de ser feita. Seja n= , então

tem-se nR 2= checkpoints. De onde, pode-se concluir que o algoritmo de 2-níveis

com partição fixa tem complexidade de espaço da ordem de ( )nnO .

Com relação à complexidade de tempo do algoritmo de 2-níveis com partição fixa tem-

se que são calculadas n2 células da matriz de PD numa execução para frente (forward)

do algoritmo de PD. Se o processo de retrocedimento é completo, então são computadas

( ) nnnnnnnn 232 2222 −=+−+ células, contra do algoritmo básico. Se o 22n

131

Page 134: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

retrocedimento é parcial, então são computadas no máximo

( ) nnnnnnnn 2222 222 ++=+

+ células da matriz de PD, contra do

algoritmo básico.

nn 22 +

( )2n n

n

Desta forma, considerando que se gaste um tempo unitário para calcular cada célula da

matriz de PD, tem-se que o algoritmo de 2-níveis com partição fixa tem complexidade

de tempo da ordem de O com queda de desempenho de nn 22 − para o

retrocedimento completo e com uma penalidade de desempenho de n2 para o

retrocedimento parcial.

4.2.2 Algoritmo G: 3-Níveis de Checkpoints Bidimensionais e Partição Fixa de

Memória

Uma versão 3-níveis do algoritmo F pode reduzir o uso de memória ainda mais, ou seja,

com a mesma quantidade de memória usada pelo algoritmo F pode-se calcular matrizes

de PD ainda maiores com uma versão de 3-níveis com partição fixa de memória. Esse é

um algoritmo de 3-níveis de checkpoints bidimensionais que realiza: computação e

armazenagem de checkpoints bidimensionais de nível-3; computação e armazenagem

provisória de checkpoints bidimensionais de nível-2 (algoritmo F); e a computação e

armazenagem provisória de checkpoints de nível-1 (algoritmo de PD básico).

Para o algoritmo com 3-níveis de checkpoints bidimensionais e partição fixa de

memória o uso de R linhas de memória disponível pode ser otimizada fazendo-se

e 2=tbR 12

222

2 33

−=−

=−

= nnRRbd . Entretanto . 2762 ≥⇒≥⇒≥ nRRbd

Supondo , tem-se, então, que a distância entre dois checkpoints sucessivos é nm =

31 nn

= . Quando é necessário efetuar um procedimento de

retrocedimento total sobre a matriz de PD com um algoritmo de 3-níveis de checkpoints

e partição fixa de memória, usam-se as duas linhas da memória de trabalho, , 2=tbR

3 2nR

ncbd

=+

=

132

Page 135: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

para computar e armazenar checkpoints bidimensionais de nível-3 na matriz de PD

completa. Com as mesmas linhas da memória de trabalho, 2=tbR , que são agora

particionadas ao meio em memória de trabalho e memória de checkpoints de nível 2, re-

computa-se e armazena-se os checkpoints bidimensionais de nível-2 (algoritmo F) em

cada seção da matriz de PD delimitada pelos checkpoints bidimensionais de nível-3,

começando sempre na última seção, aquela localizada no canto inferior direito. Com as

mesmas linhas da memória de trabalho de nível 2, que são agora particionadas e usadas

como memória de checkpoints de nível 1, re-computa-se e armazena-se os checkpoints

de nível-1 (algoritmo de PD básico) em cada seção da matriz de PD delimitada pelos

checkpoints bidimensionais de nível-2, começando sempre na última seção.

Então, o processo de retrocedimento completo sobre a matriz de PD é efetuado da

seguinte forma. Uma vez re-computados e armazenados os checkpoints de nível-1 numa

última seção, executa-se, então, o retrocedimento sobre essa seção de nível 1 e passa-se

para a próxima seção adjacente de nível 1 (localizada à esquerda), e então procede-se de

forma regular e sucessiva (caminhando sempre da direita para a esquerda) passando-se

para a próxima seção até que essas seções de nível 1, contidas numa dada seção de nível

2, sejam esgotadas e o retrocedimento sobre essa seção de nível 2 esteja completo. De

maneira análoga procede-se, de forma regular e sucessiva (caminhando sempre da

direita para a esquerda), à re-computação e retrocedimento sobre cada seção de nível 2

delimitada pelos checkpoints de nível 3 até que todas elas sejam esgotadas e o

retrocedimento sobre a matriz de PD esteja, desta forma, completo.

O processo de retrocedimento parcial é efetuado da seguinte forma. Uma vez

computados e armazenados os checkpoints de nível-3, o processo de retrocedimento

parcial inicia-se na última seção de nível 2 da matriz de PD, que contém a célula PDn,n.

Re-computa-se, então, os resultados da seção de nível 2 adjacente à linha ou coluna de

checkpoint que o alinhamento ótimo alcançou no procedimento de retrocedimento sobre

a seção de nível 2 anterior, e então executa-se o procedimento de retrocedimento sobre

essa seção. Procede-se de maneira análoga para todas as seções de nível 2 adjacentes até

que a primeira seção de nível 2 seja atingida, aquela localizada no canto superior

esquerdo, e executado o procedimento de retrocedimento sobre ela. Completando-se,

133

Page 136: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

assim, o retrocedimento parcial sobre a matriz de PD. Para executar o processo de

retrocedimento nessas seções de nível 2 chama-se o algoritmo F – de 2-níveis de

checkpoints bidimensionais e partição fixa de memória – usando as mesmas linhas da

memória de trabalho, , que são agora particionadas ao meio em memória de

trabalho e memória de checkpoints de nível 2.

2=tbR

Considerando a suposição já assumida de que m n= , então o maior número de linhas

que o algoritmo de 3-níveis com partição fixa pode calcular com R linhas é

( ) ( )82

133

33

RRRRrn bdbd =

=+== linhas.

A análise da performance deste algoritmo é simples de ser feita. Seja nR=

8

3

, então

tem-se que 32 nR = . De onde pode-se concluir que o algoritmo de 3-níveis com

partição fixa tem complexidade de espaço da ordem de ( )3 nnO .

Com relação à complexidade de tempo do algoritmo de 3-níveis de checkpoints

bidimensionais com partição fixa tem-se que são calculadas n2 células da matriz de PD

numa execução para frente (forward) do algoritmo de PD. Se o processo de

retrocedimento é completo, então são computadas

( ) 322322 2422 nnnnnnnn −=+−+ células, contra do algoritmo básico. Se o

retrocedimento é parcial, então são computadas, no máximo,

22n

( ) nnnnnnnn 24222 3 222

3 232 ++=+

+ células da matriz de PD, contra do

algoritmo básico.

nn 22 +

Desta forma, considerando que se gaste um tempo unitário para calcular cada célula da

matriz de PD, tem-se que o algoritmo de 3-níveis com partição fixa tem complexidade

de tempo da ordem de ( )2nO com queda de desempenho de 32 22 nnn − para o

retrocedimento completo e com uma penalidade de 3 24 nn para o retrocedimento

parcial.

134

Page 137: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

4.2.3 Algoritmo H: L-Níveis de Checkpoints Bidimensionais e Partição Fixa de

Memória

Nessa seção considera-se um algoritmo com nível de recursão arbitrário e

particionamento fixo de memória; o algoritmo de L-níveis de checkpoints

bidimensionais, por linhas e colunas, com particionamento fixo de memória. Essa

versão L-níveis do algoritmo G pode reduzir o uso de memória ainda mais, à medida

que L cresce.

Para o algoritmo com L-níveis de checkpoints bidimensionais e partição fixa de

memória o uso de R linhas de memória disponível pode ser otimizada fazendo-se

e 2=tbR 12

222

2−=

−=

−= L

L

bd nnRR . Entretanto . Lbd nRR 362 ≥⇒≥⇒≥

Supondo , tem-se, então, que a distância entre dois checkpoints sucessivos é nm =

L nn

1= . L L

bd

nR

nc 1−=+

=

O número máximo de linhas, , que podem ser computadas com o algoritmo de L-

níveis bidimensionais com partição fixa de memória para uma determinada quantidade

de memória disponível de R checkpoints é dado por:

( )RrL

( ) ( )L

LbdbdL

RRRr

=+=

21 . (4.1)

A análise da performance deste algoritmo é simples de ser feita. Seja nRL

L

=2

, então

tem-se que L nR 2= . De onde pode-se concluir que o algoritmo de L-níveis com

partição fixa tem complexidade de espaço da ordem de ( )L nnO .

Com relação à complexidade de tempo do algoritmo de L-níveis de checkpoints

bidimensionais com partição fixa tem-se que são calculadas n2 células da matriz de PD

numa execução para frente (forward) do algoritmo de PD. Se o processo de

135

Page 138: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

retrocedimento é completo, então ele precisa ser executado em todas as células da

matriz de PD, logo, são computadas ( )( ) ( ) LL nLnnLnnLnnLn −+=+−−+ 2222 11

( )

células, contra do algoritmo básico. Se o retrocedimento é parcial, então são

computadas, aproximadamente,

22n

nnnnLnnLL L L

L LL 22221 222

21

++≤+

− −

LLn 23 ≥≥

n2 +

células da matriz de PD. Mas como então são computadas, no máximo,

nnnn L L 222 2222 +++ − n2 ≤ células da matriz de PD, contra do algoritmo

básico.

nn 22 +

Desta forma, considerando que se gaste um tempo unitário para calcular cada célula da

matriz de PD, tem-se que o algoritmo de L-níveis com partição fixa tem complexidade

de tempo da ordem de ( )2nO com queda de desempenho de ( ) L nLnnL −− 21

computações adicionais de células de PD para o retrocedimento completo e, com uma

penalidade de cálculo de células de PD para o retrocedimento parcial. 2n

Numa análise de pior caso, quando L = n, temos que a complexidade de tempo é da

ordem de ( )2nO para o algoritmo com retrocedimento parcial, com um coeficiente igual

a 2, e de ( )3nO para o com retrocedimento completo. A complexidade de espaço é de

ordem O(n).

4.2.4 Algoritmo J: Viterbi com L-Níveis de Checkpoints Bidimensionais e

Partição Fixa de Memória

A performance do algoritmo de Viterbi para calcular o melhor caminho através da

matriz de PD pode ser melhorada aplicando a esse o princípio D&C. Grice et. al (1997)

aplicaram o princípio D&C no algoritmo de Viterbi com L-níveis de checkpoints por

diagonais. Esse algoritmo é referido como algoritmo de PD com L-níveis de checkpoints

por diagonais e retrocedimento restrito e trata-se do algoritmo E da seção 3.3.5.

Tarnas e Hughey (1998) implementaram o loop interno do pacote SAM com o

algoritmo de PD com L-níveis de checkpoints por diagonais, mas, após avaliações e

136

Page 139: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

ancorados na experiência com esses algoritmos, ficaram desapontados com as versões

destes algoritmos por diagonais e planejaram re-implementar as versões de checkpoints

por linhas. Eles avaliaram que a complexidade adicional, devido às condições de

contorno para o processamento de diagonais e armazenamento de checkpoints por

diagonais, não proporcionou um ganho de performance equivalente, mesmo quando

usando o algoritmo de Viterbi com L-níveis de checkpoints por diagonais e

retrocedimento restrito.

O princípio D&C e o paradigma de checkpoints também podem ser aplicados,

conjuntamente, aos algoritmos de PD considerando-se checkpoints em duas dimensões,

usando-se checkpoints por linhas e colunas. Essa seção trata do algoritmo de Viterbi

com L-níveis de checkpoints, completos, bidimensionais por linhas e colunas e

particionamento fixo de memória.

O uso de checkpoints por linhas e colunas simultaneamente não acarreta ineficiências

com relação ao requerimento de espaço. Necessita-se de um número de checkpoints L nLR ≤ para armazenar os checkpoints nas duas dimensões, na das linhas e na das

colunas. Além do que o algoritmo de Viterbi com L-níveis de checkpoints

bidimensionais, para uma dada quantidade de memória disponível Rbd, possibilita a

computação do melhor caminho numa matriz de PD de dimensões

( ) ( )L

LbdbdL

RRRrn

=+==

21 , considerando-se nm = , maior do que aquela que o

algoritmo com L-níveis de checkpoints por diagonais com retrocedimento restrito e

particionamento móvel de memória pode calcular com uma dada quantidade de

memória disponível R (>Rbd).

O tempo para se calcular o melhor caminho através da matriz de PD é a soma do tempo

no primeiro passo para frente durante a computação da matriz de PD, mais o tempo

gasto no retrocedimento. Usando-se o paradigma de checkpoints, com checkpoints

bidimensionais por linhas e colunas e particionamento fixo de memória, gasta-se um

tempo n2 na primeira fase, na computação da matriz de PD e armazenagem ou

salvamento dos checkpoints. Mas no passo de retrocedimento pode-se economizar

137

Page 140: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

tempo de re-computação aplicando o princípio D&C, pois não é necessário re-computar

todas as subseções formadas pela disposição dos checkpoints na matriz de PD, mas

apenas uma fração destas subseções.

Supondo-se em primeiro lugar que exista uma relação um a um entre os pontos de uma

grade ou treliça e as células da matriz de PD, em segundo lugar que o espaçamento ou

distância entre dois checkpoints sucessivos seja c, em ambas dimensões ( , e em

terceiro lugar que se o melhor caminho atinje o checkpoint k em uma das dimensões

(vertical ou horizontal), na i-ésima linha ou j-ésima coluna, então o melhor caminho

precisa alcançar ou o checkpoint (na

)nm =

k ( )i -ésima linha ou na ( )j -ésima coluna) ou o

checkpoint (na ( -ésima linha ou na 1−k )ci − ( )cj − -ésima coluna). Portanto, é

necessário re-computar somente uma região retangular contendo células da

matriz de PD.

(c )21−

Devido ao fato de que se pretende comparar esse algoritmo J com o algoritmo E da

seção 3.3.5, então será considerado uma quantidade de memória, supostamente, igual à

requerida por aquele algoritmo, mas esse pode trabalhar com a metade desta memória.

Desta forma, seja LL nLnLR 42!2 ≤= .

Tomando-se L nLR 4≤ , então existem no máximo L nL4 regiões (subseções)

contendo células da matriz de PD, que necessitam ser re-computadas. É

necessário observar que, no algoritmo com partição fixa de memória, a distância entre

checkpoints em ambas as dimensões, supondo

( )21−c

nm = , é dada para todas as regiões

(subseções) delimitadas por checkpoints pela seguinte equação:

LL

Ln

LnLnc

1

21

2

== . (4.2)

Desta forma, considerando que para um nível arbitrário L existem L nL2 checkpoints

em cada uma das dimensões, com um espaçamento entre eles de LL

nL

1

21 −

, então na fase

138

Page 141: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

de retrocedimento são re-computadas L nL4 seções de dimensões

×

−−L

LL

L

nnL

11

241

( )nL

da

matriz de PD usando o algoritmo de nível L-1. Logo, o tempo total, T , gasto pelo

algoritmo J, de L-níveis bidimensionais (por linhas e colunas) e partição fixa de

memória, para calcular o melhor caminho, pode ser estimado em termos de

complexidade assintótica conforme mostra a desigualdade (4.3), dada a seguir.

( )

( )2nO

On =

( )

+=

+≤

−L

L

L

LL

L

LL

L nTLnnnT

LnLnnT

1

12

1

122

414 (4.3)

A partir da equação (4.3) pode-se mostrar por indução que o tempo total, T , gasto

pelo algoritmo J – de L-níveis de checkpoints bidimensionais (por linhas e colunas) e

partição fixa de memória – para calcular o melhor caminho tem complexidade

assintótica da

( )nL

( )2nO , quando . Veja a prova nas equações em (4.4), a seguir. nL blog≤

( ) ( ) ( )( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( ) ( )

( ) ( ) ( )

( )

( ) ( ) ( )2log1log

2

1log2

1log

log2

log

log2

1log

25

24

52

5

24

23

42

4

222

22

32

22

221

22

21

21

log

blog

blog

n quemostrar se-deve , que se-Supondo

120602055

241244

63233

222

log1log

57

58

59

54

46

47

43

35

32

323

1

32

212

1

21

nOnTnTn

n

bnT

nnnT

nnnT

nTnOnT

nOnTnnnnnnTnnnT

nOnTnnnnnTnnnT

nTnnnnnnnnTnnnT

nOnTnnnnnnTnnnT

nOnTnnT

nnb

nb

nb

n

n

nn

bb

b

nb

nb

b

b

b

bb

=∴+≤

+=

+≤

==

=∴++++=+≤

=∴+++=+≤

∴++=

++=+≤

=∴+=×+=+≤

=∴=

−−

M

33

(4.4)

Pode ser observado das equações em (4.4) que, numa análise a priori, assintoticamente

não existe penalidade de tempo, para níveis de checkpoints nL blog≤ . Entretanto, essa

139

Page 142: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

análise descarta os coeficientes multiplicativos e, como os algoritmos são da mesma

ordem de complexidade, é necessário ser melhor elucidado a dimensão desta

penalidade, levando-se em consideração esses coeficientes. Desta forma pode-se

estabelecer um fator de queda de desempenho com base nesses coeficientes.

No estudo deste fator ou coeficiente de queda de desempenho pode ser deduzido das 5

primeiras equações em (4.4) que a desigualdade em (4.3) tem equação de recorrência,

dada a seguir em (4.5), composta de L fatores no lado direito, que incluem

necessariamente o primeiro e o último fator.

( ) ( ) ( )( ) ( ) ( )( ) ( )2213211

22212

2

LLL

−−+

−−++

−++≤

+−−

LLLn

LLLn

LLn

LnnnT

LL

LL

LL

L (4.5)

Fazendo em (4.5) e considerando logaritmos de base 2, bastante usados em

biologia computacional, e logaritmos de base 10 e 100, obtém-se as desigualdades que

são mostradas em (4.6).

nL blog=

Se for considerado em (4.6) somente a equação para logaritmos de base 2, pode ser

mostrado que a soma dos termos de segundo grau, , tende a 1 quando n cresce – veja

as equações dadas em (4.7). De onde, pode-se concluir que a queda de desempenho para

o nível , quando calculando o melhor caminho com o algoritmo de PD com

checkpoints bidimensionais (por linhas e colunas) e partição fixa de memória, não tem

penalidade de tempo, para n grande.

2n

nL 2log=

Se forem consideradas em (4.6) as equações para logaritmos de base 10 e 100, pode ser

mostrado, de maneira análoga às equações em (4.7), que a soma dos termos de segundo

grau, , tende a 1 quando n cresce. De onde, pode-se concluir que a queda de

desempenho para os níveis

2n

nLnLnL 100102 log e log ,log ===

nL blog

, quando calculando o

melhor caminho com o algoritmo de PD com checkpoints bidimensionais (por linhas e

colunas) e partição fixa de memória, não tem penalidade de tempo, para n grande. Pode-

se mostrar usando indução que, para um nível = , o coeficiente de tende a 1

para n grande, ou seja, não se tem penalidade de tempo quando se usa o algoritmo de

2n

140

Page 143: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

PD com checkpoints bidimensionais (por linhas e colunas) e partição fixa de memória

com um nível de checkpoints . nL blog≤

( )

( )

( )

( ) )(

( ) log3

2

log100

100, base de log3

2

log10

10, base de 24

loglog

2, base de

log

100

1002

10

102

2

2

22

2

log2log2

n

nn

n

nn

nn

nnn

L

bb

nb

nb

+

+−

+

+

+−

+

L

L

L

( )

(4.6)

( ) ( ) ( )

( )

( ) ( ) ( )( )

( ) ( ) ( ) ( )

( ) ( )

( ) ( ) ( )

( ) ( )

( ) ( )

( ) ( )

( ) ( )!1loglog100

41loglog100log1loglog100

1loglog100

que se- temlogaritmos se-doConsideran!1loglog10

41loglog10log1loglog10

1loglog10

que se- temlogaritmos se-doConsideran!log31loglog1loglog2

2log1loglog212log2

:que se- temlogaritmos se-doConsideran!log

31loglog1loglog

1001001002

2100100

3

2

1001003

2100

2

100

22

log

10102

21010

3

2

1010103

21010

22

log

222

2

223

2222

3

2

22

22

log

2log

100

10

2

log2log

log1log2

nn

nnn

nnn

nnn

nnn

nnnnT

nnn

nnn

nnn

nnnnnT

nn

nnn

nnn

nnnn

nnnnnT

nn

nnn

nnnnT

L

L

n

L

L

n

L

n

b

bbbn

nb

nbnb

nb

b

−+

+−

+−

+

+−

+≤

−+

+−

+−

+

+−

++≤

+−−

++

+−−

+−

++≤

+

+−

++++≤

+−

LL

L

L

LL

LL

Do exposto nos parágrafos anteriores pode-se concluir que não há queda de desempenho

para o algoritmo com L-níveis de checkpoints bidimensionais (por linhas e colunas),

quando o número de níveis for da ordem ( )nO blog

L blog

e desde que haja memória

disponível suficiente para ser utilizada com esse nível e tamanho da matriz de PD, o que

supera o desempenho assintótico do algoritmo de Viterbi com L-níveis de checkpoints

por diagonais e retrocedimento restrito, para n≤ . É bom salientar que essa

141

Page 144: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

situação é ainda mais confortável quando incorpora-se o retrocedimento restrito ao

algoritmo J.

( ) ( ) ( )( )

( ) ( ) ( ) ( ) ( )

( )

( )

( ) ( ) ( )

( ) ( ) ).( grande é quando ,!log

.0log

1 :que se- tem)( grande Para

!loglog11

!log1

log

!log21

21

21

21

21

log

!loglog2log2log2log2log2

!log31loglog241loglog2

2log1loglog21loglog2log2

2

2log

2

22

2log

22

22

22332

2

22

222

2

23

2

23

2

22

2

2

22

2222

2

223

2222

3

2

222

2

2

22

log

2

2

2

∞→+≅∴

→∞→

+

+≤∴+×+≤

≤+

++++++=

+++++++≤

≤+−

+−

++

+−−

+−

++≤

−−

−−

−−

nnn

nnnT

nnn

nn

nnnT

nn

nnn

nn

nnn

nn

nn

nn

nn

nn

nnn

nn

nnn

nnn

nnnn

nnn

nnnnT

n

n

LL

LL

LL

n

L

L

LLL

(4.7)

O algoritmo J de Viterbi com L-níveis de checkpoints bidimensionais (por linhas e

colunas) e particionamento fixo de memória, para calcular o melhor caminho, apresenta

comportamento de requerimento de espaço semelhante aos demais membros desta

família de algoritmos. Seja o espaço necessário para armazenar checkpoints de

nível L suficientes para que se faça o retrocedimento sobre a da matriz de PD contendo

n linhas. O requerimento de espaço do algoritmo de nível 1é

( )nSL

( ) 21 nnS = . O algoritmo de

nível 2 necessita de espaço para ( ) ( ) ( )nnOn =SnnnS ∴≤ 22 4 locações de

memória. O algoritmo de nível 3 necessita de espaço para

( ) ( ) ( )3 nn33 6 OnSnnnS =∴≤ locações de memória. De maneira geral o

requerimento de memória do algoritmo de L-níveis pode ser dado pela equação a seguir.

( ) LL nLnnS 2≤ (4.8)

142

Page 145: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Da desigualdade em (4.8) pode-se concluir que para o requerimento de espaço do

algoritmo J de Viterbi com L-níveis de checkpoints bidimensionais (por linhas e

colunas) e particionamento fixo de memória é da ordem de

1≥L

( ) ( )LL nnOnS = .

Desta forma o algoritmo J de Viterbi com L-níveis de checkpoints bidimensionais (por

linhas e colunas) e particionamento fixo de memória apresenta requerimento de espaço,

quando se toma nL blog= níveis, da ordem de ( ) ( )nnOnS bL log= .

Esse algoritmo de Viterbi com nL blog≤ níveis de checkpoints bidimensionais (por

linhas e colunas) e particionamento fixo de memória, que faz uso explícito do princípio

D&C, para calcular o melhor caminho através da matriz de PD aproxima a performance

assintótica do algoritmo de Hirschberg (totalmente baseado na estratégia D&C) em

termos de requerimento de espaço de ( )nO e iguala a performance com relação ao

tempo de execução de ( )2nO , mas superando o desempenho por um fator de 2. Ele,

também, apresenta desempenho semelhante no requerimento de espaço e superior no

requerimento de tempo de execução, por um fator de 2, ao do algoritmo de Viterbi com

L-níveis de checkpoints por diagonais e retrocedimento restrito, para , que

integra o estado da arte com respeito a essa família de algoritmos de PD com L-níveis

de checkpoints.

nL blog≤

143

Page 146: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

144

Page 147: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

CAPÍTULO 5

EXPERIMENTAÇÃO E ANÁLISE DOS RESULTADOS

5.1 Introdução

Nesse capítulo são apresentados alguns detalhes da implementação dos algoritmos e da

realização dos experimentos previstos na Seção 4.3 para análise de performance a

posteriori dos seguintes algoritmos de PD: algoritmo de Viterbi básico; algoritmo de

Viterbi com 2-níveis de checkpoints por linhas e particionamento móvel de memória;

algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, com retrocedimento

restrito e particionamento móvel de memória; e algoritmo de Viterbi com 2-níveis de

checkpoints bidimensionais por linhas e colunas, com retrocedimento restrito e

particionamento fixo de memória. São apresentados, também, os resultados dos

experimentos e a análise destes resultados.

A Seção 5.2 trata da metodologia de experimentação. A Seção 5.3 trata da

implementação dos algoritmos e da realização dos experimentos. Os resultados e a

análise dos experimentos são apresentados na Seção 5.4.

5.2 Metodologia de Experimentação

Seqüências biológicas referem-se ao encadeamento de resíduos que formam a estrutura

primária de determinadas macromoléculas que fazem parte do metabolismo dos seres

vivos. Elas são polímeros formados pelo encadeamento de 4 nucleotídeos ou de 20

aminoácidos. Dentre e as de maior interesse estão as proteínas e os ácidos nucléicos

(ácidos ribonucléicos - RNA - e ácidos desoxirribonucléicos - DNA). As moléculas

(seqüências) de de DNA contém subseqüencias denominadas genes que codificam a

informação necessária para a fabricação das proteínas do organismos. Várias moléculas

de DNA são agrupadas para formarem os cromossomas, e o conjunto de todos os

cromossomas formam o genoma de um organismo.

Das estatísticas publicadas pelo Swiss-Prot group (2004) sabe-se que as moléculas de

proteínas, anotadas e contidas no banco de dados Swiss-Prot, são compostas em média

145

Page 148: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

por seqüências de 368 aminoácidos, sendo a menor seqüência composta por 2

aminoácidos e a maior por 8.797 aminoácidos. Veja na Figura 5.1 a seguir o gráfico da

distribuição do tamanho das seqüências de proteínas. Esses dados motivam a realização

FIGURA 5.1 - Distribuição do tamanho das seqüências de proteínas.

de experimentos para o alinhamento de seqüências biológicas relativamente pequenas.

FONTE: Adaptada de figura do Swiss-Prot group (2004).

Segundo 01) o

tamanho médio de um gene consiste do encadeamento de 3.000 bases, formando uma

dados do Human Genome Mangement Information System (1992 e 20

seqüência primária. O maior gene humano conhecido, o dystrophin, é composto por 2,4

milhões de bases. As moléculas de DNA no genoma humano são arranjadas em 24

cromossomas distintos com tamanhos variando de 50 a 250 milhões de pares de bases.

Os genomas dos diferentes organismos possuem uma grande variação em tamanho, o

menor genoma conhecido contêm cerca de 600 mil pares de bases (bactéria), enquanto

os genomas do rato e do homem contêm cerca de 3 bilhões de pares de bases. Esses

dados motivam a realização de experimentos para o alinhamento de seqüências

biológicas relativamente grandes, pois já se fala em comparação de genomas inteiros.

146

Page 149: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Foi adotada nesse trabalho uma posição semelhante àquela adotada por Tarnas e

Hughey (1998), pag. 404: “... Para SAM rodando numa workstation, foi decidido que o

algoritmo de nível , que reduz o uso de espaço pela raiz quadrada do

comprimento da seqüência, deveria ser suficiente ...”. Pois experimentos, para a análise

de performance a posteriori, com algoritmos de PD com 2-níveis de checkpoints,

permitirão comparações com a análise de performance a posteriori já realizadas e

relatadas por Grice et al. (1995 e 1997), Tarnas e Hughey (1998) e Wheeler e Hughey

(2000).

2=L

Tendo como base os relatos em Grice et al. (1995 e 1997), Tarnas e Hughey (1998) e

Wheeler e Hughey (2000) e a pesquisa efetuada nesse trabalho, pode-se concluir que,

quando o interesse reside justamente na utilização dos algoritmos de PD de L-níveis de

checkpoints em computações que exijam retrocedimento completo, a escolha deve

recair naqueles com checkpoints unidimensionais por linhas ou por colunas. Isto é o

melhor que se pode vislumbrar até o momento, a não ser que alguma percepção nova

lance luz sobre o assunto, pois as demais versões são mais dispendiosas em termos de

espaço e de tempo – exigem o dobro de memória e adicionam mais complexidade no

tratamento e armazenagem dos checkpoints e nas re-computações das seções da matriz

de PD. Entretanto, os resultados da análise a priori dos algoritmos de L-níveis de

checkpoints bidimensionais indicam que esses assintoticamente requerem menos espaço

do que os algirmos de checkpoints por linhas e apresentam menor queda de

desempenho.

Desta forma os experimentos para análise de performance a posteriori se concentraram

somente nos algoritmos de Viterbi com L-níveis de checkpoints, particularmene nos de

2-níveis de checkpoints.

Os algorimos de PD que adotam o paradigma de checkpoints como estratégia, ou

método de economia de espaço, permitem uma amplitude de escolhas dentro do

intervalo de espaço disponível, que possibilitam fazer o balanceamento (tradeoff) entre

requerimentos de espaço e de tempo para computar uma determinada matriz de PD.

Para HMMs com um determinado número de nós e tamanhos de seqüências a serem

147

Page 150: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

analisadas, a penalidade de performance pode ser escolhida, conforme o número níveis

de checkpoints adotados, levando-se em consideração toda a hierarquia de memória e,

principalmente, a quantidade de memória de trabalho disponível – primariamente a

memória RAM e secundariamente a memória cache.

Os experimentos objetivaram comparar o desempenho dos seguintes algoritmos, no

alinhamento de seqüências biológicas pequenas (seqüências de proteínas com tamanho

médio de 368 símbolos e um alfabeto de 20 símbolos) e grandes (seqüências de DNA

contendo milhôes de símbolos e um alfabeto de 4 símbolos): o de Viterbi básico; o de

Viterbi com 2-níveis de checkpoints por linhas e particionamento móvel de memória; o

de Viterbi com 2-níveis de checkpoints bidimensionais por linhas e colunas com

retrocedimento restrito e particionamento fixo de memória; e o de Viterbi com 2-níveis

de checkpoints por diagonais com retrocedimento restrito e particionamento móvel de

memória.

No alinhamentos de seqüências grandes pretendeu-se verificar como os algoritmos

desempenham até o início de paginação. Também pretendeu-se verificar para que

tamanhos de seqüências esses algoritmos causam paginação excessiva num PC típico,

inviabilizando a obtenção de resultados para seqüência acima deste tamanho.

Finalmente, verificar se o algoritmo de Viterbi com checkpoints bidimensionais por

linhas e colunas com retrocedimento restrito e particionamento fixo de memória utiliza

eficientemente o sistema de memória, particularmente no uso da memória cache.

Entretanto o interesse maior residiu justamente na comparação entre o algoritmo de

Viterbi com 2-níveis de checkpoints bidimensionais por linhas e colunas com

retrocedimento restrito e particionamento fixo de memória e o algoritmo de Viterbi com

2-níveis de checkpoints por diagonais com retrocedimento restrito e particionamento

móvel de memória.

Para uma melhor discriminação na comparação entre esses dois algoritmos de interesse

primário nos experimentos foram utilizadas duas configurações de requerimentos de

memória. Primeiro, foi utilizada, pelo menos em suposição, uma mesma quantidade de

memória para os dois algoritmos, justamente a quantidade mínima de memória

148

Page 151: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

requerida pelo algoritmo de Viterbi com 2-níveis de checkpoints por diagonais com

retrocedimento restrito e particionamento móvel de memória. Segundo, a memória do

algoritmo de Viterbi com 2-níveis de checkpoints bidimensionais por linhas e colunas

com retrocedimento restrito e particionamento fixo de memória será reduzida pela

metade.

Esperou-se mostrar com os experimentos, que o algoritmo de Viterbi com 2-níveis de

checkpoints bidimensionais por linhas e colunas com retrocedimento restrito e

particionamento fixo de memória seja mais rápido, também em aplicações práticas, que

o algoritmo de Viterbi com checkpoints por diagonais com retrocedimento restrito e

particionamento móvel de memória tanto para o alinhamento seqüências pequenas, com

tamanhos médios de cerca de 368 resíduos (proteínas), quanto para seqüências de

comprimentos variando de 2.000 a centenas de milhares de resíduos (DNAs,

cromossomas, genomas).

Foram realizados três conjuntos de experimentos com esses algoritmos, nos quais são

considerados os tempos de CPU e os tempos totais de execução (wall time). No

primeiro experimento foi considerado um HMM com 2.000 nós e seqüências de de

comprimentos crescentes, dados por uma progressão aritmética com razão 2.000,

iniciando-se com uma seqüência de 2.000 resíduos. No segundo experimento foi

considerado um HMM com 2.000 nós e seqüências com comprimentos crescentes,

dados por uma progressão geométrica de razão 2, iniciando-se com uma seqüência de

2.000 resíduos. No terceiro experimento foi considerado um HMM com 368 nós e

seqüências com comprimentos crescentes de 100 a 1000 símbolos, dados por uma

progressão aritmética com razão 100.

Pode ser considerado a possibilidade do encadeamento ou concatenação de várias

seqüências de proteínas para serem comparadas de uma única vez a um HMM,

formando assim uma seqüência grande de resíduos de aminoácidos. Desta forma,

unicamente para facilitar e simplificar a programação e a experimentação foi

considerado nesses experimentos somente o alfabeto de aminoácidos, contendo 20

símbolos. Os dados usados como amostras nesses experimentos foram gerados

149

Page 152: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

aleatoriamente com um alfabeto composto de 20 símbolos, de forma semelhante àqueles

dados dos experimentos efetuados por Powell et al. (1999). As strings para o

treinamento dos HMMs foram geradas da seguinte forma: primeiro uma string A foi

gerada aleatoriamente com probabilidades dadas pelas freqüências f(i) (freqüências de

Dayhoff) de occorrência dos 20 aminoácidos (função massa de probabilidade para

aminoácidos) obtidas do Banco de Dados SwissProt 34, considerando 21.210.388

raidicais (Eddy, 2003) e, então, as demais strings, as strings Bs, foram geradas de A

com probabilidades fixas de 0,2 para mudança, de 0,1 para inserção e de 0,1 para

remoção (serão permitidas discordâncias para o mesmo caractere).

As strings usadas nos experimentos para alinhamento contra os HMMs foram geradas

aleatoriamente com probabilidades dadas pelas freqüências f(i) (freqüências de

Dayhoff) de occorrência dos 20 aminoácidos (função massa de probabilidade para

aminoácidos) obtidas do Banco de Dados SwissProt 34, considerando 21.210.388

raidicais (Eddy, 2003).

Foi realizado, também, uma simulação do comportamento assintótico dos requerimentos

de espaço dos algoritmos considerados nos conjuntos de experimentos 1 e 2.

Os experimentos deste trabalho foram realizados num PC rodando o sistema

operacional Linux Slackware 9.1, sob o ambiente KDE 3.1, com processador Atlhom

K7 de 800 MHz , 512 Mb de RAM, 256 Kb de cache L2 e 64 Kb de cache L1 – 32 Kb

para dados e 32 Kb para instruções, e com espaço reservado para swap limitado em

2.048 Mb. Os programas foram escritos em C ANSI e compilados usando a versão

3.2.2 do Compilador GCC da GNU.

5.3 Implementação dos Algoritmos e Realização dos Experimentos

Para uma melhor discriminação na comparação entre os dois algoritmos de interesse

primário nos experimentos foram considerados duas configurações de uso de memória

para um deles. Esses algoritmos são: o algoritmo de Viterbi com 2-níveis de checkpoints

por diagonais, com retrocedimento restrito e particionamento móvel de memória; e o

algoritmo proposto nesse trabalho, o de Viterbi com 2-níveis de checkpoints

150

Page 153: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

bidimensionais por linhas e colunas com retrocedimento restrito e particionamento fixo

de memória. O algoritmo proposto foi testado considerando-se duas quantidades de

alocação de memória para as principais estruturas de dados do algoritmo. Primeiro,

alocando-se a quantidade mínima de memória requerido pelo algoritmo de Viterbi com

2-níveis de checkpoints por diagonais, com retrocedimento restrito e particionamento

móvel de memória. Segundo, alocando-se somente a metade desta memória.

Nesse ponto, é importante adotar uma convenção para facilitar a distinção entre as duas

configurações de uso de memória do algoritmo. O algoritmo proposto com

requerimentos de memória idêntico ao algoritmo de Viterbi com 2-níveis de checkpoints

por diagonais, com retrocedimento restrito e particionamento móvel de memória, será

denominado algoritmo proposto 1 ou algoritmo com 2-níveis de checkpoints

bidimensionais 1. O algoritmo proposto com requerimentos de memória reduzido pela

metade será denominado algoritmo proposto 2 ou algoritmo com 2-níveis de

checkpoints bidimensionais 2.

É importante lembrar que existem 20 aminoácidos conhecidos, que são comuns em

organismos vivos, dos quais as proteínas são construídas. Para representá-los de forma

reduzida, tem sido utilizado dois códigos, um com uma letra e outro com três letras

(abreviatura), veja a Tabela 5.1 a seguir. Esses códigos constituem o alfabeto ou

conjunto de símbolos de emissão para os perfis-HMMs, quando eles estão modelando

seqüências de aminoácidos (proteínas).

Esse conjunto de símbolos ou alfabeto composto pelos símbolos dos aminoácidos,

Σ=A,C,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y, pode ser estendido formando um

alfabeto degenerado para as seqüências de aminoácidos. Ao conjunto ou alfabeto Σ são

acrescentados os símbolos B,U,X,Z,*,-, produzindo o alfabeto degenerado para as

proteínas, Σd=A,C,D,E,F,G,H,I,K,L,M,N,P,Q,R,S,T,V,W,Y ∪ B,U,X,Z,*,-, onde:

B = (D ou N), U = seleno-cisteína (selenocysteine), X = (radical de aminoácido

desconhecido ou indeterminado), Z = (E ou Q), * = interrupção de tradução, e – (ou -) =

lacuna (gap) de tamanho indeterminado.

151

Page 154: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

TABELA 5.1 – Códigos dos 20 Aminoácidos, com Uma e Três Letras.

Nome Completo Código Abreviatura Ácido Aspártico (Aspartate) D Asp Ácido Glutâmico (Glutamate) E Glu Alanina A Ala Arginina R Arg Asparagina N Asn Cisteína C Cys Fenilalanina F Fen Glicina G Gli Glutamina Q Gln Histidina H His Isoleucina I Ile Leucina L Leu Lisina K Lis Metionina M Met Prolina P Pro Serina S Ser Tirosina Y Tir Treonina T Tre Triptofano W Trp Valina V Val

A pontuação (o escore) de um alinhamento de uma dada seqüência a um HMM é

efetuada através da atribuição de escores em log-odds, do Modelo M, relativos a um

Modelo R – Modelo Nulo ou Aleatório – com composição aleatória da seqüência

observada. Esse modelo nulo é especificado também como um modelo totalmente

probabilístico, conforme a Figura 5.2. O estado N tem uma distribuição de

probabilidade de emissão de símbolos do alfabeto Σ, composto pelos símbolos de uma

letra dos 20 aminoácidos comuns nas proteínas dos seres vivos, dada pela freqüência

destes aminoácidos no Banco de Dados SWISSPROT 34 contendo 21.210.388 resíduos

(Eddy, 2003), veja a Tabela 5.2, mas pode ser definida, de forma alternativa, como

sendo uniformemente distribuída com probabilidade 05,0201

= . O estado E é um estado

final, silencioso, que não emite nenhum símbolo, responsável pelo encerramento da

emissão de símbolos do Modelo R. A probabilidade de transição N → N controla o

152

Page 155: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

tamanho esperado das seqüências observadas, mas na prática ela é tão próxima de 1 que

o efeito sobre o tamanho é muito pequeno.

FIGURA 5.2 - Modelo Nulo, R, totalmente probabilístico.

TABELA 5.2 – Freqüências Dayhoff de Ocorrências de Aminoácidos do Banco de Dados Swissprot 34.

Código do Aminoácido Freqüência Relativa Observada A 0,075520 C 0,016973 D 0,053029 E 0,063204 F 0,040762 G 0,068448 H 0,022406 I 0,057284 K 0,059398 L 0,093399 M 0,023569 N 0,045293 P 0,049262 Q 0,040231 R 0,051573 S 0,072214 T 0,057454 V 0,065252 W 0,012513 Y 0,031985

FONTE: Eddy (2003).

A versão log-odds do algoritmo de Viterbi, apresentada na Seção 2.2.4, é baseada em

uma arquitetura básica para perfis-HMMs, conforme a Figura 2.2, e apresenta transições

entre os estados insert e os estados delete, entretanto essas transições são bastante

improváveis e podem ser suprimidas quando não estão presentes nos modelos

construídos. Tipicamente, assume-se que a distribuição das probabilidades de emissão

dos estados Ij é a mesma distribuição do modelo nulo. Quando usado com escores em

153

Page 156: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

log-odds essas probabilidades se anulam produzindo um escore nulo:

01log)(

log ==t

j

O

tI

q

Oe. Entretanto, em termos de implementação essa suposição, apesar

de proporcionar uma redução das operações a serem realizadas a cada iteração,

melhorando o desempenho do algoritmo, implicaria num algoritmo de uso limitado a

essas instâncias particulares das versões log-odds dos algoritmos de Viterbi desenhados

especialmente para perfis-HMMs. Desta forma, considerando que a adoção ou não desta

suposição provoca impactos equivalentes em todas as versões dos algoritmos de Viterbi

que são alvos dos experimentos previstos nesse trabalho, optou-se por construir um

algoritmo de uso mais geral, que em termos práticos mostra-se ser mais útil.

As versões log-odds dos algoritmos de PD de Viterbi considerados nos experimentos

são detalhados a seguir, explicitando-se melhor as inicializações das variáveis e todas as

condições de contorno, incluindo a fase de retrocedimento e as mudanças necessárias

para essa fase. Seja V o escore em log-odds do melhor caminho concordando

(matching) a subseqüência O

)(tMj

1 ... i ao sub-modelo até o estado j, terminando com Oi

sendo emitido pelo estado Mj. Similarmente, seja V o escore do melhor caminho

terminando com O

)(tIj

i sendo emitido pelo estado Ij. Seja, de forma análoga, V o escore

para o melhor caminho terminando no estado D

)(tDj

j. Também é necessário um array

para guardar, para cada t, j e ( )tTBXj MIDX ,,∈ , o argumento que maximiza V ,

e V .

)(tMj

)(tV Ij )(tD

j

algoritmo de viterbi básico para perfis-HMMs (usando log-odds)

1) Inicialização:

154

Page 157: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

( ) ( )( ) ( )( ) ( )

( ) ( ) ( )( ) ( ) ( )( ) ( ) ( )( ) ( ) ( )( ) ( )

( ) ( ) NjTBVNjTBV

NjDDTBaVVTBaVV

TtIItTBatVtVTBaVV

TttTBtVTttTBtV

TBV

j

j

jjj

IIj

MMj

DDDDj

Dj

DDMMD

IIIII

IIMMI

MM

DD

MM

≤≤=−∞=

≤≤=−∞=

≤≤=+==+=

≤≤=+−==+=

≤≤=−∞=≤≤=−∞=

==

−−

1 para ,100 ,01 para ,100 ,0

2 para ,0 ,log0090 ,log00

2 para , ,log191 ,log01

1 para ,10 ,1 para ,10 ,

100 ,00

1

110

000

000

0

0

0

1

01

00

00

0

0

0

2) Fase de computações pra frente (forward). Computa e salva a matriz de escores

de Viterbi e as decisões tomadas a cada passo nessa fase. A computação é

efetuada conforme as seguintes equações, para TtNj ≤≤≤≤ 1 e 1 .

( ) [ ]

( ) [ ]

( ) [ ]

∈=

+

+

+

=

∈=

+−

+−

+−

+=

∈=

+−

+−

+−

+=

DDIDMDkktVtTBatVatVatV

tV

DIIIMIkktVtTBatVatVatV

q

OetV

DMIMMMkktVtTBatVatVatV

q

OetV

Dj

kD

DDDj

DIIj

DMMj

Dj

Ij

kI

IDDj

IIIj

IMMj

O

tIIj

Mj

kM

MDDj

MIIj

MMMj

O

tMMj

j

jj

jj

jj

j

jj

jj

jj

t

j

j

jj

jj

jj

t

j

,,: transiçãouma é ,)(maxarglog)(log)(log)(

max)(

,,: transiçãouma é ,)(maxarg

log)1(log)1(log)1(

max)(

log)(

,,: transiçãouma é ,)(maxarglog)1(log)1(log)1(

max)(

log)(

1

1

1

1

1

1

1

1

1

1

1

1

3) Encerramento da fase de computações pra frente (forward).

155

Page 158: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

[ ]

∈+=

+++

=+

++

+

+

+

+

DMIMMMkkTVq

aTVaTVaTV

TV

MN

kT

MDD

N

MII

N

MMM

NM

N

NN

NN

NN

,,: transiçãouma é ,)1(maxarg

log)(log)(log)(

max)1(

1*

1

1

1

1

1

4) Fase de retrocedimento sobre a matriz de escores de Viterbi.

( )

−−==+

9,,,,,,,,,e 1,,2,1, onde ,

*

**

1

IIIMIDMIMMMDDIDMDDqTTTttTBq

t

qtt

K

algoritmo de viterbi com 2-níveis de checkpoints por linhas e particionamento

móvel de memória, para perfis-hmms (usando log-odds)

1) Inicialização.

• ; ( ) 000 =MV

2) Fase de computações pra frente (forward).

• 1 ; ;0 −←←← cpcp RkRiendibegin ;

• Computa iterativamente, para cpRk <≤0 , TtNj ≤≤≤≤ 0 e 0

• Computa e salva as linhas ibegin a iend da matriz de Viterbi.

• Salva as decisões tomadas na computação das células destas linhas.

• Salva como um checkpoint(k) a linha iend e as decisões tomadas em cada

célula desta linha.

• ( ) ; ;1 ; ; −−−−+←←← kauxiendiendiendiendibeginibeginaux

• Término do loop (j = N).

• Encerramento da fase de computações pra frente (forward).

156

Page 159: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

[ ]

∈+=

+++

=+

++

+

+

+

+

DMIMMMkkTVq

aTVaTVaTV

TV

MN

kT

MDD

N

MII

N

MMM

NM

N

NN

NN

NN

,,: transiçãouma é ,)1(maxarg

log)(log)(log)(

max)1(

1*

1

1

1

1

1

3) Fase de retrocedimento sobre a matriz de escores de Viterbi.

• Executa o retrocedimento sobre as linhas N e N-1, correspondente aos

checkpoints 0 e 1.

• Re-computa iterativamente as seções da matriz entre dois checkpoints

consecutivos e executa o retrocedimento em cada uma destas seções. Assim,

para cpRk <≤2 , TtNj ≤≤−≤≤ 0 e 21 , faz-se:

• Re-computa e salva as linhas da seção k da matriz de Viterbi.

• Salva as decisões tomadas na re-computação das células destas linhas.

• Executa o retrocedimento nessa seção k.

• ;1−← kk

• Término (j = 0).

algoritmo de viterbi com 2-níveis de checkpoints por diagonais, com retrocedimento

restrito e particionamento móvel de memória, para perfis-HMMs (usando log-

odds)

1) Inicialização.

• ; ( ) 000 =MV

2) Fase de computações pra frente (forward).

• 1 ; ;0 −←←← cpcp RkRiendibegin ;

157

Page 160: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

• Computa iterativamente, para cpRk <≤0 , ( ) TtTNj ≤≤++≤≤ 0 e 10

• Computa e salva as diagonais ibegin a iend da matriz de Viterbi.

• Salva as decisões tomadas na computação das células destas diagonais.

• Salva como um checkpoint(k) a diagonal iend e as decisões tomadas em

cada célula desta diagonal.

• ( ) ; ;1 ; ; −−−−+←←← kauxiendiendiendiendibeginibeginaux

• Término do loop (j = N+T+1).

• Encerramento da fase de computações pra frente (forward).

[ ]

∈+=

+++

=+

++

+

+

+

+

DMIMMMkkTVq

aTVaTVaTV

TV

MN

kT

MDD

N

MII

N

MMM

NM

N

NN

NN

NN

,,: transiçãouma é ,)1(maxarg

log)(log)(log)(

max)1(

1*

1

1

1

1

1

3) Fase de retrocedimento sobre a matriz de escores de Viterbi.

• Executa o retrocedimento sobre as diagonais N+T+1 e N+T, correspondentes

aos checkpoints 0 e 1.

• Re-computa iterativamente as seções da matriz entre dois checkpoints

consecutivos e executa o retrocedimento em cada uma destas seções. Essa

re-computação é limitada (retrocedimento restrito) recomputando-se somente

a região entre dois checkpoints consecutivos limitada pelos pontos ,

e , onde

( )ji,

( )cji −, ( jci ,− ) ( )ji, é o ponto onde o alinhamento ótimo atingiu a

seção atual na iteração anterior. Assim, para cpRk <≤2 , 1 e

.

1−T+≤≤ Nj

Tt ≤≤0

• Re-computa e salva as diagonais da seção k da matriz de Viterbi.

158

Page 161: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

• Salva as decisões tomadas na re-computação das células destas

diagonais.

• Executa o retrocedimento nessa seção k.

• ;1−← kk

• Término (j = 0).

algoritmo de viterbi com 2-níveis de checkpoints bidimensionais, com

retrocedimento restrito e particionamento fixo de memória, para perfis-HMMs

(usando log-odds)

1) Inicialização.

• ; ( ) 000 =MV

2) Fase de computações pra frente (forward).

• Computa a linha 0 (zero).

• Salva a linha 0 (zero) no checkpoint 0 (zero).

• Salva as decisões tomadas a cada passo da computação da linha 0 (zero).

• Computa iterativamente, para TtNj ≤≤≤≤ 0 e 1 , as linhas da matriz

de Viterbi, usando 2 linhas de memória. Salva os checkpoints

bidimensionais.

• Computa e salva a linha j da matriz de Viterbi.

• Salva as decisões tomadas na computação das células da linha j.

• Salva como checkpoints as linhas 1 (ondelbdll Rcc ,,2, K

nncl 2

= ) e as

decisões tomadas em cada célula destas linhas.

159

Page 162: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

• Salva como checkpoints as células das colunas 1 .

(onde

cbdcc Rcc ,,2, K

mmcc 2

= ) e as decisões tomadas em cada célula destas células.

• Término do loop (j = N).

• Encerramento da fase de computações pra frente (forward).

[ ]

∈+=

+++

=+

++

+

+

+

+

DMIMMMkkTVq

aTVaTVaTV

TV

MN

kT

MDD

N

MII

N

MMM

NM

N

NN

NN

NN

,,: transiçãouma é ,)1(maxarg

log)(log)(log)(

max)1(

1*

1

1

1

1

1

3) Fase de retrocedimento sobre a matriz de escores de Viterbi.

• Re-computa a última seção da matriz de Viterbi – aquela situada no canto

inferior direito da matriz e delimitada pelos checkpoints (linha) e

(coluna) e a última linha e coluna da matriz . Executa o retrocedimento sobre

essa seção da matriz.

lbdRcbdR

• Iterativamente, escolhe a seção adjacente à ultima seção re-computada,

aquela que o caminho de estados recuperado pelo procedimento de

retrocedimento atingiu, até que seja alcançada a primeira seção da matriz de

Viterbi – aquela situada no canto superior esquerdo da matriz e delimitada

pelos checkpoints (linha) e c (coluna) e a primeira linha e coluna da

matriz.

lc c

• Re-computa as células de PD necessárias para o retrocedimento em cada

uma destas seções da matriz de Viterbi. Essas re-computações são

limitadas ou restringidas (retrocedimento restrito) pela linha ou coluna

do ponto onde o caminho ótimo, recuperado, atingiu a seção atual

na iteração anterior. Se a seção atual foi atingida na linha i

( ji, )

160

Page 163: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

(correspondendo ao checkpoint ), então os cálculos são limitados pela

coluna j, se foi atingida na coluna j (correspondendo ao checkpoint c ),

então são limitados pela linha i.

lc

c

• Salva as decisões tomadas na re-computação destas células.

• Executa o retrocedimento em cada uma destas seções.

• Término (j = 0).

Nos experimentos foram usados dois perfis-HMMs, um com 2.000 nós e outro com 368

nós, com arquitetura linear e conexões básicas entre os nós, conforme o modelo

apresentado e descrito na Seção 2.2.4. Eles foram treinados usando conjuntos de

seqüências geradas aleatoriamente com base no alfabeto de aminoácidos Σ, composto de

20 símbolos, de forma semelhante àqueles dados usados nos experimentos efetuados por

Powell et al. (1999).

Os perfis-HMMs foram treinados usando o pacote de software HMMpro versão 2.2.2

(B47). O HMMpro é um simulador de HMMs, de propósito geral, que pode ser

utilizado para mineração, análise e modelagem de informações biológicas geradas pelos

projetos de estudos de genomas ou por outros projetos (Net-ID, 2003).

5.4 Apresentação e Análise dos Resultados da Experimentação

Antes de iniciar a análise dos conjuntos de testes dos dois experimentos previstos é

importante que se faça uma análise da estimativa do comportamento de requerimentos

de memória dos algoritmos. Essas estimativas foram confeccionadas com base na

memória necessária para acomodar as principais estruturas de dados dos algoritmos, que

são a matriz de PD, no caso do algoritmo básico, e o arrays para armazenar os

checkpoints, no caso dos demais algoritmos. Foram considerados tamanhos de

seqüências variando de 2.000 a 2.048.000 resíduos com incrementos segundo uma

distribuição geométrica de razão 2. É importante salientar que essas estimativas, além

de desprezar as outras estruturas de dados alocadas pelo aplicativo (algoritmo), não

161

Page 164: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

considera a memória ocupada pelo Sistema Operacional e pelo ambiente gráfico KDE,

mas essas têm o mesmo impacto em todos os aplicativos (algoritmos).

As Tabelas 5.3 e 5.4 sintetizam os dados destas estimativas, sendo que na Tabela 5.3

estão relatados os dados brutos, com valores absolutos expressos em Mega bytes (Mb).

A Tabela 5.4 contém as estimativas da Tabela 5.3 normalizados pelos valores do

algoritmo básico. O algoritmo básico foi tomado como referência da normalização,

ficando, portanto, com um valor de 1 unidade.

O Sistema Operacional precisou alocar pelo menos 1.099 Mb para o algoritmo de

Viterbi básico alinhar seqüências de 32.000 resíduos, relativas à memória para

armazenagem das principais estruturas de dados, entretanto não foi possível alocar

2.197 Mb para o alinhamento de seqüências com 64.000 resíduos. O mesmo aconteceu

com o algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, que só

conseguiu memória suficiente para alinhar seqüencias de até 64.000 resíduos, mas não

conseguiu alocar 1.584 Mb – memória para as principais estruturas de dados – para

alinhar seqüências com 128.000 resíduos.

Os demais algoritmos de Viterbi foram testados contra seqüências de até 512.000

resíduos, mas observando os dados da Tabela 5.3, sabendo-se que foi possível alocar

1.099 Mb para o algoritmo de Viterbi básico e supondo tamanhos crescentes de

seqüências conforme uma progressão geométrica com razão 2, espera-se que seja

possível alinhar seqüências com 1.024.000 resíduos com o algoritmo de Viterbi com 2-

níveis de checkpoints por linhas, que necessita de pelo menos 1.107 Mb, e com o

algoritmo de Viterbi proposto 2, necessita de pelo menos 808 Mb.

Observando-se os dados da Tabela 5.4 pode-se verificar o comportamento assintótico, a

posteriori, dos requisitos de memória dos algoritmos considerados nos experimentos,

quando comparados com o algoritmo de Viterbi básico. O algoritmo de Viterbi com 2-

níveis de checkpoints por linhas requer 3,2% de memória para todos os tamanhos de

seqüências. Os algoritmos de Viterbi propostos 1 e 2 apresentam queda nos requisitos de

memória para tamanhos crescentes de seqüências, sendo que o proposto 1 decresce de

8,8% (2.000 resíduos) para 4,5% (2.048.000 resíduos) e o proposto 2 decresce de 4,4%

162

Page 165: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

(2.000 resíduos) para 2,3% (2.048.000 resíduos). Já o algoritmo de Viterbi com 2-níveis

de checkpoints por diagonais apresenta comportamento crescente dos requisitos de

memória para tamanhos crescentes de seqüências, ele cresce de 6,3% (2.000 resíduos)

para 143,2% (2.048.000 resíduos).

O que se deve observar nesses algoritmos com requisitos de memória reduzido é

justamente o comportameno assintótico dos requisitos de memória destes quando o

tamanho das seqüências cresce, pois eles são úteis justamente para o alinhamento de

seqüências cujos comprimentos estão além daqueles que o algoritmo de Viterbi básico

pode tratar. Nesse sentido, o algoritmo de Viterbi com 2-níveis de checkpoints por

diagonais, que apresenta um comportamento crescente dos requisitos de memória,

chegando mesmo a ultrapassar os requisitos do algoritmo de Viterbi básico, é o que

apresentou o pior comportamento assintótico, indicando que os requisitos de memória

crescem para o infinito mais rapidamente que o algoritmo de Viterbi básico, quando o

tamanho das seqüências tendem ao infinito.

TABELA 5.3 – Resultados das Estimativas dos Requerimentos de Memória para os Algoritmos de Viterbi (Valores em Mb).

Viterbi com 2-níveis de checkpoints (por) Comp. Seqüências

Viterbi Básico Linhas Diagonais Proposto 1 Proposto 2

2.000 69 2 4 6 3 4.000 137 4 11 10 5 8.000 275 9 27 18 9 16.000 549 17 74 33 16 32.000 1.099 35 202 61 30 64.000 2.197 69 564 114 57 128.000 4.395 138 1.584 218 109 256.000 8.789 277 4.460 421 211 512.000 17.578 554 12.595 823 411

1.024.000 35.156 1.107 35.596 1.616 808 2.048.000 70.313 2.215 100.652 3.192 1.596

O algoritmo de Viterbi com 2-níveis de checkpoints por linhas apresentou um

comportamento assintótico constante dos requisitos de memória, relativamente ao

algoritmo de Viterbi básico. Os algoritmos de Viterbi com 2-níveis de checkpoints

163

Page 166: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

propostos, 1 e 2, apresentaram comportamento assintótico decrescente dos requisitos de

memória, relativamente ao algoritmo de Viterbi básico. Mostrando-se, desta forma,

serem mais úteis do que os demais algoritmos considerados nesses experimentos,

quando a economia de espaço é a variável de performance mais importante a ser

considerada.

TABELA 5.4 – Resultados das Estimativas Normalizadas dos Requerimentos de Memória para os Algoritmos de Viterbi (Básico = 1).

Viterbi com 2-níveis de checkpoints (por) Comp. Seqüências

Viterbi Básico Linhas Diagonais Proposto 1 Proposto 2

2.000 1,000 0,032 0,063 0,088 0,044 4.000 1,000 0,032 0,077 0,076 0,038 8.000 1,000 0,032 0,100 0,066 0,033 16.000 1,000 0,032 0,134 0,060 0,030 32.000 1,000 0,032 0,184 0,055 0,028 64.000 1,000 0,032 0,257 0,052 0,026 128.000 1,000 0,032 0,361 0,050 0,025 256.000 1,000 0,032 0,508 0,048 0,024 512.000 1,000 0,032 0,717 0,047 0,023

1.024.000 1,000 0,032 1,013 0,046 0,023 2.048.000 1,000 0,032 1,432 0,045 0,023

Após a codificação, otimização e compilação dos algoritmos, foram efetuados os

conjuntos de testes previstos nos dois experimentos. O conjunto de testes previsto no

primeiro experimento foi preparado para verificar o comportamento dos algoritmos até

que os recursos de memória do PC fossem exauridos por algum dos algoritmos, ou seja,

até o início de paginação excessiva. O conjunto de testes previsto no segundo

experimento foi preparado para verificar o comportamento dos algoritmos, pelo menos,

até que os recursos de memória do PC fossem exauridos por algum dos algoritmos de

interesse primário nessa investigação, por um dos algoritmos propostos ou pelo

algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, com retrocedimento

restrito e particionamento móvel de memória. Entretanto, o conjunto de testes previsto

no segundo experimento foi ampliado para verificar, também, o comportamento do

algoritmo de Viterbi com 2-níveis de checkpoints por linhas até o ponto em que esse

exaurisse os recursos de memória do PC. O conjunto de testes previsto no terceiro

164

Page 167: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

experimento foi preparado para verificar o comportamento dos algoritmos no

alinhamento de seqüências relativamente pequenas, por exemplo as seqüências de

proteínas que possuem comprimento médio de 368 resíduos.

Nos três conjuntos de testes realizados, foram calculados os tempos de CPU e os tempos

totais de execução (wall time), para todos os algoritmos. Nesses testes foram tomadas as

médias aritméticas de 5 re-estimações do tempo de CPU e de 5 re-estimações do tempo

total de execução (Wall Time) para cada seqüência de aminoácidos (proteínas). Os

resultados do primeiro e do segundo conjunto de testes, os tempos de CPU e os tempos

totais de execução (Wall time), estão sintetizados nas Tabelas 5.5 e 5.6,

respectivamente. Esses tempos foram medidos tomando-se um segundo como a unidade

básica de medida do tempo e estão registrados nas tabelas como valores absolutos em

segundos.

TABELA 5.5 – Resultados dos Tempos de CPU e dos Tempos Totais de Execução (Wall Time) do Primeiro Experimento para os Algoritmos de Viterbi (Tempos em Segundos).

Viterbi com 2-níveis de checkpoints (por) Viterbi Básico linhas diagonais proposto 1 proposto 2Comp. seqüên. CPU Wall CPU Wall CPU Wall CPU Wall CPU Wall

2.000 1,248 1,304 2,326 2,338 2,154 2,480 1,016 1,066 1,024 1,025 4.000 2,552 2,580 4,798 4,826 5,124 5,155 2,158 2,175 2,176 2,315 6.000 3,896 3,993 7,330 7,385 8,232 8,280 3,342 3,369 3,292 3,305 8.000 6,254 6,292 11,946 12,044 11,148 11,224 5,926 5,953 5,902 5,923

10.000 7,862 7,916 14,968 15,065 14,260 14,368 7,448 7,501 7,394 7,428 12.000 22,498 76,456 16,034 18,148 17,280 17,389 9,150 9,204 8,938 8,974 14.000 22,048 113,206 21,078 21,210 20,436 20,564 10,498 10,560 10,498 10,53816.000 28,270 126,858 24,092 24,241 23,506 23,656 12,012 12,080 11,962 12,01518.000 33,548 144,600 27,078 27,243 26,474 26,626 13,492 13,569 13,406 13,46120.000 33,042 169,249 30,104 30,292 29,096 29,282 15,022 15,121 14,902 14,97522.000 38,735 189,375 33,156 33,366 32,286 32,474 16,560 16,668 16,404 16,47824.000 44,480 197,929 36,258 36,440 35,588 35,844 18,026 18,141 17,902 17,97926.000 48,318 208,825 39,362 39,639 38,596 38,820 19,556 19,681 19,496 19,58028.000 48,292 211,159 42,286 42,662 41,898 42,177 21,076 21,202 21,088 21,18630.000 59,052 223,569 45,314 45,621 44,760 45,069 22,622 22,762 22,506 22,60232.000 67,618 224,634 48,306 48,613 48,148 48,454 24,070 24,227 23,928 24,043

165

Page 168: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Essas medidas – os resultados – do desempenho dos algoritmos foram, também,

apresentados em dois conjuntos de gráficos, para permitir um melhor entendimento

através da visualização do comportamento destes. Foram elaborados quatro gráficos de

linhas para os dados das Tabelas 5.5 e 5.6, sendo dois para cada uma das tabelas. Os

gráficos das Figuras 5.3 e 5.4 foram confeccionados com os dados da Tabela 5.5,

representando os tempos de CPU e os tempos totais de execução, respectivamente,

enquanto que os gráficos das Figuras 5.5 e 5.6 foram confeccionados com os dados da

Tabela 5.6, representando os tempos de CPU e os tempos totais de execução,

respectivamente.

Os gráficos das Figuras 5.3 e 5.4 foram confeccionados com base nos dados

normalizados em relação aos dados do algoritmo de Viterbi básico (básico = 1). Os

gráficos das Figuras 5.5 e 5.6 foram confeccionados adotando-se a escala logarítmica

em ambos os eixos de coordenadas cartesianas, pois a amplitude dos dados é muito

grande para assumir uma escala linear e ainda assim ressaltar o comportamento da

performance dos algoritmos usados nos experimentos.

Das Tabelas 5.5 e 5.6, e das Figuras 5.3 a 5.6, pode ser verificado que: o algoritmo de

Viterbi básico provocou atividade de paginação significativa para o alinhamento de

seqüências de tamanhos maiores ou iguais a 12.000 resíduos e provocou o início de

atividade excessiva de paginação para seqüências compostas por 32.000 resíduos; o

algoritmo de Viterbi com 2-níveis de checkpoints por diagonais provocou o início da

atividade significativa de paginação para o alinhamento de seqüências de tamanhos

compreendidos entre 32.000 e 64.000 resíduos e provocou atividade excessiva de

paginação para seqüências compostas por 64.000 resíduos; o algoritmo de Viterbi com

2-níveis de checkpoints por linhas provocou o início da atividade significativa de

paginação para o alinhamento de seqüências de tamanhos compreendidos entre 256.000

e 512.000 resíduos e provocou atividade excessiva de paginação para seqüências

compostas por 512.000 resíduos.

Por outro lado, pode-se observar que os algoritmos de Viterbi propostos 1 e 2 somente

apresentaram pequenos aumentos nos tempos totais de execução para seqüências

166

Page 169: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

compostas por 512.000 resíduos, quando comparados com os respectivos tempos de

CPU. O algoritmo de Viterbi proposto 1 apresentou um aumento de aproximadamente

30%, o que talvez possa indicar proximidade do início de atividade significativa de

paginação. Já, o algoritmo de Viterbi proposto 2 apresentou um aumento de

aproximadamente 10%, o que ainda não chega a ser uma sinalização do início de

atividade significativa de paginação. Assim, o algoritmo de Viterbi proposto 1 ainda

pode alinhar seqüências bem maiores do que 512.000 resíduos (no PC usado para os

testes deste trabalho) e, o algoritmo de Viterbi proposto 2 ainda pode alinhar seqüências

maiores do que 1.024.000 resíduos no PC usado para os testes deste trabalho,

requerendo alocação de pelo menos 808 Mb de memória (Tabela 5.3).

TABELA 5.6 – Resultados dos Tempos de CPU e dos Tempos Totais de Execução (Wall Time) do Segundo Experimento para os Algoritmos de Viterbi (Tempos Em Segundos).

Viterbi com 2-níveis de checkpoints (por) Viterbi Básico linhas diagonais proposto 1 proposto 2Comp.

seqüên. CPU Wall CPU Wall CPU Wall CPU Wall CPU Wall2.000 1,248 1,304 2,326 2,338 2,154 2,480 1,016 1,066 1,024 1,025 4.000 2,552 2,580 4,798 4,826 5,124 5,155 2,158 2,175 2,176 2,315 8.000 6,254 6,292 11,946 12,044 11,148 11,224 5,926 5,953 5,902 5,923 16.000 28,270 126,858 24,092 24,241 23,506 23,656 12,012 12,080 11,962 12,015 32.000 67,618 224,634 48,306 48,613 48,148 48,454 24,070 24,227 23,928 24,043 64.000 - - 97,962 98,373 104,618 640,054 48,676 48,876 48,240 48,469

128.000 - - 196,884 197,777 - - 97,500 97,938 96,980 97,427 256.000 - - 393,384 396,260 - - 206,320 253,470 193,026 194,222512.000 - - 1924,024 6729,183 - - 436,422 559,476 423,274 468,180

Os algoritmos propostos 1 e 2 tiveram desempenhos semelhantes em relação aos tempos

de CPU e tempos totais de execução no alinhamento de seqüências no intervalo de

2.000 a 32.000 resíduos a um HMM com 2.000 nós, veja a Tabela 5.5 e as Figuras 5.3 e

5.4. Com relação aos tempos de CPU o algoritmo proposto 2 foi, na maioria das vezes,

mais rápido do que o 1, chegando a uma diferença máxima de 2.5% (6.000 resíduos),

sendo mais lento, somente, para as seqüências com 4.000 (0,8%) e 28.000 (0.06%)

resíduos. Com relação aos tempos totais de execução o algoritmo proposto 2 foi mais

167

Page 170: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

lento do que o 1 apenas para seqüências de 4.000 resíduos (6%) e, foi no máximo 2.5%

mais rápido (12.000 resíduos) para as demais seqüências.

Observando-se a Tabela 5.5 e as Figuras 5.3 e 5.4, pode-se notar que os algoritmos

propostos 1 e 2 sempre apresentaram desempenho superior aos demais para as medidas

de tempos no alinhamento de seqüências no intervalo de 2.000 a 32.000 a um HMM

com 2.000 nós, sendo no mínimo 5,2% (8.000 e 10.000 resíduos) mais rápido que o

algoritmo de Viterbi básico para as seqüências no intervalo considerado. Os algoritmos

de Viterbi propostos 1 e 2 foram com relação aos tempos de CPU, pelo menos 2,1 (em

14.000 resíduos) vezes mais rápidos, e com relação aos tempos totais de execução cerca

de 8 a 10 vezes mais rápidos do que o algoritmo de Viterbi básico para seqüências

maiores ou iguais a 12.000 resíduos – início da degradação do uso de memória pelo

algoritmo de Viterbi básico.

0,00

0,50

1,00

1,50

2,00

2,50

2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32

Tamanhos das seqüência (x 1000 radicais)

Tem

pos d

e C

PU n

orm

aliz

ados

(bás

ico

= 1)

Viterbi Básico

2-níveis Checkpoints Linha

2-níveis Checkpoints Bidimensional 1

2-níveis Checkpoints Bidimensional 2

2-níveis Checkpoints Diagonais

FIGURA 5.3 – Tempos de CPU dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 2000 nós.

168

Page 171: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O algoritmo de Viterbi com 2-níveis de checkpoints por linhas e o por diagonais

desempenharam de forma semelhante no intervalo de 2.000 a 32.000, veja a Tabela 5.5

e as Figuras 5.3 e 5.4, com discrepâncias significativas no intervalo de 2.000 a 10.000

resíduos, mas com desempenho praticamente semelhante para seqüências no intervalo

de 12.000 a 32.000 resíduos.

0,00

0,50

1,00

1,50

2,00

2,50

2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32

Tamanhos das Seqüência (x 1000 radicais)

Tem

pos t

otai

s de

exec

ução

nor

mal

izad

os (b

ásic

o =

1)

Viterbi Básico

2-níveis Checkpoints Linha

2-níveis Checkpoints Bidimensional 1

2-níveis Checkpoints Bidimensional 2

2-níveis Checkpoints Diagonais

FIGURA 5.4 – Tempos totais de execução dos algoritmos de Viterbi para alinhamentos de seqüências de aminoácidos a um HMM com 2000 nós.

Da Tabela 5.5 e da Figura 5.3 pode-se notar que o algoritmo de Viterbi com 2-níveis de

checkpoints por linhas e o por diagonais sempre apresentaou desempenho inferior aos

algoritmos propostos 1 e 2 para as medidas de tempos no alinhamento de seqüências no

intervalo de 2.000 a 32.000 resíduos a um HMM com 2.000 nós. Os algoritmos de

Viterbi propostos 1 e 2 foram, respectivamente, cerca de 1,7 a 2,5 vezes mais rápidos,

em tempos de CPU, do que o algoritmo de Viterbi com 2-níveis de checkpoints por

linhas e do que o algoritmo por diagonais. Essas comparações são muito semelhantes

para os tempos totais de execução, veja a Figura 5.4, com os algoritmos propostos 1 e 2

169

Page 172: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

sendo cerca de 1,9 a 2,5 vezes mais rápidos que o algoritmo de Viterbi com 2-níveis de

checkpoints por linhas e do que o algoritmo por diagonais.

O algoritmo de Viterbi com 2-níveis de checkpoints por linhas e o por diagonais

apresentaram desempenho inferior ao algoritmo de Viterbi básico para as medidas de

tempos no intervalo de 2.000 a 10.000 resíduos, com queda de desempenho por um

fator de ~1,8, veja a Tabela 5.5 e as Figuras 5.3 e 5.4. Por outro lado para seqüências no

intervalo de 14.000 a 32.000 resíduos eles apresentaram um desempenho superior ao

algoritmo de Viterbi básico para as medidas de tempos, com um ganho de desempenho

de até ~1,4 para os tempos de CPU e de até ~5,0 para os tempos totais de execução.

1

10

100

1000

10000

2 4 8 16 32 64128

256512

Tamanhos das seqüências (x 1000 radicais)

Tem

pos

de C

PU (s

egun

dos)

Viterbi Básico2-níveis Checkpoints Linha2-níveis Checkpoints Bidimensional 12-níveis Checkpoints Bidimensional 22-níveis Checkpoints Diagonal

FIGURA 5.5 – Tempos de CPU dos algoritmos de Viterbi para alinhamentos de seqüências de aminoácidos a um HMM com 2000 nós (eixos logarítmicos).

Os dados sintetizados na Tabela 5.6 e nas Figuras 5.5 e 5.6, apresentam resultados

relativos aos tempos de CPU e tempos totais de execução semelhantes aos da Tabela 5.5

e das Figuras 5.3 e 5.4, exceto para os pontos em que o algoritmo de Viterbi com 2-

170

Page 173: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

níveis de checkpoints por diagonais e o algoritmo por linhas exauriram os recursos de

memória do PC. O algoritmo de Viterbi com 2-níveis de checkpoints por diagonais

provocou atividade excessiva de paginação para seqüências de 64.000 resíduos e não

conseguiu alocar recursos de memória para alinhar seqüências com 128.000 resíduos,

veja nas Figuras 5.5 e 5.6, enquanto que o algoritmo de Viterbi com 2-níveis de

checkpoints por linhas provocou atividade excessiva de paginação para seqüências de

512.000 resíduos, veja nas Figuras 5.5 e 5.6.

Os algoritmos propostos 1 e 2 tiveram desempenhos semelhantes em relação aos tempos

de CPU e tempos totais de execução no intervalo de 2.000 a 512.000 resíduos, veja a

Tabela 5.6 e as Figuras 5.5 e 5.6. Enquanto, o algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais e o algoritmo por linhas, também, desempenharam de forma

semelhante até o ponto em que o algoritmo por diagonais exauriu os recursos de

memória do PC, ou seja, para seqüências no intervalo de 2.000 a 32.000 resíduos.

1

10

100

1000

10000

2 4 8 16 32 64 128

256

512

Tamanhos das seqüências (x 1000 radicais)

Tem

pos

tota

is d

e ex

ecuç

ão (s

egun

dos)

Viterbi Básico2-níveis Checkpoints Linha2-níveis Checkpoints Bidimensional 12-níveis Checkpoints Bidimensional 22-níveis Checkpoints Diagonal

FIGURA 5.6 – Tempos totais de execução dos algoritmos de Viterbi para alinhamentos de seqüências de aminoácidos a um HMM com 2000 nós (eixos logarítmicos).

171

Page 174: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

O algoritmo de Viterbi com 2-níveis de checkpoints por linhas e os algoritmos propostos

1 e 2 tiveram comportamento bastante semelhante no intervalo considerado, até que o

algoritmo por linhas exaurisse os recursos de memória do PC, o que ocorreu para

seqüências com 512.000 resíduos, veja a Tabela 5.6 e as Figuras 5.5 e 5.6. Pode-se notar

que os algoritmos propostos 1 e 2 foram ~2 vezes mais rápidos do que o algoritmo de

Viterbi com 2-níveis de checkpoints por linhas, no intervalo correspondendo a

seqüências com 2.000 a 256.000 resíduos.

0,00

10,00

20,00

30,00

40,00

50,00

60,00

70,00

80,00

2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32

Tamanhos das seqüência (x 1000 radicais)

Tem

pos d

e C

PU (s

egun

dos)

Viterbi Básico2-níveis Checkpoints Linha2-níveis Checkpoints Bidimensional 12-níveis Checkpoints Bidimensional 22-níveis Checkpoints Diagonais

FIGURA 5.7 – Tempos de CPU dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 2000 nós.

Nota-se, também, observando as Figuras 5.3, 5.4, 5.5 e 5.6, que os algoritmos propostos

1 e 2 tiveram desempenhos similares no alinhamento de seqüências a um HMM com

2.000 nós, em todo o intervalo considerado, quase sempre com uma leve vantagem em

desempenho para o algoritmo proposto 2. É importante salientar que esse ganho em

desempenho, mesmo que por um fator muito pequeno é uma informação significativa,

pois o algoritmo proposto 2 usa menos memória e efetua mais re-computações que o

algoritmo proposto 1 e, logo, deveria sofrer uma queda de desempenho maior. Talvez

172

Page 175: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

isto se deva ao fato que o algoritmo proposto 2 divida a matriz de PD em um número 4

vezes menor de seções do que o algoritmo proposto 1 e, portanto, tire mais proveito do

retrocedimento restrito. O que ressalta o comportamento, até intuitivo, do uso de

procedimentos de retrocedimento restrito nesses algoritmos com checkpoints é que: o

aumento da memória disponível para uma determinada instância do problema provoca

uma redução no ganho de performance adicional devido ao uso do retrocedimento

restrito, ou alternativamente, a redução da memória disponível para uma determinada

instância do problema provoca um aumento no ganho de performance adicional devido

ao uso do retrocedimento restrito.

0,00

50,00

100,00

150,00

200,00

250,00

2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32

Tamanhos das seqüência (x 1000 radicais)

Tem

pso

tota

is d

e ex

ecuç

ão (s

egun

dos)

Viterbi Básico2-níveis Checkpoints Linha2-níveis Checkpoints Bidimensional 12-níveis Checkpoints Bidimensional 22-níveis Checkpoints Diagonais

FIGURA 5.8 – Tempos totais de execução dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 2000 nós.

É importante comparar esses resultados com aqueles obtidos nos experimentos de

Tarnas e Hughey (1998), para análise do comportamento de algoritmos de programação

dinâmica usados na computação de medidas de interesse em HMMs. Nesses

experimentos seqüências de aminoácidos menores do que ou iguais a 10.000 resíduos

são utilizadas em computações com o algoritmo de Viterbi, ambos com e sem

173

Page 176: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

checkpoints. O uso significativo de memória virtual pelos algoritmos de programação

dinâmica básicos inicia-se para seqüências maiores do que 7.000 resíduos e tornam-se

críticos para seqüências maiores do que 8.000 resíduos. Eles consideram o algoritmo de

Viterbi básico e o com 2-níveis de checkpoints por diagonais e retrocedimento restrito,

que é justamente o algoritmo alvo com o qual se deseja comparar os algoritmos com 2-

níveis de checkpoints bidimensionais 1 e 2, propostos nesse trabalho.

Para uma melhor comparação visual dos dados obtidos no conjunto de testes do

experimento 1 deste trabalho com aqueles obtidos por Tarnas e Hughey (1998) em seus

experimentos, foram elaborados dois gráficos, veja Figuras 5.7 e 5.8, com os dados em

valores absolutos, referentes aos dados sintetizados na Tabela 5.5, além daqueles

normalizados (básico = 1) mostrados nas Figuras 5.3 e 5.4. Os resultados obtidos por

Tarnas e Hughey (1998) são apresentados nas Figuras 5.9 e 5.10 e foram extraídos da

Figura 3 do trabalho deles.

FIGURA 5.9 – Tempos de CPU para efetuar 4 execuções de computações de programação dinâmica de seqüências de aminoácidos contra um HMM com 500 nós. FONTE: Extraído da Figura 3 de Tarnas e Hughey (1998).

Os comportamentos obtidos no experimento 1 desta tese apresentam diferenças

significativas, com relação às medidas dos tempos de execução, daqueles obtidos por

174

Page 177: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Tarnas e Hughey (1998), na comparação com o algoritmo de Viterbi básico. Os

experimentos efetuados nesse estudo indicaram que o algoritmo de Viterbi com 2-níveis

de checkpoints por diagonais apresentaram queda de desempenho, em relação ao

algoritmo de Viterbi básico, por um fator de ~1,8 no intervalo anterior ao início de

atividade de paginação siginificativa, tanto para os tempos de CPU quanto para os

tempos totais de execução. Nesses experimentos, após o início de atividade significativa

de paginação pelo algoritmo de Viterbi básico, o algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais apresentou um ganho de desempenho por um fator de ~1,4

para os tempos de CPU e de até ~5,0 para os tempos totais de execução. Ao passo que

os experimentos em Tarnas e Hughey (1998) apresentaram desempenhos bastante

próximos para os tempos de CPU em todo o intervalo considerado e para os tempos

totais de execução até o início de atividade de paginação siginificativa. Apresentaram

ganho de perfomance do algoritmo por diagonais sobre o básico, por um fator de até

~10 vezes para os tempos totais de execução, após o início de atividade de paginação

siginificativa. Veja a Tabela 5.5 e as Figuras 5.7, 5.8, 5.9 e 5.10.

FIGURA 5.10 – Tempos totais de execução para efetuar 4 execuções de computações de programação dinâmica de seqüências de aminoácidos contra um HMM com 500 nós. FONTE: Extraída da Figura 3 de Tarnas e Hughey (1998).

175

Page 178: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Os resultados do terceiro conjunto de testes, para alinhar seqüências de proteinas a um

HMM com 368 nós, estão resumidos na Tabela 5.7, eles são compostos pelos tempos de

CPU e pelos tempos totais de execução (Wall time). Para permitir um melhor

entendimento do comportamento destes resultados através da visualização gráfica,

foram confeccionados gráficos de linhas com base nos dados normalizados em relação

aos dados do algoritmo de Viterbi básico (básico = 1). Esses gráficos estampados nas

Figuras 5.11 e 5.12, referem-se aos tempos de CPU e tempos totais de execução,

respectivamente.

Pode ser verificado, observando-se a Tabela 5.7 e as Figuras 5.11 e 5.12, que o

algoritmo de Viterbi com 2-níveis de checkpoints por linhas com particionamento móvel

de memória e o algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, com

particionamento móvel de memória e retrocedimento restrito, foram mais lentos do que

o algoritmo algoritmo de Viterbi básico. Eles foram em média 74,7% e 10,3% mais

lentos com relação aos tempos de CPU, respectivamente, e em média 72,4% e 10,1%

com relação aos totais de exucução, respectivamente.

TABELA 5.7 – Resultados dos Tempos de CPU e dos Tempos Totais de Execução (Wall Time) do Terceiro Experimento para os Algoritmos de Viterbi (Tempos em Segundos).

Viterbi com 2-níveis de checkpoints (por) Viterbi Básico linhas diagonais proposto 1 proposto 2 Comp.

seqüên. CPU Wall CPU Wall CPU Wall CPU Wall CPU Wall 100 0,011 0,012 0,018 0,019 0,011 0,012 0,010 0,011 0,010 0,011 200 0,022 0,024 0,036 0,038 0,022 0,024 0,020 0,021 0,020 0,020 300 0,033 0,035 0,055 0,057 0,036 0,038 0,030 0,032 0,030 0,031 400 0,044 0,046 0,075 0,077 0,050 0,050 0,040 0,041 0,040 0,040 500 0,055 0,058 0,096 0,098 0,062 0,064 0,050 0,052 0,050 0,051 600 0,066 0,068 0,119 0,120 0,075 0,077 0,060 0,061 0,060 0,060 700 0,081 0,082 0,142 0,144 0,090 0,091 0,070 0,072 0,070 0,071 800 0,087 0,089 0,161 0,164 0,100 0,103 0,079 0,080 0,075 0,079 900 0,100 0,104 0,189 0,192 0,115 0,120 0,091 0,092 0,090 0,091

1.000 0,115 0,117 0,206 0,210 0,130 0,131 0,099 0,099 0,095 0,099

Observando-se a Tabela 5.7 e as Figuras 5.11 e 5.12 pode-se notar a superioridade dos

algoritmos propostos 1 e 2 em relação aos demais algoritmos, tanto no que tange aos

176

Page 179: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

tempos de CPU quanto aos tempos totais de execução. Os algoritmos propostos 1 e 2

foram superiores em desempenho até mesmo ao próprio algorimo de Viterbi básico: eles

foram em média 9,5% e 11%, respectivamente, mais rápidos do que o algorimo de

Viterbi básico para os tempos de CPU; e foram em média 10,7% e 12,4%,

respectivamente, mais rápidos do que o algorimo de Viterbi básico para os tempos de

totais de execução. Eles foram superiores em desempenho ao algoritmo de Viterbi com

2-níveis de checkpoints por diagonais, com particionamento móvel de memória e

retrocedimento restrito: o algoritmo por diagonais foi em média 22% e 24% mais lento

que os algoritmos propostos 1 e 2, respectivamente, com relação aos tempos de CPU; e

foi em média 23,6% e 25,7% mais lento que os algoritmos propostos 1 e 2,

respectivamente, com relação aos tempos totais de execução.

0,00

0,20

0,40

0,60

0,80

1,00

1,20

1,40

1,60

1,80

2,00

1 2 3 4 5 6 7 8 9 10

Tamanhos das seqüências (x 100 radicais)

Tem

pos d

e C

PU n

orm

aliz

ados

(bás

ico

= 1)

Viterbi Básico

2-níveis Checkpoints Linha

2-níveis Checkpoints Bidimensional 1

2-níveis Checkpoints Bidimensional 2

2-níveis Checkpoints Diagonais

FIGURA 5.11 – Tempos de CPU dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 368 nós.

É importante salientar que, também, para esse terceiro conjunto de experimentos o

algoritmo proposto 1 foi ligeiramente superior ao algoritmo proposto 2: ele foi em

177

Page 180: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

média 1,6% mais rápido com relação aos tempos de CPU e em média 1,8% mais rápido

com relação totais de execução. Nota-se, novamente, que esse ganho em desempenho

talvez se deva ao fato que o algoritmo proposto 2 tire mais proveito do uso do

procedimento de retrocedimento restrito, mesmo para seqüências pequenas, com

comprimentos variando de 100 a 1000 símbolos.

0,00

0,20

0,40

0,60

0,80

1,00

1,20

1,40

1,60

1,80

2,00

1 2 3 4 5 6 7 8 9 10

Tamanhos das Seqüências (x 100 radicais)

Tem

pos t

otai

s de

exec

ução

nor

mal

izad

os (b

ásic

o =

1)

Viterbi Básico

2-níveis Checkpoints Linha

2-níveis Checkpoints Bidimensional 1

2-níveis Checkpoints Bidimensional 2

2-níveis Checkpoints Diagonais

FIGURA 5.12 – Tempos totais de execução dos algoritmos Viterbi para alinhamento de seqüências de aminoácidos a um HMM com 368 nós.

Analisando os resultados obtidos nos experimentos desta tese, nota-se que o algoritmo

de Viterbi com 2-níveis de checkpoints por linhas e particionamento móvel de memória

se comportou como o esperado para essa família de algoritmos com checkpoints.

Primeiro, o seu requerimento de memória, supondo m = n, que é da ordem de ( )nnO –

pois nnnnR 22 ≤≈ – foi sempre cerca de 3,2% do requerimento do algoritmo de

Viterbi básico. Segundo, ele apresentou perda de performance por um fator ~1,8 vezes o

algoritmo de Viterbi básico em condições de uso de memória virtual baixo e ganho de

performance por um fator de no máximo ~1,4 vezes para os tempos de CPU e de no

máximo ~5,0 vezes para os tempos totais de execução, após o algoritmo de Viterbi

178

Page 181: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

básico começar a fazer uso significativo da memória virtual. Terceiro, ele itera de forma

regular sobre a matriz de PD, fazendo uso adequado da hierarquia de memória do PC.

Quarto, o comportamento do crescimento dos seus tempos de execução foi proporcional

ao comportamento de seus requerimentos de memória. Além disso, ele não fez uso do

procedimento de retrocedimento restrito.

Da análise dos resultados obtidos nos experimentos desta tese, nota-se que o algoritmo

de Viterbi com 2-níveis de checkpoints propostos 1 e 2 se comportaram melhor que o

esperado, de uma maneira geral, para essa família de algoritmos com checkpoints.

Primeiro, o seu requerimento de memória, supondo m = n, que é da ordem de ( )nnO –

pois nnR 4= para o proposto 1 e nnR 2= para o proposto 2 – foi sempre

decrescente para tamanhos crescentes de seqüências, em relação ao requerimento do

algoritmo de Viterbi básico, chegando, inclusive, a ser menor do que aqueles do

algoritmo de Viterbi por linhas para as seqüências maiores, que são as que interessam.

Segundo, ele apresentou, nos experimentos 1 e 2, ganho sobre todos os outros

algoritmos: ganho de no mínimo 5,2% sobre o algoritmo de Viterbi básico em condições

de uso de memória virtual baixo e ganho de performance por um fator de no mínimo ~2

vezes para os tempos de CPU e de ~8,0 a 10 vezes para os tempos totais de execução,

após o algoritmo de Viterbi básico começar a fazer uso significativo da memória virtual.

Terceiro ele apresentou desempenho superior aos demais algoritmos para o alinhamento

de seqüências pequenas a um HMM também pequeno, conforme os resultados do

experimento 3, com ganho de performance de aproximadamente 10% em relação ao

algoritmo de Viterbi básico e superior a 20% em relação ao algoritmo por diagonais.

Quarto, ele itera de forma regular sobre a matriz de PD, fazendo uso adequado da

hierarquia de memória do PC. Quinto, o comportamento do crescimento dos seus

tempos de execução foi proporcional ao comportamento de seus requerimentos de

memória. Além disso, ele fez uso do procedimento de retrocedimento restrito e se

beneficia do aumento de desempenho proporcionado por esse procedimento,

principalmente quando se usa menos linhas, R, de memória para as mesmas instâncias

do problema.

179

Page 182: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Com relação ao algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, com

particionamento móvel de memória e retrocedimento restrito, analisando-se os

resultados obtidos nos experimentos, nota-se que ele se comportou aquém do esperado,

em geral, para essa família de algoritmos com checkpoints. Primeiro, o seu

requerimento de memória, supondo m = n, que é da ordem de ( )nnO – pois

nnnndnR 884 ≤=≈ – foi sempre crescente para tamanhos crescentes de

seqüências, em relação ao requerimento do algoritmo de Viterbi básico; com os

requerimentos de memória do algoritmo por diagonais tendendo ao infinito mais

rapidamente do que o algoritmo básico, quando o tamanho das seqüências tendem ao

infinito. Segundo, ele apresentou comportamento de desempenho inferior ao algoritmo

por linhas, pois além de poucas vezes superá-lo por um fator irrisório, não foi capaz de

computar alinhamentos de seqüências acima de 64.000 resíduos. Ele, também,

apresentou perda de performance por um fator ~1,8 vezes com relação ao algoritmo de

Viterbi básico em condições de uso de memória virtual baixo e ganho de performance

por um fator de no máximo ~1,4 vezes para os tempos de CPU e de no máximo ~5,0

vezes para os tempos totais de execução, após o algoritmo de Viterbi básico começar a

fazer uso significativo da memória virtual. Terceiro, mas dentro do critério de escolha

dos tamanhos de seqüências ele só conseguiu alinhar seqüências de até 64.000 resíduos,

já com uso intenso de memória virtual. Quarto, ele itera de forma regular sobre a matriz

de PD, fazendo uso adequado da hierarquia de memória do PC. Quinto, o

comportamento do crescimento dos seus tempos de execução foi proporcional ao

comportamento de seus requerimentos de memória, que crescem mais rapidamente do

que os requerimentos do algoritmo básico. Além disso, ele fez uso do procedimento de

retrocedimento restrito.

Sabe-se, ao menos teoricamente, que o algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais, com particionamento móvel de memória e sem o

retrocedimento restrito, para as mesmas instâncias do problema, computa matrizes de

PD ~2 vezes maiores que os demais algoritmos considerados nos experimentos desta

tese, e que isto acarreta uma perda de performance de ~2 vezes em relação ao algoritmo

de Viterbi com 2-níveis de checkpoints por linhas, com particionamento móvel de

180

Page 183: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

memória e sem retrocedimento restrito. Além disso foi levantado que existiria perda de

performance devido ao tratamento das condições de contorno para computar diagonais

ao invés de linhas ou colunas. Mas Tarnas e Hughey (1998) esperavam que, com a

adição do procedimento de retrocedimento restrito, o algoritmo por diagonais pudesse

superar o algorimo por linhas. Entretanto, eles realizaram e divulgaram experimentos

onde o algoritmo por diagonais apresentou um desempenho quase próximo ao

desempenho do algoritmo de Viterbi básico em condições normais de uso da memória

virtual e superior para condições de uso intenso da memória virtual.

Adicionalmente, nas suas conclusões eles relataram que o ganho de performance não foi

equivalente ao aumento de recursos de memória alocados ao algoritmo e às dificuldades

impostas pelas condições de contorno para computar diagonais ao invés de linhas ou

colunas, tanto no nível de codificação do algoritmo, quanto no nível de desempenho

deste, e planejaram retornar ao desenvolvimento do algoritmo por linhas.

Por outro lado, os resultados do presente estudo apontam, principalmente com relação

ao comportamento assintótico dos requerimentos de memória, mas também com relação

ao desempenho, que o algoritmo com 2-níveis de checkpoints por diagonais parece

bastante limitado para ser tomado como uma alternativa consistente ao algoritmo básico

para a computação de matrizes de PD, quando os tamanhos das instâncias do problema

implicam em exaustão dos recursos de memória do PC pelo algoritmo básico. O que é

agravado, tanto pelo comportamento assintótico de requerimentos de memória dos

outros algoritmos considerados, quanto pelo desempenho apresentado por esses nos

testes efetuados nesse estudo. O algoritmo por diagonais só pôde computar

alinhamentos de seqüências de comprimentos até 64.000 resíduos, enquanto os demais

algoritmos, com exceção do básico, computaram seqüências de tamanhos até 512.000

resíduos e apresentaram desempenho superior a esse.

Os algoritmos propostos 1 e 2 apresentaram o melhor comportamento assintótico dos

requerimentos de memória e tiveram desempenhos melhores que os demais algoritmos

considerados nos experimentos. Considerando instâncias grandes do problema de

alinhamento de seqüências a um HMM com 2.000 nós os algoritmos propostos foram

181

Page 184: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

bem melhores que o segundo colocado, o algoritmo de Viterbi com 2-níveis de

checkpoints por linhas, com particionamento móvel de memória (sem retrocedimento

restrito). Eles apresentaram ganho de desempenho de ~2 vezes para instâncias do

problema que provoquem uso moderado da memória virtual, e ganhos maiores para

instâncias do problema que provoquem uso intenso da memória virtual, ganho de ~5

vezes nos tempos de CPU e de ~12 vezes nos tempos totais de execução. Mesmo

considerando os resultados do experimento 3 para instâncias pequenas do problema,

quando foram alinhadas seqüências de 100 a 1000 resíduos a um HMM com 368 nós, os

algoritmos propostos 1 e 2 foram cerca de 10% mais rápidos que o algorito básico e

cerca de 20% mais rápidos do que o algoritmo por diagonais. O que indica,

possivelmente, um melhor uso da hierarquia de memória do PC – principalmente da

memória cache – pelo algoritmos propostos 1 e 2 do que o algoritmo de Viterbi com 2-

níveis de checkpoints por linhas.

Essa maior eficiência no uso da hierarquia do sistema de memória do PC,

particularmente com relação ao uso da cache, se deve à forma de particionamento da

matriz de PD em seções pelos algoritmos propostos, em forma de grade. Desta forma na

fase de retrocedimento as seções a serem recomputadas pelos algoritmos propostos têm

linhas de comprimentos menores ou iguais a 2R

n (máximos), contra linhas de

tamanhos n (tamanho da seqüência) no caso do algoritmo por linhas. Isto ressalta a

eficiência no uso da hierarquia do sistema de memória do PC por esses algoritmos

propostos 1 e 2. É importante salientar que o uso de retrocedimento restrito pode reduzir

significativamente os tamanhos destas seções.

Considerando-se apenas 2-níveis de checkpoints, os algoritmos testados no presente

estudo não fazem uso de recursividade, nem durante a primeira fase de cálculos para

frente e nem durante a segunda fase de cálculos para trás, durante o retrocedimento.

Entretanto, se forem considerados níveis mais altos de checkpoints, os membros desta

família de algoritmos, que recorrem ao método de checkpoints e que adotam

particionamento da matriz de PD apenas em uma dimensão, precisam, durante a fase de

retrocedimento, recorrer a processos recursivos, ainda que não explicitamente. Já

182

Page 185: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

aqueles que adotam particionamento da matriz de PD em duas dimensões não fazem uso

de nenhum processo recursivo, nem durante a fase para frente e, nem durante a fase de

retrocedimento.

Verifica-se, na prática, que o algoritmo de Viterbi com 2-níveis de checkpoints

bidimensionais por linhas e colunas, com particionamento fixo de memória e

retrocedimento restrito, utiliza de forma mais eficiente o sistema de memória,

particularmente com relação ao uso da cache, que os membros com 2-níveis de

checkpoints por linhas. Isto indica que o uso explícito e não recursivo do princípio D&C

em conjunto com o paradigma de checkpoints se beneficia de uso mais eficiente da

hierarquia de memória do PC, além de reduzir significativamente a quantidade de

recalculações nas seções da matriz de PD.

183

Page 186: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

184

Page 187: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

CAPÍTULO 6

CONCLUSÕES E COMENTÁRIOS

Os HMMs lineares e mais precisamente os perfis-HMMs que são os modelos que

motivaram essa tese, utilizam métodos de PD para o cálculo de medidas de desempenho

e para o treinamento (cálculo dos parâmetros) do modelo. Os algoritmos que

implementam esses métodos de PD, que são o objeto deste estudo, podem se beneficiar

de técnicas ou métodos de economia de espaço (memória de trabalho). Essa economia

de espaço, geralmente, é obtida em troca de uma queda de desempenho no tempo de

execução do algoritmo.

Existem na literatura pertinente duas classes principais de algoritmos de PD usados na

comparação de strings ou seqüências de símbolos, que recorrem a técnicas (métodos ou

paradigmas) de economia de espaço. Uma delas fundamentada no princípio D&C e a

outra no paradigma de checkpoints. O algoritmo pioneiro de Hirschberg (1975) é o

principal algoritmo da classe de algoritmos que adotam primariamente o princípio

D&C para economia de espaço, sendo os demais variantes deste, ou que agregam

secundariamente outras técnicas ao núcleo deste (programação dinâmica, caminhos

mínimos, contornos, vetores de bit), ou que estendem o original para efetuar outros tipos

de comparações. Esses algoritmos têm sido utilizados na computação da menor

substring comum (LCS) entre duas strings (ou do problema dual do script de edição

mínimo) e na comparação de pares de strings ou seqüências, usando funções de custo

um pouco mais complexas, como funções de custo afim que foram apresentadas por

Myers e Miller (1988), entretanto, não existe, até onde se conhece, na literatura

nenhuma aplicação destes algoritmos D&C em conjunto com HMMs.

Talvez, alguns dos algoritmos desta classe, fundamentada no princípio D&C, possam

ser estendidos para comparar seqüências a um perfil-HMM e recuperar o melhor

caminho de estados, que provavelmente tenha produzido determinada seqüência de

observações O. Contudo, esses métodos não são convenientes para serem usados com

procedimentos de retrocedimento completo sobre a matriz de PD, quando é necessário

recuperar todos os caminhos de estados num HMM, que provavelmente tenham

185

Page 188: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

produzido uma dada seqüência de observações O. Mas, ainda permanecem como

desafio, a utilização das idéias usadas nesses algoritmos no desenvolvimento de outros

algoritmos que possam ser usados para computar medidas de interesse nos perfis-

HMMs.

A outra classe se refere a uma família de métodos, apresentada no Capítulo 3, que

fazem uso do método de PD e do paradigma (ou estratégia) de checkpoints para

comparação e/ou alinhamento de seqüências com economia de espaço – com

necessidade reduzida de uso de memória de trabalho – que podem ser usados com

procedimentos de retrocedimento tanto parciais quanto completos, e que são

convenientes para implementação tanto serial quanto paralela. Numa implementação

serial eles requerem uso de memória da ordem de O( L nm ) com um fator de queda de

desempenho proporcional a kL, com 0 1≤< k . Desta forma, essa família de métodos

com o paradigma de checkpoints para economia de espaço se constitui na opção mais

interessante para serem usadas, quando se requer tanto a aplicação de retrocedimento

parcial, quanto a de retrocedimento completo sobre a matriz de PD, para a computação

de medidas de interesse.

Quando se usa perfis-HMMs para análise e modelagem de seqüências biológicas é

necessário se obter medidas ou cálculos que requeiram tanto os procedimentos de

retrocedimento parcial, quanto os de retrocedimento completo. Desta forma, o presente

estudo voltou-se exclusivamente para essa segunda classe de algoritmos de PD, que

fazem uso do paradigma de checkpoints como método de economia de espaço.

No presente estudo foi estudado um conjunto de algoritmos desta família de algoritmos

de PD com L-níveis de checkpoints, dos quais não foram encontradas qualquer

referência na literatura pertinente, que usam o paradigma de checkpoints em conjunto

com o princípio D&C, recorrendo a armazenagem de checkpoints nas duas dimensões

da matriz de PD – linhas e colunas. Esses algoritmos foram denominados algoritmos

com L-níveis de checkpoints bidimensionais e foram detalhados no Capítulo 4. Os

estudos da complexidade destes algoritmos foram restritos àqueles com particionamento

186

Page 189: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

fixo de memória, entretanto eles podem ser estendidos, sem perda de generalidade, para

cobrir o caso de particionamento móvel de memória.

Para efeito de análise, comparação e experimentação, o presente estudo se concentrou

nos algoritmos com 2-níveis de checkpoints com retrocedimento parcial usados na

comparação e alinhamento de seqüências a um perfil-HMM. Um destes algoritmos

propostos, o de Viterbi com 2-níveis de checkpoints bidimensionais com

particionamento fixo de memória e retrocedimento restrito, foi escolhido para

realização, a posteriori, de testes de desempenho numa comparação com o algoritmo de

Viterbi com 2-níveis de checkpoints por diagonais, com particionamento móvel de

memória e retrocedimento restrito – considerado o estado da arte desta família de

algoritmos com L-níveis de checkpoints – e com o algoritmo de Viterbi com 2-níveis de

checkpoints por linhas com particionamento móvel de memória.

Os estudos teóricos indicaram, para instâncias genéricas do problema, e sem considerar

a aplicação de procedimento de retrocedimento restrito, que esses algoritmos de Viterbi

com L-níveis checkpoints bidimensionais e particionamento fixo de memória

apresentam um comportamento assintótico de requerimentos de espaço e de tempos de

execução semelhantes aos demais membros desta família. Considerando-se m = n e um

nível L arbitrário, os requerimentos de tempos são da ordem de ( )2nO e os

requerimentos de espaço são da ordem de ( )L nnO para os algoritmos bidimensionais e

para os algoritmos unidimensionais por linhas, ou por colunas, ou por diagonais.

Entretanto, se forem considerados os coeficientes das funções de complexidade

assintótica, verifica-se uma superioridade em desempenho dos algoritmos de Viterbi

com L-níveis de checkpoints bidimensionais 1 e 2. Os requerimentos de memória do

algoritmo de Viterbi com L-níveis de checkpoints por linhas ou colunas são dados por LL nLnLR ≤≤ ! , apresentando portanto um coeficiente kL, 10 << k , para a função de

complexidade de espaço. O algoritmo de Viterbi com L-níveis de checkpoints por

diagonais tem requerimento de espaço dado por LLn 42! ≤L nLR 2≤ , apresentando

portanto um coeficiente 4kL, 10 << k . Enquanto o algoritmo de Viterbi com L-níveis

187

Page 190: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

de checkpoints bidimensionais 1 tem requerimento de espaço dado por LL nnR 22 ≤≤ ,

apresentando portanto um coeficiente 2, o algoritmo de Viterbi com L-níveis de

checkpoints bidimensionais 2 tem requerimento de espaço dado por L nR ≤ ,

apresentando portanto um coeficiente 1. Essa redução nos requerimentos de espaço é

obtida em troca de uma queda de desempenho. Os algoritmos de Viterbi com L-níveis

de checkpoints bidimensionais 1 e 2 apresentam queda de desempenho dada por um

coeficiente proporcional a 2k, com 0 1≤< k

10

. Já o algoritmo de Viterbi com L-níveis de

checkpoints por linhas ou colunas apresenta queda de desempenho dada por um

coeficiente proporcional a kL, com ≤< k , e o algoritmo de Viterbi com L-níveis de

checkpoints por diagonais apresenta queda de desempenho dada por um coeficiente

proporcional a 2kL, com (detalhes nos Capítulos 3 e 4). 10 ≤< k

( )3nO

Adicionalmente, pode-se verificar que a superioridade dos algoritmos de Viterbi com L-

níveis de checkpoints bidimensionais 1 e 2 é maior quando é levado em consideração

uma análise de pior caso. Numa análise de pior caso pode-se tomar um nível L = n de

checkpoints. Para essas instâncias do problema o algoritmo de Viterbi com L-níveis de

checkpoints por linhas (ou colunas) e o de Viterbi com L-níveis de checkpoints por

diagonais com particionamento móvel de memória apresentam complexidade assintótica

de tempo da ordem de , com coeficiente de complexidade igual a 1 para o

algoritmo por linhas (ou colunas) e igual 2 para o algoritmo por diagonais. Enquanto a

complexidade dos algoritmos de Viterbi com L-níveis de checkpoints bidimensionais 1 e

2 com particionamento fixo de memória é uma ordem de magnitude mais rápido,

apresentando complexidade assintótica da ordem de ( )2nO , com coeficiente de

complexidade igual a 2. Para essas instâncias do problema os requerimentos de

memória destes algoritmos são mínimos, com o algoritmo por linhas e o algoritmo por

colunas requerendo apenas duas linhas de memória, R = 2 checkpoints, o algoritmo por

diagonais requerendo três linhas de memória, R = 3, e os algoritmos bidimensionais

requerendo 4 linhas, R = 4.

Quando a análise aborda instâncias onde o nível de checkpoints é tomado como sendo

, o comportamento destes algoritmos de Viterbi com L-níveis de checkpoints nL blog=

188

Page 191: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

são menos discrepantes em termos dos coeficientes de complexidade. Os algoritmos

bidimensionais de Viterbi com L-níveis de checkpoints apresentam complexidade da

ordem de ( )2nO , com queda de desempenho dada por um fator de no máximo 1 vez,

enquanto o algoritmo de Viterbi com L-níveis de checkpoints por linhas, ou por colunas,

ou por diagonais apresenta ordem de complexidade de ( )2nO

~ e

, com queda de

desempenho dada por um fator de ~ vezes,

respectivamente. Para essas instâncias do problema, o comportamento assintótico da

complexidade dos requerimentos de memória são praticamente uma ordem de

magnitude menores para todos esses algoritmos, eles são da ordem de O .

nblog~ ,

L blog

nblog nblog2

nn blog

L =

( )

=

Mesmo quando é adicionado um procedimento de retrocedimento restrito ao algoritmo

de Viterbi com L-níveis de checkpoints por diagonais e particionamento móvel de

memória, o comportamento assintótico da complexidade de tempo deste ainda continua

sendo inferior ao dos algoritmos de Viterbi com L-níveis de checkpoints bidimensionais

e particionamento fixo de memória, mesmo sem a utilização do procedimento de

retrocedimento restrito pelos algoritmos bidimensionais, além de requerer 2L ou 4L

vezes mais memória do que o algroritmos bidimensionais para computar uma

determinada matriz de PD para um dado nível L de checkpoints.

Grice et al. (1997, p. 51) concluíram que não existe qualquer penalidade na performance

do algoritmo de Viterbi com L-níveis de checkpoints por diagonais, com

particionamento móvel de memória e retrocedimento restrito, quando o nível

. Mas, seguindo os passos das demonstrações esboçadas por eles no relatório

pode-se mostrar que, ao contrário da conclusão apresentada por eles, o algoritmo por

diagonais sofre uma perda de performance de até 2 vezes, quando o nível ,

veja mais detalhes na Seção 3.3.5, página 94. Enquanto, no presente estudo foi mostrado

que o algoritmo proposto, para um nível

nL blog=

n2log

n , o algoritmo de Viterbi com L-

níveis de checkpoints bidimensionais e particionamento fixo de memória não apresenta

perda de performance, mesmo sem a necessidade de recorrer a um procedimento de

retrocedimento restrito, veja mais detalhes na Seção 4.2.4, página 118.

189

Page 192: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Do exposto nos parágrafos anteriores, foi verificado que os coeficientes da

complexidade de tempo e de espaço do algoritmo de Viterbi com L-níveis de

checkpoints por diagonais e particionamento móvel de memória, mesmo quando esse

recorreu ao uso de retrocedimento restrito, se mostraram, sempre, superiores aos

coeficientes dos outros algoritmos com checkpoints considerados nesse estudo. Desta

forma, do ponto de vista teórico, esse algoritmo por diagonais se constitui na escolha

mais pobre como alternativa ao algoritmo de Viterbi básico, para instâncias intratáveis

por esse, numa determinada máquina (PC) serial, do que os outros algoritmos com

economia de espaço: o algoritmo de Viterbi com L-níveis de checkpoints por linhas (ou

colunas) e particionamento móvel de memória e os algoritmos de Viterbi com L-níveis

de checkpoints bidimensionais e particionamento fixo de memória.

Foi verificado, assim, que o algoritmos propostos, algoritmos de Viterbi com L-níveis

de checkpoints bidimensionais e particionamento fixo de memória, foram os que

apresentaram os menores coeficientes para as complexidades de espaço e de tempo

dentre esses algoritmos com economia de espaço, considerados no presente estudo.

Portanto, do ponto de vista teórico, esses algoritmos propostos se constituem na escolha

mais consistente, dentre os algoritmos considerados, como alternativa ao algoritmo de

Viterbi básico, para instâncias intratáveis por esse algoritmo básico numa determinada

máquina (PC) serial. Se a esses algoritmos bidimensionais forem agregados um

procedimento de retrocedimento restrito, essa superioridade ficará ainda mais evidente.

Entretanto, é bom salientar que o algoritmo de Viterbi com L-níveis de checkpoints por

linhas (ou colunas) e particionamento móvel de memória obteve coeficientes bem

melhores do que o algoritmo por diagonais e portanto, também, se constitui em uma

escolha boa como alternativa ao algoritmo de Viterbi básico.

A utilização de técnicas ou métodos de economia de espação, a princípio, é útil para o

tratamento de instâncias do problema que não podem ser manipuladas por um algoritmo

padrão (ou básico). Desta forma o comportamento assintótico dos requerimentos de

espaço e a penalização que se paga em troca, em termos de tempos de execução, devem

ser levados em consideração. Supondo que o algoritmo básico requeira uma quantidade

190

Page 193: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

de memória definida como MEM, espera-se que os requerimentos dos algoritmos com

economia de espaço sejam uma fração dos requerimentos de espaço do algoritmo

básico, ou seja k×MEM para 10 ≤< k , principalmente para instâncias do problema que

não podem ser manipulados pelo algoritmo básico. Melhor ainda se fossem limitados

por alguma função de complexidade menor do que aquela da complexidade do

requerimento de espaço do algoritmo básico, ou se fossem constantes.

Foi verificando o comportamento assintótico dos requerimentos de memória destes

algoritmos com 2-níveis de checkpoints em relação aos requerimentos de memória do

algoritmo de Viterbi básico, através de uma simulação das estimativas da memória

necessária para acomodar as principais estruturas de dados.

Essa simulação do comportamento assintótico dos requerimentos de memória do

algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, com particionamento

móvel de memória e retrocedimento restrito apresentou indícios de que os requisitos de

memória deste crescem para o infinito mais rapidamente que os requisitos de memória

do algoritmo de Viterbi básico, quando o tamanho das seqüências crescem

indefinidamente. Sendo esses requerimentos maiores do que os do algoritmo de Viterbi

básico já para seqüências com 2.048.000 resíduos, o que indica um comportamento

assintótico de requerimentos de espaço contrário ao esperado, pelo menos para os

algoritmos com 2-níveis de checkpoints. Desta forma, esse algoritmo com 2-níveis de

checkpoints por diagonais parece não constituir uma alternativa consistente ao algoritmo

de Viterbi básico.

O comportamento assintótico dos requerimentos de espaço do algoritmo de Viterbi com

2-níveis de checkpoints por linhas e particionamento móvel de memória está em

conformidade com o esperado. Esse requerimento foi sempre uma fração constante de

~3,2% relativamente ao algoritmo de Viterbi básico. Sendo, desta forma uma alternativa

consistente ao algoritmo de Viterbi básico.

O comportamento assintótico dos requerimentos de espaço dos algoritmos de Viterbi

com 2-níveis de checkpoints bidimensionais e particionamento fixo de memória está em

conformidade com o esperado. Esse requerimento foi sempre uma fração decrescente

191

Page 194: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

relativamente ao algoritmo de Viterbi básico. Constituindo-se, desta forma na melhor

alternativa consistente ao algoritmo de Viterbi básico.

Foram realizados no presente estudo um conjunto de testes divididos em três

experimentos para análise, a posteriori, do desempenho dos algoritmos de Viterbi no

alinhamento de seqüências a um perfil-HMM. Os algoritmos bidimensionais,

denominados algoritmos propostos 1 e 2, foram considerados com duas configuraçães

de requerimentos de memória: o algoritmo proposto 1 supostamente usaria uma

quantidade de memória próxima àquela requerida pelo algoritmo por diagonais, mas na

prática, conforme o exposto em parágrafos anteriores, ela foi assintóticamente muito

menor; e o algoritmo proposto 2 que usou metade da quantidade de memória do

algoritmo proposto 1.

Os testes relativos aos experimentos 1 e 2 mostraram que o algoritmo de Viterbi básico

provocou uso intenso de memória virtual para o alinhamento de seqüências com 32.000

resíduos, gastando mais tempo na troca de páginas entre memória principal e memória

virtual do que no processamento do alinhamento, e não conseguiu alocar memória

suficiente para alinhar seqüências com 64.000 resíduos. O mesmo aconteceu com o

algoritmo com 2-níveis de checkpoints por diagonais, ele provocou atividade intensa de

paginação para o alinhamento de seqüências com 64.000 e não conseguir alinhar

seqüências com 128.000 resíduos. Outro algoritmo que provocou atividade intensa de

paginação foi o algoritmo com 2-níveis de checkpoints por linhas, ele provocou o uso

intenso de memória virtual pelo PC para seqüências com 512.000 resíduos. Esses dados

indicam uma vantagem para os algoritmos propostos 1 e 2, que não iniciaram atividade

significativa de paginação para nenhum tamanho de seqüência no intervalo considerado,

2.000 a 512.000 resíduos, podendo portanto alinhar seqüências bem maiores do que os

demais algoritmos considerados.

Esses testes relativos aos 3 experimentos mostraram que os algoritmos propostos 1 e 2

obtiveram desempenho bastante próximos um do outro, com ligeira vantagem para o

algoritmo proposto 2. Mostraram ainda que eles obtiveram um desempenho melhor do

que o próprio algoritmo de Viterbi básico, para todas as instâncias do problema, que

192

Page 195: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

foram consideradas, foram pelo menos 5,2% mais rápido em condições de uso de

memória virtual baixo que algoritmo de Viterbi básico. Dos experimentos 1 e 2 pode-se

verificar que eles obtiveram ganho de performance por um fator de pelo menos 2 vezes

para os tempos de CPU e de pelo menos 8,0 a 10 vezes para os tempos totais de

execução, após o algoritmo de Viterbi básico começar a fazer uso significativo da

memória virtual. Dos testes do experimento 3 pode-se verificar que os algoritmos

propostos obtiveram desempenho superior ao algoritmo de Viterbi básico, em média,

cerca de 10% e 11% com relação aos tempos de CPU e aos tempos totais de execução,

respectivamente. Esses dados indicam que os algoritmos propostos podem ser usados,

preferencialmente, no lugar do próprio algoritmo básico, tanto no alinhamento de de

seqüências pequenas, por exemplo proteínas, quanto no alinhamento de seqüências

grandes, por exemplo moléculas de DNA, cromossomos, genomas.

Nenhum dos outros dois algoritmos com 2-níveis de checkpoints conseguiu igualar essa

performance dos algorimos propostos para nenhuma das instâncias do problema que

foram consideradas nos conjuntos de testes. Com relação ao algoritmo de Viterbi com 2-

níveis de checkpoints por linhas, com particionamento móvel de memória, os algoritmos

propostos foram pelo menos 2 vezes mais rápidos, nos testes dos experimentos 1 e 2,

para instâncias do problema com uso moderado da memória virtual, e foram ainda mais

rápidos para instâncias do problema com uso intenso da memória virtual, com ganhos

de performance de até ~5 vezes para os tempos de CPU e de até ~12 vezes para os

tempos totais de execução. Já, com relação ao algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais, com particionamento móvel de memória e retrocedimento

restrito, os algoritmos propostos foram pelo menos 2 vezes mais rápidos para instâncias

do problema com uso moderado da memória virtual, e foram ainda mais rápidos para

instâncias do problema com uso intenso da memória virtual, com ganhos de

performance de ~2 vezes para os tempos de CPU e de ~13 vezes para os tempos totais

de execução. Os dados dos testes do experimento 3 indicam que os algoritmos propostos

foram superiores em média cerca de 94% ao algoritmo de Viterbi com 2-níveis de

checkpoints por linhas e particionamento móvel de memória, e em média cerca de 24%

193

Page 196: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

ao algoritmo de Viterbi com 2-níveis de checkpoints por diagonais, com

particionamento móvel de memória e procedimento de retrocedimento restrito.

Pode-se observar, também, dos resultados do experimento 3 que o algoritmo de Viterbi

básico foi superior em média cerca de 10% ao algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais, com particionamento móvel de memória e procedimento de

retrocedimento restrito. Já esses resultados, do experimento 3, indicam que o algoritmo

de Viterbi com 2-níveis de checkpoints por diagonais, com particionamento móvel de

memória e procedimento de retrocedimento restrito, obteve desempenho superior em

média 36% ao algoritmo de Viterbi com 2-níveis de checkpoints por linhas e

particionamento móvel de memória.

Uma das características destes algoritmos de PD que utilizam o paradigma de

checkpoints é iterar de forma regular sobre a matriz de PD, o que possibilita um uso

mais adequado da hierarquia de memória do PC. Os resultados confirmaram esse

comportamento, indicando, possivelmente, um melhor uso da hierarquia de memória do

PC – principalmente da memória cache – pelos algoritmos de Viterbi com 2-níveis de

checkpoints por linhas, ou por diagonais, ou bidimensionais. Entretanto, os resultados

indicam um aproveitamento melhor desta característica pelos algoritmos propostos 1 e

2, do que pelos outros dois algoritmos, o que, possivelmente, se deve à forma de

particionamento da matriz de PD em seções pelos algoritmos propostos, em forma de

grade, possibilitando que os algoritmos propostos computem na fase de retrocedimento

“linhas” de comprimentos menores ou iguais a 2R

n (máximos), contra “linhas” de

tamanhos n (tamanho da seqüência) no caso dos outros dois algoritmos. É importante,

salientar que o uso de retrocedimento restrito pode reduzir significativamente os

tamanhos destas seções, e, portanto, pode reduzir o tamanho das “linhas” a serem

recomputadas.

Desta forma, pode-se verificar que o uso explícito e não recursivo do princípio D&C em

conjunto com o paradigma de checkpoints e com o procedimento de retrocedimento

restrito, pelos algoritmos propostos 1 e 2, reduziu significativamente a quantidade de

194

Page 197: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

recalculos nas seções da matriz de PD, durante o processo de retrocedimento parcial.

Além do que a iteração regular sobre a matriz de PD e essa redução significativa nos

comprimentos das seções das “linhas” a serem computadas numa determinada iteração,

durante o procedimento de retrocedimento parcial, permitiram uma utilização mais

eficiente da hierarquia de memória do PC pelos algoritmos propostos 1 e 2.

O melhor desempenho do algoritmo proposto 2 sobre o algoritmo proposto 1 só pode

ser explicado pelo comportamento, já relatado, do uso do procedimento de

retrocedimento restrito nesses algoritmos com L-níveis de chekpoints. O algoritmo

proposto 2 se beneficiou mais do aumento de desempenho proporcionado por esse

procedimento de retrocedimento restrito, pois ele necessita apenas de metade das linhas,

R, de memória requeridas pelo algoritmo proposto 1, para as mesmas instâncias do

problema.

Sabe-se, ao menos teoricamente, que o algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais, com particionamento móvel de memória e sem o

retrocedimento restrito, computa matrizes de PD ~2 vezes maiores, para as mesmas

instâncias do problema, que os demais algoritmos considerados nos experimentos desta

tese, e que isto acarreta uma perda de performance de ~2 vezes quando tomada em

relação ao algoritmo de Viterbi com 2-níveis de checkpoints por linhas, com

particionamento móvel de memória e sem retrocedimento restrito, e de ~4 vezes quando

tomada em relação ao algoritmo de Viterbi básico. Além disso, foi levantado a

existência de perda de desempenho devido ao tratamento das condições de contorno

para computar diagonais ao invés de linhas ou colunas.

Apesar disso, Tarnas e Hughey (1998) esperavam que, com a adição do procedimento

de retrocedimento restrito, o algoritmo de Viterbi com 2-níveis de checkpoints por

diagonais e particionamento móvel de memória pudesse superar o algorimo de Viterbi

com 2-níveis de checkpoints por linhas e particionamento móvel de memória. Contudo,

a análise apresentada na Seção 3.3.5 indica uma perda de performance de até 2 vezes

em relação ao algoritmo de Viterbi básico, quando foi acrescido um procedimento de

195

Page 198: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

retrocedimento restrito ao algoritmo de Viterbi com 2-níveis de checkpoints por

diagonais.

Eles realizaram e divulgaram experimentos onde o algoritmo de Viterbi com 2-níveis de

checkpoints por diagonais, com particionamento móvel de memória e retrocedimento

restrito, apresentou um desempenho superior ao algoritmo de Viterbi com 2-níveis de

checkpoints por linhas e particionamento móvel de memória. O desempenho deste foi

muito próximo ao desempenho do algoritmo de Viterbi básico em condições normais de

uso da memória virtual, e foi superior para condições de uso significativo da memória

virtual. Entretanto, nas suas conclusões eles relataram que o ganho de performance não

foi equivalente ao aumento de recursos de memória alocados ao algoritmo e às

dificuldades impostas pelas condições de contorno para computar diagonais ao invés de

linhas ou colunas, ao nível de codificação e de desempenho do algoritmo. E planejaram

retornar ao desenvolvimento do algoritmo por linhas.

Os resultados dos experimentos realizados nessa tese estão mais consonantes com os

resultados esperados para o algoritmo de Viterbi com 2-níveis de checkpoints por

diagonais, com particionamento móvel de memória e retrocedimento restrito. A perda

de performance devido à computação de matrizes de PD cerca de 2 vezes maiores e às

complexidades adicionais advindas da computação por diagonais foi recuperada com a

aplicação do procedimento de retrocedimento restrito, possibilitando ao algoritmo por

diagonais com a adição de procedimento restrito empatar em desempenho, nos testes

dos experimentos 1 e 2, com o algoritmo de Viterbi com 2-níveis de checkpoints por

linhas e com particionamento móvel de memória, em condições normais de utilização

da memória virtual, conforme o esperado pela análise feita na Seção 3.3.5.

Por outro lado, os resultados desta tese apontam, principalmente com relação ao

comportamento assintótico dos requerimentos de memória, mas também com relação ao

desempenho, que o algoritmo com 2-níveis de checkpoints por diagonais, com

particionamento móvel de memória e retrocedimento restrito, parece bastante limitado

para ser tomado como uma alternativa consistente ao algoritmo básico para a

computação de matrizes de PD, quando os tamanhos das instâncias do problema

196

Page 199: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

implicam em exaustão dos recursos de memória do PC pelo algoritmo básico. O

algoritmo por diagonais conseguiu alinhar somente seqüências de até 64.000 resíduos a

um HMM com 2.000 nós. Na simulação do crescimento dos requerimentos de memória

necessários para alinhar seqüências de tamanhos crescentes, o algoritmo por diagonais

apresentou taxa de crescimento maior do que a do algoritmo de Viterbi básico. Tudo

isso é agravado, tanto pelo comportamento assintótico de requerimentos de memória

dos outros algoritmos considerados no presente estudo, quanto pelo desempenho

apresentado por esses nos testes efetuados; eles computaram seqüências de tamanhos

até 512.000 resíduos e apresentaram desempenho igual, no caso do algoritmo por linhas,

ou 2 vezes superior, no caso dos algoritmos propostos, ao do algoritmo por diagonais no

intervalo de 2.000 a 32.000 resíduos.

Do exposto nos parágrafos anteriores deste capítulo pode-se concluir que os algoritmos

de Viterbi com 2-níveis de checkpoints bidimensionais, com particionamento fixo de

memória e retrocedimento restrito, são a melhor alternativa, a alternativa mais consiste,

ao algoritmo de Viterbi básico, para a computação de instâncias intratáveis por esse

algoritmo básico numa determinada máquina (PC) serial. Até mesmo para instâncias

tratáveis pelo algoritmo de Viterbi básico os algoritmos de Viterbi com 2-níveis de

checkpoints bidimensionais foram superiores em desempenho. Entretanto, é bom,

salientar que o algoritmo de Viterbi com L-níveis de checkpoints por linhas (ou colunas)

e particionamento móvel de memória, também, constitui uma escolha boa como

alternativa ao algoritmo de Viterbi básico.

Foi verificado, na análise teórica e nos testes de desempenho a posteriori realizados com

esses algoritmos de Viterbi com 2-níveis de checkpoints, que os algoritmos de Viterbi

com L-níveis de checkpoints bidimensionais, com particionamento fixo de memória e

retrocedimento restrito, são a melhor alternativa, a mais consiste, ao algoritmo de

Viterbi básico, para todas as instâncias, principalmente para aquelas intratáveis por esse

algoritmo básico numa determinada máquina (PC) serial. Pois, esses algoritmos

propostos apresentaram coeficientes de complexidade menores na análise teórica

quando comparado aos demais algoritmos, tiveram melhor comportamento assintótico

dos requerimentos de espaço que os demais algoritmos, relativamente ao algoritmo de

197

Page 200: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Viterbi básico, e foram sempre superiores aos demais nos requerimentos de tempo de

execução, inclusive ao próprio algoritmo de Viterbi básico. Desta forma, os algoritmos

propostos são convenientes tanto para efetuar buscas por seqüências similares de

proteínas num BD de seqüências, por exemplo no BD Swiss-Prot, que envolvem

comparações de todas as seqüências do BD ao HMM, efetuando, portanto, milhares de

comparações, quanto para efetuar comparações de seqüências grandes a um HMM, por

exemplo na comparação de genomas inteiros, que constituem instâncias intratáveis pelo

algoritmo de Viterbi básico.

Adicionalmente ao melhor desempenho, nas análises a priori e a posteriori, tanto ao

nível de requerimentos de espaço, quanto ao nível de requerimentos de tempo de

execução, esses algoritmos de Viterbi com L-níveis de checkpoints bidimensionais e

particionamento fixo de memória apresentam outras características, que os tornam ainda

mais atraentes. Esses algoritmos propostos, diferentemente, dos outros membros desta

família de algoritmos que fazem uso do paradigma de checkpoints são bastante flexíveis

com relação à quantidade de memória alocada para a computação de uma determinada

matriz de PD. Isto se deve principalmente a dois fatos ou particularidades, a saber:

• O particionamento fixo da memória disponível ou necessária para as

computações (ou re-computações), entre memória de trabalho e memória de

checkpoints;

• O uso explícito e não recursivo do princípio D&C, que requer uma quantidade

menor de memória de trabalho para recomputações de seções da matriz de PD

na fase de retrocedimento.

Desta forma, fixado a quantidade de linhas e colunas de checkpoints que serão usadas,

pode-se calcular a quantidade de linhas de memória de trabalho em função dos

requerimentos de memória de trabalho na fase de retrocedimento e do nível L de

checkpoints que será adotado, sendo que são necessárias no mínimo 2 linhas de

memória de trabalho na fase de computações para frente (forward), o que constitui um

requerimento mínimo. Ao contrário, para os outros algoritmos membros, fixado uma

198

Page 201: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

quantidade de memória disponível ou necessária, pode-se escolher somente o nível de

checkpoints que pode acomodar está memória para a solução da matriz de PD.

Entretanto algumas questões ainda permanecem abertas. Elas podem ser objetos de

novos trabalhos, que poderiam estender o alcance dos resultados aqui auferidos. Podem

ser destacados as seguintes questões:

• Realização de estudos comparativos do comportamento assintótico dos

requerimentos de memória destes algoritmos com L-níveis de chekpoints

bidimensionais para outros valores de L, inclusive considerando . nL blog=

• Realização de estudos do desempenho a posteriori dos requerimentos de tempo

do algoritmo com L-níveis de chekpoints bidimensionais para outros valores de

L, inclusive considerando nL blog= .

• Verificar, na prática, o limite do pior caso de desempenho destes algoritmos, que

é de perda de desempenho por um fator de no máximo 2 vezes, com relação ao

requerimentos de tempo, para um nível L = n.

• Realização de estudos do desempenho a posteriori dos algoritmos de L-níveis de

checkpoints bidimensionais com partição fixa de memória em aplicações dos

HMMs que requeiram a utilização de procedimentos de retrocedimento

completo sobre a matriz de PD.

• Realização de estudos do desempenho a posteriori do algoritmo com 2-níveis de

chekpoints por linhas quando a esse é adicionado um procedimento de

retrocedimento restrito.

E ao nível mais geral, a realização de estudos da possibilidade de utilização das idéias

usadas para aceleração do desempenho dos algoritmos D&C, nos algoritmos de PD com

checkpoints ou no desenvolvimento de outros algoritmos, que possam ser usados para

computar medidas de interesse nos perfis-HMMs.

199

Page 202: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

200

Page 203: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

REFERÊNCIAS BIBLIOGRÁFICAS

Barret, C.; Hughey, R.; Karplus, K. Scoring hidden Markov models. CABIOS, v. 13,

n.2, 191-199, 1997.

Birney, E. Sequence Alignment in Bioinformatics. Tese (Doutorado em

Bioinformática) - The Sanger Centre in Cambridge, Hinxton, 133 p., 2000.

Birney, E. Hidden Markov Models in biological sequence analysis. IMB J. Res. &

Dev., v. 45, n. 3/4, p. 449-454, 2001.

Cappé, O. Ten years of HMMs. Paris, França. 16p., Mar. 2001. Disponível em:

http://www.tsi.enst.fr/~cappe/docs/hmmbib.html . Acesso em: Jun. 2001.

Comet, J. P. Programmation Dynamique et Alignements de Séquences Biologiques.

Tese (Doutorado em Controle de Sistemas) – Université de Technologie de

Compiègne, Compiègne, 214 p.,1998.

Couvreur, C.; Fontaine, V.; Gaunard, P.; Mubikangiey, C. G. Automatic Classification

of Enviromental Noise Events by Hidden Markov Models. Applied Acoustics, v.

54, n. 3, p. 187-206, 1998.

Crochemore, M.; Iliopoulos, C. S.; Pizon, Y. J. Speeding-up Hirschberg and Hunt-

Szymanski LCS algorithms. Fundamenta Informaticae, v. 56, n. 1 e 2, p. 89-103,

2003.

Cuesta, D.; Micó, P.; Aboy, M.; Novák_, D.; Brezny_, R.; Samblas_, L.; Pastor, D.;

Sancho, S. Biosignal Laboratory: A Software Tool for Complete Biomedical Signal

Processing and Analysis. In: International Conference of IEEE Engineering in

Medicine and Biology Society , 25. (EMBS), 2003, Cancun. Proceedings. IEEE, v.

4, p. 3544-3547, 2003.

Durbin, R.; Eddy, S.; Krogh, A.; Mitchison, G. Biological sequence analysis:

probabilistic models of proteins and nucleic acids. New York: Cambridge

University Press, 1998. 356p.

201

Page 204: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Eddy, S. R. Profile Hidden Markov Models. Bioinformatics, v.14, n.9, p. 755-763,

1998.

Eddy, S. R. HMMER – User’s Guide Biological sequence analysis using profile

hidden Markov models. St. Louis, EUA, Instituto Médico Howard Hughes e

Departamento de Genética da Escola de Medicina da Universidade de Washington,

98p., Out. 2003. Versão 2.3.2. Disponível em:

ftp://ftp.genetics.wustl.edu/pub/eddy/hmmer/current/userguide.pdf . Acesso em:

Nov. 2003.

Goeman, H.; Clausen, M. A new practical linear space algorithm for the longest

common subsequence problem. Information Processing Letters, v. 75, p. 275-281,

2000.

Gotoh, O. An improved algorithm for matching biological sequences. Journal of

Molecular Biology, n. 162, p. 705-708, 1982.

Grate, L.; Hughey, R.; Karplus, K.; Sjölander, K. Stochastic Modeling Techniques:

Understanding and using hidden Markov models. Santa Cruz, USA,

Universidade da California, 35p, Jun. 1996. Disponível em:

http://www.cse.ucsc.edu/research/compbio/papers/ismb96_tutorial.ps. Acesso em:

Jul. 1999.

Grice, J. A.; Hughey, R.; Speck, D. Parallel sequence alignment in limited space. In:

Int. Conf. Intelligent Systems for Molecular Biology, 1995, Cambridge.

Proceedings. AAAI/MIT Press, 1995. p. 145-153.

Grice, J. A.; Hughey, R.; Speck, D. Reduced space sequence alignment. CABIOS, v.

13, n. 1, p. 45-53, 1997.

Haussler, D.; Krogh, A.; Mian, I. S.; Sjölander, K. Protein Modeling using Hidden

Markov Models: Analysis of Globins. In: 26th Hawaii International Conference on

System Sciences, 26., Honolulu, 1993. Proceedings. Los Alamitos: IEEE Computer

Society Press, p. 792-802. 1993.

202

Page 205: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Hirschberg, D. S. A linear space algorithm for computing maximal common

subsequences. Communications of the ACM, v. 18, n. 6, p. 341-343, 1975.

Hirschberg, D. S. Algorithms for the Longest Common Subsequence Problem. Journal

of the ACM, v. 24, n. 4, p. 664-675, 1977.

Hughey, R.; Krogh, A. Hidden Markov models for sequence analysis: extension e

analysis of the basic method. CABIOS, v. 12, n. 2, p. 95-107, 1996.

Hughey, R.; Karplus, K.; Krogh, A. SAM – Sequence Alignment and Modeling

Software System. Santa Cruz, USA: Centro Baskin para Ciência e Engenharia de

Computação da Universidade da California. 168p., Jul. 2003. Versão 3.4. (UCSC-

CRL-99-11). Disponível em:

http://www.cse.ucsc.edu/research/compbio/papers/sam_doc.pdf. Acesso em: Jul.

2003.

Hunt, J. W.; Szymanski, T. G. A fast algorithm for computing longest common

subsequence. Communications of the ACM, v. 20, n. 5, p. 350-353, 1977.

Human Genome Management Information System (HGMIS). Primer on Molecular

Genetics. Washington, DC: U.S. Department of Energy Human Genome Program,

1992. Disponível em:

http://genome.gsc.riken.go.jp/hgmis/publicat/primer/primer.pdf. Acesso em: Jul.

1999.

Human Genome Management Information System (HGMIS). Genomics and Its

Impact on Medicine and Society - A 2001 Primer. Washington, DC: U.S.

Department of Energy Human Genome Program, 2001. Disponível em:

http://genome.gsc.riken.go.jp/hgmis/publicat/primer2001/primer11.pdf. Acesso em:

Jun. 2001.

Karplus, K.; Barret, C.; Hughey, R. Hidden Markov Models for Detecting Remote

Protein Homologies. Bioinformatics, v.14, n.10, p. 846-856, 1998.

203

Page 206: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Kearney, J. K.; Sedlmeyer, R. L.; Thompson, W. B.; Gray, M. A.; Adler, M. A.

Software Complexity Measurement. Communications of the ACM, v. 29, n. 11, p.

1044-1050, 1986.

Kehagias, A. Bayesian Classification of Hidden Markov Models. Math. Comput.

Modelling, v. 23, n. 5, p. 25-43, 1996.

Knuth, D. The Art of Computer Programming: Fundamental Algorithms.

Massachusetts: Addison-Wesley, 1973.

Koski, A. Modelling ECG signals with hidden Markov models. Artificial Intelligence

in Medicine, n. 8, p.453-471, 1996.

Koski, T. A Course on Hidden Markov Models: with applications to

bioinformatics. Faculdade de estatística e Matemática do KTH Instituto Real de

Tecnologia, Estocolmo, Suecia. 327p., Mar. 1999. Disponível em:

http://www.math.kth.se/matstat/users/timo/komp.html. Acesso em: Out. 2000.

Kowalski, R. Algorithm = Logic + Control. Communications of the ACM, v. 22, n. 7,

p. 424-436, 1979.

Krogh, A.; Brown, M.; Mian, I. S.; Sjölander, K.; Haussler, D. Hidden Markov

Models in Computational Biology: applications to protein modeling. Santa Cruz,

USA, Laboratórios Sinsheimer de Computação e Ciência da Informação da

Universidade da California, 35p., Set. 1993. Disponível em:

http://www.cse.ucsc.edu/research/compbio/papers/sam_doc.pdf . Acesso em: Out.

2003.

Leeuwen, J. van; Wiedermann, J. The turing machine paradigm in contemporary

computing. Utrecht, Netherlands: Departamento de Ciência da Computação da

Universidade de Utrecht. 19p., 2000. (UU-CS-2000-33). Disponível em:

http://www.archive.cs.uu.nl/pub/ruu/cs/techreps/cs-2000/2000-33.pdf. Acesso em:

Abr. 2004.

204

Page 207: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Ma, X.; Shi, J. A New Method for Discrimination Between Fault and Magnetzing Irush

Current Using HMM. Eletric Power System Research, N. 56, p.43-49, 2000.

Myers, E. W. An O(nd) difference algorithm and its variations. Algorithmica, n. 1, p.

251-266, 1986.

Myers, E. W.; Miller, W. Optimal alignments in linear space. CABIOS, v. 4, n. 1, p.

11-17, 1988.

Myers, E. W. An overview of sequence comparison algorithms in molecular biology.

Tucson, AZ, USA: Departamento de Ciência da Computação, Universidade do

Arizona, 25 p., Dez. 1991. Disponível em:

http://www.cs.arizona.edu/people/gene/papers/compbio.survey.ps. Acesso em: Jul.

2002.

Needleman, S. B.; Wunsch, C. D. A general method applicable to the search for

similarities in the amino acid sequence of two proteins. Journal of Molecular

Biology, n. 48, p. 443-453, 1970.

Net-ID, Inc. HMMpro. Copyright Net-ID Inc., 1998. Ver. 2.2.1 (B47). Disponível em:

http://www.netid.com. Acesso em: Abr. de 2003.

Pedersen, C. N. S. Algorithms in Computational Biology. Aarhus, 210 p. Tese

(Doutorado em Ciência da Computação) – Faculty of Science of the University of

Aarhus, 2000.

Powell, D. R.; Allison, L.; Dix, T. I. A versatile divide and conquer technique for

optimal string alignment. Information Processing Letters, n. 70, p. 127-139, 1999.

Rabiner, L. R. A tutorial on hidden Markov models and selected applications in speech

recognition. Proceedings of the IEEE, v. 77, n. 2, p. 257-286, Fev. 1989.

Resende, P.J.; Stolfi, J. Fundamentos de Geometria Computacional. Recife: IX

Escola de Computação – UFPE, 1994.

205

Page 208: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Rick, C. A New Flexible Algorithm for the Longest Common Subsequence Problem.

In: 6th Symposium on Combinatorial Pattern Matching (CPM'95), Espoo, Finland,

1995. Proceedings. Lecture Notes in Computer Science, v. 937, p. 340-351, 1995.

Disponível em: http://theory.cs.uni-bonn.de/blum/mitarbeiter/rick/lcs.dvi. Acesso

em: Jan. 2003.

Rick, C. Simple and fast linear space computation of longest common subsequences.

Information Processing Letters, v. 75, n. 6, p. 275-281, 2000.

Sato, M.; Sugaya, N.; Murakami, H.; Aburatani, S.; Horimoto, K. Profile-Profile

Comparison Based on Hidden Markov Models. Genome Informatics, v. 14, p. 522-

523, 2003.

Smith, T. F.; Waterman, M. F. Identification of common molecular subsequences.

Journal of Molecular Biology, n. 147, p. 195-197, 1981.

Swiss-Prot group. Swiss-Prot protein knowledgebase release 43.0 statistics. Geneva:

Swiss Institute of Bioinformatics, 2004.

Tarnas, C.; Hughey, R. Reduced space hidden Markov model training. Bioinformatics,

v. 14, n. 5, p. 401-406, 1998.

Toscani, L. V.; Veloso, P. A. S. Complexidade de Algoritmos. Porto Alegre: Instituto

de Informática da UFRGS: Editora Sagra Luzzatto, 2001. 202p.

Wheeler, R.; Hughey, R. Optimizing Reduced-Space Sequence Analysis.

Bioinformatics, v. 16, n. 12, p. 1082-1090, 2000.

Wu, S; Mamber, U.; Myers, G; Miller, W. An ( )npO sequence comparison algorithm.

Inform. Process. Lett., v. 35, p. 317-323, 1990.

206

Page 209: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

BIBLIOGRAFIA COMPLEMENTAR

Dunmur, A. P.; Titterington, D. M. The influence of initial conditions on maximum

likelihood estimation of the parameters of a binary hidden Markov model. Statistics

& Probability Letters, v. 40, p. 67-73, 1998.

Golding, B. Course BIOL721 – Elementary Sequence Analysis. Hamilton, Ontario,

Canadá, Departamento de Biologia da Universidade McMaster, 198 p., 2003.

Disponível em: http://helix.biology.mcmaster.ca/4k03.pdf . Acesso em: Jun. 2003.

Hughes, J. P. Computing the observed information in the hidden Markov model using

the EM algorithm. Statistics & Probability Letters, v. 32, p. 107-114, 1997.

Ikeda, S. Acceleration of the EM algorithm. Systems and Computers in Japan, v. 31,

n. 2, p. 10-18, 2000.

Karchin, R. Hidden Markov Models and Protein Sequence Analysis. Santa Cruz,

USA, Centro Baskin para Ciência da Computação e Engenharia da Universidade da

California, 1998, 18 p. Disponível em:

http://www.cse.ucsc.edu/research/compbio/ismb99.handouts/kk185fp.html. Acesso

em: Set. 2000.

Karchin, R.; Hughey, R. Weighting hidden Markov models for maximum

discrimination. Bioinformatics, v. 14, n. 9, p. 772-782, 1998.

Lin, G. H.; Ma, B.; Zhang, K. Edit distance between two RNA structures. In: 5a

Conferência Anual Internacional em Biologia Computacional (RECOMB).

Proccedings. New York, NY, USA, ACM Press, p. 211-220, 2001.

McClure, M. A. Parameterization studies for the SAM and HMMER methods of

Hidden Markov Model generation. In: Fourth International Conference on

Intelligent Systems for Molecular Biology. Proceedings. Menlo Park, CA, USA,

AAAI Press, p.155-164, 1996.

207

Page 210: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

Minka, T. P. From hidden Markov models to linear dynamical systems. Cambridge,

MA, USA: MIT Media Lab, 10 p., July 1999. (MIT-TR-531). Disponível em:

ftp://whitechapel.media.mit.edu/pub/tech-reports/tr-531.pdf. Acesso em: Set. 2002.

Morgenstern, B. A space-efficient algorithm for aligning large genomic sequences.

Bioinformatics, v. 16, n. 10, p. 948-949, 2000.

Murphy, K. P. Kevin Murphy. Learning Markov Process. The Encyclopedia of

Cognitive Science, Nadel et al. (eds), Nature Macmillan, 2002. No prelo.

Myers, E. W.; Miller, W. Optimal alignments in linear space. CABIOS, v. 4, n. 1, p.

11-17, 1998.

Rydén, T. On recursive estimation for hidden Markov models. Stochastic Process and

their applications, v. 66, p. 79-96, 1997.

Schneider, T. D. Information Theory Primer. Laboratório de Biologia Experimental e

Computacional, Centro Frederick de Desenvolvimento e Pesquisa do Cancer do

Instituto Nacional do Cancer, USA. Jan. 2003, 14 p. Disponível em:

http://www.lecb.ncifcrf.gov/~toms/paper/primer/latex/paper.pdf. Aceso em: Mai.

2003.

Shatkay, H.; Kaelbling, L. P. Learning hidden Markov models with geometric

information. Providence, Rhode Island: Departamento de Ciência da Computação

da Universidade Brown, 1997. 16 p. (CS- 97-04). Disponível em:

ftp://ftp.cs.brown.edu/pub/techreports/97/cs97-04.ps.z. Acesso em: Jan. 2001.

Sin, B.; Kim, J. H. Nonstationary hidden Markov model. Signal Processing, v. 46, p.

31-46, 1995.

Tsoka, S.; Ouzounis, C. A. Recent developments and future directions in computational

genomics. FEBS Letters, v. 480, p. 42-48, 2000.

208

Page 211: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

APÊNDICE 1

ALGORITMO DE VITERBI BÁSICO

/* **************************************************************************************** * * Algoritmo de Viterbi para alinhar seqüências de aminoácidos * Copyright (C) 2003 José Olimpio Ferreira * Instituto Nacional de Pesquisas Espaciais e * Universidade Católica de Goiás * All Rights Reserved * **************************************************************************************** * * align_viterbi_basic_uni.c * * --> Algoritmo de Viterbi para obter o melhor alinhamento de seqüências de * aminoácidos a um HMM. * --> Este é um algoritmo básico, que necessita de espaço da ordem O(mn). * **************************************************************************************** * * Entradas: * --> Arquivo de probabilidades de transição de estados --> hmm[nos].transicoes * --> Arquivo de probabilidades de emissão de símbolos --> hmm[nos].emissoes * --> Arquivo contendo 1 seqüência para alinhamento ao HMM --> <nome_arquivo> * --> nós = tamnho do HMM * * Saída: * --> Escore e alinhamento Viterbi da seqüência ao HMM --> hmm[nos].ubalign * **************************************************************************************** * * Compilação - Linkedição - Construção do executável -> exemplo para intel pentium 3 celerom * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 align_viterbi_basic_uni.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpctim.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpcwall.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 etime.c * --> gcc -o align_viterbi_basic_uni align_viterbi_basic_uni.o hpctim.o hpcwall.o etime.o -lm * * Compilação - Linkedição - Construção do executável -> exemplo para amd athlon k-7 * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 align_viterbi_basic_uni.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpctim.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpcwall.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 etime.c * --> gcc -o align_viterbi_basic_uni align_viterbi_basic_uni.o hpctim.o hpcwall.o etime.o -lm * **************************************************************************************** */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> /*Definie algumas macros. */ #define IAA(x) (strchr(aa, (x)) - aa) /* Índice de símbolos de aminoácidos. */ #define log2(x) ((x)>0.0 ? log(x)*1.44269504 : -19) /* logaritmo de base 2. */ #define exp2(x) (exp(x)*0.69314718) /* exponencial de base 2. */ #define INFINITO 99999999 /* infinito para log-probilidades de valores zero. */

209

Page 212: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

#define aa_alfabeto "ACDEFGHIKLMNPQRSTVWY" /* Alfabeto não-degenerado de aminoácidos. */ /* String contendo os símbolos não-degenerados dos aminoácidos. */ const char aa[21] = aa_alfabeto; /* Índices para as probabilidades de transição de estado do HMM. */ enum transicoes_indices PDD=0,PMD=1,PID=2,PDM=3,PMM=4,PIM=5,PDI=6,PMI=7,PII=8; /* Variáveis: * NS --> número de seqüências. * TS --> tamanho da maior seqüência. * NN --> número de nós do HMM. * Model --> HMM 500,1000,2000,4000,8000 nós. * Arquivo --> nome do arquivo que contém as seqüências no formato HMMpro. */ const long int NS, TS, NN; char Model[80], Arquivo[80]; /* --> Freqüências f(i) - Dayhoff - de ocorrência dos 20 aminoácidos. * (função massa de probabilidade para aminoácidos). * --> Obtidos do BD SwissProt 34: considerando 21.210.388 raidicais. * --> Apresentadas em ordem alfabética por código de letras simples. * --> Usadas como probabilidades de emissão de símbolos no modelo nulo ou aleatório (R). */ const float f_aa[20] = (float)-3.72699742, /* (float)0.075520, /* A */ (float)-5.88061460, /* (float)0.016973, /* C */ (float)-4.23707464, /* (float)0.053029, /* D */ (float)-3.98384032, /* (float)0.063204, /* E */ (float)-4.61663135, /* (float)0.040762, /* F */ (float)-3.86884780, /* (float)0.068448, /* G */ (float)-5.47997107, /* (float)0.022406, /* H */ (float)-4.12572395, /* (float)0.057284, /* I */ (float)-4.07344183, /* (float)0.059398, /* K */ (float)-3.42044908, /* (float)0.093399, /* L */ (float)-5.40696564, /* (float)0.023569, /* M */ (float)-4.46456809, /* (float)0.045293, /* N */ (float)-4.34338099, /* (float)0.049262, /* P */ (float)-4.63554859, /* (float)0.040231, /* Q */ (float)-4.27724022, /* (float)0.051573, /* R */ (float)-3.79157763, /* (float)0.072214, /* S */ (float)-4.12144885, /* (float)0.057454, /* T */ (float)-3.93783407, /* (float)0.065252, /* V */ (float)-6.32042847, /* (float)0.012513, /* W */ (float)-4.96646070 /* (float)0.031985 /* Y */ ; /* Ponteiros para arquivos. */ FILE *seq_in, *pt_in, *pe_in, *align_out, *scores_out; /* Declaração dos protótipos das funções internas. */ int analisa_arq_seq(long int vseq[4]); int read_seq (char As[TS+1]); int read_model (float PT[][9], float mPE[][20], float iPE[][20]); int alinha_viterbi_basico(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]); /* Declaração dos protótipos das funções externas. */ void etime(float *result); void hpcwall(double *retval); void hpctim(float *wtime, float *ctime, float *wbegin, float *cbegin); /* FUNÇÃO PRINCIPAL --> MAIN. */ int main (int argc, char *argv[]) long int i, j, vseq[4]=0,0,0,0, *ptr; int status; float *ptrf; /* Trata os argumentos da função principal. */ if(argc!=3) printf("\nVoce esqueceu de informar:\n"); printf("o tamanho do HMM (Opções: 500, 1000, 2000, 4000 e 8000),\n"); printf("o nome do arquivo contendo as seqüências.\n\n"); printf("Entre: <nome_programa> <tamanho_HMM> <nome_arquivo>\n"); printf("O arquivo deve estar no formato HMMpro --> http://www.netid.com\n"); exit(1); /* Inicializa algumas variáveis globais do tipo char* e const int. Usadas como informações de nomes de arquivos, de tamnho do modelo e de dimesões de matrizes. */

210

Page 213: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

strcpy(Model,argv[1]); (const) ptr = &NN; *ptr = atoi(argv[1]); strcpy(Arquivo,argv[2]); /* Chamada para a função análise do arquivo de seqüências. */ status = analisa_arq_seq(vseq); /* Verifica o status do retorno da função analisa_arq_seq. */ if(status!=1) printf("Erro na análise do arquivo de sequecias.\n"); return(0); (const) ptr = &NS; *ptr = vseq[0]; (const) ptr = &TS; *ptr = vseq[1]; /* Matriz usada para armazenar a seqüência a ser alinhada. */ char strAs[TS+1]; /* Matriz de probabilidades de transição do HMM.*/ float pt_hmm[NN+2][9]; for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = (float)0.0; /* Matriz de probabilidades de emissão de símbolos dos estados MATCH. */ float mf_aa[NN][20]; for(i=0;i<NN;i++) for(j=0;j<20;j++) mf_aa[i][j] = 0.0; /* Matriz de probabilidades de emissão de símbolos dos estados INSERT. */ float if_aa[NN+1][20]; for(i=0;i<NN+1;i++) for(j=0;j<20;j++) if_aa[i][j] = 0.0; /* Chamada para a função de leitura da seqüência. */ status = read_seq(strAs); /* Verifica o status do retorno da função read_seq. */ if(status!=2) printf("Erro na leitura do arquivo de sequecias\n"); return(0); /* Chamada para a função de leitura de parâmetros do HMM. */ status = read_model(pt_hmm, mf_aa, if_aa); /* Verifica o status do retorno da função read_model. */ if(status!=3) printf("Erro na leitura do arquivo de parâmetros do HMM\n"); return(0); /* Converte probabilidades para log-probabilidades. */ for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = log2(pt_hmm[i][j]); for(i=0;i<NN;i++) for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); for(j=0;j<20;j++) mf_aa[i][j] = log2(mf_aa[i][j]); for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); /* Chamada para a função de alinhamento de seqüências ao HMM. */ status = alinha_viterbi_basico(pt_hmm, mf_aa, if_aa, strAs); /* Verifica o status do retorno da função alinha_viterbi_basico. */ if(status!=4) printf("Erro no alinhamento da seqüência ao HMM\n"); return(0); /* Término da função principal --> retorno para o SO. */ return (0); /* FUNÇÃO ANALISA ARQUIVO DE SEQÜÊNCIAS. */ int analisa_arq_seq(long int vseq[4]) int i,j,k; long int count=0, soma=0, max=2147483647, maximo=0,minimo=2147483647, media=0; char string[1024000]; /* Abindo arquivo de seqüências para leitura. */ seq_in=fopen(Arquivo,"r"); if(seq_in==NULL) perror("Erro abrindo arquivo de seqüências.\n"); exit(0); /* Verificando o cabecalho de formato do arquivo. */ fgets(string,max,seq_in); if(j=strcmp(string,"ObjectData:\n")) printf("(1) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"DATA: string\n")) printf("(2) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"\n")) printf("(3) Problemas no formato do arquivo --> (%d).\n",j); exit(1); i=0;j=1; /* Obendo estatÍsticas das seqüências. */ while(j) fgets(string,max,seq_in); if(string[0]!='\n' && string[0]!='\r' && string[0]!=' ' && !feof(seq_in)) count = strlen(string)-1; soma += count; if(count>maximo) maximo=count;

211

Page 214: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

if(count<minimo && count!=1) minimo=count; i++; string[0]=' '; else j=0; fprintf(stdout,"O arquivo de seqüências contém: NS = %d, TS (médio) = %d, NÓs do HMM = %s.\n",i,maximo,Model); media = (int) soma/i; vseq[0] = i; vseq[1] = maximo+1; vseq[2] = minimo; vseq[3] = media; return(1); /* FUNÇÃO DE LEITURA DA SEQÜÊNCIA. */ int read_seq (char As[TS+1]) int i,j; char string[TS+1]; /* Reinicializa o indicador de posição do arquivo. */ rewind(seq_in); if(seq_in==NULL) perror("Erro reinicializando indicador de posição\n"); exit(0); /* Copiando a seqüência do arquivo para uma matriz de strings. */ for(i=0;i<3;i++) fgets(string,TS+1,seq_in); fgets(As,TS+1,seq_in); j=(int)strlen(As); As[j-1]='\0'; /* Fechado o arquivo de seqüências */ if(fclose(seq_in)!=0) perror("Erro fechando arquivo de seqüências."); exit(0); return(2); /* FUNÇÃO DE LEITURA DOS PARÂMETROS DO MODELO. */ int read_model (float PT[][9], float mPE[][20], float iPE[][20]) char nome_arquivo[20] = "hmm", string[TS+1]; long int i, j, state; /* Abre arquivo de probabilidades de transições de estados para leitura. */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".transicoes"); pt_in=fopen(nome_arquivo,"r"); if(pt_in==NULL) perror("Erro abrindo arquivo de probabiledades de transições do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de probabilidades de transições. */ /* Lendo as 2 primeiras linhas do arquivo (bloco 0 do HMM) e preenchendo as 2 primeiras linhas da matriz. */ fscanf(pt_in,"%d%f%f%f",&state,&PT[1][4],&PT[0][7],&PT[1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[1][5],&PT[0][8],&PT[1][2]); /* Lendo as próximas 3*NN-2 linhas do arquivo (blocos de 1 a NN-1 do HMM) e preenchendo as próximas NN-1 linhas da matriz. */ for(i=1;i<NN;i++) fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][3],&PT[i][6],&PT[i+1][0]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][4],&PT[i][7],&PT[i+1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][5],&PT[i][8],&PT[i+1][2]); /* Lendo as 2 primeiras linhas do arquivo e preenchendo as 2 primeiras linhas da matriz. */ fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][3],&PT[NN][6]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][4],&PT[NN][7]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][5],&PT[NN][8]); /* Fechado o arquivo da seqüência. */ if(fclose(pt_in)!=0) perror("Erro fechando arquivo de probabilidade de transições."); exit(0); /* Abre arquivo de probabilidades de emissões de símbolos para leitura. */ strcpy(nome_arquivo,"hmm\0"); strcat(nome_arquivo, Model); strcat(nome_arquivo, ".emissoes"); pe_in=fopen(nome_arquivo,"r"); if(pe_in==NULL) perror("Erro abrindo arquivo de probabiledades de emissões do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de probabilidades de emissões. */ for(i=0;i<NN;i++) fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[i][j]); fscanf(pe_in,"%d",&state);

212

Page 215: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

for(j=0;j<20;j++) fscanf(pe_in,"%f",&mPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[NN][j]); /* Fechado o arquivo de seqüências. */ if(fclose(pe_in)!=0) perror("Erro fechando arquivo de probabilidades emissões."); exit(0); return(3); /* FUNÇÃO ALINHA SEQÜÊNCIA --> ALGORITMO DE VITERBI BÁSICO */ int alinha_viterbi_basico(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]) long int s, t, b, aTS; char nome_arquivo[20] = "hmm"; float logodds, escore, max, aux1, aux2; int tcbk=0, teste=0, origem=0; /* Variáveis usadas para a medição de tempo. */ float wtimes=0.0, ctimes=0.0, wbegins=0.0, cbegins=0.0; aTS=strlen(As); /* Matriz de escores de Viterbi. */ struct matriz_PD float del, mat, ins; /* Guarda os escores dos estados delete, insert e match. */ int tb[3]; /* Guarda o estado anterior no caminho Viterbi: td=0, ti=1 e tm=2. */ align[NN+1][aTS+1]; /* Inicializa rotina de tempo. */ hpctim(&wtimes,&ctimes,&wbegins,&cbegins); hpctim(&wtimes,&ctimes,&wbegins,&cbegins); /* Inicializa a matriz de escores de Viterbi. */ for(b=0;b<NN+1;b++) for(t=0;t<aTS+1;t++) align[b][t].del = 0.0; align[b][t].mat = 0.0; align[b][t].ins = 0.0; align[b][t].tb[0] = 11; align[b][t].tb[1] = 11; align[b][t].tb[2] = 11; /* Abre arquivo para escrita do alinhamento. */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".ubalign"); align_out = fopen(nome_arquivo,"w"); if(align_out == NULL) perror("Erro abrindo arquivo de escrita do alinhamento ao HMM.\n"); exit(0); /* Efetua o alinhamento da seqüência ao modelo e escreve este para o arquivo. */ fprintf(align_out,"Algoritmo de Viterbi Básico.\n"); fprintf(align_out,"Alinhamento da seqüência com %d radicais a um HMM com %s nós.\n",aTS,Model); /* Efetua o cálculo da memória mínima requerida */ aux1=(float) 18*NN*aTS; aux2=aux1/1024; max=aux2/1024; fprintf(align_out,"A memória mínima alocada é de %f bytes (= %f Kb = %f Mb).\n",aux1,aux2,max); printf("A memória mínima alocada é de %f bytes (= %f Kb = %f Mb.)\n",aux1,aux2,max); /* Efetua a tomada de tempo no início do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); /* Enche a linha 0 da matriz de Viterbi para a seqüência s (somente insert). */ /* Primeiro Enche as células 0 e 1. */ align[0][0].del=(float)-INFINITO; align[0][0].tb[0]=10; align[0][0].mat=0.0; align[0][0].tb[1]=9; align[0][0].ins=(float)-INFINITO; align[0][0].tb[2]=10; align[0][1].del=(float)-INFINITO; align[0][1].tb[0]=10; align[0][1].mat=(float)-INFINITO; align[0][1].tb[1]=10; align[0][1].ins=iPE[0][IAA(As[0])]-f_aa[IAA(As[0])]+PT[0][PMI]; align[0][1].tb[2]=9; /* Depois as células de t=2 até aTS da linha 0. */ for(t=2;t<aTS+1;t++) align[0][t].del=(float)-INFINITO; align[0][t].tb[0]=10; align[0][t].mat=(float)-INFINITO; align[0][t].tb[1]=10; align[0][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+align[0][t-1].ins+PT[0][PII]; align[0][t].tb[2]=PII; /* Enche as demais linhas da matriz de Viterbi para a seqüência s. */ for(b=1;b<NN+1;b++) /* Preenche a célula 0 da linha b (somente delete). */ if(b==1) align[b][0].del=align[b-1][0].mat+PT[b][PMD]; align[b][0].tb[0]=9; else align[b][0].del=align[b-1][0].del+PT[b][PDD]; align[b][0].tb[0]=PDD; align[b][0].mat=(float)-INFINITO; align[b][0].tb[1]=10;

213

Page 216: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

align[b][0].ins=(float)-INFINITO; align[b][0].tb[2]=10; /* Preenche as demais células da linha b. */ for(t=1;t<aTS+1;t++) /* Preenche o campo delete da célula t da linha b. */ max=align[b-1][t].del+PT[b][PDD]; align[b][t].tb[0]=PDD; aux1=align[b-1][t].mat+PT[b][PMD]; aux2=align[b-1][t].ins+PT[b][PID]; if(max<=aux1) max=aux1; align[b][t].tb[0]=PMD; if(max<aux2) max=aux2; align[b][t].tb[0]=PID; align[b][t].del=max; /* Preenche o campo match da célula t da linha b. */ max=align[b-1][t-1].del+PT[b][PDM]; align[b][t].tb[1]=PDM; aux1=align[b-1][t-1].mat+PT[b][PMM]; aux2=align[b-1][t-1].ins+PT[b][PIM]; if(max<=aux1) max=aux1; if(b==1) align[b][t].tb[1]=9; else align[b][t].tb[1]=PMM; if(max<aux2) max=aux2; align[b][t].tb[1]=PIM; align[b][t].mat=mPE[b-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da linha b. */ max=align[b][t-1].del+PT[b][PDI]; align[b][t].tb[2]=PDI; aux1=align[b][t-1].mat+PT[b][PMI]; aux2=align[b][t-1].ins+PT[b][PII]; if(max<=aux1) max=aux1; align[b][t].tb[2]=PMI; if(max<aux2) max=aux2; align[b][t].tb[2]=PII; align[b][t].ins=iPE[b][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Conclui a fase forward --> Preenche o campo match da célula t da linha b. */ max=align[b-1][t-1].del+PT[b][PDM]; origem=PDM; aux1=align[b-1][t-1].mat+PT[b][PMM]; aux2=align[b-1][t-1].ins+PT[b][PIM]; if(max<=aux1) max=aux1; origem=PMM; if(max<aux2) max=aux2; origem=PIM; logodds=max; escore=exp2(logodds); fprintf(align_out,"Escore Viterbi: seqüência com %d radicais -> %f (log-odds = %f).\n",aTS,escore,logodds); /* Fase de retrocedimento sobre a matriz de Viterbi para recuperar o melhor caminho * Usar a função scanf() para imprimir os símbolos numa matriz (string) de char e * depois fazer as manipulações necessarias para imprimir corretamente num arquivo. */ b=NN+1; t=aTS+1; tcbk=origem; while(tcbk!=9) teste=tcbk; if(teste==PDD || teste==PMD || teste==PID) b--; if(teste==PDD) fprintf(align_out,"D%d(-) ",b); tcbk=align[b][t].tb[0]; else if(teste==PMD) fprintf(align_out,"M%d(%c) ",b,As[t-1]); tcbk=align[b][t].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[t-1]); tcbk=align[b][t].tb[2]; if(teste==PDM || teste==PMM || teste==PIM) b--; t--; if(teste==PDM) fprintf(align_out,"D%d(-) ",b); tcbk=align[b][t].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",b,As[t-1]); tcbk=align[b][t].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[t-1]); tcbk=align[b][t].tb[2]; if(teste==PDI || teste==PMI || teste==PII) t--; if(teste==PDI) fprintf(align_out,"D%d(-) ",b); tcbk=align[b][t].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",b,As[t-1]); tcbk=align[b][t].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[t-1]); tcbk=align[b][t].tb[2]; fprintf(align_out,"\n"); /* Efetua a tomada de tempo no final do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); printf("WALL time gasto no alinhamento --> %f.\n", wtimes); printf("CPU time gasto no alinhamento --> %f.\n", ctimes);

214

Page 217: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

fprintf(align_out,"WALL time gasto no alinhamento --> %f.\n",wtimes); fprintf(align_out,"CPU time gasto no alinhamento --> %f.\n",ctimes); /* Fechado o arquivo de escrita do escore e do alimhamento da seqüência. */ if(fclose(align_out)!=0) perror("Erro fechando arquivo de escrita do alinhamento."); exit(0); return(4);

215

Page 218: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

216

Page 219: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

APÊNDICE 2

ALGORITMO DE VITERBI COM DE 2-NÍVEIS CHECKPOINTS POR LINHAS E PARTICIONAMENTO MÓVEL DE MEMÓRIA

/*

* * Algoritmo de Viterbi para alinhar seqüências de aminoácidos * Copyright (C) 2003 José Olimpio Ferreira * Instituto Nacional de Pesquisas Espaciais e * Universidade Católica de Goiás * All Rights Reserved * **************************************************************************************** * * align_viterbi_ckpts_line_uni.c * * --> Algoritmo de Viterbi para obter o melhor alinhamento de seqüências de aminoácidos a um HMM. * --> Este é um algoritmo de 2-níveis com checkpoints por linhas (padrão) * --> Requer espaço de ordem O(m*SQRT(2*n)). * **************************************************************************************** * * Entradas: * --> Arquivo de probabilidades de transição de estados --> hmm[nos].transições * --> Arquivo de probabilidades de emissão de símbolos --> hmm[nos].emissões * --> Arquivo de seqüências para alinhamento ao HMM --> <nome_arquivo> * --> nós = tamnho do HMM * * Saída: * --> Escore e alinhamento Viterbi da seqüência ao HMM --> hmm[nos].urclalign * * Compilação - Linkedição - Construção do executável -> exemplo para intel pentium 3 celerom * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 align_viterbi_ckpts_line_uni.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpctim.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpcwall.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 etime.c * --> gcc -o align_viterbi_ckpts_line_uni align_viterbi_ckpts_line_uni.o hpctim.o hpcwall.o etime.o -lm * * Compilação - Linkedição - Construção do executável -> exemplo para amd athlon k-7 * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 align_viterbi_ckpts_line_uni.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpctim.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpcwall.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 etime.c * --> gcc -o align_viterbi_ckpts_line_uni align_viterbi_ckpts_line_uni.o hpctim.o hpcwall.o etime.o -lm * **************************************************************************************** */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> /*definie algumas macros. */ #define IAA(x) (strchr(aa, (x)) - aa) /* Índice de símbolos de aminoácidos. */ #define log2(x) ((x)>0.0 ? log(x)*1.44269504 : -19) /* logaritmo de base 2. */ #define exp2(x) (exp(x)*0.69314718) /* exponencial de base 2. */ #define INFINITO 99999999 /* infinito para log-probilidades de valores zero. */ #define aa_alfabeto "ACDEFGHIKLMNPQRSTVWY" /* Alfabeto não-degenerado de aminoácidos. */ /* String contendo os símbolos não-degenerados dos aminoácidos - Alfabeto de aminoácidos. */

217

Page 220: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

const char aa[21] = aa_alfabeto; /* Índices para as probabilidades de transição de estado do HMM. */ enum transicoes_indices PDD=0,PMD=1,PID=2,PDM=3,PMM=4,PIM=5,PDI=6,PMI=7,PII=8; /* Variáveis: * NS --> número de seqüências. * TS --> tamanho da maior seqüência. * NN --> número de nós do HMM. * Model --> HMM com 500,1000,2000,4000,8000 nós. * Arquivo --> nome do arquivo que contém as seqüências no formato HMMpro. */ const long int NS, TS, NN; char Model[80], Arquivo[80]; /* --> Freqüências f(i) - Dayhoff - de occorrência dos 20 aminoácidos * (função massa de probabilidade para aminoácidos). * --> Obtidos do BD SwissProt 34: considerando 21.210.388 raidicais. * --> Apresentadas em ordem alfabetica por código de letras simples. * --> Usadas como probabilidades de emissão de símbolos no modelo nulo ou aleatório (R). */ const float f_aa[20] = (float)-3.72699742, /* (float)0.075520, /* A */ (float)-5.88061460, /* (float)0.016973, /* C */ (float)-4.23707464, /* (float)0.053029, /* D */ (float)-3.98384032, /* (float)0.063204, /* E */ (float)-4.61663135, /* (float)0.040762, /* F */ (float)-3.86884780, /* (float)0.068448, /* G */ (float)-5.47997107, /* (float)0.022406, /* H */ (float)-4.12572395, /* (float)0.057284, /* I */ (float)-4.07344183, /* (float)0.059398, /* K */ (float)-3.42044908, /* (float)0.093399, /* L */ (float)-5.40696564, /* (float)0.023569, /* M */ (float)-4.46456809, /* (float)0.045293, /* N */ (float)-4.34338099, /* (float)0.049262, /* P */ (float)-4.63554859, /* (float)0.040231, /* Q */ (float)-4.27724022, /* (float)0.051573, /* R */ (float)-3.79157763, /* (float)0.072214, /* S */ (float)-4.12144885, /* (float)0.057454, /* T */ (float)-3.93783407, /* (float)0.065252, /* V */ (float)-6.32042847, /* (float)0.012513, /* W */ (float)-4.96646070 /* (float)0.031985 /* Y */ ; /* Ponteiros para arquivos. */ FILE *seq_in, *pt_in, *pe_in, *align_out, *scores_out; /* Declaração dos prototipos das funções internas. */ int analisa_arq_seq(long int vseq[4]); int read_seq (char As[TS+1]); int read_model (float PT[][9], float mPE[][20], float iPE[][20]); int viterbi_L2_ckpt_line(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]); /* Declaração dos prototipos das funções externas. */ void etime(float *result); void hpcwall(double *retval); void hpctim(float *wtime, float *ctime, float *wbegin, float *cbegin); /* FUNÇÃO PRINCIPAL --> MAIN. */ int main (int argc, char *argv[]) long int i, j, vseq[4]=0,0,0,0, *ptr; int status; float *ptrf; /* Trata os argumentos da função principal. */ if(argc!=3) printf("\nVoce esqueceu de informar:\n"); printf("o tamanho do HMM (Opções: 500, 1000, 2000, 4000 e 8000).\n"); printf("o nome do arquivo contendo as seqüências.\n\n"); printf("Entre: <nome_programa> <tamanho_HMM> <nome_arquivo>.\n"); printf("O arquivo deve estar no formato HMMpro --> http://www.netid.com.\n"); exit(1); /* Inicializa algumas variáveis globais do tipo char* e const int. Usadas como informações de nomes de arquivos, de tamnho do modelo e de dimensões de matrizes. */ strcpy(Model,argv[1]); (const) ptr = &NN; *ptr = atoi(argv[1]); strcpy(Arquivo,argv[2]); /* Chamada para a função analise das seqüências */

218

Page 221: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

status = analisa_arq_seq(vseq); /* Verifica o status do retorno da função analisa_arq_seq */ if(status!=1) printf("Erro na analise das sequecias.\n"); return(0); (const) ptr = &NS; *ptr = vseq[0]; (const) ptr = &TS; *ptr = vseq[1]; /* Matriz usada para armazenar as seqüências a serem alinhadas. */ char strAs[TS+1]; /* Matriz de probabilidades de transição do HMM. */ float pt_hmm[NN+2][9]; for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = (float)0.0; /* Matriz de probabilidades de emissão de símbolos dos estados MATCH. */ float mf_aa[NN][20]; for(i=0;i<NN;i++) for(j=0;j<20;j++) mf_aa[i][j] = 0.0; /* Matriz de probabilidades de emissão de símbolos dos estados INSERT. */ float if_aa[NN+1][20]; for(i=0;i<NN+1;i++) for(j=0;j<20;j++) if_aa[i][j] = 0.0; /* Chamada para a função de leitura da seqüência. */ status = read_seq(strAs); /* Verifica o status do retorno da função read_seq. */ if(status!=2) printf("Erro na leitura da sequecia.\n"); return(0); /* Chamada para a função de leitura de parametros do HMM. */ status = read_model(pt_hmm, mf_aa, if_aa); /* Verifica o status do retorno da função read_model. */ if(status!=3) printf("Erro na leitura dos parametros do HMM.\n"); return(0); /* Converte probabilidades para log-probabilidades. */ for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = log2(pt_hmm[i][j]); for(i=0;i<NN;i++) for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); for(j=0;j<20;j++) mf_aa[i][j] = log2(mf_aa[i][j]); for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); /* Chamada para a função de alinhamento da seqüência ao HMM. */ status = viterbi_L2_ckpt_line(pt_hmm, mf_aa, if_aa, strAs); /* Verifica o status do retorno da função viterbi_L2_ckpt_line. */ if(status!=4) printf("Erro no alinhamento da seqüência ao HMM.\n"); return(0); /* Término da função principal --> retorno para o SO */ return (0); /* FUNÇÃO ANALISA ARQUIVO DAS SEQÜÊNCIAs. */ int analisa_arq_seq(long int vseq[4]) int i,j,k; long int count=0, soma=0, max=2147483647, maximo=0,minimo=2147483647, media=0; char string[1024000]; /* Abrindo arquivo das seqüências para leitura. */ seq_in=fopen(Arquivo,"r"); if(seq_in==NULL) perror("Erro abrindo arquivo de seqüências.\n"); exit(0); /* Verificando o cabeçalho de formato do arquivo. */ fgets(string,max,seq_in); if(j=strcmp(string,"ObjectData:\n")) printf("(1) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"DATA: string\n")) printf("(2) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"\n")) printf("(3) Problemas no formato do arquivo --> (%d).\n",j); exit(1); i=0; j=1; /* Obendo estatísticas das seqüências. */ while(j) fgets(string,max,seq_in); if(string[0]!='\n' && string[0]!='\r' && string[0]!=' ' && !feof(seq_in)) count = strlen(string)-1; soma += count; if(count>maximo) maximo=count; if(count<minimo && count!=1) minimo=count; i++; string[0]=' '; else j=0;

219

Page 222: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

fprintf(stdout,"Arquivo de seqüências: NS = %d, TS = %d, Nós do HMM = %s.\n",i,maximo,Model); media = (int) soma/i; vseq[0] = i; vseq[1] = maximo+1; vseq[2] = minimo; vseq[3] = media; return(1); /* FUNÇÃO DE LEITURA DA SEQÜÊNCIA. */ int read_seq (char As[TS+1]) int i,j; char string[TS+1]; /* reinicializa o indicador de posição do arquivo. */ rewind(seq_in); if(seq_in==NULL) perror("Erro reinicializando indicador de posição.\n"); exit(0); /* Copiando a seqüência do arquivo para uma matriz de strings. */ for(i=0;i<3;i++) fgets(string,TS+1,seq_in); fgets(As,TS+1,seq_in); j=(int)strlen(As); As[j-1]='\0'; /* Fechado o arquivo da seqüência. */ if(fclose(seq_in)!=0) perror("Erro fechando arquivo da seqüência."); exit(0); return(2); /* FUNÇÃO DE LEITURA DOS PARAMETROS DO MODELO. */ int read_model (float PT[][9], float mPE[][20], float iPE[][20]) char nome_arquivo[20] = "hmm", string[TS+1]; long int i, j, state; /* Abre arquivo de probabilidades de transições de estados para leitura. */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".transicoes"); pt_in=fopen(nome_arquivo,"r"); if(pt_in==NULL) perror("Erro abrindo arquivo de Probabilidades de Transições do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de Probilidades de Transiçõe. s*/ /* Lendo as 2 primeiras linhas do arquivo (bloco 0 do HMM) e preenchendo as 2 primeiras linhas da matriz. */ fscanf(pt_in,"%d%f%f%f",&state,&PT[1][4],&PT[0][7],&PT[1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[1][5],&PT[0][8],&PT[1][2]); /* Lendo as próximas 3*NN-2 linhas do arquivo (blocos de 1 a NN-1 do HMM) e preenchendo as próximas NN-1 linhas da matriz. */ for(i=1;i<NN;i++) fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][3],&PT[i][6],&PT[i+1][0]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][4],&PT[i][7],&PT[i+1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][5],&PT[i][8],&PT[i+1][2]); /* Lendo as 2 últimas linhas do arquivo e preenchendo as 2 últimas linhas da matriz. */ fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][3],&PT[NN][6]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][4],&PT[NN][7]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][5],&PT[NN][8]); /* Fechado o arquivo de probabilidades de transições. */ if(fclose(pt_in)!=0) perror("Erro fechando arquivo de Probilidades de Transições."); exit(0); /* Abre arquivo de probabilidades de emissões de símbolos para leitura. */ strcpy(nome_arquivo,"hmm\0"); strcat(nome_arquivo, Model); strcat(nome_arquivo, ".emissoes"); pe_in=fopen(nome_arquivo,"r"); if(pe_in==NULL) perror("Erro abrindo arquivo de Probabiledades de Emissões do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de Probilidades de Emissões. */ for(i=0;i<NN;i++) fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&mPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[NN][j]); /* Fechado o arquivo de Probilidades de Emissões. */ if(fclose(pe_in)!=0) perror("Erro fechando arquivo de Probilidades de Emissões."); exit(0);

220

Page 223: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

return(3); /* FUNÇÃO ALINHA SEQÜÊNCIAS --> VITERBI 2-NÍVEIS DE CHECKPOINTS POR LINHAS */ int viterbi_L2_ckpt_line(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]) long int s, t, tt, b, ckpt, inc, li, aTS; char nome_arquivo[20] = "hmm", ch[5]; float logodds, escore, max, aux1, aux2; int i, k, j, dec, ccko, tcbk=0, teste=0, origem=0, CP=0; /* Variáveis usadas para a medição de tempo. */ float wtimes=0.0, ctimes=0.0, wbegins=0.0, cbegins=0.0; /* Obtém o tamanho da seqüência e determina a quantidade de linhas de checkpoints necessárias. */ aTS=strlen(As); CP=(int)sqrt((double)2*NN); if((CP*(CP+1)/2)<NN) CP++; long int ackpt[CP+1]; /* Array para salvar as posições dos checkpoints na matriz_PD. */ /* Array de Checkpoints da Matriz de Viterbi. */ struct matriz_PD float del, mat, ins; /* Guarda os escores dos estados delete, insert e match. */ int tb[3]; /* Guarda o estado anterior no caminho Viterbi: delete=0, insert=1 e match=2.*/ align[CP+1][aTS+1]; /* Uma linha extra para armazenar a linha zero da Matriz_PD. */ /* Inicializa rotina de tempo. */ hpctim(&wtimes,&ctimes,&wbegins,&cbegins); hpctim(&wtimes,&ctimes,&wbegins,&cbegins); /* Inicializa a matriz de checkpoints linha para armazenar os escores de Viterbi. */ for(b=0;b<CP+1;b++) ackpt[b]=INFINITO; for(t=0;t<aTS+1;t++) align[b][t].del = 0.0; align[b][t].mat = 0.0; align[b][t].ins = 0.0; align[b][t].tb[0] = 11; align[b][t].tb[1] = 11; align[b][t].tb[2] = 11; /* Abre arquivo para escrita do escore e do alinhamento. */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".uclalign"); align_out = fopen(nome_arquivo,"w"); if(align_out == NULL) perror("Erro abrindo arquivo de Alinhamentos do HMM.\n"); exit(0); /* Efetua o alinhamento da seqüência ao modelo e escreve este para um arquivo. */ fprintf(align_out,"Aloritmo de viterbi com checkpoints por linhas (padrão).\n"); fprintf(align_out,"Alinhamento da seqüências com %d radicais a um HMM com %s nós.\n",aTS,Model); /* Efetua o calculo da memória minima requerida. */ aux1=(float) 18*(CP+1)*aTS; aux2=aux1/1024; max=aux2/1024; fprintf(align_out,"A memória mínima alocada é de %f bytes (= %f Kb = %f Mb).\n",aux1,aux2,max); printf("A memória mínima alocada é de %f bytes (= %f Kb = %f Mb).\n",aux1,aux2,max); /* Efetua a tomada de tempo no inicio do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); /* Enche a linha 0 da matriz de Viterbi para a seqüência s (somente insert). */ /* Primeiro Enche as celulas 0 e 1. */ align[CP][0].del=(float)-INFINITO; align[CP][0].tb[0]=10; align[CP][0].mat=0.0; align[CP][0].tb[1]=9; align[CP][0].ins=(float)-INFINITO; align[CP][0].tb[2]=10; align[CP][1].del=(float)-INFINITO; align[CP][1].tb[0]=10; align[CP][1].mat=(float)-INFINITO; align[CP][1].tb[1]=10; align[CP][1].ins=iPE[0][IAA(As[0])]-f_aa[IAA(As[0])]+PT[0][PMI]; align[CP][1].tb[2]=9; /* Depois as celulas de t=2 até aTS da linha 0. */ for(t=2;t<aTS+1;t++) align[CP][t].del=(float)-INFINITO; align[CP][t].tb[0]=10; align[CP][t].mat =(float)-INFINITO; align[CP][t].tb[1]=10; align[CP][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+align[CP][t-1].ins+PT[0][PII]; align[CP][t].tb[2]=PII; /* Enche as demais linhas da matriz de Viterbi para a seqüência s. */ ackpt[CP]=0; /* Linha 0 armazenada no checkpoint CP+1. */ b=1; /* Numeração verdadeira das linhas da Matriz de Viterbi. */

221

Page 224: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

ckpt=CP; inc=1; /* Delimitam o tamanho dos ciclos conforme a memória de trabalho disponível. */ dec=0; /* Auxilia no acesso ao último checkpoint salvo a cada ciclo. */ while(ckpt<=NN) for(b=inc;b<=ckpt;b++) if((b-inc)==0) /* Preenche a celula 0 da linha b (somente delete). */ if(b==1) align[b-inc][0].del=align[CP-dec][0].mat+PT[b][PMD]; align[b-inc][0].tb[0]=9; else align[b-inc][0].del=align[CP-dec][0].del+PT[b][PDD]; align[b-inc][0].tb[0]=PDD; align[b-inc][0].mat=(float)-INFINITO; align[b-inc][0].tb[1]=10; align[b-inc][0].ins=(float)-INFINITO; align[b-inc][0].tb[2]=10; /* Preenche as demais celulas da linha b. */ for(t=1;t<aTS+1;t++) /* Preenche o campo delete da celula t da linha b. */ max=align[CP-dec][t].del+PT[b][PDD]; align[b-inc][t].tb[0]=PDD; aux1=align[CP-dec][t].mat+PT[b][PMD]; aux2=align[CP-dec][t].ins+PT[b][PID]; if(max<=aux1) max=aux1; align[b-inc][t].tb[0]=PMD; if(max<aux2) max=aux2; align[b-inc][t].tb[0]=PID; align[b-inc][t].del=max; /* Preenche o campo match da celula t da linha b. */ max=align[CP-dec][t-1].del+PT[b][PDM]; align[b-inc][t].tb[1]=PDM; aux1=align[CP-dec][t-1].mat+PT[b][PMM]; aux2=align[CP-dec][t-1].ins+PT[b][PIM]; if(max<=aux1) max=aux1; if(b==1) align[b-inc][t].tb[1]=9; else align[b-inc][t].tb[1]=PMM; if(max<aux2) max=aux2; align[b-inc][t].tb[1]=PIM; align[b-inc][t].mat=mPE[b-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da celula t da linha b. */ max=align[b-inc][t-1].del+PT[b][PDI]; align[b-inc][t].tb[2]=PDI; aux1=align[b-inc][t-1].mat+PT[b][PMI]; aux2=align[b-inc][t-1].ins+PT[b][PII]; if(max<=aux1) max=aux1; align[b-inc][t].tb[2]=PMI; if(max<aux2) max=aux2; align[b-inc][t].tb[2]=PII; align[b-inc][t].ins=iPE[b][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; if((b-inc)>0) /* Preenche a celula 0 da linha b (somente delete). */ align[b-inc][0].del=align[b-inc-1][0].del+PT[b][PDD]; align[b-inc][0].tb[0]=PDD; align[b-inc][0].mat=(float)-INFINITO; align[b-inc][0].tb[1]=10; align[b-inc][0].ins=(float)-INFINITO; align[b-inc][0].tb[2]=10; /* Preenche as demais celulas da linha b. */ for(t=1;t<aTS+1;t++) /* Preenche o campo delete da celula t da linha b. */ max=align[b-inc-1][t].del+PT[b][PDD]; align[b-inc][t].tb[0]=PDD; aux1=align[b-inc-1][t].mat+PT[b][PMD]; aux2=align[b-inc-1][t].ins+PT[b][PID]; if(max<=aux1) max=aux1; align[b-inc][t].tb[0]=PMD; if(max<aux2) max=aux2; align[b-inc][t].tb[0]=PID; align[b-inc][t].del=max; /* Preenche o campo match da celula t da linha b. */ max=align[b-inc-1][t-1].del+PT[b][PDM]; align[b-inc][t].tb[1]=PDM; aux1=align[b-inc-1][t-1].mat+PT[b][PMM]; aux2=align[b-inc-1][t-1].ins+PT[b][PIM]; if(max<=aux1) max=aux1; align[b-inc][t].tb[1]=PMM; if(max<aux2) max=aux2; align[b-inc][t].tb[1]=PIM; align[b-inc][t].mat=mPE[b-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da celula t da linha b. */ max=align[b-inc][t-1].del+PT[b][PDI]; align[b-inc][t].tb[2]=PDI; aux1=align[b-inc][t-1].mat+PT[b][PMI]; aux2=align[b-inc][t-1].ins+PT[b][PII]; if(max<=aux1) max=aux1; align[b-inc][t].tb[2]=PMI; if(max<aux2) max=aux2; align[b-inc][t].tb[2]=PII; align[b-inc][t].ins=iPE[b][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; dec++;

222

Page 225: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

ackpt[b-1-inc]=ckpt; /* Linha ckpt-1 (ou b-1) armazenada no checkpoint b-1-inc. */ if(ckpt==NN) ckpt++; else if((ckpt+CP-dec)<=NN) inc=ckpt+1; ckpt=ckpt+CP-dec; else inc=ckpt+1; ckpt=NN; ckpt--; ccko=dec; if(ackpt[CP-dec]!=INFINITO) k=CP-dec+1; else k=NN-inc+1; dec=CP-k+1; /* Conclui a fase forward --> Preenche o campo match da celula t da linha b. */ max=align[CP-dec][t-1].del+PT[b][PDM]; origem=PDM; aux1=align[CP-dec][t-1].mat+PT[b][PMM]; aux2=align[CP-dec][t-1].ins+PT[b][PIM]; if(max<=aux1) max=aux1; origem=PMM; if(max<aux2) max=aux2; origem=PIM; logodds=max; escore=exp2(logodds); fprintf(align_out,"Escore Viterbi: seqüência com %d radicais -> %f (log-odds = %f).\n",aTS,escore,logodds); /* Fase de retrocedimento sobre a matriz de Viterbi para recuperar o melhor caminho * Usar a função scanf() para imprimir os símbolos numa matriz (string) de char e * depois fazer as manipulações necessárias para imprimir corretamente num arquivo. */ b=NN+1;li=0; tt=aTS+1; tcbk=origem; while(ccko>=1) while(k>0) teste=tcbk; if(teste==PDD || teste==PMD || teste==PID) b--; k--; if(teste==PDD) fprintf(align_out,"D%d(-) ",b); tcbk=align[k][tt].tb[0]; else if(teste==PMD) fprintf(align_out,"M%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[2]; if(teste==PDM || teste==PMM || teste==PIM) b--; tt--; k--; if(teste==PDM) fprintf(align_out,"D%d(-) ",b); tcbk=align[k][tt].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[2]; if(teste==PDI || teste==PMI || teste==PII) tt--; if(teste==PDI) fprintf(align_out,"D%d(-) ",b); tcbk=align[k][tt].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[2]; while(tcbk==PDI || tcbk== PMI || tcbk==PII) teste=tcbk; if(teste==PDI || teste==PMI || teste==PII) tt--; if(teste==PDI) fprintf(align_out,"D%d(-) ",b); tcbk=align[k][tt].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[k][tt].tb[2]; ccko--; if(ccko>=1) k=CP-ccko+1; li=ackpt[k]+1; for(j=li;j<b;j++) if((j-li)==0) /* Preenche a celula 0 da linha b (somente delete). */ if(j==1) align[j-li][0].del=align[k][0].mat+PT[j][PMD]; align[j-li][0].tb[0]=9; else align[j-li][0].del=align[k][0].del+PT[j][PDD]; align[j-li][0].tb[0]=PDD; align[j-li][0].mat=(float)-INFINITO; align[j-li][0].tb[1]=10; align[j-li][0].ins=(float)-INFINITO; align[j-li][0].tb[2]=10; /* Preenche as demais celulas da linha b. */

223

Page 226: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

for(t=1;t<aTS+1;t++) /* Pode-se implementar retrocedimento restrito fazendo aTS==tt. */ /* Preenche o campo delete da celula t da linha b. */ max=align[k][t].del+PT[j][PDD]; align[j-li][t].tb[0]=PDD; aux1=align[k][t].mat+PT[j][PMD]; aux2=align[k][t].ins+PT[j][PID]; if(max<=aux1) max=aux1; align[j-li][t].tb[0]=PMD; if(max<aux2) max=aux2; align[j-li][t].tb[0]=PID; align[j-li][t].del=max; /* Preenche o campo match da celula t da linha b. */ max=align[k][t-1].del+PT[j][PDM]; align[j-li][t].tb[1]=PDM; aux1=align[k][t-1].mat+PT[j][PMM]; aux2=align[k][t-1].ins+PT[j][PIM]; if(max<=aux1) max=aux1; if(j==1) align[j-li][t].tb[1]=9; else align[j-li][t].tb[1]=PMM; if(max<aux2) max=aux2; align[j-li][t].tb[1]=PIM; align[j-li][t].mat=mPE[j-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da celula t da linha b. */ max=align[j-li][t-1].del+PT[j][PDI]; align[j-li][t].tb[2]=PDI; aux1=align[j-li][t-1].mat+PT[j][PMI]; aux2=align[j-li][t-1].ins+PT[j][PII]; if(max<=aux1) max=aux1; align[j-li][t].tb[2]=PMI; if(max<aux2) max=aux2; align[j-li][t].tb[2]=PII; align[j-li][t].ins=iPE[j][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; if((j-li)>0) /* Preenche a celula 0 da linha b (somente delete). */ align[j-li][0].del=align[j-li-1][0].del+PT[j][PDD]; align[j-li][0].tb[0]=PDD; align[j-li][0].mat=(float)-INFINITO; align[j-li][0].tb[1]=10; align[j-li][0].ins=(float)-INFINITO; align[j-li][0].tb[2]=10; /* Preenche as demais celulas da linha b. */ for(t=1;t<aTS+1;t++) /* Pode-se implementar retrocedimento restrito fazendo aTS==tt. */ /* Preenche o campo delete da celula t da linha b. */ max=align[j-li-1][t].del+PT[j][PDD]; align[j-li][t].tb[0]=PDD; aux1=align[j-li-1][t].mat+PT[j][PMD]; aux2=align[j-li-1][t].ins+PT[j][PID]; if(max<=aux1) max=aux1; align[j-li][t].tb[0]=PMD; if(max<aux2) max=aux2; align[j-li][t].tb[0]=PID; align[j-li][t].del=max; /* Preenche o campo match da celula t da linha b. */ max=align[j-li-1][t-1].del+PT[j][PDM]; align[j-li][t].tb[1]=PDM; aux1=align[j-li-1][t-1].mat+PT[j][PMM]; aux2=align[j-li-1][t-1].ins+PT[j][PIM]; if(max<=aux1) max=aux1; align[j-li][t].tb[1]=PMM; if(max<aux2) max=aux2; align[j-li][t].tb[1]=PIM; align[j-li][t].mat=mPE[j-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da celula t da linha b. */ max=align[j-li][t-1].del+PT[j][PDI]; align[j-li][t].tb[2]=PDI; aux1=align[j-li][t-1].mat+PT[j][PMI]; aux2=align[j-li][t-1].ins+PT[j][PII]; if(max<=aux1) max=aux1; align[j-li][t].tb[2]=PMI; if(max<aux2) max=aux2; align[j-li][t].tb[2]=PII; align[j-li][t].ins=iPE[j][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; if(tcbk==PIM || tcbk== PID) while(tcbk==PIM || tcbk== PID || tcbk== PII) teste=tcbk; if(teste==PDD || teste==PMD || teste==PID) b--; fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[CP][tt].tb[2]; if(teste==PIM) b--; tt--; fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[CP][tt].tb[2];

224

Page 227: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

if(teste==PII) tt--; fprintf(align_out,"I%d(%c) ",b,As[tt-1]); tcbk=align[CP][tt].tb[2]; fprintf(align_out,"\n"); /* Efetua a tomada de tempo no final do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); printf("WALL time gasto no alinhamento --> %f.\n", wtimes); printf("CPU time gasto no alinhamento --> %f.\n", ctimes); fprintf(align_out,"WALL time gasto no alinhamento da seqüência --> %f.\n",wtimes); fprintf(align_out,"CPU time gasto no alinhamento da seqüência --> %f.\n",ctimes); /* Fechado o arquivo de Alimhamentos */ if(fclose(align_out)!=0) perror("Erro fechando arquivo de escrita do alinhamento."); exit(0); return(4);

225

Page 228: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

226

Page 229: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

APÊNDICE 3

ALGORITMO DE VITERBI COM 2-NÍVEIS DE CHECKPOINTS POR DIAGONAIS, COM PARTICIONAMENTO MÓVEL DE MEMÓRIA E

RETROCEDIMENTO RESTRITO

/* **************************************************************************************** * Algoritmo de Viterbi para alinhar seqüências de aminoácidos * Copyright (C) 2004 José Olimpio Ferreira * Instituto Nacional de Pesquisas Espaciais e * Universidade Católica de Goias * All Rights Reserved * **************************************************************************************** * * align_viterbi_ckpts_diag_uni.c * * --> Algoritmo de Viterbi para obter o melhor alinhamento de seqüências de aminoácidos a um HMM. * --> Este é um algoritmo de 2-níveis com checkpoints por diagonais com retrocedimento restrito * --> Requer espaço da ordem de O(4*m*SQRT(n)). * **************************************************************************************** * * Entradas: * --> Arquivo de probabilidades de transição de estados --> hmm[nos].transicoes * --> Arquivo de probabilidades de emissão de símbolos --> hmm[nos].emissoes * --> Arquivo de seqüências para alinhamento ao HMM --> <nome_arquivo> * --> nós = tamnho do HMM * * Saida: * --> Escore + Alinhamento Viterbi da seqüência ao HMM --> hmm[nos].dlalign * **************************************************************************************** * * Compilação - Linkedição - Construção do executável -> exemplo para intel pentium 3 celerom * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 align_viterbi_ckpts_diag_uni.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpctim.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpcwall.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 etime.c * --> gcc -o align_viterbi_ckpts_diag_uni align_viterbi_ckpts_diag_uni.o hpctim.o hpcwall.o etime.o -lm * * Compilação - Linkedição - Construção do executável -> exemplo para amd athlon k-7 * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 align_viterbi_ckpts_diag_uni.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpctim.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpcwall.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 etime.c * --> gcc -o align_viterbi_ckpts_diag_uni align_viterbi_ckpts_diag_uni.o hpctim.o hpcwall.o etime.o -lm * **************************************************************************************** */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> /*definie algumas macros */ #define IAA(x) (strchr(aa, (x)) - aa) /* Índice de símbolos de aminoácidos */ #define log2(x) ((x)>0.0 ? log(x)*1.44269504 : -19) /* logaritmo de base 2 */ #define exp2(x) (exp(x)*0.69314718) /* exponencial de base 2 */ #define INFINITO 99999999 /* infinito para log-probilidades de valores zero */ #define aa_alfabeto "ACDEFGHIKLMNPQRSTVWY" /* Alfabeto não-degenerado de aminoácidos */

227

Page 230: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

/* String contendo os símbolos não-degenerados dos aminoácidos */ const char aa[21] = aa_alfabeto; /* Índices para as probabilidades de transição de estado do HMM */ enum transicoes_indices PDD=0,PMD=1,PID=2,PDM=3,PMM=4,PIM=5,PDI=6,PMI=7,PII=8; /* Variáveis: * NS --> número de seqüências * TS --> tamanho da maior seqüência * NN --> número de nós do HMM * Model --> HMM[500,1000,2000,4000,8000] nós * Arquivo --> nome do arquivo que contém as seqüências no formato HMMpro */ const long int NS, TS, NN; char Model[80], Arquivo[80]; /* --> Freqüências f(i) - Dayhoff - de occorrência dos 20 aminoácidos (função massa de probabilidade * para aminoácidos) convertidas em log-probabilidades. * --> Obtidos do BD SwissProt 34: considerando 21.210.388 radicais. * --> Apresentadas em ordem alfabética por código de letras simples. * --> Usadas como probabilidades de emissão de símbolos no modelo nulo ou aleatório (R). */ const float f_aa[20] = (float)-3.72699742, /* (float)0.075520, /* A */ (float)-5.88061460, /* (float)0.016973, /* C */ (float)-4.23707464, /* (float)0.053029, /* D */ (float)-3.98384032, /* (float)0.063204, /* E */ (float)-4.61663135, /* (float)0.040762, /* F */ (float)-3.86884780, /* (float)0.068448, /* G */ (float)-5.47997107, /* (float)0.022406, /* H */ (float)-4.12572395, /* (float)0.057284, /* I */ (float)-4.07344183, /* (float)0.059398, /* K */ (float)-3.42044908, /* (float)0.093399, /* L */ (float)-5.40696564, /* (float)0.023569, /* M */ (float)-4.46456809, /* (float)0.045293, /* N */ (float)-4.34338099, /* (float)0.049262, /* P */ (float)-4.63554859, /* (float)0.040231, /* Q */ (float)-4.27724022, /* (float)0.051573, /* R */ (float)-3.79157763, /* (float)0.072214, /* S */ (float)-4.12144885, /* (float)0.057454, /* T */ (float)-3.93783407, /* (float)0.065252, /* V */ (float)-6.32042847, /* (float)0.012513, /* W */ (float)-4.96646070 /* (float)0.031985 /* Y */ ; /* Ponteiros para arquivos */ FILE *seq_in, *pt_in, *pe_in, *align_out; /* Declaração dos protótipos das funções internas .*/ int analisa_arq_seq(long int vseq[4]); int read_seq (char As[TS+1]); int read_model (float PT[][9], float mPE[][20], float iPE[][20]); int viterbi_L2_ckpt_diag(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]); /* Declaração dos protótipos das funções externas */ void etime(float *result); void hpcwall(double *retval); void hpctim(float *wtime, float *ctime, float *wbegin, float *cbegin); /* FUNÇÃO PRINCIPAL --> MAIN */ int main (int argc, char *argv[]) long int i, j, vseq[4]=0,0,0,0, *ptr; int status; /* Trata os argumentos da função principal. */ if(argc!=3) printf("\nVocê esqueceu de informar:\n"); printf("o tamanho do HMM (Opções: 500, 1000, 2000, 4000 e 8000),\n"); printf("o nome do arquivo contendo as seqüências.\n\n"); printf("Entre: <nome_programa> <tamanho_HMM> <nome_arquivo>\n"); printf("O arquivo deve estar no formato HMMpro --> http://www.netid.com\n"); exit(1); /* Inicializa algumas variáveis globais do tipo char* e const int. Usadas como informações de nomes de arquivos, de tamnho do modelo e de dimensões de matrizes. */ strcpy(Model,argv[1]); (const) ptr = &NN; *ptr = atoi(argv[1]);

228

Page 231: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

strcpy(Arquivo,argv[2]); /* Chamada para a função análise das seqüências. */ status = analisa_arq_seq(vseq); /* Verifica o status do retorno da função analisa_arq_seq. */ if(status!=1) printf("Erro na análise das seqüêcias.\n"); return(0); (const) ptr = &NS; *ptr = vseq[0]; (const) ptr = &TS; *ptr = vseq[1]; /* Matriz usada para armazenar a seqüência a ser alinhada. */ char strAs[TS+1]; /* Chamada para a função de leitura da seqüência. */ status = read_seq(strAs); /* Verifica o status do retorno da função read_seq. */ if(status!=2) printf("Erro na leitura da seqüêcia.\n"); return(0); /* Matriz de probabilidades de transição do HMM. */ float pt_hmm[NN+2][9]; for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = (float)0.0; /* Matriz de probabilidades de emissão de símbolos dos estados MATCH. */ float mf_aa[NN][20]; for(i=0;i<NN;i++) for(j=0;j<20;j++) mf_aa[i][j] = 0.0; /* Matriz de probabilidades de emissão de símbolos dos estados INSERT. */ float if_aa[NN+1][20]; for(i=0;i<NN+1;i++) for(j=0;j<20;j++) if_aa[i][j] = 0.0; /* Chamada para a função de leitura de parametros do HMM. */ status = read_model(pt_hmm, mf_aa, if_aa); /* Verifica o status do retorno da função read_model. */ if(status!=3) printf("Erro na leitura dos parametros do HMM\n"); return(0); /* convert probabilidades para log-probabilidades. */ for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = log2(pt_hmm[i][j]); for(i=0;i<NN;i++) for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); for(j=0;j<20;j++) mf_aa[i][j] = log2(mf_aa[i][j]); for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); /* Chamada para a função de alinhamento da seqüência ao HMM. */ status = viterbi_L2_ckpt_diag(pt_hmm, mf_aa, if_aa, strAs); /* Verifica o status do retorno da função viterbi_L2_ckpt_diag. */ if(status!=4) printf("Erro no alinhamento da seqüência ao HMM\n"); return(0); return (0); /* FUNÇÃO ANALISA ARQUIVO DE SEQÜÊNCIAS. */ int analisa_arq_seq(long int vseq[4]) int i,j; long int count=0, soma=0, max=2048000, maximo=0,minimo=2048000, media=0; char string[2048000]; /* Abindo arquivo de seqüências para leitura. */ seq_in=fopen(Arquivo,"r"); if(seq_in==NULL) perror("Erro abrindo arquivo de seqüências.\n"); exit(0); /* Verificando o cabeçalho de formato do arquiv. */ fgets(string,max,seq_in); if(j=strcmp(string,"ObjectData:\n")) printf("(1) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"DATA: string\n")) printf("(2) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"\n")) printf("(3) Problemas no formato do arquivo --> (%d).\n",j); exit(1); i=0; j=1; /* Obendo estatísticas das seqüências. */ while(j) fgets(string,max,seq_in); if(string[0]!='\n' && string[0]!='\r' && string[0]!=' ' && !feof(seq_in)) count = strlen(string)-1; soma += count; if(count>maximo) maximo=count; if(count<minimo && count!=1) minimo=count; i++; string[0]=' '; else j=0;

229

Page 232: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

fprintf(stdout,"NS = %d, TS = %d, Nós do HMM = %s.\n",i,maximo,Model); media = (int) soma/i; vseq[0] = i; vseq[1] = maximo+1; vseq[2] = minimo; vseq[3] = media; return(1); /* FUNÇÃO DE LEITURA DA SEQÜÊNCIA. */ int read_seq (char As[TS+1]) int i; char string[TS+1]; /* reinicializa o indicador de posição do arquivo. */ rewind(seq_in); if(seq_in==NULL) perror("Erro reinicializando indicador de posição.\n"); exit(0); /* Copiando a seqüência do arquivo para uma matriz de strings. */ for(i=0;i<3;i++) fgets(string,TS+1,seq_in); fgets(As,TS+1,seq_in); i=(int)strlen(As); As[i-1]='\0'; /* Fechado o arquivo da seqüência. */ if(fclose(seq_in)!=0) perror("Erro fechando arquivo de seqüências."); exit(0); return(2); /* FUNÇÃO DE LEITURA DOS PARÂMETROS DO MODELO */ int read_model (float PT[][9], float mPE[][20], float iPE[][20]) char nome_arquivo[20] = "hmm", string[TS+1]; long int i, j, state; /* Abre arquivo de probabilidades de transições de estados para leitura. */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".transicoes"); pt_in=fopen(nome_arquivo,"r"); if(pt_in==NULL) perror("Erro abrindo arquivo de Probabiledades de Transições do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de Probabilidades de Transições. */ /* Lendo as 2 primeiras linhas do arquivo (bloco 0 do HMM) e preenchendo as 2 primeiras linhas da matriz. */ fscanf(pt_in,"%d%f%f%f",&state,&PT[1][4],&PT[0][7],&PT[1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[1][5],&PT[0][8],&PT[1][2]); /* Lendo as próximas 3*NN-2 linhas do arquivo (blocos de 1 a NN-1 do HMM) e preenchendo as próximas NN-1 linhas da matriz. */ for(i=1;i<NN;i++) fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][3],&PT[i][6],&PT[i+1][0]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][4],&PT[i][7],&PT[i+1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][5],&PT[i][8],&PT[i+1][2]); /* Lendo as 2 últimas linhas do arquivo e preenchendo as 2 últimas linhas da matriz. */ fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][3],&PT[NN][6]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][4],&PT[NN][7]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][5],&PT[NN][8]); /* Fechado o arquivo de de Probabilidades de Transições. */ if(fclose(pt_in)!=0) perror("Erro fechando arquivo de Probabilidades Transições."); exit(0); /* Abre arquivo de probabilidades de emissões de símbolos para leitura. */ strcpy(nome_arquivo,"hmm\0"); strcat(nome_arquivo, Model); strcat(nome_arquivo, ".emissoes"); pe_in=fopen(nome_arquivo,"r"); if(pe_in==NULL) perror("Erro abrindo arquivo de Probabiledades de Emissões do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de Probabilidades de Emissões. */ for(i=0;i<NN;i++) fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&mPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[NN][j]); /* Fechado o arquivo de Probabilidades de Emissões. */

230

Page 233: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

if(fclose(pe_in)!=0) perror("Erro fechando arquivo de Probabilidade Emissões."); exit(0); return(3); /* FUNÇÃO ALINHA SEQÜÊNCIA --> VITERBI 2-NÍVEIS DE CHECKPOINTS POR DIAGONAIS */ int viterbi_L2_ckpt_diag(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]) long int t, tt, b, aTS, ckpt, inc, l, li, low, lower, upper, d; char nome_arquivo[20] = "hmm", ch[5]; float logodds, escore, max, aux1, aux2; int i, k, j, dec, ccko, tcbk=0, teste=0, origem=0, CP=0, flag=0; /* Variáveis usadas para a medição de tempo. */ float wtimes=0.0, ctimes=0.0, wbegins=0.0, cbegins=0.0; /* Obtém o tamanho da seqüência e determina o número de diagonais da matriz de viterbi. */ aTS=strlen(As); d=NN+aTS+1; /* Determina a quantidade de diagonais de checkpoints necessárias. */ CP=(int)sqrt((double)(4*d))); /* O(2*sqrt(d)) <=> O(2*sqrt(2n)) <= O(4*sqrt(n)) */ if(CP%2>0) CP++; while((0.5*CP*(0.5*CP+1))<d) CP=CP+2; CP=CP+2; /* 2 diagonais extras para armazenar as diagonais 0 e 1. */ long int ackpt[CP]; /* Array para salvar as posições dos checkpoints na matriz_PD. */ /* Array de Checkpoints da Matriz de Viterbi (matriz_PD). */ struct matriz_PD float del, mat, ins; /* Guarda os escores para os estados delete, insert e match. */ int tb[3]; /* Guarda o estado anterior no caminho Viterbi: delete=0, match=1 e insert=2. */ diag[CP][aTS+1]; /* Inicializa rotina de tempo. */ hpctim(&wtimes,&ctimes,&wbegins,&cbegins); hpctim(&wtimes,&ctimes,&wbegins,&cbegins); /* Inicializa o Array de Checkpoints da matriz de escores de Viterbi. */ for(b=0;b<CP;b++) ackpt[b]=INFINITO; for(t=0;t<aTS+1;t++) diag[b][t].del = 0.0; diag[b][t].mat = 0.0; diag[b][t].ins = 0.0; /* poderia ser + INFINITO */ diag[b][t].tb[0] = 11; diag[b][t].tb[1] = 11; diag[b][t].tb[2] = 11; /* Abre arquivo de saída para escrita do alinhamento */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".udalign"); align_out = fopen(nome_arquivo,"w"); if(align_out == NULL) perror("Erro abrindo arquivo de do alinhamento ao HMM.\n"); exit(0); /* Efetua o alinhamento da seqüência ao modelo e escreve este para o arquivo. */ fprintf(align_out,"Aloritmo de viterbi de 2-níveis de checkpoints por diagonais e retrocedimento restrito.\n"); fprintf(align_out,"Alinhamento da seqüência com %d radicais a um HMM com %s nós\n",aTS,Model); // fprintf(align_out,"Foram usados %d diagonais de checkpoints.\n",CP); // fprintf(align_out,"Seqüência --> %s\n",As); /* Efetua o cálculo da memória mínima requerida. */ aux1=(float) 18*(CP)*(aTS+1); aux2=aux1/1024; max=aux2/1024; fprintf(align_out,"A memória mínima alocada é de %f bytes (= %f Kbytes = %f Mbytes)\n",aux1,aux2,max); printf("A memória mínima alocada é de %f bytes (= %f Kbytes = %f Mbytes)\n",aux1,aux2,max); /* Efetua a tomada de tempo no início do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); /* Enche a diagonal 0 da matriz de Viterbi para a seqüência (somente insert). */ diag[CP-2][0].del=(float)-INFINITO; diag[CP-2][0].tb[0]=10; diag[CP-2][0].mat=0.0; diag[CP-2][0].tb[1]=9; diag[CP-2][0].ins=(float)-INFINITO; diag[CP-2][0].tb[2]=10; ackpt[CP-2]=0; /* diagonal 0 armazenada no checkpoint CP-2. */ /* Enche a diagonal 1 da matriz de Viterbi para a seqüência. */ diag[CP-1][0].del=diag[CP-1][0].mat+PT[1][PMD]; diag[CP-1][0].tb[0]=9; diag[CP-1][0].mat=(float)-INFINITO; diag[CP-1][0].tb[1]=10; diag[CP-1][0].ins=(float)-INFINITO; diag[CP-1][0].tb[2]=10; diag[CP-1][1].del=(float)-INFINITO; diag[CP-1][1].tb[0]=10;

231

Page 234: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

diag[CP-1][1].mat=(float)-INFINITO; diag[CP-1][1].tb[1]=10; diag[CP-1][1].ins=iPE[0][IAA(As[0])]-f_aa[IAA(As[0])]+PT[0][PMI]; diag[CP-1][1].tb[2]=9; ackpt[CP-1]=1; /* diagonal 1 armazenada no checkpoint CP-1. */ /* Enche as demais diagonais da matriz de Viterbi para a seqüência. */ b=2; /* Numeração verdadeira das diagonais da Matriz de Viterbi. */ ckpt=CP-1; inc=2; /* Delimitam o tamanho dos ciclos conforme a memória de trabalho disponível. */ dec=0; /* Auxilia no acesso aos dois último checkpoints salvos a cada ciclo. */ while(ckpt<d) for(b=inc;b<=ckpt;b++) /* Caso o HMM seja maior do que a seqüência (NN > aTS). */ if(NN>aTS) if(b<=aTS) lower=0; upper=b; if(b>aTS && b<=NN) lower=0; upper=aTS; if(b>NN) lower=b-NN; upper=aTS; /* Caso o HMM seja igual a seqüência (NN = aTS). */ if(NN==aTS) if(b<=NN) lower=0; upper=b; else lower=b-NN; upper=aTS; /* Caso o HMM seja menor do que a seqüência (NN < aTS). */ if(NN<aTS) if(b<=NN) lower=0; upper=b; if(b<=aTS && b>NN) lower=b-NN; upper=b; if(b>aTS) lower=b-NN; upper=aTS; low=lower; if((b-inc)==0) /* A diagonal b-inc (checkpoint 0) depende dos dois últimos checkpoints armazenados. */ if(lower==0) /* Preenche a célula 0 (linha b) da diagonal b (somente delete). */ diag[b-inc][0].del=diag[CP-dec-1][0].del+PT[b][PDD]; diag[b-inc][0].tb[0]=PDD; diag[b-inc][0].mat=(float)-INFINITO; diag[b-inc][0].tb[1]=10; diag[b-inc][0].ins=(float)-INFINITO; diag[b-inc][0].tb[2]=10; lower++; /* Preenche as demais células da diagonal b. */ for(t=lower;t<=upper;t++) if((b-t)>0) /* Preenche as células correspondentes às linhas b-1 até 1 na diagonal b. */ /* Preenche o campo delete da célula t da diagonal b. */ max=diag[CP-dec-1][t].del+PT[b-t][PDD]; diag[b-inc][t].tb[0]=PDD; aux1=diag[CP-dec-1][t].mat+PT[b-t][PMD]; aux2=diag[CP-dec-1][t].ins+PT[b-t][PID]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[0]=PMD; if(max<aux2) max=aux2; diag[b-inc][t].tb[0]=PID; diag[b-inc][t].del=max; /* Preenche o campo match da célula t da diagonal b. */ max=diag[CP-dec-2][t-1].del+PT[b-t][PDM]; diag[b-inc][t].tb[1]=PDM; aux1=diag[CP-dec-2][t-1].mat+PT[b-t][PMM]; aux2=diag[CP-dec-2][t-1].ins+PT[b-t][PIM]; if(max<=aux1) max=aux1; if(b==2 && (b-t)==1) diag[b-inc][t].tb[1]=9; else diag[b-inc][t].tb[1]=PMM; if(max<aux2) max=aux2; diag[b-inc][t].tb[1]=PIM; diag[b-inc][t].mat=mPE[b-t-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da diagonal b. */ max=diag[CP-dec-1][t-1].del+PT[b-t][PDI]; diag[b-inc][t].tb[2]=PDI; aux1=diag[CP-dec-1][t-1].mat+PT[b-t][PMI]; aux2=diag[CP-dec-1][t-1].ins+PT[b-t][PII]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[2]=PMI; if(max<aux2) max=aux2; diag[b-inc][t].tb[2]=PII; diag[b-inc][t].ins=iPE[b-t][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Se b-t == 0 a célula pertence a linha 0 e recebe tratamento especial (somente insert). */ else /* Preenche a célula correspondentes à linha 0 na diagonal b. */ diag[b-inc][t].del=(float)-INFINITO; diag[b-inc][t].tb[0]=10; diag[b-inc][t].mat=(float)-INFINITO; diag[b-inc][t].tb[1]=10;

232

Page 235: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

diag[b-inc][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+diag[CP-dec-1][t-1].ins+PT[0][PII]; diag[b-inc][t].tb[2]=PII; if((b-inc)==1) /* A diagonal b-inc (checkpoint 0) depende do último checkpoint armazenado. */ if(lower==0) /* Preenche a célula 0 (linha b) da diagonal b (somente delete). */ diag[b-inc][0].del=diag[b-inc-1][0].del+PT[b][PDD]; diag[b-inc][0].tb[0]=PDD; diag[b-inc][0].mat=(float)-INFINITO; diag[b-inc][0].tb[1]=10; diag[b-inc][0].ins=(float)-INFINITO; diag[b-inc][0].tb[2]=10; lower++; /* Preenche as demais células da diagonal b. */ for(t=lower;t<=upper;t++) if((b-t)>0) /* Preenche as células correspondentes às linhas b-1 até 1 na diagonal b. */ /* Preenche o campo delete da célula t da diagonal b. */ max=diag[b-inc-1][t].del+PT[b-t][PDD]; diag[b-inc][t].tb[0]=PDD; aux1=diag[b-inc-1][t].mat+PT[b-t][PMD]; aux2=diag[b-inc-1][t].ins+PT[b-t][PID]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[0]=PMD; if(max<aux2) max=aux2; diag[b-inc][t].tb[0]=PID; diag[b-inc][t].del=max; /* Preenche o campo match da célula t da diagonal b. */ max=diag[CP-dec-1][t-1].del+PT[b-t][PDM]; diag[b-inc][t].tb[1]=PDM; aux1=diag[CP-dec-1][t-1].mat+PT[b-t][PMM]; aux2=diag[CP-dec-1][t-1].ins+PT[b-t][PIM]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[1]=PMM; if(max<aux2) max=aux2; diag[b-inc][t].tb[1]=PIM; diag[b-inc][t].mat=mPE[b-t-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da diagonal b. */ max=diag[b-inc-1][t-1].del+PT[b-t][PDI]; diag[b-inc][t].tb[2]=PDI; aux1=diag[b-inc-1][t-1].mat+PT[b-t][PMI]; aux2=diag[b-inc-1][t-1].ins+PT[b-t][PII]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[2]=PMI; if(max<aux2) max=aux2; diag[b-inc][t].tb[2]=PII; diag[b-inc][t].ins=iPE[b-t][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Se b-t == 0 a célula pertence a linha 0 e recebe tratamento especial (somente insert). */ else /* Preenche a célula correspondentes à linha 0 na diagonal b. */ diag[b-inc][t].del=(float)-INFINITO; diag[b-inc][t].tb[0]=10; diag[b-inc][t].mat=(float)-INFINITO; diag[b-inc][t].tb[1]=10; diag[b-inc][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+diag[b-inc-1][t-1].ins+PT[0][PII]; diag[b-inc][t].tb[2]=PII; if((b-inc)>1) /* A diagonal b (checkpoint > 1) não depende dos dois últimos checkpoints armazenados. */ if(lower==0) /* Preenche a célula 0 da diagonal b (somente delete). */ diag[b-inc][0].del=diag[b-inc-1][0].del+PT[b][PDD]; diag[b-inc][0].tb[0]=PDD; diag[b-inc][0].mat=(float)-INFINITO; diag[b-inc][0].tb[1]=10; diag[b-inc][0].ins=(float)-INFINITO; diag[b-inc][0].tb[2]=10; lower++; /* Preenche as demais células da diagonal b. */ for(t=lower;t<=upper;t++) if((b-t)>0) /* Preenche as células correspondentes às linhas b-1 até 1 na diagonal b. */ /* Preenche o campo delete da célula t da diagonal b. */ max=diag[b-inc-1][t].del+PT[b-t][PDD]; diag[b-inc][t].tb[0]=PDD; aux1=diag[b-inc-1][t].mat+PT[b-t][PMD]; aux2=diag[b-inc-1][t].ins+PT[b-t][PID]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[0]=PMD; if(max<aux2) max=aux2; diag[b-inc][t].tb[0]=PID; diag[b-inc][t].del=max;

233

Page 236: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

/* Preenche o campo match da célula t da diagonal b. */ max=diag[b-inc-2][t-1].del+PT[b-t][PDM]; diag[b-inc][t].tb[1]=PDM; aux1=diag[b-inc-2][t-1].mat+PT[b-t][PMM]; aux2=diag[b-inc-2][t-1].ins+PT[b-t][PIM]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[1]=PMM; if(max<aux2) max=aux2; diag[b-inc][t].tb[1]=PIM; diag[b-inc][t].mat=mPE[b-t-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da diagonal b. */ max=diag[b-inc-1][t-1].del+PT[b-t][PDI]; diag[b-inc][t].tb[2]=PDI; aux1=diag[b-inc-1][t-1].mat+PT[b-t][PMI]; aux2=diag[b-inc-1][t-1].ins+PT[b-t][PII]; if(max<=aux1) max=aux1; diag[b-inc][t].tb[2]=PMI; if(max<aux2) max=aux2; diag[b-inc][t].tb[2]=PII; diag[b-inc][t].ins=iPE[b-t][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Se b-t == 0 a célula pertence a linha 0 e recebe tratamento especial (somente insert). */ else /* Preenche a célula correspondentes à linha 0 na diagonal b. */ diag[b-inc][t].del=(float)-INFINITO; diag[b-inc][t].tb[0]=10; diag[b-inc][t].mat=(float)-INFINITO; diag[b-inc][t].tb[1]=10; diag[b-inc][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+diag[b-inc-1][t-1].ins+PT[0][PII]; diag[b-inc][t].tb[2]=PII; dec=dec+2; b--; if((b-inc-1)>=0) ackpt[b-inc-1]=ckpt-1; /* Diagonal ckpt-1 (ou b-1) armazenada no checkpoint b-inc-1. */ if((b-inc)>=0) ackpt[b-inc]=ckpt; /* Diagonal ckpt (ou b) armazenada no checkpoint b-inc. */ if(ckpt==d-1) ckpt++; else if((ckpt+CP-2-dec)<d-1) inc=ckpt+1; ckpt=ckpt+CP-2-dec; else inc=ckpt+1; ckpt=d-1; /* Conclui a fase forward --> Preenche o campo match da célula t da diagonal b. */ b++; max=diag[b-inc-1][aTS].del+PT[b-aTS][PDM]; origem=PDM; aux1=diag[b-inc-1][aTS].mat+PT[b-aTS][PMM]; aux2=diag[b-inc-1][aTS].ins+PT[b-aTS][PIM]; if(max<=aux1) max=aux1; origem=PMM; if(max<aux2) max=aux2; origem=PIM; logodds=max; escore=exp2(logodds); fprintf(align_out,"Escore Viterbi: seqüência com %d radicais -> %f (log-odds = %f).\n",aTS,escore,logodds); b--; /* Fase de retrocedimento sobre a matriz de Viterbi para recuperar o melhor caminho * Usar a função scanf() para imprimir os símbolos numa matriz (string) de char e * depois fazer as manipulações necessárias para imprimir corretamente num arquivo. */ ccko=dec; k=b-inc+1; b=NN+aTS+1; li=0; tt=aTS+1; teste=origem; ckpt=ckpt-k; /* Retrocede sobre a última seção da matriz de PD. */ /* Retrocede de NN+1 para NN ---> procedimento especial. */ if(teste==PDM || teste==PMM || teste==PIM) b--; tt--;k--; if(teste==PDM) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; /* Retrocede sobre as demais linhas da seção. */ while(k>0) teste=tcbk; if(teste==PDD || teste==PMD || teste==PID) b--; k--; if(teste==PDD) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMD) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2];

234

Page 237: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

if(teste==PDM || teste==PMM || teste==PIM) if(k==1) k--; b--; flag=1; else b=b-2; tt--; k=k-2; if(teste==PDM) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; if(teste==PDI || teste==PMI || teste==PII) b--; tt--; k--; if(teste==PDI) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; while(ccko>=2) /* Recalcula as seções da matriz de PD durante a fase de retrocedimento. */ ccko=ccko-2; if(ccko>=2) k=CP-ccko; /* Tamanho do ciclo em cada seção. */ /* Limite inferior para os índices das diagonais no recalculo das seções da matriz PD. */ li=ckpt-k; ckpt=ckpt-k; for(j=li;j<b;j++) /* Caso o HMM seja maior do que a seqüência (NN > aTS). */ if(NN>aTS) if(j<=aTS) lower=0; upper=j; if(j>aTS && j<=NN) lower=0; upper=aTS; if(j>NN) lower=j-NN; upper=aTS; /* Caso o HMM seja igual a seqüência (NN = aTS). */ if(NN==aTS) if(j<=NN) lower=0; upper=j; else lower=j-NN; upper=aTS; /* Caso o HMM seja menor do que a seqüência (NN < aTS). */ if(NN<aTS) if(j<=NN) lower=0; upper=j; if(j<=aTS && j>NN) lower=j-NN; upper=j; if(j>aTS) lower=j-NN; upper=aTS; /* A próxima linha implementa o retrocedimento restrito. */ if(j>(b-tt)) lower=j-b+tt; if(upper>tt) upper=tt; low=lower; /* A diagonal j-li (checkpoint 0) depende dos dois últimos checkpoints armazenados. */ if((j-li)==0) if(lower==0) /* Preenche a célula 0 (linha j) da diagonal j (somente delete). */ diag[j-li][0].del=diag[k+1][0].del+PT[j][PDD]; diag[j-li][0].tb[0]=PDD; diag[j-li][0].mat=(float)-INFINITO; diag[j-li][0].tb[1]=10; diag[j-li][0].ins=(float)-INFINITO; diag[j-li][0].tb[2]=10; lower++; /* Preenche as demais células da diagonal j. */ for(t=lower;t<=upper;t++) if((j-t)>0) /* Preenche as células correspondentes às linhas l=j-1 até 1. */ /* Preenche o campo delete da célula t da diagonal j. */ max=diag[k+1][t].del+PT[j-t][PDD]; diag[j-li][t].tb[0]=PDD; aux1=diag[k+1][t].mat+PT[j-t][PMD]; aux2=diag[k+1][t].ins+PT[j-t][PID]; if(max<=aux1) max=aux1; diag[j-li][t].tb[0]=PMD; if(max<aux2) max=aux2; diag[j-li][t].tb[0]=PID; diag[j-li][t].del=max; /* Preenche o campo match da célula t da diagonal j. */

235

Page 238: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

max=diag[k][t-1].del+PT[j-t][PDM]; diag[j-li][t].tb[1]=PDM; aux1=diag[k][t-1].mat+PT[j-t][PMM]; aux2=diag[k][t-1].ins+PT[j-t][PIM]; if(max<=aux1) max=aux1; if(j==2 && (j-t)==1) diag[j-li][t].tb[1]=9; else diag[j-li][t].tb[1]=PMM; if(max<aux2) max=aux2; diag[j-li][t].tb[1]=PIM; diag[j-li][t].mat=mPE[j-t-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da diagonal j. */ max=diag[k+1][t-1].del+PT[j-t][PDI]; diag[j-li][t].tb[2]=PDI; aux1=diag[k+1][t-1].mat+PT[j-t][PMI]; aux2=diag[k+1][t-1].ins+PT[j-t][PII]; if(max<=aux1) max=aux1; diag[j-li][t].tb[2]=PMI; if(max<aux2) max=aux2; diag[j-li][t].tb[2]=PII; diag[j-li][t].ins=iPE[j-t][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Se j-t==0 a célula pertence a linha 0 e recebe tratamento especial (somente insert). */ else /* Preenche a célula correspondente à linha 0 na diagonal j. */ diag[j-li][t].del=(float)-INFINITO; diag[j-li][t].tb[0]=10; diag[j-li][t].mat=(float)-INFINITO; diag[j-li][t].tb[1]=10; diag[j-li][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+diag[k+1][t-1].ins+PT[0][PII]; diag[j-li][t].tb[2]=PII; if((j-li)==1) /* A diagonal j-inc (checkpoint 0) depende do último checkpoint armazenado. */ if(lower==0) /* Preenche a célula 0 (linha j) da diagonal j (somente delete). */ diag[j-li][0].del=diag[j-li-1][0].del+PT[j][PDD]; diag[j-li][0].tb[0]=PDD; diag[j-li][0].mat=(float)-INFINITO; diag[j-li][0].tb[1]=10; diag[j-li][0].ins=(float)-INFINITO; diag[j-li][0].tb[2]=10; lower++; /* Preenche as demais células da diagonal j. */ for(t=lower;t<=upper;t++) if((j-t)>0) /* Preenche as células correspondentes às linhas l=j-1 até 1. */ /* Preenche o campo delete da célula t da diagonal j. */ max=diag[j-li-1][t].del+PT[j-t][PDD]; diag[j-li][t].tb[0]=PDD; aux1=diag[j-li-1][t].mat+PT[j-t][PMD]; aux2=diag[j-li-1][t].ins+PT[j-t][PID]; if(max<=aux1) max=aux1; diag[j-li][t].tb[0]=PMD; if(max<aux2) max=aux2; diag[j-li][t].tb[0]=PID; diag[j-li][t].del=max; /* Preenche o campo match da célula t da diagonal j. */ max=diag[k+1][t-1].del+PT[j-t][PDM]; diag[j-li][t].tb[1]=PDM; aux1=diag[k+1][t-1].mat+PT[j-t][PMM]; aux2=diag[k+1][t-1].ins+PT[j-t][PIM]; if(max<=aux1) max=aux1; diag[j-li][t].tb[1]=PMM; if(max<aux2) max=aux2; diag[j-li][t].tb[1]=PIM; diag[j-li][t].mat=mPE[j-t-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da diagonal j. */ max=diag[j-li-1][t-1].del+PT[j-t][PDI]; diag[j-li][t].tb[2]=PDI; aux1=diag[j-li-1][t-1].mat+PT[j-t][PMI]; aux2=diag[j-li-1][t-1].ins+PT[j-t][PII]; if(max<=aux1) max=aux1; diag[j-li][t].tb[2]=PMI; if(max<aux2) max=aux2; diag[j-li][t].tb[2]=PII; diag[j-li][t].ins=iPE[j-t][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Se j-t==0 a célula pertence a linha 0 e recebe tratamento especial (somente insert). */ else /* Preenche a célula correspondentes à linha 0 na diagonal j. */ diag[j-li][t].del=(float)-INFINITO; diag[j-li][t].tb[0]=10; diag[j-li][t].mat=(float)-INFINITO; diag[j-li][t].tb[1]=10; diag[j-li][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+diag[j-li-1][t-1].ins+PT[0][PII]; diag[j-li][t].tb[2]=PII;

236

Page 239: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

/* A diagonal j (checkpoint > 1) não depende dos dois últimos checkpoints armazenados. */ if((j-li)>1) if(lower==0) /* Preenche a célula 0 da diagonal j (somente delete). */ diag[j-li][0].del=diag[j-li-1][0].del+PT[j][PDD]; diag[j-li][0].tb[0]=PDD; diag[j-li][0].mat=(float)-INFINITO; diag[j-li][0].tb[1]=10; diag[j-li][0].ins=(float)-INFINITO; diag[j-li][0].tb[2]=10; lower++; /* Preenche as demais células da diagonal j. */ for(t=lower;t<=upper;t++) if((j-t)>0) /* Preenche as células correspondentes às linhas l=j-1 até 1. */ /* Preenche o campo delete da célula t da diagonal j. */ max=diag[j-li-1][t].del+PT[j-t][PDD]; diag[j-li][t].tb[0]=PDD; aux1=diag[j-li-1][t].mat+PT[j-t][PMD]; aux2=diag[j-li-1][t].ins+PT[j-t][PID]; if(max<=aux1) max=aux1; diag[j-li][t].tb[0]=PMD; if(max<aux2) max=aux2; diag[j-li][t].tb[0]=PID; diag[j-li][t].del=max; /* Preenche o campo match da célula t da diagonal j. */ max=diag[j-li-2][t-1].del+PT[j-t][PDM]; diag[j-li][t].tb[1]=PDM; aux1=diag[j-li-2][t-1].mat+PT[j-t][PMM]; aux2=diag[j-li-2][t-1].ins+PT[j-t][PIM]; if(max<=aux1) max=aux1; diag[j-li][t].tb[1]=PMM; if(max<aux2) max=aux2; diag[j-li][t].tb[1]=PIM; diag[j-li][t].mat=mPE[j-t-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da diagonal j. */ max=diag[j-li-1][t-1].del+PT[j-t][PDI]; diag[j-li][t].tb[2]=PDI; aux1=diag[j-li-1][t-1].mat+PT[j-t][PMI]; aux2=diag[j-li-1][t-1].ins+PT[j-t][PII]; if(max<=aux1) max=aux1; diag[j-li][t].tb[2]=PMI; if(max<aux2) max=aux2; diag[j-li][t].tb[2]=PII; diag[j-li][t].ins=iPE[j-t][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Se j-t==0 a célula pertence a linha 0 e recebe tratamento especial (somente insert). */ else /* Preenche a célula correspondentes à linha 0 na diagonal j. */ diag[j-li][t].del=(float)-INFINITO; diag[j-li][t].tb[0]=10; diag[j-li][t].mat=(float)-INFINITO; diag[j-li][t].tb[1]=10; diag[j-li][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+diag[j-li-1][t-1].ins+PT[0][PII]; diag[j-li][t].tb[2]=PII; /* Retrocede sobre as seções da matriz de PD. */ if(flag==1) b--; tt--; k--; flag=0; if(teste==PDM) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; while(k>0) teste=tcbk; if(teste==PDD || teste==PMD || teste==PID) b--; k--; if(teste==PDD) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMD) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; if(teste==PDM || teste==PMM || teste==PIM) if(k==1) k--; b--; flag=1; else b=b-2; tt--; k=k-2; if(teste==PDM) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0];

237

Page 240: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

else if(teste==PMM) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; if(teste==PDI || teste==PMI || teste==PII) b--; tt--; k--; if(teste==PDI) fprintf(align_out,"D%d(-) ",b-tt); tcbk=diag[k][tt].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[1]; else fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); tcbk=diag[k][tt].tb[2]; /* Retrocedimento sobre as diagonais 0 e 1 da matriz_PD --> tratamento especial. */ teste=tcbk; if(b==2 && tt==0) if(teste==PDD) b--; fprintf(align_out,"D%d(-) ",b-tt); if(b==2 && tt==1) if(teste==PDI) b--; tt--; fprintf(align_out,"D%d(-) ",b-tt); if(teste==PID) b--; fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); if(b==2 && tt==2) if(teste==PII) b--; tt--; fprintf(align_out,"I%d(%c) ",b-tt,As[tt-1]); fprintf(align_out,"\n"); /* Efetua a tomada de tempo no final do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); printf("WALL time gasto no alinhamento --> %f.\n", wtimes); printf("CPU time gasto no alinhamento --> %f.\n", ctimes); fprintf(align_out,"WALL time gasto no alinhamento da seqüência --> %f.\n",wtimes); fprintf(align_out,"CPU time gasto no alinhamento da seqüência --> %f.\n",ctimes); /* Fechado o arquivo de saída para gravar o escore e o alimhamento. */ if(fclose(align_out)!=0) perror("Erro fechando arquivo de saída do alinhamento."); exit(0); return(4);

238

Page 241: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

APÊNDICE 4

ALGORITMO DE VITERBI COM 2-NÍVEIS DE CHECKPOINTS BIDIMENSIONAIS, PARTICIONAMENTO FIXO DE MEMÓRIA E

RETROCEDIMENTO RESTRITO

/* **************************************************************************************** * Algoritmo de Viterbi para alinhar seqüências de aminoácidos * Copyright (C) 2003 José Olimpio Ferreira * Instituto Nacional de Pesquisas Espaciais e * Universidade Catolica de Goias * All Rights Reserved * **************************************************************************************** * * align_vit_ckpts_bid_uni.c * * --> Algoritmo de Viterbi para obter o melhor alinhamento de seqüências de aminoácidos a um HMM. * --> Este é um algoritmo de 2-níveis com checkpoints (completos) bidimensionais * por linhas x colunas (padrão) * --> Requer espaço da ordem O(2*m*SQRT(n) + 2*n*SQRT(m)). * --> É útil para alinhamento de seqüências e modelos grandes * --> Tamanhos mínimos de seqüências e modelos --> 50 * **************************************************************************************** * * Entradas: * --> Arquivo de probabilidades de transição de estados --> hmm[nos].transicoes * --> Arquivo de probabilidades de emissão de símbolos --> hmm[nos].emissoes * --> Arquivo de seqüências para alinhamento ao HMM --> <nome_arquivo> * --> nós = tamnho do HMM * * Saída: * --> Escore e alinhamento Viterbi da seqüência ao HMM --> hmm[nos].upalign * * Compilação - Linkedição - Construção do executavel ---> exemplo para intel pentium 3 * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 align_vit_ckpts_bid_uni.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpctim.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 hpcwall.c * --> gcc -c -march=pentium3 -mcpu=pentium3 -mfpmath=sse -O3 etime.c * --> gcc -o align_vit_ckpts_bid_uni align_vit_ckpts_bid_uni.o hpctim.o hpcwall.o etime.o -lm * * Compilação - Linkedição - Construção do executavel ---> exemplo para amd athlon k7 * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 align_vit_ckpts_bid_uni.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpctim.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 hpcwall.c * --> gcc -c -march=athlon -mcpu=athlon -mfpmath=387 -O3 etime.c * --> gcc -o align_vit_ckpts_bid_uni align_vit_ckpts_bid_uni.o hpctim.o hpcwall.o etime.o -lm * **************************************************************************************** */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <string.h> /*definie algumas macros */ #define IAA(x) (strchr(aa, (x)) - aa) /* Índice de símbolos de aminoácidos. */ #define log2(x) ((x)>0.0 ? log(x)*1.44269504 : -19) /* logaritmo de base 2. */ #define exp2(x) (exp(x)*0.69314718) /* exponencial de base 2. */ #define INFINITO 99999999 /* infinito para log-probilidades de valores zero. */ #define aa_alfabeto "ACDEFGHIKLMNPQRSTVWY" /* Alfabeto não-degenerado de aminoácidos. */

239

Page 242: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

/* String contendo os símbolos não-degenerados dos aminoácidos - Alfabeto de aminoácidos. */ const char aa[21] = aa_alfabeto; /* Índices para as probabilidades de transição de estado do HMM. */ enum transicoes_indices PDD=0,PMD=1,PID=2,PDM=3,PMM=4,PIM=5,PDI=6,PMI=7,PII=8; /* Váriaveis: * NS --> número de seqüências. * TS --> tamanho da maior seqüência. * NN --> número de nós do HMM. * Model --> HMM 500,1000,2000,4000,8000 nós. * Arquivo --> nome do arquivo que contém as seqüências no formato HMMpro. */ const long int NS, TS, NN; char Model[80], Arquivo[80]; /* --> Freqüências f(i) - Dayhoff - de occorrência dos 20 aminoácidos. * (função massa de probabilidade para aminoácidos). * --> Obtidos do BD SwissProt 34: considerando 21.210.388 raidicais. * --> Apresentadas em ordem alfabética por código de letras simples. * --> Usadas como probabilidades de emissão de símbolos no modelo nulo ou aleatorio (R). */ const float f_aa[20] = (float)-3.72699742, /* (float)0.075520, /* A */ (float)-5.88061460, /* (float)0.016973, /* C */ (float)-4.23707464, /* (float)0.053029, /* D */ (float)-3.98384032, /* (float)0.063204, /* E */ (float)-4.61663135, /* (float)0.040762, /* F */ (float)-3.86884780, /* (float)0.068448, /* G */ (float)-5.47997107, /* (float)0.022406, /* H */ (float)-4.12572395, /* (float)0.057284, /* I */ (float)-4.07344183, /* (float)0.059398, /* K */ (float)-3.42044908, /* (float)0.093399, /* L */ (float)-5.40696564, /* (float)0.023569, /* M */ (float)-4.46456809, /* (float)0.045293, /* N */ (float)-4.34338099, /* (float)0.049262, /* P */ (float)-4.63554859, /* (float)0.040231, /* Q */ (float)-4.27724022, /* (float)0.051573, /* R */ (float)-3.79157763, /* (float)0.072214, /* S */ (float)-4.12144885, /* (float)0.057454, /* T */ (float)-3.93783407, /* (float)0.065252, /* V */ (float)-6.32042847, /* (float)0.012513, /* W */ (float)-4.96646070 /* (float)0.031985 /* Y */ ; /* Ponteiros para arquivos. */ FILE *seq_in, *pt_in, *pe_in, *align_out, *scores_out; /* Declaração dos protótipos das funções internas. */ int analisa_arq_seq(long int vseq[4]); int read_seq (char As[TS+1]); int read_model (float PT[][9], float mPE[][20], float iPE[][20]); int viterbi_L2_ckpt_bid(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]); /* Declaração dos protótipos das funções externas. */ void etime(float *result); void hpcwall(double *retval); void hpctim(float *wtime, float *ctime, float *wbegin, float *cbegin); /* FUNÇÃO PRINCIPAL --> MAIN */ int main (int argc, char *argv[]) long int i, j, vseq[4]=0,0,0,0, *ptr; int status; float *ptrf; /* Trata os argumentos da função principal. */ if(argc!=3) printf("\nVocê esqueceu de informar:\n"); printf("o tamanho do HMM (Exemplo: 100, 500, 1000, 2000, 4000),\n"); printf("o nome do arquivo contendo as seqüências.\n\n"); printf("Entre: <nome_programa> <tamanho_HMM> <nome_arquivo>.\n"); printf("O arquivo deve estar no formato HMMpro --> http://www.netid.com.\n"); exit(1); /* Inicializa algumas variáveis globais do tipo char* e const int. Usadas como informações de nomes de arquivos, de tamanho do modelo e de dimensões de matrizes. */ strcpy(Model,argv[1]);

240

Page 243: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

(const) ptr = &NN; *ptr = atoi(argv[1]); strcpy(Arquivo,argv[2]); /* Chamada para a função análise das seqüências. */ status = analisa_arq_seq(vseq); /* Verifica o status do retorno da função analisa_arq_seq. */ if(status!=1) printf("Erro na análise das sequecias.\n"); return(0); (const) ptr = &NS; *ptr = vseq[0]; (const) ptr = &TS; *ptr = vseq[1]; /* Matriz usada para armazenar as seqüências a serem alinhadas. */ char strAs[TS+1]; /* Matriz de probabilidades de transição do HMM. */ float pt_hmm[NN+2][9]; for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = (float)0.0; /* Matriz de probabilidades de emissão de símbolos dos estados MATCH. */ float mf_aa[NN][20]; for(i=0;i<NN;i++) for(j=0;j<20;j++) mf_aa[i][j] = 0.0; /* Matriz de probabilidades de emissão de símbolos dos estados INSERT */ float if_aa[NN+1][20]; for(i=0;i<NN+1;i++) for(j=0;j<20;j++) if_aa[i][j] = 0.0; /* Chamada para a função de leitura da seqüência. */ status = read_seq(strAs); /* Verifica o status do retorno da função read_seq. */ if(status!=2) printf("Erro na leitura da seqüência.\n"); return(0); /* Chamada para a função de leitura de parâmetros do HMM. */ status = read_model(pt_hmm, mf_aa, if_aa); /* Verifica o status do retorno da função read_model. */ if(status!=3) printf("Erro na leitura dos parametros do HMM.\n"); return(0); /* Converte probabilidades para log-probabilidades. */ for(i=0;i<NN+2;i++) for(j=0;j<9;j++) pt_hmm[i][j] = log2(pt_hmm[i][j]); for(i=0;i<NN;i++) for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); for(j=0;j<20;j++) mf_aa[i][j] = log2(mf_aa[i][j]); for(j=0;j<20;j++) if_aa[i][j] = log2(if_aa[i][j]); /* Chamada para a função de alinhamento da seqüência ao HMM. */ status = viterbi_L2_ckpt_bid(pt_hmm, mf_aa, if_aa, strAs); /* Verifica o status do retorno da função viterbi_L2_ckpt_bid */ if(status!=4) printf("Erro no alinhamento da seqüência ao HMM.\n"); return(0); return (0); /* FUNÇÃO DE ANÁLISE ARQUIVO DE SEQÜÊNCIAS. */ int analisa_arq_seq(long int vseq[4]) int i,j,k; long int count=0, soma=0, max=1024000, maximo=0,minimo=1024000, media=0; char string[1024000]; /* Abindo arquivo de seqüências para leitura. */ seq_in=fopen(Arquivo,"r"); if(seq_in==NULL) perror("Erro abrindo arquivo de seqüências.\n"); exit(0); /* Verificando o cabeçalho de formato do arquivo. */ fgets(string,max,seq_in); if(j=strcmp(string,"ObjectData:\n")) printf("(1) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"DATA: string\n")) printf("(2) Problemas no formato do arquivo --> (%d).\n",j); exit(1); fgets(string,max,seq_in); if(j=strcmp(string,"\n")) printf("(3) Problemas no formato do arquivo --> (%d).\n",j); exit(1); i=0;j=1; /* Obendo estatísticas das seqüências. */ while(j) fgets(string,max,seq_in); if(string[0]!='\n' && string[0]!='\r' && string[0]!=' ' && !feof(seq_in)) count = strlen(string)-1; soma += count; if(count>maximo) maximo=count; if(count<minimo && count!=1) minimo=count; i++; string[0]=' ';

241

Page 244: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

else j=0; fprintf(stdout,"NS = %d, TS = %d, Nos do HMM = %s.\n",i,maximo,Model); media = (int) soma/i; vseq[0] = i; vseq[1] = maximo+1; vseq[2] = minimo; vseq[3] = media; return(1); /* FUNÇÃO DE LEITURA DA SEQÜÊNCIA. */ int read_seq (char As[TS+1]) int i,j; char string[TS+1]; /* reinicializa o indicador de posição do arquivo. */ rewind(seq_in); if(seq_in==NULL) perror("Erro reinicializando indicador de posição.\n"); exit(0); /* Copiando as seqüências do arquivo para uma matriz de strings. */ for(i=0;i<3;i++) fgets(string,TS+1,seq_in); fgets(As,TS+1,seq_in); j=(int)strlen(As); As[j-1]='\0'; /* Fechado o arquivo da seqüência. */ if(fclose(seq_in)!=0) perror("Erro fechando arquivo da seqüência. "); exit(0); return(2); /* FUNÇÃO DE LEITURA DOS PARÂMETROS DO MODELO */ int read_model (float PT[][9], float mPE[][20], float iPE[][20]) char nome_arquivo[20] = "hmm", string[TS+1]; long int i, j, state; /* Abre arquivo de probabilidades de transições de estados para leitura. */ strcat(nome_arquivo, Model); strcat(nome_arquivo, ".transicoes"); pt_in=fopen(nome_arquivo,"r"); if(pt_in==NULL) perror("Erro abrindo arquivo de Probabiledades de Transições do HMM..\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de Probilidades de Transições. */ /* Lendo as 2 primeiras linhas do arquivo (bloco 0 do HMM) e preenchendo as 2 primeiras linhas da matriz. */ fscanf(pt_in,"%d%f%f%f",&state,&PT[1][4],&PT[0][7],&PT[1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[1][5],&PT[0][8],&PT[1][2]); /* Lendo as proximas 3*NN-2 linhas do arquivo (blocos de 1 a NN-1 do HMM) e preenchendo as próximas NN-1 linhas da matriz. */ for(i=1;i<NN;i++) fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][3],&PT[i][6],&PT[i+1][0]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][4],&PT[i][7],&PT[i+1][1]); fscanf(pt_in,"%d%f%f%f",&state,&PT[i+1][5],&PT[i][8],&PT[i+1][2]); /* Lendo as 2 últimas linhas do arquivo e preenchendo as 2 últimas linhas da matriz. */ fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][3],&PT[NN][6]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][4],&PT[NN][7]); fscanf(pt_in,"%d%f%f",&state,&PT[NN+1][5],&PT[NN][8]); /* Fechado o arquivo de probabilidades de transições. */ if(fclose(pt_in)!=0) perror("Erro fechando arquivo de Probilidades de Transições."); exit(0); /* Abre arquivo de probabilidades de emissoes de símbolos para leitura. */ strcpy(nome_arquivo,"hmm\0"); strcat(nome_arquivo, Model); strcat(nome_arquivo, ".emissoes"); pe_in=fopen(nome_arquivo,"r"); if(pe_in==NULL) perror("Erro abrindo arquivo de Probabiledades de Emissoes do HMM.\n"); exit(0); /* Copiando as probabilidades do arquivo para a matriz de Probilidades de Emissoes. */ for(i=0;i<NN;i++) fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&mPE[i][j]); fscanf(pe_in,"%d",&state); for(j=0;j<20;j++) fscanf(pe_in,"%f",&iPE[NN][j]); /* Fechado o arquivo de probabilidades de emissões de símbolos. */

242

Page 245: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

if(fclose(pe_in)!=0) perror("Erro fechando arquivo de Probilidades de Emissoes."); exit(0); return(3); /* FUNÇÃO ALINHA SEQÜÊNCIAS --> VITERBI 2-NÏVEIS DE CHECKPOINTS BIDIMENSIONAIS. */ int viterbi_L2_ckpt_bid(float PT[][9],float mPE[][20],float iPE[][20],char As[TS+1]) long int s, kk, t, tt, b, bt, aTS, nn, ats; char nome_arquivo[20] = "hmm", ch[5]; float logodds, escore, max, aux1, aux2; double aux3; int i, k, l, j, ki, kj, ii, dd, tcbk, teste, origem, CP, RL, RC, il, ic, src, trt; /* Váriaveis usadas para a medição de tempo. */ float wtimes=0.0, ctimes=0.0, wbegins=0.0, cbegins=0.0; /* Obtém o tamanho da seqüência s. */ aTS=strlen(As); /* Determina a quantidade de linhas de checkpoints necessárias. */ CP=(int)sqrt((double)NN); RL=(int)2*CP-1; /* Número de linhas de checkpoint.s */ CP=(int)sqrt((double)aTS); RC=(int)2*CP-1; /* Número de colunas de checkpoints. */ /* Intervalo entre dois checkpoints-linha sucessivos. */ aux1=(float)NN/RL; aux2=(float)modf((double)aux1,&aux3); il=(int)aux3; if(aux2>=0.5) il++; while(il*RL>=NN) RL--; /* Intervalo entre dois checkpoints-coluna sucessivos. */ aux1=(float)(aTS+1)/RC; aux2=(float)modf((double)aux1,&aux3); ic=(int)aux3; if(aux2>=0.5) ic++; while(ic*RC>=aTS) RC--; /* Struct de células da Matriz de Viterbi. */ /* Matrizes de checkpoints para armazenar células da Matriz de Viterbi. */ struct celula_PD float del, mat, ins; /* Guarda os escores dos estados delete, insert e match. */ int tb[3]; /* Guarda o estado anterior no caminho Viterbi: delete=0, match=1 e insert=2. */ linha[RL+1][aTS+1],coluna[NN+1][RC+1]; /* Linha/Coluna extra para armazenar a linha/coluna zero. */ /* Matriz de Checkpoints de Trabalho para calcular linhas/seções-linha da Matriz de Viterbi. */ union ckpt_trab struct celula_PD fw[2][aTS+1]; struct celula_PD bw[2*il][2*ic]; align; /* Inicializa rotina de tempo. */ hpctim(&wtimes,&ctimes,&wbegins,&cbegins); hpctim(&wtimes,&ctimes,&wbegins,&cbegins); /* Inicializa as matrizes de checkpoints linha/colunas (de escores Viterbi). */ for(b=0;b<RL+1;b++) for(t=0;t<aTS+1;t++) linha[b][t].del = 0.0; linha[b][t].mat = 0.0; linha[b][t].ins = 0.0; linha[b][t].tb[0] = 11; linha[b][t].tb[1] = 11; linha[b][t].tb[2] = 11; for(b=0;b<NN+1;b++) for(t=0;t<RC+1;t++) coluna[b][t].del = 0.0; coluna[b][t].mat = 0.0; coluna[b][t].ins = 0.0; coluna[b][t].tb[0] = 11; coluna[b][t].tb[1] = 11; coluna[b][t].tb[2] = 11; /* Inicializa as matrizes da união ckpt_trab. */ for(b=0;b<2;b++) for(t=0;t<aTS+1;t++) align.fw[b][t].del = 0.0; align.fw[b][t].mat = 0.0; align.fw[b][t].ins = 0.0; align.fw[b][t].tb[0] = 11; align.fw[b][t].tb[1] = 11; align.fw[b][t].tb[2] = 11; /* Abre arquivo para escrita dos alinhamentos. */ strcat(nome_arquivo, Model);

243

Page 246: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

strcat(nome_arquivo, ".upalign"); align_out = fopen(nome_arquivo,"w"); if(align_out == NULL) perror("Erro abrindo arquivo de Alinhamentos do HMM.\n"); exit(0); /* Efetua o alinhamento da seqüência ao modelo e escreve este para um arquivo. */ fprintf(align_out,"Alinhamento da seqüência com %d radicais ao HMM com %s nós.\n",aTS,Model); fprintf(align_out,"Algoritmo de viterbi de 2-níveis de checkpoints bidimensionais (padrão).\n"); /* Efetua o calculo da memoria mínima requerida. */ aux1=(float) 18*RL*aTS+18*RC*NN+2*18*aTS; aux2=aux1/1024; max=aux2/1024; fprintf(align_out,"A memória mínima alocada é de %f bytes (= %f Kb = %f Mb).\n",aux1,aux2,max); printf("A memória mínima alocada é de %f bytes (= %f Kb = %f Mb).\n",aux1,aux2,max); /* Efetua a tomada de tempo no início do alinhameto da seqüência. */ hpctim(&wtimes, &ctimes, &wbegins, &cbegins); /* Inicia a fase de cálculos para frente --> FORWARD. */ /* Enche a linha 0 da matriz de Viterbi para a seqüência s (somente insert). */ /* Primeiro Enche as células 0 e 1. */ align.fw[0][0].del=(float)-INFINITO; align.fw[0][0].mat=0.0; align.fw[0][0].ins=(float)-INFINITO; align.fw[0][0].tb[0]=10; align.fw[0][0].tb[1]=9; align.fw[0][0].tb[2]=10; align.fw[0][1].del=(float)-INFINITO;

linha[0][t].del = align.fw[0][t].del;

coluna[0][j].ins = align.fw[0][kk].ins;

align.fw[trt][0].mat=(float)-INFINITO;

align.fw[0][1].mat=(float)-INFINITO; align.fw[0][1].ins=iPE[0][IAA(As[0])]-f_aa[IAA(As[0])]+PT[0][PMI]; align.fw[0][1].tb[0]=10; align.fw[0][1].tb[1]=10; align.fw[0][1].tb[2]=9; /* Depois as células de t=2 ate aTS da linha 0. */ for(t=2;t<=aTS;t++) align.fw[0][t].del=(float)-INFINITO; align.fw[0][t].mat=(float)-INFINITO; align.fw[0][t].ins=iPE[0][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+align.fw[0][t-1].ins+PT[0][PII]; align.fw[0][t].tb[0]=10; align.fw[0][t].tb[1]=10; align.fw[0][t].tb[2]=PII; /* Salva a linha 0 no checkpoint-linha 0. */ for(t=0;t<=aTS;t++)

linha[0][t].mat = align.fw[0][t].mat; linha[0][t].ins = align.fw[0][t].ins; linha[0][t].tb[0] = align.fw[0][t].tb[0]; linha[0][t].tb[1] = align.fw[0][t].tb[1]; linha[0][t].tb[2] = align.fw[0][t].tb[2]; /* Salva a posição (linha) 0 dos checkpoints-colunas. */ for(kk=0;kk<=RC*ic;kk=kk+ic) j=(int)kk/ic; coluna[0][j].del = align.fw[0][kk].del; coluna[0][j].mat = align.fw[0][kk].mat;

coluna[0][j].tb[0] = align.fw[0][kk].tb[0]; coluna[0][j].tb[1] = align.fw[0][kk].tb[1]; coluna[0][j].tb[2] = align.fw[0][kk].tb[2]; /* Enche as demais linhas da matriz de Viterbi para a seqüência s. */ i=1; for(b=1;b<=NN;b++) src=(b-1)%2; trt=b%2; /* Preenche a célula 0 da linha b (somente delete). */ if(b==1) align.fw[trt][0].del=align.fw[src][0].mat+PT[b][PMD]; align.fw[trt][0].tb[0]=9; else align.fw[trt][0].del=align.fw[src][0].del+PT[b][PDD]; align.fw[trt][0].tb[0]=PDD;

align.fw[trt][0].ins=(float)-INFINITO;

244

Page 247: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

align.fw[trt][0].tb[1]=10; align.fw[trt][0].tb[2]=10; /* Preenche as demais células da linha b. */ for(t=1;t<=aTS;t++) /* Preenche o campo delete da célula t da linha b. */ max= align.fw[src][t].del+PT[b][PDD]; aux1=align.fw[src][t].mat+PT[b][PMD]; aux2=align.fw[src][t].ins+PT[b][PID]; align.fw[trt][t].tb[0]=PDD; if(max<=aux1) max=aux1; align.fw[trt][t].tb[0]=PMD; if(max<aux2) max=aux2; align.fw[trt][t].tb[0]=PID;

coluna[b][j].ins = align.fw[trt][kk].ins;

linha[i][t].tb[1] = align.fw[trt][t].tb[1];

if(max<aux2) max=aux2; origem=PIM;

align.fw[trt][t].del=max; /* Preenche o campo match da célula t da linha b. */ max= align.fw[src][t-1].del+PT[b][PDM]; aux1=align.fw[src][t-1].mat+PT[b][PMM]; aux2=align.fw[src][t-1].ins+PT[b][PIM]; align.fw[trt][t].tb[1]=PDM; if(max<=aux1) max=aux1; if(b==1) align.fw[trt][t].tb[1]=9; else align.fw[trt][t].tb[1]=PMM; if(max<aux2) max=aux2; align.fw[trt][t].tb[1]=PIM; align.fw[trt][t].mat=mPE[b-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da linha b. */ max= align.fw[trt][t-1].del+PT[b][PDI]; aux1=align.fw[trt][t-1].mat+PT[b][PMI]; aux2=align.fw[trt][t-1].ins+PT[b][PII]; align.fw[trt][t].tb[2]=PDI; if(max<=aux1) max=aux1; align.fw[trt][t].tb[2]=PMI; if(max<aux2) max=aux2; align.fw[trt][t].tb[2]=PII; align.fw[trt][t].ins=iPE[b][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Salva a posição (linha) b dos checkpoints-colunas. */ for(kk=0;kk<=RC*ic;kk=kk+ic) j=(int)kk/ic; coluna[b][j].del = align.fw[trt][kk].del; coluna[b][j].mat = align.fw[trt][kk].mat;

coluna[b][j].tb[0] = align.fw[trt][kk].tb[0]; coluna[b][j].tb[1] = align.fw[trt][kk].tb[1]; coluna[b][j].tb[2] = align.fw[trt][kk].tb[2]; /* Salva as linhas i*il (i=1,2,..,RL) nos checkpoints-linha il(s). */ if(b==i*il && i<=RL) for(t=0;t<=aTS;t++) linha[i][t].del = align.fw[trt][t].del; linha[i][t].mat = align.fw[trt][t].mat; linha[i][t].ins = align.fw[trt][t].ins; linha[i][t].tb[0] = align.fw[trt][t].tb[0];

linha[i][t].tb[2] = align.fw[trt][t].tb[2]; i++; /* Conclui a fase forward --> Preenche o campo match da célula t da linha b. */ max= align.fw[trt][t-1].del+PT[b][PDM]; aux1=align.fw[trt][t-1].mat+PT[b][PMM]; aux2=align.fw[trt][t-1].ins+PT[b][PIM]; origem=PDM; if(max<=aux1) max=aux1; origem=PMM;

logodds=max; escore=exp2(logodds); fprintf(align_out,"Escore Viterbi: seqüência com %d radicais -> %f (log-odds = %f).\n",aTS,escore,logodds); /* Fase de retrocedimento (Restrito) sobre a matriz de Viterbi para recuperar o melhor caminho. Primeiro --> faz-se as recomputações da última seção - a seção do canto inferior direiro, que contém a célula (NN,aTS) com o maior escore. Segundo --> faz-se o retrocedimento nesta seção. Passa-se entao para a próxima seção e procede com os passos Primeiro e Segundo. Prosegue desta forma ate alcancar a célula (0,0) no canto superior esquerdo.

245

Page 248: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

*/ tcbk=origem; ki=0; kj=0; nn=NN; ats=aTS; bt=NN+1; tt=aTS+1; i=il*RL; j=ic*RC; ii=0; while(tcbk!=9 && ii==0) /* Recomputações de seções da matriz de Viterbi para a seqüência. */ /* Copia a seção do checkpoint-linha RL-ki (linha[RL-ki][:]) na matriz-seção align.bw[0][:]. */ for(t=j;t<=ats;t++) align.bw[0][t-j].del = linha[RL-ki][t].del; align.bw[0][t-j].mat = linha[RL-ki][t].mat; align.bw[0][t-j].ins = linha[RL-ki][t].ins; align.bw[0][t-j].tb[0] = linha[RL-ki][t].tb[0]; align.bw[0][t-j].tb[1] = linha[RL-ki][t].tb[1]; align.bw[0][t-j].tb[2] = linha[RL-ki][t].tb[2]; /* Copia a seção do checkpoint-coluna RC-ki (coluna[:][RC-ki]) na matriz-seção align.bw[:][0]. */

align.bw[b-i][0].tb[1] = coluna[b][RC-kj].tb[1];

for(b=i+1;b<=nn;b++) align.bw[b-i][0].del = coluna[b][RC-kj].del; align.bw[b-i][0].mat = coluna[b][RC-kj].mat; align.bw[b-i][0].ins = coluna[b][RC-kj].ins; align.bw[b-i][0].tb[0] = coluna[b][RC-kj].tb[0];

align.bw[b-i][0].tb[2] = coluna[b][RC-kj].tb[2]; /* Enche as demais linhas/colunas das seções da matriz de Viterbi para a seqüência. */ for(b=i+1;b<=nn;b++) for(t=j+1;t<=ats;t++) /* Preenche o campo delete da célula t da linha b. */ max= align.bw[b-i-1][t-j].del+PT[b][PDD]; aux1=align.bw[b-i-1][t-j].mat+PT[b][PMD]; aux2=align.bw[b-i-1][t-j].ins+PT[b][PID]; align.bw[b-i][t-j].tb[0]=PDD; if(max<=aux1) max=aux1; align.bw[b-i][t-j].tb[0]=PMD; if(max<aux2) max=aux2; align.bw[b-i][t-j].tb[0]=PID; align.bw[b-i][t-j].del=max; /* Preenche o campo match da célula t da linha b. */ max= align.bw[b-i-1][t-j-1].del+PT[b][PDM]; aux1=align.bw[b-i-1][t-j-1].mat+PT[b][PMM]; aux2=align.bw[b-i-1][t-j-1].ins+PT[b][PIM]; align.bw[b-i][t-j].tb[1]=PDM; if(max<=aux1) max=aux1; if(b==1) align.bw[b-i][t-j].tb[1]=9; else align.bw[b-i][t-j].tb[1]=PMM; if(max<aux2) max=aux2; align.bw[b-i][t-j].tb[1]=PIM; align.bw[b-i][t-j].mat=mPE[b-1][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Preenche o campo insert da célula t da linha b. */ max= align.bw[b-i][t-j-1].del+PT[b][PDI]; aux1=align.bw[b-i][t-j-1].mat+PT[b][PMI]; aux2=align.bw[b-i][t-j-1].ins+PT[b][PII]; align.bw[b-i][t-j].tb[2]=PDI; if(max<=aux1) max=aux1; align.bw[b-i][t-j].tb[2]=PMI; if(max<aux2) max=aux2; align.bw[b-i][t-j].tb[2]=PII; align.bw[b-i][t-j].ins=iPE[b][IAA(As[t-1])]-f_aa[IAA(As[t-1])]+max; /* Recupera o caminho de Viterbi atraves das seções da matriz de Viterbi para a seqüência. */ dd=0; do teste=tcbk;dd=1; if((teste==PDD || teste==PMD || teste==PID) && bt>i) bt--; dd=0; if(teste==PDD) fprintf(align_out,"D%d(-) ",bt); tcbk=align.bw[bt-i][tt-j].tb[0]; else if(teste==PMD) fprintf(align_out,"M%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[1]; else fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[2]; if((teste==PDM || teste==PMM || teste==PIM) && bt>i && tt>j) bt--; tt--; dd=0;

246

Page 249: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

if(teste==PDM) fprintf(align_out,"D%d(-) ",bt); tcbk=align.bw[bt-i][tt-j].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[1];

if(bt==i && tt==j && i>0 && j>0) nn=i; ats=j; ki++; kj++;i=il*(RL-ki); j=ic*(RC-kj);

if(tcbk=PIM) bt--; tt--; fprintf(align_out,"I%d(%c) ",bt,As[tt-1]);

else fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[2]; if((teste==PDI || teste==PMI || teste==PII) && tt>j) tt--; dd=0; if(teste==PDI) fprintf(align_out,"D%d(-) ",bt); tcbk=align.bw[bt-i][tt-j].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[1]; else fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[2]; while(dd==0 && tcbk!=9 && tt>1 && bt>1); /* Atualiza as variáveis nn, ats, i, j, ki e kj. */ if(i==0 && j==0) ii++;

else if(bt>i && tt==j && j>0) ats=j; kj++; j=ic*(RC-kj); nn=bt; else if(tt>j && bt==i && i>0) nn=i; ki++; i=il*(RL-ki); ats=tt; else ii++; /* Demais possibilidades --> (bt>i && tt>j && (i>0 || j>0)) */ /* Encerra a fase de retrocedimento para a seqüência. */ if(i==0 && j==0) dd=0; while(dd==0 && tcbk!=9) teste=tcbk;dd=1; if((teste==PDD || teste==PMD || teste==PID) && bt>i) bt--; dd=0; if(teste==PDD) fprintf(align_out,"D%d(-) ",bt); tcbk=align.bw[bt-i][tt-j].tb[0]; else if(teste==PMD) fprintf(align_out,"M%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[1]; else fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[2]; if((teste==PDM || teste==PMM || teste==PIM) && bt>i && tt>j) bt--; tt--; dd=0; if(teste==PDM) fprintf(align_out,"D%d(-) ",bt); tcbk=align.bw[bt-i][tt-j].tb[0]; else if(teste==PMM) fprintf(align_out,"M%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[1]; else fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[2]; if((teste==PDI || teste==PMI || teste==PII) && tt>j) tt--; dd=0; if(teste==PDI) fprintf(align_out,"D%d(-) ",bt); tcbk=align.bw[bt-i][tt-j].tb[0]; else if(teste==PMI) fprintf(align_out,"M%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[1]; else fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); tcbk=align.bw[bt-i][tt-j].tb[2]; else if(i>0) if(tcbk=PDM) bt--; tt--; fprintf(align_out,"D%d(-) ",bt); else tt--; fprintf(align_out,"D%d(-) ",bt); while(bt>1) bt--; fprintf(align_out,"D%d(-) ",bt); else /* Caso em que j>0. */

else bt--; fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); while(tt>1) tt--; fprintf(align_out,"I%d(%c) ",bt,As[tt-1]); fprintf(align_out,"\n"); /* Efetua a tomada de tempo no final do alinhameto da seqüência. */

247

Page 250: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

hpctim(&wtimes, &ctimes, &wbegins, &cbegins); printf("WALL time gasto no alinhamento --> %f\n", wtimes); printf("CPU time gasto no alinhamento --> %f\n", ctimes); fprintf(align_out,"WALL time gasto no alinhamento da seqüência --> %f\n",wtimes); fprintf(align_out,"CPU time gasto no alinhamento da seqüência --> %f\n",ctimes); /* Fechado o arquivo de escrita do escore e do alimhamento. */ if(fclose(align_out)!=0) perror("Erro fechando arquivo de Alinhamento."); exit(0); return(4);

248

Page 251: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

GLOSSÁRIO

Aminoácidos: Qualquer uma de uma classe de 20 moléculas que são combinadas para

formar as proteínas. A seqüência de aminoácidos numa proteína é determinada pelo

código genético contido em um gene de uma mólecula de DNA, e desta forma a sua a

sua própria função.

DNA: A molécula que codifica a informação genética. O DNA é uma molécula formada

por dois filamentos que são mantidos juntos por pontes (ligações) fracas entre dos pares

de bases de nucleotídeos. Elas são constituídas por quatro bases de nucleotídeos:

adenina (A), guanina (G), citosina (C) e timina (T). Na natureza os pares de bases

encontrados são formados somente entre A e T, e entre G e C, assim a seqüência de

bases de um simples filamento pode ser deduzido de seu filamento complementar.

Exons: São as seqüências de ADN de um gene que codificam a proteína. Essas

seqüências são separadas pelos introns.

Introns: São as seqüências de ADN que se encontram no meio de um gene,

interrompendo as seqüências de codificação da proteína, que são denominadas exons.

Essas seqüências são transcritas no pré-mARN mas são removidas antes que o mARN

este seja traduzido em proteína.

Nucleotídeos: Uma subunidade do DNA ou do RNA composta de uma base de

nitrogenada, uma molécula fosfato e uma molécula de açucar. As bases nitrogenadas

são adenina, guanina, timina e citosina no DNA e adenina, guanina, uracila e citosina

no RNA. As moléculas de açucar são ribose (RNA) e desoxirribose (DNA). Milhares de

nucleotídeos são ligados para formar uma molécula de DNA ou de RNA.

Resíduo: Cada um dos fragmentos constituintes de um biopolímero, e que mantém, em

grande parte, a estrutura de um dos monômeros que deram origem ao biopolímero.

RNA: Um composto químico encontrado no núcleo e no citoplasma das células, cuja

estrutura química é similar à do DNA. Eles têm um papel importante na síntese das

proteínas e em outras atividades químicas da célula.

249

Page 252: ALGORITMOS DE PROGRAMAÇÃO DINÂMICA USADOS EM

RNAm: Uma classe de RNA que servem como moldes para a síntese das proteínas. Eles

são transcritos de um gene da seqüência de DNA e o código genético contido nele em

forma de códons pode ser usado para prognosticar a seqüência de aminoácidos na

síntese das proteínas.

RNAt: Uma classe de RNA cujas seqüências formam códons – uma tripla de

nucleotídeos – que são complementares aos códons das seqüências de RNAm. O papel

dos RNAt na síntese das proteínas é se ligarem aos aminoácidos e transferíi-los para os

ribossomos, onde as proteínas são fabricadas (montadas) de acordo com o código

genético contido no RNAm.

250