Skip to content

Commit f91c6aa

Browse files
author
Pascual Martinez-Gomez
committed
modified TreeContains and drafted premise-conclusion matching using anonymous variables
1 parent 79d0046 commit f91c6aa

File tree

3 files changed

+231
-13
lines changed

3 files changed

+231
-13
lines changed

scripts/abduction_tools.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import codecs
55
import logging
6+
import re
67
from subprocess import call, Popen
78
import subprocess
89

@@ -11,7 +12,7 @@
1112
from knowledge import GetLexicalRelationsFromPreds
1213
from semantic_tools import is_theorem_defined
1314
from tactics import get_tactics
14-
from tree_tools import tree_or_string
15+
from tree_tools import tree_or_string, TreeContains
1516

1617
# Check whether the string "is defined" appears in the output of coq.
1718
# In that case, we return True. Otherwise, we return False.
@@ -80,7 +81,7 @@ def GetConclusionLine(coq_output_lines):
8081
return None
8182
return coq_output_lines[line_index_last_conclusion_sep + 1]
8283

83-
def GetPremisesThatMatchConclusionArgs(premises, conclusion):
84+
def GetPremisesThatMatchConclusionArgs_(premises, conclusion):
8485
"""
8586
Returns premises where the predicates have at least one argument
8687
in common with the conclusion.
@@ -95,22 +96,26 @@ def GetPremisesThatMatchConclusionArgs(premises, conclusion):
9596
'\nPremise args: ' + str(premise_args))
9697
if premise_args.intersection(conclusion_args):
9798
candidate_premises.append(premise)
98-
# logging.debug('candidates: ' + str(candidate_premises))
9999
return candidate_premises
100100

101-
def GetPremisesThatMatchConclusionArgs_(premises, conclusion):
101+
def GetPremisesThatMatchConclusionArgs(premises, conclusion):
102102
"""
103-
Returns premises where the predicates have exactly the same
104-
arguments as in the conclusion.
103+
Returns premises where the predicates have at least one argument
104+
in common with the conclusion.
105105
"""
106-
conclusion_terms = conclusion.split()
107106
candidate_premises = []
108-
for premise in premises:
109-
premise_terms = premise.split()[2:]
110-
logging.debug(premise_terms)
111-
if len(premise_terms) == len(conclusion_terms) and \
112-
premise_terms[1:] == conclusion_terms[1:]:
113-
candidate_premises.append(premise)
107+
conclusion = re.sub(r'\?([0-9]+)', r'?x\1', conclusion)
108+
conclusion_args = get_tree_pred_args(conclusion, is_conclusion=True)
109+
if conclusion_args is None:
110+
return candidate_premises
111+
for premise_line in premises:
112+
# Convert anonymous variables of the form ?345 into ?x345.
113+
premise_line = re.sub(r'\?([0-9]+)', r'?x\1', premise_line)
114+
premise_args = get_tree_pred_args(premise_line)
115+
logging.debug('Conclusion args: ' + str(conclusion_args) + \
116+
'\nPremise args: ' + str(premise_args))
117+
if TreeContains(premise_args, conclusion_args):
118+
candidate_premises.append(premise_line)
114119
return candidate_premises
115120

116121
def MakeAxiomsFromPremisesAndConclusion(premises, conclusion):

scripts/abduction_tools_test.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#!/usr/bin/python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright 2017 Pascual Martinez-Gomez
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
import unittest
19+
20+
from abduction_tools import get_tree_pred_args
21+
from abduction_tools import GetPremisesThatMatchConclusionArgs
22+
from tree_tools import tree_or_string
23+
24+
class GetTreePredArgsTestCase(unittest.TestCase):
25+
def test_premise_one_var(self):
26+
coq_line = 'H2 : _kiss x1'
27+
expected_args = tree_or_string('x1')
28+
args = get_tree_pred_args(coq_line)
29+
self.assertEqual(expected_args, args)
30+
31+
def test_premise_zero_var(self):
32+
coq_line = 'H2 : True'
33+
expected_args = None
34+
args = get_tree_pred_args(coq_line)
35+
self.assertEqual(expected_args, args)
36+
37+
def test_premise_case_var(self):
38+
coq_line = 'H2 : _woman (Acc x1)'
39+
expected_args = tree_or_string('(Acc x1)')
40+
args = get_tree_pred_args(coq_line)
41+
self.assertEqual(expected_args, args)
42+
43+
class GetPremisesThatMatchConclusionArgsTestCase(unittest.TestCase):
44+
def test_one_casevar_one_prem_match(self):
45+
premise_lines = [
46+
"H2 : _kiss x1",
47+
"H : _man (Subj x1)",
48+
"H0 : _woman (Acc x1)"]
49+
conclusion_line = "_person (Acc x1)"
50+
expected_premises = [
51+
"H0 : _woman (Acc x1)"]
52+
matching_premises = GetPremisesThatMatchConclusionArgs(
53+
premise_lines, conclusion_line)
54+
self.assertEqual(expected_premises, matching_premises)
55+
56+
def test_one_casevar_zero_prem_match(self):
57+
premise_lines = [
58+
"H2 : _kiss x1",
59+
"H : _man (Subj x1)",
60+
"H0 : _woman (Acc x1)"]
61+
conclusion_line = "_person (Acc x2)"
62+
expected_premises = []
63+
matching_premises = GetPremisesThatMatchConclusionArgs(
64+
premise_lines, conclusion_line)
65+
self.assertEqual(expected_premises, matching_premises)
66+
67+
def test_one_casevar_zero_prem_match2(self):
68+
premise_lines = [
69+
"H2 : _kiss x1",
70+
"H : _man (Subj x1)",
71+
"H0 : _woman (Acc x1)"]
72+
conclusion_line = "_person (Ind x1)"
73+
expected_premises = []
74+
matching_premises = GetPremisesThatMatchConclusionArgs(
75+
premise_lines, conclusion_line)
76+
self.assertEqual(expected_premises, matching_premises)
77+
78+
def test_one_var_one_prems_match(self):
79+
premise_lines = [
80+
"H2 : _kiss x1",
81+
"H : _man (Subj x1)",
82+
"H0 : _woman (Acc x1)"]
83+
conclusion_line = "_greets x1"
84+
expected_premises = [
85+
"H2 : _kiss x1"]
86+
matching_premises = GetPremisesThatMatchConclusionArgs(
87+
premise_lines, conclusion_line)
88+
self.assertEqual(expected_premises, matching_premises)
89+
90+
def test_one_var_zero_prems_match(self):
91+
premise_lines = [
92+
"H2 : _kiss x1",
93+
"H : _man (Subj x1)",
94+
"H0 : _woman (Acc x1)"]
95+
conclusion_line = "_greets x2"
96+
expected_premises = []
97+
matching_premises = GetPremisesThatMatchConclusionArgs(
98+
premise_lines, conclusion_line)
99+
self.assertEqual(expected_premises, matching_premises)
100+
101+
def test_one_anonvar_one_prems_match(self):
102+
# Use anonymous variables (e.g. ?284 in the conclusion) to match
103+
# any variable in the premises.
104+
premise_lines = [
105+
"H2 : _kiss x1",
106+
"H : _man (Subj x1)",
107+
"H0 : _woman (Acc x1)"]
108+
conclusion_line = "_greets ?284"
109+
expected_premises = ["H2 : _kiss x1"]
110+
matching_premises = GetPremisesThatMatchConclusionArgs(
111+
premise_lines, conclusion_line)
112+
self.assertEqual(expected_premises, matching_premises)
113+
114+
def test_one_anonvar_two_prems_match(self):
115+
# Use anonymous variables (e.g. ?284 in the conclusion) to match
116+
# any case-constrained variable in the premises.
117+
premise_lines = [
118+
"H2 : _kiss x1",
119+
"H : _bird (Subj x2)",
120+
"H : _man (Subj x1)",
121+
"H0 : _woman (Acc x1)"]
122+
conclusion_line = "_greets (Subj ?284)"
123+
expected_premises = [
124+
"H : _bird (Subj x2)",
125+
"H : _man (Subj x1)"]
126+
matching_premises = GetPremisesThatMatchConclusionArgs(
127+
premise_lines, conclusion_line)
128+
self.assertEqual(expected_premises, matching_premises)
129+
130+
if __name__ == '__main__':
131+
suite1 = unittest.TestLoader().loadTestsFromTestCase(GetTreePredArgsTestCase)
132+
suite2 = unittest.TestLoader().loadTestsFromTestCase(
133+
GetPremisesThatMatchConclusionArgsTestCase)
134+
suites = unittest.TestSuite([suite1, suite2])
135+
unittest.TextTestRunner(verbosity=2).run(suites)

scripts/tree_tools.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright 2017 Pascual Martinez-Gomez
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
from copy import deepcopy
19+
from itertools import chain
20+
21+
from nltk import Tree as NLTKTree
22+
from nltk import ImmutableTree
23+
24+
def IsString(variable):
25+
return isinstance(variable, str)
26+
27+
def get_top(tr):
28+
"""Given a thing that might be a tree or maybe a terminal string, return
29+
the 'top' of it -- either the node of a tree, or just the string itself."""
30+
return (tr if IsString(tr) else tr.label())
31+
32+
33+
def TreeContains(tree, subtree):
34+
tree_is_inst_nltk = isinstance(tree, NLTKTree)
35+
subtree_is_inst_nltk = isinstance(subtree, NLTKTree)
36+
37+
# Subtree is a variable, and matches everything.
38+
subtree_top = get_top(subtree)
39+
tree_top = get_top(tree)
40+
if subtree_top.startswith('?x') and not tree_is_inst_nltk:
41+
# Get type of the variable.
42+
var_type = '|'.join(subtree_top.split('|')[1:])
43+
if var_type == '' or tree_top == var_type:
44+
return True
45+
else:
46+
return False
47+
48+
# Both are strings and one of them is a QA variable "[]"
49+
if (not tree_is_inst_nltk and not subtree_is_inst_nltk) and \
50+
(tree_top == "[]" or subtree_top == "[]"):
51+
return True
52+
53+
# tree and subtree are different types, or they have different POS tag,
54+
# or they have different number of children.
55+
if tree_is_inst_nltk and not subtree_is_inst_nltk \
56+
or (not tree_is_inst_nltk and subtree_is_inst_nltk) \
57+
or (tree_top != subtree_top and subtree_top != "[]") \
58+
or len(tree) != len(subtree):
59+
return False
60+
61+
# Both are strings and equal to each other.
62+
if (not tree_is_inst_nltk) and tree_top == subtree_top:
63+
return True
64+
65+
# Both are trees, and their subtrees are equal.
66+
for i, src_branch in enumerate(tree):
67+
trg_branch = subtree[i]
68+
if not TreeContains(src_branch, trg_branch):
69+
return False
70+
return True
71+
72+
def tree_or_string(s):
73+
"""Given a string loaded from the yaml, produce either a Tree or a string,
74+
if it's just a terminal."""
75+
if s.startswith(u"("):
76+
return NLTKTree.fromstring(s)
77+
return s
78+

0 commit comments

Comments
 (0)