I am developing an expert system which will make admission decisions using PyClips. However, the code kept generating 'out of memory' errors. I think I have isolated my problem. It is something wrong with my CLIPS file. Hoping someone can see what I'm doing wrong here. This is the most simple version of the code....I simplified in order to debug it: So here's my template, and for the sake of the argument, there's only 1 rule: if the transcript is received, then the app-complete attribute is marked as TRUE.
; template for application facts
(deftemplate application "structure of an application"
(slot app-id (type INTEGER))
(slot app-complete (type SYMBOL))
(slot transcript-received (type SYMBOL))
)
(defrule complete "rule for app completeness"
?f <- (application
(transcript-received Yes)
)
=>
(modify ?f
(app-complete TRUE)
)
)
; end.
So when I do (assert (application (app-id 123) (transcript-received Yes)))
then, I fact is added.
When I click run though....the window in CLIPS starts getting overloaded with thousands of facts...the app-complete attribute looks to be marked as TRUE, however, the facts just keep looping and looping, nonstop. When it gets to be too many facts, I'm talking about 100K or something...then CLIPS just quits....Any idea what I'm doing wrong in here? My syntax is messed up somehow? My intentions are to then have a sqlite db to be able to read the 'facts' from the db for the system to be able to make decisions....but I can't get past this!
The important point to remember is that when you modify a deftemplate
fact, the fact is retracted and a new (modified) fact is asserted. Your rule is also matching the modified fact, which is then again modified and matched, etc., resulting in an infinite loop. If you were to watch facts and activations when you run your code, you would see something like this:
<== f-1 (application (app-id 123) (app-complete nil) (transcript-received Yes))
==> f-2 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> Activation 0 complete: f-2
<== f-2 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> f-3 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> Activation 0 complete: f-3
<== f-3 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> f-4 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
==> Activation 0 complete: f-4
(etc.)
You can prevent this by only matching applications that aren't already complete. Here is a modified version of your code that adds a default FALSE
value to the app-complete
slot and only matches applications that are not complete:
(deftemplate application "structure of an application"
(slot app-id (type INTEGER))
(slot app-complete (type SYMBOL) (default FALSE))
(slot transcript-received (type SYMBOL)))
(defrule complete "rule for app completeness"
?f <- (application (transcript-received Yes) (app-complete FALSE))
=>
(modify ?f (app-complete TRUE)))
Now, if you watch facts and activations, you should see the following when you assert your fact and run:
CLIPS> (assert (application (app-id 123) (transcript-received Yes)))
==> f-0 (application (app-id 123) (app-complete FALSE) (transcript-received Yes))
==> Activation 0 complete: f-0
<Fact-0>
CLIPS> (run)
<== f-0 (application (app-id 123) (app-complete FALSE) (transcript-received Yes))
==> f-1 (application (app-id 123) (app-complete TRUE) (transcript-received Yes))
CLIPS>