3 Lógicas Temporais para a Validação de Sistemas Neste capítulo apresentamos diversas lógicas que têm sido utilizadas PUC-Rio - Certificação Digital Nº 0310848/CB para a validação de sistemas. Do ponto de vista prático, a validação ocorre, tipicamente, através de duas abordagens: a verificação de modelos (model checking) (CGP99), na qual o sistema e a propriedade a ser validada são representados, respectivamente, como um modelo, geralmente finito, e uma fórmula da lógica; e a prova automática de teoremas (automated theorem proving) (Fit96), na qual tanto o sistema como a propriedade são representadas como fórmulas da lógica. Diversas linguagens lógicas têm sido utilizadas para ambas as abordagens a exemplo da lógica proposicional, da lógica de primeiraordem, e das lógicas modais e temporais, entre outras. Cada abordagem possui as suas vantagens e desvantagens. Vale ainda ressaltar que essas duas técnicas podem ser combinadas para a validação de sistemas. Abaixo iremos declarar o problema da verificação de modelos e da prova automática de teoremas para lógicas em geral. A partir daí, apresentamos as lógicas temporais: full Computation Tree Logic (CTL*) (EH86); Computation Tree Logic (CTL) (CE81); e Linear Temporal Logic (LTL). A lógica µ-calculus, que expressa CTL e LTL, não será apresentada neste capítulo. Em particular, estaremos mais interessados neste trabalho na lógica CTL, que é base da lógica proposta neste trabalho. Por fim, esboçaremos a verificação de modelos para a lógica CTL através do verificador de modelos Symbolic Model Verifier (SMV) (McM93). Em verificação de modelos se está interessado em procedimentos ‘eficientes’ que decidam se uma fórmula α, que representa uma propriedade do sistema, é satisfeita (|=) na estrutura G, geralmente finita, que representa o sistema. Uma formulação geral para o problema de verificação de modelos é introduzido como: G |= α A técnica de verificação de modelos (model checking) (CGP99) é freqüentemente aplicada na ciência da computação para a validação de software e hardware (BCL91). Esta técnica consiste na verificação automática de propriedades acerca do comportamento de sistemas através de enumeração exaustiva Capítulo 3. Lógicas Temporais para a Validação de Sistemas 58 de todos os estados alcançáveis. Diversas implementações estão disponíveis na literatura, tais como o Symbolic Model Verifier (SMV) (McM93) e o SPIN (Hol97). Algumas outras também incluem características especificas na sua modelagem: UPPAAL (BLL+ 95) trata de tempo-real; HYTECH (HHWT95) de autômatos híbridos; e o PRISM (Par02) de autômatos estocásticos. Recentemente, a verificação de modelos vem sendo empregada na verificação de propriedades de jogos (Vas03, BSWZ01, vOvdHW04, vdHW02, KP04). Embora a uma primeira vista esta abordagem pareça simples, a exemplo PUC-Rio - Certificação Digital Nº 0310848/CB da lógica proposicional, veremos que este problema para algumas lógicas pode ser uma tarefa difícil. Para algumas lógicas a complexidade para determinar se uma fórmula é satisfeita em uma estrutura é P SP ACE-completo. A outra abordagem que vem sendo aplicada em ciência da computação é a de provas automáticas de teoremas (automated theorem proving) (Fit96), onde utiliza-se de uma linguagem lógica para representar o sistema e a propriedade que se deseja verificar, então utiliza-se um método de prova (cálculo dedutivo) para verificar a correção da propriedade de forma automática. Diversos cálculos dedutivos têm sido desenvolvidos, tais como axiomático (ou à la Hilbert), dedução natural, cálculo de sequentes, tableaux, resolução, entre outros. Cada um dos sistemas têm as suas vantagens e desvantagens. Sejam Γ e α um conjunto finito de fórmulas e uma fórmula, respectivamente, de uma lógica com um sistema de prova K. Então uma formulação geral para o problema de prova de teoremas é introduzido como: Γ !K α Este problema está relacionado ao problema de determinar se um conjunto de fórmulas Γ ∪ {¬α} é insatisfatível, i.e., se não existe uma estrutura que satisfaz todas as fórmulas de Γ ∪ {¬α}. Para lógica proposicional, por exemplo, o problema da satisfatibilidade é NP-Completo e o da prova de teoremas é, então, CoNP-Completo. Além da dificuldade de resolver este problema para algumas lógicas, temos que este problema é indecidível, a exemplo da lógica de primeira-ordem. Apesar disso, existe muita pesquisa na área de prova automática de teoremas, e existem diversos provadores automáticos de teoremas disponíveis na literatura. Os provadores Otter (Kal01), SPASS(Wei97), Vampire (RV02) e Waldmeister (THL97) usam lógica de primeira-ordem, enquanto os provadores ACL2 (MKM00), e HOL (Age94) usam lógica de alta-ordem. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 59 3.1 Lógicas Temporais Lógicas temporais foram introduzidas por filósofos para o estudo da relação com o tempo, possuindo assim operadores temporais, a exemplo dos operadores de F e P , que expressam futuro e passado, respectivamente. Recentemente, diversas lógicas temporais têm sido utilizadas para a validação de propriedades acerca do comportamento de sistemas reativos (Pnu77, CGP99, Hol97). Sistemas reativos têm como caracterização básica estados e transições. Estado é a descrição do sistema em um dado instante de tempo, ou seja, os valores associados as suas variáveis naquele instante. Transição é uma relação entre dois estados. Chamamos de computação uma PUC-Rio - Certificação Digital Nº 0310848/CB seqüência infinita de estados, onde cada estado é obtido através de um estado anterior e uma relação de transição entre eles. A interpretação de lógicas temporais se dá, tradicionalmente, através de estrutura de Kripke, que captura a intuição do comportamento de um sistema reativo. Uma estrutura de Kripke é um conjunto de estados, um conjunto de transições entre estados e uma função, que rotula cada estado com o conjunto de propriedades verdadeiras nele. Caminhos em estrutura de Kripke são computações em sistemas reativos. Deve-se ainda considerar que existe uma restrição na abordagem lógica: as relações de transições devem ser totais. As estruturas de Kripke são simples e suficientes para capturar os aspectos mais relevantes do comportamento dos sistemas reativos. A definição formal de estrutura de Kripke para lógica temporal é apresentada abaixo. A figura 3.1 apresenta uma representação da estrutura de Kripke apresentada no exemplo 3.2, que tem um caráter apenas ilustrativo. Definição 3.1 Uma estrutura de Kripke é definida por #S, So , R, L$, onde – Um conjunto não-vazio de estados S. – Um conjunto de estados iniciais So ⊆ S. – Uma relação R ⊆ S × S total1 , i.e., para todo estado s ∈ S existe um estado s! ∈ S tal que #s, s! $ ∈ R. – Uma função de rótulos L : S → 2Π , onde Π é o conjunto de proposições. Exemplo 3.2 Exemplo de uma estrutura de Kripke K3.2 = #S, So , R, L$, onde – S = {s, sa , sab }. – So = {s}. 1 A restrição da relação R ser total é válida para lógicas temporais. Vale ressaltar que diversas outras lógicas modais não possuem esta restrição. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 60 – #s, sa $ ∈ R, #sa , s$ ∈ R, #s, sab $ ∈ R, #sa , sab $ ∈ R, #sab , sab $ ∈ R. – L(s) = ∅, L(sa ) = {a}, L(sab ) = {a, b}. Um caminho em uma estrutura de Kripke a partir de um estado s ∈ S é uma seqüencia π = s0 , s1 , . . . tal que s = s0 e ∀k ≥ 0, #sk , sk+1 $ ∈ R. Referenciamos um caminho começando em um estado s como um s-caminho. Denotamos πk , π0,k , πk,∞ como o estado sk do caminho π, o prefixo s0 , s1 , . . . , sk de π e o sufixo sk , sk+1 , . . . de π, respectivamente. Na estrutura K3.2 temos os seguintes caminhos a partir do estado s: s, sa , s, sa , . . .; s, sa , s, sa , . . . , sab , sab , . . .; s, sab , sab , . . .. PUC-Rio - Certificação Digital Nº 0310848/CB #$ # " s !" """" " " "" " " "" " " $$ " %" #$ #$ a ! a b sa sab !" !" Figura 3.1: Estrutura de Kripke. As propriedade dos sistemas reativos que desejamos verificar são usualmente divididas em dois tipos: segurança (safety) que expressa que ‘nada ruim acontecerá’; e progresso (liveness) que expressa que ‘algo bom acontecerá’. Abaixo apresentamos alguns exemplos destas propriedades. – Segurança (safety): ‘nada ruim acontecerá’. – O sistema nunca irá entrar em deadlock. – Nunca dois processos entram na região crítica ao mesmo tempo. – Progresso (liveness): ‘algo bom acontecerá’. – Cada processo irá entrar na região crítica. – Toda requisição será atendida. As propriedades a serem verificadas em um sistema reativo são expressas como fórmulas, que especificam os comportamentos desejados, de uma linguagem lógica temporal. As lógicas podem ser dividas em lógica temporal de tempo ramificado e em lógica temporal de tempo linear. Tais lógicas se referem a noção de seqüências de estados que descrevem possíveis computações do sistema, e não Capítulo 3. Lógicas Temporais para a Validação de Sistemas #$ 61 !" "& s " && #$ #$ "" & % " & ' a a b ab a !" !" *( * ( * + ) ( $ #$ #$ #$ s s a b a b !" !" s !" sab sab *( ( * ) ( * + $ $ PUC-Rio - Certificação Digital Nº 0310848/CB Figura 3.2: Árvore de Computação Infinita. a valores de tempos, ou a intervalos de tempos; uma vez que se deseja tratar de comportamentos de sistemas não-determinísticos que envolvem diferentes caminhos. Isto é, ou cada estado pode ter vários sucessores em termos de ramificação, ou o comportamento é dado por um conjunto de caminhos que são lineares. Em lógica de tempo ramificado a multiplicidade de comportamentos pode ser especificada explicitamente por uma propriedade de todos os próximos estados ou por uma propriedade do próximo estado, enquanto em lógica de tempo linear, esta multiplicidade é expressa de forma implícita2 . A seguir iremos apresentar as lógicas CTL* e CTL, que são lógicas de tempo ramificado, e a lógica LTL, que é um lógica de tempo linear. No fim desta seção iremos fazer uma breve discussão sobre essas lógicas. A lógica full Computation Tree Logic (CTL*) descreve propriedades de árvores de computações, que são obtidas a partir de um estado através do desdobramento de uma estrutura de Kripke em uma árvore de computação infinita, como é ilustrado na árvore de computação apresentada na figura 3.2 a partir do estado s da estrutura de Kripke apresentada na figura 3.1. A linguagem lógica de CTL* é definida em duas partes: uma linguagem que expressa propriedades de estados como apresentada na gramática Φs a seguir. Por exemplo, a fórmula Eα significa que existe um caminho tal que a fórmula α vale, enquanto que a fórmula Aα significa que em todos os caminhos a fórmula α vale; e uma linguagem temporal como apresentado na gramática Φp a seguir, expressando propriedades sobre um caminho. Os operadores X (‘Next time’), F (‘in the Future’), G (‘Globally’) e U (‘Until ’) são lidos da seguinte forma. – Xα significa ‘no próximo estado do caminho vale α’. 2 Através do conjunto dos caminhos lineares de uma estrutura. 62 Capítulo 3. Lógicas Temporais para a Validação de Sistemas – F α significa ‘em algum estado no caminho vale α’. – Gα significa ‘em todo estado no caminho vale α’. – αUβ significa ‘vale α no caminho até que vale β’. &! &! &! &! %! &! %! %! α α (a) - Xα. (c) - Gα. %! %! &! %! α α (b) - F α. α α α &! &! &! %! α β (d) - (αUβ). Figura 3.3: Operadores temporais X, F, G e U. PUC-Rio - Certificação Digital Nº 0310848/CB Abaixo apresentamos a linguagem e a semântica de CTL*. Definição 3.3 (Sintaxe de CTL*) Sejam Π um conjunto de letras proposicionais e P ∈ Π uma letra proposicional. A linguagem lógica de CTL* é gerada pela seguinte BNF: Φs ::= P | (¬Φs ) | (Φs ∧ Φs ) | (Φs ∨ Φs ) | (Φs → Φs ) | (EΦp ) | (AΦp ) Φp ::= Φs | (¬Φp ) | (Φp ∧Φp ) | (Φp ∨Φp ) | (Φp → Φp ) | (XΦp ) | (F Φp ) | (GΦp ) | (Φp U Φp ) Definição 3.4 (Semântica de CTL*) A definição de satisfação |= de CTL* é definida em duas partes. – Se α é uma fórmula da linguagem Φs , escrevemos K |=s α para dizer que a fórmula α é satisfeita na estrutura K no estado s. A definição de satisfação é apresentada abaixo. – – – – – – – K |=s K |=s K |=s K |=s K |=s K |=s K |=s P ⇐⇒ P ∈ L(s). (¬α) ⇐⇒ NÃO K |=s α. (α ∧ β) ⇐⇒ K |=s α E K |=s β. (α ∨ β) ⇐⇒ K |=s α OU K |=s β. (α → β) ⇐⇒ SE K |=s α ENTÃO K |=s β. (Eα) ⇐⇒ Existe um caminho π a partir de s tal que K |=π α. (Aα) ⇐⇒ Para todo caminho π a partir de s vale que K |=π α. – Se α é uma fórmula da linguagem Φp , escrevemos K |=π α para dizer que a fórmula α é satisfeita na estrutura K no caminho π. A definição de satisfação é apresentada abaixo. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 63 K |=π α ⇐⇒ se α é uma fórmula da linguagem Φs , K |=π0 α. K |=π (¬α) ⇐⇒ NÃO K |=π α. K |=π (α ∧ β) ⇐⇒ K |=π α E K |=π β. K |=π (α ∨ β) ⇐⇒ K |=π α OU K |=π β. K |=π (α → β) ⇐⇒ SE K |=π α ENTÃO K |=π β. K |=π (Xα) ⇐⇒ K |=π1,∞ α (veja a figura 3.3.a). K |=π (F α) ⇐⇒ Existe um k ≥ 0 tal que K |=πk,∞ α (veja a figura 3.3.b). – K |=π (Gα) ⇐⇒ Para todo k ≥ 0 vale que K |=πk,∞ α (veja a figura 3.3.c). – K |=π (αUβ) ⇐⇒ Existe k ≥ 0 tal que K |=πk,∞ β e para todo 0 ≤ l < k vale que K |=πl,∞ α (veja a figura 3.3.d). – – – – – – – Abaixo são apresentadas algumas fórmulas com suas relações de satisfa- PUC-Rio - Certificação Digital Nº 0310848/CB ção para o exemplo 3.2. – K3.2 |=s (E(G(¬b))), que pode ser lido como ‘existe uma caminho a partir de s tal que sempre não vale b’. – K3.2 |=s (A(F a)), que pode ser lido como ‘para todos os caminhos a partir de s no futuro vale a’. – K3.2 |=s,sab ,sab ,... (F (G(a ∧ b))), que pode ser lido como ‘no caminho s, sab , sab , . . . vale no futuro que sempre vale a e b’. – K3.2 1|=s,sa ,s,sa ,... (G a), que pode ser lido como ‘não vale no caminho s, sa , s, sa , . . . que sempre vale a’. – K3.2 1|=s (A(G(a ∨ b))), que pode ser lido como ‘não vale para todo caminho a partir de s que sempre vale a ou b’. Duas lógicas que estão contidas e são diferentes de CTL* são a lógica Linear-time Temporal Logic (LTL) (Pnu77) e a lógica Computation Tree Logic (CTL) (CE81), que são interpretados somente sobre caminhos e estados, respectivamente. A sintaxe e semântica de ambas são apresentadas abaixo, bem como algumas fórmulas para o exemplo 3.2. Iremos delimitar os operadores temporais de CTL através de ‘[’ e ‘]’ com o intuito de deixar claro que temos apenas um operador temporal. Por exemplo, [EX] é apenas um operador, não podendo ser desmembrado no operador E ou no operador X como no caso de CTL*. Os operadores de CTL podem ser lidos como segue. – [EX]α - ‘existe um caminho tal que no próximo estado vale α’. – [AX]α - ‘para todo caminho no próximo estado vale α’. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 64 – [EF ]α - ‘existe um caminho tal que no futuro vale α’. – [AF ]α - ‘para todo caminho no futuro vale α’. – [EG]α - ‘existe um caminho tal que sempre vale α’. – [AG]α - ‘para todo caminho vale sempre α’. – E(αUβ) - ‘existe um caminho tal que vale α até que vale β’. – A(αUβ) - ‘para todo caminho vale α até que vale β’. Definição 3.5 (Sintaxe de CTL) Sejam Π um conjunto de letras proposicionais e P ∈ Π uma letra proposicional. A linguagem lógica de CTL é gerada pela seguinte BNF: Φs ::= P | (¬Φs ) | (Φs ∧ Φs ) | (Φs ∨ Φs ) | (Φs → Φs ) | ([EX]Φs ) | ([AX]Φs ) PUC-Rio - Certificação Digital Nº 0310848/CB | ([EF ]Φs ) | ([AF ]Φs ) | ([EG]Φs ) | ([AG]Φs ) | (E(Φs U Φs )) | (A(Φs U Φs )) Definição 3.6 (Semântica de CTL) Sejam Π um conjunto de letras proposicionais, P uma letra proposicional em Π, K = #S, So , R, L$ uma estrutura de Kripke para Π, s ∈ S um estado da estrutura K, e α uma fórmula de CTL. Escrevemos K |=s α para indicar que a fórmula α é satisfeita no estado s da estrutura K. A definição de satisfação |= é como segue. – K |=s P ⇐⇒ P ∈ L(s). – K |=s (¬α) ⇐⇒ NÃO K |=s α. – K |=s (α ∧ β) ⇐⇒ K |=s α E K |=s β. – K |=s (α ∨ β) ⇐⇒ K |=s α OU K |=s β. – K |=s (α → β) ⇐⇒ SE K |=s α ENTÃO K |=s β. – K |=s ([EX]α) ⇐⇒ Existe um caminho π a partir de s tal que K |=π1 α (veja a figura 3.4.a). – K |=s ([AX]α) ⇐⇒ Para todo caminho π a partir de s vale que K |=π1 α (veja a figura 3.4.b). – K |=s ([EF ]α) ⇐⇒ Existe um caminho π a partir de s tal que existe um k ≥ 0 tal que K |=πk α (veja a figura 3.4.c). – K |=s ([AF ]α) ⇐⇒ Para todo caminho π a partir de s vale que existe um k ≥ 0 tal que K |=πk α (veja a figura 3.4.d). – K |=s ([EG]α) ⇐⇒ Existe um caminho π a partir de s tal que para todo k ≥ 0 vale que K |=πk α (veja a figura 3.4.e). Capítulo 3. Lógicas Temporais para a Validação de Sistemas 65 – K |=s ([AG]α) ⇐⇒ Para todo caminho π a partir de s vale que para todo k ≥ 0 vale que K |=πk α (veja a figura 3.4.f ). – K |=s (E(αUβ)) ⇐⇒ Existe um caminho π a partir de s tal que existe k ≥ 0 tal que K |=πk α e para todo 0 ≤ l < k vale que K |=πl α (veja a figura 3.4.g). – K |=s (A(αUβ)) ⇐⇒ Para todo caminho π a partir de s vale que existe k ≥ 0 tal que K |=πk α e para todo 0 ≤ l < k vale que K |=πl α (veja a figura 3.4.h). Assim como em diversas outras lógicas, alguns dos operadores de CTL são definidos através dos outros. Por exemplo, os operadores ¬, →, [EX], [EG] e EU definem os operadores ∧, ∨, [AX], [EF ], [AF ], [AG] e AU como abaixo. Vale ressaltar que existem outros conjuntos de operadores, que expressam os PUC-Rio - Certificação Digital Nº 0310848/CB demais operadores. • α ∧ β ⇐⇒ ¬(α → ¬β) • α ∨ β ⇐⇒ (¬α) → β • [AX]α ⇐⇒ ¬[EX](¬α) • [EF ]α ⇐⇒ E(2 U α) • [AF ]α ⇐⇒ ¬[EG](¬α) • [AG]α ⇐⇒ ¬[EF ](¬α) • A(αUβ) ⇐⇒ (¬E((¬β) U (¬α ∧ ¬β))) ∧ (¬[EG](¬β)) Quando utilizamos CTL, dizemos que o sistema é correto em relação a uma propriedade se a propriedade é satisfeita na estrutura, que representa o sistema, para todos os estados iniciais. Abaixo são apresentadas algumas fórmulas com suas relações de satisfação para o exemplo 3.2. – K3.2 |=s ([EG](¬b)), que pode ser lido como ‘existe uma caminho a partir de s tal que sempre não vale b’. – K3.2 |=s ([AF ] a), que pode ser lido como ‘para todo o caminho a partir de s no futuro vale a’. – K3.2 |=s ([EF ]([AG](a∧b))), que pode ser lido como ‘existe uma caminho a partir de s tal que no futuro sempre vale a e b’. – K3.2 1|=s ([EG] a), que pode ser lido como ‘não existe um caminho a partir de s tal que sempre vale a’. – K3.2 1|=s ([AG](a∨b)), que pode ser lido como ‘não vale para todo caminho a partir de s que sempre vale a ou b’. 66 Capítulo 3. Lógicas Temporais para a Validação de Sistemas )* '( *( * ( * ( )* * + ) α ( + '( ,. ,. , . , . , . , . )* )* ,./)* ,./)* '( . .. '( . '( . .. .. )* (a) - [EX]α '( . .. PUC-Rio - Certificação Digital Nº 0310848/CB '( *( * ( * ( )* )* * + ) ( '( '( ,. ,. , . , . . , . α ,-, )* ./)* ,./)* + '( '( . . '( . . .. .. .. .. (c) - [EF ]α α .. '( *( * ( * ( α* + ) α ( + + ,. ,. , . , . , . , . )* )* ,./)* ,./)* '( . .. '( . '( . .. .. )* .. .. , , α ,+ .. '( . '( . .. .. '( . .. .. + *( ( * ( * α* ) α ( + + + ,. . .. . . ./ α + .. . α ,-, + , ,. . .. . . ./ α + .. . (f) - [AG]α α + α + '( . .. (d) - [AF ]α (e) - [EG]α *( * ( * ( β* )* + ) ( + '( ,. ,. , . , . , . , . )* )* ,./)* ,./)* .. (b) - [AX]α α .. '( . .. '( *( * ( * ( )* * + ) α ( + '( ,. ,. , . , . . , . α ,-, ./ α )* ,./)* + + '( . . '( . . + *( ( * ( * α* )* ) ( + + '( ,. ,. , . , . , . . α ,-, )* ,./)* ./)* + '( '( . . '( . . .. )* , *( * ( * ( β* + ) α ( + + ,. . , , . )* ,./)*β ,+ '( '( . . . .. .. , ,. .. (h) - A(αUβ) (g) - E(αUβ) Figura 3.4: Operadores de CTL. . . ./ β + .. . Capítulo 3. Lógicas Temporais para a Validação de Sistemas 67 Definição 3.7 (Sintaxe de LTL) Sejam Π um conjunto de letras proposicionais e P ∈ Π uma letra proposicional. A linguagem lógica de LTL é gerada pela seguinte BNF: Φp ::= P | (¬Φp ) | (Φp ∧Φp ) | (Φp ∨Φp ) | (Φp → Φp ) | (XΦp ) | (F Φp ) | (GΦp ) | (Φp U Φp ) Definição 3.8 (Semântica de LTL) Sejam Π um conjunto de letras proposicionais, P uma letra proposicional em Π, K = #S, So , R, L$ uma estrutura de Kripke para Π, π um caminho da estrutura K, e α uma fórmula de LTL. Escrevemos K |=π α para dizer que a fórmula α é satisfeita na estrutura K no caminho π. A definição de satisfação é apresentada abaixo. – K |=π P ⇐⇒ P ∈ L(π0 ). PUC-Rio - Certificação Digital Nº 0310848/CB – K |=π (¬α) ⇐⇒ NÃO K |=π α. – K |=π (α ∧ β) ⇐⇒ K |=π α E K |=π β. – K |=π (α ∨ β) ⇐⇒ K |=π α OU K |=π β. – K |=π (α → β) ⇐⇒ SE K |=π α ENTÃO K |=π β. – K |=π (Xα) ⇐⇒ K |=π1,∞ α (veja a figura 3.3.a). – K |=π (F α) ⇐⇒ Existe um k ≥ 0 tal que K |=πk,∞ α (veja a figura 3.3.b). – K |=π (Gα) ⇐⇒ Para todo k ≥ 0 vale que K |=πk,∞ α (veja a figura 3.3.c). – K |=π (αUβ) ⇐⇒ Existe k ≥ 0 tal que K |=πk,∞ α e para todo 0 ≤ l < k vale que K |=πl,∞ α (veja a figura 3.3.d). Abaixo são apresentadas algumas fórmulas com suas relações de satisfação para o exemplo 3.2. – K3.2 |=s,sab ,sab ,... ((¬(a ∨ b))U(a ∧ b)), que pode ser lido como ‘no caminho s, sab , sab , . . . vale não a ou b até que vale a e b’. – K3.2 1|=s,sa ,s,sa ,... (G a), que pode ser lido como ‘não vale no caminho s, sa , s, sa , . . . que sempre vale a’. – K3.2 |=s,sab ,sab ,... (F (G a)), que pode ser lido como ‘vale no caminho s, sab , sab , . . . que no futuro sempre vale a’. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 68 Quando validamos sistemas reativos utilizando a lógica LTL, subentendemos uma quantificação universal sobre todos os caminhos de uma estrutura de Kripke. Assim, um sistema é considerado correto em relação a um propriedade se ela é satisfeita para todos os caminhos da estrutura a partir de todos os estados iniciais. Na verdade, a lógica CTL* foi proposta em (EH86), como uma extensão das duas lógicas CTL e LTL, com o intuito de estudar a relação entre estas duas lógicas em resposta ao artigo (Lam80), que afirmava que: uma lógica de tempo PUC-Rio - Certificação Digital Nº 0310848/CB linear, a exemplo de LTL, expressa melhor algumas propriedades acerca de sistemas concorrentes do que lógicas de tempo ramificado, a exemplo de CTL. Em geral, na prova da correção de um sistema utilizamos o seguinte princípio: ‘ao longo de qualquer caminho p será verdadeiro no futuro, ou será sempre falso’. Este princípio é facilmente expresso em LTL como a seguinte fórmula (F p) ∨ (G(¬p)), que é sempre válida. Por outro lado, não há uma fórmula equivalente em CTL, por exemplo, a fórmula ([AF ]p) ∨ ([AG](¬p)) não é válida. A partir deste e outros argumentos conclui-se que, lógica com tempo linear é melhor para expressar propriedades de sistemas concorrentes do que lógicas com tempo ramificado. Em (EH86), esta questão é reexaminada, e argumenta-se que este princípio pode ser facilmente expresso em CTL*, que é uma lógica de tempo ramificado, como (A((F p) ∨ (G(¬p)))). Há ainda um enorme debate sobre qual lógica, CTL ou LTL, é melhor para expressar propriedades sobre sistemas concorrentes. Contudo, as propriedades usualmente utilizadas na verificação de tais sistemas podem ser expressas em ambas as lógicas. Diversos cálculos dedutivos corretos e completos existem para as lógicas CTL*, CTL e LTL, tais como: uma axiomatização para CTL* em (Rey01); um sistema de dedução natural para CTL* em (Ren04); uma axiomatização para CTL em (Gol92); um sistema de dedução natural para CTL em (RH02); um sistema de resolução para CTL em (BF97, Bol00); um sistema de resolução para LTL em (Fis91); entre outros sistemas. A tabela apresentada na figura 3.5 resume a complexidade para o problema da verificação de modelos e da prova automática de teoremas das lógicas CTL*, LTL e CTL. Uma observação sobre a complexidade da verificação de modelos de LTL se faz necessária, é demonstrado em (LP85) que a verificação de modelos de LTL é PSPACE-Completo, porém ela é exponencial no tamanho da fórmula e linear no tamanho da estrutura. A partir daí, muitos argumentam (Hol97) que, geralmente, o tamanho da fórmula a ser verificada é pequeno em comparação ao tamanho da estrutura. Assim, na prática a verificação de modelos de LTL é realizada de forma eficiente. Capítulo 3. Lógicas Temporais para a Validação de Sistemas Lógica CTL* LTL CTL Verificação de Modelos PSPACE-Completo (LP85) PSPACE-Completo (LP85) Linear (CES86) 69 Satisfatibilidade 2EXP-Completo (EH86) PSPACE-Completo (SC85) EXP-Completo (CE81) Figura 3.5: Complexidade de CTL*, LTL e CTL. 3.2 Verificação de Modelos em CTL Sejam K = #S, So , R, L$ uma estrutura de Kripke, que representa um sistema reativo com estados finitos (S finito), e α uma fórmula de CTL, que expressa uma propriedade do sistema. O problema da verificação de modelos é encontrar o conjunto dos estados que satisfazem a fórmula α: {s ∈ S | K |=s α} PUC-Rio - Certificação Digital Nº 0310848/CB Dizemos que o sistema satisfaz a propriedade se todos estados iniciais s ∈ So estão neste conjunto. Um verificador de modelos é uma ferramenta que automatiza a formulação acima. O processo de validação segue os três passos abaixo: 1. Especificar em CTL quais são as propriedades que o sistema deverá ter para que seja considerado correto. Por exemplo, podemos querer que o sistema nunca entre em deadlock, ou ainda, que ele sempre alcance um determinado estado. 2. O segundo passo é a construção do modelo formal do sistema, que é definido, geralmente, em uma linguagem de alto nível (a linguagem do verificador). O modelo deve capturar todas as propriedades essenciais do sistema para verificar a correção do mesmo, contudo, também deverá possuir abstrações de detalhes do sistema que não afetem a correção das propriedades a serem verificadas. Por exemplo, em protocolos de comunicações se está interessado em testar propriedade de quando uma mensagem é recebida e não do conteúdo dela. 3. O terceiro e último passo é a própria execução do verificador de modelos para validar as propriedades especificadas do sistema. Neste passo, já temos as propriedades e o modelo. Assim, aplicamos o verificador e conseguimos garantir se o modelo do sistema possui ou não as propriedades desejadas. Caso todas as propriedades sejam verdadeiras, então o sistema está correto. Caso não obedeça a alguma propriedade, então é gerado um contra exemplo mostrando o porquê da não verificação da propriedade. Desta forma, podemos detectar o erro e realizar a correção Capítulo 3. Lógicas Temporais para a Validação de Sistemas 70 do modelo. Esse processo deve ser feito até que o sistema obedeça todas as propriedades, realizando assim um ajuste na especificação. A seguir iremos apresentar um algoritmo para a verificação de modelos de CTL na seção 3.2.1, bem como o verificador de modelos Symbolic Model Verifier (SMV) (McM93) na seção 3.2.2. 3.2.1 Algoritmo com Representação Explícita O algoritmo descrito nesta seção utiliza uma representação explícita de PUC-Rio - Certificação Digital Nº 0310848/CB uma estrutura de Kripke K = #S, R, L$, através de um grafo, direcionado e rotulado da seguinte forma. Os nós do grafo representam os estados S, os arcos definem a relação de transição R, e os rótulos associados com cada nó descrevem a função L : S → 2Π , onde Π é um conjunto de proposições. No problema da verificação de modelos de CTL estamos interessados em saber o conjunto de estados de S que satisfazem a fórmula α. O algoritmo proposto (veja o algoritmo 1) opera rotulando cada estado s com o conjunto label(s) de sub-fórmulas de α que são verdadeiros em s. Inicialmente, label(s) é somente L(s), e então o algoritmo é executado uma série de passos (o número de operadores em α). A cada passo sub-fórmulas com k − 1 operadores são processadas. Quando uma fórmula é processada, a mesma é adicionada ao label(s) do estado s se ela é verdadeira. Assim, K |=s α ⇐⇒ α ∈ label(s). Lembremos que as fórmulas de CTL podem ser expressas em termos de ¬, ∨, [EX], [EG] e EU. Desta forma, é necessário tratarmos apenas estes casos como segue. – Para o caso (¬α1 ) (veja o algoritmo 2), rotulamos os estados que não são rotulados por α1 . A complexidade é O(|S|). – Para o caso (α1 → α2 ) (veja o algoritmo 3), rotulamos os estados que não são rotulados por α1 ou são rotulados por α2 . A complexidade é O(|S|). – Para o caso [EX]α1 (veja o algoritmo 4), rotulamos os estados que tem algum sucessor rotulado por α1 . A complexidade é O(|S| + |R|). – Para o caso E(α1 Uα2 ) (veja o algoritmo 5), adicionamos E(α1 Uα2 ) a to- dos os estados rotulados com α2 , e então adicionamos E(α1 Uα2 ) retroativamente, utilizando a inversa da relação R, a todos os estados rotulados com α1 que podem ser alcançados por um caminho. A complexidade é O(|S| + |R|). – Para o caso [EG]α1 (veja o algoritmo 6) temos os seguintes passos. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 71 1. Construímos K! = #S ! , R! , L! $ a partir de K = #S, R, L$, removendo todos os estados de S que não vale α1 e restringindo R e L. 2. Utilizamos o algoritmo do Tarjan (Tar72) para particionar o grafo #S ! , R! $ em SCCs (Strongly Connected Components), onde um SCC é um subgrafo maximal C tal que todo nó em C é alcançável a partir de qualquer outro nó em C através de um caminho em C. Este algoritmo tem complexidade O(|S ! | + |R! |). 3. Adicionamos [EG]α1 aos estados que pertencem aos SCCs nãotriviais, onde um SCC é não-trivial se, e somente se, ele tem mais de um nó ou ele contém um nó relacionado com ele mesmo. 4. Por fim, adicionamos [EG]α1 retroativamente, utilizando a inversa PUC-Rio - Certificação Digital Nº 0310848/CB da relação R, a todos os estados rotulados com α1 que podem ser alcançados por um caminho. A complexidade de toda esta computação é O(|S| + |R|). Fundamental para a correção de tal algoritmo é o lema abaixo. Lema 3.9 K |=s [EG]α1 ⇐⇒ s ∈ S ! e existe um caminho em K! que leva o estado s a algum estado t em um SCC não-trivial de #S ! , R! $. A partir dos algoritmos acima temos o seguinte teorema. Teorema 3.10 Existe um algoritmo para determinar se uma fórmula α de CTL é satisfeita em um estado s de K = #S, R, L$, cuja complexidade algorítmica é O(|α| × (|S| + |R|)). Capítulo 3. Lógicas Temporais para a Validação de Sistemas Algoritimo 1 procedure Check(α) while |α| ≥ 1 do if α = (¬α1 ) then Check(α1 ) CheckN OT (α1 ) else if α = (α1 → α2 ) then Check(α1 ) Check(α2 ) PUC-Rio - Certificação Digital Nº 0310848/CB CheckIM P (α1 , α2 ) else if α = [EX]α1 then Check(α1 ) CheckEX(α1 ) else if α = [EG]α1 then Check(α1 ) CheckEG(α1 ) else if α = E(α1 Uα2 ) then Check(α1 ) Check(α2 ) CheckEU (α1 , α2 ) end if end while Algoritimo 2 procedure CheckNOT(α) for all s ∈ S do if α 1∈ label(s) then label(s) := label(s) ∪ {(¬α)} end if end for Algoritimo 3 procedure CheckIMP((α1 → α2 ) for all s ∈ S do if α1 1∈ label(s) or α2 ∈ label(s) then label(s) := label(s) ∪ {(α1 → α2 )} end if end for 72 Capítulo 3. Lógicas Temporais para a Validação de Sistemas Algoritimo 4 procedure CheckEX(α1 ) T := {s | α1 ∈ label(s)} while T 1= ∅ do choose s ∈ T T := T \{s} for all t such that #t, s$ ∈ R do if [EX]α1 1∈ label(t) then label(t) := label(t) ∪ {[EX]α1 } PUC-Rio - Certificação Digital Nº 0310848/CB end if end for end while Algoritimo 5 procedure CheckEU(α1 ,α2 ) T := {s | α2 ∈ label(s)} for all s ∈ T do label(s) := label(s) ∪ {E(α1 Uα2 )} end for while T 1= ∅ do choose s ∈ T T := T \{s} for all t such that #t, s$ ∈ R do if E(α1 Uα2 ) 1∈ label(t) and α1 ∈ label(t) then label(t) := label(t) ∪ {E(α1 Uα2 )} T := T ∪ {t} end if end for end while 73 Capítulo 3. Lógicas Temporais para a Validação de Sistemas 74 Algoritimo 6 procedure CheckEG(α1 ) S ! := {s | α1 ∈ label(s)} SCC := {C | C is a nontrivial SCC of S ! } T := ∪C∈SCC {s | s ∈ C} for all s ∈ T do label(s) := label(s) ∪ {[EG]α1 } end for while T 1= ∅ do PUC-Rio - Certificação Digital Nº 0310848/CB choose s ∈ T T := T \{s} for all t such that t ∈ S ! and #t, s$ ∈ R do if [EG]α1 1∈ label(t) then label(t) := label(t) ∪ {[EG]α1 } T := T ∪ {t} end if end for end while 3.2.2 Symbolic Model Verifier (SMV) Boa parte da pesquisa em verificação de modelos tem se concentrado no desenvolvimento de algoritmos que usem uma representação dos sistemas de forma mais eficiente. Uma das técnicas que obteve relativo sucesso foi a representação através de Ordered Binary Decision Diagram (OBDD) (Bry92). OBDDs são representações canônicas para funções booleanas, que representam os sistemas reativos. OBDDs são freqüentemente muito mais compactas do que representações em forma normal conjuntiva ou disjuntiva, e elas podem ser eficientemente manipuladas. Iremos apenas descrever o funcionamento geral de OBDDs, uma vez que não utilizaremos tal técnica nesta tese por motivos que ficarão claros em seções posteriores. No final desta seção, iremos apresentar a linguagem do verificador de modelos Symbolic Model Verifier (SMV) (McM93) que foi o primeiro a utilizar tal técnica para lógicas temporais. Para a verificação de modelos utilizando OBDD, temos os seguintes passos. 1. Representar uma estrutura de Kripke (um sistema) através de uma fórmula booleana. Isto é feito, utilizando o conjunto Π de proposições, tipicamente booleanas, para representar a estrutura de Kripke, onde Capítulo 3. Lógicas Temporais para a Validação de Sistemas 75 as possíveis combinações dos valores associados a cada variável v em V representam todos os possíveis estados da estrutura. A partir daí, podemos expressar um estado através de uma fórmula booleana. No exemplo apresentado na figura 3.2, temos duas variáveis a e b, e seus valores podem ser verdadeiro ou falso, assim temos quatro possíveis estados. Desta forma, podemos representar todos os possíveis estados, mas, por outro lado, não temos como representar as relações entre os estados utilizando tais variáveis. Para representarmos tal conceito, utilizamos um cópia V ! das variáveis de V , onde cada variável em v ∈ V tem uma v ! ∈ V ! correspondente no próximo estado. Por exemplo, a estrutura da figura 3.2 é representada pela seguinte fórmula. ! (¬A ∧ ¬B ∧ A! ∧ ¬B ! ) ∨ (A ∧ ¬B ∧ ¬A! ∧ ¬B ! ) ∨ (¬A ∧ ¬B ∧ A! ∧ B ! ) ∨ (A ∧ ¬B ∧ A! ∧ B ! ) ∨ (A ∧ B ∧ A! ∧ B ! ) PUC-Rio - Certificação Digital Nº 0310848/CB 2. Construir a OBDD a partir de tal fórmula booleana. Uma OBDD é uma representação concisa de uma árvore de decisão binária que utiliza uma ordenação nas variáveis V . A figura 3.6 abaixo representa uma árvore de decisão binária para a fórmula (a1 ↔ b1 ) ∧ (a2 ↔ b2 ). A partir daí, uma OBDD é construída unificando as sub-árvores que resultam no mesmo valor de verdade. Este processo é repetido até não haver nenhuma subárvore equivalente. Por exemplo, na figura 3.7.a temos a OBDD obtida a partir da árvore 3.6 com ordenação a1 5 b1 5 a2 5 b2 . Note que esta é uma representação muito mais concisa do que a OBDD obtida com ordenação a1 5 a2 5 b1 5 b2 apresentada na figura 3.7.b. De fato, o tamanho da OBDD gerada depende fortemente da ordenação das variáveis. Em (Bry92), é demonstrado que mesmo verificar que uma ordenação é ótima é NP-Completo, e para alguns casos o tamanho da OBDD é exponencial para qualquer que seja a ordenação. Diversas heurísticas são utilizadas para tentar achar uma boa ordenação. 3. Utilizar algoritmos simbólicos para a validação de propriedades. Estes algoritmos manipulam as OBDDs e são baseados em computação de ponto-fixo, nos quais os operadores temporais de CTL são definidos em função do menor ou maior ponto fixo. Veja (CGP99) para maiores detalhes. " 76 Capítulo 3. Lógicas Temporais para a Validação de Sistemas )* '( 66777 7 66 a1 66 66 )* 6 06 66 77 1 77 77 7 )* 7 '( 45 5 1 4 04 5 5 4 55 4 4 )* )* '( 45 5 1 4 04 5 5 4 55 4 4 )* )* b1 b1 '( 23 02 3 1 2 3 2 3 )* )* a2 '( 23 02 3 1 2 3 2 3 )* )* a2 '( 23 02 3 1 2 3 2 3 )* )* a2 '( 23 02 3 1 2 3 2 3 )* )* a2 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 '( 0 01 1 0 1 00 11 1 0 0 0 0 0 1 0 b2 0 b2 1 b2 0 b2 0 b2 0 b2 0 b2 0 b2 PUC-Rio - Certificação Digital Nº 0310848/CB Figura 3.6: Árvore de decisão binária da fórmula (a1 ↔ b1 ) ∧ (a2 ↔ b2 ). )* )* a1 a1 '( 23 02 31 2 3 2 3 )* )* '( '( <; ; < 0 ;1 < 1 ;0 ; ; < )* < < ; ; a2 ; ; '( ; 9; ; ; 9 0 ; ;91 9 ; ; )* 9 9)* ; ; b2 b2 ; '(; '( ; ; 4 ( 0 1 ; 41 0 ; ( 4 ; ; 4 ( 4 ; ;; * ) ,4 .( b1 b1 - /' 1 0 ( (a) - Ordem a1 5 b1 5 a2 5 b2 . '( 45 5 1 04 4 5 4 5 55 44 )* )* '( 23 02 3 1 2 3 2 3 )* )* a2 '( 23 02 3 1 2 3 2 3 )* )* a2 1: '( : '( 8808 0'( 0 5 1:: '( 43 1 9 , 0 84 8 : 5 9 8 : 1 94 8 : 5 , 388 : : : 888 5, 4 9 ::3 888 5 : : 44 )* )* 9 : 8 ,85 : 3 : 9 b2 b2 , 3 9 0'( '( , 6 9 3 770 6 ( 77 9 3 ,6 * 1 77 1( 9 3 666 , * 77 69 6 ( * 7 67 9 , 3 6 3 6. )7 9, * * ( , b1 b1 - 1 /' b1 0 b1 ( (b) - Ordem a1 5 a2 5 b1 5 b2 . Figura 3.7: OBDD da fórmula (a1 ↔ b1 ) ∧ (a2 ↔ b2 ). Abaixo descrevemos as principais características da linguagem de especificação de modelos do SMV. – Módulos – O usuário pode decompor um sistema de estados finitos em módulos, que encapsulam uma coleção de declarações: ‘VAR’ define as variáveis do módulo, que podem ser booleanos, conjunto enumerado de constantes ou instâncias de outros módulos; ‘INIT’ inicializa as variáveis; ‘ASSIGN’ define as relações de transições; ‘FAIRNESS’ definem as 1 Capítulo 3. Lógicas Temporais para a Validação de Sistemas 77 fairness constraints, que são fórmulas em CTL3 ; ‘SPEC’ especificações em CTL. Os módulos podem ser instanciados várias vezes, podem referenciar variáveis de outros módulos e possuem parâmetros que podem ser expressões, estados de outros módulos ou até mesmo instâncias de módulos. – Sincronismo e Assincronismo (Interleaved ) – módulos podem ser compostos de forma síncrona ou assíncrona. Em composição síncrona, um passo corresponde a um passo de cada módulo. Em composição assíncrona, cada passo corresponde a um passo de um único módulo. Se a palavra reservada ‘process’ preceder uma instância de um módulo, assincronismo é usado. Caso contrário, a composição síncrona é assumida. Cada processo tem uma variável running que indica se o processo está ativo ou não4 . PUC-Rio - Certificação Digital Nº 0310848/CB – Transições Não-Determinísticas – as transições de um estado em um modelo podem ser determinísticas ou não-determinísticas. Transição nãodeterminística é usada para descrever modelos mais abstratos onde certos detalhes são omitidos. – Relações entre transições – as relações de transições de um módulo podem ser especificadas explicitamente em termos de relações binárias entre o atual e o próximo estado das variáveis, ou implicitamente como um conjunto de comandos de atribuições paralelas. Os comandos de atribuições paralelas definem o valor das variáveis no próximo estado em termos dos valores no estado atual e são definidos através da declaração ‘NEXT’ para cada atribuição. Abaixo apresentamos um exemplo de uma especificação no SMV. Exemplo 3.11 Apresentamos na figura 3.8 como, seria descrito na linguagem do SMV, um programa que utiliza uma variável semáforo ( semaforo) para implementar exclusão mútua entre dois processo assíncronos. Será definido um módulo usuário que terá uma variável estado que possui quatro estados: ocioso, o processo não quer entrar na região crítica; entrando, o processo quer entrar na região crítica; critica, o processo está utilizando a região crítica; e saindo, o processo não irá mais usar a região crítica. O módulo ‘ main’ terá uma variável semáforo, que será inicializada com 0, e os dois usuários. Como os processos 3 Fairness constraints são condições que são inseridas para garantir justiça aos caminhos em CTL. Um exemplo simples é o de duas avenidas que se entroncam. Suponha que somente passem carros de uma das avenidas. Isto claramente não seria justo. Assim, deve-se garantir que os carros das duas avenidas possam passar no entroncamento. 4 Se a condição de running for definido nas condições de fairness de um módulo significará que o módulo não poderá ficar indefinidamente sem ser executado. Capítulo 3. Lógicas Temporais para a Validação de Sistemas 78 são assíncronos os usuários serão definidos com a palavra ‘ process’. A fórmula AG!(proc1.estado = critica & proc2.estado = critica) expressa que nenhum dos dois processos estão utilizando a região crítica ao mesmo instante. A fórmula AG(proc1.estado = entrando − > AF (proc1.estado = critica)) significa que PUC-Rio - Certificação Digital Nº 0310848/CB se um processo deseja entrar na região crítica em algum instante no futuro ele utilizará a região crítica. MODULE main VAR semaforo : boolean; proc1 : process usuario; proc2 : process usuario; ASSIGN init(semaforo) :=0; SPEC AG !(proc1.estado=critica & proc2.estado=critica) SPEC AG(proc1.estado = entrando -> AF(proc1.estado = critica)) MODULE usuario VAR estado : {ocioso, entrando, critica, saindo}; ASSIGN init(estado) := ocioso; next(estado) := case estado = ocioso : {ocioso,entrando}; estado = entrando & !semaforo :critica; estado = critica : {critica, saindo}; estado = saindo : ocioso; 1 : estado; esac; next(semaforo) := case estado = entrando : 1; estado = saindo :0; 1 : semaforo; esac; FAIRNESS running Figura 3.8: Exclusão mútua na linguagem do SMV.