Python NLTK ResolutionProver finds a solution when in fact there is no
I have this rule of format "if A&B then C": if b is_man and s is sister_of b, then b is brother_of s
I add the fact that is_man(luc) and I request if there is any 'b' for brother_of(b,leia)
I expect the answer to be negative because it is not said that leia is sister_of luc, so in my rule "if A&B then C" the B part is not true, but ResolutionProver surprisingly answers 'luc' ??!?
import nltk.inference
rexp = nltk.sem.Expression.fromstring
p1 = rexp('is_man(luc)')
p2 = rexp('(is_man(b) & sister_of(s,b)) -> brother_of(b,s)')
answer = rexp('brother_of(b,leia) -> ANSWER(b)')
tp = nltk.ResolutionProverCommand(None, [p1,p2,answer])
if list(tp.find_answers()):
for ans in list(tp.find_answers()): print(ans,end=' ')
else: print('No answer')
It should not answer Luc but False, how can I get False ? And get 'luc' only in case I'd declare in addition sister_of(leia,luc). Thank you !
PS: for info, here is the proof:
[1] {is_man(luc)} A (that is p1)
[2] {-is_man(z368), -sister_of(z369,z368), brother_of(z368,z369)} A (that is p2)
[3] {-brother_of(z370,leia), ANSWER(z370)} A (that is answer)
[4] {-sister_of(z369,luc), brother_of(luc,z369)} (1, 2) (combination of 1 & 2)
[5] {ANSWER(z370), -is_man(z370), -sister_of(leia,z370)} (2, 3)
[6] {-sister_of(leia,luc), ANSWER(luc)} (1, 5)
[7] {-sister_of(leia,luc), ANSWER(luc)} (3, 4)
Answer
The issue you're encountering stems from the way ResolutionProverCommand
in NLTK works, and how it processes logical expressions and rules. In your case, you're using the rule:
(is_man(b) & sister_of(s,b)) -> brother_of(b,s)
This means that if b
is a man and s
is a sister of b
, then b
is a brother of s
. However, in your query, you're asking whether there is any b
such that brother_of(b, leia)
holds, without specifying that leia
is a sister of anyone.
Here's the core issue: ResolutionProverCommand
is inferring the possible values of b
based on the rules you provided, and in this case, it's making an inference based on the is_man(luc)
fact. Since the rule is a conditional (if-then) statement, and there's no specific clause saying sister_of(leia, luc)
or similar, the solver still concludes that the condition could be true for luc
due to the way logical resolution works.
Why does the prover answer 'luc'?
Looking at the proof:
is_man(luc)
is provided as a fact.- The rule
(is_man(b) & sister_of(s,b)) -> brother_of(b,s)
says that ifb
is a man ands
is a sister ofb
, thenb
is a brother ofs
. For this rule to apply toluc
, the prover attempts to satisfy the conditionsister_of(leia, luc)
forluc
andleia
. Since this is not explicitly given in the facts, the prover may use the assumption that the condition could hold (which is an issue in this specific setup). - The prover concludes that
luc
could potentially be theb
for whichbrother_of(b, leia)
holds because it infers that the rule could apply ifleia
were a sister ofluc
.
How to ensure 'luc' is returned only when sister_of(leia,luc)
is given?
To get the correct behavior (i.e., not return luc
unless it's explicitly stated that leia
is a sister of luc
), you need to make sure the system does not assume implicit relationships.
To fix this, you should add an explicit check for sister_of(leia, luc)
before running the query for brother_of(b, leia)
. Only if this relationship is explicitly stated will the inference process correctly apply the rule.
Solution: Add the sister_of(leia, luc)
fact
You can achieve this by explicitly adding the fact sister_of(leia, luc)
if you want the inference to correctly return luc
as the brother of leia
. Here's the updated code:
import nltk.inference
rexp = nltk.sem.Expression.fromstring
# Facts
p1 = rexp('is_man(luc)')
p2 = rexp('(is_man(b) & sister_of(s,b)) -> brother_of(b,s)')
p3 = rexp('sister_of(leia, luc)') # Add the explicit sister relationship
# Answer query
answer = rexp('brother_of(b,leia) -> ANSWER(b)')
# Running the prover
tp = nltk.ResolutionProverCommand(None, [p1,p2,p3,answer])
if list(tp.find_answers()):
for ans in list(tp.find_answers()): print(ans,end=' ')
else:
print('No answer')
What happens now?
With the additional fact sister_of(leia, luc)
, the rule (is_man(b) & sister_of(s,b)) -> brother_of(b,s)
will apply because the conditions for b
being a brother of leia
are now satisfied (since luc
is a man and leia
is a sister of luc
). Therefore, the prover will return luc
.
If you do not add the fact sister_of(leia, luc)
, the prover will not return any answer because the condition for applying the rule is not satisfied.
Conclusion:
The key to getting the expected behavior is to ensure that the conditions for applying the rule are explicitly stated. The prover is working correctly by returning luc
in the case where the rule applies, but if you want to avoid this, make sure that relationships like sister_of(leia, luc)
are explicitly defined when querying for brother_of(b, leia)
.