2.8 PARADIGMAS DECLARATIVOS
2.8.3 Sistemas Baseados em Regras (SBR)
2.8.3.2 Algoritmo RETE
O algoritmo RETE, que significa rede em latim (pronunciado ree-tee), tem sido usado em shells de SBR para melhorar o desempenho da fase de matching. Atualmente, o RETE é o
algoritmo de inferência de maior impacto industrial, sendo aplicado, pelo menos, nos seguintes shells: OPS5, ART, CLIPS, RuleWorks, ILOG Rules e JESS.
O algoritmo RETE foi desenvolvido em 1982 por Charles Forgy fundamentado em uma rede de nós interconectados. Cada rede é usada para representar uma regra, mas usualmente as redes representativas de várias regras se fundem pelo compartilhamento de alguns nós, aparentando se tratar de uma única rede. Em uma rede, cada nó representa um ou mais testes encontrados na parte condicional de uma dada regra (FRIEDMAN-HILL, 2003).
O algoritmo RETE entra em funcionamento sobre a ocorrência de três eventos na Base de Fatos: o evento de inserção, de modificação ou de remoção de um elemento da base de fatos. Na inserção, o elemento da base de fatos se torna algo chamado α-token que percorre os nós da rede para a ativação de uma regra. Na remoção, o elemento da base de fatos também se torna um α-token que percorre os nós da rede para a desativação de uma regra. Na modificação de um elemento da base de fatos, há a construção de dois α-tokens, um para remover as informações dos nós da rede e o outro para inserir as novas informações relativas ao elemento.
2.8.3.2.1 Estrutura
O RETE implementa um processo de busca otimizado, manipulando elementos da base de fatos e regras sobre a estrutura de redes ou grafos. Mais precisamente, o RETE guarda informação sobre avaliações anteriores das regras para evitar avaliações repetidas e ainda avalia as regras somente quando a Base de Fatos é atualizada, ou seja, quando um elemento da base de fatos é inserido/modificado/removido da Base de Fatos (FORGY, 1982). Com isto, o RETE resolve o problema das redundâncias temporais do PI. Esta solução se faz possível porque o RETE é composto de duas sub-redes chamadas rede alfa (α-network) e rede beta (β-network), as quais evitam muito processamento desnecessário.
A α-network é composta por certos nós sendo cada qual responsável pela avaliação lógica de fatos. A β-network, por sua vez, é composta por outros nós, sendo cada qual responsável pela correlação entre fatos. Se as avaliações e correlações forem satisfeitas para uma regra, a mesma é aprovada sendo, portanto, inserida no conjunto de conflito (DOORENBOS, 1995).
O algoritmo RETE pode ser mais bem compreendido por meio da análise da sua estrutura. A estrutura do RETE é ilustrada em forma de rede na Figura 28, que representa a
α-network (à esquerda) e a β-α-network (à direita). Na verdade, esta rede é composta por duas sub-redes, referente às regras Regra-1 e Regra-2, devido ao compartilhamento de um nó entre estas duas sub-redes. As mesmas regras são apresentadas em forma textual na mesma figura no formato das expressões causais se-então.
Figura 28: Estrutura do algoritmo RETE
Pela análise da rede, pode se constatar que a estrutura do RETE é composta por sete diferentes tipos de nós:
Nó Raiz: representa o ponto de entrada dos elementos da base de fatos na rede.
Na verdade, o elemento inserido na rede é transformado em outro elemento chamado α-token:
o O α-token tem a função de representar o elemento da base de fatos e o tipo de evento ao qual ele corresponde, ou seja, ele pode se referir a um evento de inserção ou remoção na Base de Fatos. O α-token encapsula o elemento da base de fatos inserido e por meio de uma tag sinaliza os eventos pertinentes (i.e. inserção ou remoção) (DOORENBOS, 1995).
o A sintaxe do α-token é a seguinte: (tag FATO-ID). A variável tag pode assumir dois valores, ―+‖ ou ―-‖, quais representam respectivamente a inserção ou remoção de elementos na Base de Fatos. A variável FATO-ID, chamada de Time Tag, é um identificador atribuído ao token quando o mesmo é criado ou modificado. Na modificação de um elemento da base de fatos, apesar de apenas o valor ser modificado, o respectivo α-token entra na rede assumindo um novo Tag Time.
Nó Tipo: analisa o tipo/classe das instâncias dos elementos da base de fatos.
Nó Filtro: representa um simples teste condicional (i.e. uma premissa). Ele filtra os α-tokens, armazenando-os em um nó de memória, o Nó Memória-α.
Memória-α (α-memory): armazena temporariamente os α-tokens que passaram pelo processo de filtragem.
Nós de Junção: correlaciona instâncias de duas α-memories distintas. O Nó de Junção é o nó mais complexo da rede e por isto precisa de uma explicação mais detalhada:
o À direita na Figura 28, o Nó de Junção (i.e. nó em forma de trapézio mais à esquerda) referente à correlação entre os ids de A e B recebe dados pelas suas duas entradas (entrada à direita e entrada à esquerda) e gera uma saída.
o A entrada à esquerda recebe um α-token de um nó α-memory superior (e.g. nó α2) enquanto a entrada à direita recebe uma referência para um conjunto de tokens, os β-tokens, localizados em outra memória, chamada β-memory (e.g. nó β1). Um β-token é o resultado da saída da computação de um Nó de Junção.
o Desta forma, se ambas as entradas forem satisfeitas, o Nó de Junção é ativado para execução. Em execução, o Nó de Junção realiza testes de consistência entre o α-token e os β-tokens e guarda temporariamente o resultado no β-token, o qual é armazenado no β-memory seguinte (nó β2).
o O β-token é mais complexo do que um α-token, pois aquele é composto por uma lista de α-tokens que satisfizeram os testes de consistência.
Memória-β (β-memory): armazena β-tokens recebidos da saída do Nó de Junção, os quais podem alimentar a entrada de outro Nó de Junção posicionado de acordo com o fluxo de seqüência na rede. Estes β-tokens representam os fatos avaliados que satisfizeram parcialmente a condição de uma regra.
Nó Regra: apresenta função similar ao β-memory. Porém, o Nó Regra armazena um β-token que satisfez completamente a condição de uma regra. Quando o token alcança este nó, a regra representada é ativada.
2.8.3.2.2 Funcionamento
O funcionamento do algoritmo RETE pode ser melhor compreendido por meio de um exemplo prático. Para isto será adotada a mesma representação da Figura 28. Para explicar a
propagação de tokens pela rede, dois elementos da base de fatos (A e B) são supostamente inseridos na Base de Fatos a fim de ativar a regra Regra-1 para o conjunto de conflito.
+ FATO-1: [A (x TRUE) (id 1)] e
+ FATO-2: [B (x TRUE) (y TRUE) (id 1)]
Primeiramente, o α-token (+FATO-1) é inserido na rede. A inserção ocorre através do Nó Raiz para que o α-token seja propagado pela α-network. Após a inserção, o α-token passa pelo teste de tipo de classe para determinar se ele realmente guarda um elemento da base de fatos do tipo A. Conseqüentemente, o (+FATO-1) é direcionado ao Nó Filtro localizado de acordo com o fluxo de seqüência. Este nó verifica o estado do atributo x de A. Se o estado for TRUE, então (+FATO-1) é armazenado em α-memory (α1), que é o nó subseqüente. Esta ação ativa a β-network.
A β-network (Figura 28 à direita) é ativada com a execução do primeiro Nó de Junção.
Apesar da ativação aparente de uma única entrada do Nó de Junção pelo nó α-memory (α1), a execução ocorre devido ao uso do nó auxiliar β-memory (β0). β0 é usado como entrada à esquerda do primeiro Nó de Junção, este armazena um β-token vazio que simplifica a implementação da rede13. Segundo a versão representada na Figura 28, o resultado da junção entre β0-memory e α1-memory reflete os mesmos valores do α1-memory, os quais são armazenados na seguinte β-memory (β1) (GIARRATANO e RILEY, 1993).
Ao observar a Figura 28, nota-se um aperfeiçoamento do algoritmo para garantir melhor desempenho. O nó β1-memory é compartilhado por dois Nós de Junção para poupar espaço em memória ao evitar a replicação de dados e principalmente para evitar avaliações redundantes da mesma premissa. Este compartilhamento resolve o problema das redundâncias estruturais do PI.
Mesmo compartilhando dados, nenhum Nó de Junção foi ainda ativado para execução.
Em ambos, o (+FATO-1) ativou somente a entrada à esquerda de cada Nó de Junção. Para ativar completamente o nó, é necessário que as memórias α2 ou α3 sejam atualizadas para ativar a entrada à direita. Esta atualização ocorrerá com a inserção do (+FATO-2) (GIARRATANO e RILEY, 1993).
O (+FATO-2) é inserido na α-network com a intenção de ativar a regra Regra-1.
Primeiramente, o (+FATO-2) passa pelo teste de tipo que verifica se este elemento é do tipo B. Com isto, (+FATO-2) é enviado para o ramo do Nó Filtro. Este ramo é complexo, pois
13 Este uso do nó auxiliar β-memory não é generalizado pois algumas implementações do algoritmo RETE, como o OPS5 (BROWNSTON, FARRELL, KANT e MARTIN, 1985), usam duas referências para α-memories como entradas para o primeiro Nó de Junção da β-network.
comporta três Nós Filtros. O primeiro Nó Filtro (B.x == TRUE) avalia o (+FATO-2) similarmente como ocorreu com o (+FATO-1). Porém, o token não é prontamente armazenado em um nó α-memory, devido à existência de outros dois Nós Filtros suplementares, sendo necessário passar por um deles14 (GIARRATANO e RILEY, 1993).
Após a avaliação do primeiro Nó Filtro, o (sub) Nó Filtro mais à esquerda é ativado (B.y == TRUE), já que o (+FATO-2) guarda uma instância do elemento da base de fatos B com o estado B.y como TRUE. Assim sendo, a avaliação neste Nó Filtro é satisfeita, resultando na ativação da memória α2.
Por fim, a ativação da memória α2 ativa a entrada à direita do Nó de Junção correspondente. Desta forma, o Nó de Junção correlaciona os β-token(s) na β1-memory (i.e.
(+FATO-1)) com os α-token(s) na α2-memory (i.e. (+FATO-2)), gerando um outro β-token como saída. Esta saída ativa o β2-memory seguinte, que finalmente ativa a Regra-1, representada pelo Nó Regra, para o conjunto de conflito. Na fase de execução, a Regra-1 usa o β-token que a ativou para referenciar os elementos da base de fatos em sua ação.
Além da inserção, o RETE oferece outras duas maneiras de atuar sobre a rede:
removendo e modificando um token. Para remover um token da rede (representado por um sinal de subtração (-)) é adotado um processo similar ao de inserção. Porém, neste caso, o token deve percorrer todos os α- e β-memories para remover o token alvo. Para modificar o estado de um elemento da base de fatos é necessário que o token percorra a rede por duas vezes consecutivas. Na primeira vez, o respectivo token alvo é removido e na seqüência, o mesmo é inserido novamente com um valor modificado. O token modificado entra na rede assumindo um novo Time Tag.