CLP – (Constraint Logic Programs) Programação Lógica com Restrições Ruben Carlo Benante Doutorando em Ciência da Computação pelo CIn – UFPE, Recife-PE Mestre em Filosofia pela UNESP, Marília-SP Bacharel em Ciência da Computação pela UNESP, S.J.Rio Preto-SP [email protected] Disciplina de MCI Prof. Jacques Robin Baseado nas transparências disponibilizadas na internet por Marriott & Stuckey Capítulo 4: Programação Lógica com Restrições Restrições Definidas pelo Usuário Programando com Regras Avaliação Árvores de Derivação e Falhas Finitas Avaliação de Objetivos Árvores de Derivação Simplificadas O esquema CLP Capítulo 5: Modelos Simples Modelando Modelando Escolhas Otimização Capítulo 7: Controle de Busca Estimando a eficiência de um programa CLP Ordenando as Regras Ordenando os Literais Adicionando Restrições Redundantes Minimização Capítulo 8: Modelos com Domínios Finitos Domínios e Rótulos Restrições Complexas Rotulando (Labelling) Modelando Problemas Diferentes Capítulo 4: Restrições Definidas pelo Usuário (RDU) Muitos exemplos de modelagem podem ser particionados em duas partes: Uma descrição geral do objeto ou processo Uma informação específica sobre uma situação real O programador deve ser capaz de definir suas próprias restrições específicas ao problema As Regras permitem isso Regras + V R1 Uma RDU para definir o modelo de circuito elétrico: R2 V I1 I parallel_resistors(V,I,R1,R2) -3_ -- I2 - E uma regra definindo-a: parallel_resistors(V,I,R1,R2) :V = I1 * R1, V = I2 * R2, I1 + I2 = I. Usando Regras parallel_resistors(V,I,R1,R2) :V = I1 * R1, V = I2 * R2, I1 + I2 = I. Comportamento com resistores de 10 e 5 Ohms parallel_resistors(V , I , R1, R2) R1 10 R2 5 Comportamento com bateria de 10V, com resistores iguais parallel_resistors(10, I , R, R) Isso representa a restrição: (substituição macro) 10 I 1 R 10 I 2 R I 1 I 2 I Restrições Definidas pelo Usuário (RDU) RDU: p(t1,...,tn) onde p é um predicado n-ário e t1,...,tn são expressões literal: uma restrição primária ou RDU objetivo (goal): uma seqüência de literais L1,...,Lm regra: A :- B onde A é RDU e B é um objetivo programa: uma seqüência de regras Renomeação (Renamings) Uma renomeação r é um mapa bijetivo de variáveis em variáveis Um objeto sintático é uma restrição, um RDU, um objetivo ou uma regra Aplicar uma renomeação em um objeto sintático nos dá um objeto com cada variável x substituída por r(x) variante o’ de um objeto o tem sua renomeação como r(o’)=o Não é substituição de macros Reescrita de RDU objetivo G da forma (ou vazio m=0 []) L1, ..., Li-1, Li, Li+1, ..., Lm Li é da forma p(t1,...,tn) R é da forma p(s1,...,sn) :- B r é a renomeação das variáveis em r(R) e não em G A reescrita de G com Li por R usando a renomeação r é L1,...,Li1,t1=r(s1),...,tn=r(sn),r(B),Li+1,...,Lm Programando com Regras 1 if N 0 N! N ( N 1)! if N 1 Considere a função fatorial. Como poderemos escrever regras para o predicado fac(N,F) onde (F = N!) ? (R1) fac(0,1). (R2) fac(N,N*F) :- N >= 1, fac(N-1, F). Note que a definição é recursiva (em termos dela mesma) e imita a definição matemática Programando com Regras (R1) fac(0,1). (R2) fac(N,N*F) :- N >= 1, fac(N-1, F). Reescrevendo o objetivo fac(2,X) (i.e. o que é 2!) fac 22,,,,XX fac fac XX)))) fac(((2 R RR2 22 N NN,,,X XX N NN F FF,,,N NN fac(((N N 222 1 11,,, fac fac N 111,,,F FF))) RR22 22 NN,,XX NN FF,,NN 11,,NN 11 NN'',,FF NN''FF'',,N N''11,, fac fac((N N''11,,FF'')) R1 2 N , X N F , N 1, N 1 N ' , F N ' F ' , N ' 1, N '1 0, F ' 1 Simplificado na variável X, a resposta é X = 2 Avaliação (Evaluation) Em cada passo da reescrita, deveremos checar se a conjunção de restrições primitivas pode ser satisfeita A derivação faz isso Em cada passo, os literais… Restrição primitiva: são adicionados ao lado direito (das restrições) RDU: são reescritos Avaliação estado: <G1| C1> onde G1 é um objetivo e C1 é uma restrição Passo de derivação: G1 é L1, L2, ..., Lm L1 é uma restrição primitiva, C2 is C1 /\ L1 • Se solv(C /\ L1) = false então G2 = [] • Senão G2 = L2, ..., Lm L1 é uma RDU, C2 é C1 e G2 é a reescrita de G1 com L1 usando alguma regra e a renomeação Avaliação derivação de <G0 | C0>: G0| C0 G1| C1 G2| C2 Onde cada <Gi | Ci> até <Gi+1 | Ci+1> é um passo de derivação derivação de G é a derivação do estado <G | true> Derivação de fac(1,Y) fac true )|)|true true fac((((1111,,,,YY true fac YY)|)| fac R R RR2222 R 2 fac( N 1, F )| true 11 N N N F N fac N YY N N F FF,,,,N N 1111,,,,fac N N,,,,YY N N fac(((N N 11,,FF)|)|true true 11 1 N ,Y N F , N 1, fac( N 1, F )| true N F N fac N 111,,,F FF)| )|)|111 N N YY N N F F N 1111,,,,fac fac N N F,,,,N N fac((((N N N YY N 1 Y YY N N F FF N N N 11,,, fac fac N 111,,,F FF)| )|)|111 N fac(((N N N N fac(( N N F F N N 11 Y N fac N 1 1,, F F )| )|11 N N Y R R11 N || N F F N N 11 N 1 1 0 0,, F F 11 11 N N Y Y N F || F 11 11 N N Y Y N N F F N N 11 N N 11 00 []|1 N Y N F N 1 N 1 0 F 1 Resposta simplificada em Y é Y = 1 Derivações Para a derivação iniciando em <G0 | C0> Estado de sucesso: <[] | C> onde solv(C) != false Derivação de sucesso: o último estado é de sucesso resposta: simpl(C, vars(<G0 | C0>)) Estado de falha: <[] | C> onde solv(C) = false Derivação falha: o último estado é de falha Árvores de derivação Árvore de derivação para o objetivo G Raíz é < G | true > Os filhos de cada estado são estados alcançáveis em um passo de derivação Codifica todas as possíveis derivações Quando o literal mais à esquerda é uma restrição primitiva, somente há um filho Caso contrário, os filhos estão ordenados conforme a ordem das regras Exemplo de Árvores de Derivação fac(1, Y )| true R1 R2 1 0, Y 1| true 1 N , Y N F , N 1, fac( N 1, F )| true []|1 0 Y N F , N 1, fac( N 1, F )| C1 1 N Derivação falhou N 1, fac( N 1, F )| C 2 C1 Y N F R1 R2 N 1 0, F 1| C 3 N 1 N ' , F N ' F ' , N ' 1, fac( N '1, F ' )| C 3 F 1| C 4 C 3 N 1 0 F N ' F ' , N ' 1, fac( N '1, F ' )| C 6 C 3 N 1 N ' []| C5 C 4 F 1 N ' 1, fac( N '1, F ' )| C 7 C 6 F N ' F ' resposta: Y = 1 fac( N 1, F )| C 3 C 2 N 1 []| C8 C 7 N ' 1 Derivação falhou Árvore de Derivação O exemplo anterior mostrou três derivações, 2 falharam e uma foi bem sucedida Falha finita: Se a árvore de derivação é finita e todas as derivações falham Árvore de Derivação Infinita: algumas derivações são infinitas Exemplo de árvore de derivação infinita (S1) stupid(X) :- stupid(X). (S2) stupid(1). stupid ( X )| true S1 S2 X X ' , stupid ( X ' )| true X 1| true stupid ( X ' )| X X ' S1 []| X 1 S2 X ' X ' ' , stupid ( X ' ' )| X X ' X ' 1| X X ' stupid ( X ' ' )| X X ' X ' X ' ' []| X X ' X ' 1 S1 S2 Derivação Infinita Resposta: X=1 Resposta: X=1 Avaliação de Objetivos A Avaliação de um objetivo realiza uma busca em profundidade, e na direção da esquerda para a direita Quando encontra um estado de sucesso, o sistema retorna a resposta O usuário pode perguntar por outras respostas, o que faz a busca continuar A execução pára quando o usuário não pede mais respostas ou a árvore inteira é explorada A execução entra em laço infinito se a árvore é infinita e não apresenta resposta Árvores de Derivação Simplificadas As árvores de derivação são muito grandes Uma forma simplificada tem a informação considerada mais útil Restrições na forma simplificada (variáveis na forma do objetivo inicial, e como parte dos estados do objetivo) Remoção de estados sem interesse Estados Simplificados Estado simplificado: <G0 | C0> na derivação de G substitua C0 por C1=simpl(C0, vars(G,G0)) se x=t em C1 substitua x por t em G0 levando à G1 substitua C1 por C2=simpl(C1, vars(G,G1)) Exemplo fac( N '1, F ')|1 N Y N F N 1 N 1 N ' F F ' vars {Y , N ', F '} fac( N '1, F ')| N ' 0 Y F ' replace N ' by 0 and simplify again fac( 1, F ')|Y F ' Derivação Simplificada Um estado é critico se ele é o primeiro ou o último estado de uma derivação, ou se o primeiro literal é uma RDU Uma derivação simplificada para o objetivo G contém todos os estados críticos na forma simplificada Similarmente para uma árvore de derivação simplificada Exemplo de Árvore Simplificada fac(1, Y )| true R1 R2 []| false R1 []|Y 1 fac(0, F )|Y F R2 []| false Nota: estados de falha são <[] | false> e estados de sucesso contém as respostas O Esquema CLP O esquema define uma família de linguagens de programação Uma linguagem CLP(X) é definida por Domínio de restrição X Resolvedor para o domínio de restrição X Simplificador para o domínio de restrição X Nos exemplos usamos CLP(Real) Outro exemplo seria o CLP(Tree) CLP(R) Os elementos são árvores contendo constantes reais Restrições são { , } para árvores {, , , , } e para aritimética Capítulo 5: Modelando Escolha as variáveis que serão usadas para representar os parâmetros do problema (isto pode ser trivial ou difícil) Modele as relações idealizadas entre estas variáveis usando as restrições primitivas disponíveis no domínio Exemplo de Modelagem Uma viajante deseja atravessar o rio infestado de tubarões tão rápido quanto possível. A rota mais rápida é atravessar o rio nadando em linha reta, deixando que a correnteza faça a parte dela livremente. Ela deseja saber onde (P) ela vai parar após essa tarefa? Largura do rio: W Velocidade do rio: S Posição da nadadora: P Velocidade da nadadora: R R P S W Exemplo de Modelagem Raciocínio: enquanto a nadadora atravessa o rio na sua largura, gastando um tempo T, ela segue junto com a correnteza para o rio abaixo uma distância P dada pela velocidade do rio, com o mesmo tempo T. Daí o modelo: river(W, S, R, P) :- T = W/R, P = S*T. Supondo que ela nade a R=1.5m/s, a velocidade do rio é de S=1m/s e seu comprimento é de W=24m. river(24, 1, 1.5, P). A única resposta é P = 16 Largura do rio: W Velocidade do rio: S Posição da nadadora: P Velocidade da nadadora: R Exemplo de Modelagem (cont.) Se ela nada numa velocidade entre 1 e 1.3 m/s e não pode se afastar mais que 20m do ponto de origem, será que ela conseguirá? 1 <= R, R <= 1.3, P <= 20, river(24,1,R,P). Esta é a flexibilidade do modelo baseado em restrições! Escolhas mais complicadas Uma opção de “call” dá ao seu portador o direito de comprar 100 ações a um preço de exercício E fixo Uma opção de “put” dá ao seu portador o direito de vender 100 ações a um preço de exercício E fixo O pagamento (lucro ou prejuízo) de uma opção é determinado por seu custo C e pelo preço corrente da ação S e.g. custo do call C=$200, Exercício E=$300 Preço do mercado = $2, não utiliza o call: • pagamento = -$200 Preço do mercado = $9, utiliza o call: • pagamento = $400 Opções de Mercado call C=200, E = 300 200 100 0 -100 -200 0 1 2 3 4 5 6 7 call, selling call, buying 1 2 butterfly 3 4 5 200 100 0 -100 -200 0 1 2 3 4 5 6 7 100 50 0 -50 -100 0 put C=100, E = 300 6 put, selling put, buying Operação Borboleta: Compre um “call” por C=100 com E=500 e outro por C=400 com E=100. Venda dois “calls” por C=200, com E=300 Opções de Mercado PG= S – E – C (para compra de “call”) PG = E + C – S (para venda de “call”) (C=100,E=500, compra) PG1 = 100S – 600 p/ S>5 PG1 = – 100 p/ S<=5 (C=400,E=100, compra) PG2 = 100S – 500 p/ S>1 PG2 = – 100 p/ S<=100 (C=200,E=300, venda) PG3 = – S100 + 500 p/ S>3 PG3 = 200 p/ S<=3 100 50 0 -50 -100 0 1 2 butterfly 3 4 5 6 Operação Borboleta: Compre um “call” por C=100 com E=500 e outro por C=400 com E=100. Venda dois “calls” por C=200, com E=300 Aplicadas as restrições temos: PG = PG1 + PG2 + 2PG3 PG = – 100 p/ s>5 PG = – 100 p/ s<1 PG = 100S – 200 P/ 1<S<3 PG = –100S + 400 p/ 3<S<5 Modelando Funções C if 0 S E / 100 call _ payoff ( S , C, E ) if S E / 100 100S E C Modelar uma função com n argumentos como um predicado com n+1 argumentos. Os testes são restrições, e o resultado é uma equação. buy_call_payoff(S,C,E,P) :0 <= S, S <= E/100, P = -C. buy_call_payoff(S,C,E,P) :S >= E/100, P = 100*S - E - C. Modelando Opções Acrescente um argumento extra B=1 (compra), B = -1 (venda) call_option(B,S,C,E,P) :- 0 <= S, S <= E/100, P = -C * B. call_option(B,S,C,E,P) :S >= E/100, P = (100*S - E - C)*B. O objetivo é dado por: call_option(1, 7, 200, 300, P) E a resposta é P = 200 Usando o Modelo butterfly(S, P1 + P2 + 2*P3) :Buy = 1, Sell = -1, call_option(Buy, S, 100, 500, P1), call_option(Buy, S, 400, 100, P2), call_option(Sell, S, 200, 300, P3). Define a curva dada no gráfico anterior, para lucros: P >= 0, butterfly(S,P). Tem duas respostas: P 100S 200 2 S S 3 P 100S 400 3 S S 4 Por quê restrições e não Linguagem C? Ambos os programas podem responder o objetivo call_option(1, 7, 200, 300, P). Mas o CLP pode responder, sem muito esforço, o objetivo: butterfly(3, P1 + P2 + 2*P3). P = 100 E até mesmo o objetivo: P >= 0, butterfly(S,P). P = 100S – 200 /\ 2<=S /\ S<=3 P = -100S + 400 /\ 3<=S /\ s<=4 Otimização Muitos problemas requerem a “melhor” solução Literal de minimização: minimize(G,E) Responde as questões sobre o objetivo G que minimiza a expressão E (no contexto do estado) Exemplos de Otimização p(X,Y) := X = 1. p(X,Y) :- Y = 1. X >= 0, Y >= 0, minimize(p(X,Y), X+Y) Respostas: X = 1 /\ Y = 0 e X = 0 /\ Y = 1 X >= 0, X >= Y, minimize(true, X-Y) Resposta: X >= 0 /\ X = Y minimize(butterfly(S,P), -P) Resposta: S = 3 /\ P = 100 Avaliação de Otimização Um valor de v é uma solução para um estado se é alguma das soluções deste estado Passo da derivação de uma minimização: <G1 | C1> para <G2 | C2> onde G1 = L1,L2,...,Lm, e L1 é “minimize(G,E)” existe uma solução v de <G | C1> com v(E) = m e para todas as outras soluções w temos que m <= w(E) • Então G2 é G,L2,...,Lm e C2 é C1 /\ E = m Senão G2 é [] e C2 é falso Exemplo de Otimização X >= X>=0, Y<=X, m=min(X-Y) m=X-Y 0, minimize(X >= Y, X-Y) Tomemos X=0, o menor X m=-Y Mas Y<=X Y<=0 X 0, minimize( X Y , X Y )| true -Y>=0 Tomemos –Y=0, o menor Y m=0-0 m=0 X-Y=0 minimize( X Y , X Y )| X 0 X Y| X 0 X Y| X 0 X Y 0 []| X 0 X Y []| X 0 X Y 0 X Y Simplificado: X 0 X Y Valor mínimo de X-Y é 0 e.g. { X 3, Y 3} Otimização A otimização não precisa necessariamente ser um objetivo: straddle(S,C1+C2,E,P1+P2) :Buy = 1, call_option(Buy, S, C1, E, P1), put_option(Buy, S, C2, E, P2). best_straddle(C,E,P) :minimize(straddle(S,C,E,P),-P). Capítulo 7: Estimando Eficiência Avaliação é a busca na árvore de derivação O tamanho e a forma da árvore de derivação determinam a eficiência (ignorando o custo da solução) Quanto menor: menos busca Respostas do lado esquerdo da árvore:menos busca antes de achar a primeira resposta A árvore de derivação depende do modo de uso (MU) Modo de Uso (MU) modo de uso (MU): define os tipos de restrições nos argumentos de um predicado quando avaliado fixo: o conjunto de restrições implica em um único valor para todas as soluções livre: o conjunto de restrições permite todos os valores outros: limitado superiormente, limitado inferiormente Exemplo de Modo de Uso sumlist([], 0). sumlist([N|L], N+S) :- sumlist(L, S). MU primeiro arg fixo segundo livre sumlist([1],S). L=[1,2],S > Z, sumlist(L,S). Estados na árvore de derivação com a chamada de sumlist <sumlist([1], S)|true> <sumlist(L’,S’)|[1]=[N’|L’]/\S=N’+S’> <sumlist(L’’,S’’)|[]=[N’’|L’’]/\S’=N’’+S’’> <N’’=0,S’’=0|/\S’=N’’+S’’> Exemplo de Controle de Busca Pense em escrever um programa para computar: S 0 1 2 N Raciocíne recursivamente: A soma de N números é a soma de (N-1) + N A soma de 0 números é 0 (S1) sum(N, S+N) :- sum(N-1, S). (S2) sum(0, 0). Problema: sum(1,S) não tem resposta Exemplo de Controle de Busca sum(1, S )| true S1 S2 sum(0, S ' )| S 1 S ' []| false S1 sum( 1, S ' ')| S 1 S ' ' S2 []| S 1 S1 sum( 2, S '' ' )| S 0 S ' '' S2 []| false S1 Árvore de derivação simplificada para sum(1,S). Note que é infinita no ramo à esquerda, pois N assumiu valores negativos. Exemplo de Controle de Busca Derivação infinita antes da resposta (S3) sum(0, 0). (S4) sum(N, S+N) :- sum(N-1, S). sum(1,S) resposta: S=1, sum(1,0)| true S4 Mas e sum(1,0)? sum( 0,1)| true N=1, S+N=0 S=-1 N’=0, S’+N’=-1 S’=-1 N’’=-1, S’’+N’’=-1 S’’=0 N’’’=-2, S’’’+N’’’=0 S’’’=2 ... S4 sum( 1,1)| true S4 sum( 2,0)| true S4 Exemplo de Controle de Busca O programa não foi feito para tratar com números negativos. Corrija para: (S5) sum(0, 0). (S6) sum(N, S+N) :- sum(N-1, S), N >= 1. sum (1,0) true S6 sum (0,1),1 1 true S6 Ainda não deu: derivação infinita de sum(N,S+N) sum ( 1,1),0 1,1 1 true S6 Exemplo de Controle de Busca Lembre-se do processamento da esquerda para direita (S7) sum(0, 0). (S8) sum(N, S+N) :- N >= 1, sum(N-1, S). sum(1,S) dá S = 1, sum(1,0) responde no Métodos: Reordenamento de regras Adição de restrições redundantes Reordenamento de literais Ordenando Regras Regra geral Coloque as regras não-recursivas antes das regras recursivas (isto irá evitar derivações infinitas) Regra heurística Coloque regras que “parecem levar a respostas rápidas” antes (acima) das outras (isto tende a mover os estados de sucesso para a esquerda da árvore) Ordenando Literais Restrições Primitivas Coloque uma restrição no ponto mais à esquerda possível no qual ela poderia causar uma falha (para um determinado MU) fac(N,F) com N fixo e F livre fac(N, F) :- N = 0,F = 1. fac(N, FF) :- N >= 1, FF = N * F, N1 = N - 1, fac(N1, F). fac(N, F) :- N = 0,F = 1. fac(N, FF) :- N >= 1, N1 = N - 1, fac(N1, F), FF = N * F. Ordenando Literais RDU: Coloque os literais determinísticos antes dos outros determinístico: o literal p(s1,...,sn) em um programa é determinístico para uma árvore de derivação se a cada ponto de escolha onde ele é reescrito, todas as derivações falham, exceto uma, antes da reescrita da RDU (em outras palavras: no máximo uma tem sucesso) Predicados Deterministicos sumlist(L,S) é determinístico para o MU L fixo e S livre. Não é para L livre e S fixo. sum(N,S) é similar Predicados determinísticos requerem menos busca para achar uma resposta CUIDADO! Mover predicados pode mudálo de determinístico para não_determinístico (o bom é que o inverso também é válido!) Adicionando Restrições Redundantes Uma restrição que pode ser removida de uma regra sem alterar as respostas válidas de uma árvore é chamada de redundante. (Pode alterar a ordem.) Restrição de Resposta Redundante (ARC): o mesmo conjunto de respostas para: H :- L1, ..., Li, Li+1, ..., Ln H :- L1, ..., Li, r, Li+1, ..., Ln Vantagem (para um conjunto de restrições C, num MU) <L1,...,Li,r|C> falha, mas não <L1,...,Li|C> Adicionando Restrições Redundantes (ARC) A restrição N >= 1 adicionada ao programa sum é uma ARC. Outro exemplo: sum(N,7) (novo MU) sum( N ,7)| true S8 sum( N ' , S ' )| N N '1 S ' 6 N ' N ' 0 S8 sum( N ' ' , S ' ' )| N N ' '2 S ' ' 4 2 N ' ' N ' ' 0 S8 sum( N ' ' ' , S ' ' ' )| N N ' ' '3 S ' ' ' 1 3 N ' ' ' N ' ' ' 0 S8 sum( N ' ' ' ' , S ' ' ' ' )| N N ' ' ' '4 S ' ' ' ' 3 4 N ' ' ' ' N ' ' ' ' 0 Adicionando Restrições Redundantes (ARC) Sabemos que cada soma é não-negativa (S9) sum(0, 0). (S10) sum(N, S+N) :N >= 1, S >= 0, sum(N-1, S). sum( N ,7)| true S10 sum( N ' , S ' )| N N '1 S ' 6 N ' N ' 0 N ' 6 S10 sum( N ' ' , S ' ' )| N N ' '2 S ' ' 4 2 N ' ' N ' ' 0 N ' ' 2 S10 sum( N ' ' ' , S ' ' ' )| N N ' ' '3 S ' ' ' 1 3 N ' ' ' N ' ' ' 0 N ' ' ' 1 / 3 S10 []| false Restrições Redundantes de Resolvedor Restrição Resolvedor Redundante (SRC): uma restrição primitiva r é SRC se é implicada pelo conjunto de restrições Vantagem: Se o resolvedor não está vendo a implicação (resolvedor parcial ou árvore infinita), adicioná-la pode trazer informação extra (falha) F >= 1, N >= 1, FF = N*F, FF >= 1 Exemplo de Restrições Redundantes de Resolvedor (SRC) (F1) fac(N, F) :- N = 0,F = 1. (F2) fac(N, FF) :- N >= 1, N1 = N - 1, FF = N * F, fac(N, F). O objetivo fac(N,7) é infinito, como era sum(N,7). (F3) fac(N, F) :- N = 0,F = 1. (F4) fac(N, FF) :- N >= 1, N1 = N - 1, FF = N * F, F >= 1, fac(N, F). O objetivo fac(N,7) ainda é infinito! Exemplo de Restrições Redundantes de Resolvedor (SRC) fac( N ,7)| true F4 fac( N 1, F ' )| F ' 1 N 1 7 N F ' F4 fac( N 2, F ' ' )| F ' ' 1 N 2 7 N ( N 1) F ' ' F4 fac( N 3, F '" ' )| F '" ' 1 N 3 7 N ( N 1) ( N 2) F ' ' ' F4 fac( N 4, F ' ' ' ' )| F ' ' ' ' 1 N 4 7 N ( N 1) ( N 2) ( N 3) F ' ' ' ' Dado que F’’’’ >= 1 e N >= 4 então N ( N 1) ( N 2) ( N 3) F '''' Deve ser pelo menos 24. Esta restrição não foi satisfeita por não ter sido detectada (resolvedor parcial) Exemplo de Restrições Redundantes de Resolvedor (SRC) Correção: adicione a SRC, N * F >= N Que é implicada por N >= 1, F >= 1 CUIDADO: 1 = N * F , 2 = N * F tem sucesso ao se instanciar F, por isso, use um nome único para N*F (F3) fac(N, F) :- N = 0,F = 1. (F4) fac(N, FF) :- N >= 1, N1 = N - 1, FF = N * F, FF >= N, F >= 1, fac(N, F). Agora o objetivo fac(N,7) falha em tempo finito! Minimização Minimizar literais causam a busca de respostas em uma árvore de derivação diferente Precisamos entender a forma desta árvore para lidar com ela minimize(G,E) tem o mesmo MU de E < m, G Para um uso eficiente da minimização, tenha certeza que G é eficiente quando E é limitado superiormente, por um m qualquer dado. Exemplo de Minimização Programa que acha os nós folhas e seus níveis L1:leaf(node(null,X,null),X,0). L2:leaf(node(L,_,_),X,D+1) :leaf(L,X,D). L3:leaf(node(_,_,R),X,D+1) :leaf(R,X,D). Objetivo: a leaf(t(a),X,D) Respostas: (h,3),(j,4),(k,4), (m,4),(o,5),(p,5), (f,2),(s,4), (t,4),(r,3) b c d h e i j f l k m q n o g s p r t Exemplo de Minimização Objetivo minimize(leaf(t(a),X,D), D): Após encontrar X = h /\ D = 3, o objetivo fica: D < 3, leaf(t(a),X,D), o que faz que nunca se visite nós com profundidade superior a 3 (?) a D 3, leaf (t ( a ), X , D ) true leaf (t ( a ), X , D ) D 3 L2 leaf (t (b), X , D 1) D 3 L2 leaf (t ( d ), X , D 2) D 3 L3 b c d h e i j f l k m leaf (t (i ), X , D 3) D 3 q n s L3 g r r leaf (t ( k ), X , D 4) D 3 L1 [] ( D 3 D 4 0) false o p Exemplo de Minimização Melhore o leaf para o MU com D limitado superiormente: adicione uma ARC D 3, leaf (t (a ), X , D true leaf (t (a ), X , D) D 3 L4:leaf(node(null,X,null),X,0). L5 L5:leaf(node(L,_,_),X,D+1) :leaf (t (b), X , D 1) D 3 D 1 D >= 0, leaf(L,X,D). L5 L6:leaf(node(_,_,R),X,D+1) :leaf (t (d ), X , D 2) D 3 D 2 D >= 0, leaf(R,X,D). L6 [] ( D 3 D 3) false Minimização A busca pode nem sempre ser beneficiada pelos limites e.g. minimize(leaf(t(a),X,D), -D) Ainda deve visitar todos os nós antes de achar um nó folha de maior profundidade A formulação original é superior, uma vez que envolve menos restrições (a restrição redundante D>=0 não diminui o espaço de busca) Conceito-Chave: lembre-se do MU de E<m, G, com m fixo. Capítulo 8: Domínios e Rótulos Novos requisitos: precisamos definir os domínios e as variáveis aritmético X :: [1,2,3,4] ou X :: [1..4] não-aritmético X :: [red, blue, yellow] multiplas variáveis [X,Y,Z] :: [0..10] Se nenhum domínio é dado, é usado o padrão, por exemplo [-10000..10000] Exemplo de Domínios Objetivo para o problema do peso da sacola do contrabandista: [W,P,C] :: [0..9], 4*W + 3*P + 2*C <= 9, 15*W + 10*P + 7*C >= 30. D(W ) [0..2], D( P) [0..3], D(C) [0..4] O sistema CLP(FD) retorna unknown. Mas como achar uma solução? Devemos chamar um resolvedor completo (com backtracking) Rótulos (Labelling) O predicado interno labeling chama o resolvedor de restrições completo labeling(Vs) toma uma lista de variáveis de domínio finito Vs e encontra uma solução [W,P,C] :: [0..9], 4*W + 3*P + 2*C <= 9, 15*W + 10*P + 7*C >= 30, labeling([W,P,C]). Acha as soluções: W 0 P 1 C 3 W 0 P 3 C 0 W 1 P 1 C 1 W 2 P 0 C 0 Restringe e Gera! Forma típica de um programa de domínio finito Variáveis e domínios são definidos As restrições que modelam o problema são dadas O labeling é adicionado para chamar o resolvedor completo A minimização pode ser aplicada no labeling Exemplo: [W,P,C] :: [0..9], 4*W + 3*P + 2*C <= 9, 15*W + 10*P + 7*C >= 30, minimize(labeling([W,P,C]), -15*W-10*P-7*C). Exemplo Send+More=Money Problema Cripto-aritmético, cada dígito é diferente, e a equação deve ficar correta M S M O E O N N R E D E Y smm(S,E,N,D,M,O,R,Y) :[S,E,N,D,M,O,R,Y] :: [0..9], constrain(S,E,N,D,M,O,R,Y), labeling([S,E,N,D,M,O,R,Y]). constrain(S,E,N,D,M,O,R,Y) :S != 0, M != 0, alldifferent_neq([S,E,N,D,M,O,R,Y]), 1000*S + 100*E + 10*N + D + 1000*M + 100*O + 10*R + E = 10000*M + 1000*O + 100*N + 10*E + Y. Exemplo Send+More=Money Após a declaração do domínio, temos: D( S ) D( E ) D( N ) D( D) D( M ) D(O) D( R) D(Y ) [0..9] Após S 0 M 0 temos D( S ) D( M ) [1..9] alldifferent_neq adiciona a desigualdade (não muda o domínio) Equação final após uma regra de propagação: max( D, S ) max( D, E ) max( D, D) max( D, R) M 1 1 1 10 min( D, O) 100 min( D, N ) 9000 min( D, Y ) 1 9 91 9000 1 9000 1 900 Considerando o domínio corrente, temos: M 9 9 91 9 9000 9 9000 9 900 0 10 0 100 0 9000 1102 . Exemplo Send+More=Money Portanto D(M) = [1..1] A propagação continua até que: D( S ) [9..9] D( E ) [4..7] D( N ) [5..8] D( D) [2..8] D( M ) [1..1] D(O) [0..0] D( R) [2..8] D(Y ) [2..9] Observe: 3 variáveis estão fixadas e todos os domínios estão reduzidos. O labeling tenta S=0, S=1, ..., S=8 (que falham imediatamente) e então S=9. Depois E=0, E=1, E=2, E=3 (que falham imediatamente) e E=4 (que eventualmente falha), então E=5 dá como resposta: D( S ) [9..9] D( E ) [5..5] D( N ) [6..6] D( D) [7..7] D( M ) [1..1] D(O) [0..0] D( R) [8..8] D(Y ) [2..2] Gera e Testa! Método sem restrições: primeiro gera uma possível solução então testa a solução smm(S,E,N,D,M,O,R,Y) :[S,E,N,D,M,O,R,Y] :: [0..9], labeling([S,E,N,D,M,O,R,Y]), constrain(S,E,N,D,M,O,R,Y). Este programa requer 95671082 escolhas antes de achar a solulção. O anterior requer apenas 35 escolhas (ou 2 que não falham imediatamente) Restrições Complexas Restrições Complexas como alldifferent, cumulative e element Modelagem do problema mais sucinta Melhor propagação = mais eficiência Pois são primitivas! (reveja a definição) Exemplo: alldifferent_neq([S,E,N,D,M,O,R,Y]) Rotulagem Há duas escolhas feitas pelo labeling: labeling padrão: Escolha de qual variável instanciar primeiro Escolha de qual valor do domínio tentar primeiro Tenta as variáveis na ordem dada pela lista Tenta os valores do menor para o maior (dado pela primitiva dom) Podemos programar estratégias diferentes Escolha de Variáveis A escolha das variáveis afeta o tamanho da árvore de derivação Heurística: escolha variáveis com o conjunto do domínio menor (portanto com menos possíveis respostas) Exemplo D( S ) [9..9] D( E ) [4..7] D( N ) [5..8] D( D) [2..8] D( M ) [1..1] D(O) [0..0] D( R) [2..8] D(Y ) [2..9] O labeling tentaria primeiro S, M, O (a escolha já está dada pelo domínio). Melhor que E ou N. Muito melhor que Y primeiro. Escolha do Valor do Domínio O valor da variáveis somente afeta a ordem em que os ramos da árvore é explorado Conhecimento específico do problema pode levar a uma busca mais rápida da solução Também é importante para a otimização: Boas soluções encontradas cedo reduzem a busca posterior Eficiência da Rotulagem Podemos utilizar ambas ordenações (de variáveis e de valores) juntas Número de escolhas diferentes para o problema das N-Rainhas com estratégias diferentes • Padrão • Primeira a falhar • Primeiro no meio 10000 1000 Default First-fail Middleout 100 10 1 10-19 30-39 50-59 70-79 Divisão de Domínios (Domain Splitting) O labeling não precisa ser do tipo Ligar uma variável a um valor Ele simplesmente deve reduzir o domínio de uma variável Divisão de Domínios divide o domínio corrente de uma determinada variável pela metade Modelando diferentemente o (mesmo) Problema Visões diferentes de um problema levam a diferentes modelos Modelos diferentes = Conjunto de variáveis diferentes Dependendo das capacidades do resolvedor, um modelo pode requerer menos busca que outro para achar uma resposta A comparação empírica pode ser a medida do tempo ou esforço gasto Modelando diferentemente o (mesmo) Problema Problema de simples atribuição: quatro trabalhadores w1,w2,w3,w4 e quatro produtos p1,p2,p3,p4. Atribua um produto a um trabalhador de modo que o lucro L >= 19 A matriz de Lucro é: w1 p1 p2 7 1 p3 p4 3 4 w2 w3 8 4 2 3 5 7 1 2 w4 3 1 6 3 Modelo 1: Pesquisa de Operações 16 variáveis booleanas Bij significando que o trabalhador i está com o produto j 4 i 1 4 j 1 4 j 1 4 i 1 Bij 1 Bij 1 7 B11 B12 8 B21 2 B22 P 4 B31 3B32 3B41 B42 P 19 Somatória de j é 1 (um só produto por trabalhador) Somatória de i é 1 (um só trabalhador por produto) 3B13 5 B23 7 B33 6 B43 4 B14 B24 2 B34 3B44 w1 p1 p2 7 1 p3 p4 3 4 w2 w3 8 4 2 3 5 7 1 2 w4 3 1 6 3 B23 11 restrições primárias, 28 escolhas para achar todas as quatro soluções Modelo 2: melhor Usando a desigualdade e restrições complexas. Quatro variáveis W1,W2,W3,W4 correspondendo aos trabalhadores W1 w1 W2 w2 W3 w3 W4 w4 1 2 3 4 p1 p2 7 1 p3 p4 3 4 8 4 2 3 5 7 1 2 3 1 6 3 alldifferent ([W1, W 2, W 3, W 4]) element (W1,[7,1,3,4], WP1) element (W 2,[8,2,5,1], WP2) element (W 3,[4,3,7,2],WP 3) element (W 4,[3,1,6,3], WP4) P WP1 WP2 WP3 WP4 P 19 7 restrições primárias, 14 escolhas para achar todas as quatro soluções Modelo 3: Diferente T1 T2 T3 T4 Quatro variáveis T1,T2,T3,T4 correspondendo aos produtos. 1 w1 2 w2 3 w3 4 w4 p1 p2 7 1 p3 p4 3 4 8 4 2 3 5 7 1 2 3 1 6 3 alldifferent ([T1, T 2, T 3, T 4]) element (T1,[7,8,4,3], TP1) element (T 2,[1,2,3,1], TP 2) element (T 3,[3,5,7,6], TP 3) element (T 4,[4,1,2,3], TP 4) P TP1 TP 2 TP 3 TP 4 P 19 7 restrições primárias, 7 escolhas para achar todos as quatro soluções Comparando os Modelos A eficiência relativa vem de: Mapeamento mais direto às restrições primitivas Menos variáveis Normalmente requer avaliação empírica Outro critério de eficiência: flexibilidade (adicionar restrições novas) e.g. trabalhador 3 deve trabalhar num produto > que o do trabalhador 2 B31 0 B24 0 B32 B21 B33 B21 B22 B34 B21 B22 B23 Modelo 1 W3 W2 Modelo 2 ????? Modelo 3 Combinando Modelos Combine os modelos relacionando as variáveis e seus valores em cada um dos modelos e.g. B13 = 1 significa W1 = 3 que significa T3 = 1 Modelos combinados podem ganhar mais eficiência atráves da informação que é propagada A restrição primária iff(V1,D1,V2,D2) é verdade para V1=D1 se e somente se V2=D2 Modelos Combinados alldifferent ([W1, W 2, W 3, W 4]) element (W1,[7,1,3,4], WP1) element (W 2,[8,2,5,1], WP2) element (W 3,[4,3,7,2],WP 3) element (W 4,[3,1,6,3], WP4) P WP1 WP2 WP3 WP4 P 19 alldifferent ([T1, T 2, T 3, T 4]) element (T1,[7,8,4,3], TP1) element (T 2,[1,2,3,1], TP 2) element (T 3,[3,5,7,6], TP3) element (T 4,[4,1,2,3], TP 4) P TP1 TP 2 TP3 TP 4 iff (W11 , , T11 , ) iff (W1,2, T 2,1) iff (W1,3, T 3,1) iff (W1,4, T 4,1) iff (W 2,1, T1,2) iff (W 2,2, T 2,2) iff (W 2,3, T 3,2) iff (W 2,4, T 4,2) iff (W 3,1, T1,3) iff (W 3,2, T 2,3) iff (W 3,3, T 3,3) iff (W 3,4, T 4,3) iff (W 4,1, T1,4) iff (W 4,2, T 2,4) iff (W 4,3, T 3,4) iff (W 4,4, T 4,4) 39 restrições prim. 5 escolhas para achar todas soluções Resolvedor baseado em Consistência do Arco Assumimos que o sistema CLP(FD) usa um resolvedor de propagação de limites A Consistência do Arco permite que mais valores sejam eliminados nas relações binárias das variáveis Exemplo: Suponha o problema das 8-Rainhas, com uma solução parcial de D(R1)={2}, D(R2)={4}, D(R3)={6} O resolvedor de restrições baseado em consistência de limites determinará os domínios: • D(R4)=[1..8], D(R5)=[3..5], D(R6)=[1..5], D(R7)=[1..7], D(R8)=[3..8] O resolvedor de restrições baseado em consistência de arcos determinará os valores: • D(R4)={1,3,8}, D(R5)={3,5}, D(R6)={1,5}, D(R7)={1,3,5,7}, D(R8)={3,5,7,8} Resolvedor baseado em Consistência do Arco O resolvedor baseado em consistência do arco dá informações muito mais acuradas dos valores possíveis para as variáveis Outra vantagem é que este resolvedor trabalha tão bem com valores limitados quanto com coleções, enquanto o resolvedor baseado em limites trabalha bem apenas com restrições aritméticas O programador define a RDU como uma relação binária com múltiplos fatos, e as rotula com a palavra reservada “consistent”, que indica ao CLP que ele deve resolver a relação utilizando consistência de arcos. Resolvedor baseado em Consistência do Arco Por exemplo, o programa: likes(joaquim, maria). likes(joaquim, nicole). likes(joaquim, erica). likes(pedro, maria). likes(bernardo, nicole). likes(bernardo, maria). likes(bernardo, erica). E os objetivos: Obj1: [N,M,E]::[joaquim, pedro, bernardo], N M , N E, M E likes(N, neila),likes(M, maria),likes(E, erica). Obj2: [N,M,E]::[joaquim, pedro, bernardo], N M , N E, M E likes(N, neila) consistent, likes(M, maria) consistent, likes(E, erica) consistent, labeling([N,M,E]). Resolvedor baseado em Consistência do Arco Predicados de aridade maior se resolvem pela consistência de hiper-arcos e com predicados unários, consistência de nós. No exemplo anterior, no objetivo 2, ao invés de fazer o backtracking pelas diferentes regras (caso do objetivo 1), é utilizada a consistência de arcos para avaliar a relação likes. Assim, as desigualdades e as restrições levam ao seguinte domínio: D(N)={joaquim, bernardo}, D(M)={joaquim, bernardo, pedro}, D(E)={joaquim, bernardo} A execução do labeling leva arco devolve D(N)={joaquim}, e a consistência do D(M)={pedro}, D(E)={bernardo} Assim nenhuma outra escolha é necessária para achar a primeira solução. Se for pedida outra solução, o backtraking faz imediatamente D(N)={bernardo} e as restrições da consistência do arco se propagam, não sendo necessária nenhuma outra escolha para a segunda solução Restrição Reified Uma restrição reified r B adiciona uma variável booleana B a uma restrição primitiva r Se r ocorre, então B = 1 Se r não ocorre, então B = 0 A propagação é em ambas as direções Restrição Reified Usada para construir combinações complexas sem usar “escolhas” Imagine que queremos que, das nossas 4 restrições primitivas, sejam satisfeitas apenas 2 ou 3. Isso geraria regras com muitas combinações… comb(X,Y):comb(X,Y):comb(X,Y):comb(X,Y):comb(X,Y):. . . c1, c2, ¬c3, ¬c4. c1, ¬c2, c3, ¬c4. c1, ¬c2, ¬c3, c4. ¬c1, c2, c3, ¬c4. ¬c1, c2, ¬c3, c4. C1 B1, C2 B2, C3 B3, C4 B4, B = B1 + B2 + B3 + B4, 2<=B, B<=3