symbolicOutput(0). % set to 1 to see symbolic output only; 0 otherwise. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % To use this prolog template for other optimization problems, replace the code parts 1,2,3,4 below. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Find the minimal number of colors needed to color a graph. %% Example input: numNodes(15). adjacency(1, [ 2, 5, 9,10,11,12,13,14,15]). adjacency(2, [1, 6,7, 9, 11, 15]). adjacency(3, [1,2, 4,5, 7,8, 10, 12, 14 ]). adjacency(4, [1,2,3, 6,7, 9, 11, 13, 15]). adjacency(5, [1,2,3,4, 7,8,9 ]). adjacency(6, [1, 3, 15]). adjacency(7, [ 2, 5, 10, 14 ]). adjacency(8, [ 6,7, 9,10, 13 ]). adjacency(9, [ 3,4,5, 7,8, 11 ]). adjacency(10,[ 4, 12, 15]). adjacency(11,[1, 4, 6,7, 10, 13,14 ]). adjacency(12,[ 2, 4,5, 11, 13,14,15]). adjacency(13,[1, 3,4,5, 12, 14,15]). adjacency(14,[ 7,8, 10, 12,13 ]). adjacency(15,[1,2, 5,6, 8,9, 12,13,14 ]). %%%%%% Some helpful definitions to make the code cleaner: node(I):- numNodes(N), between(1,N,I). edge(I,J):- adjacency(I,L), member(J,L). color(C):- numNodes(N), between(1,N,C). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 1.- Declare SAT variables to be used % x(I,C) meaning "node I has color C" satVariable( x(I,C) ):- node(I), color(C). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 2. This predicate writeClauses(MaxCost) generates the clauses that guarantee that % a solution with cost at most MaxCost is found writeClauses(infinite):- !, numNodes(N), writeClauses(N),!. writeClauses(MaxColors):- eachNodeExactlyOnecolor(MaxColors), noAdjacentNodesWithSameColor(MaxColors), true,!. writeClauses(_):- told, nl, write('writeClauses failed!'), nl,nl, halt. eachNodeExactlyOnecolor(MaxColors):- node(I), findall(x(I,C), between(1,MaxColors,C), Lits ), exactly(1,Lits), fail. eachNodeExactlyOnecolor(_). noAdjacentNodesWithSameColor(MaxColors):- edge(I,J), between(1,MaxColors,C), writeClause([ -x(I,C), -x(J,C) ]), fail. noAdjacentNodesWithSameColor(_). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 3. This predicate displays a given solution M: displaySol(M):- node(I), member(x(I,C),M), write(I-C), write(' '), fail. displaySol(_):- nl,!. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % 4. This predicate computes the cost of a given solution M: % Here the sort predicate is used to remove repeated elements of the list: costOfThisSolution(M,Cost):- findall(C,member(x(_,C),M),L), sort(L,L1), length(L1,Cost), !. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % No need to modify anything below this line: main:- symbolicOutput(1), !, writeClauses(infinite), halt. % print the clauses in symbolic form and halt main:- told, write('Looking for initial solution with arbitrary cost...'), nl, initClauseGeneration, tell(clauses), writeClauses(infinite), told, tell(header), writeHeader, told, numVars(N), numClauses(C), write('Generated '), write(C), write(' clauses over '), write(N), write(' variables. '),nl, shell('cat header clauses > infile.cnf',_), write('Launching picosat...'), nl, shell('picosat -v -o model infile.cnf', Result), % if sat: Result=10; if unsat: Result=20. treatResult(Result,[]),!. treatResult(20,[] ):- write('No solution exists.'), nl, halt. treatResult(20,BestModel):- nl,costOfThisSolution(BestModel,Cost), write('Unsatisfiable. So the optimal solution was this one with cost '), write(Cost), write(':'), nl, displaySol(BestModel), nl,nl,halt. treatResult(10,_):- % shell('cat model',_), nl,write('Solution found '), flush_output, see(model), symbolicModel(M), seen, costOfThisSolution(M,Cost), write('with cost '), write(Cost), nl,nl, displaySol(M), Cost1 is Cost-1, nl,nl,nl,nl,nl, write('Now looking for solution with cost '), write(Cost1), write('...'), nl, initClauseGeneration, tell(clauses), writeClauses(Cost1), told, tell(header), writeHeader, told, numVars(N),numClauses(C), write('Generated '), write(C), write(' clauses over '), write(N), write(' variables. '),nl, shell('cat header clauses > infile.cnf',_), write('Launching picosat...'), nl, shell('picosat -v -o model infile.cnf', Result), % if sat: Result=10; if unsat: Result=20. treatResult(Result,M),!. treatResult(_,_):- write('cnf input error. Wrote something strange in your cnf?'), nl,nl, halt. initClauseGeneration:- %initialize all info about variables and clauses: retractall(numClauses( _)), retractall(numVars( _)), retractall(varNumber(_,_,_)), assert(numClauses( 0 )), assert(numVars( 0 )), !. writeClause([]):- symbolicOutput(1),!, nl. writeClause([]):- countClause, write(0), nl. writeClause([Lit|C]):- w(Lit), writeClause(C),!. w(-Var):- symbolicOutput(1), satVariable(Var), write(-Var), write(' '),!. w( Var):- symbolicOutput(1), satVariable(Var), write( Var), write(' '),!. w(-Var):- satVariable(Var), var2num(Var,N), write(-), write(N), write(' '),!. w( Var):- satVariable(Var), var2num(Var,N), write(N), write(' '),!. w( Lit):- told, write('ERROR: generating clause with undeclared variable in literal '), write(Lit), nl,nl, halt. % given the symbolic variable V, find its variable number N in the SAT solver: var2num(V,N):- hash_term(V,Key), existsOrCreate(V,Key,N),!. existsOrCreate(V,Key,N):- varNumber(Key,V,N),!. % V already existed with num N existsOrCreate(V,Key,N):- newVarNumber(N), assert(varNumber(Key,V,N)), !. % otherwise, introduce new N for V writeHeader:- numVars(N),numClauses(C), write('p cnf '),write(N), write(' '),write(C),nl. countClause:- retract( numClauses(N0) ), N is N0+1, assert( numClauses(N) ),!. newVarNumber(N):- retract( numVars( N0) ), N is N0+1, assert( numVars(N) ),!. % Getting the symbolic model M from the output file: symbolicModel(M):- get_code(Char), readWord(Char,W), symbolicModel(M1), addIfPositiveInt(W,M1,M),!. symbolicModel([]). addIfPositiveInt(W,L,[Var|L]):- W = [C|_], between(48,57,C), number_codes(N,W), N>0, varNumber(_,Var,N),!. addIfPositiveInt(_,L,L). readWord( 99,W):- repeat, get_code(Ch), member(Ch,[-1,10]), !, get_code(Ch1), readWord(Ch1,W),!. % skip line starting w/ c readWord(115,W):- repeat, get_code(Ch), member(Ch,[-1,10]), !, get_code(Ch1), readWord(Ch1,W),!. % skip line starting w/ s readWord(-1,_):-!, fail. %end of file readWord(C,[]):- member(C,[10,32]), !. % newline or white space marks end of word readWord(Char,[Char|W]):- get_code(Char1), readWord(Char1,W), !. :-dynamic(varNumber / 3). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Express that Var is equivalent to the disjunction of Lits: expressOr( Var, Lits) :- symbolicOutput(1), write( Var ), write(' <--> or('), write(Lits), write(')'), nl, !. expressOr( Var, Lits ):- member(Lit,Lits), negate(Lit,NLit), writeClause([ NLit, Var ]), fail. expressOr( Var, Lits ):- negate(Var,NVar), writeClause([ NVar | Lits ]),!. % Express that Var is equivalent to the conjunction of Lits: expressAnd( Var, Lits) :- symbolicOutput(1), write( Var ), write(' <--> and('), write(Lits), write(')'), nl, !. expressAnd( Var, Lits):- member(Lit,Lits), negate(Var,NVar), writeClause([ NVar, Lit ]), fail. expressAnd( Var, Lits):- findall(NLit, (member(Lit,Lits), negate(Lit,NLit)), NLits), writeClause([ Var | NLits]), !. %%%%%% Cardinality constraints on arbitrary sets of literals Lits: exactly(K,Lits):- symbolicOutput(1), write( exactly(K,Lits) ), nl, !. exactly(K,Lits):- atLeast(K,Lits), atMost(K,Lits),!. atMost(K,Lits):- symbolicOutput(1), write( atMost(K,Lits) ), nl, !. atMost(K,Lits):- % l1+...+ln <= k: in all subsets of size k+1, at least one is false: negateAll(Lits,NLits), K1 is K+1, subsetOfSize(K1,NLits,Clause), writeClause(Clause),fail. atMost(_,_). atLeast(K,Lits):- symbolicOutput(1), write( atLeast(K,Lits) ), nl, !. atLeast(K,Lits):- % l1+...+ln >= k: in all subsets of size n-k+1, at least one is true: length(Lits,N), K1 is N-K+1, subsetOfSize(K1, Lits,Clause), writeClause(Clause),fail. atLeast(_,_). negateAll( [], [] ). negateAll( [Lit|Lits], [NLit|NLits] ):- negate(Lit,NLit), negateAll( Lits, NLits ),!. negate( -Var, Var):-!. negate( Var, -Var):-!. subsetOfSize(0,_,[]):-!. subsetOfSize(N,[X|L],[X|S]):- N1 is N-1, length(L,Leng), Leng>=N1, subsetOfSize(N1,L,S). subsetOfSize(N,[_|L], S ):- length(L,Leng), Leng>=N, subsetOfSize( N,L,S). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%