forward-chainer.org 4.2 KB

Forward Chaining Inference with Common Lisp

Just for kicks, I'm going to build a forward chaining inference system using Common Lisp. This is going to be a challenge, but I'd like to do it in part to expand my knowledge, and also to build a free-software knowledge representation system that works well and is adaptable for various purposes.

Requirements and Package Definition

This system requires very little in the way of outside packages, only one, in fact, optima, a very high-power pattern matching library, used to implement the matching portion of the system.

  (defpackage #:forward-chainer
    (:use #:cl
          #:optima
          #:fare-quasiquote-optima
          #:fare-quasiquote-readtable)
    (:export #:assert-knowledge
             #:assert-rule
             #:query-knowledge))

  (in-package #:forward-chainer)
  (named-readtables:in-readtable :fare-quasiquote)

Knowledge Database

Two of the most important parts of the forward-chainer are the knowledge and rules database. Both of these are lists of lists, used to provide knowledge management.

  (defvar *knowledge-database* '()
    "Knowledge Database, a list of lists.")

  (defvar *rules* '()
    "Rules list, each rule is a cons of a name and a rule object.")

The assert-knowledge Macro

  (defmacro assert-knowledge ((name &rest arguments))
    (let ((knowledge `'(,name ,@arguments)))
      `(pushnew ,knowledge *knowledge-database* :test #'equalp)))

The remove-knowledge Macro

  (defmacro remove-knowledge ((name &rest arguments))
    (let ((knowledge `'(,name ,@arguments)))
      `(delete ,knowledge *knowledge-database* :test #'equalp)))

The assert-rule Macro

  (defmacro assert-rule ((name &rest arguments) matcher &rest actions)
    (declare (ignorable name arguments matcher actions))
    )

The query-knowledge Macro

  (defmacro query-knowledge ((name &rest arguments))
    (let* ((match-variables (remove-if (lambda (var)
                                         (not (and (typep var 'symbol) (string= "?" (subseq (format nil "~a" var) 0 1)))))
                                       arguments))
           (matcher `'(,name ,@arguments)))
      `(map 'list #'(lambda (knowledge)
                      (match knowledge ',matcher))
            ,*knowledge-database*)))

Putting it together

  ;;; forward-chainer.lisp

  <<package-definition>>

  ;; Begin main forward-chainer

  <<assert-knowledge>>

  <<remove-knowledge>>

  <<assert-rule>>

  <<query-knowledge>>

  <<knowledge-database>>

  ;;; End of "forward-chainer.lisp"
*RULES*