Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foil #946

Merged
merged 8 commits into from
Aug 8, 2018
Merged

Foil #946

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added images/knowledge_FOIL_grandparent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/knowledge_foil_family.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 45 additions & 24 deletions knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from itertools import combinations, product
from logic import (FolKB, constant_symbols, predicate_symbols, standardize_variables,
variables, is_definite_clause, subst, expr, Expr)
from functools import partial

# ______________________________________________________________________________

Expand Down Expand Up @@ -297,44 +298,59 @@ def new_literals(self, clause):
share_vars = variables(clause[0])
for l in clause[1]:
share_vars.update(variables(l))

for pred, arity in self.pred_syms:
new_vars = {standardize_variables(expr('x')) for _ in range(arity - 1)}
for args in product(share_vars.union(new_vars), repeat=arity):
if any(var in share_vars for var in args):
yield Expr(pred, *[var for var in args])
# make sure we don't return an existing rule
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

if not Expr(pred, args) in clause[1]:
yield Expr(pred, *[var for var in args])

def choose_literal(self, literals, examples):
"""Choose the best literal based on the information gain."""
def gain(l):
pre_pos = len(examples[0])
pre_neg = len(examples[1])
extended_examples = [sum([list(self.extend_example(example, l)) for example in
examples[i]], []) for i in range(2)]
post_pos = len(extended_examples[0])
post_neg = len(extended_examples[1])
if pre_pos + pre_neg == 0 or post_pos + post_neg == 0:
return -1

# number of positive example that are represented in extended_examples
T = 0
for example in examples[0]:
def represents(d):
return all(d[x] == example[x] for x in example)
if any(represents(l_) for l_ in extended_examples[0]):
T += 1
def choose_literal(self, literals, examples):
"""Choose the best literal based on the information gain."""

return T * log((post_pos*(pre_pos + pre_neg) + 1e-4) / ((post_pos + post_neg)*pre_pos))
return max(literals, key = partial(self.gain , examples = examples))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better.


def gain(self, l ,examples):
"""
Find the utility of each literal when added to the body of the clause.
Utility function is:
gain(R, l) = T * (log_2 (post_pos / (post_pos + post_neg)) - log_2 (pre_pos / (pre_pos + pre_neg)))

where:

pre_pos = number of possitive bindings of rule R (=current set of rules)
pre_neg = number of negative bindings of rule R
post_pos = number of possitive bindings of rule R' (= R U {l} )
post_neg = number of negative bindings of rule R'
T = number of possitive bindings of rule R that are still covered
after adding literal l

"""
pre_pos = len(examples[0])
pre_neg = len(examples[1])
post_pos = sum([list(self.extend_example(example, l)) for example in examples[0]], [])
post_neg = sum([list(self.extend_example(example, l)) for example in examples[1]], [])
if pre_pos + pre_neg ==0 or len(post_pos) + len(post_neg)==0:
return -1
# number of positive example that are represented in extended_examples
T = 0
for example in examples[0]:
represents = lambda d: all(d[x] == example[x] for x in example)
if any(represents(l_) for l_ in post_pos):
T += 1
value = T * (log(len(post_pos) / (len(post_pos) + len(post_neg)) + 1e-12,2) - log(pre_pos / (pre_pos + pre_neg),2))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might consider splitting the computation into 2 lines for readability. Also certain code analysis tools like Pep8 limit the line length to 79 characters.

return value

return max(literals, key=gain)

def update_examples(self, target, examples, extended_examples):
"""Add to the kb those examples what are represented in extended_examples
List of omitted examples is returned."""
uncovered = []
for example in examples:
def represents(d):
return all(d[x] == example[x] for x in example)
represents = lambda d: all(d[x] == example[x] for x in example)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good use of an anonymous function.

if any(represents(l) for l in extended_examples):
self.tell(subst(example, target))
else:
Expand Down Expand Up @@ -400,3 +416,8 @@ def false_positive(e, h):

def false_negative(e, h):
return e["GOAL"] and not guess_value(e, h)





Loading