Ocelot User's Guide

Although the Generic Frame Protocol (GFP) specification documents many aspects of the Ocelot Frame Representation System, some aspects of Ocelot are not described at the GFP level. This document provides additional information on Ocelot. An Ocelot programming example is also available.

Packages

Ocelot is usually accessed via the GFP API, which is in package gfp. Much of Ocelot is implemented in package ocelot-gfp (nicknames oot and ocelot).

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.

Opening Ocelot Knowledge Bases

Ocelot can manage knowledge bases (KBs) that are stored persistently in two ways: within disk files, and within an Oracle database. From a user's point of view, the two types of KBs are treated identically, with the exceptions that they are opened differently, and that various setup operations are required to configure the Oracle database before it can be used. This manual currently focuses on KBs stored within files.

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)

Opens an existing Ocelot KB named kb-name from the file whose name is filename, reads that KB into memory, and makes that KB the current KB. This function is a shorthand for performing a gfp:create-kb followed by a gfp:open-kb. Returns the KB.

make-ocelot-kb (kb-name &key filename package)

Creates and opens a new Ocelot file KB named kb-name that will reside in the file whose name is filename. The kb-package will be specifed as package, which defaults to User package. This new KB becomes the current KB. This function is a shorthand for performing a gfp:create-kb followed by a gfp:open-kb. Returns the created KB.

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.

Ocelot Frame Names

Ocelot frame names must be Lisp symbols whose length does not exceed 40 characters. Frame name symbols must be in the kb-package for the Ocelot KB that the frame resides in.

Frame names may contain alphanumeric characters plus the following characters:
-_.:+?

Slot Frames

In Ocelot, for every slot defined within a KB, a frame called a slot frame is created in the KB that stores information about the properties of that slot. For example, if we created a slot called height, then a frame called height would be created in the KB. The slots in the height frame define properties of the slot, such as the class(es) to which it applies, encoded in a slot called :domain. Slots on slot frames are also called facets. Most Ocelot facets and their possible values are described in detailed in the section of the Generic Frame Protocol (GFP) specification called Standard Facets.

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.

The :Value-Type Facet

The :value-type facet specifies constraints on the allowable values of a slot. The value of the :value-type facet consists of a single Lisp s-expression. Valid value-types accepted by Ocelot are:

The :Inheritance-Type Facet

Different modes of inheritance may be specified for different slots using a single value of the :inheritance-type facet of the associated slotunit, as follows. If no inheritance type is specified, the default is :Override. This is the mechanism used by Ocelot to control inheritance of values from classes to their children. The notion of template slots versus own slots (see GFP spec) is not used in Ocelot. For example, a class cannot contain two sets of values for one slot, where one set of values are own-slot values and another set are template-slot values. The Ocelot GFP implementation ignores slot operations in which the slot-type is specified to be :template.

The :Domain Facet

The :domain slot of a slotunit specifies what classes the slot is defined to be present in, that is, it defines the scope of the slot within a KB. The value of the :domain slot of a slotunit is a set of one or more items D, where each D is either:

The slot is allowed to be used in the union of all class subtrees specified by all of the items D.

Examples of valid domains:

|Proteins|
(|Proteins| |Genes|)
((|Chemicals| :Except |Polynucleotides|))
((|Chemicals| :Except (DNA RNA)))

The :Inverse Facet

Ocelot provides full support for maintenance of inverse relationships.

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.

The :Collection-Type Facet

The :Collection-Type slot of a slotunit specifies the collection semantics of the list of values of that slot. Possible values of :Collection-Type:

Computed Slots -- the :Get-Methods Facet

An Ocelot slot can be configured so that its value is computed dynamically by Ocelot when the value of the slot is requested by an operation such as get-slot-value. Such computed slots are equivalent to database views, and are useful when the value of a slot can be inferred computationally from the values of other slots.

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.

Built-In Slots with Special Functions

Several built-in slots exist in Ocelot that are used by programs that interact with an Ocelot KB.

:Instance-Name-Template

This slot provides a template for automatically generating names for new instances of some class. The template must be in the form of a string, which may contain the wildcard character '*'. To generate the instance name, the first occurrence of '*' in the template should be replaced by a unique sequence number. For example, if the template for a class is "FRAME-*", then the first three instances generated would be "FRAME-1", "FRAME-2", and "FRAME-3", assuming that frames with those names do not already exist in the KB. If a template exists for a class but contains no wildcard character, no automatic name generation can occur. However, the template value can be supplied as the initial suggested value for the new frame name, so that the user can edit it. GKB Editor contains methods that implement name generation.

:Member-Sort-Fn

If you wish the subclasses and instances of some class to be sorted by some function other than the default, supply a function name (a symbol) as the value of this slot.

:Key-Slot

This slot specifies which slot (if any) in a given frame contains a key for indexing that frame (that is, an official name for the frame that is a string). Usually defined on a per-KB and/or a per-class basis. See Ocelot function find-indexed-frame for information on how to do indexed lookups of information in the indexed slot.

:Synonym-Slots

Specifies which slot(s) (if any) in a given frame contains synonyms (strings) for the name of that frame. Usually defined on a per-KB and/or a per-class basis. A typical value is the value SYNONYMS. This slot is used by code that searches for frames by name so that it knows in a KB-independent fashion what slots to look in during that search. See Ocelot function find-indexed-frame for information on how to do indexed lookups of information in the indexed slot.

Annotations

Annotations are an Ocelot mechanism for attaching structured data to slot values. Annotations are an Ocelot extension to GFP, and are not an official part of GFP.

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 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. To store that annotation:

(put-value-annot reaction 'Left 'B 'Coefficient 2)
To retrieve that annotation:
(get-value-annot reaction 'Left 'B 'Coefficient)
=> 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.

GFP Operations for Annotations


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 slot/value/label.


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 slot/value/label.


value-has-annot-p frame slot value label &key (kb (current-kb))
Returns T iff slot/value/label has an annotation under label.

Destructive Operations on Ocelot Data

Users who query Ocelot data using GFP Lisp functions need to be aware that these GFP functions return pointers to the actual Ocelot internal data structures. Therefore, applying destructive Lisp operations on those data structures will alter the underlying Ocelot data, which can have disastrous results. Users could unintentionally alter the DB content, or users who perform destructive operations with the intent of altering the DB content could find that their changes are not saved permanently.

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.

Constraint Checker

Examples

File ocelot-example.lisp contains example GFP calls to operate on an Ocelot knowledge base.


Peter D. Karp