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 (nickname oot), 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.
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 analogous to a slot name, and Annot is analogous to a slot value.
An example use of Ocelot annotations within Pathway/Genome Databases (PGDBs) is
for the coefficients of substrates in reaction frames. Consider a
reaction 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.
When 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.
The following GFP operations are provided for annotations.
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.