nilp0inter / experta Goto Github PK
View Code? Open in Web Editor NEWThis project forked from buguroo/pyknow
Expert Systems for Python
License: GNU Lesser General Public License v3.0
This project forked from buguroo/pyknow
Expert Systems for Python
License: GNU Lesser General Public License v3.0
It would be fantastic if experta could be made available on Anaconda Cloud, either in its own channel or in conda-forge.
https://docs.anaconda.com/anaconda-cloud/user-guide/howto/#build-packages
This is pretty trivial to do and could be done by anyone, but is best done by the package owners/contributors in lockstep with releases made on PyPi.
THANKS!
You can just keep running the code below, the result will be arbitrary, which is wierd.
from experta import *
class Light(Fact):
"""Info about the traffic light."""
pass
class RobotCrossStreet(KnowledgeEngine):
@Rule(NOT(Light(color=L('red'))))
def red_light(self):
print("dont")
@Rule(NOT(Light(color=L('yellow'))))
def yellow_light(self):
print("wait")
engine = RobotCrossStreet()
engine.reset()
engine.declare(Light(color='red'))
engine.run()
This is a copy of this issue.
Rules are being called on already retracted facts.
P = [[None, 2, None, 6, None, 8, None, None, None],
[5, 8, None, None, None, 9, 7, None, None],
[None, None, None, None, 4, None, None, None, None],
[3, 7, None, None, None, None, 5, None, None],
[6, None, None, None, None, None, None, None, 4],
[None, None, 8, None, None, None, None, 1, 3],
[None, None, None, None, 2, None, None, None, None],
[None, None, 9, 8, None, None, None, 3, 6],
[None, None, None, 3, None, 6, None, 9, None]]
class Possible(Fact):
pass
class Solver(KnowledgeEngine):
@DefFacts()
def init_puzzle(self):
for x, row in enumerate(P):
for y, cell in enumerate(row):
block = ((y // 3) * 3) + (x // 3)
if cell is None:
yield Fact(value=None, y=y, x=x, block=block)
for i in range(1, 10):
yield Possible(value=i, y=y, x=x, block=block)
else:
yield Fact(value=cell, y=y, x=x, block=block)
@Rule(Fact(value=~L(None) & MATCH.v, y=MATCH.y),
AS.p << Possible(value=MATCH.v, y=MATCH.y))
def discarded_by_column(self, p):
self.retract(p)
@Rule(Fact(value=~L(None) & MATCH.v, x=MATCH.x),
AS.p << Possible(value=MATCH.v, x=MATCH.x))
def discarded_by_row(self, p):
self.retract(p)
@Rule(Fact(value=~L(None) & MATCH.v, block=MATCH.b),
AS.p << Possible(value=MATCH.v, block=MATCH.b))
def discarded_by_block(self, p):
self.retract(p)
@Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
NOT(Possible(value=~MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b)))
def only_one_possible(self, cell, v):
self.retract(cell)
self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))
@Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
NOT(Possible(value=MATCH.v, x=~MATCH.x, y=~MATCH.y, block=MATCH.b)))
def unique_candidate_block(self, cell, v):
self.retract(cell)
self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))
@Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
NOT(Possible(value=MATCH.v, x=~MATCH.x, y=MATCH.y, block=~MATCH.b)))
def unique_candidate_col(self, cell, v):
self.retract(cell)
self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))
@Rule(AS.cell << Fact(value=None, x=MATCH.x, y=MATCH.y, block=MATCH.b),
Possible(value=MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
NOT(Possible(value=MATCH.v, x=MATCH.x, y=~MATCH.y, block=~MATCH.b)))
def unique_candidate_row(self, cell, v):
self.retract(cell)
self.declare(Fact(value=v, x=cell['x'], y=cell['y'], block=cell['block']))
@Rule(Fact(value=~L(None) & MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b),
AS.p << Possible(value=~MATCH.v, x=MATCH.x, y=MATCH.y, block=MATCH.b))
def remove_other_candidates(self, p):
self.retract(p)
watch('RULES', 'FACTS')
s = Solver()
s.reset()
s.run()
This code raises the following exception:
INFO:pyknow.watchers.FACTS: <== <f-130>: Possible(value=2, y=1, x=2, block=0)
INFO:pyknow.watchers.RULES:FIRE 531 unique_candidate_col: <f-130>, <f-128>
Traceback (most recent call last):
File "sudoku.py", line 95, in <module>
s.run()
File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/engine.py", line 168, in run
for k, v in activation.context.items()
File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/rule.py", line 87, in __call__
return self._wrapped(*args, **kwargs)
File "sudoku.py", line 76, in unique_candidate_col
self.retract(cell)
File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/engine.py", line 124, in retract
self.facts.retract(idx_or_declared_fact)
File "/home/nil/.local/share/virtualenvs/sudoku-VtHvu9jk/lib/python3.7/site-packages/pyknow/factlist.py", line 111, in retract
raise IndexError('Fact not found.')
IndexError: Fact not found.
A similar exception is raised if the method modify
is used instead of retract
+declare
.
This is a copy of the issue: buguroo#10
The usage of multiple ORFC is causing a combinatorial explosion in the RETE matcher as discovered on buguroo#7.
Introduce a new engine feature to allow returning values from the RHS.
This has been requested many times, last time here: buguroo#47
In this case the last activation is not executed
from experta import *
class KE(KnowledgeEngine):
@Rule(
Fact(s=True),
Fact('a')
)
def foo(self):
print("foo")
@Rule(
AS.f << Fact(s=False),
Fact('a')
)
def bar(self, f):
print("bar")
self.modify(f, s=True)
ke=KE()
ke.reset()
ke.declare(Fact("a"))
ke.declare(Fact(s=False))
ke.run()
It should print bar
and then foo
, but it only prints bar
.
While fixing #10 I discovered that Activation objects don't seem to have coherent ordering which causes issues with bisect ordering in the Agenda resulting in more complex code than needed.
The last activation on the Greetings example from the docs is not executed. So, when ask_name is executed, ask_location is not and vice-versa. Therefore, greet is never executed. I saw that you had a similar issue (#7 ) that is supposedly fixed, but I think this might be a similar thing.
I noticed that if salience is set, then this just works fine. When not set, the Activations' keys are the same and that's why the second activation is removed from the list when _update_agenda is called after the first activation is fired.
I'm currently using the latest code of the develop branch. This is the Greeting code I'm using:
from experta import *
class Greetings(KnowledgeEngine):
@DefFacts()
def _initial_action(self):
yield Fact(action="greet")
@Rule(Fact(action='greet'),
NOT(Fact(name=W())))
def ask_name(self):
self.declare(Fact(name=input("What's your name? ")))
@Rule(Fact(action='greet'),
NOT(Fact(location=W())))
def ask_location(self):
self.declare(Fact(location=input("Where are you? ")))
@Rule(Fact(action='greet'),
Fact(name=MATCH.name),
Fact(location=MATCH.location))
def greet(self, name, location):
print("Hi %s! How is the weather in %s?" % (name, location))
engine = Greetings()
engine.reset() # Prepare the engine for the execution.
engine.run() # Run it!
I don't know if my question is caused by a misunderstanding of the topic. I have a list of facts that I need to be able to easily modify.
The documentation shows the modify
method as follows:
>>> engine.facts
<f-0> InitialFact()
<f-1> Fact(color='red')
>>> engine.modify(engine.facts[1], color='yellow', blink=True)
<f-2>
>>> engine.facts
<f-0> InitialFact()
<f-2> Fact(color='yellow', blink=True)
But to use it you need to know the index of the fact you want to modify in the list of facts, and I don't necessarily know the order in which the facts are declared. What's more, if I make successive modifications, the disorder of the list increases.
It would be great to have a way to modify facts like:
>>> engine.modify(Fact(color='red'), color='yellow', blink=True)
Or maybe a way to search for facts like:
>>> index = engine.facts.search(Fact(color='red'))
>>> engine.modify(engine.facts[index], color='yellow', blink=True)
I really appreciate the help. If it's something that wasn't covered in the code and you think it would be great to add, I'd be happy to consider working on it. If the solution to the question is a different approach, please share it with me.
I am trying to run this code:
class RuleEng(KnowledgeEngine):
@Rule(salience=1)
def r1(self):
print("Execution r1 second")
pass
@Rule(salience=2)
def r2(self):
print("Execution r2 first")
pass
@Rule(Fact(foo="bar"))
def Foo(self):
print("Execute Foo")
pass
engine = RuleEng()
engine.reset()
engine.declare(Fact(foo="bar"))
print(engine.facts)
engine.modify(engine.facts[1], foo="bar2")
engine.modify(engine.facts[1], foo="bar")
and got this error:
<f-0>: InitialFact()
<f-1>: Fact(foo='bar')
Traceback (most recent call last):
File ***, in <module>
engine.modify(engine.facts[1], foo="bar")
~~~~~~~~~~~~^^^
KeyError: 1
I have a fact:
AS.run << Run(size=MATCH.size,
startX=MATCH.x,
startY=MATCH.y,
configuration=MATCH.conf,
lastX=MATCH.xe,
lastY=MATCH.ye,
length=MATCH.length,
plan=MATCH.plan)
.
I want add condition (NOT(EXISTS (Run (length = MATCH.length + 1))). \ I want verify, that there no eny facts Run, which have length ofcurrent fact "Run" - 1
I know, that "MATCH.length + 1" is elligal, but how i can it another way?
Python Python 3.10.9
Jupyter Notebook 6.5.2
!pip install experta
Requirement already satisfied: experta in c:\programdata\anaconda3\lib\site-packages (1.9.4)
Requirement already satisfied: frozendict==1.2 in c:\programdata\anaconda3\lib\site-packages (from experta) (1.2)
Requirement already satisfied: schema==0.6.7 in c:\programdata\anaconda3\lib\site-packages (from experta) (0.6.7)
from experta import *
AttributeError Traceback (most recent call last)
Cell In[9], line 1
----> 1 from experta import *
File C:\ProgramData\anaconda3\lib\site-packages\experta_init_.py:5
3 try:
4 from .conditionalelement import AND, OR, NOT, TEST, EXISTS, FORALL
----> 5 from .engine import KnowledgeEngine
6 from .fact import Fact, InitialFact, Field
7 from .fieldconstraint import L, W, P
File C:\ProgramData\anaconda3\lib\site-packages\experta\engine.py:13
10 from experta import abstract
12 from experta.agenda import Agenda
---> 13 from experta.fact import InitialFact
14 from experta.factlist import FactList
15 from experta.rule import Rule
File C:\ProgramData\anaconda3\lib\site-packages\experta\fact.py:9
6 from schema import Schema
8 from experta.pattern import Bindable
----> 9 from experta.utils import freeze, unfreeze
10 from experta.conditionalelement import OperableCE
11 from experta.conditionalelement import ConditionalElement
File C:\ProgramData\anaconda3\lib\site-packages\experta\utils.py:4
1 from functools import singledispatch
2 import collections.abc
----> 4 from frozendict import frozendict
6 from .fieldconstraint import P
9 class frozenlist(tuple):
File C:\ProgramData\anaconda3\lib\site-packages\frozendict_init_.py:16
10 OrderedDict = NotImplemented
13 iteritems = getattr(dict, 'iteritems', dict.items) # py2-3 compatibility
---> 16 class frozendict(collections.Mapping):
17 """
18 An immutable wrapper around dictionaries that implements the complete :py:class:collections.Mapping
19 interface. It can be used as a drop-in replacement for dictionaries where immutability is desired.
20 """
22 dict_cls = dict
AttributeError: module 'collections' has no attribute 'Mapping'
After deleting the site-packages
!pip install experta
Collecting experta
Using cached experta-1.9.4-py3-none-any.whl (35 kB)
Requirement already satisfied: frozendict==1.2 in c:\programdata\anaconda3\lib\site-packages (from experta) (1.2)
Requirement already satisfied: schema==0.6.7 in c:\programdata\anaconda3\lib\site-packages (from experta) (0.6.7)
Installing collected packages: experta
Successfully installed experta-1.9.4
from experta import *
...
AttributeError: module 'collections' has no attribute 'Mapping'
If in the case of AND, as long as one of the multiple facts is false, then there is no need to judge again, but the current situation is that every fact under AND will be judged.
In the same way, in the case of OR, as long as one of the multiple facts is true, there is no need to judge later, but the current situation is that every fact under the OR will be judged.
I have this code, for animals:
from experta import rule
from experta.engine import KnowledgeEngine
from experta.fieldconstraint import L
from experta.rule import Rule
from experta.fact import Fact, Field
from experta.deffacts import DefFacts
from experta.shortcuts import MATCH
from experta.conditionalelement import TEST
class animal(Fact):
name = Field(str, mandatory=True)
number = Field(int, mandatory=True)
class AnimalAnalysis(KnowledgeEngine):
@DefFacts()
def init_animal(self):
yield animal(name = 'dog',
number = 23)
yield animal(name = 'cat',
number = 5)
yield animal(name = 'snake',
number = 14)
@Rule(animal(name=MATCH.name, number=MATCH.number), TEST(lambda number: number > 10))
def animal_analysis(self, name, number):
return print(name, 'has more than 10 ', number)
engine = AnimalAnalysis()
engine.reset()
engine.run()
Which result:
snake has more than 10 14
dog has more than 10 23
How can I import, at once, a complete excel or txt list of facts?
(Below: )
animal number
dog 23
cat 5
snake 14
dog 32
bird 78
cat 48
dog 52
dog 36
cat 28
bird 14
snake 24
snake 17
Thanks folks! :)
Require to generate rules Dynamically how can i do that can you give me example please
I want to let the user know which rule are fired based on their respond. How do I show the list of rules that are fired?
As shown by #10, the order in which activations are yielded by the current implementation of the RETE network is not idempotent.
I have a rule with this structure:
@Rule(
# NOT(Excluded(patient_id=MATCH.patient_id)),
EXISTS(
FeverGreaterThanFourDays(
patient_id=MATCH.patient_id,
appointment_id=MATCH.appointment_id,
weight=MATCH.fever_weight,
present=W(),
),
ConjunctivalInjection(
patient_id=MATCH.patient_id,
appointment_id=MATCH.appointment_id,
weight=MATCH.conjunctival_weight,
present=W(),
),
# etc...
)
def _ (patient_id, appointment_id, **kwargs):
# my function.
These are some sample facts:
FactList([(0, InitialFact()),
(1, Patient(age=5, patient_id=5223)),
(2, Patient(age=45, patient_id=1223)),
(3,
Objective(patient_id=5223, appointment_id=2, code='42631002', code_system='SNOMEDCT')),
(4,
Objective(patient_id=5223, appointment_id=2, code='82014009', code_system='SNOMEDCT')),
(5,
Objective(patient_id=5223, appointment_id=2, code='99999984', code_system='SNOMEDCT'))])
If I replace "EXISTS" with "OR", I can get matches almost as documented--basically I see the rule fire for each fact that matches. Unfortunately this is not what the doc says--it says it should fire for each combination of facts that match, so I'd expect to see it fire on individual facts, then pairs, then triples, etc. I suspect this is just an error in the docs.
This is the result using "OR":
Code system match: SNOMEDCT
Calculating initial score for 5223 (appointment 2) on available data: dict_items([('rash_weight', 0)])
Code system match: SNOMEDCT
Calculating initial score for 5223 (appointment 2) on available data: dict_items([('peripheral_weight', 1)])
Code system match: SNOMEDCT
Calculating initial score for 5223 (appointment 2) on available data: dict_items([('strawberry_tongue_weight', 1)])
Patient 1223 does not meet age criteria 45 > 10
Resulting facts are:
FactList([(0, InitialFact()),
(1, Patient(age=5, patient_id=5223)),
(2, Patient(age=45, patient_id=1223)),
(3,
Objective(patient_id=5223, appointment_id=2, code='42631002', code_system='SNOMEDCT')),
(4,
Objective(patient_id=5223, appointment_id=2, code='82014009', code_system='SNOMEDCT')),
(5,
Objective(patient_id=5223, appointment_id=2, code='99999984', code_system='SNOMEDCT')),
(6,
PolymorphousRash(patient_id=5223, appointment_id=2, weight=0, present=False)),
(7, InitialRiskScore(patient_id=5223, appointment_id=2, score=0)),
(8,
PeripheralEdema(patient_id=5223, appointment_id=2, weight=1, present=True)),
(9, InitialRiskScore(patient_id=5223, appointment_id=2, score=1)),
(10,
StrawberryTongue(patient_id=5223, appointment_id=2, weight=1, present=True)),
(11, Excluded(patient_id=1223))])
However, with the same set of facts that match with OR, if I change from OR to EXISTS it ceases matching. My expectation is that the rule will fire once, and that the associated function will receive all the weight parameters for matching facts. Instead none of the facts match...
Code system match: SNOMEDCT
Code system match: SNOMEDCT
Code system match: SNOMEDCT
Patient 1223 does not meet age criteria 45 > 10
Resulting facts are:
FactList([(0, InitialFact()),
(1, Patient(age=5, patient_id=5223)),
(2, Patient(age=45, patient_id=1223)),
(3,
Objective(patient_id=5223, appointment_id=2, code='42631002', code_system='SNOMEDCT')),
(4,
Objective(patient_id=5223, appointment_id=2, code='82014009', code_system='SNOMEDCT')),
(5,
Objective(patient_id=5223, appointment_id=2, code='99999984', code_system='SNOMEDCT')),
(6,
PolymorphousRash(patient_id=5223, appointment_id=2, weight=0, present=False)),
(7,
PeripheralEdema(patient_id=5223, appointment_id=2, weight=1, present=True)),
(8,
StrawberryTongue(patient_id=5223, appointment_id=2, weight=1, present=True)),
(9, Excluded(patient_id=1223))])
It seems like EXISTS is critical for "aggregating" information from multiple facts. If this is not the intended behavior of EXISTS, is there something else I should use? I need to be able to reason on the largest collection of facts in a set that match.
Let me know if there is a different approach I should use.
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.
https://travis-ci.org/nilp0inter/experta/jobs/616530283
state = StrategyStateMachine()
v1 = state.declare(fs={0}, r=0)
v2 = state.declare(fs={0}, r=1)
v3 = state.declare(fs={1}, r=0)
v4 = state.declare(fs={0}, r=2)
state.retract(act=v4)
state.teardown()
Hi, thanks for this interesting library!
I tried implementing a version of the water jugs problem (of which a version was seen on the Die Hard movie), but it gets stuck in loop. The problem is usually formulated as this:
Suppose we have two jugs. One jug is capable of holding 3 gallons of water. A second jug can hold up to 4 gallons of water. There are no measurement lines on either jug. Therefore we can never determine the exact amount of water in either jug. However, by looking in either jug, we can determine if the jug is empty, full, or contains some water. We can empty a jug, fill a jug, or pour water from one jug into the other.
I thought the implementation would be straightforward, but after many tries I was unable to make work. I'm sure I'm missing something.
from experta import KnowledgeEngine, Fact, Field, Rule, DefFacts, AS, MATCH, TEST, P, W, L
class Jug(Fact):
content = Field(int, default=0, mandatory=True)
class Jug3(Jug): pass
class Jug4(Jug): pass
class Jugs(KnowledgeEngine):
@DefFacts()
def init(self):
yield Jug3(content=0)
yield Jug4(content=0)
@Rule(Jug4(content=L(2)))
def goal(self):
print("Done")
self.halt()
@Rule(
AS.jug3 << Jug3(content=MATCH.content3),
AS.jug4 << Jug4(content=MATCH.content4),
TEST(lambda content3, content4: content3 < 3 and content4 > 0),
)
def pour_jug4_into_jug3(self, jug3, jug4, content3, content4):
content_to_pour = min(3 - content3, content4, 3)
jug3_content = content3 + content_to_pour
jug4_content = content4 - content_to_pour
self.modify(jug3, content=jug3_content)
self.modify(jug4, content=jug4_content)
print("Pour jug4 into jug3")
@Rule(
AS.jug3 << Jug3(content=MATCH.content3),
AS.jug4 << Jug4(content=MATCH.content4),
TEST(lambda content3, content4: content3 > 0 and content4 < 4),
)
def pour_jug3_into_jug4(self, jug3, jug4, content3, content4):
content_to_pour = min(4 - content4, content3, 4)
jug3_content = content3 - content_to_pour
jug4_content = content4 + content_to_pour
self.modify(jug3, content=jug3_content)
self.modify(jug4, content=jug4_content)
print("Pour jug3 into jug4")
@Rule(AS.jug3 << Jug3(content=P(lambda x: x > 0)))
def empty_jug3(self, jug3):
self.modify(jug3, content=0)
print("Empty jug3")
@Rule(AS.jug4 << Jug4(content=P(lambda x: x > 0)))
def empty_jug4(self, jug4):
self.modify(jug4, content=0)
print("Empty jug4")
@Rule(AS.jug3 << Jug3(content=P(lambda x: x < 3)))
def fill_jug3(self, jug3):
self.modify(jug3, content=3)
print("Fill jug3")
@Rule(AS.jug4 << Jug4(content=P(lambda x: x < 4)))
def fill_jug4(self, jug4):
self.modify(jug4, content=4)
print("Fill jug4")
engine = Jugs()
engine.reset()
engine.run()
Implementation of multifield wildcard for patern match - CLIPS equivalent for $?
. Started at branch feature-multifieldwildcard
a long time ago. Is there any milestone for this feature?
Hi,
I've been testing out Experta a little bit, and so far it really looks like what I need. This might be more a question than an actual issue, but I figured it might be something to look at if not already existing.
I noticed that some rules (for example when no TESTs are used) only fired once by default. Others, where I am using a TEST, do keep firing as long as the test is fulfilled. I was wondering if there is a way to limit the amount of times a rule is fired.
Thanks in advance for the help!
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 found
error, 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).
I use the KnowledgeEngine as my Rule Engine. but the instance Of Light() ect would not actually release in memory.
I use it with a loop and at least 10000 user which has a large number Fact. The amount of memory will raise very quickly and be killed because of the memory is not enough.
Try to use gc.collect()
. But it has the same result. And I need some help about that. Thanks.
This is my test sample of Light() has not be release in memory after the loop. And I use objgraph
package for checking it .
# here's the demo code use by a loop
from random import choice
from experta import *
class Light(Fact):
"""Info about the traffic light."""
pass
class RobotCrossStreet(KnowledgeEngine):
@Rule(Light(color='green'))
def green_light(self):
print("Walk")
@Rule(Light(color='red'))
def red_light(self):
print("Don't walk")
@Rule(AS.light << Light(color=L('yellow') | L('blinking-yellow')))
def cautious(self, light):
print("Be cautious because light is", light["color"])
def test_func():
engine = RobotCrossStreet()
engine.reset()
engine.declare(Light(color=choice(['green', 'yellow', 'blinking-yellow', 'red'])))
engine.run()
# will do some record..
if __name__ == '__main__':
import objgraph
objgraph.show_growth()
for i in range(10):
test_func()
print('======after======')
objgraph.show_growth()
# the first print of show_growth()
'''
function 2389 +2389
dict 1211 +1211
wrapper_descriptor 1043 +1043
tuple 831 +831
builtin_function_or_method 789 +789
method_descriptor 743 +743
weakref 731 +731
getset_descriptor 455 +455
member_descriptor 299 +299
type 231 +231
'''
# the second print of show_growth
'''
dict 1311 +100
weakref 786 +55
set 155 +46
builtin_function_or_method 821 +32
FactCapture 30 +30
list 209 +29
Light 32 +29 <---- here
tuple 858 +27
method 43 +15
ChildNode 15 +15
'''
Hi I have a question relating to looping Experta infinitely. I am currently creating a rule based retrieval chat bot and I would like to use Experta as my Knowledge engine.
To give a quick background I need to get train ticket information from the user and use this to scrape a website. I need to assign facts based on a user input i.e assign destination, current location, date etc.
As it is a chatbot I need experta to run infinitely until it is given an instruction to break or it completes it's task. For example if the chatbot asks, "what station are you at? " and the response is not a station, then it should repeat the question.
At the moment my rules will fire once and then the process will exit. How can i implement this looping system?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.