This shows you the differences between two versions of the page.
pp:25:teme:haskell-sat [2025/04/27 09:47] mihnea.muraru [Haskell: SAT Solving] |
pp:25:teme:haskell-sat [2025/05/08 23:34] (current) mihnea.muraru [Haskell: SAT Solving] |
||
---|---|---|---|
Line 2: | Line 2: | ||
* Data publicării: 09.04.2025 | * Data publicării: 09.04.2025 | ||
- | * Data ultimei modificări: 27.04.2025 | + | * Data ultimei modificări: 08.05.2025 |
* Deadline hard: ziua laboratorului 10 | * Deadline hard: ziua laboratorului 10 | ||
* [[https://curs.upb.ro/2024/mod/forum/view.php?id=152382|Forum]] | * [[https://curs.upb.ro/2024/mod/forum/view.php?id=152382|Forum]] | ||
Line 131: | Line 131: | ||
O altă situație interesantă vizează **literalii puri**. Din moment ce complementul unui literal nu apare în formulă, este întotdeauna avantajos să asumăm acel literal, fără teama inducerii vreunui conflict. La fel ca la clauzele unitare, eliminarea unui literal pur poate genera **noi literali puri**, deci prelucrarea trebuie **repetată**. De exemplu, în formula ''(x1 ∨ x2) ∧ (¬x2 ∨ x3) ∧ (¬x2 ∨ ¬x3)'', nu există clauze unitare, dar există un singur literal pur, ''x1''. Prin eliminarea lui, se obține formula ''(¬x2 ∨ x3) ∧ (¬x2 ∨ ¬x3)'', în care apare literalul pur ''¬x2'', care nu era pur în formula originală. Eliminându-l și pe acesta, se obține formula vidă. | O altă situație interesantă vizează **literalii puri**. Din moment ce complementul unui literal nu apare în formulă, este întotdeauna avantajos să asumăm acel literal, fără teama inducerii vreunui conflict. La fel ca la clauzele unitare, eliminarea unui literal pur poate genera **noi literali puri**, deci prelucrarea trebuie **repetată**. De exemplu, în formula ''(x1 ∨ x2) ∧ (¬x2 ∨ x3) ∧ (¬x2 ∨ ¬x3)'', nu există clauze unitare, dar există un singur literal pur, ''x1''. Prin eliminarea lui, se obține formula ''(¬x2 ∨ x3) ∧ (¬x2 ∨ ¬x3)'', în care apare literalul pur ''¬x2'', care nu era pur în formula originală. Eliminându-l și pe acesta, se obține formula vidă. | ||
- | De remarcat că cele două prelucrări de mai sus pot **interacționa**: satisfacerea unei clauze unitare poate introduce nu numai alte clauze unitare, ci și literali puri, iar eliminarea unui literal pur poate introduce nu numai alți literali puri, ci și clauze unitare. În cazul în care sunt disponibile ambele opțiuni, se preferă **satisfacerea clauzelor unitare mai întâi**, întrucât ele pot conduce la **conflicte**, și este de dorit evidențierea **cât mai timpurie** a acestora, pentru a scuti alt efort de calcul care oricum nu ar împiedica generarea unui conflict. Numai dacă formula nu conține **nici clauze unitare**, și **nici literali puri**, are rost să recurgem la **asumpții oarecare**. | + | De remarcat că cele două prelucrări de mai sus pot **interacționa**: satisfacerea unei clauze unitare poate introduce nu numai alte clauze unitare, ci și literali puri. În cazul în care sunt disponibile ambele opțiuni, se preferă **satisfacerea clauzelor unitare mai întâi**, întrucât ele pot conduce la **conflicte**, și este de dorit evidențierea **cât mai timpurie** a acestora, pentru a scuti alt efort de calcul care oricum nu ar împiedica generarea unui conflict. Numai dacă formula nu conține **nici clauze unitare**, și **nici literali puri**, are rost să recurgem la **asumpții oarecare**. |
În exemplul final, demonstrăm toate cele trei tipuri de prelucrări pe formula ''(¬x1 ∨ x4) ∧ (x1 ∨ x2 ∨ ¬x3) ∧ (¬x2 ∨ x3 ∨ ¬x4)'': | În exemplul final, demonstrăm toate cele trei tipuri de prelucrări pe formula ''(¬x1 ∨ x4) ∧ (x1 ∨ x2 ∨ ¬x3) ∧ (¬x2 ∨ x3 ∨ ¬x4)'': | ||
Line 168: | Line 168: | ||
În plus, se impune operarea direct pe reprezentările de mulțimi sau tablouri ale clauzelor, formulelor și interpretărilor. **Conversiile intermediare la liste și înapoi la mulțimi sau tablouri atrag depunctarea totală a funcțiilor implementate în acest fel!** | În plus, se impune operarea direct pe reprezentările de mulțimi sau tablouri ale clauzelor, formulelor și interpretărilor. **Conversiile intermediare la liste și înapoi la mulțimi sau tablouri atrag depunctarea totală a funcțiilor implementate în acest fel!** | ||
+ | ===== Etapa 3 ===== | ||
+ | |||
+ | Etapa 2 a abordat situațiile în care o formulă poate fi satisfăcută mergând numai **„înainte”**, alegând în fiecare pas unul dintre cele trei tipuri de acțiuni de eliminare, în această ordine, a clauzelor unitare, a literalilor puri și a literalilor oarecare. Rămâne de stabilit cum trebuie procedat când, la un moment dat, eliminarea unui literal produce o **clauză vidă** (**conflict**). | ||
+ | |||
+ | De exemplu, plecând de la formula ''(¬x7 ∨ x1) ∧ (¬x6 ∨ ¬x2 ∨ x1) ∧ (¬x5 ∨ x1) ∧ (¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1 ∨ x6) ∧ (¬x2 ∨ x5 ∨ x7) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2)'', se aplică mai întâi următoarea secvență de acțiuni, conform celor discutate în etapa 2: | ||
+ | |||
+ | - Cum nu există clauze unitare sau literali puri, se decide mai întâi eliminarea **literalului oarecare** ''¬x7'', care produce formula: ''(¬x6 ∨ ¬x2 ∨ x1) ∧ (¬x5 ∨ x1) ∧ (¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1 ∨ x6) ∧ (¬x2 ∨ x5) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2)''. | ||
+ | - Similar, eliminarea **literalului oarecare** ''¬x6'' produce formula: ''(¬x5 ∨ x1) ∧ (¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1) ∧ (¬x2 ∨ x5) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2)''. | ||
+ | - Apoi, eliminarea **literalului oarecare** ''¬x5'' produce formula: ''(¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1) ∧ (¬x2) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2)''. | ||
+ | - În acest moment se evidențiază **clauza unitară** ''(¬x2)'', care conduce prin eliminare la formula: ''(¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x1) ∧ (x1)''. | ||
+ | - Mai departe, oricare dintre **clauzele unitare** ''(¬x1)'' sau ''(x1)'' ar fi eliminate, s-ar obține o **clauză vidă** (**conflict**). | ||
+ | |||
+ | Ce ar trebui făcut în acest caz? Intuitiv, ar trebui revenit prin **//backtracking//** la o asumpție anterioară și explorat opusul ei. Dar acest lucru ridică o altă întrebare: la ce punct decizional ar trebui să ne întoarcem? Evident, **clauzele unitare impun alegerea**, și nu ar avea sens să încercăm opusul; de exemplu, revenirea la asumpția ''¬x2'' (pasul 4 de mai sus), dictată de clauza unitară ''(¬x2)'', și explorarea variantei ''x2'' ar fi inutile. Prin urmare, ar trebui să revenim la cel mai recent punct decizional în care există o **alternativă viabilă**; de exemplu, am putea reveni la asumpția ''¬x5'' (pasul 3), arbitrară, și explora asumpția ''x5''. Din păcate, și această cale ar conduce la conflict, și ar trebui revenit un nivel și mai sus, la asumpția ''¬x6'' (pasul 2) etc. | ||
+ | |||
+ | Se ridică următoarea întrebare interesantă: am putea oare reveni într-un **punct mai precis din trecut**, astfel încât **să evităm** încercarea măcar a câtorva alternative care în final ar conduce **tot la conflict**? În exemplul de mai sus, atât secvența inițială ''¬x7 - ¬x6 - ¬x5'', cât și secvența alternativă ''¬x7 - ¬x6 - x5'' au condus la conflicte. Puteam să o evităm cu totul pe a doua? | ||
+ | |||
+ | La baza răspunsului stă observația că un conflict poate fi indus de fapt **timpuriu**, printr-o anumită **combinație de asumpții incompatibile** realizate în trecut, dar manifestat **târziu**. În exemplul de mai sus, combinația de asumpții incompatibile este ''{¬x7, ¬x5}'', care conduce la clauza unitară ''(¬x2)'', provenită din clauza originală ''(¬x2 ∨ x5 ∨ x7)''. Eliminarea acesteia conduce pe ambele căi de mai sus la conflicte. Prin urmare, ar trebui revenit într-un **punct cât mai distant din trecut** în care incompatibilitatea să fie **înlăturată**, astfel încât toate asumpțiile făcute ulterior asupra altor variabile să se armonizeze cu modificarea realizată. Acest lucru nu previne complet apariția altor conflicte pe măsură ce se realizează noi asumpții, dar **reduce spațiul de căutare**. | ||
+ | |||
+ | Remarcăm că, din moment ce **clauzele unitare** impun literalii eliminați, ca urmare a asumpțiilor anterioare, ei **nu vor apărea** în combinația conflictuală („nu aveam de ales decât să-i eliminăm”). Ne interesează să identificăm literalii pentru care asumpția opusă era **viabilă**. | ||
+ | |||
+ | Evident că rămân **întrebările**: | ||
+ | |||
+ | - Cum determinăm **asumpțiile incompatibile**? | ||
+ | - Cum determinăm **punctul în care ne întoarcem**? | ||
+ | |||
+ | ==== Învățarea de noi clauze ==== | ||
+ | |||
+ | Pentru a răspunde la **întrebarea (1)**, introducem mai întâi conceptul de **rezoluție a două clauze**, care va fi prezentat formal în capitolul de programare logică al cursului. Pentru temă, este suficient să o definim astfel: dacă o clauză ''C1'' conține literalul ''xi'', iar o clauză ''C2'', complementul său, ''¬xi'' (sau viceversa), cele două clauze pot fi **rezolvate în raport cu literalii respectivi**, obținând o nouă clauză, ''R'' (rezolvent), care conține toți literalii celor două clauze, mai puțin ''xi'' și ''¬xi''. În plus, din punct de vedere logic, avem că ''(C1 ∧ C2) ⟹ R'', adică ''R'' este adevărat ori de câte ori ''C1'' și ''C2'' sunt. | ||
+ | |||
+ | De exemplu: | ||
+ | |||
+ | * Clauzele ''(x1 ∨ x2)'' și ''(¬x1 ∨ x3 ∨ x4)'' pot fi **rezolvate** în raport cu variabila ''x1'', producând clauza ''(x2 ∨ x3 ∨ x4)''. | ||
+ | * Clauzele ''(x1 ∨ x2)'' și ''(x1 ∨ x3 ∨ x4)'' **nu pot fi rezolvate** în raport cu nicio variabilă, întrucât nu există niciun literal din prima clauză al cărui complement să apară în a doua. | ||
+ | |||
+ | Utilizând rezoluția, introducem următorul algoritm de **determinare a asumpțiilor incompatibile**: | ||
+ | |||
+ | - În momentul obținerii unei **clauze vide**, se determină **clauza originală** din care aceasta a provenit, care servește drept **clauză curentă**. | ||
+ | - Se parcurge istoricul de asumpții realizate în **sens anticronologic** (prezent-trecut) și: | ||
+ | - Dacă acțiunea curentă este de eliminare a unei **clauze unitare** (''Unit''), **rezolvăm** (dacă este posibil, conform definiției de mai sus a rezoluției) clauza stocată în acțiune cu clauza curentă, în raport cu literalul stocat de asemenea în acțiune, rezultând o nouă clauză curentă. | ||
+ | - Dacă acțiunea curentă este de altă natură, clauză curentă rămâne neschimbată. | ||
+ | - Varianta finală a clauzei curente rezumă **motivul conflictului**. | ||
+ | |||
+ | Rezolvând doar în raport cu literalii impuși de eliminarea **clauzelor unitare**, obținem efectul dorit ca aceștia să **nu apară** în combinația de asumpții incompatibile. Nevoia de operare pe clauzele originale justifică **formulele extinse** din etapa 2. | ||
+ | |||
+ | Să aplicăm algoritmul pe exemplul de mai sus, operând de data aceasta în **ordine inversă**, dinspre pasul 5, care a generat conflictul, către pasul 1: | ||
+ | |||
+ | * 5. Presupunând că s-a decis eliminarea **clauzei unitare** ''(¬x1)'', care a condus la **vidarea** clauzei ''(x1)'' (**conflict**), se determină clauza originală din care s-a obținut această **clauză vidă**, și anume ''(x1 ∨ x2)'', care devine **clauza curentă**. Apoi, începe consultarea acțiunilor realizate deja, conform algoritmului de mai sus. Mai întâi, se analizează cea mai recentă acțiune, de eliminare a clauzei unitare ''(¬x1)'', care stochează în reprezentarea sa literalul ''¬x1'' și clauza originală din care s-a obținut această **clauză unitară**, și anume ''(¬x1 ∨ x2)''. **Rezolvând-o** pe aceasta cu clauza curentă ''(x1 ∨ x2)'' în raport cu literalul ''¬x1'', se obține rezolventul ''(x2)'', reprezentând **noua clauză curentă**. | ||
+ | * 4. Revenind încă un pas în trecut, se întâlnește acțiunea de eliminare a clauzei unitare ''(¬x2)'', care stochează în reprezentarea sa literalul ''¬x2'' și clauza originală din care s-a obținut această **clauză unitară**, și anume ''(¬x2 ∨ x5 ∨ x7)''. **Rezolvând-o** pe aceasta cu clauza curentă ''(x2)'' în raport cu literalul ''¬x2'', se obține rezolventul ''(x5 ∨ x7)'', reprezentând **noua clauză curentă**. | ||
+ | * 3, 2, 1. Acțiunile rămase nu sunt de eliminare a unei clauze unitare, și prin urmare clauza curentă rămâne ''(x5 ∨ x7)''. | ||
+ | |||
+ | Pentru a înțelege semnificația acestei clauze, ''(x5 ∨ x7)'', să ne amintim **combinația de asumpții incompatibile** depistată mai sus, ''{¬x7, ¬x5}'', conform căreia ambele variabile, ''x7'' și ''x5'', au fost asumate false. Pentru a depăși incompatibilitatea, **cel puțin una dintre variabile** trebuie asumată adevărată, adică exact ce **codifică clauza** ''(x5 ∨ x7)''. Proprietatea rezolventului afirmă că acesta este întotdeauna adevărat, dacă clauzele pe baza căruia a fost obținut sunt adevărate. Prin urmare, dacă vrem ca clauza vidată ''(x1 ∨ x2)'' și clauzele devenite unitare și eliminate ''(¬x1 ∨ x2)'' și ''(¬x2 ∨ x5 ∨ x7)'' să fie **simultan adevărate** (într-o anumită interpretare), atunci **și clauza** ''(x5 ∨ x7)'' trebuie să fie adevărată (în acea interpretare). Observați cum într-adevăr clauza referă doar variabile asupra cărora s-au realizat **asumpții arbitrare**, ca ''x5'' și ''x7'', nu și variabile pentru care asumpțiile au fost impuse, ca ''x2''. | ||
+ | |||
+ | ''(x5 ∨ x7)'' poartă numele de **//clauză învățată//** (//learned clause//), întrucât nu face parte din formula originală. Dacă **clauza învățată însăși** este **vidă**, înseamnă că este **imposibil** ca clauzele din care a fost obținută să fie adevărate simultan, și deci formula originală este **nesatisfiabilă**. | ||
+ | |||
+ | Pentru a ne asigura că **noua clauză**, ''(x5 ∨ x7)'', nu va mai fi niciodată vidată de asumpțiile realizate, o putem **adăuga la formula originală**, și ne putem întoarce în trecut la **cel mai distant punct** în care această clauză **devine unitară** (funcția ''backtrackToUnitClause'' din etapa 2), astfel încât următoarea acțiune să o **satisfacă imediat**. Revenirea se poate face cu un număr arbitrar de pași în trecut, motiv pentru care poartă numele de **//backtracking necronologic//**. Revenirea la cel mai distant punct, și nu la cel mai recent, scade probabilitatea de eșec al căii curente, datorat altor asumpții realizate pe anterioara cale eșuată. Astfel, răspundem și la **întrebarea (2)**. Remarcăm că, din moment ce rezolventul derivă logic din anumite clauze ale formulei originale, formula rezultată prin adăugarea clauzei învățate la formula originală este **echivalentă** cu formula originală. | ||
+ | |||
+ | În exemplul de mai sus, se adaugă clauza învățată ''(x5 ∨ x7)'' la formula originală, obținându-se formula ''(¬x7 ∨ x1) ∧ (¬x6 ∨ ¬x2 ∨ x1) ∧ (¬x5 ∨ x1) ∧ (¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1 ∨ x6) ∧ (¬x2 ∨ x5 ∨ x7) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2) ∧ (x5 ∨ x7)'': | ||
+ | |||
+ | - Prin //backtracking necronologic// se revine la pasul 1, imediat după eliminarea literalului ''¬x7'', în urma căreia **clauza învățată**, ''(x5 ∨ x7)'', **devine unitară**, ''(x5)''. Noua formulă este ''(¬x6 ∨ ¬x2 ∨ x1) ∧ (¬x5 ∨ x1) ∧ (¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1 ∨ x6) ∧ (¬x2 ∨ x5) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2) ∧ (x5)''. | ||
+ | - Noul pas 2 este diferit acum, întrucât prezența **clauzei unitare** ''(x5)'' determină eliminarea acesteia și obținerea formulei ''(¬x6 ∨ ¬x2 ∨ x1) ∧ (x1) ∧ (¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ ¬x1 ∨ x6) ∧ (¬x1 ∨ x2) ∧ (x1 ∨ x2)''. Acesta este punctul în care **clauza învățată** își produce efectele. | ||
+ | - Se elimină **clauza unitară** ''(x1)'' și se obține formula ''(¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (¬x2 ∨ x6) ∧ (x2)''. | ||
+ | - Se elimină **clauza unitară** ''(x2)'' și se obține formula ''(¬x4 ∨ x3) ∧ (¬x3 ∨ x4) ∧ (x6)''. | ||
+ | - Se elimină **clauza unitară** ''(x6)'' și se obține formula ''(¬x4 ∨ x3) ∧ (¬x3 ∨ x4)''. | ||
+ | - Se asumă **literalul oarecare** ''¬x4'' și se obține formula ''(¬x3)''. | ||
+ | - Se elimină **clauza unitară** ''(¬x3)'' și se obține **formula vidă**. **SUCCES!** | ||
+ | |||
+ | Observați cum combinația alternativă **neproductivă** menționată mai sus, ''¬x7 - ¬x6 - x5'', a fost **evitată**, prin realizarea de data aceasta a asumpției ''x5'' înaintea asumpției pentru variabila ''x6''. | ||
+ | |||
+ | ==== Algoritmul complet de satisfacere ==== | ||
+ | |||
+ | Am ajuns în punctul în care putem integra toate conceptele discutate până acum, în algoritmul complet de satisfacere: | ||
+ | |||
+ | - Se prelucrează toate **clauzele unitare**. | ||
+ | - Dacă se obține **formula vidă**, formula originală este **SATISFIABILĂ** și se construiește **interpretarea** utilizând istoricul curent. STOP. | ||
+ | - Dacă formula conține **clauza vidă** (**conflict**), se **învață** o nouă clauză. | ||
+ | - Dacă clauza învățată este **vidă**, formula este **NESATISFIABILĂ**. STOP. | ||
+ | - Altfel, se revine în istoric la **cel mai distant punct** în care clauza învățată este **unitară**, și se sare la pasul 1. | ||
+ | - Se prelucrează toți **literalii puri** și se sare la pasul 1. | ||
+ | - Numai dacă nu există literali puri, se asumă un **literal oarecare** și se sare la pasul 1. | ||
+ | |||
+ | ==== Aplicație la problema 3-colorare ==== | ||
+ | |||
+ | Problema **//3-colorare//** a unui **graf neorientat** urmărește asocierea unei culori din trei (''Red'', ''Green'', ''Blue'') fiecărui nod din graf, astfel încât oricare două **noduri adiacente** să fie **colorate diferit**. Problema poate fi rezolvată prin **reducere la SAT**, parcurgând etapele: | ||
+ | |||
+ | - **Codificarea** instanței //3-colorare// într-o formulă CNF. | ||
+ | - **Satisfacerea** formulei, dacă este posibil. | ||
+ | - **Decodificarea** eventualei interpretări care satisface formula pentru a obține colorarea grafului. | ||
+ | |||
+ | Vom întrebuința următoarea schemă de **codificare** a unui graf într-o formulă CNF: | ||
+ | |||
+ | - Fiecărui nod ''n'' din graf îi corespund **trei variabile booleene**, aferente celor trei culori posibile: ''x[n,R]'', ''x[n,G]'', ''x[n,B]''. | ||
+ | - Fiecare nod ''n'' are **cel puțin o culoare**: ''(x[n,R] ∨ x[n,G] ∨ x[n,B])''. | ||
+ | - Fiecare nod ''n'' are **cel mult o culoare**: ''(¬x[n,R] ∨ ¬x[n,G]) ∧ (¬x[n,R] ∨ ¬x[n,B]) ∧ (¬x[n,G] ∨ ¬x[n,B])''. | ||
+ | - Fiecare muchie ''(n, o)'' are **capetele colorate diferit**: ''(¬x[n,R] ∨ ¬x[o,R]) ∧ (¬x[n,G] ∨ ¬x[o,G]) ∧ (¬x[n,B] ∨ ¬x[o,B])''. | ||
+ | |||
+ | Considerăm că **nodurile** grafului sunt reprezentate prin numere naturale mai mari sau egale cu 1, iar **cele trei variabile booleene** aferente unui nod ''n'' sunt reprezentate prin numerele ''10n+1'', ''10n+2'', ''10n+3''. De exemplu, nodului ''1'' îi corespund variabilele ''11'', ''12'', ''13'', nodului ''2'', variabilele ''21'', ''22'', ''23'' etc. Variabila ''11'' corespunde propoziției „Nodul 1 este roșu”, variabila ''22'', propoziției „Nodul 2 este verde”, variabila ''33'', propoziției „Nodul 3 este albastru” etc. | ||
+ | |||
+ | De exemplu, pentru graful neorientat cu nodurile ''{1, 2}'' și muchia ''(1, 2)'', se obține **formula** cu următoarele variabile și clauze (numerele itemilor oglindesc enumerarea de mai sus): | ||
+ | |||
+ | - Variabilele ''11'', ''12'', ''13'', ''21'', ''22'', ''23''. | ||
+ | - Clauzele de tip **cel puțin o culoare**: ''{11, 12, 13}'', ''{21, 22, 23}''. | ||
+ | - Clauzele de tip **cel mult o culoare**: ''{-11, -12}'', ''{-11, -13}'', ''{-12, -13}'', ''{-21, -22}'', ''{-21, -23}'', ''{-22, -23}''. | ||
+ | - Clauzele de tip **capete colorate diferit**: ''{-11, -21}'', ''{-12, -22}'', ''{-13, -23}''. | ||
+ | |||
+ | Pentru întreaga formulă descrisă mai sus, algoritmul de satisfacere ar putea produce **interpretarea** ''{-23, -22, -13, -11, 12, 21}'', care corespunde **colorării** ''{(1, Green), (2, Red)}''. | ||
+ | |||
+ | ==== Precizări ==== | ||
+ | |||
+ | A treia etapă a temei abordează **rezolvarea** clauzelor, **învățarea** clauzelor, algoritmul de **satisfacere** și aplicația la problema **//3-colorare//**. | ||
+ | |||
+ | Construcțiile și mecanismele de limbaj pe care le veți exploata în rezolvare, pe lângă cele din etapele 1 și 2, sunt: | ||
+ | |||
+ | * **polimorfismul ad-hoc** | ||
+ | * **clasele**. | ||
+ | |||
+ | Modulul de interes din **schelet** este ''Solver'', care conține **operațiile** pe care trebuie să le implementați. Găsiți detaliile despre **funcționalitate** și despre **constrângerile de implementare**, precum și **exemple**, direct în fișier. Aveți de completat definițiile care încep cu ''%%*** TODO ***%%''. | ||
+ | |||
+ | Pentru **rularea testelor**, încărcați în interpretor modulul ''TestSolver'' și evaluați ''main''. Ultimul test (''stress'') utilizează grafuri mai mari și execuția poate dura câteva secunde. | ||
+ | |||
+ | Este suficient ca arhiva pentru **vmchecker** să conțină modulele ''Solver'', ''ExtendedFormula'' din etapa 2 și ''Formula'' din etapa 1. | ||
+ | |||
+ | **Depunctările** urmează aceleași principii ca în primele două etape. | ||
===== Resurse ===== | ===== Resurse ===== | ||
* {{:pp:25:teme:haskell:etapa1.zip|Schelet etapa 1}} | * {{:pp:25:teme:haskell:etapa1.zip|Schelet etapa 1}} | ||
* {{:pp:25:teme:haskell:etapa2.zip|Schelet etapa 2}} | * {{:pp:25:teme:haskell:etapa2.zip|Schelet etapa 2}} | ||
+ | * {{:pp:25:teme:haskell:etapa3.zip|Schelet etapa 3}} | ||
+ | |||
+ | ===== Referințe ===== | ||
+ | |||
+ | * //[[https://www.cs.ox.ac.uk/people/james.worrell/lecture06.pdf|The DPLL Algorithm]]// | ||
===== Changelog ===== | ===== Changelog ===== | ||
+ | * 08.05 (23:30): Publicat testele etapei 3 și actualizat tipurile ''Graph'', ''Color'' și ''ThreeColoring'' din ''Solver.hs'' cu ''deriving (Show, Eq)'' în loc de ''deriving Show''. | ||
+ | * 30.04 (22:30): Publicat etapa 3, momentan fără teste | ||
+ | * 27.04 (09:50): Etapa 2: Actualizat teste ''backtrackToUnitClause'' | ||
* 24.04 (20:05): Publicat etapa 2 | * 24.04 (20:05): Publicat etapa 2 | ||
* 09.04 (12:07): Publicat etapa 1 | * 09.04 (12:07): Publicat etapa 1 | ||
+ |