├── Logic.py ├── README.md └── test.pl /Logic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | name_identity = 1000 4 | def is_variable(term): 5 | if term[0].isupper() or term.startswith('_'): 6 | return True 7 | else: 8 | return False 9 | class Term: 10 | #Constant or variable 11 | def __init__(self, term): 12 | term = term.strip() 13 | self.is_var = is_variable(term) 14 | self.term = term 15 | def __str__(self): 16 | return str(self.term) 17 | 18 | def __eq__(self, cmp): 19 | return self.is_var == cmp.is_var and self.term == cmp.term 20 | 21 | 22 | class Statement: 23 | def __init__(self, list_of_terms = [], predicate = '', negative = False): 24 | self.list_of_terms = [] 25 | for term in list_of_terms: 26 | self.list_of_terms.append(Term(term.term)) 27 | self.predicate = predicate 28 | self.negative = negative 29 | def negate(self): 30 | return Statement(list_of_terms=self.list_of_terms, predicate=self.predicate, negative = not self.negative) 31 | def add_new_term(self,term): 32 | self.list_of_terms.append(term) 33 | def simplify(self): 34 | if len(self.list_of_terms)==1 and isinstance(self.list_of_terms[0],Conjunction): 35 | self.list_of_terms = self.list_of_terms[0].list_of_statements 36 | def __str__(self): 37 | output = self.predicate + '(' 38 | for term in self.list_of_terms: 39 | output += str(term) + ', ' 40 | output = output[:-2]+')' 41 | return output 42 | def __eq__(self, cmp): 43 | equal = True 44 | if len(self.list_of_terms)!=len(cmp.list_of_terms): 45 | return False 46 | else: 47 | for i in range(len(self.list_of_terms)): 48 | if not self.list_of_terms[i] == cmp.list_of_terms[i]: 49 | equal = False 50 | break 51 | return self.predicate == cmp.predicate and self.negative == cmp.negative and equal 52 | def is_identical(self,cmp): 53 | equal = True 54 | if len(self.list_of_terms)!=len(cmp.list_of_terms): 55 | return False 56 | else: 57 | for i in range(len(self.list_of_terms)): 58 | if self.list_of_terms[i].is_var == False and cmp.list_of_terms[i].is_var == False \ 59 | and not self.list_of_terms[i] == cmp.list_of_terms[i]: 60 | equal = False 61 | break 62 | return self.predicate == cmp.predicate and self.negative == cmp.negative and equal 63 | def is_unificable(self,obj): 64 | return self.predicate == obj.predicate and self.negative == obj.negative and \ 65 | len(self.list_of_terms) == len(obj.list_of_terms) 66 | def is_opposite(self,obj): 67 | return self.predicate == obj.predicate and self.negative != obj.negative and \ 68 | len(self.list_of_terms) == len(obj.list_of_terms) 69 | def standardlize_variable(self,bind_dict = {}): 70 | global name_identity 71 | for index, term in enumerate(self.list_of_terms): 72 | if term.is_var: 73 | if not term.term in bind_dict: 74 | name_identity += 1 75 | bind_dict[term.term] = '_' + str(name_identity) 76 | self.list_of_terms[index] = Term(term = bind_dict[term.term]) 77 | return bind_dict 78 | def get_variable_list(self): 79 | res = [] 80 | for term in self.list_of_terms: 81 | if term.is_var: 82 | res.append(term.term) 83 | return res 84 | class Conjunction: 85 | #Conjunction of statements 86 | def __init__(self, list_of_statements = []): 87 | self.list_of_statements = [] 88 | for statement in list_of_statements: 89 | if isinstance(statement,Statement): 90 | self.list_of_statements.append(Statement(predicate = statement.predicate,\ 91 | list_of_terms = statement.list_of_terms, negative = statement.negative)) 92 | elif isinstance(statement,Conjunction): 93 | self.list_of_statements.append(Conjunction(statement.list_of_statements)) 94 | elif isinstance(statement,Disjunction): 95 | self.list_of_statements.append(Disjunction(statement.list_of_statements)) 96 | def negate(self): 97 | new_statements = [] 98 | for statement in self.list_of_statements: 99 | new_statements.append(statement.negate()) 100 | return Disjunction(new_statements) 101 | def add_new_statement(self, statement): 102 | self.list_of_statements.append(statement) 103 | def simplify(self): 104 | index = 0 105 | while index < len(self.list_of_statements): 106 | if isinstance(self.list_of_statements[index],Conjunction): 107 | self.list_of_statements[index].simplify() 108 | l = len(self.list_of_statements[index].list_of_statements) 109 | for i,statement in enumerate(self.list_of_statements[index].list_of_statements): 110 | self.list_of_statements.insert(index + i + 1,statement) 111 | del self.list_of_statements[index] 112 | index+=l-1 113 | elif isinstance(self.list_of_statements[index],Disjunction): 114 | self.list_of_statements[index].simplify() 115 | index+=1 116 | 117 | def __str__(self): 118 | output = '(' 119 | for statement in self.list_of_statements: 120 | output+=str(statement)+' & ' 121 | return output[:-3]+')' 122 | def __eq__(self,cmp): 123 | if len(self.list_of_statements) != len(cmp.list_of_statements): 124 | return False 125 | else: 126 | equal = True 127 | for i in range(len(self.list_of_statements)): 128 | if not self.list_of_statements[i] == cmp.list_of_statements[i]: 129 | equal = False 130 | break 131 | return equal 132 | def split(self): 133 | list_of_new_con = [] 134 | has_dis = False 135 | for index,statement in enumerate(self.list_of_statements): 136 | if isinstance(statement,Disjunction): 137 | has_dis = True 138 | for statement_dis in statement.list_of_statements: 139 | new_list = self.list_of_statements.copy() 140 | new_list[index] = statement_dis 141 | new_conj = Conjunction(new_list) 142 | new_conj.simplify() 143 | list_of_new_con = list_of_new_con + new_conj.split() 144 | break 145 | if has_dis == False: 146 | list_of_new_con.append(self) 147 | return list_of_new_con 148 | 149 | 150 | class Disjunction: 151 | #Disjunction of statements 152 | def __init__(self, list_of_statements = []): 153 | self.list_of_statements = [] 154 | for statement in list_of_statements: 155 | if isinstance(statement,Statement): 156 | self.list_of_statements.append(Statement(predicate = statement.predicate,\ 157 | list_of_terms = statement.list_of_terms, negative = statement.negative)) 158 | elif isinstance(statement,Conjunction): 159 | self.list_of_statements.append(Conjunction(statement.list_of_statements)) 160 | elif isinstance(statement,Disjunction): 161 | self.list_of_statements.append(Disjunction(statement.list_of_statements)) 162 | def negate(self): 163 | new_statements = [] 164 | for statement in self.list_of_statements: 165 | new_statements.append(statement.negate()) 166 | return Conjunction(new_statements) 167 | def add_new_statement(self, statement): 168 | self.list_of_statements.append(statement) 169 | def __str__(self): 170 | output = '(' 171 | for statement in self.list_of_statements: 172 | output+=str(statement)+' ; ' 173 | return output[:-3]+')' 174 | def simplify(self): 175 | index = 0 176 | while index < len(self.list_of_statements): 177 | if isinstance(self.list_of_statements[index],Disjunction): 178 | self.list_of_statements[index].simplify() 179 | l = len(self.list_of_statements[index].list_of_statements) 180 | for i,statement in enumerate(self.list_of_statements[index].list_of_statements): 181 | self.list_of_statements.insert(index + i + 1,statement) 182 | del self.list_of_statements[index] 183 | index+=l-1 184 | elif isinstance(self.list_of_statements[index],Conjunction): 185 | self.list_of_statements[index].simplify() 186 | index+=1 187 | def __eq__(self,cmp): 188 | if len(self.list_of_statements) != len(cmp.list_of_statements): 189 | return False 190 | else: 191 | equal = True 192 | for i in range(len(self.list_of_statements)): 193 | if not self.list_of_statements[i] == cmp.list_of_statements[i]: 194 | equal = False 195 | break 196 | return equal 197 | class Rule: 198 | #Define a rule with left and right hand side are Conjunction of statements 199 | def __init__(self, lhs, rhs = None): 200 | self.lhs = lhs 201 | self.rhs = rhs 202 | self.difference = [] 203 | self.supported = [[] for _ in range(len(rhs.list_of_statements))] 204 | def __str__(self): 205 | return str(self.lhs) + ' <- ' + str(self.rhs) 206 | def split(self): 207 | list_of_new_rules = [] 208 | new_conj = self.rhs.split() 209 | for conj in new_conj: 210 | list_of_new_rules.append(Rule(Statement(list_of_terms=self.lhs.list_of_terms,predicate = self.lhs.predicate, negative = self.lhs.negative),conj)) 211 | return list_of_new_rules 212 | def get_supported_fact(self,list_of_facts): 213 | for index in range(len(self.rhs.list_of_statements)): 214 | for fact in list_of_facts: 215 | if fact.is_unificable(self.rhs.list_of_statements[index]): 216 | self.supported[index].append(fact) 217 | def standardlize_variable(self): 218 | bind_dict = {} 219 | bind_dict = self.lhs.standardlize_variable(bind_dict=bind_dict) 220 | for statement in self.rhs.list_of_statements: 221 | bind_dict = statement.standardlize_variable(bind_dict=bind_dict) 222 | for difference in self.difference: 223 | difference[0].term = bind_dict[difference[0].term] 224 | difference[1].term = bind_dict[difference[1].term] 225 | def list_supported_facts(self): 226 | support_list = [[]] 227 | for fact_list in self.supported: 228 | if len(fact_list) == 0: 229 | return [] 230 | else: 231 | new = [] 232 | for fact in fact_list: 233 | for sublist in support_list: 234 | new_sublist = [] 235 | for statement in sublist: 236 | new_sublist.append(Statement(predicate= statement.predicate, negative = statement.negative,\ 237 | list_of_terms = statement.list_of_terms)) 238 | new_sublist.append(fact) 239 | new.append(new_sublist) 240 | support_list = new 241 | return support_list 242 | def check_difference(self, binding): 243 | differ = True 244 | for difference in self.difference: 245 | if binding.bind(difference[0]) == binding.bind(difference[1]): 246 | differ = False 247 | break 248 | return differ 249 | def eliminate_difference(self): 250 | for index,statement in enumerate(self.rhs.list_of_statements): 251 | if statement.predicate =='\=': 252 | self.difference.append([Term(statement.list_of_terms[0].term),Term(statement.list_of_terms[1].term)]) 253 | del self.rhs.list_of_statements[index] 254 | del self.supported[index] 255 | 256 | 257 | def is_operator(c): 258 | return c=='&' or c == ';' 259 | def is_higher_precedence(a,b): 260 | return a == '&' and b == ';' 261 | 262 | #Change to Reverse Polish Notation 263 | def change_to_RPN(tokens): 264 | operator_stack = [] 265 | output = [] 266 | for token in tokens: 267 | if token == '(': 268 | operator_stack.append(token) 269 | elif token == ')': 270 | operator = operator_stack.pop() 271 | while operator != '(': 272 | output.append(operator) 273 | operator = operator_stack.pop() 274 | elif is_operator(token): 275 | while len(operator_stack)>0 and (operator_stack[-1].endswith('*') or \ 276 | is_higher_precedence(operator_stack[-1],token) or operator_stack[-1]==token) and (operator_stack[-1]!='('): 277 | output.append(operator_stack.pop()) 278 | operator_stack.append(token) 279 | else: 280 | if token.endswith('*'): 281 | operator_stack.append(token) 282 | else: 283 | output.append(token) 284 | operator_stack.reverse() 285 | output = output + operator_stack 286 | return output 287 | 288 | #Process RPN and convert to conjunction 289 | def convert_to_conjunction(tokens): 290 | stack = [] 291 | for token in tokens: 292 | if token.endswith('*'): 293 | operands = stack.pop() 294 | operands = operands.term.split(',') 295 | list_of_terms = [] 296 | for operand in operands: 297 | list_of_terms.append(Term(operand)) 298 | statement = Statement(list_of_terms=list_of_terms,predicate=token[:-1]) 299 | stack.append(statement) 300 | elif is_operator(token): 301 | list_of_statements = [] 302 | list_of_statements.append(stack.pop()) 303 | list_of_statements.append(stack.pop()) 304 | list_of_statements.reverse() 305 | new_operand = [] 306 | if token == '&': 307 | new_operand = Conjunction(list_of_statements=list_of_statements) 308 | elif token == ';': 309 | new_operand = Disjunction(list_of_statements=list_of_statements) 310 | stack.append(new_operand) 311 | else: 312 | stack.append(Term(token)) 313 | output = Conjunction([stack.pop()]) 314 | output.simplify() 315 | return output 316 | 317 | def process_string(s): 318 | #Turn string to list of tokens 319 | tokens = [] 320 | name = '' 321 | for ch in s: 322 | if is_operator(ch) or ch == '(' or ch == ')': 323 | if name!='': 324 | if ch == '(': 325 | name += '*' 326 | tokens.append(name) 327 | name = '' 328 | tokens.append(ch) 329 | elif ch==' ': 330 | continue 331 | else: 332 | name+=ch 333 | rpn = change_to_RPN(tokens) 334 | #print(rpn) 335 | output_line = convert_to_conjunction(rpn) 336 | return output_line 337 | 338 | class Binding: 339 | def __init__(self): 340 | self.binding_dict = {} 341 | self.is_fail = False 342 | def add_new_binding(self, key, value): 343 | #Key and value is term 344 | self.binding_dict[key.term] = value.term 345 | def already_has(self, key): 346 | return key.term in self.binding_dict 347 | def bind(self,x): 348 | if isinstance(x,Term): 349 | if x.term in self.binding_dict: 350 | return Term(self.binding_dict[x.term]) 351 | else: 352 | return x 353 | elif isinstance(x,Statement): 354 | list_of_terms = [] 355 | for term in x.list_of_terms: 356 | list_of_terms.append(self.bind(term)) 357 | return Statement(list_of_terms=list_of_terms,predicate=x.predicate, negative = x.negative) 358 | elif isinstance(x,Conjunction): 359 | list_of_statements = [] 360 | for statement in x.list_of_statements: 361 | list_of_statements.append(self.bind(statement)) 362 | return Conjunction(list_of_statements=list_of_statements) 363 | elif isinstance(x,Disjunction): 364 | list_of_statements = [] 365 | for statement in x.list_of_statements: 366 | list_of_statements.append(self.bind(statement)) 367 | return Disjunction(list_of_statements=list_of_statements) 368 | def merge(self,obj): 369 | res = Binding() 370 | if self.is_fail or obj.is_fail: 371 | res.is_fail = True 372 | else: 373 | is_fail = False 374 | for key in self.binding_dict.keys(): 375 | term = self.binding_dict[key] 376 | while is_variable(term) and term in self.binding_dict: 377 | term = self.binding_dict[term] 378 | while is_variable(term) and term in obj.binding_dict: 379 | term = obj.binding_dict[term] 380 | if key in res.binding_dict and term != res.binding_dict[key]: 381 | is_fail =True 382 | break 383 | res.binding_dict[key] = term 384 | for key in obj.binding_dict.keys(): 385 | term = obj.binding_dict[key] 386 | while is_variable(term) and term in obj.binding_dict: 387 | term = obj.binding_dict[term] 388 | while is_variable(term) and term in self.binding_dict: 389 | term = self.binding_dict[term] 390 | if key in res.binding_dict and term != res.binding_dict[key]: 391 | is_fail =True 392 | break 393 | res.binding_dict[key] = term 394 | res.is_fail = is_fail 395 | return res 396 | 397 | class ListOfBinding: 398 | def __init__(self, binding_list = []): 399 | if isinstance(binding_list,Binding): 400 | binding_list = [binding_list] 401 | self.binding_list = binding_list 402 | def merge_or(self, obj): 403 | if isinstance(obj,Binding): 404 | obj = ListOfBinding(binding_list=[obj]) 405 | new_binding_list = [] 406 | for binding in self.binding_list: 407 | if not binding.is_fail: 408 | new_binding_list.append(binding) 409 | for binding in obj.binding_list: 410 | if not binding.is_fail: 411 | new_binding_list.append(binding) 412 | return ListOfBinding(new_binding_list) 413 | def merge_and(self,obj): 414 | if isinstance(obj,Binding): 415 | obj = ListOfBinding(binding_list=[obj]) 416 | new_binding_list = [] 417 | for self_bind in self.binding_list: 418 | for obj_bind in obj.binding_list: 419 | if not self_bind.is_fail and not obj_bind.is_fail: 420 | new_bind = self_bind.merge(obj_bind) 421 | if not new_bind.is_fail: 422 | new_binding_list.append(new_bind) 423 | return ListOfBinding(new_binding_list) 424 | 425 | class CNF: 426 | def __init__(self, body, difference = []): 427 | self.body = body 428 | self.difference = difference 429 | def standardlize_variable(self): 430 | bind_dict = {} 431 | for statement in self.body.list_of_statements: 432 | bind_dict = statement.standardlize_variable(bind_dict=bind_dict) 433 | for difference in self.difference: 434 | difference[0].term = bind_dict[difference[0].term] 435 | difference[1].term = bind_dict[difference[1].term] 436 | def check_difference(self, binding): 437 | differ = True 438 | for difference in self.difference: 439 | if binding.bind(difference[0]) == binding.bind(difference[1]): 440 | differ = False 441 | break 442 | return differ 443 | 444 | def unify(x,y, binding): 445 | if binding.is_fail: 446 | return binding 447 | elif x == y: 448 | return binding 449 | elif isinstance(x,Term) and x.is_var: 450 | return unify_var(x,y,binding) 451 | elif isinstance(y,Term) and y.is_var: 452 | return unify_var(y,x,binding) 453 | elif isinstance(x,Statement) and isinstance(y,Statement): 454 | if x.is_unificable(y): 455 | return unify(x.list_of_terms,y.list_of_terms,binding) 456 | elif (isinstance(x,Conjunction) and isinstance(y,Conjunction)) or (isinstance(x,Disjunction) and isinstance(y,Disjunction)): 457 | if len(x.list_of_statements)==len(y.list_of_statements): 458 | return unify(x.list_of_statements,y.list_of_statements,binding) 459 | elif isinstance(x,list) and isinstance(y,list): 460 | if len(x) == len(y): 461 | return unify(x[0],y[0],unify(x[1:],y[1:],binding)) 462 | binding.is_fail = True 463 | return binding 464 | 465 | def unify_var(var,x,binding): 466 | if binding.already_has(var): 467 | return unify(binding.bind(var),x,binding) 468 | elif binding.already_has(x): 469 | return unify(var,binding.bind(x),binding) 470 | else: 471 | binding.add_new_binding(var,x) 472 | return binding 473 | class KnowledgeBase: 474 | #Knowledge Base with set of rules (facts are rules without right hand side) 475 | def __init__(self): 476 | self.list_of_rules = [] 477 | self.list_of_facts = [] 478 | self.list_of_query = [] 479 | def input_from_file(self, filename): 480 | with open(filename,'r') as f: 481 | for line in f: 482 | line = line.strip() 483 | if line.startswith('?-'): 484 | line = line[2:].strip() 485 | self.list_of_query.append(process_string(line).list_of_statements[0]) 486 | elif not line.startswith('%') and line != '': 487 | expr = line.split(':-') 488 | #print(expr[0]) 489 | new_rule = None 490 | if len(expr) == 2: 491 | new_rule = Rule(process_string(expr[0]).list_of_statements[0], process_string(expr[1])) 492 | self.list_of_rules.append(new_rule) 493 | elif len(expr)==1: 494 | new_rule = process_string(expr[0]).list_of_statements[0] 495 | new_rule.standardlize_variable() 496 | self.list_of_facts.append(new_rule) 497 | # Split rule 498 | list_of_new_rule = [] 499 | for rule in self.list_of_rules: 500 | list_of_new_rule = list_of_new_rule + rule.split() 501 | self.list_of_rules = list_of_new_rule 502 | # Get supported fact for each rule 503 | for rule in self.list_of_rules: 504 | rule.eliminate_difference() 505 | rule.get_supported_fact(self.list_of_facts) 506 | rule.standardlize_variable() 507 | 508 | def __str__(self): 509 | output = '' 510 | for rule in self.list_of_rules: 511 | output+= str(rule) + '\n' 512 | for fact in self.list_of_facts: 513 | output+= str(fact) + '\n' 514 | return output 515 | def forward_chaining(self,goal): 516 | for fact in self.list_of_facts: 517 | if fact.is_unificable(goal): 518 | binding = Binding() 519 | unify(fact,goal,binding) 520 | if not binding.is_fail: 521 | return binding 522 | while True: 523 | new = [] 524 | for rule in self.list_of_rules: 525 | supported_facts_list = rule.list_supported_facts() 526 | if len(supported_facts_list)==0: 527 | continue 528 | for fact_list in supported_facts_list: 529 | new_conj = Conjunction(fact_list) 530 | binding = Binding() 531 | unify(rule.rhs,new_conj,binding) 532 | if not binding.is_fail and rule.check_difference(binding): 533 | new_fact = binding.bind(rule.lhs) 534 | already_has = False 535 | for fact in new: 536 | if fact.is_identical(new_fact): 537 | already_has = True 538 | break 539 | if not already_has: 540 | for fact in knowledge_base.list_of_facts: 541 | if fact.is_identical(new_fact): 542 | already_has = True 543 | break 544 | if not already_has: 545 | new.append(new_fact) 546 | if goal.is_unificable(new_fact): 547 | goal_bind = Binding() 548 | unify(goal,new_fact,goal_bind) 549 | if not goal_bind.is_fail: 550 | return goal_bind 551 | self.list_of_facts += new 552 | for rule in self.list_of_rules: 553 | rule.get_supported_fact(new) 554 | if len(new)==0: 555 | break 556 | return False 557 | # Backward chaining 558 | def backward_chaining_ask(self,goal): 559 | return self.backward_chaining_or(goal) 560 | def backward_chaining_or(self,goal): 561 | binding_list = ListOfBinding([]) 562 | for fact in self.list_of_facts: 563 | if fact.is_unificable(goal): 564 | binding = Binding() 565 | unify(fact,goal,binding) 566 | if not binding.is_fail: 567 | binding_list.binding_list.append(binding) 568 | for rule in self.list_of_rules: 569 | if rule.lhs.is_unificable(goal): 570 | binding = Binding() 571 | unify(rule.lhs, goal, binding) 572 | if not binding.is_fail: 573 | binding_list = binding_list.merge_or(self.backward_chaining_and(rule,binding)) 574 | return binding_list 575 | def backward_chaining_and(self, rule, binding_rhs): 576 | binding_list = ListOfBinding([binding_rhs]) 577 | new_rule = binding_rhs.bind(rule.rhs) 578 | for goal in new_rule.list_of_statements: 579 | or_binding = self.backward_chaining_or(goal) 580 | var_list = goal.get_variable_list() 581 | for binding in or_binding.binding_list: 582 | new_dict = {} 583 | for key in binding.binding_dict.keys(): 584 | if key in var_list: 585 | new_dict[key] = binding.binding_dict[key] 586 | binding.binding_dict = new_dict 587 | binding_list = binding_list.merge_and(or_binding) 588 | for binding in binding_list.binding_list: 589 | if (not rule.check_difference(binding)) or binding.is_fail: 590 | binding_list.binding_list.remove(binding) 591 | return binding_list 592 | def resolution(self): 593 | list_of_cnfs = [] 594 | binding_query = [] 595 | for fact in self.list_of_facts: 596 | list_of_cnfs.append(CNF(Disjunction([fact]))) 597 | for rule in self.list_of_rules: 598 | new_dis = rule.rhs.negate() 599 | new_dis.list_of_statements.append(rule.lhs) 600 | list_of_cnfs.append(CNF(new_dis,rule.difference)) 601 | for query in self.list_of_query: 602 | binding = self.resolution_step(list_of_cnfs, Disjunction([query.negate()])) 603 | binding_query.append(binding) 604 | return binding_query 605 | def resolution_step(self,list_of_cnfs,goal): 606 | list_of_binding = ListOfBinding() 607 | if len(goal.list_of_statements) == 0: 608 | list_of_binding = ListOfBinding(Binding()) 609 | return list_of_binding 610 | goal_list = goal.list_of_statements 611 | for cnf in list_of_cnfs: 612 | cnf.standardlize_variable() 613 | cnf_list = cnf.body.list_of_statements 614 | subgoal = goal_list[0] 615 | 616 | for statement in cnf_list: 617 | if statement.is_opposite(subgoal): 618 | binding = Binding() 619 | unify(statement.negate(),subgoal,binding) 620 | if not binding.is_fail: 621 | difference = [] 622 | for differ in cnf.difference: 623 | difference.append([Term(differ[0].term),Term(differ[1].term)]) 624 | new_binding = ListOfBinding(binding) 625 | new_list_of_statements = goal_list[1:] + [x for x in cnf_list if not x == statement] 626 | new_dis = Disjunction(new_list_of_statements) 627 | new_dis = binding.bind(new_dis) 628 | subgoal_bind = new_binding.merge_and(self.resolution_step(list_of_cnfs,new_dis)) 629 | # Check difference 630 | for bind in subgoal_bind.binding_list: 631 | differ = True 632 | for pair in difference: 633 | if bind.bind(pair[0]) == bind.bind(pair[1]): 634 | differ = False 635 | break 636 | if not differ: 637 | subgoal_bind.binding_list.remove(bind) 638 | list_of_binding = list_of_binding.merge_or(subgoal_bind) 639 | return list_of_binding 640 | 641 | 642 | 643 | 644 | 645 | knowledge_base = KnowledgeBase() 646 | knowledge_base.input_from_file('input.txt') 647 | # print(str(knowledge_base)) 648 | start = time.time() 649 | 650 | # # Forward chaining 651 | # for query in knowledge_base.list_of_query: 652 | # print('?- ' + str(query)) 653 | # binding = knowledge_base.forward_chaining(query) 654 | # if binding == False: 655 | # print('False') 656 | # else: 657 | # for term in query.list_of_terms: 658 | # if term.is_var: 659 | # print(term.term + ' = ' + binding.binding_dict[term.term]) 660 | # print('True') 661 | 662 | # # Backward chaining 663 | for query in knowledge_base.list_of_query: 664 | print('?- ' + str(query)) 665 | binding_list = knowledge_base.backward_chaining_ask(query) 666 | is_fail = True 667 | for binding in binding_list.binding_list: 668 | if binding.is_fail == False: 669 | is_fail = False 670 | for term in query.list_of_terms: 671 | if term.is_var: 672 | print(term.term + ' = ' + binding.binding_dict[term.term]) 673 | print(not is_fail) 674 | 675 | # Resolution 676 | # query_binding = knowledge_base.resolution() 677 | # for index in range(len(knowledge_base.list_of_query)): 678 | # print('?- ' + str(knowledge_base.list_of_query[index])) 679 | # list_of_binding = query_binding[index] 680 | # is_fail = True 681 | # for binding in list_of_binding.binding_list: 682 | # if not binding.is_fail: 683 | # is_fail = False 684 | # for term in knowledge_base.list_of_query[index].list_of_terms: 685 | # if term.is_var: 686 | # print(term.term + ' = ' + binding.binding_dict[term.term]) 687 | # print(not is_fail) 688 | end = time.time() 689 | print(str(end - start)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Logic 2 | Forward chaining, Backward Chaining and Resolution in First-Order Logic 3 | -------------------------------------------------------------------------------- /test.pl: -------------------------------------------------------------------------------- 1 | %Define parent relationship level 0 2 | parent(queen_elizabeth, prince_charles). 3 | parent(queen_elizabeth, princess_anne). 4 | parent(queen_elizabeth, prince_andrew). 5 | parent(queen_elizabeth, prince_edward). 6 | 7 | parent(prince_phillip, prince_charles). 8 | parent(prince_phillip, princess_anne). 9 | parent(prince_phillip, prince_andrew). 10 | parent(prince_phillip, prince_edward). 11 | 12 | %Define parent relationship level 1, from left to right 13 | %1 14 | parent(princess_diana, prince_william). 15 | parent(princess_diana, prince_harry). 16 | 17 | parent(prince_charles, prince_william). 18 | parent(prince_charles, prince_harry). 19 | 20 | %2 21 | parent(captain_mark_phillips, peter_phillips). 22 | parent(captain_mark_phillips, zara_phillips). 23 | 24 | parent(princess_anne, peter_phillips). 25 | parent(princess_anne, zara_phillips). 26 | 27 | %3 28 | parent(sarah_ferguson, princess_beatrice). 29 | parent(sarah_ferguson, princess_eugenie). 30 | 31 | parent(prince_andrew, princess_beatrice). 32 | parent(prince_andrew, princess_eugenie). 33 | 34 | %4 35 | parent(sophie_rhys_jones, james). 36 | parent(sophie_rhys_jones, lady_louise). 37 | 38 | parent(prince_edward, james). 39 | parent(prince_edward, lady_louise). 40 | 41 | %Define parent relationship level 2, from left to right 42 | %1 43 | parent(prince_william, prince_george). 44 | parent(prince_william, princess_charlotte). 45 | 46 | parent(kate_middleton, prince_george). 47 | parent(kate_middleton, princess_charlotte). 48 | 49 | %2 50 | parent(autumn_kelly, savannah_phillips). 51 | parent(autumn_kelly, isla_phillips). 52 | 53 | parent(peter_phillips, savannah_phillips). 54 | parent(peter_phillips, isla_phillips). 55 | 56 | %3 57 | parent(zara_phillips, mia_grace_tindall). 58 | 59 | parent(mike_tindall, mia_grace_tindall). 60 | 61 | %Define gender 62 | 63 | %male 64 | male(prince_phillip). 65 | male(prince_charles). 66 | male(captain_mark_phillips). 67 | male(timothy_laurence). 68 | male(prince_andrew). 69 | male(prince_edward). 70 | male(prince_william). 71 | male(prince_harry). 72 | male(peter_phillips). 73 | male(mike_tindall). 74 | male(james). 75 | male(prince_george). 76 | 77 | %female 78 | female(queen_elizabeth). 79 | female(princess_diana). 80 | female(camilla_parker_bowles). 81 | female(princess_anne). 82 | female(sarah_ferguson). 83 | female(sophie_rhys_jones). 84 | female(kate_middleton). 85 | female(autumn_kelly). 86 | female(zara_phillips). 87 | female(princess_beatrice). 88 | female(princess_eugenie). 89 | female(lady_louise). 90 | female(princess_charlotte). 91 | female(savannah_phillips). 92 | female(isla_phillips). 93 | 94 | %married 95 | married(queen_elizabeth, prince_phillip). 96 | married(prince_charles, camilla_parker_bowles). 97 | married(princess_anne, timothy_laurence). 98 | married(sophie_rhys_jones, prince_edward). 99 | married(prince_william, kate_middleton). 100 | married(autumn_kelly, peter_phillips). 101 | married(zara_phillips, mike_tindall). 102 | 103 | married(prince_phillip, queen_elizabeth). 104 | married(camilla_parker_bowles, prince_charles). 105 | married(timothy_laurence, princess_anne). 106 | married(prince_edward, sophie_rhys_jones). 107 | married(kate_middleton, prince_william). 108 | married(peter_phillips, autumn_kelly). 109 | married(mike_tindall, zara_phillips). 110 | 111 | %married(Y, X) :- married(X, Y). 112 | 113 | %divorced 114 | divorced(princess_diana, prince_charles). 115 | divorced(captain_mark_phillips, princess_anne). 116 | divorced(sarah_ferguson,prince_andrew). 117 | 118 | divorced(prince_charles, princess_diana). 119 | divorced(princess_anne, captain_mark_phillips). 120 | divorced(prince_andrew, sarah_ferguson). 121 | 122 | %divorced(Y, X) :- divorced(X, Y). 123 | 124 | husband(Husband, Wife) :- married(Husband, Wife), male(Husband). 125 | wife(Wife, Husband) :- married(Husband, Wife), female(Wife). 126 | father(Parent, Child) :- parent(Parent, Child), male(Parent). 127 | mother(Parent, Child) :- parent(Parent, Child), female(Parent). 128 | child(Child, Parent) :- parent(Parent, Child). 129 | son(Child, Parent) :- parent(Parent, Child), male(Child). 130 | daughter(Child, Parent) :- parent(Parent, Child), female(Child). 131 | 132 | grandparent(GP, GC) :- parent(GP, Parent), parent(Parent, GC). 133 | grandmother(GM, GC) :- parent(GM, Parent), parent(Parent, GC), female(GM). 134 | grandfather(GF, GC) :- parent(GF, Parent), parent(Parent, GC), male(GF). 135 | grandchild(GC, GP) :- parent(GP, Parent), parent(Parent, GC). 136 | grandson(GS, GP) :- parent(GP, Parent), parent(Parent, GS), male(GS). 137 | granddaughter(GD, GP) :- parent(GP, Parent), parent(Parent, GD), female(GD). 138 | 139 | sibling(Person1, Person2) :- father(Father,Person1), mother(Mother,Person1),father(Father,Person2),mother(Mother,Person2),Person1\=Person2. 140 | brother(Person, Sibling) :- sibling(Person, Sibling),male(Person). 141 | sister(Person, Sibling) :- sibling(Person,Sibling),female(Person). 142 | aunt(Person, NieceNephew) :- parent(Parent,NieceNephew),(sister(Person,Parent);(brother(Uncle,Parent),wife(Person,Uncle))). 143 | uncle(Person, NieceNephew) :- parent(Parent,NieceNephew), (brother(Person,Parent);(sister(Aunt,Parent),husband(Person,Aunt))). 144 | niece(Person, AuntUncle) :- (aunt(AuntUncle,Person);uncle(AuntUncle,Person)),female(Person). 145 | nephew(Person, AuntUncle) :- (aunt(AuntUncle,Person);uncle(AuntUncle,Person)),male(Person). 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | --------------------------------------------------------------------------------