Each KB is associated with a single Lisp package, which is specified using the initargs argument to create-kb. That package is called the "KB package", and can be retrieved from the kb-package slot of the kb object.
The names of all frames and all slots created within the KB must be symbols in the KB package.
New and existing KBs can be opened using the standard GFP interface functions: create-kb and open-kb, as described later in this section. Or they can be opened using two Ocelot-specific shortcuts that we provide. These two functions reside in package ocelot, and provide simpler ways of opening file KBs.
open-file-kb (kb-name &key filename)
make-ocelot-kb (kb-name &key filename package)
Because the arguments to create-kb are somewhat complicated, we provide an example of its use here. The purpose of create-kb is to create a KB descriptor that describes a new or existing KB. Once the descriptor exists, the programmer calls open-kb to acutally construct a new KB, or to open an existing KB. An example call to create-kb is as follows.
(setq kb (gfp:create-kb 'bird :prototype (gfp:get-prototype 'gfp:ocelot-kb) :initargs `(:package ,(find-package :user) :testfn ,#'gfp::fequal :dbms-type :file :dbms-host nil ;; ,dbms-server :filename "~/bird.ocelot" ) ) )This call creates a KB whose name is "bird," and that is an Ocelot KB (as opposed to a KB managed by some other frame knowledge representation system. Its package is defined to be user package, and its test function for comparing frames for equality must be defined to be the function fequal, which properly compares both frame objects and symbol frame names for equality. The dbms-type argument defines the KB to be of type file, meaning it resides in a file, not in Oracle, and therefore no dbms-host needs to be specified (this argument is required only for Oracle KBs). The file in which the KB will be stored is "~/bird.ocelot".
File ocelot-example.lisp provides an extended example that shows how to open a KB once its descriptor has been created.
Frame names may contain alphanumeric characters plus the following characters:
This section provides additional details on the facets employed by Ocelot, their meaning, and their possible values. The programmer can alter the values of facets on slot frames using standard GFP operations such as
(put-slot-value 'height :value-type :integer)or by using the GKB Editor frame editor to edit the slot frame.
Examples of valid domains:
|Proteins| (|Proteins| |Genes|) ((|Chemicals| :Except |Polynucleotides|)) ((|Chemicals| :Except (DNA RNA)))
Imagine that in a KB describing people, we define two slots called parent and child. Since these two slots describe inverse relationships, each slot should be marked as an inverse of the other:
(put-slot-value 'child :inverse 'parent) (put-slot-value 'parent :inverse 'child)Ocelot maintains inverse relationships for a pair of slots that are inverses of one another such that, when the user calls a GFP operation on one slot, Ocelot automatically performs the appropriate operation on its inverse. For example, imagine that the user has created two instances, John and Bill. If the user then executes that John is the parent of Bill:
(put-slot-value 'john 'parent 'bill)Ocelot will then automatically assert that Bill is the child of John, in effect automatically asserting:
(put-slot-value 'bill 'child 'john)If the user were to then execute:
(remove-slot-value 'bill 'child 'john)Ocelot will automatically remove the value Bill from the Parent slot of John.
Ocelot performs automatic updating for the GFP put, add, and remove operations.
A computed slot is defined by storing the names of one or more Lisp functions that will be used to compute the slot value on the :get-methods slot of the slotunit for that slot. That function will be called with three mandatory arguments: the frame, slot, and KB for which a value is being requested. In addition, get-methods can be stored as a facet of a class, but for get-methods to be defined as facets on a class, then a value of T must be stored in the :Get-Methods of the slotunit; otherwise the class will not be checked for :Get-Methods.
For example, imagine that we wish to compute the value of the slot "siblings" for a given frame by taking the union of the values of its "sisters" and "brothers" slots. To do so, define a function called get-siblings(frame slot value). Its definition should return the union of the values of the sisters and brothers slots for frame. Then, assert (add-slot-value 'siblings :get-methods 'get-siblings), and values of this slot will now be computed.
Although it is not advisable to store values in a slot whose value is computed, Ocelot will not prevent such values from being stored. The existence of such values will prevent the associated method from being called -- Ocelot will return the stored slot values.
An annotation can be thought of a five-tuple
of the form
(Frame Slot Value Label Annot), where
Label is a symbol that is analogous to a slot name, and Annot is analogous to a slot value.
However, annots may not include frame objects -- any references to frames must
be encoded as symbols.
An example use of Ocelot annotations within Pathway/Genome Databases (PGDBs) is
for the coefficients of substrates in reaction frames. Consider a
A + 2B = C + D. In a PGDB, the reactants are stored in a
slot called Left. This equation would be represented with a Left slot
containing two values, A and B. To model this situation, the B value is given
an annotation whose Label is "Coefficient" and whose Annot is 2.
To store that annotation:
(put-value-annot reaction 'Left 'B 'Coefficient 2)To retrieve that annotation:
(get-value-annot reaction 'Left 'B 'Coefficient) => 2When a value is removed from a slot, all of its associated annotations are also removed. However, when a slot value is replaced using replace-slot-value, all annotations that existed for the old slot value are retained by the new value.
get-all-annots frame slot value &key (kb (current-kb))This function returns the set of annotation labels that are currently in use for slot/value, in no guaranteed order. It returns a list of labels. Example:
(get-all-annots reaction 'Left 'Trp) => (COEFFICIENT)
get-value-annots frame slot value label &key (kb (current-kb))This function returns a list of all annotation valuess of slot/value/label. Example:
(get-value-annots reaction 'Left 'Trp 'COEFFICIENT) => (2)
get-value-annot frame slot value label &key (kb (current-kb))This function returns a single arbitrary member of the set of annotations of slot/value/label. It is commonly used when that set is expected to have one member.
put-value-annots frame slot value label annots &key (kb (current-kb))
This function sets the annotations of slot/value/label to be
annots, which is assumed to be a list. The order of the elements of
annots will not necessarily be maintained by the FRS.
put-value-annot frame slot value label annot &key value (kb (current-kb))
This sets the set of annotations of slot/value/label to be a set
consisting of a single element: annot.
add-value-annot frame slot value label annot &key (kb (current-kb))
If the annotations of slot/value/label do not already contain
annot, then annot is added to the set of annotations of slot/value/label.
replace-value-annot frame slot value label old-annot new-annot &key (kb (current-kb))
If old-annot is currently a member of the set of annotations of
slot/value/label, then old-annot is replaced by new-annot.
remove-value-annot frame slot value label annot &key (kb (current-kb))
If annot is currently a member of the set of annotations of
slot/value/label, then annot is removed from the annotations of
remove-all-value-annots frame slot value &key value (kb (current-kb))
Removes all annotations in slot/value of frame under all labels.
member-value-annot-p frame slot value label annot &key value (kb (current-kb))
Returns non-nil iff annot is a value in the annotations of
value-has-annot-p frame slot value label &key (kb (current-kb))
Returns T iff slot/value/label has an annotation under label.
For example, imagine that the call (get-slot-values 'Jim 'Brothers) returns a list of brothers: (Fred Joe Bill). The list returned by get-slot-values is in fact a Lisp pointer to the same physical memory location in which Ocelot stores the values of this slot.
It would be a mistake to think of the list returned by get-slot-values as distinct from the slot value list stored in Ocelot. If the user happened to want to sort the list of brothers alphabetically, and applied the sort function to this list, not only would the user see their list changed, but the list of slot values would also be changed. The reason is that the Lisp sort function is a destructive operation -- it rearranges the pointers in the physical Lisp list that is its input, rather than sorting a new list in a new region of memory. Similarly, applying other destructive Lisp operations such as rplaca, delete, or nconc would also alter the Ocelot slot value. On the other hand, non-destructive Lisp operations such as append, subseq, or remove would have no effect on the data stored in Ocelot because those functions create a copy of their input data before computing the result.
However, the user should not use destructive operations with the intent of altering the data stored within Ocelot. For example, do not use operations such as delete or nconc to intentionally alter the set of values within an Ocelot slot. One reason for this is that when it comes time to save your changes to an Oracle Ocelot KB, Ocelot only knows what frames to save to Oracle because GFP operations keep track of what frames they have changed. So even if you successfully alter a list of values using nconc, Ocelot will not know that those values have been changed, and it will not save the relevant frame to Oracle. The general rule is: Only use GFP operations to update Ocelot data.