#!/usr/bin/python3 import os, sys, subprocess # list of strings: clauses=[] # k=tuple: ("high-level" variable name, number of bit (0..4)) # v=variable number in CNF vars={} vars_last=1 def read_lines_from_file (fname): f=open(fname) new_ar=[item.rstrip() for item in f.readlines()] f.close() return new_ar def run_minisat (CNF_fname): child = subprocess.Popen(["minisat", CNF_fname, "results.txt"], stdout=subprocess.PIPE) child.wait() # 10 is SAT, 20 is UNSAT if child.returncode==20: os.remove ("results.txt") return None if child.returncode!=10: print ("(minisat) unknown retcode: ", child.returncode) exit(0) solution=read_lines_from_file("results.txt")[1].split(" ") os.remove ("results.txt") return solution def write_CNF(fname, clauses, VARS_TOTAL): f=open(fname, "w") f.write ("p cnf "+str(VARS_TOTAL)+" "+str(len(clauses))+"\n") [f.write(c+" 0\n") for c in clauses] f.close() def mathematica_to_CNF (s, d): for k in d.keys(): s=s.replace(k, d[k]) s=s.replace("!", "-").replace("||", " ").replace("(", "").replace(")", "") s=s.split ("&&") return s def add_popcnt1(v1, v2, v3, v4, v5): global clauses s="(!a||!b)&&" \ "(!a||!c)&&" \ "(!a||!d)&&" \ "(!a||!e)&&" \ "(!b||!c)&&" \ "(!b||!d)&&" \ "(!b||!e)&&" \ "(!c||!d)&&" \ "(!c||!e)&&" \ "(!d||!e)&&" \ "(a||b||c||d||e)" clauses=clauses+mathematica_to_CNF(s, {"a":v1, "b":v2, "c":v3, "d":v4, "e":v5}) def add_right_or_left (n1, n2): global clauses s="(!a1||!b1)&&(!a1||!c1)&&(!a1||!d1)&&(!a1||!e1)&&(a1||b1||c1||d1||e1)&&(!a2||b1)&&" \ "(!a2||!b2)&&(!a2||!c2)&&(!a2||!d2)&&(!a2||!e2)&&(a2||b2||c1||c2||d1||e1)&&(a2||b2||c2||d1||d2)&&" \ "(a2||b2||c2||d2||e2)&&(!b1||!b2)&&(!b1||!c1)&&(!b1||!d1)&&(!b1||!e1)&&(b1||b2||c1||d1||e1)&&" \ "(!b2||!c2)&&(!b2||!d1)&&(!b2||!d2)&&(!b2||!e1)&&(!b2||!e2)&&(!c1||!c2)&&(!c1||!d1)&&(!c1||!e1)&&" \ "(!c2||!d2)&&(!c2||!e1)&&(!c2||!e2)&&(!d1||!d2)&&(!d1||!e1)&&(!d2||!e2)" clauses=clauses+mathematica_to_CNF(s, { "a1": vars[(n1,0)], "b1": vars[(n1,1)], "c1": vars[(n1,2)], "d1": vars[(n1,3)], "e1": vars[(n1,4)], "a2": vars[(n2,0)], "b2": vars[(n2,1)], "c2": vars[(n2,2)], "d2": vars[(n2,3)], "e2": vars[(n2,4)]}) def add_right (n1, n2): global clauses s="(!a1||!b1)&&(!a1||!c1)&&(!a1||!d1)&&(a1||b1||c1||d1)&&!a2&&(!b1||!b2)&&(!b1||!c1)&&(!b1||!d1)&&" \ "(b1||b2||c1||d1)&&(!b2||!c1)&&(!b2||!c2)&&(!b2||!d1)&&(!b2||!d2)&&(!b2||!e2)&&(b2||c1||c2||d1)&&" \ "(b2||c2||d1||d2)&&(b2||c2||d2||e2)&&(!c1||!c2)&&(!c1||!d1)&&(!c2||!d1)&&(!c2||!d2)&&(!c2||!e2)&&" \ "(!d1||!d2)&&(!d2||!e2)&&!e1" clauses=clauses+mathematica_to_CNF(s, { "a1": vars[(n1,0)], "b1": vars[(n1,1)], "c1": vars[(n1,2)], "d1": vars[(n1,3)], "e1": vars[(n1,4)], "a2": vars[(n2,0)], "b2": vars[(n2,1)], "c2": vars[(n2,2)], "d2": vars[(n2,3)], "e2": vars[(n2,4)]}) # n=1..5 def add_eq_var_n (name, n): global clauses global vars for i in range(5): if i==n-1: clauses.append(vars[(name,i)]) # always True else: clauses.append("-"+vars[(name,i)]) # always False def alloc_distinct_variables(names): global vars global vars_last for name in names: for i in range(5): vars[(name,i)]=str(vars_last) vars_last=vars_last+1 add_popcnt1(vars[(name,0)], vars[(name,1)], vars[(name,2)], vars[(name,3)], vars[(name,4)]) # make them distinct: for i in range(5): clauses.append(vars[(names[0],i)] + " " + vars[(names[1],i)] + " " + vars[(names[2],i)] + " " + vars[(names[3],i)] + " " + vars[(names[4],i)]) def add_eq_clauses(var1, var2): global clauses clauses.append(var1 + " -" + var2) clauses.append("-"+var1 + " " + var2) def add_eq (n1, n2): for i in range(5): add_eq_clauses(vars[(n1,i)], vars[(n2, i)]) def print_vars(clause): for c in clause: if c.startswith("-")==False and c!="0": # https://stackoverflow.com/questions/8023306/get-key-by-value-in-dictionary t=list(vars.keys())[list(vars.values()).index(c)] print (t[0], t[1]+1) alloc_distinct_variables(["Yellow", "Blue", "Red", "Ivory", "Green"]) alloc_distinct_variables(["Norwegian", "Ukrainian", "Englishman", "Spaniard", "Japanese"]) alloc_distinct_variables(["Water", "Tea", "Milk", "OrangeJuice", "Coffee"]) alloc_distinct_variables(["Kools", "Chesterfield", "OldGold", "LuckyStrike", "Parliament"]) alloc_distinct_variables(["Fox", "Horse", "Snails", "Dog", "Zebra"]) # 2.The Englishman lives in the red house. add_eq("Englishman","Red") # 3.The Spaniard owns the dog. add_eq("Spaniard","Dog") # 4.Coffee is drunk in the green house. add_eq("Coffee","Green") # 5.The Ukrainian drinks tea. add_eq("Ukrainian","Tea") # 6.The green house is immediately to the right of the ivory house. add_right("Ivory", "Green") # 7.The Old Gold smoker owns snails. add_eq("OldGold","Snails") # 8.Kools are smoked in the yellow house. add_eq("Kools","Yellow") # 9.Milk is drunk in the middle house. add_eq_var_n("Milk",3) # i.e., 3rd house # 10.The Norwegian lives in the first house. add_eq_var_n("Norwegian",1) # 11.The man who smokes Chesterfields lives in the house next to the man with the fox. add_right_or_left("Chesterfield","Fox") # left or right # 12.Kools are smoked in the house next to the house where the horse is kept. add_right_or_left("Kools","Horse") # left or right # 13.The Lucky Strike smoker drinks orange juice. add_eq("LuckyStrike","OrangeJuice") # 14.The Japanese smokes Parliaments. add_eq("Japanese","Parliament") # 15.The Norwegian lives next to the blue house. add_right_or_left("Norwegian","Blue") # left or right write_CNF("1.cnf", clauses, vars_last-1) solution=run_minisat ("1.cnf") print_vars(solution)