experta icon indicating copy to clipboard operation
experta copied to clipboard

Fact can only be modified once based on the fact name

Open cdemulde opened this issue 5 years ago • 4 comments

Hi,

I would like for my rule engine to run, save some values and then use those values, after the run, to modify some facts. For background: I want to run the engine 'dynamically': for each time step, I am running the engine, it provides a result, and based on that my facts change for the next time instance (the next run).

A minimal working example (without any relevant facts and rules):

import experta as exp
   
class Fact_1(exp.Fact):
    value_1 = exp.Field(int)

class Fact_2(exp.Fact):
    value_2 = exp.Field(int)
    
class Rules(exp.KnowledgeEngine):
    @exp.Rule(Fact_1(value_1 = exp.MATCH.v1),
              Fact_2(value_2 = exp.MATCH.v2))
    def subtract(self, v1, v2):
        return v1-v2
    
    
engine = Rules()
fact_1 = Fact_1(value_1 = 1)
fact_2 = Fact_2(value_2 = 3)
engine.reset()
engine.declare(fact_1)
engine.declare(fact_2)

engine.run()
engine.modify(fact_1)

engine.run()
engine.modify(fact_1)

This yields a Fact not founderror, I think because any time a fact is modified, it moves to the end of the fact list (engine.facts), while it is the number in this list that is used for the next modification.

I am new to working with rule-engines, so this might be a very specific problem that only I am facing, but I still have the feeling this might be a useful thing to have (i.e. repeated modification of facts by use of their name).

cdemulde avatar Dec 06 '19 10:12 cdemulde

The facts that you can use with modify are the ones returned by declare or the ones received in a rule as parameters. In your example:

engine = Rules()
fact_1 = Fact_1(value_1 = 1)
fact_2 = Fact_2(value_2 = 3)
engine.reset()
declared_1 = engine.declare(fact_1)
declared_2 = engine.declare(fact_2)
engine.run()

engine.modify(declared_1, value_1=5)
engine.modify(declared_2, value_2=3)
engine.run()

This is due to the fact that you can instantiate a fact and use it with multiple engines at the same time or be modified in an unsafe manner by the user. To avoid data corruption, when you declare a fact an immutable copy is made and managed by the engine.

Do this solve your problem?

nilp0inter avatar Dec 10 '19 10:12 nilp0inter

Hi,

I did not actually, at least not for the test case. I noticed that in your answer, you modify declared_1 and declared_2, while what I would like to be able to do is modify declared_1 twice, with an engine run in between. And that still throws the same error (except when I use the fix I proposed earlier).

cdemulde avatar Dec 12 '19 17:12 cdemulde

In that case what you need to do is to store the fact returned by modify, this is an example:

import experta as exp

class Fact_1(exp.Fact):
    value_1 = exp.Field(int)

class Fact_2(exp.Fact):
    value_2 = exp.Field(int)

class Rules(exp.KnowledgeEngine):
    @exp.Rule(Fact_1(value_1 = exp.MATCH.v1),
              Fact_2(value_2 = exp.MATCH.v2))
    def subtract(self, v1, v2):
        print(v1-v2)


engine = Rules()
fact_1 = Fact_1(value_1 = 1)
fact_2 = Fact_2(value_2 = 3)

engine.reset()
f1 = engine.declare(fact_1)
f2 = engine.declare(fact_2)
engine.run()

f1_1 = engine.modify(f1, value_1=3)
engine.run()

f1_2 = engine.modify(f1_1, value_1=8)
engine.run()

nilp0inter avatar Dec 17 '19 11:12 nilp0inter

Aha, that does the trick indeed, for the working example at least. For the time being, I'll keep working with the fix I proposed though. Somehow, I think it does make more sense to update a fact and leave it in place, i.e. with the same fact number and everything.

Thank you for your answers!

cdemulde avatar Dec 17 '19 13:12 cdemulde