Thanks for publishing Pyke-- it's really a terrific tool: I've really appreciated how the .krb rules are actually readable (unlike almost every other rules-based system I've seen), and how I can use my existing python objects with it, rather than having to declare all my known objects in some custom language. For making a single decision, pyke is completely awesome. So here's a question: how hard would it be to add the ability to revoke facts? I've done the trick where you cache all the case specific facts, then revoke one by resetting the knowledgebase, and adding back all the cached facts except the one which is no longer valid... but this approach seems really really inefficient. I've noticed references to Rete in other postings here; but I can't say I understand that enough to know whether that applies here, or what other scope is involved in doing this efficiently.
One idea for an API which could do this would be to add a transaction, so that the rules are reevaluated only when the transition is committed:
transaction = engine.newTransaction()
transaction.add_fact("kb", "fact1", (args,))
transaction.remove_fact("kb", "fact2", (args,))
Would this approach make any sense? Any notes on what changes make sense for revoking facts? Is this just completely impossible? I think with enough clues, I'm quite happy to make the changes... but so far, it looks like a few more clues would be necessary. Thanks in advance!
I have two questions concerning revoking facts.
1. What situations are you considering where this would be required? I know that forward-chaining productions systems like ops-5 and clips allow revoking facts, but have never used these, so don't know why/when this would be done. But it sounds like you aren't wanting to revoke facts during the processing of rules, but between times without having to do a full reset. Are you aware of engine.add_universal_fact? These facts are not deleted when a reset is done, but otherwise act like normal facts.
2. What would you expect to happen when you revoke a fact? For example, would you expect that forward-chaining rules that were fired based on that fact's existence would be "un-fired", revoking any facts asserted by these rules?
I've not used clips or ops-5 either; so I don't have any specific knowledge to add from those. But you can imagine that if an application has a current state--where the state is the collection of all the facts and forward chained productions--that you could proceed from one state to the next by varying the current facts; then use backward chaining do decide what to do next... and executing those plans would probably alter your current state, creating a sort of a feedback loop. Your code generator might be a good example for this: suppose that you're feeding in pieces of a program's source code; and that some of those source artifacts can be dropped once a plan has generated the right object code. The facts would want to continue to know stuff like 'variable a is in register r0', etc. When variable a goes out of scope, r0 wouldn't have any more facts specifically talking about it. (I also like the idea of saving these states, permitting a kind of undo.)
Changing facts wouldn't happen during searches--so calling prove or prove_1 wouldn't change the state. Adding new facts or revoking existing ones would cause forward chaining to take place; so productions that are no longer valid would disappear: I've not specifically used any code with forward chained rules, so it's hard for me to recommend what might happen when that production is lost--seems like a little snooping on ops-5 or clips would be valuable here.
It's pretty important to allow updating of more than one fact in an atomic action, so that you can safely pass through what logic designers call 'hazards': when you're changing more than one input, between the time that the first input changes and the last one is done, that partially updated set of inputs might produce inconsistent forward chained productions: that's why I suggested the transaction interface (a pattern I've boldly borrowed from databases without permission)... you may have a better idea.
I'm not sure how to undo FC productions when facts are revoked. Currently, you can reset the engine, reassert all relevant facts, activate the rule base(s) of choice and then do your backward-chaining for each iteration that you speak of above. This undoes all FC productions (in the reset) and also allows you to decide to use different rule bases on each iteration (which may have different FC rules, in addition to different BC rules).
In the approach above, the calling code must remember the facts that it wants to keep. You could always write a fact storage facility that lets you store and revoke facts in the fact storage, and then assert all remaining facts in the fact storage to pyke each time through the iteration. You could probably use python's built-in sets to do this very efficiently. This should provide you with what you are looking for without the need for transaction semantics and undoing FC productions in pyke.
Finally, as I understand it, ops5 and clips don't undo productions when facts are revoked that those productions depended on. If anybody that's worked with either of these can verify this, I'd appreciate it!
So I've spent some time learning more about how other systems work; specifically, prolog. What they do is avoid any forward chaining entirely. Instead, you submit your facts and rules; and then use queries to activate those rules--without a query touching it, a rule wouldn't ever run. Given that, I set up one super-rule, which tried to match each thing that I would have specified as a forward-chained rule in pyke... and as long as all those were matched with an OR condition, my queries against that super rule caused it to behave pretty much the same way that pyke does. Prolog is almost entirely based around backward chaining, so they give you that stuff for free. They also give you 'assert' and 'retract' functors, which adds or removes items in the database; and the implementation I played with (SWI-prolog) makes it so that any assert or retract isn't seen until after the query is finished.
There are quite a few python to prolog interfaces, all of which are interesting; but they all seem to be lacking in a few important ways, all of which pyke got right: you should be able to have more than one independent knowledge base in a single program; you should be able to trasparently use python objects as terms or atoms, and allowing a python function as a part of a predicate (which makes it easy to use imperative programming when convenient) really takes care of the scaling and logical difficulties that prolog seems constrained with.
One such situation where facts might be transient might be a rule like mouse_in_window() (check $mouseX>=$activeWindowLeft and $mouseX<$activeWindowRight, etc…), where this fact triggers a particular plan while true but is certainly not constant (where as for the most part in the examples like siblings, family relationships, or each isolated URI request) but where one wants the code to properly react when the condition changes as we certainly don't want our plan (using wxPython perhaps) reacting to events outside the window; however, having to reset and reconfigure the plan whenever the mouse pointer moves would be prohibitively expensive computationally.
Log in to post a comment.