H

ICS 361, Artificial Intelligence Programming

Assignment 4

In Lisp, write a semantic network system using CLOS with the following interface functions:

Here are the top level class definitions:

(defclass Node ()
  ((name :accessor name :initarg :name :type 'symbol
         :documentation "the name of the Cocept or Relation"))
  (:documentation "The top level class, subclasses include Concepts and Relations"))

(defclass Concept (Node)
  ((froms :type list :accessor froms :initarg :froms :initform nil
          :documentation "a list of the Relations whose from slot is this Concept")
   (tos :type list :accessor tos :initarg :tos :initform nil
        :documentation "a list of the Relations whose to slot is this Concept"))
  (:documentation "An physical or conceptual object"))

(defclass Relation (Node)
  ((from :type Concept :accessor from :initarg :from
         :documentation "the Concept that this Relation is from")
   (to :type Concept :accessor to :initarg :to
       :documentation "the Concept that this Relation is to"))
  (:documentation "A relationship between two Concepts"))

The semantic network system should ensure that the to and from slots of all Relations are consistent with the tos and froms slots of all Concepts. For example, whenever a new Relation instance is created by calling make-relation and either a from Concept or a to Concept is given, then those Concept's tos or froms slots should be updated to add in the new Relation. Likewise, when a Relation's to slot or from slot is changed by calling set-to or set-from, the old Concept's tos or froms respectively should be updated to delete the Relation and the new Concept's tos or froms slot should have the Relation added.

Include a test run of your program with the following test program:

(defun test-a4 ()
  (print "(define-concept Human) -> ")
  (print (define-concept Human))
  (print "human -> ")
  (print human)
  (print "(define-concept Dog) -> ")
  (print (define-concept Dog))
  (print "dog -> ")
  (print dog)
  (print "(define-relation Owns Human Dog) -> ")
  (print (define-relation Owns Human Dog))
  (print "(setq h1 (make-concept 'Human 'John)) -> ")
  (print (setq h1 (make-concept 'Human 'John)))
  (print "(name h1) -> ")
  (print (name h1))
  (print "(setq d1 (make-concept 'Dog 'Fido)) -> ")
  (print (setq d1 (make-concept 'Dog 'Fido)))
  (print "(name d1) -> ")
  (print (name d1))
  (print "(setq o1 (make-relation 'Owns 'owns1 :from h1 :to d1)) -> ")
  (print (setq o1 (make-relation 'Owns 'owns1 :from h1 :to d1)))
  (print "(from o1) -> ")
  (print (from o1))
  (print "(to o1) -> ")
  (print (to o1))
  (print "(froms h1) -> ")
  (print (froms h1))
  (print "(tos d1) -> ")
  (print (tos d1))
  (print "(setq h2 (make-concept 'Human 'Sue)) -> ")
  (print (setq h2 (make-concept 'Human 'Sue)))
  (print "(setf (from o1) h2) -> ")
  (print (setf (from o1) h2))
  (print "(from o1) -> ")
  (print (from o1))
  (print "(name (from o1)) -> ")
  (print (name (from o1)))
  (print "(froms h1) -> ")
  (print (froms h1))
  (print "(froms h2) -> ")
  (print (froms h2))
  (print "(setq d2 (make-concept 'Dog 'Lassie)) -> ")
  (print (setq d2 (make-concept 'Dog 'Lassie)))
  (print "(setf (to o1) d2) -> ")
  (print (setf (to o1) d2))
  (print "(to o1) -> ")
  (print (to o1))
  (print "(name (to o1)) -> ")
  (print (name (to o1)))
  (print "(tos d1) -> ")
  (print (tos d1))
  (print "(tos d2) -> ")
  (print (tos d2))
  "done"
)

When I run the above test code with my solution, I get:

CG-USER(7): (test-a4)

"(define-concept Human) -> " 
#<STANDARD-CLASS HUMAN> 
"human -> " 
#<STANDARD-CLASS HUMAN> 
"(define-concept Dog) -> " 
#<STANDARD-CLASS DOG> 
"dog -> " 
#<STANDARD-CLASS DOG> 
"(define-relation Owns Human Dog) -> " 
#<STANDARD-CLASS OWNS> 
"(setq h1 (make-concept 'Human 'John)) -> " 
#<HUMAN @ #x21119422> 
"(name h1) -> " 
JOHN 
"(setq d1 (make-concept 'Dog 'Fido)) -> " 
#<DOG @ #x21119cf2> 
"(name d1) -> " 
FIDO 
"(setq o1 (make-relation 'Owns 'owns1 :from h1 :to d1)) -> " 
#<OWNS @ #x2111a612> 
"(from o1) -> " 
#<HUMAN @ #x21119422> 
"(to o1) -> " 
#<DOG @ #x21119cf2> 
"(froms h1) -> " 
(#<OWNS @ #x2111a612>) 
"(tos d1) -> " 
(#<OWNS @ #x2111a612>) 
"(setq h2 (make-concept 'Human 'Sue)) -> " 
#<HUMAN @ #x2111cd6a> 
"(setf (from o1) h2) -> " 
#<HUMAN @ #x2111cd6a> 
"(from o1) -> " 
#<HUMAN @ #x2111cd6a> 
"(name (from o1)) -> " 
SUE 
"(froms h1) -> " 
NIL 
"(froms h2) -> " 
(#<OWNS @ #x2111a612>) 
"(setq d2 (make-concept 'Dog 'Lassie)) -> " 
#<DOG @ #x2111e312> 
"(setf (to o1) d2) -> " 
#<DOG @ #x2111e312> 
"(to o1) -> " 
#<DOG @ #x2111e312> 
"(name (to o1)) -> " 
LASSIE 
"(tos d1) -> " 
NIL 
"(tos d2) -> " 
(#<OWNS @ #x2111a612>) 
"done"

Hints

Keeping the bookkeeping up to date is easily done by defining :before methods for (setf to) and (setf from) of Relation that delete the relation from the tos or froms slot of the old Concept. A :before method is needed here because a :after method would no longer have access to the old Concept as the to/from slot would already have been overwritten by the new Concept. Likewise a :after method can be used to add the Relation to the tos/froms slot of the new Concept. Be sure to check if the to/from slot is bound with slot-boundp before trying to get its value.

If a function can take multiple types of arguments such as either a Concept class or the name of a Concept class, then it is best to make the function a generic function defined by using defmethod. The method that takes a symbol can use find-class to get the actual Concept class from the symbol and pass this to itself.

My solution is 72 lines of code, not counting blank lines or comment lines and of those, 16 lines consist of the top level class definitions given above. If your solution is taking much longer, you are probably doing something wrong and should come see me or our TA for help.

Submitting

Submit by zipping together your source code files (e.g., a4.lisp) and your typescript file (e.g., typescripta.txt) into a4.zip and then upload a4.zip to Assignment 4 on Laulima.


David N. Chin / Chin@Hawaii.Edu