Show/Hide Toolbars

RiverSoftAVG Products Help

Writing efficient and robust expert systems is an art.  The following tips are meant as general guidelines, most of the time they help but your specific expert system may require a different structure.  You will notice that some of these tips contradict each other, trial and error is the only way to see what works best for you.


When writing rules, order patterns (conditions) to limit the number of partial matches


Pattern matches are matches made between conditions of a rule and the facts in the fact base.  Reducing the number of partial pattern matches reduces the amount of work the inference engine has to do.  There are three ways to limit partial pattern matches:


1.Put the patterns that can match the fewest facts at the beginning of the LHS of the rule. For example, a condition like "(person ?p)" is much more general and produces many more matches than "(person (name tom))".

2.Put facts that will be asserted and retracted often at the end of the LHS of the rule. Every time a fact is asserted or retracted, the inference engine must compare that fact against patterns that match that fact.  The inference engine is smart enough to ignore rules which have no conditions that can possibly equal the fact, but it will try to perform a match unless the rule has already been rejected because of some earlier condition.

3.Put the most specific patterns at the beginning of the LHS of the rule. For example, if you know that you need three fields from a fact, specify the three fields.  This ensures that the inference engine does not even try to match a fact with two fields, e.g., "(vector ?x ?y ?z)" will match fewer facts than "(vector ?*vector)" which will match 1D vectors, 2D vectors, and 3D vectors.


When using multiple TInferenceEngine components, use external TUserPackages.


By default, every TInferenceEngine component creates the user functions it needs to execute properly.  However, if you are using multiple TInferenceEngine components, you can save significant memory and execution time by sharing the functions.  To share the functions perform the following steps:


1.For all your TInferenceEngine components, disable their default packages. At design-time, select your TInferenceEngine components and set the Packages property to empty.

2.Add the packages you need to your form. Drop the packages you need, TStandardPackage, TMathPackage, etc. onto the form.

3.Connect the packages to all the TInferenceEngine components. At design-time, the inference engine architecture allows you to connect the external packages to only one TInferenceEngine component.  However, at run-time, you can connect them to however many engines you want.  To connect the package to a TInferenceEngine component, put the following code in the form's OnCreate event handler:

Package.Reasoners.Add( InferenceEngine1 as IReasoner );


Speed up complex interpreted functions or RHS actions by converting them to compiled user functions


The Inference Engine component suite is very powerful by allowing you to specify deffunctions which are interpreted functions.  At run-time, the deffunction is interpreted and executed.  While powerful, this can be slow especially if the function performs many complex actions and functions calls (say a math algorithm).  Once you have debugged and frozen an interpreted function, it can be much faster to redefine the function as a user function which can be compiled into your application.  To define a user function:


1.Drop a TUserFunction component onto your form or data module. Set the Engine property to the TInferenceEngine component that you are using.  Also, set the FunctionName property to define the name by which the inference engine recognizes and calls your function.

2.Define an OnCall event handler for the component. This is the code that executes when the function is called.  The FunCall parameter defines how this function was called, what arguments were passed in, etc.  Use the ArgumentCount and Argument properties to get the calling arguments for the functions.  Remember that these arguments are unresolved, e.g., if a variable was passed in, then the variable name is the argument (this can be very powerful because you can have functions that manipulate the variable names, function calls, etc passed to them)  Remember to resolve the arguments by calling their Resolve method (FunCall.Argument[0].Resolve(Context)).  The Context parameter defines the context or scope that the rule is firing in, it contains any bound variables, global variables, etc (see TIEContext for more details).  Finally, if the function returns a result, assign it to the Result parameter (even if the function doesn't return a result, it is safer to assign FCNil or FCFalse to the result (result := FCNil)).


Speed up interpreted rules by creating compiled rules


The standard rules you define using the defrule keyword in a script/file or setting the Rules property of the TInferenceEngine component are built and executed at run-time.  As such, these rules are interpreted.  When a rule fires, each action is popped off the list, resolved and executed.  Especially for complex actions, such as the while function or foreach function, execution can be sped up by compiling the RHS of a rule.  To create a rule at design-time:


1.Drop a TRule component onto your form or data module. Make sure the Engine property is set to the TInferenceEngine component you are using.

2.Double click the TRule component to bring up the Rule Editor for this component.

3.Define the IF/Conditions portion of the rule to control under what conditions the rule fires. Leave the THEN/Actions portion of the rule blank.  Close the editor when you are finished.

4.Define an OnFire Event Handler to specify the THEN/Actions the rule should take when the rule fires. The Context parameter defines the context or scope that the rule is firing in, it contains any bound variables, global variables, etc (see TIEContext for more details).


Limit the shared information between DefModules.


DefModules allow sharing information between modules.   In addition to completely separate constructs, the IECS allows you to selectively share Type Definitions, Fact Templates, and Facts. Sharing occurs by having a DefModule export defined types and/or fact templates and having other DefModules import those structures from the exporting module.  When a shared fact is asserted, the fact is added to every shared module's fact list. Note that shared facts, while very convenient, can be inefficient.  If a shared fact type is constantly being retracted and asserted, that fact is being retracted and asserted in all sharing DefModules.  It is recommended for efficiency to share few facts and only share facts that will not be constantly updating.


RiverSoftAVG Products Help © 1996-2016 Thomas G. Grubb