experta icon indicating copy to clipboard operation
experta copied to clipboard

’EXISTS' cannot return accuracy results, influenced by the pattern order insider.

Open AndrewChung-GitHub opened this issue 5 years ago • 9 comments

from experta import *


class Goal(Fact):
    pass


class Hero(Fact):
    name = Field(str)
    status = Field(str, default="unoccupied")


class KE(KnowledgeEngine):
    @DefFacts()
    def goal_and_heroes(self):
        yield Goal('save-the-day')
        yield Hero(name="Death Defying Man")
        yield Hero(name="Stupendous Man")
        yield Hero(name="Incredible Man")

    @Rule(
        Goal('save-the-day'),
        EXISTS(            # bugs: return results influenced by the patterns order
            Hero(name="Death Defying Man"),
            Hero(status='occupied'),
            # Hero(status='occupied'),
            # Hero(name="Death Defying Man")
        )
    )
    def save_the_day(self):
        print("The day is saved")


if __name__ == '__main__':
    ke = KE()
    ke.reset()
    watch('RULES')
    ke.run()

Result:

The day is saved
INFO:experta.watchers.RULES:FIRE 1 save_the_day: <f-1>, <f-0>

However, if I reverse the patterns order inEXISTS, the result would be empty.

AndrewChung-GitHub avatar Nov 25 '19 03:11 AndrewChung-GitHub

Hi,

I am trying to reproduce the issue but in my tests the rule:

    @Rule(
        Goal('save-the-day'),
        EXISTS(
            Hero(name="Death Defying Man"),
            Hero(status='unoccupied'),
        )
    )
    def save_the_day(self):
        print("The day is saved")

and

    @Rule(
        Goal('save-the-day'),
        EXISTS(
            Hero(status='unoccupied'),
            Hero(name="Death Defying Man"),
        )
    )
    def save_the_day(self):
        print("The day is saved")

both yields the same result.

Which experta version are you using?

nilp0inter avatar Nov 25 '19 06:11 nilp0inter

experta.__version__

'1.9.4'

please take note I used Hero(status='occupied') nor 'unoccupied'. Due to Hero(status='occupied') hadn't be defined above, it didn't exist. Could you try again, thanks.

AndrewChung-GitHub avatar Nov 25 '19 06:11 AndrewChung-GitHub

Oh, I think I understand. The example is not very clear, but the behavior is correct.

Let me explain: In the definition of Hero the field status has a default value unoccupied. That means that any declared Hero fact that lacks the status field will have an implicit value of unoccupied.

This is why the rule in the original example matched and yours don't. The explicit version would be:

    @DefFacts()
    def goal_and_heroes(self):
        yield Goal('save-the-day')
        yield Hero(name="Death Defying Man", status="unoccupied")
        yield Hero(name="Stupendous Man", status="unoccupied")
        yield Hero(name="Incredible Man", status="unoccupied")

In your case the rule is not matching because there are no heroes with status="occupied".

nilp0inter avatar Nov 25 '19 06:11 nilp0inter

I modified the example to remove the implicit value. Please let me know if this way is more clear.

nilp0inter avatar Nov 25 '19 06:11 nilp0inter

Thanks for your quickly response, however, you may misunderstood my point. My concern is on the function of 'EXIST', I think as long as there is one or more facts matching, it should continue to execute following actions. However, in this case, it yields different results.

AndrewChung-GitHub avatar Nov 25 '19 06:11 AndrewChung-GitHub

You are very welcome, thank you for using experta.

For EXISTS to match, there must be at least one instance of each fact inside the EXISTS clause. As you can see the rule is not satisfied because none of the declared facts has status="occupied" in it.

nilp0inter avatar Nov 25 '19 06:11 nilp0inter

yeah, indeed the facts status="occupied" didn't exist, but the fact Hero(name="Death Defying Man") always exists. Hence, I think no matter the order of these two patterns adjusted, the results should be the same and output the result of print("The day is saved").

AndrewChung-GitHub avatar Nov 25 '19 06:11 AndrewChung-GitHub

The behavior that you are describing is not the behavior of the EXISTS clause but the OR clause. Please try the OR clause to see if it meets your requirements.

Also, check #8 for another clause that can be helpful to you.

nilp0inter avatar Nov 25 '19 08:11 nilp0inter

Thanks for your patient explanations.

Yeah, I have not yet fully understood the function of "EXISTS", and fall into confusion about the descriptions in your reference file.

This CE receives a pattern and matches if one or more facts matches this pattern. This will match only once while one or more matching facts exists and will stop matching when there is no matching facts.

Could you give me more explanations and introductions on application scenarios? Thanks again.

AndrewChung-GitHub avatar Nov 25 '19 09:11 AndrewChung-GitHub