Years ago, I studied computer programming. I was a decent programmer and worked for a few years in that domain. It must have been around 5 years.
Currently, my interest in programming has started once again. Honestly, when I started studying programming 1995, I really didn’t what I was getting myself into. It was only later that I really took a keen interest. I used to program in Visual Basic and C++. At the same time, I decided to pursue my interest in mathematics fulltime.
Right now, Python (with Pyke), AJAX, Ruby on Rails have my interest. Since I will be pursuing more scholarly subjects in the coming year, I plan on studying them and learning them as well. Since I have studied computer programming, I think that this will be a good challenge.
I was intrigued to learn of Pyke. The artificial intelligence angle got my interest. From the documentation on the website, Pyke is an inference engine completely written in Python. Simplistically, an inference engine is what an expert system uses to make decisions.
Let’s remind ourselves what exactly an inference engine is:
In computer science, and specifically the branches of knowledge engineering and artificial intelligence, an inference engine is a computer program that tries to derive answers from a knowledge base. It is the “brain” that expert systems use to reason about the information in the knowledge base for the ultimate purpose of formulating new conclusions. Inference engines are considered to be a special case of reasoning engines, which can use more general methods of reasoning.
Pyke is a knowledge-based inference engine (expert system) written in 100% python that can:
- Do both forward-chaining (data driven) and backward-chaining (goal directed) inferencing.
- Pyke may be embedded into any python program.
- Automatically generate python programs by assembling individual python functions into complete call graphs.
- This is done through a unique design where the individual python functions are attached to backward-chaining rules.
- Unlike other approaches to code reuse (e.g. Zope adapters and generic functions), this allows the inference engine to ensure that all of the function’s requirements are completely satisfied, by examining the entire call graph down to the leaves, before any of the functions are executed.
- This is an optional feature. You don’t need to use it if you just want the inferencing capability by itself.
- Multiple fact bases, each with its own list of facts.
- Both forward-chaining rules and backward-chaining rules.
- Multiple rule bases, each with its own list of forward-chaining and/or backward-chaining rules.
- Rule base inheritance — activating the derived rule base includes the rules from the parent rule base.
- The inference rules are compiled into python functions, allowing python code snippets to be used within the rules. This greatly enhances the expressiveness of the rules.
- Calls the generated python programs plans.
- Plans may be run multiple times without needing to rerun the inference rules.
- Plans may be pickled and cached to disk to be used by other programs or in later runs of the same program.
- No pyke modules are required to run the plans.
- Complicated decision making applications.
- The back-end (code generation and optimization) of compilers. Pyke is used as the back-end of its own compiler that translates rules into python code.
- Automatic SQL statement generation.
- Automatic HTML generation and automatic HTML template processing.
- Automatic program builder to reuse a common set of functions for many different specific situations. This could also easily incorporate a new custom function into a much larger program, where the use of the custom function might influence the choice of other standard functions in other parts of the program.
- The control module for a web framework tool.
- A high-level planner to automatically distribute the modules of a large system over several computers in a distributed system to meet specific performance and capacity goals. This could be used to automatically scale the same system code from a small one program, one computer system to much larger distributed systems to meet a wide range of performance goals.
- Diagnosis systems, including automated customer service systems.
- Program or library customization for specific uses.
- In addition to being able to build programs, pyke can instantiate, configure and interconnect a network of objects to meet a specific need or situation.
Pyke is an inference engine that applies rules to facts to establish additional facts (through forward-chaining rules), and/or to prove goals and optionally assemble python functions into customized call graphs, called plans (through backward-chaining rules).
Knowledge is made up of both facts and rules. These are organized into named repositories called knowledge bases. A knowledge base is like a directory for files on disk, except that knowledge bases may not be nested. Therefore, facts and goals always have a two-level name.
Here are some examples of facts you might see in a web server application:
header.cookie('session=123456789;remember=the_alamo') cookie.session(123456789) cookie.remember(the_alamo) header.accept_encoding(gzip) header.accept_encoding(deflate) request.path('/my/site.html') request.path_segment(0, my) request.path_segment(1, 'site.html') request.path_segment(-2, my) request.path_segment(-1, 'site.html')
Note that three different knowledge bases (all fact bases) are shown here named header, cookie, and request; each with multiple facts.
There are different types of knowledge bases.
- Those that contain facts are called fact bases.
- Those that contain rules are called rule bases.
- It is also possible to create other kinds of knowledge bases that lookup facts and prove goals in different ways. The only one of these in pyke is the special knowledge base.
All knowledge bases share the same name space; so that no two of them, regardless of their type, may have the same name.
Rules have two parts to them: an if part (the premises), and a then part (the conclusions). (Though pyke uses different names for these). Each of these if and then parts contain one or more facts or goals represented by patterns.
Logically, the rule says that if all of the premises in the if part of the rule are true, then each of the conclusions in the then part of the rule must also be true.
Rules are specified individually within a rule base. They are not nested or explicitly linked to each other. So pyke must automatically figure out how to combine these rules to accomplish some larger task. This is called inferencing and there are two different approaches that pyke uses, depending on the rule’s type.
- All forward-chaining rules are processed when a rule base is activated.
- Backward-chaining rules are processed when your program asks pyke to prove a specific goal.
Plans and Automatic Program Generation
Once you understand how backward-chaining works, it is relatively easy to do automatic program generation.
The way this is done is by attaching python functions to the backward-chaining rules. These functions are written at the end of each rule in the .krb file. They don’t affect how the rules run, but are gathered up to form a call graph that is returned along with the pattern variable bindings that prove the top-level goal.
Consider a small rule base to construct programs to transfer money between bank accounts. Each from_acct and to_acct takes one of two forms:
- (name, account_type)
- This is a local account with this bank.
- Example: (‘bruce’, ‘checking’).
- (bank, name, account_type)
- This is a foreign account with another bank.
- Example: (‘my_other_bank’, ‘bruce’, ‘checking’).
At least one of the bank accounts must be a local account.
Here’s the example rule base:
1 transfer1: 2 use transfer($from_acct, $to_acct) taking (amount) 3 when 4 withdraw($from_acct) 5 $$(amount) 6 deposit($to_acct) 7 $$(amount) 8 transfer2: 9 use transfer($from_acct, $to_acct) taking (amount) 10 when 11 transfer_ach($from_acct, $to_acct) 12 $$(amount) 13 withdraw: 14 use withdraw(($who, $acct_type)) taking (amount) 15 with 16 print "withdraw", amount, "from", $who, $acct_type 17 deposit: 18 use deposit(($who, $acct_type)) taking (amount) 19 with 20 print "deposit", amount, "to", $who, $acct_type 21 transfer_ach1: 22 use transfer_ach($from_acct, ($bank, $who, $acct_type)) taking (amount) 23 when 24 withdraw($from_acct) 25 $$(amount) 26 deposit((central_accts, ach_send_acct)) 27 $$(amount) 28 with 29 print "send", amount, "to bank", $bank, "acct", $who, $acct_type 30 transfer_ach2: 31 use transfer_ach($from_acct, $to_acct) taking (amount) 32 when 33 get_ach($from_acct) 34 $$(amount) 35 withdraw((central_accts, ach_recv_acct)) 36 $$(amount) 37 deposit($to_acct) 38 $$(amount) 39 get_ach: 40 use get_ach(($bank, $who, $acct_type)) taking (amount) 41 with 42 print "get", amount, "from bank", $bank, "acct", $who, $acct_type
The plan is created as a byproduct of proving the goal:
>>> import pyke >>> pyke.load('examples') >>> pyke.activate('plan_example') >>> no_vars, plan1 = pyke.prove_1('plan_example', 'transfer', ... (('bruce', 'checking'), ... ('bruce', 'savings')), ... 0)
plan1 is now a program to transfer X amount from ‘bruce’, ‘checking’ to ‘bruce’, ‘savings’.
>>> plan1(100) withdraw 100 from bruce checking deposit 100 to bruce savings
The program may be used multiple times:
>>> plan1(50) withdraw 50 from bruce checking deposit 50 to bruce savings
Notice the strings: bruce, checking and savings in the output. These were specified as pattern variables in the code and are cooked into the plan along with the function call graph.
Let’s create another program:
>>> no_vars, plan2 = pyke.prove_1('plan_example', 'transfer', ... (('my_other_bank', 'bruce', 'checking'), ... ('bruce', 'savings')), ... 0)
plan2 is now a program to transfer X amount from ‘my_other_bank’, ‘bruce’, ‘checking’ to ‘bruce’, ‘savings’.
>>> plan2(150) get 150 from bank my_other_bank acct bruce checking withdraw 150 from central_accts ach_recv_acct deposit 150 to bruce savings
And the final use case:
>>> no_vars, plan3 = pyke.prove_1('plan_example', 'transfer', ... (('bruce', 'checking'), ... ('my_other_bank', 'bruce', 'savings')), ... 0) >>> plan3(200) withdraw 200 from bruce checking deposit 200 to central_accts ach_send_acct send 200 to bank my_other_bank acct bruce savings
So you can see that it quite easy to use pyke to automatically combine python functions into programs!
It also allows data within each python function to be specified using a pattern variable so that pyke can customize these values to match the specific situation.
Most text taken from the Pyke homepage.