Optionally, you can partition your expert system into multiple modules (defmodule). Modules allow you to define the facts and rules for a single domain, yet share information between modules. Partitioning your expert system into multiple modules can make a lot of senseE by enabling simpler and easier to debug rules. Each module has its own Type Definitions, Fact Templates, Facts, Rules, Fact Sets, Agenda, and Rete network. The IECS allows you to execute in one module, pause its execution, execute in another module, and then switch (focus) back. Depending on the structure of your rules and how much information is shared between modules, modules can also make inference faster.
To work with defmodules, think about your problem domain and how it can be structurally divided. Do you have logical steps for your expert system? For example, does your expert system needs an input phase, an evaluation phase, and an output phase? Each of these phases are possible candidates for a separate module. Each phase can have its own rules and facts for doing the work of that phase, without worrying about the other domains. You can then share information with other modules so that they can do their work.
You should define your modules before you starting adding facts, rules, etc to your expert system. When you define a defmodule, you define which Defined Types and Fact Templates it exports, as well as which Defined Types and Fact Templates it imports from other modules. Note that the fact template or type definition does not have to exist (and usually will not exist) to export it. To define these modules, you would use the defmodule command:
(export deftemplate candidate ))
(export deftemplate score )
(import INPUT deftemplate candidate))
(import EVALUATION deftemplate score))
Note that INPUT module exports its candidate fact template (and facts), which are imported by the EVALUATION module. In turn, the EVALUATION module exports its score fact template (and facts), which are imported by the OUTPUT phase. Sharing occurs by having a DefModule export defined types and/or fact templates and having other DefModules import those structures from the exporting module. Circular references are not allowed (e.g., Module1 cannot export fact templates to Module2 AND import fact templates from Module2). However, facts are completely shareable between DefModules. Facts based on shared fact templates may be asserted or retracted from any module that shares that fact template. When a shared fact is asserted, the fact is added to every shared module's fact list. So in the example above, if the EVALUATION module asserts a new score fact the INPUT module will see the new fact. 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.
When a module is created, it becomes the default current module. Any new DefType, DefTemplate, DefRule, or DefFacts commands or Assertions and Retractions occur in the current module's domain space. To create DefType, DefTemplate, DefRule, or DefFacts in another module, you need to prepend the module name to the structure defintion, e.g.,
(printout t "Hello INPUT Module"))
By defining the module name, you change the default current module. In the example above, not only is MyRule added to the INPUT module's rule base, but the current module becomes INPUT. To get the current module, use the get-current-module command, e.g., (get-current-module). To change the current module, use the set-current-module command, e.g., (set-current-module OUTPUT). Not only does the set-current-module command switch which module has focus, it returns the last module that had focus (in our example, it would return INPUT).
For each module, you need to:
The IECS uses something called the Focus Stack to maintain a list of modules to obtain execution focus during inference. The expert system works by getting the current focus from the focus stack, matching facts to rules on the current module, infer new facts (which may be shared with other modules), and, when the current module's Agenda is empty, get the next module off the focus stack.
Note that as shared facts are asserted or retracted, the inference engine performs the pattern matching for every module that shares the fact, which updates their rete network and agenda. This update of a module (even the non-focused one) happens immediately upon assertion/retraction and is not deferred until the module gets focus. This is necessary as a modified fact must be processed by a rete network to avoid inference errors. Also, if a rule has declared it wants AutoFocus, then the inference engine is supposed to immediately switch focus to that rule's focus when it is activated.
When the Focus Stack is empty and there are no more activations on the current module's agenda, inference stops.
Use the focus command (e.g., (focus INPUT EVALUATION OUTPUT)) to define the focus stack. By setting the focus, you change the execution focus (and current module) to the new module. A subsequent run command will execute the agenda of that module. The focus command not only changes the focus to the new module but returns the module that previously had focus (the previous module is still on the focus stack but is no longer at the top). By specifying more than one module to the focus command, the modules are pushed onto the focus stack from right to left, i.e., the left module will have active focus and be executed first. To see the focus stack, use the list-focus-stack command (e.g., (list-focus-stack)). The clear-focus-stack command clears the focus stack. Note that when the focus stack is empty, the inference engine pushes the MAIN module unto the focus-stack before execution so that some inference always occurs.
You can use the focus command to change the focus and the focus stack. However, there are two other commands for manipulating the focus stack: pop-focus and return. Pop-focus removes the current focus from the focus stack and changes focus to the next module on the focus stack. If the pop-focus command is used in the right hand side of a rule, the rule finishes executing before the focus changes. The return command, when used in the right hand side of a rule without parameters (e.g., (return)), not only pops the current focus off the stack but immediately stops executing the rest of the rule's actions. Finally, remember that the auto-focus declaration when defining a rule will cause the inference engine to change focus when that rule is activated.
The following expert system will show some fact sharing and focus control. It defines two modules: MAIN and DETECTION.
The MAIN module exports the Initial-Fact deftemplate. It has one rule, start, which when executed changes the focus to the DETECTION module.
The DETECTION module imports the Initial-Fact deftemplate. It defines 2 rules, one using pop-focus and one using return.
(export deftemplate Initial-Fact ))
(deftemplate Initial-Fact "(Implied)")
(import MAIN deftemplate Initial-Fact ))
(printout t "Will printout" crlf))
(printout t "No printout" crlf))
By watching the focus and then running, we can see the start rule fire and change focus to the DETECTION module. In the DETECTION module, the example-1 rule fires. It changes focus to MAIN. It also returns prints out some text. Since there are no more activations on MAIN's agenda, execution stops.
FIRE 0 start f-0
==> Focus DETECTION from MAIN
FIRE 1 example-1 f-0
==> Focus DETECTION to MAIN
<== Focus MAIN
If you notice, example-2 is still on DETECTION's agenda:
0 example-2 : f-0 (1.0000)
for a total of 1 activations.
If we switch focus to DETECTION and run, example-2 will fire. It too switches focus to the MAIN module. However, the printout function never gets called!
==> Focus DETECTION
FIRE 0 example-2 f-0
<== Focus DETECTION