;;;====================================================== ;;; Circuit Input/Output Simplification Expert System ;;; ;;; This program simplifies the boolean decision ;;; table for a circuit consisting of inputs (SOURCES) ;;; and outputs (LEDs). ;;; ;;; The simplification procedure works as follows: ;;; 1) The connections between components of the ;;; circuit are initialized. ;;; 2) The response of the circuit when all SOURCEs ;;; are set to zero is determined. ;;; 3) Source input values are changed one at a time ;;; and the response of the circuit is determined. ;;; All possible input combinations are iterated ;;; through using a gray code (a number representation ;;; system using binary digits in which successive ;;; integers differ by exactly one binary digit). ;;; For example, the gray code for the numbers 0 to 7 ;;; is 0 = 000, 1 = 001, 2 = 011, 3 = 010, 4 = 110, ;;; 5 = 111, 6 = 101, 7 = 100. By using a gray code, ;;; only one SOURCE has to be changed at a time to ;;; determine the next response in the decision ;;; table (minimizing execution time). ;;; 4) As responses are determined, a rule checks to ;;; see if any two sets of inputs with the same ;;; response differ if a single input. If so, then ;;; the single input can be replaced with a * ;;; (indicating that it does not matter what the ;;; value of the input is given the other inputs). ;;; For example, if the input 0 1 0 gave a response ;;; of 1 0 and the input 0 0 0 gave the same response, ;;; then the decision table can be simplified by ;;; indicating that 0 * 0 gives a response of 1 0. ;;; 5) Once all responses and simplifications have been ;;; determined, the decision table for the circuit is ;;; printed. ;;; ;;; This example illustrates the use of most of the ;;; constructs available in CLIPS 5.0 and also shows how ;;; COOL can be effectively integrated with rules. ;;; Generic functions are used to connect the components ;;; of the circuit during initialization. Classes, ;;; message-handlers, and deffunctions are used to ;;; determine the response of the circuit to a set of ;;; inputs. Rules, deffunctions, and global variables ;;; are used to control execution, iterate through all ;;; possible input combinations, simplify the boolean ;;; decision tree, and print out the simplified decision ;;; tree. ;;; ;;; CLIPS Version 6.0 Example ;;; ;;; To execute, load this file, load one of the circuit ;;; files (circuit1.clp, circuit2.clp, or circuit3.clp), ;;; reset, and run. ;;;====================================================== ;;;*********** ;;; DEFCLASSES ;;;*********** (defclass COMPONENT (is-a USER) (slot ID# (create-accessor write))) (defclass NO-OUTPUT (is-a USER) (slot number-of-outputs (access read-only) (default 0) (create-accessor read))) (defmessage-handler NO-OUTPUT compute-output ()) (defclass ONE-OUTPUT (is-a NO-OUTPUT) (slot number-of-outputs (access read-only) (default 1) (create-accessor read)) (slot output-1 (default UNDEFINED) (create-accessor write)) (slot output-1-link (default GROUND) (create-accessor write)) (slot output-1-link-pin (default 1) (create-accessor write))) (defmessage-handler ONE-OUTPUT put-output-1 after (?value) (send ?self:output-1-link (sym-cat put-input- ?self:output-1-link-pin) ?value)) (defclass TWO-OUTPUT (is-a ONE-OUTPUT) (slot number-of-outputs (access read-only) (default 2) (create-accessor read)) (slot output-2 (default UNDEFINED) (create-accessor write)) (slot output-2-link (default GROUND) (create-accessor write)) (slot output-2-link-pin (default 1) (create-accessor write))) (defmessage-handler TWO-OUTPUT put-output-1 after (?value) (send ?self:output-2-link (sym-cat put-input- ?self:output-2-link-pin) ?value)) (defclass NO-INPUT (is-a USER) (slot number-of-inputs (access read-only) (default 0) (create-accessor read))) (defclass ONE-INPUT (is-a NO-INPUT) (slot number-of-inputs (access read-only) (default 1) (create-accessor read)) (slot input-1 (default UNDEFINED) (visibility public) (create-accessor read-write)) (slot input-1-link (default GROUND) (create-accessor write)) (slot input-1-link-pin (default 1) (create-accessor write))) (defmessage-handler ONE-INPUT put-input-1 after (?value) (send ?self compute-output)) (defclass TWO-INPUT (is-a ONE-INPUT) (slot number-of-inputs (access read-only) (default 2) (create-accessor read)) (slot input-2 (default UNDEFINED) (visibility public) (create-accessor write)) (slot input-2-link (default GROUND) (create-accessor write)) (slot input-2-link-pin (default 1) (create-accessor write))) (defmessage-handler TWO-INPUT put-input-2 after (?value) (send ?self compute-output)) (defclass SOURCE (is-a NO-INPUT ONE-OUTPUT COMPONENT) (role concrete) (slot output-1 (default UNDEFINED) (create-accessor write))) (defclass SINK (is-a ONE-INPUT NO-OUTPUT COMPONENT) (role concrete) (slot input-1 (default UNDEFINED) (create-accessor read-write))) ;;;******************* ;;; NOT GATE COMPONENT ;;;******************* (defclass NOT-GATE (is-a ONE-INPUT ONE-OUTPUT COMPONENT) (role concrete)) (deffunction not# (?x) (- 1 ?x)) (defmessage-handler NOT-GATE compute-output () (if (integerp ?self:input-1) then (send ?self put-output-1 (not# ?self:input-1)))) ;;;******************* ;;; AND GATE COMPONENT ;;;******************* (defclass AND-GATE (is-a TWO-INPUT ONE-OUTPUT COMPONENT) (role concrete)) (deffunction and# (?x ?y) (if (and (!= ?x 0) (!= ?y 0)) then 1 else 0)) (defmessage-handler AND-GATE compute-output () (if (and (integerp ?self:input-1) (integerp ?self:input-2)) then (send ?self put-output-1 (and# ?self:input-1 ?self:input-2)))) ;;;****************** ;;; OR GATE COMPONENT ;;;****************** (defclass OR-GATE (is-a TWO-INPUT ONE-OUTPUT COMPONENT) (role concrete)) (deffunction or# (?x ?y) (if (or (!= ?x 0) (!= ?y 0)) then 1 else 0)) (defmessage-handler OR-GATE compute-output () (if (and (integerp ?self:input-1) (integerp ?self:input-2)) then (send ?self put-output-1 (or# ?self:input-1 ?self:input-2)))) ;;;******************** ;;; NAND GATE COMPONENT ;;;******************** (defclass NAND-GATE (is-a TWO-INPUT ONE-OUTPUT COMPONENT) (role concrete)) (deffunction nand# (?x ?y) (if (not (and (!= ?x 0) (!= ?y 0))) then 1 else 0)) (defmessage-handler NAND-GATE compute-output () (if (and (integerp ?self:input-1) (integerp ?self:input-2)) then (send ?self put-output-1 (nand# ?self:input-1 ?self:input-2)))) ;;;******************* ;;; XOR GATE COMPONENT ;;;******************* (defclass XOR-GATE (is-a TWO-INPUT ONE-OUTPUT COMPONENT) (role concrete)) (deffunction xor# (?x ?y) (if (or (and (= ?x 1) (= ?y 0)) (and (= ?x 0) (= ?y 1))) then 1 else 0)) (defmessage-handler XOR-GATE compute-output () (if (and (integerp ?self:input-1) (integerp ?self:input-2)) then (send ?self put-output-1 (xor# ?self:input-1 ?self:input-2)))) ;;;******************* ;;; SPLITTER COMPONENT ;;;******************* (defclass SPLITTER (is-a ONE-INPUT TWO-OUTPUT COMPONENT) (role concrete)) (defmessage-handler SPLITTER compute-output () (if (integerp ?self:input-1) then (send ?self put-output-1 ?self:input-1) (send ?self put-output-2 ?self:input-1))) ;;;************** ;;; LED COMPONENT ;;;************** (defclass LED (is-a ONE-INPUT NO-OUTPUT COMPONENT) (role concrete)) ;;; Returns the current value of each LED ;;; instance in a multifield value. (deffunction LED-response () (bind ?response (create$)) (do-for-all-instances ((?led LED)) TRUE (bind ?response (create$ ?response (send ?led get-input-1)))) ?response) ;;;*************************** ;;; DEFGENERICS AND DEFMETHODS ;;;*************************** (defgeneric connect) ;;; Connects a one output component to a one input component. (defmethod connect ((?out ONE-OUTPUT) (?in ONE-INPUT)) (send ?out put-output-1-link ?in) (send ?out put-output-1-link-pin 1) (send ?in put-input-1-link ?out) (send ?in put-input-1-link-pin 1)) ;;; Connects a one output component to one pin of a two input component. (defmethod connect ((?out ONE-OUTPUT) (?in TWO-INPUT) (?in-pin INTEGER)) (send ?out put-output-1-link ?in) (send ?out put-output-1-link-pin ?in-pin) (send ?in (sym-cat put-input- ?in-pin -link) ?out) (send ?in (sym-cat put-input- ?in-pin -link-pin) 1)) ;;; Connects one pin of a two output component to a one input component. (defmethod connect ((?out TWO-OUTPUT) (?out-pin INTEGER) (?in ONE-INPUT)) (send ?out (sym-cat put-output- ?out-pin -link) ?in) (send ?out (sym-cat put-output- ?out-pin -link-pin) 1) (send ?in put-input-1-link ?out) (send ?in put-input-1-link-pin ?out-pin)) ;;; Connects one pin of a two output component ;;; to one pin of a two input component. (defmethod connect ((?out TWO-OUTPUT) (?out-pin INTEGER) (?in TWO-INPUT) (?in-pin INTEGER)) (send ?out (sym-cat put-output- ?out-pin -link) ?in) (send ?out (sym-cat put-output- ?out-pin -link-pin) ?in-pin) (send ?in (sym-cat put-input- ?in-pin -link) ?out) (send ?in (sym-cat put-input- ?in-pin -link-pin) ?out-pin)) ;;;**************************** ;;; DEFGLOBALS AND DEFFUNCTIONS ;;;**************************** (defglobal ?*gray-code* = (create$) ?*sources* = (create$) ?*max-iterations* = 0) ;;; Given the current iteration, determines the next ;;; bit in the gray code to change. ;;; Algorithm courtesy of John R. Kennedy (The BitMan). (deffunction change-which-bit (?x) (bind ?i 1) (while (and (evenp ?x) (!= ?x 0)) do (bind ?x (div ?x 2)) (bind ?i (+ ?i 1))) ?i) ;;; Forward declaration since the initial configuration ;;; is stored in a separate file. (deffunction connect-circuit ()) ;;;********* ;;; DEFRULES ;;;********* (defrule startup => ;; Initialize the circuit by connecting the components (connect-circuit) ;; Setup the globals. (bind ?*sources* (find-all-instances ((?x SOURCE)) TRUE)) (do-for-all-instances ((?x SOURCE)) TRUE (bind ?*gray-code* (create$ ?*gray-code* 0))) (bind ?*max-iterations* (round (** 2 (length$ ?*sources*)))) ;; Do the first response. (assert (current-iteration 0))) (defrule compute-response-1st-time ?f <- (current-iteration 0) => ;; Set all of the sources to zero. (do-for-all-instances ((?source SOURCE)) TRUE (send ?source put-output-1 0)) ;; Determine the initial LED response. (assert (result ?*gray-code* =(implode$ (LED-response)))) ;; Begin the iteration process of looping through the gray code combinations. (retract ?f) (assert (current-iteration 1))) (defrule compute-response-other-times ?f <- (current-iteration ?n&~0&:(< ?n ?*max-iterations*)) => ;; Change the gray code, saving the changed bit value. (bind ?pos (change-which-bit ?n)) (bind ?nv (- 1 (nth$ ?pos ?*gray-code*))) (bind ?*gray-code* (replace$ ?*gray-code* ?pos ?pos ?nv)) ;; Change the single changed source (send (nth$ ?pos ?*sources*) put-output-1 ?nv) ;; Determine the LED response to the input. (assert (result ?*gray-code* =(implode$ (LED-response)))) ;; Assert the new iteration fact (retract ?f) (assert (current-iteration =(+ ?n 1)))) (defrule merge-responses (declare (salience 10)) ?f1 <- (result $?b ?x $?e ?response) ?f2 <- (result $?b ~?x $?e ?response) => (retract ?f1 ?f2) (assert (result $?b * $?e ?response))) (defrule print-header (declare (salience -10)) => (assert (print-results)) (do-for-all-instances ((?x SOURCE)) TRUE (format t " %3s " (sym-cat ?x))) (printout t " | ") (do-for-all-instances ((?x LED)) TRUE (format t " %3s " (sym-cat ?x))) (format t "%n") (do-for-all-instances ((?x SOURCE)) TRUE (printout t "-----")) (printout t "-+-") (do-for-all-instances ((?x LED)) TRUE (printout t "-----")) (format t "%n")) (defrule print-result (print-results) ?f <- (result $?input ?response) (not (result $?input-2 ?response-2&:(< (str-compare ?response-2 ?response) 0))) => (retract ?f) ;; Print the input from the sources. (while (neq ?input (create$)) do (printout t " " (nth$ 1 ?input) " ") (bind ?input (rest$ ?input))) ;; Print the output from the LEDs. (printout t " | ") (bind ?response (explode$ ?response)) (while (neq ?response (create$)) do (printout t " " (nth$ 1 ?response) " ") (bind ?response (rest$ ?response))) (printout t crlf)) ;;;====================================================== ;;; Example Circuit #3 ;;; ;;; An example circuit to be loaded for use with ;;; the "electronic.clp" example program. ;;; ;;; LEGEND ;;; ------------ ;;; S = Source ;;; P = Splitter ;;; N = NOT Gate ;;; A = AND Gate ;;; D = NAND Gate ;;; O = OR Gate ;;; X = XOR Gate ;;; L = LED ;;; ;;; ;;; /----------\ ;;; S1>--P1>-| O1>-------------\ /--N3>--L1 ;;; | /---/ X2>----P4>-| ;;; | | /---/ \-------L2 ;;; \------)----N1>---\ | ;;; | X1>--/ ;;; /--N2>-/ /-------/ ;;; S2>--P2>-| | ;;; \---------)-------\ ;;; | A1>---------\ ;;; /---------/ /----/ | ;;; S3>--P3>-| | | ;;; \------------)----\ \---\ ;;; | D1>------\ D2>---------L3 ;;; S4>-------------------/ / O2>---/ ;;; | /-----/ ;;; S5>------------------------/ | ;;; | ;;; S6>----------------------------/ ;;; ;;;====================================================== (definstances circuit (S-1 of SOURCE) (S-2 of SOURCE) (S-3 of SOURCE) (S-4 of SOURCE) (S-5 of SOURCE) (S-6 of SOURCE) (P-1 of SPLITTER) (P-2 of SPLITTER) (P-3 of SPLITTER) (P-4 of SPLITTER) (N-1 of NOT-GATE) (N-2 of NOT-GATE) (N-3 of NOT-GATE) (O-1 of OR-GATE) (O-2 of OR-GATE) (X-1 of XOR-GATE) (X-2 of XOR-GATE) (A-1 of AND-GATE) (D-1 of NAND-GATE) (D-2 of NAND-GATE) (L-1 of LED) (L-2 of LED) (L-3 of LED)) (deffunction connect-circuit () (connect [S-1] [P-1]) (connect [S-2] [P-2]) (connect [S-3] [P-3]) (connect [S-4] [A-1] 2) (connect [S-5] [D-1] 2) (connect [S-6] [O-2] 2) (connect [P-1] 1 [O-1] 1) (connect [P-1] 2 [N-1]) (connect [P-2] 1 [N-2]) (connect [P-2] 2 [A-1] 1) (connect [P-3] 1 [X-1] 2) (connect [P-3] 2 [D-1] 1) (connect [N-1] [X-1] 1) (connect [N-2] [O-1] 2) (connect [O-1] [X-2] 1) (connect [X-1] [X-2] 2) (connect [A-1] [D-2] 1) (connect [D-1] [O-2] 1) (connect [X-2] [P-4]) (connect [O-2] [D-2] 2) (connect [P-4] 1 [N-3]) (connect [P-4] 2 [L-2]) (connect [D-2] [L-3]) (connect [N-3] [L-1]))