diff --git a/week5/material/fol_example.py b/week5/material/fol_example.py new file mode 100644 index 0000000000000000000000000000000000000000..93bf9343c78eba98974a271d89137d58975889e5 --- /dev/null +++ b/week5/material/fol_example.py @@ -0,0 +1,23 @@ +from pyDatalog import pyDatalog + +pyDatalog.create_terms('IsFemale,IsMale,AreParents,IsHungry,Want,X,Y,Z') + +#+ IsFemale('Jane') +#+ IsMale('Joe') ++ IsMale('Bob') ++ AreParents('Jane','Joe','Bob') ++ IsHungry('Bob') + +# adding last fact with .load method +pyDatalog.load("+ Want('Bob','Milk')") + +# adding clauses (and commenting the two given facts above) +IsFemale(X) <= AreParents(X, Y, Z) +IsMale(Y) <= AreParents(X, Y, Z) + +result = IsMale(X) +print(result) + +# asking with .ask method +answer = pyDatalog.ask('IsMale(X) & IsHungry(X)') +print(answer) \ No newline at end of file diff --git a/week5/material/guess_who.csv b/week5/material/guess_who.csv new file mode 100644 index 0000000000000000000000000000000000000000..67fdf1c47177e731d7b4621a9c1949ea7eaf7ac1 --- /dev/null +++ b/week5/material/guess_who.csv @@ -0,0 +1,25 @@ +Name,Gender,EyeColour,HairColour,HairLength,LipsSize,HasMoustache,HasBeard,WearHat,IsBald,WearGlasses,WearEarrings +Alex,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No +Alfred,Male,Blue,Red,Short,Small,Yes,No,No,No,No,No +Anita,Female,Blue,White,Short,Small,No,No,No,No,No,No +Anne,Female,Brown,Black,Short,Small,No,No,No,No,No,Yes +Bernard,Male,Brown,Brown,Short,Small,No,No,Yes,No,No,No +Bill,Male,Brown,Red,Short,Small,No,Yes,No,Yes,No,No +Charles,Male,Brown,Blond,Short,Big,Yes,No,No,No,No,No +Claire,Female,Brown,Red,Long,Small,No,No,Yes,No,Yes,No +David,Male,Brown,Blond,Long,Small,No,Yes,No,No,No,No +Eric,Male,Brown,Blond,Short,Small,No,No,Yes,No,No,No +Frans,Male,Brown,Red,Short,Small,No,No,No,No,No,No +George,Male,Brown,White,Short,Small,No,No,Yes,No,No,No +Herman,Male,Brown,Red,Short,Small,No,No,No,Yes,No,No +Joe,Male,Brown,Blond,Short,Small,No,No,No,No,Yes,No +Maria,Female,Brown,Brown,Long,Small,No,No,Yes,No,No,Yes +Max,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No +Paul,Male,Brown,White,Short,Small,No,No,No,No,Yes,No +Peter,Male,Blue,White,Short,Big,No,No,No,No,No,No +Philip,Male,Brown,Black,Short,Small,No,Yes,No,No,No,No +Richard,Male,Brown,Brown,Short,Small,Yes,Yes,No,Yes,No,No +Robert,Male,Blue,Brown,Short,Small,No,No,No,No,No,No +Sam,Male,Brown,White,Short,Small,No,No,No,Yes,Yes,No +Susan,Female,Brown,Blond,Long,Big,No,No,No,No,No,Yes +Tom,Male,Blue,Black,Short,Small,No,No,No,Yes,Yes,No \ No newline at end of file diff --git a/week5/material/guess_who.py b/week5/material/guess_who.py new file mode 100644 index 0000000000000000000000000000000000000000..3ccfaa492e145dfeb931d2178350325d3a85b604 --- /dev/null +++ b/week5/material/guess_who.py @@ -0,0 +1,92 @@ +import csv +import random +from pyDatalog import pyDatalog +import guess_who_kb + +def find_best_question(remaining_characters, remaining_predicates): + # TODO + pass + +if __name__ == '__main__': + # MAIN APPLICATION + + names_answer = pyDatalog.ask("Gender(X, Y)") + names = [] + for item in names_answer.answers: + names.append(item[0]) + + with open('guess_who.csv', newline='', mode='r', encoding='utf-8-sig') as f: + reader = csv.reader(f) + properties = None + for i, row in enumerate(reader): + if i == 0: + # First row with the predicates + properties = row[1:] + break + + # selecting a character randomly that the AI agent would need to guess + character_name = random.choice(names) + print("The AI agent will need to guess the character with name '{0}'".format(character_name)) + + # start with all the possible characters + available_characters = names.copy() + + # start with all the predicates we can use for questions + available_predicates = {} + for prop in properties: + query_outputs = pyDatalog.ask("{0}(X, Y)".format(prop)) + predicate_values = [] + for output in query_outputs.answers: + if output[1] not in predicate_values: + predicate_values.append(output[1]) + available_predicates[prop] = predicate_values + + # main body + print("AI starts the game with the following names on the board:") + print(available_characters) + + # The game stops when we have only one character available or we have no more questions to ask + while len(available_characters) > 1 and len(available_predicates.keys()) > 0: + # looking for the best next question + best_question = find_best_question(available_characters, available_predicates) + if best_question is None: + print("Function find_best_question not implemented. Asking a random question") + predicate = random.choice(list(available_predicates.keys())) + value = random.choice(available_predicates[predicate]) + best_question = (predicate, value) + + question = "{0}(X, '{1}')".format(best_question[0], best_question[1]) + print("AI Asking query: {0}".format(question)) + # retrieving the names with the considered feature + names_with_feature = [r[0] for r in pyDatalog.ask(question).answers if r[0] in available_characters] + + # checking if answer to the question is YES or NO + yes_no_answer = pyDatalog.ask("{0}('{1}','{2}')".format(best_question[0], character_name, best_question[1])) + yes_no_answer = "YES" if yes_no_answer is not None else "NO" + print("Opponent answering: The answer is {0}".format(yes_no_answer)) + + # removing predicate + available_predicates[best_question[0]].remove(best_question[1]) + # if there is less than 2 options or the answer was YES, we can remove the predicate completely + if yes_no_answer == "YES" or len(available_predicates[best_question[0]]) < 2: + del available_predicates[best_question[0]] + + # updating the remaining characters on the board + if yes_no_answer == "YES": + available_characters = names_with_feature.copy() + else: + for name in names_with_feature: + available_characters.remove(name) + + print("AI updates the remaining names on the board. Remaining names:") + print(available_characters) + + # making a guess + if len(available_characters) == 1: + # we are sure of the guess + print("AI says: The hidden character is {0}".format(available_characters[0])) + else: + # we are not sure, random guess + print("AI says: I am not sure who the character is. I will make a guess.") + guess = random.choice(available_characters) + print("AI says: The hidden character is {0}".format(guess)) \ No newline at end of file diff --git a/week5/material/guess_who_kb.py b/week5/material/guess_who_kb.py new file mode 100644 index 0000000000000000000000000000000000000000..495f05a34165aa9110c82d26129d4ad54ee0837a --- /dev/null +++ b/week5/material/guess_who_kb.py @@ -0,0 +1,7 @@ +import csv +from pyDatalog import pyDatalog + +# KNOWLEDGE BASE + +# loading the features of the guess who characters from the csv +# TODO \ No newline at end of file diff --git a/week5/solution/exercise1-2.py b/week5/solution/exercise1-2.py new file mode 100644 index 0000000000000000000000000000000000000000..29d98b7d0c59c9d92a5a8df20feecb4274583220 --- /dev/null +++ b/week5/solution/exercise1-2.py @@ -0,0 +1,39 @@ +# Exercise 1 + +QUERIES = { + 'alpha1': (lambda p, q, z: not (p or q) and z, ['P', 'Q', 'Z']), + 'alpha2': (lambda p, q, z: (not p and q) or z, ['P', 'Q', 'Z']), # using logic equivalence + 'alpha3': (lambda p, q, z: (p or q) == q, ['P', 'Q']), + 'alpha4': (lambda p, q, z: (p and q) or (p or q) and not(p and z), ['P', 'Q', 'Z']), + 'alpha5': (lambda p, q, z: p and not p, ['P']) +} + +for query, query_val in QUERIES.items(): + query_lambda, propsym = query_val + print("") + print("Models of M({0}):".format(query)) + models = [] + # list all models + for p in [True, False]: + for q in [True, False]: + for z in [True, False]: + if query_lambda(p, q, z): + model = {} + for sym in propsym: + model[sym] = eval(sym.lower()) + + if model not in models: + models.append(model) + print(model) + +""" +Exercise 2: +Check the output from the script for exercise 1 for the models in M(alpha<N>). + +1. For all models of alpha2 we have that Q OR Z is True, so alpha2 |= Q OR Z +2. For P = False and Q = True in M(alpha3), P <=> Q is False, so alpha3 does not entail P <=> Q +3. The sentence P => (Q OR NOT Z) is logically equivalent to NOT P OR (Q OR NOT Z). +For P = False, the sentence is always True. +For P = True the sentence is True when Q is True or Z is False. All the remaining models in M(alpha4) satisfy that. +So, alpha4 |= P => (Q OR NOT Z) +""" \ No newline at end of file diff --git a/week5/solution/exercise3.py b/week5/solution/exercise3.py new file mode 100644 index 0000000000000000000000000000000000000000..2a1791a87db69e5fa8f7ad27721ab83cd78c1126 --- /dev/null +++ b/week5/solution/exercise3.py @@ -0,0 +1,52 @@ +KB = { + 'P->Q': lambda p, q, r, s: not p or q, # using the logic equivalence + 'not R -> not P or Q': lambda p, q, r, s: r or (not p or q), + 'S -> not Q or R': lambda p, q, r, s: not s or (not q or r) +} + +QUERIES = { + 'P and S': lambda p, q, r, s: p and s, + 'not Q and not R': lambda p, q, r, s: not q and not r, + 'not P or Q': lambda p, q, r, s: not p or q +} + +if __name__ == '__main__': + + # listing all models in M(KB) + KB_models = [] + for p in [True, False]: + for q in [True, False]: + for r in [True, False]: + for s in [True, False]: + valid = True + for rule in KB.values(): + # Testing if current model + # makes KB rules true + # If only one rules is not true + # then set not valid and break + if not rule(p, q, r, s): + valid = False + break + # if a valid model, save it + if valid: + KB_models.append({ + 'P': p, + 'Q': q, + 'R': r, + 'S': s + }) + # listing the models in M(KB) + print("Models in M(KB):") + for m in KB_models: + print(m) + + # testing queries + for query, query_lambda in QUERIES.items(): + valid = True + for m in KB_models: + if not query_lambda(m['P'], m['Q'], m['R'], m['S']): + valid = False + print("The query '{0}' is not entailed by the KB: for P={1}, Q={2}, R={3}, S={4} KB is True but query is not".format(query, m['P'], m['Q'], m['R'], m['S'])) + break + if valid: + print("The query '{0}' is entailed by the KB".format(query)) diff --git a/week5/solution/fol_example.py b/week5/solution/fol_example.py new file mode 100644 index 0000000000000000000000000000000000000000..93bf9343c78eba98974a271d89137d58975889e5 --- /dev/null +++ b/week5/solution/fol_example.py @@ -0,0 +1,23 @@ +from pyDatalog import pyDatalog + +pyDatalog.create_terms('IsFemale,IsMale,AreParents,IsHungry,Want,X,Y,Z') + +#+ IsFemale('Jane') +#+ IsMale('Joe') ++ IsMale('Bob') ++ AreParents('Jane','Joe','Bob') ++ IsHungry('Bob') + +# adding last fact with .load method +pyDatalog.load("+ Want('Bob','Milk')") + +# adding clauses (and commenting the two given facts above) +IsFemale(X) <= AreParents(X, Y, Z) +IsMale(Y) <= AreParents(X, Y, Z) + +result = IsMale(X) +print(result) + +# asking with .ask method +answer = pyDatalog.ask('IsMale(X) & IsHungry(X)') +print(answer) \ No newline at end of file diff --git a/week5/solution/guess_who.csv b/week5/solution/guess_who.csv new file mode 100644 index 0000000000000000000000000000000000000000..67fdf1c47177e731d7b4621a9c1949ea7eaf7ac1 --- /dev/null +++ b/week5/solution/guess_who.csv @@ -0,0 +1,25 @@ +Name,Gender,EyeColour,HairColour,HairLength,LipsSize,HasMoustache,HasBeard,WearHat,IsBald,WearGlasses,WearEarrings +Alex,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No +Alfred,Male,Blue,Red,Short,Small,Yes,No,No,No,No,No +Anita,Female,Blue,White,Short,Small,No,No,No,No,No,No +Anne,Female,Brown,Black,Short,Small,No,No,No,No,No,Yes +Bernard,Male,Brown,Brown,Short,Small,No,No,Yes,No,No,No +Bill,Male,Brown,Red,Short,Small,No,Yes,No,Yes,No,No +Charles,Male,Brown,Blond,Short,Big,Yes,No,No,No,No,No +Claire,Female,Brown,Red,Long,Small,No,No,Yes,No,Yes,No +David,Male,Brown,Blond,Long,Small,No,Yes,No,No,No,No +Eric,Male,Brown,Blond,Short,Small,No,No,Yes,No,No,No +Frans,Male,Brown,Red,Short,Small,No,No,No,No,No,No +George,Male,Brown,White,Short,Small,No,No,Yes,No,No,No +Herman,Male,Brown,Red,Short,Small,No,No,No,Yes,No,No +Joe,Male,Brown,Blond,Short,Small,No,No,No,No,Yes,No +Maria,Female,Brown,Brown,Long,Small,No,No,Yes,No,No,Yes +Max,Male,Brown,Black,Short,Big,Yes,No,No,No,No,No +Paul,Male,Brown,White,Short,Small,No,No,No,No,Yes,No +Peter,Male,Blue,White,Short,Big,No,No,No,No,No,No +Philip,Male,Brown,Black,Short,Small,No,Yes,No,No,No,No +Richard,Male,Brown,Brown,Short,Small,Yes,Yes,No,Yes,No,No +Robert,Male,Blue,Brown,Short,Small,No,No,No,No,No,No +Sam,Male,Brown,White,Short,Small,No,No,No,Yes,Yes,No +Susan,Female,Brown,Blond,Long,Big,No,No,No,No,No,Yes +Tom,Male,Blue,Black,Short,Small,No,No,No,Yes,Yes,No \ No newline at end of file diff --git a/week5/solution/guess_who.py b/week5/solution/guess_who.py new file mode 100644 index 0000000000000000000000000000000000000000..307e740f0b14e13a4c74cc4775a32449af6705c3 --- /dev/null +++ b/week5/solution/guess_who.py @@ -0,0 +1,116 @@ +import csv +import random +from pyDatalog import pyDatalog +import guess_who_kb + +def find_best_question(remaining_characters, remaining_predicates): + best_question = None + max_avg_eliminated = 0 + for predicate in remaining_predicates.keys(): + for value in remaining_predicates[predicate]: + answer = pyDatalog.ask("{0}(X, '{1}')".format(predicate, value)) + + # characters I will be able to eliminate if answer is NO + names = [r[0] for r in answer.answers if r[0] in remaining_characters] + n_eliminated_no = len(names) + # whereas if answer is YES + n_eliminated_yes = len(remaining_characters) - len(names) + + # we need to make sure that the question is not a silly one + # silly questions are questions that we already know the answer + # because all the remaining characters share the same common feature + # These questions lead to eliminating all the characters if the answer is YES or NO + if n_eliminated_no == len(remaining_characters) or n_eliminated_yes == len(remaining_characters): + continue + + n_avg_eliminated = (n_eliminated_no + n_eliminated_yes)/2 + if best_question is None or n_avg_eliminated > max_avg_eliminated: + best_question = (predicate, value) + max_avg_eliminated = n_avg_eliminated + + return best_question + + +if __name__ == '__main__': + # MAIN APPLICATION + + names_answer = pyDatalog.ask("Gender(X, Y)") + names = [] + for item in names_answer.answers: + names.append(item[0]) + + with open('guess_who.csv', newline='', mode='r', encoding='utf-8-sig') as f: + reader = csv.reader(f) + properties = None + for i, row in enumerate(reader): + if i == 0: + # First row with the predicates + properties = row[1:] + break + + # selecting a character randomly that the AI agent would need to guess + character_name = random.choice(names) + print("The AI agent will need to guess the character with name '{0}'".format(character_name)) + + # start with all the possible characters + available_characters = names.copy() + + # start with all the predicates we can use for questions + available_predicates = {} + for prop in properties: + query_outputs = pyDatalog.ask("{0}(X, Y)".format(prop)) + predicate_values = [] + for output in query_outputs.answers: + if output[1] not in predicate_values: + predicate_values.append(output[1]) + available_predicates[prop] = predicate_values + + # main body + print("AI starts the game with the following names on the board:") + print(available_characters) + + # The game stops when we have only one character available or we have no more questions to ask + while len(available_characters) > 1 and len(available_predicates.keys()) > 0: + # looking for the best next question + best_question = find_best_question(available_characters, available_predicates) + if best_question is None: + print("Function find_best_question not implemented. Asking a random question") + predicate = random.choice(list(available_predicates.keys())) + value = random.choice(available_predicates[predicate]) + best_question = (predicate, value) + + question = "{0}(X, '{1}')".format(best_question[0], best_question[1]) + print("AI Asking query: {0}".format(question)) + # retrieving the names with the considered feature + names_with_feature = [r[0] for r in pyDatalog.ask(question).answers if r[0] in available_characters] + + # checking if answer to the question is YES or NO + yes_no_answer = pyDatalog.ask("{0}('{1}','{2}')".format(best_question[0], character_name, best_question[1])) + yes_no_answer = "YES" if yes_no_answer is not None else "NO" + print("Opponent answering: The answer is {0}".format(yes_no_answer)) + + # removing predicate + available_predicates[best_question[0]].remove(best_question[1]) + # if there is less than 2 options or the answer was YES, we can remove the predicate completely + if yes_no_answer == "YES" or len(available_predicates[best_question[0]]) < 2: + del available_predicates[best_question[0]] + + # updating the remaining characters on the board + if yes_no_answer == "YES": + available_characters = names_with_feature.copy() + else: + for name in names_with_feature: + available_characters.remove(name) + + print("AI updates the remaining names on the board. Remaining names:") + print(available_characters) + + # making a guess + if len(available_characters) == 1: + # we are sure of the guess + print("AI says: The hidden character is {0}".format(available_characters[0])) + else: + # we are not sure, random guess + print("AI says: I am not sure who the character is. I will make a guess.") + guess = random.choice(available_characters) + print("AI says: The hidden character is {0}".format(guess)) \ No newline at end of file diff --git a/week5/solution/guess_who_kb.py b/week5/solution/guess_who_kb.py new file mode 100644 index 0000000000000000000000000000000000000000..602ae93def87958206f4ad52463ccb8e270369bf --- /dev/null +++ b/week5/solution/guess_who_kb.py @@ -0,0 +1,25 @@ +import csv +from pyDatalog import pyDatalog + +# KNOWLEDGE BASE + +# loading the features of the guess who characters from the csv +names = [] +with open('guess_who.csv', newline='', mode='r', encoding='utf-8-sig') as f: + reader = csv.reader(f) + properties = None + for i, row in enumerate(reader): + if i == 0: + # First row with the predicates + # create the terms to use (variables and predicates) + properties = row[1:] + predicates = ','.join(row[1:]) + pyDatalog.create_terms('{0},X,Y,Z'.format(predicates)) + continue + + # rows with names and values for the predicates + name = row[0] + names.append(name) + for idx, propval in enumerate(row[1:]): + # add a fact for each predicate of the character + pyDatalog.load("+ {0}('{1}', '{2}')".format(properties[idx], name, propval)) \ No newline at end of file