Closed MobileSolutionsPL closed 7 years ago
Just quick note here, at the very beginning I've started with two methods - one for explicit relation and one for conjunction. Further I've assumed that adding existing relations to the fact database may cause problem, I've tried to combine it into one condition.
Hi, thanks for posting the question. Would you mind pasting the full code sample that leads to the infinite loop? Regarding the derivation of "is mother" from two facts "is parent" and "is female". This is how I interpret the rule:
from durable.lang import *
with ruleset('test'):
@when_all(c.female << (m.verb == 'is') & (m.direct_object == 'female'),
c.parent << (m.subject == c.female.subject) & (m.verb == 'is') & (m.direct_object == 'parent'))
def isMother(c):
c.assert_fact({ 'subject': c.parent.subject, 'verb': 'is', 'direct_object': 'mother', 'indirect_object': c.parent.indirect_object })
@when_all(c.male << (m.verb == 'is') & (m.direct_object == 'male'),
c.parent << (m.subject == c.male.subject) & (m.verb == 'is') & (m.direct_object == 'parent'))
def isFather(c):
c.assert_fact({ 'subject': c.parent.subject, 'verb': 'is', 'direct_object': 'father', 'indirect_object': c.parent.indirect_object })
@when_all(+m.subject)
def output(c):
if not c.m.indirect_object:
print('{0} {1} {2}'.format(c.m.subject, c.m.verb, c.m.direct_object))
else:
print('{0} {1} {2} of {3}'.format(c.m.subject, c.m.verb, c.m.direct_object, c.m.indirect_object))
@when_start
def start(c):
c.assert_fact('test', { 'subject': 'Sally', 'verb': 'is', 'direct_object': 'female' })
c.assert_fact('test', { 'subject': 'John', 'verb': 'is', 'direct_object': 'male' })
c.assert_fact('test', { 'subject': 'Sally', 'verb': 'is', 'direct_object': 'parent', 'indirect_object': 'Alex' })
c.assert_fact('test', { 'subject': 'John', 'verb': 'is', 'direct_object': 'parent', 'indirect_object': 'Alex' })
run_all()
What do you think? Is that what you have in mind?
Hi. Thanks for the immediate reply. It covers first part of what I'm thinking about. It is true that female person who is a parent is mother (if A and B then C). But the relation can be reversed and we can infer that a mother is both a parent and a female (so if C then A and B are also true). And that causes the infinite loop. I've assumed that the reasoner will check that C or A and B facts are already inferred and won't check the rule again. But it seems they're calling each other.
Please check the edited code.
from durable.lang import * with ruleset('test'):
@when_all(c.female << (m.verb =='is') & (m.direct_object =='female'),
c.parent << (m.subject == c.female.subject) & (m.verb =='is') & (m.direct_object =='parent'))
def isMother(c):
c.assert_fact({ 'subject': c.parent.subject, 'verb': 'is', 'direct_object': 'mother', 'indirect_object': c.parent.indirect_object })
#infer sex and parent relationship from "is mother" relation
@when_all(m.direct_object=='mother')
def addNewFacts(c):
c.assert_fact({ 'subject': c.m.subject, 'verb': 'is', 'direct_object': 'female' })
c.assert_fact({ 'subject': c.m.subject, 'verb': 'is', 'direct_object': 'parent', 'indirect_object': c.m.indirect_object })
@when_all(+m.subject)
def output(c):
if not c.m.indirect_object:
print('{0} {1} {2}'.format(c.m.subject, c.m.verb, c.m.direct_object))
else:
print('{0} {1} {2} of {3}'.format(c.m.subject, c.m.verb, c.m.direct_object, c.m.indirect_object))
@when_start
def start(c):
c.assert_fact('test', { 'subject': 'Sharon Marsh', 'verb': 'is', 'direct_object': 'female' })
c.assert_fact('test', { 'subject': 'Sharon Marsh', 'verb': 'is', 'direct_object': 'parent', 'indirect_object': 'Stan' })
c.assert_fact('test', { 'subject': 'Liane Cartman', 'verb': 'is', 'direct_object': 'mother', 'indirect_object': 'Eric' })
run_all()
Hi, thanks for clarifying. I understand the scenario now. Currently, every fact which doesn't have an 'id' property is considered a different fact. That is why you see the recursion. You can break the recursion by using the none() clause (see example below). This is a usability problem I'm intending to address (two facts with the same properties and values should be considered equivalent).
with ruleset('test'):
@when_all(c.female << (m.verb == 'is') & (m.direct_object == 'female'),
c.parent << (m.subject == c.female.subject) & (m.verb == 'is') & (m.direct_object == 'parent'),
none((m.subject == c.female.subject) & (m.verb == 'is') & (m.direct_object == 'mother')))
def isMother(c):
c.assert_fact({ 'subject': c.parent.subject, 'verb': 'is', 'direct_object': 'mother', 'indirect_object': c.parent.indirect_object })
@when_all((m.verb == 'is') & (m.direct_object == 'mother'))
def addNewFacts(c):
c.assert_fact({ 'subject': c.m.subject, 'verb': 'is', 'direct_object': 'female' })
c.assert_fact({ 'subject': c.m.subject, 'verb': 'is', 'direct_object': 'parent', 'indirect_object': c.m.indirect_object })
@when_all(+m.subject)
def output(c):
if not c.m.indirect_object:
print('{0} {1} {2}'.format(c.m.subject, c.m.verb, c.m.direct_object))
else:
print('{0} {1} {2} of {3}'.format(c.m.subject, c.m.verb, c.m.direct_object, c.m.indirect_object))
@when_start
def start(c):
c.assert_fact('test', { 'subject': 'Sally', 'verb': 'is', 'direct_object': 'female' })
c.assert_fact('test', { 'subject': 'Sally', 'verb': 'is', 'direct_object': 'parent', 'indirect_object': 'Alex' })
run_all()
Hi. That's perfect solution, regardless the way of implementation :) I've thought about negating and tried with none() function before, but I think I've done mistakes in the syntax.
Hi. I'm quite new to your rule engine, I'm trying to build my rule base on the family-tree description (let's assume it's very simple ontology here). I've extended the Kermit the frog example, also because the naming sounds like RDF graphs :) I've built such a code:
`@when_all(c.ismotherfact << (m.verb=='is mother of'), c.isfemalefact << (m.predicate=='female') & (c.ismotherfact.subject!=m.subject), c.isparentfact << (m.verb=='is parent') & (c.issexfact.subject==m.subject))
the business idea behind it is that "is mother of" relation may be set directly or inferred from "is parent" and "female" facts (someone who is a mother must be a parent and female).
the issue here is that in the background, there is infinite loop of calls on that rule. I've put log as a first line of the isMother method to check it. Am I doing something wrong here or it's kind of a bug in the framework?
Maybe there is kind of tutorial to the @when_all or @when_any syntax? for instance I've tried to create validationRuleFact where I've tried to compare c.isparentfact.subject with c.ismotherfact.subject but it fails somehow... could you please advise?