W ramach laboratorium należy:
Wszystkie programy powinny być implementowane w języku Python (wersja 3.x.y). Mogą Państwo (i powinni) korzystać z poniższych programów:
dimacs.py - mikrobiblioteka pozwalająca na wczytywanie grafów, nagrywanie wyników, najbardziej podstawowe operacje na grafach, oraz nagrywanie formuł logicznych CNF w formacie DIMACS ascii (z którego korzystają solwery SAT)
Biblioteka pycoSAT
Biblioteka pycoSAT tworzy interface do solwera PicoSAT dla języka Python (PicoSAT odnosił sukcesy na turniejach rozwiązywania instancji SAT—SAT Competition—ok. roku 2007; dzięki wygodnemu interface’owi będzie świetnym narzędziem na początek)
Instalacja pycoSAT. Należy zainstalować pakiet pycosat wykonując polecenie
pip install pycosat (w niektórych systemach pip3 zamiast pip)
Reprezentacja formuł. pycoSAT reprezentuje formuły jako listy klauzul, gdzie każda klauzula to lista numerów zmiennych, które w niej występują. Wartość ujemna oznacza zanegowanie danej zmiennej. Na przykład lista:
cnf = [ [-1,2,3], [2,-3], [1,-2,-3] ]
reprezentuje formułę ( -x1 ∨ x2 ∨ x3 ) ∧ ( x2 ∨ -x3 ) ∧ ( x1 ∨ -x2 ∨ -x3 ). Zmienna o numerze 0 nie istnieje.
Szukanie rozwiązania. Aby sprawdzić czy formuła jest
spełnialna i znaleźć odpowiednie wartościowanie należy wykonać
funkcję solve( cnf ). Poniższy kod pokazuje przykład:
import pycosat
cnf = [ [-1,2,3], [2,-3], [1,-2,-3] ]
sol = pycosat.solve( cnf )
print( sol )
Ten kod jako wyjście wypisze [-1, 2, -3], co oznacza,
że formuła jest spełnialna i świadczy o tym wartościowanie:
fałsz,prawda,fałsz.Formalnie, funkcja solve zwraca następujące wartości:
u'UNSAT' jeśli formuła jest niespełnialnafałsz oraz dodatnią
jeśli w rozwiązaniu ma wartość prawda.Tę mikrobibliotekę znają Państwo z Laboratorium 1: VertexCover. W ramach tego laboratorium pojawiły się dwie nowe funkcja:
saveCNF( name, cnf )
która nagrywa formułę ze zmiennej cnf (reprezentowaną jako lista klauzul) do pliku o nazwie name
w formacie DIMACS ascii. Taki plik można bezpośrednio przekazać do zewnętrznego solwera.
Druga nowa funkcja to:
loadX3C( name )
która wczytuje instancję problemu X3C (jest omówiona przy redukcji X3C do SAT).
Solwerami zewnętrznymi należy zająć się po zakończeniu wszystkich innych prac
glucose i maple to dwa solwery SAT (wywodzące się z tej samej bazy kodu), które odnosiły sukcesy w ostatnich turniejach SAT. Po ściągnięciu ich źródeł należy je skompilować.
Odpowiednie instrukcje znajdują się w pliku README. Sprowadza się to do wykonania poleceń:
cd simp
make rs
W przypadku glucose może być konieczna podmiana opcji kompilatora w pliku
mtl/template.mk (zmiana linii zaczynającej się od CFLAGS na
CFLAGS ?= -Wall -Wno-parentheses -std=c++0x).
Wywołanie solwera. Jeśli solwer ma nazwę solver oraz mamy
plik cnf z zapisaną formułą, to należy wykonać polecenie:
solver cnf
W ramach tego zadania należy sporządzić wykres, który pokazuje prawdopodobieństwo, że losowo wygenerowana formuła w formacie k-CNF (k literałów na klauzulę) jest spełnialna, w zależności od ilorazu liczby klauzul i liczby zmiennych.
Generowanie losowej klauzuli. Załóżmy, że mamy n zmiennych do dyspozycji (o numerach 1, …, n). Losowa klauzula rozmiaru k składa się z losowo wybranych k zmiennych (z powtarzaniem), z których każda jest zanegowana/niezanegowana z prawdopodobieństwem 1/2.
Od strony technicznej przydatny mozę być następuący fragment kodu:
import random # biblioteka liczb pseudolosowych
n = 5
S = [1,-1] # lista +/-
V = range(1,n+1) # lista zmiennych 1...n
x = random.choice(V)*random.choice(S) # losowo wybrana zmienna z losowym negowaniem
print( x )
Wykonanie eksperymentu. Ustalmy k=3, czyli problem 3CNF-SAT. Proszę ustalić pewną liczbę zmiennych n (np. 10, 50, 100) oraz pewną liczbę powtórzeń T (np. T = 100). Następnie, dla wartości a z przedziału 1 do 10 (np. z krokiem 0.1) proszę:
Następnie proszę wygenerować wykres funkcji S/T w zależności od a.
Proszę powtórzyć eksperyment dla kilku wartości n oraz dla innej wartości k.
Jeśli dane są w pliku tekstowym dane, np. o treści:
1 0.1
2 0.4
3 0.8
4 0.16
5 0.32
to można zrobić ich wykres przy pomocy narzędzia gnuplot,
wpisując w jego konsoli:
set yrange [-0.1:1.1]
plot "dane" using 1:2 with lines
Alternatywa: Wykorzystanie pakietu OpenOffice lub Google Sheets.
W tym zadaniu należy zaimplementować redukcję pewnego wariantu problemu X3C do SAT oraz wykorzystać solwer SAT do rozwiązywania przykładowych instancji X3C.
W problemie X3C mamy dany zbiór elementów N = {1, …, 3k} oraz rodzinę zbiorów S = {S1, …, Sm} gdzie m <= 3k. Każdy zbiór Si zawiera trzy elementy ze zbioru N a każdy element z N występuje najwyżej w trzech zbiorach z rodziny S. Pytanie brzmi czy da się wybrać k zbiorów tak, że każdy element zbioru N należy do dokładnie jednego wybranego zbioru.
Pomysł redukcji polega na tym, że dla każdego zbioru Si tworzymy zmienną logiczną xi, której wartość interpretujemy następująco:
Następnie tworzymy następujące klauzule. Dla każdego elementu j ze zbioru N tworzymy klauzulę, która składa się ze zmiennych odpowiadających zbiorom, do których należy j. Na przykład, jeśli element 1 należy do zbiorów S6, S9 oraz S11 to tworzymy klauzulę:
( (x6 ∨ x9 ∨ x11) )
która wymusza, że co najmniej jeden z tych zbiorów jest wybrany. Następnie dla każdej takiej klauzuli należy zapewnić, że żadne dwa zbiory zawierające ten sam element nie są wybrane jednocześnie. Dla powyższego przykładu wystarczy dodać trzy klauzule:
( (-x6 ∨ -x9) ∧ (-x6 ∨ -x11) ∧ (-x9 ∨ -x11) )
Każda z powyższych klauzul mówi, że spośród dwóch zbiorów co najwyżej jeden może być wybrany.
Jako dane testowe proszę wykorzystać instancje X3C z pliku x3c.zip. Nazwa pliku mówi, czy instancja ma rozwiązanie czy nie, oraz daje pogląd na temat rozmiaru danych (np. plik 10.yes.x3c ma rozwiązanie i zawiera zbiór N składający się z 3*10 elementów). Instancje z tego pliku można wczytać przy pomocy funkcji
loadX3C( name )
która wczytuje instancję z pliku name i zwraca parę n, sets, gdzie n to liczba elementów do pokrycia (numerowanych od 1 do n) a sets to lista zbiorów, gdzie każdy zbiór jest listą trzyelementową. N
W tym zadaniu rozważamy problem kolorowania grafów, zdefiniowany następująco:
W ramach zadania proszę napisać program, który wczytuje graf oraz dostaje liczbę k dopuszczalnych kolorów, oblicza formułę logiczną, która jest spełnialna wtedy i tylko wtedy gdy graf posiada odpowiednie kolorowanie, sprawdza spełnialność tej formuły (i w przypadku spełnialności wypisuje numery kolorów przypisanych wierzchołkom).
Można wykorzystać następującą redukcję do SAT. Mamy graf G = (V,E), gdzie V = {v1, …, vn} oraz k kolorów do wykorzystania. Dla każdego wierzchołka vi oraz koloru j tworzymy zmienną xi,j, której wartość interepretujemy następująco:
Dla każdego wierzchołka xi tworzymy serię klauzul, które mówią, że ten wierzchołek ma dokładnie jeden kolor:
( xi,1 ∨ xi,2 ∨ … ∨ xi,k ) ∧ ( -xi,1 ∨ -xi,2 ) ∧ ( -xi,1 ∨ -xi,3 ) ∧… ∧ ( -xi,1 ∨ -xi,k ) ∧ ( -xi,2 ∨ -xi,3 ) ∧ ( -xi,2 ∨ -xi,4 ) ∧ … ∧ ( -xi,2 ∨ -xi,k ) ∧ … ∧ ( -xi,k-1 ∨ -xi,k )
Dla każdej krawędzi { vi,vt } i dla każdego koloru j tworzymy klauzulę, która mówi, że oba wierzchołki nie mogą mieć jednocześnie koloru j:
( -xi,j ∨ -xt,j )
Jako dane testowe proszę wykorzystać zestaw grafów pochodzący ze zbioru benchmarków Graph Coloring Benchmarks ( wygasły link). Proszę pamiętać o odczytaniu kolorów wierzchołków z wartościowania formuły oraz zrobienia wewnętrznego testu w programie, sprawdzającego czy kolorowanie jest poprawne.
Jeśli pycoSAT okaże się za wolny, można próbować nagrywać formułę na
dysk (saveCNF) oraz uruchamiać solwery glucose lub maple.