def test_cnf_negation(self): ''' Ensure we can get into conjunctive normal form ''' alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) delta = Predicate('D', ['z']) s = ~(Universal(['x', 'y', 'z'], (~(alpha | beta) & delta))) self.assertEqual(repr(s.push_complete()), "∃(x,y,z)[(A(x) | B(y) | ~D(z))]") s = ~(Universal(['x', 'y', 'z'], ~((alpha | beta) & delta))) self.assertEqual(repr(s.push_complete()), "∃(x,y,z)[((A(x) | B(y)) & D(z))]") s = ~((~alpha | ~beta) & ~delta) self.assertEqual(repr(s.push_complete()), "((A(x) & B(y)) | D(z))") ## Test to make sure the recursino into nested stuff actually work s = (~~~~~~~~~alpha).push_complete() self.assertEqual(repr(s), '~A(x)') s = (~~~~~~~~alpha).push_complete() self.assertEqual(repr(s), 'A(x)')
def test_distribution(self): ''' Ensure that distribution over conjunctions work ''' #(b & (a | (c & b))) alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) charlie = Predicate('C', ['t']) delta = Predicate('D', ['z']) s = delta | (alpha & beta) ret = s.distribute(s.terms[0], s.terms[1]) self.assertEqual(repr(ret), '((D(z) | A(x)) & (D(z) | B(y)))') s = (alpha | beta) & delta ret = s.distribute(s.terms[0], s.terms[1]) self.assertEqual(repr(ret), '((D(z) & A(x)) | (D(z) & B(y)))') s = (alpha | beta) & (beta | delta) ret = s.distribute(s.terms[0], s.terms[1]) self.assertEqual(repr(ret), '(((A(x) | B(y)) & B(y)) | ((A(x) | B(y)) & D(z)))') # Simple case - single distribute s = beta | (alpha & (delta | charlie)) ret = s.distribute(s.terms[0], s.terms[1]) self.assertEqual('((B(y) | A(x)) & (B(y) | D(z) | C(t)))', repr(ret)) # Slightly more complex s = (beta & charlie) | (alpha & (delta | charlie)) ret = s.distribute(s.terms[0], s.terms[1]) self.assertEqual( '(((B(y) & C(t)) | A(x)) & ((B(y) & C(t)) | D(z) | C(t)))', repr(ret))
def test_connective_to_onf(self): alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) charlie = Predicate('C', ['u']) delta = Predicate('D', ['z']) # Trivial case -- already in CNF two = (beta | alpha) & (delta) self.assertEqual(repr(two.to_onf()), '((B(y) | A(x)) & D(z))') two = (beta & alpha) | (delta) self.assertEqual(repr(two.to_onf()), '((D(z) | B(y)) & (D(z) | A(x)))') # Reversed case two = (delta) | (beta & alpha) self.assertEqual(repr(two.to_onf()), '((D(z) | B(y)) & (D(z) | A(x)))') # Nested distribution one = (alpha & beta) | (charlie & delta) self.assertEqual( repr(one.to_onf()), '((A(x) | C(u)) & (A(x) | D(z)) & (B(y) | C(u)) & (B(y) | D(z)))') # Nested distribution one = (alpha | (beta & (charlie | (delta & alpha)))) self.assertEqual( repr(one.to_onf()), '((A(x) | B(y)) & (C(u) | A(x) | D(z)) & (C(u) | A(x) | A(x)))')
def test_axiom_to_pcnf(self): a = Predicate('A', ['x']) b = Predicate('B', ['y']) c = Predicate('C', ['z']) # Simple test of disjunction over conjunction axi_one = Axiom(Universal(['x', 'y', 'z'], a | b & c)) axi_one = axi_one.ff_pcnf() self.assertEqual('∀(z,y,x)[((A(z) | B(y)) & (A(z) | C(x)))]', repr(axi_one)) # Test recursive distribution #axi_one = Axiom(Universal(['x','y','z'], a | (b & (a | (c & b))))) #print(repr(axi_one)) #self.assertEqual('', repr(axi_one.to_pcnf())) # Simple sanity check, it's already FF-PCNF axi_two = Axiom(Universal(['x', 'y', 'z'], (a | b) & c)) axi_two = axi_two.ff_pcnf() self.assertEqual('∀(z,y,x)[(C(x) & (A(z) | B(y)))]', repr(axi_two)) # Sanity check we remove functions c = Predicate('C', ['z', Function('F', ['z'])]) axi_three = Axiom(Universal(['x', 'y', 'z'], a | b & c)) axi_three = axi_three.ff_pcnf() self.assertEqual( '∀(z,y,x,w)[((A(z) | C(x,w) | ~F(x,w)) & (A(z) | B(y)))]', repr(axi_three))
def test_axiom_connecive_rescoping(self): a = Predicate('A', ['x']) b = Predicate('B', ['y']) universal = Universal(['x'], a) existential = Existential(['y'], b) conjunction = universal & existential disjunction = universal | existential # Ensure we handle single quantifier case self.assertEqual(repr((universal & b).rescope()), '∀(x)[(A(x) & B(y))]') self.assertEqual(repr((existential & a).rescope()), '∃(y)[(B(y) & A(x))]') self.assertEqual(repr((universal | b).rescope()), '∀(x)[(A(x) | B(y))]') self.assertEqual(repr((existential | a).rescope()), '∃(y)[(B(y) | A(x))]') # Ensure we catch error condition where lookahead is needed self.assertRaises(ValueError, (existential | universal).rescope) # Ensure that we can promote Universals when a conjunction lives above us top = a & disjunction self.assertEqual(repr(disjunction.rescope(top)), '∀(x)[∃(y)[(A(x) | B(y))]]') # Ensure that we can promote Existentials when a conjunction lives above us top = a | conjunction self.assertEqual(repr(conjunction.rescope(top)), '∃(y)[∀(x)[(B(y) & A(x))]]')
def test_axiom_function_replacement(self): f = Function('f', ['x']) t = Function('t', ['y']) a = Predicate('A', [f]) b = Predicate('B', [f, t]) axi = Axiom(Universal(['x'], a | a & a)) self.assertEqual(repr(axi), '∀(x)[(A(f(x)) | (A(f(x)) & A(f(x))))]') axi = Axiom(Universal(['x', 'y'], b))
def test_owl_subclass(self): a = Predicate('A', ['x']) b = Predicate('B', ['x']) c = Predicate('C', ['x']) d = Predicate('D', ['x']) subclass_relation = Axiom(Universal(['x'], ~d | b | c)) onto = Ontology("Derp") onto.axioms.append(subclass_relation) print(onto.to_owl())
def test_conjunction_form(self): """ Ensure basic & operator overloading is working """ alpha = Predicate('A', ['x']) beta = Predicate('B', ['x', 'y']) delta = Predicate('D', ['z']) self.assertEqual(repr(alpha & beta), '(A(x) & B(x,y))') self.assertEqual(repr(alpha & beta & delta), '(A(x) & B(x,y) & D(z))')
def test_disjunction_form(self): ''' Ensure basic | operator overloading is working ''' alpha = Predicate('A', ['x']) beta = Predicate('B', ['x', 'y']) delta = Predicate('D', ['z']) self.assertEqual(repr(alpha | beta), '(A(x) | B(x,y))') self.assertEqual(repr(alpha | beta | delta), '(A(x) | B(x,y) | D(z))')
def test_axion_to_tptp(self): a = Predicate('A', ['x']) b = Predicate('B', ['y']) c = Predicate('C', ['z']) d = Predicate('D', ['u']) axiom_one = Axiom(Universal(['x', 'y', 'z', 'u'], ~a | ~d | b | c)) axiom_two = Axiom(Universal(['x', 'y', 'z', 'u'], ~a | ~d | b | c)) print() print(axiom_one.to_tptp()) print(axiom_two.to_tptp())
def test_can_filter_axioms(self): a = Predicate('A', ['x']) b = Predicate('B', ['x']) simple_subclass = Axiom(Universal(['x'], ~a | b)) simple_disjoint = Axiom(Universal(['x'], ~a | ~b)) matching_patterns = Filter.filter_axiom(simple_subclass) self.assertTrue(Pattern.subclass_relation in matching_patterns) not_matching = Filter.filter_axiom(simple_disjoint) self.assertFalse(Pattern.subclass_relation in not_matching)
def test_cnf_quantifier_simplfy(self): alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) uni_one = Universal(['x'], alpha) mixer = uni_one | beta uni_two = Universal(['y'], mixer) uni_nested = Universal(['z'], alpha & (beta | (alpha & uni_one))) self.assertEqual('∀(z)[(A(x) & (B(y) | (A(x) & ∀(x)[A(x)])))]', repr(uni_nested)) self.assertEqual('∀(z,x)[(A(x) & (B(y) | (A(x) & A(x))))]', repr(uni_nested.simplify())) self.assertEqual(repr(uni_two), "∀(y)[(∀(x)[A(x)] | B(y))]") self.assertEqual(repr(uni_two.simplify()), "∀(y,x)[(B(y) | A(x))]")
def test_axiom_variable_standardize(self): a = Predicate('A', ['x']) b = Predicate('B', ['y', 'x']) c = Predicate('C', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']) axi = Axiom(Universal(['x'], a | a & a)) self.assertEqual(repr(axi.standardize_variables()), '∀(z)[(A(z) | (A(z) & A(z)))]') axi = Axiom(Universal(['x', 'y'], b)) self.assertEqual(repr(axi.standardize_variables()), '∀(z,y)[B(y,z)]') axi = Axiom( Existential(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'], c)) self.assertEqual(repr(axi.standardize_variables()), '∃(z,y,x,w,v,u,t,s,r)[C(z,y,x,w,v,u,t,s,r)]')
def test_axiom_simple_function_replacement(self): f = Function('f', ['x']) t = Function('t', ['y']) p = Function('p', ['z']) a = Predicate('A', [f, t, p]) b = Predicate('B', [f, t]) c = Predicate('C', [f]) axi = Axiom(Universal(['x', 'y', 'z'], a)) #self.assertEqual(repr(axi.substitute_functions()), '∀(x,y,z)[∀(f2,t3,p4)[(A(f2,t3,p4) | ~(f(x,f2) & t(y,t3) & p(z,p4)))]]') axi = Axiom(Universal([ 'x', ], ~c)) #self.assertEqual(repr(axi.substitute_functions()), '∀(x)[~~∀(f5)[(C(f5) | ~f(x,f5))]]') #c = Predicate('C', [Function('f', [Function('g', [Function('h', ['x'])])])]) axi = Axiom(Universal(['x'], c))
def test_mixed_form(self): ''' Ensure that the & and | operators work when chained ''' alpha = Predicate('A', ['x']) beta = Predicate('B', ['x', 'y']) delta = Predicate('D', ['z']) self.assertEqual(repr((alpha & beta) | delta), '((A(x) & B(x,y)) | D(z))') self.assertEqual(repr(alpha & (beta | delta)), '(A(x) & (B(x,y) | D(z)))') self.assertEqual(repr((alpha & beta) | (alpha & delta)), '((A(x) & B(x,y)) | (A(x) & D(z)))') self.assertEqual(repr((alpha | beta) & (alpha | delta)), '((A(x) | B(x,y)) & (A(x) | D(z)))')
def test_quantifiers(self): alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) delta = Predicate('D', ['z']) uni = Universal(['x', 'y', 'z'], alpha | beta | delta) exi = Existential(['x', 'y', 'z'], alpha & beta & delta) self.assertEqual(repr(uni), "∀(x,y,z)[(A(x) | B(y) | D(z))]") self.assertEqual(repr(exi), "∃(x,y,z)[(A(x) & B(y) & D(z))]") self.assertEqual(repr(~uni), "~∀(x,y,z)[(A(x) | B(y) | D(z))]") self.assertEqual(repr(~exi), "~∃(x,y,z)[(A(x) & B(y) & D(z))]") self.assertEqual(repr((~uni).push()), "∃(x,y,z)[~(A(x) | B(y) | D(z))]") self.assertEqual(repr((~exi).push()), "∀(x,y,z)[~(A(x) & B(y) & D(z))]")
def test_axiom_quantifier_coalesence(self): a = Predicate('A', ['x']) b = Predicate('B', ['y']) universal = Universal(['x'], a) universal_two = Universal(['y'], b) existential = Existential(['y'], b) existential_two = Existential(['x'], a) # Reduce over conjunction should coalesce Universals and merge existentials conjunction = universal & universal_two & existential & existential_two self.assertEqual(repr(conjunction.coalesce()), '(∀(x)[(B(x) & A(x))] & ∃(y,x)[(B(y) & A(x))])') # Reduce over disjunction should coealesce Existentials and merge Universals disjunction = universal | universal_two | existential | existential_two self.assertEqual(repr(disjunction.coalesce()), '(∃(y)[(A(y) | B(y))] | ∀(x,y)[(A(x) | B(y))])')
def test_cnf_quantifier_scoping(self): a = Predicate('A', ['x']) b = Predicate('B', ['y']) c = Predicate('C', ['z']) e = Existential(['x'], a) u = Universal(['y'], b) # Test the effect over an OR self.assertEqual('∃(x)[(A(x) | B(y))]', repr((e | b).rescope())) self.assertEqual('∀(y)[(B(y) | A(x))]', repr((u | a).rescope())) # Test the effect over an AND self.assertEqual('∃(x)[(A(x) & B(y))]', repr((e & b).rescope())) self.assertEqual('∀(y)[(B(y) & A(x))]', repr((u & a).rescope())) # Test with more than two to make sure things aren't dropped self.assertEqual('∀(y)[(B(y) & (A(x) | C(z) | B(y)))]', repr((u & (a | c | b)).rescope()))
def test_negation(self): ''' Ensure we can get into conjunctive normal form ''' alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) delta = Predicate('D', ['z']) s = alpha | beta self.assertEqual(repr(~s), "~(A(x) | B(y))") s = alpha & beta self.assertEqual(repr(~s), "~(A(x) & B(y))") s = alpha & beta self.assertEqual(repr((~s).push()), "(~A(x) | ~B(y))") s = alpha | beta self.assertEqual(repr((~s).push()), "(~A(x) & ~B(y))") s = (alpha | beta) & delta self.assertEqual(repr((~s).push()), "(~(A(x) | B(y)) | ~D(z))")
def substitute_functions(self): """ Recurse over the contained logical replacing any nested functions with new predicates. """ functions = [] ret_object = copy.deepcopy(self.sentence) axiom = Axiom(Util.dfs_functions(ret_object, functions, None)) # Form explicit function declaration to keep tight logical equivalence declarations = [] unique_functions = {f.name for f in functions if len(f.variables) == 2} for function in unique_functions: LOGGER.debug('Adding function declaration on: {}'.format(function)) term_one = ~(Predicate(function, ['a', 'b'])) term_two = ~(Predicate(function, ['a', 'c'])) equality = Predicate('=', ['c', 'b']) sentence = Universal(['a', 'b', 'c'], term_one | term_two | equality) declarations.append(Axiom(sentence)) return axiom, declarations
def test_onf_detection(self): alpha = Predicate('A', ['x']) beta = Predicate('B', ['y']) delta = Predicate('D', ['z']) uni = Universal(['x', 'y', 'z'], alpha | beta | delta) exi = Existential(['x', 'y', 'z'], alpha & beta | delta) self.assertEqual(alpha.is_onf(), True) self.assertEqual((alpha | beta).is_onf(), True) self.assertEqual((alpha & beta).is_onf(), True) self.assertEqual((alpha | (beta & delta)).is_onf(), False) self.assertEqual((alpha & (beta | delta)).is_onf(), True) self.assertEqual((~(alpha | beta)).is_onf(), False) self.assertEqual((~(alpha & beta)).is_onf(), False) self.assertEqual(uni.is_onf(), True) self.assertEqual(exi.is_onf(), False) # Note that is_onf() is not a recursive call, it's a top level feature # If will actually if you need an ONF axiom then create a Logical.Axiom and to_onf() self.assertEqual((alpha & (alpha | (beta & delta)) & delta).is_onf(), False)
def prenex_parser(logical, prenex, parent): """ DFS through a prenex accumulating each quantifier in the [prenex] list. Order is important. """ if isinstance(logical, Quantifier): LOGGER.debug("Adding to the quantifier") prenex.append(logical) prenex_parser(logical.get_term()[0], prenex, logical) else: # Hit the bottom of the prenex, insert a dummy Predicate null = Predicate("Null", ['null']) if parent is not None: parent.set_term(null) return
def test_can_match_subclass_pattern(self): # Simple sanity check a = Predicate('A', ['x']) b = Predicate('B', ['x']) simple = Axiom(Universal(['x'], ~a | b)) match = Pattern.subclass_relation(simple) self.assertIsNotNone(match) self.assertEqual(match[1].pop(), a) self.assertEqual(match[2].pop(), b) # Check against longer disjunctions a = Predicate('A', ['x']) b = Predicate('B', ['x']) c = Predicate('C', ['x']) d = Predicate('D', ['x']) ext = Axiom(Universal(['x'], ~a | ~b | c | d)) match = Pattern.subclass_relation(ext) self.assertIsNotNone(match) self.assertEqual(match[1], [a, b]) self.assertEqual(match[2], [c, d])
def p_predicate(p): """ predicate : LPAREN NONLOGICAL parameter RPAREN """ p[0] = Predicate(p[2], p[3])