W ramach laboratorium należy:
Grafy do testów należy wziąć z wcześniejszych laboratoriów.
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)
sortnet.py - mikrobiblioteka pozwalająca tworzyć formuły CNF implementujące sieci sortujące
Biblioteka pycoSAT
Proszę zrealizować redukcję problemu VertexCover do SAT. Najpierw zajmiemy się elementarną częścią redukcji, a potem zrealizujemy funkcję progową.
Mamy graf G = (V,E), gdzie V = { v1, …, vn } oraz pytamy, czy istnieje pokrycie wierzchołkowe wykorzystujące k wierzchołków. Dla każdego wierzchołka vi tworzymy zmienną xi, której wartość interpretujemy następująco:
Każda krawędź musi być pokryta przez jakiś wierzchołek. Stąd dla każdej krawędzi { vi,vj } tworzymy klauzulę wymagającą, że co najmniej jeden z jej końców został wybrany:
( xi ∨ xj )
Taka redukcja wystarcza, żeby solwer SAT znalazł jakieś pokrycie wierzchołkowe, ale nic go nie zmusza do minimalizacji liczby użytych wierzchołków.
Trudniejszą częścią redukcji VertexCover do SAT jest wymuszenie, że
najwyżej k zmiennych spośród x1, …, xn
może mieć wartość prawda.
W pierwszym podejściu tworzymy zmienne yi,j, które interpretujemy następująco:
prawda,Wymuszamy poprawną wartość zmiennych następująco. Przede wszystkim zmienne yi,0 są prawdziwe dla wszystkich i, a zmienne y0,j są fałszywe dla wszystkich j > 0:
( y0,0 ) ∧ ( y1,0 ) ∧ … ∧ ( yn,0 ) ∧ (-y0,1 ) ∧ ( -y0,2 ) ∧ … ∧ ( -y0,n )
Następnie dla każdej pary wartości 0 < i,j ≤ n dodajemy następujące implikacje (każda z nich może być zapisana jako pojedyncza klauzula):
( yi-1,j ⇒ yi,j ) ∧ ( ( yi-1,j-1 ∧ xi ) ⇒ yi,j )
Na samym końcu dodajemy pojedynczą klauzulę:
( -yn,k+1 )
która wymusza, że nie używamy więcej niż k wierzchołków do pokrycia.
W tym zadaniu należy odróżnić zmienne xi od zmiennych yi,j. Niestety pycoSAT (oraz inne solwery) stosują prostą numerację zmiennych. W związku z tym konieczne jest tłumaczenie numerów “dwuwymiarowych” na jednowymiarowe. W tym celu przydatna może być poniższa funkcja, która zamienia parę indeksów i, j na jedną, jednoznacznie określoną, liczbę naturalną:
def index( i, j ):
return (i+j)*(i+j+1)/2+i
Jako dane testowe proszę wykorzystać zestaw grafów z poprzedniego laboratorium: graph.zip
Alternatywne rozwiązanie polega na zastosowaniu sieci sortujące (patrz
wykład 6). Sieć sortującą zrealizujemy w oparciu o clasę sorterNet:
class sorterNet:
def __init__( self, start, lines, equiv ):
# stwórz sieć sortującą
# start -- numer pierwszej dostępnej zmiennej
# lines -- tablica numerów zmiennych, które są sortowane
# equiv -- czy komparatory mają być realizowane jako równoważności,
czy jako implikacje
def comp( self, i, j ):
# dodaj komparator między liniami i oraz j
# większa wartość wędruje do linii o niższym numerze
def getCNF( self )
# odczytaj formułę realizującą zadane komparatory
def getLines( self ):
# odczytaj obecne numery zmiennych przechowujących kolejne linie
Sprawdź, czy powyższa klasa działa poprawnie. Stwórz formułę, która
zmiennym 1, 2 i 3 przypisuje—na przykład—wartości False, True,
False, stwórz obiekt sorterNet dla lines = [1,2,3] (oraz
używającej równoważności) i dodaj komparatory (1,2), (2,3),
(1,2). Przekonaj się, że połączenie Twojej formuły oraz tej, którą
zwraca sorterNet poprawnie sortuje zmienne 1,2,3 (żeby to sprawdzić,
musisz wywołać getLines po dodaniu komparatorów, żeby wiedzieć jaka
zmienna realizuje jaką linię).
Najprostszą siecią sortującą jest sieć realizująca sortowanie przez wstawianie. W powyższym zadaniu stworzyliśmy taką sięc dla trzech linii. Można to łatwo uogólnić do sieci dla dowolnej liczby linii.
Wykład 6 zawiera także opis sieci opartej o MergeSort.
Proszę połączyć sieci sortujące z poprzedniego punktu z redukcją z VertexCover. Najpierw wykonujemy elementarną część redukcji, która używa zmiennych od 1 do n do opisu wykorzystania wierzchołków. Następnie dodajemy sieć sortującą, która traktuje te zmienne jako linie wejściowe. Na koniec wymuszamy, że k+1-sza linia ma wartość False
Przedstawione metody kodowania warunku liczności nie są optymalne. Istnieje wiele lepszych metod, z których jedna jest opisana w pracy: