CLP Programowanie logiczne z ograniczeniami. Wstęp Programowanie z ograniczeniami (Constraint Programming CP) stało się w ostatnich latach popularnym sposobem modelowania i rozwiązywania wielu problemów z dziedziny: -sztucznej inteligencji -problemów kombinatorycznych -przetwarzania mowy -harmonogramowania. -przetwarzania języków naturalnych (konstruowanie efektywnych parserów) -systemów baz danych (zapewnienie spójności danych) -biologii molekularnej (sekwencjonowanie DNA) -inżynierii elektronicznej (lokalizacja błędów) -projektowania obwodów drukowanych -etc. Jego główną zaletą jest deklaratywność, czyli sformułowanie zadania jest od razu programem rozwiązującym to zadanie. Programowanie to bazuje na modelowaniu zadania jako problemu spełnienia ograniczeń (Constraint Satisfacton Problem CSP). Ograniczenia są zależne od dziedzin zmiennych, których dotyczą. Najpopularniejszą i pierwszą dziedziną zmiennych była skończona dziedzina liczb naturalnych. Innymi dziedzinami są: skończone zbiory, drzewa, rekordy, przedziały rzeczywiste. Najistotniejszą cechą i największą zaleta programowania z ograniczeniami jest ich propagacja. Propagacja ograniczeń Sprawiła, że ta technika stała się najlepsza metodą dla wielu problemów kombinatorycznych. Zasadą działania propagacji jest usuwanie wartości nie spełniających ograniczeń z domen zmiennych. Języki do programowania z ograniczeniami mają możliwość wyrażania zmiennych z zakresu domen liczb naturalnych (najczęściej stosowane), przedziałów liczb rzeczywistych, zbiorów i innych. Aby zobrazować propagacje ograniczeń wprowadzimy prosty przykład: x {1..5}, y {1..6} Gdy na powyższe dwie zmienne wprowadzimy ograniczenie x>y+1, wtedy propagacja ograniczeń zredukuje powyższe domeny do następujących wartości: x {3, 4, 5}, y {1, 2, 3} ponieważ wartości {1, 2} z domeny x nie spełniają ograniczenia x>y+1 dla żadnej z wartości z domeny y. Podobnie można rozpatrywać wartości {4, 5, 6} z domeny y. Jednak możemy wprowadzić takie ograniczenie jak x+y=6, które nie usuwa żadnych wartości z domen. Ograniczenia nie są zazwyczaj tak proste ja to przedstawiono, często łączą ze sobą wiele zmiennych, a metody usuwania poszczególnych wartości, zwane algorytmami filtracyjnymi są bardzo złożone. Sama propagacja z ograniczeniami rzadko daje rozwiązanie. Dlatego jest ona zawsze łączona z dystrybucją i poszukiwaniem. Dystrybucja i poszukiwanie W większości przypadków propagacja ograniczeń nie prowadzi do rozwiązania (jak to
zostało przedstawione w powyższym przykładzie). Dlatego programowanie z ograniczeniami jest ściśle związane z dystrybucją połączoną z poszukiwaniem. Dystrybucja polega na wprowadzeniu dodatkowego ograniczenia (często jest to przyporządkowanie jednej wartości do zmiennej, a zadaniem dystrybucji jest odpowiednie wybranie zmiennej i wartości). Kiedy to nastąpi sprawdzana jest spójność poprzez propagację ograniczeń i istnieją trzy możliwości: - znalezione zostanie rozwiązanie (wszystkie zmienne mają po jednej wartości w swojej domenie), - domeny niektórych zmiennych zostaną zawężone, ale jednoznaczne rozwiązanie nie jest wciąż wyznaczone, więc dystrybucja jest dokonywana na kolejnej zmiennej, - dodatkowe ograniczenie jest niespójne z pozostałymi ograniczeniami, więc proces nawrotu jest dokonywany, a wybrana wartość z domeny wybranej zmiennej jest usuwana. Ten proces jest dokonywany iteracyjnie i jest nazywany poszukiwaniem. Poszukiwanie jest odpowiedzialne za zatrzymanie; po znalezieniu pierwszego rozwiązania lub pewnej liczby rozwiązań lub wszystkich rozwiązań. Poszukiwanie tworzy tzw. drzewo poszukiwań, gdzie każdy węzeł jest stanem zmiennej. Na Rys.1. przedstawiliśmy drzewo poszukiwań dla przykładu podanego powyżej. W językach do programowania w logice wywodzących się z Prologu poszukiwanie jest mechanizmem wbudowanym. Dlatego pierwsze języki, wciąż cieszące się powodzeniem bazują na Prologu (CHIP, Eclipse, GNU Prolog, SICStus) i noszą nazwę języków do programowania w logice z ograniczeniami (Constraint Logic Programming CLP). Jednak języki te poprzez swoją zamkniętą strukturę nie spełniają wymagań stawianych im przez programistów. Dlatego popularność zyskały biblioteki w C/C++, które umożliwiają korzystanie z zalet najpopularniejszego języka obiektowego, a dodatkowo pozwalają deklarować zmienne z określonych dziedzin, wprowadzać ograniczenia oraz wybierać strategię poszukiwania (ILOG Solver). Inne podejście prezentują języki do współbieżnego programowania z ograniczeniami. Najpopularniejszy z nich system Mozart, implementacja języka Oz,
posiada takie zalety jak: możliwość formułowania strategii poszukiwań, zagnieżdżonych ograniczeń, rozproszenia obliczeń oraz obiektowość. Porównanie dwóch języków GNU Prolog IF/Prolog Definiowanie dziedzin fd_domain(lista_zmiennych_lub_zmienna, min, max)ogranicza dziedzinę zmiennych z listy (lub zmiennej) do przedziału min..max. Predykat Zmienna in Zakres definiuje dziedzinę złożoną z liczb z zadanego zakresu.?- X in 1..8, Z in 12..27. Narzucanie ograniczeń (więzów) Relacje miedzy wyrażeniami arytmetycznymi zawierającymi zmienne są podobne jak w standardzie Prologu ale poprzedzone znakiem # X+Y#=3 jest ograniczeniem arytmetycznym na zmienne X,Y. Suma zmiennych musi być równa 3 (#=). Relacje miedzy wyrażeniami arytmetycznymi zawierającymi zmienne są podobne jak w standardzie Prologu ale poprzedzone znakiem??=,?=<,?>=, itp.?- X in 1..10, X?> 5, X?< 8. X = 6..7 Etykietowanie (tj. podstawianie pod zmienne wartości z ich zawężonych dziedzin) fd_labeling(lista_zmiennych)nadaje zmiennym wartości z ich dziedzin, tak aby spełnione były wszystkie ograniczenia (czyli rozwiązuje zagadnienie CSP).?- fd_domain(x,1,3), fd_domain(y,1,4), X+Y#=3, fd_labeling([x,y]) X=1 Y=2; X=2 Y=1 Służy do tego predykat indomain(zmienna), który podstawia pod zmienną kolejne wartości z jej dziedziny oraz predykat label(listazmiennych) etykietujący zmienne znajdujące się na liście.?- X in 1..10, X?< 3, indomain(x). X = 1 ; X = 2 ; Przykładowy program Znaleźć rozwiązanie następującego równania na słowach: S E N D +M O R E ------------------- M O N E Y
Send:- LD=[S,E,N,D,M,O,R,Y], fd_all_different(ld), fd_domain(ld,0,9), fd_domain([s,m],1,9), 1000*S+100*E+10*N+D + 1000*M+100*O+10*R+E #= 10000*M+1000*O+100*N+10*E+Y, fd_labeling(ld), write(ld). Send:- LD = [M,O,S,N,R,E,D,Y], Digits in 0..9, all_distinct(digits), 1000*S+100*E+10*N+D + 1000*M+100*O+10*R+E?= 10000*M+1000*O+100*N+10*E+Y, M?>= 1, S?>= 1, label(ld). %Rozw. % [S,E,N,D,M,O,R,Y] % [9,5,6,7,1,0,8,2] Inne przykłady: Przykład 1. (GNU Prolog) Rozwiąż układ równań z czterema niewiadomymi całkowitymi: a+4b+c=10 a+2c=5 b+c=3 uklad:- LD = [A, B, C], fd_domain(ld,-10,10), A+4*B+C#=10, A+2*C#=5, B+C#=3, fd_labeling(ld), write(ld). Rozwiązanie: % [A, B, C]=[3,2,1] Przykład 2. (IF/Prolog) Saper
Zmiennymi od A do N oznaczono pola leżące na brzegu odkrytego obszaru. Dziedzina każdej zmiennej jest zbiór dwuelementowy: 0 nie maminy 1 jest mina. Cyfry oznaczają liczbę min znajdujących się na ośmiu sąsiednich polach. Każdej cyfrze odpowiada jedno ograniczenie np. D + E + F + G + H + I? = 4. Poniższy predykat saper(listapol) ustala możliwe wartości dla zmiennych opisujących pola: saper(listapol) :- ListaPol = [A, B, C, D, E, F, G, H, I, J, K, L, M, N], ListaPol in 0..1, A+B?= 1, A+B+C+D?= 2, D?= 1, D+E+I?= 3, D+E+F+G+H+I?= 4, I?= 1, I+H+J?= 3, J?= 1, J+K?= 1, J+K+L+M+N?= 2, M+N?= 1, label(listapol). Jeśli w pewnym rozwiązaniu pole ma wartość 1 (ew. 0), to może zawiera minę (ew. może nie zawierać). Dopiero znajomość wszystkich rozwiązań pozwala zlokalizować miny.?- saper(x). % A B C D E F G H I J K L M N X = [0,1,0,1,1,0,0,1,1,1,0,0,0,1] ; X = [0,1,0,1,1,0,0,1,1,1,0,0,1,0] ; X = [1,0,0,1,1,0,0,1,1,1,0,0,0,1] ; X = [1,0,0,1,1,0,0,1,1,1,0,0,1,0] ; (10 ms) no Miny są na polach D, E, H, I oraz J, natomiast nie ma ich na polach C, F, G, K i L. Pozostałych pól nie można określić.
Przykład 3 (IF/Prolog) Znaleźć minimalna wartość wyrażenia 3 _ x y, dla x 2 {2, 3, 4}, y 2 {1, 2, 3, 4, 5}:?- X in 2..4, Y in 1..5, Z?= 3*X-Y, minimize_bb(label([x, Y]), Z). Predykat minimize_bb/2 służy do znajdowania minimum wartości funkcji celu: minimize_bb(label([x1,x2,...,xn]),optimum). Literatura Pisząc pracę korzystaliśmy z następujących dokumentów: 1. Metody sztucznej inteligencji autor Przemysław Kobylański www2.ioz.pwr.wroc.pl/ przemko/dyd/msi 2. PLANOWANIE ZAJĘĆ METODAMI PROGRAMOWANIA Z OGRANICZENIAMI autorzy Wojciech Legirski, Paweł Parys 3. referat Constraint Logic Programming = Programowanie logiczne z użyciem ograniczeń autorzy Tomasz Bielecki Janusz Marcinkowski. http://www-users.mat.uni.torun.pl/~fly/materialy/pl/referaty/clp/index.html