Carnegie Mellon University
School of Computer Science
Copyright Notice:
This manual is copyright © 2003 by Scott E. Fahlman.
Carnegie Mellon University holds the copyright to the Scone software. The Scone software is made available to the public under the CPL 1.0 Open Source license. A copy of this license is distributed with the software. The license can also be found at http://www.opensource.org/licenses/cpl.php.
By using, modifying, reproducing, or distibuting the Scone software, you agree to be bound by the terms and conditions set forth in the CPL 1.0 Open Source License. If you do not agree to these terms and conditions, or if they are not legally applicable in the jurisdiction where such use takes place, then you may not use the Scone software.
Scone incoporates some parts of the NETL2 Knowledge Representation System, developed by Scott E. Fahlman for IBM Coporation between June 2001 and May 2003. IBM holds the copyright on NETL2 and has made that software available to the author under the CPL 1.0 Open Source license.
Acknowledgment:
Development of Scone has been supported in part by the Defense Advanced Research Projects Agency (DARPA) under Contract No. NBCHC030029. Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of DARPA or the Department of Interior-National Business Center (DOI-NBC).
Table of Contents
2.1 Structure
of a Scone Element
2.2 Referring
to Scone Elements
5. User-Level
Queries and Operations
5.9 Accessing
User-Defined Relations
6. Adding
Elements to the Knowledge Base
6.2 Single-Element
Add-Functions
6.3 Higher-Level
Add-Functions
6.7 Domain-Specific
Creation Functions
7.3 The
:xname Keyword Argument
Scone is a knowledge-base (KB) system intended for use as a module in a wide range of software applications. Scone comprises a search/inference engine and a base-load of common knowledge. Application-specific knowledge and interfaces to other applications can be added on top of that common base.
The current version of Scone is written in Common Lisp. Scone can be run as a server process on an Intel/Linux system, communicating with other software applications via character-stream I/O over the TCP socket mechanism. The other software applications may be written in any computer language and may be on the same machine or a different one on the Internet.
Scone is designed for efficiency of search and inference on conventional high-end workstations, though the architecture was originally designed with parallelism in mind. It is therefore well suited for future implementation on a massively parallel machine or perhaps a computing grid.
The greatest problem for past knowledge-base systems has been the difficulty of adding new knowledge to the system and making that knowledge fully effective. This gives rise to spotty coverage of common-sense domains. Scone attempts to ease this burden by relatively clean design and by separating system-efficiency concerns from knowledge-entry concerns.
Note: This manual covers the operation and functions of the Scone software. It is not meant to be a tutorial on knowledge representation in Scone. I assume that the reader has some general familiarity with Common Lisp syntax.
A Scone knowledge base is made up of elements that are connected together to form a semantic network. Think of the Scone knowledge base as a kind of active memory. In the current software implementation, which is designed to run on a serial workstation, an element is just a data structure, but conceptually it is a simple parallel-processing element with a few bits of internal state. All of these elements can simultaneously execute simple commands broadcast by the central control computer, a conventional serial machine (or program) that runs the show. In the current implementation, this control function is performed by the Scone engine program.
An element in Scone can be either a node or a link. Nodes represent conceptual entities such as “George Washington” or “Panama” or “the typical elephant”. Note that the node represents the concept in each case, not a word or word-definition.
Links represent relations or assertions of some kind, such as “Clyde is an elephant” or “The wife of George is Martha” or “Every elephant hates P. T. Barnum.”
%%% Fix this. %%%
Each Scone element has a few bits of state information, known as the element’s marker bits. Operations on marker bits generally refer to the marker they want using a numerical index, starting at 0. The constant n-markers indicates how many marker bits each unit has – currently the number is 22, so the maximum legal marker index is 21. By default, some of the higher-numbered markers are permanently assigned to particular functions during marker scans (see below), while others are assigned dynamically to various tasks.
Scone represents its knowledge in the pattern of interconnections among elements. Each element has a few distinct wires by which it can be connected to other elements in the knowledge base. A node may have a parent-wire and a context-wire. A link will have both of these, plus an A-wire and a B-wire.. A type-node, representing the typical member of some set, has a set-wire to the node representing that set. The meaning and function of these wires will be described below. Every element has a terminal point to which any number of incoming wires from other elements may be attached.
The lowest-level commands to which the Scone elements can respond generally involve setting and clearing of the marker bits. For example:
· Attention all elements: set (or clear) marker 0.
· All elements with markers 1 and 2 turned on: set marker 3.
· All is-a links: if you have marker 0 set and if you sense that the element attached to your A-wire has marker 1 set, tell the element attached to your B-wire to set its marker 2.
· All elements with marker 4 set: Line up and report your identity, one-by-one, to the control program.
In principle, all elements should be able to execute such commands (all but the last one) in parallel and in constant time. Of course, in a serial-machine implementation, the work is actually done in serial and requires time proportional to the number of units responding.
Note that these are the lowest-level commands; the knowledge-base commands and queries that the users actually deal with are much higher level, corresponding to queries such as “What is the capital of France?” or “Does Bolivia have any seaports?”
Here is a potentially confusing complication: We think of a link as connecting two nodes (A and B), and representing some statement about the relationship between these two nodes: “A is a B” or “A hates B” or whatever. However, the link also is an entity in the knowledge base, representing the statement itself, and we can make statements (sometimes referred to as “meta-statements”) about this statement: who told us, whether we believe it, whether exceptions are possible, and so on. So each link element really has two parts: the A and B wires, which are used to implement the statement the link is making, plus the marker bits, terminal point, and connection wires of a node, representing the statement itself. We sometimes refer to this built-in node as the handle node of the link.
Here is another potentially confusing complication: We think of a node as representing an entity, about which we can make various statements by means of attached links. But every node (except the top-level “thing” node) will be a member of at least one superior class, and every node has at least one context in which it exists. So to save on the total number of elements, we equip each node with two wires that serve as “quick and dirty” links. The parent wire serves as a virtual is-a link to one superior class, generally the node’s most fundamental identity (whatever that means). So the “George Washington” individual-node may have a parent-wire going to the “person” type-node, rather than an is-a link to “person”.
Similarly, a node’s context wire serves as a virtual has-a link connecting the node to the context in which it exists. For example, the “Harry Potter” node might have its context wire connected to the “Harry Potter World” context node, while “George W. Bush” would have a context wire to the “general context” node, representing actual reality (whatever that means).
While the use of these parent and context wires cuts down the number of elements required in a Scone knowledge base by at least a factor of 2, there is a cost: because these are not full-fledged links with integral handle nodes, we cannot attach meta-information to these wires. If it becomes necessary to do that, we must detach these wires, replace then with full-fledged links, and then proceed. The functions convert-context-wire-to-link and convert-parent-wire-to-link handle this task when it becomes necessary. (See their descriptions below for details.)
Many of the user-level functions described in this document take one or more elements as arguments. Within the running Scone system, each element is represented as a Common Lisp data structure (specifically, a defstruct). So if you have a reference to one of these element-objects, you can supply this directly wherever an element argument is required.
Alternatively, you can refer to an element by its internal name or “iname” – a Common Lisp string, surrounded (as are all Common Lisp strings) by double-quotes. So, for example, the internal name of the node at the top of the type hierarchy is “thing”, and this string can be supplied in place of the Common Lisp object representing this concept.
These iname strings are matched in a case-insensitive way; that is, “Clyde”, “clyde”, and “CLYDE” all refer to the same element. However, Scone remembers the exact string, including case, that was used when an element was first created, and that string is used whenever the system must print out a reference to the element. If, for some reason, is it desirable to run the system with case-sensitive inames, set the variable *case-sensitive-internal-names* to t before loading any knowledge bases.
Internal name strings may contain spaces and other punctuation characters, but not the colon character, which is reserved for another use (see below).
(Do not confuse these internal names with the external names of an element, described in section 7 of this document. These external names are for use by external natural-language processing systems that may wish to interface with Scone. External names can not be used interchangeably with internal names.)
To avoid ambiguity, internal names are organized into namespaces. Within its namespace, an internal name must be unique. That is, it refers to only one element. Any attempt to create a second node with the same iname in a given namespace will signal an error.
Namespaces are designated by Common Lisp strings such as “common” or “astronomy”. These namespace names must be globally unique so that accidental collisions do not occur if we try to combine separately developed knowledge bases. A registry of Scone namespace names that are currently in use is maintained by the author. Contact the author to register a new namespace.
To designate an internal name without any danger of ambiguity we use its full name. This is a Common Lispstring divided into two parts by a colon. The part of the string before the colon designates the namespace, while the part after the colon is the unique internal name within that namespace. So “biology:mouse” and “computer:mouse” may co-exist without creating a conflict. There may be only one colon in an internal name string.
There is a function called in-namespace that takes a string as an argument. That string is converted to a namespace object, which becomes the value of the *namespace* variable. Whenever the system is given an iname without a colon in it (referred to as a short name), the value of the *namespace* variable is used as the default namespace. Typically a KB file will have an in-namespace form at the start and will use short-names internally, except when it is necessary to refer to an iname in another namespace.
A namespace may optionally include one other namespace, which may include another, and so on. If an attempt is made to look up an internal name in a given namespace and the iname is not found there, the system will look in the included namespace (if any) and its included namespace, and so on until the end of the chain is reached. If the iname is not found anywhere in this chain of namespaces, the name is undefined.
If we create a new iname, it will be placed directly into the current *namespace*, but an error will be signaled if the name conflixts with the same namestring in some included namespace. This namespace-inclusion mechanism is often used to include the “common” namespace in some more specialized namespace that the user is creating, so that elements like “common:thing” and “common:part” can be referred to simply as “thing” and “part”.
Links normally do not have internal names. However, if you believe that you will want to refer to a link, you can give it an internal name via the :iname argument of the “add” function that creates the link.
When loading a KB file, a reference to an undefined element normally signals an error. However, there are some situations where it is useful to allow forward or circular references. (This occurs, for example, in the “bootstrap.lisp” file that defines the root structures in the knowledge base.) In such situations, set *defer-unknown-connections* to t in that file. When this variable is non-null, unknown element references are pushed onto the *deferred-connections* list, and the engine will try to complete the connection at a later time. The load-kb function calls process-deferred-connections after each file is loaded, and users may call this directly.
Note: This
information refers specifically to running Scone on an Intel/Linux system on
which CMU Common Lisp has been installed.
To run a saved Scone core image just type the following at the shell:
lisp –core /directory/my-filename.core
Of course you would substitute the directory and filename of your choice. This starts a Lisp listener that is ready to continue from whatever state the Lisp was in when the core files was saved.
To create a new core-image file, run lisp, load what you like, and then execute the form (save-lisp “/directory/my-filename.core”), again with the directory and filename of your choice. The system will go through a few steps (garbage collection, “purification” of the core image), then dump the core file and exit.
Note: Saving
Lisp files and running the saved core images cannot be done when you are
running as a slave Lisp under the Hemlock editor front-end. You must use a “raw” Lisp for this.
To start up a saved Scone core image as a TCP server, modify the command line as follows:
lisp –core /dir/my-filename.core –eval “(start-server <port>)”
where <port> is the port number you want the server process to use. If no port is supplied, the default is 6517 – an arbitrary number of no particular significance, except that it looks a bit like the word “LISP” if you stand on your head.
Once this server is running, other application programs can send Lisp forms to the server as text strings, terminated by a newline character. The server will evaluate these forms and print the result (and any additional printed output) back to the socket stream, and also to the window in which the Lisp is running.
To load and run a Scone system from scratch, do the following:
1. Start up a Common Lisp system. You are now talking to the Lisp listener.
2. If necessary, compile the Lisp file engine.lisp, and then load it.
3.
Tell Scone where to find its knowledge-base files. The built-in default is C:/Scone/kb/anonymous.lisp. To over-ride this, type in something like
this into the Lisp listener:
(setq
*default-text-kb-pathname* (pathname “<your-path>”))
If you prefer, this form may be added to your init.lisp
file.
4. Execute (run-script “common”). This is a script that loads a number of KB files that comprise the system’s common knowledge – that is, knowledge that will be useful in almost any application. The unspecified parts of the pathname to this file will be filled in from the default supplied in step 3.
5. Load any additional KB files of your choice. For example, to load a KB file called “mythology.lisp” in the directory specified in step 3, you would simply type (load-kb “mythology”).
6. You can now begin typing expressions into the Lisp listener to augment or query the knowledge base. When you want to end the session, type (quit).
When developing and exploring Scone, you probably want to run the lisp process you are working on as a slave process under Hemlock, an Emacs-like editor. Start the CMU Common Lisp system by invoking lisp, then when you get a prompt, type (ed). This should bring up Hemlock, which will create an editor window to work in. Type control-meta-c to start a slave Lisp in its own editor buffer, which will have a name like “Slave 1”. To create additional slave processes in separate buffers under the same Hemlock, type control-u control-meta-c.
You can now type Lisp forms into the editor window and edit them however you like. When you type a newline, the form will be passed to the slave lisp process for evaluation. The result will be passed back and printed in the same editor buffer. One advantage of working this way is that it creates a transcript of what you have done. When building up a new KB file, you may choose to type the knowledge-creating forms into a running Scone process, and then edit the transcript down (removing errors, prompts, etc.) to create a new KB file.
The knowledge-base (or “KB”) files in Scone are just files of Common Lisp expressions and comments, following the syntax of Common Lisp. Users can easily modify these files offline, using the text-editor of their choice. (Emacs is recommended, as it has some special facilities for editing and indenting Lisp syntax.) These files have the extension “.lisp”. Since these text-format KB files are just Common Lisp source files, any Common Lisp expressions you like may be included in one of these files, mixed with the Scone-specific expressions that create new elements.
The following lisp function handles loading of knowledge-bases files:
(load-kb filename &key
:verbose)
Loads the named file, with missing parts of the pathname filled in from the default in *default-text-kb-pathname*. If there is an ID-dictionary file, we use this as a source of ID-numbers for any named element that has been seen before. If new IDs are created while loading the file, load-kb dumps an updated dictionary file at the end of the load.
If the :verbose
keyword is present with a non-nil value, the result of loading and executing
each form in the file will be printed.
Example of usage: (load-kb
“geopolitics” :verbose t)
(run-script filename &key :verbose)
This is just like the lisp load form, but it it s bit more conveninent because it
fills in the missing parts of the pathname from the default in *default-text-kb-pathname*. This does not do all the bookkeeping that load-kb does. It is normally used to load script files
that in turn contain a number of load-kb calls.
In the following functions, an “element” argument can use any of the methods described in section 5 for designating an element: a Lisp expression that evaluates to an element object, an internal names string (surrounded by double quotes), or a numerical
A “marker” argument is a non-negative integer less than *n-markers*, typically between 22 and 28, depending on the properties of the underlying Common Lisp implementation.
Markers are normally used in pairs: one to mark a set of elements, and one to serve as a cancellation marker, marking elements that are definitely not part of that set. The former are spoken of as “user markers” and they occupy the lower half of the space of available markers. The *n-user-markers* variable tells us how many of these there are. When given a user-marker m, the function (cancel-marker m) will return the corresponding cancel marker.
Some user-markers are reserved for special purposes during marker scans and other operations. These are the highest-numbered user-markers; markers assigned dynamically normally start at 0 and work upward. Lisp constants are used to give symbolic names to these pre-assigned markers. Users should use these names and not the numeric values, which may change in future releases.
Used to mark the current value of *context* and its superiors in the is-a hierarchy.
Used to mark the node or nodes that are the result or answer in certain operations.
Used during some operations for temporary purposes that are localized within the function.
Element c becomes the current context. The *context* variable is set to c and context-marker is placed on c and upscanned, marking the superiors of c in the is-a hierarchy. The context cancel-marker is placed on any elements cancelled in this context.
New elements are, by default, created in the current *context*.
Returns the number of elements currently marked with marker m.
The e argument must be a type-node. Get the corresponding set-node (an indv node descended from “set”).
The e argument must be a set-node (an indv node descended from “set”). Returns the type node representing the set’s typical member.
Returns the context of element e. This is either the element at the end of e’s context-wire or the element on the A side of an active has-a link. Returns nil if it can’t find the context.
This takes an element-designator e (an element or iname string) and returns the corresponding element object. Signals an error if the argument does not correspond to an existing element in the KB.
Return the full internal name (namespace-colon-name) of element e as a string.
(extract-namespace string)
(extract-name string)
Given a string that may designate an element, these return the part before the colon and the part after the colon, respectively. The returned value is a new string. If there is no colon, extract-namespace returns nil and extract-name returns the string argument unchanged.
(make-namespace name &key :include)
Create a new empty namespace object with the specified name (a string). If the :include argument is supplied, this indicates an existing namespace that is included in the new one. The :include argument may be either a namespace object or a string designating a namespace.
(in-namespace name &key :include)
The namespace designated by name becomes the new default namespace (the value of the *namespace* variable). If the specified namespace does not already exist, it is created, using the :include argument if one is present.
(process-deferred-connections)
Normally we build a Scone knowledge base in orderly layers. A new element will be connected to other elements that have already been created. But it some situations it is necessary to create forward references or reference loops. In such cases, we use the “deferred connections” mechanism. This facility is enabled by placing (setq *defer-unknown-connections* t) at the start of a file that contains forward references. Normally this variable is nil, and references to unknown elements signal an error.
When *defer-unknown-connections* is non-null and an element-creation routine is told to connect a wire to some element, specified by its iname, and if no such element exists at present in the KB, we push an entry onto the *deferred-connections* list so that we can try to create the connection later, after additional elements have been created or loaded. This allows us to handle situations with forward or circular references, even if the circularities span multiple file boundaries.
The process-deferred-connections function says that we should scan the *deferred-connections* list now to see if any of the deferred connections can now be created. Any connections successfully processed are removed from the *deferred-connections* list; the rest remain on the list. This function is called after leading each new KB file, but users may occasionally want to call it directly.
Prints a table showing the number of elements marked with each marker.
Prints a table showing how many elements of various types exist in the KB.
(show-elements &key :n :all :last)
Print a table of elements in the KB in the order in which the elements were defined. If :all is supplied and is non-null, print all the elements in the KB. Otherwise, print the first :n elements (default is 100). If :last is supplied and non-null, print the last N elements instead of the first N. If :last is given an integer argument, this becomes the value of N.
(show-marked m &key :n :all :last)
Like show-elements, but print elements marked with marker m, in the order in which the markers were placed.
(show-not-marked m &key :n :all :last)
Like show-marked, but prints all elements not marked with m.
Note: In each of the following, the mark-x function places marker m on some set of elements. The show-x version places result-marker on these same elements and then prints them, using the same :n, :all, and :last parameters as show-marked.
(mark-supertypes e m)
(show-supertypes e &key :n :all :last)
(mark-subtypes e m)
(show-subtypes e &key :n :all :last)
(mark-instances e m)
(show-instances e &key :n :all :last)
(mark-children e m)
(show-children e)
Mark or print the immediate descendants of element e, whether or not these are active in the current context. This is useful for seeing the participating nodes in a split, for example.
(mark-roles e m &key :type-roles :indv-roles :set-roles
:maps
:n :all :last)
(show-roles e
&key :type-roles :indv-roles :set-roles :maps
:n :all :last)
Mark or print the roles attached to element e in the current context. The various keyword arguments control which kinds of roles are marked or printed. :type-roles and :indv-roles default to t. The :set-roles argument (default nil) controls printing of the set-nodes associated with type-roles. The :maps argument controls printing of any map-nodes that map the various roles.
(mark-lowermost m1 m2)
(show-lowermost m &key :n :all :last)
Mark or print the lowermost elements in the set of elements currently marked with m (or m1). A lowermost element in a marked set is one that has no other marked element immediately below it in current context.
(show-most-specific m &key :n :all :last)
Print the most specific node or nodes marked with marker m. These are lowermost individual nodes if any exist, and lowermost type or map-nodes otherwise. This is used to print the answer to questions such as “Who is the president of Peru?”
Given a marker m, return the corresponding cancel-marker.
Predicate to determine whether element e has marker m turned on.
(marker-off? e m)
Predicate to determine whether element e has marker m turned off.
Returns the number of elements currently marked with m.
Marks element e with marker m. If e is already marked with m, do nothing. Return the number of elements marked with m after this operation.
Marks every node in the KB with marker m. Return the number of elements marked with m after this operation.
Removes marker m from element e. If e is not marked with m, do nothing. Return the number of elements marked with m after this operation.
Clears marker m from all elements in the KB.
Clears both marker m and the corresponding cancel marker.
Clears all markers from all elements in the KB.
(mark-boolean m must-be-set must-be-clear)
The m argument is a marker. The must-be-set and must-be-clear arguments are lists of markers. Conceptually, we scan every element in the KB. If all of the must-be-set markers are on and none of the must-be-clear markers are on, turn on marker m for this element. Returns the number of elements marked with m after this operation.
So, for example, (mark-boolean 3 ‘(0 1) ‘(2)) places marker 3 on every element that is currently marked with 0 and 1 and that is not marked with 2.
(unmark-boolean m must-be-set must-be-clear)
Like mark-boolean, but clears marker m from the selected elements.
The various marker scans are powerful and rather complex internal functions that perform the pseudo-parallel search and inference in Scone. Once the Scone system is complete, users will not have to deal directly with these scans, but they are documented here for completeness.
(upscan start-element m &key
:owner-marker :augment)
Mark start-element with marker m. Propagate this marker upwards, across parent wires and is-a links that are active in the current context, while respecting is-not-a links and cancellations. Places the cancel-marker corresponding to m on nodes that explicitly are not above start-element.
If an :owner-marker is supplied, start-element is a role that we want to upscan relative to its owner. We have already marked the owner with this owner-marker and upscanned that marker. The owner-marker gates the flow of m markers through map-nodes.
Normally the m marker and its cancel-marker are cleared before start-element is marked and the scan is performed. However, if the :augment argument is non-null, we leave any pre-existing m markers in place and they are propagated upward as well.
Returns the number of elements marked with m after the upscan.
(downscan start-element m &key :augment)
Like upscan, but propagates marker m and its cancel-marker downward from start-element. That is, the direction is from a type to its subtypes and instances. The :augment argument is the same as in upscan. Returns the number of elements marked with m after the operation.
(eq-scan start-element m &key :augment)
Like upscan, but propagates marker m only to elements that are equivalent to start-element. That is, this does not propagate upward or downward in the type hierarchy, but only crosses eq links.
Assume we have marked one or more elements with m and have upscanned m. Now scan all elements with marker m to find any violated splits that are not cancelled and are active in the current context. Return the number of such elements, or nil if there are none. This operation leaves result-marker on the violated split nodes.
The x and y arguments are elements. This is a Predicate to determine whether x is known to be a y in the current context.
The x and y arguments are elements. This is a predicate to determine whether x can be a y in the current context. In other words, if we were to add an is-a link from x to y, would that violate any split markers? As a side-effect, this function leaves *result-marker* on any violated split-nodes.
The x and y arguments are elements. This predicate determines whether element x has an individual y role, whether directly or by inheritance, in the current context.
Note: In the following functions, the role-chain is a list of elements (or internal names or numeric element ID’s) that represent a chain of role relations. For example, to represent “Clyde’s mother’s mother’s age”, the of-chain would be (“Clyde” “mother” “mother” “age”). This might alternatively be read as “the age of the mother of the mother of Clyde”.
Functions that take a role-chain argument also take a start-marker argument, which defaults to 0. In the above example, if the start-marker is 2, then Clyde ends up marked with marker 2, Clyde’s mother gets marker 3, Clyde’s mother’s mother ends up with marker 4, and Clyde’s mother’s mother’s age ends up with marker 5. The length of the role-chain is limited by the number of free markers available, beginning with the start-marker. These functions may also leave behind cancel markers on nodes that are inoperative in this nested description.
(mark-<role-chain> role-chain &key :start-marker :predicate)
This is the basic function for marking a role-chain as described above. Returns the number of nodes marked with the final marker.
By default, if some role in the chain does not exist for the specified owner, an error is signaled. However, if the :predicate argument is supplied and is non-nil, the error is suppressed and mark-role-chain simply returns nil.
(mark-the-x-of-y x y &key :start-marker :predicate)
This is just mark-role-chain for the common case of a chain with two elements – a single role relation.
(show-<role-chain> role-chain &key :start-marker)
Uses mark-<role-chain> to mark the nested roles in the role-chain argument. Then prints the most specific node corresponding to the role-chain expression. Leaves various the markers in place, including result-marker on the answer nodes.
(show-the-x-of-y x y &key :start-marker )
This is just show-<role-chain> for the common case of a chain with two elements – a single role relation.
(can-x-be-<role-chain>? x role-chain &key :start-marker)
This predicate determines whether element x can legally fill the role specified by the role-chain. The arguments are as in mark-role-chain.
(can-x-be-the-y-of-z? x y z &key start-marker)
This is just can-x-be-<role-chain>? for the common case of a chain with two elements – a single role relation.
(can-x-be-a-<role-chain>? x role-chain &key :start-marker)
Like can-x-be-<role-chain>?, but in this case the role-chain should resolve to a type-node and we are asking if it’s OK to put an is-a link from X to this type.
(can-x-be-a-y-of-z? x y z &key :start-marker)
This is just can-x-be-a-<role-chain> for the common case of a chain with two elements – a single role relation.
(statement-true? a rel b :start-marker)
A predicate that tests whether (a rel b) is true in the current context. The a and b arguments are element of any kind. The rel argument must be a relation node. Uses three marker pairs, starting with :start-marker, which defaults to 0.
The a argument is an element. The rel argument is a relation-node. Place marker mb on all elements b for which the statement (a rel b) is known to be true in the current context.
This operation uses two marker-pairs in addition to mb. Marker mb+1 is used to mark a and all of its superiors in the net. Marker mb+2 is used to mark rel and all of its inferiors in the net.
Like mark-rel, but we cross the statement links in the opposite direction. That is, we place marker ma on all elements a such that (a rel b).
(show-rel a rel &key :start-marker :n :all :last)
Mark all nodes b such that (a rel b), then print out the individual nodes in this set. Uses three markers starting with :start-marker. The :n, :all, and :last arguments behave the same as in other “show” functions.
(show-rel-inverse rel b &key :start-marker :n :all :last)
Like show-rel, but the rel links are crossed in the opposite direction. That is, we print all individual elements a such that (a rel b).
New elements are added to the knowledge base by the family of add-<element type> functions: add-indv, add-type, add-is-a and so on.
All of these add-functions have certain features in common. All will create at least one new element in the KB, and the new element object created (or the primary element created, if there are more than one) is returned as the value of executing the add-function. A text-format KB file is just a collections of such add-functions, with perhaps some comments mixed in.
Add-functions that create a node, such as add-indv, generally take a single required argument, the internal name of the new node (a string). So a typical call might be something like (add-indv “Clyde”).
Add-functions that create a link take two required arguments, the two elements that the link connects, called the A and B arguments. So a typical call might be something like (add-is-a “Clyde” “elephant”).
All add-functions take optional keyword arguments. The :parent and :context arguments, if present, must be elements . These create the parent-wire and context-wire connections described in section 2. The :context argument usually defaults to the value of *context*, whose default value is the “common:general context” node. If you are creating a lot of new elements in some other context (such as “Harry Potter world”), it is probably easier to temporarily rebind *context* using in-context than to provide a :context argument for every add-call.
If iname is not a required argument for an add-function, it may still be possible to add an internal name to a new element using an optional :iname keyword argument. You should give the element a name if you think it may be necessary for some other element to refer to it.
Some of the add-functions also take an optional :xname keyword argument. The purpose of the :xname argument is described in section 7.3 of this document.
So a typical add-function call might look something like this:
(add-indv “Iraq” :parent “country”
:context “general
context”)
When adding a lot of elements by hand to a Scone KB, the Common Lisp * convention may save a good deal of typing. When you type an expression into the Common Lisp listener (whether typed directly or via some Emacs-like front-end), the system evaluates the form and prints out the value returned by the form. When evaluating the next form the variable named * is bound to the result of the previous form. So you can type something like this:
(add-type “kingdom” :parent “country”)
(add-indv “England” :parent *)
The first form returns the newly-created “kingdom” node, and the second form refers to that node.
Similarly, the variable ** is bound to the result of evaluating the expression prior to the last one, and the variable *** is bound to the one before that. (Wisely, the Common Lisp designers decided to stop this at three levels.) It is not recommended that you rely upon this convention when creating a KB file by hand, since subsequent edits and rearrangements can create very subtle bugs.
The add-functions currently defined in Scone are the following:
(add-indv iname &key :parent :context :xname)
Creates an indv or “individual” node, representing a specific entity that exists in some context or description.
(add-integer value &key :parent :context)
(add-real value &key :parent :context)
(add-word value &key :parent :context)
These functions create integer, real, and word nodes. All of these node-types are specializations of the indv node with values (stored as inames) that happen to be directly meaningful to programs. The value argument must be a Common Lisp integer, float, or string, respectively.
(add-type-node iname &key :parent :context :set :xname)
Analogous to add-indv, but creates a type-node representing the typical member of some class.
The :set argument is the set-node associated with this type-node. The context-wire is normally tied to the same node as the set-node’s context-wire.
Normally the user does not call add-type-node directly. The add-type function (see below) creates both the type node and the associated set node.
(add-relation-node iname domain range &key
:parent :context :xname
:reflexive :symmetric :transitive)
Creates and returns a relation node. This is a kind of type-node that defines a relation with the specified iname (string), domain (element), and range (element). The :parent, :context, and :xname arguments are the same as in other “add” functions. The :reflexive, :symmetric, and :transitive arguments, if present and non-nil, indicate that these are properties of the new relation.
(add-split-node iname &key :parent :context :set :xname)
Like add-type-node, but creates a split node, which is a specialized form of type-node. Again, the user does not normally call this directly. See add-split below.
(add-map role owner &key :iname :xname)
Create a map-node representing the specified role of the owner element. An internal name for this node may be supplied by the user via the :iname argument. If no :iname is supplied, the system tries to create a suitable one. For example, if the function call is (add-map “king” “Afghanistan”), the system would create the iname “king of Afghanistan” for this map-node.
Note that this map node is not a statement that can be cancelled or modified. If you want to say that Afghanistan has no king, the proper way to do that is to use a cancel link to eliminate the “king” role that would otherwise be inherited.
(add-is-a a b &key :ianme :parent :context)
Creates a link stating that “A is a B” in the specified context.
(add-eq a b &key :iname :parent :context)
Creates a link stating that “A and B are the same entity” in the specified context. Often used to equate an individual node to a role, such as “king-of-Afghanistan”. Note that A and B are interchangeable. Unlike most Scone links, this one is symmetrical in its operation.
(add-has-a a b &key :iname :parent :context)
Creates a link that says “for every copy of description A, there exists an B”. B may be an individual or a type-set pair. This is equivalent to connecting B’s context wire to A.
(add-is-not-a a b &key :iname :parent :context)
Creates a link stating that “A is not a B” in the specified context. Use this in situations where A would normally inherit class-membership in B, but you want to cancel this. It may be necessary to add new is-a links from A to type-nodes above B if you want A to retain membership in those classes.
(add-cancel a b &key :iname :parent :context)
Creates a link stating that “for A, statement B is cancelled” in the specified context. This is used in situations where the description of A would otherwise be affected by statement B and we want to cancel B in this description. Do not use this to cancel class-membership – use an is-not-a link instead. And do not use this to eliminate an inherited role – use a cancel link.
(add-statement a rel b &key :iname :context)
The a and b arguments may be elements of any kind. The rel argument must be a relation node. Creates and returns a link representing the statement that a rel b in the specified :context.
The following calls represent higher-level idioms, many of which create more than one element in the KB.
(add-type iname &key :parent :context :set-node-parent :xname)
This creates two nodes – a type node and the associated set node – and returns the type node as the value of the function. The vast majority of type-nodes are initially created in this way. If a :parent argument is specified, that element becomes the parent of the type-node. The parent of the set node is normally “set”, but a more specific element may optionally be specified via the :set-parent argument. The :context argument, if supplied, becomes the context of both the set-node and the type-node. The type-mode’s set-wire will point to the set-node.
(add-relation iname domain range &key :parent :context
:set-node-parent :xname :reflexive
:symmetric :transitive)
Creates and returns a pair of nodes: a relation node and the associated set node. This is a kind of type-node that defines a relation with the specified iname (string), domain (element), and range (element). The :parent, :context, :set-node-parent and :xname arguments are the same as in add-type. The :reflexive, :symmetric, and :transitive arguments, if present and non-nil, indicate that these are properties of the relations described by the new def-type node.
(add-complete-type iname &key :parent :context :xname)
This is like add-type, but the parent of the new set-node is “complete-set”. This implies that this is a complete set or complete type. That is, the immediate subtypes of the type node completely cover the set of possibilities. Every member of the parent class must belong to at least one of these subtypes.
(add-split iname &key :parent :context :xname)
This is like add-type, but the new type-node is actually a split-node, and the parent of the new set-node is “split-set”. This implies that the immediate subtypes of the type node are disjoint. A subtype or individual cannot belong to more than one of these subtypes. So, for example, "bird", "reptile", and "mammal" form a split whose parent is "vertebrate". The system automatically and efficiently detects any attempt to violate a split.
(add-complete-split iname &key :parent :context :xname)
This is like add-type, but the new type-node is actually a split-node, and the parent of the new set-node is “complete-split-set”. This implies that the immediate subtypes of the type node are both disjoint and complete. An individual member of the parent class must belong to exactly one of these subtypes. A very common situation is a two-member complete split, such as animate/inanimate.
(add-split-subtypes parent items &key :context :xname)
Create a set of disjoint subtypes under parent. First we create a new split-set under parent. The items argument is a list of strings. For each of these, we create a new type/set pair (in the specified context) that is an immediate descendant of the new split. Return the new split node.
(add-members parent items &key :context :xname)
Like add-split-subtypes, but the items become new indv nodes rather than subtypes.
(add-complete-split-subtypes parent items &key :context :xname)
Like add-split-subtypes, but creates a complete split.
(add-complete-members parent items &key :context :xname)
Like add-complete-split-subtypes, but the items become new indv nodes rather than subtypes. In other words, this states that the items list contains all the individual members of the parent set.
(type-is-complete type-node &key :context)
The type-node argument must already exist. (It may be a split.) Declare that it is now a complete type. This is accomplished by adding an is-a link from the set-node to “complete-set”. Returns the new is-a link.
(split-under parent items &key :context)
The items argument is a list of nodes that already exist. Declare that these form a split-set under parent. Returns the new split node.
(add-indv-role iname owner &key :parent :xname)
Creates an individual role node with the specified iname and owner. The owner is the description (normally a type-node) within which this role exists. This function is actually just a wrapper for add-indv, with the owner acting as the new node’s context. :parent, if not supplied, defaults to “thing”.
(add-type-role iname owner &key :parent :n :xname)
Creates a type/set pair that is a role within owner. This is just a wrapper around make-type with owner serving as the context argument. The :parent argument is the parent of the new type-node. It defaults to “thing”.
(create-<role-chain> role-chain &key :start-marker)
The role-chain argument is a list of nested roles, as described in section 6.7. This function finds and returns the specified role-node or map-node, creating it if necessary. Signals an error if this role is not defined.
(create-the-x-of-y x y &key :start-marker)
Returns the node representing the x of y, creating it if necessary.. This is just create-role-chain for the common case of a two-element role-chain – a single role-relation.
(x-is-<role-chain> x role-chain &key :start-marker)
Uses create-<role-chain> to find or create the specified role-node or map-node. Checks whether element x can legally be equated (with an EQ-link) to this node without violating any type restrictions. If so, create and return the EQ-link.
(<role-chain>-is-x role-chain x &key :start-marker)
Alternative form of <role-chain>-is-x. For this and several other calls creating EQ-links, I can never remember the proper order, so I include both forms.
(x-is-the-y-of-z x y z &key :start-marker)
Find or create the node representing “the y of z”. Then equate element x to this if it is legal to do so. This is just x-is-<role-chain> for the common case of a two-element role-chain – a single role relation.
(the-x-of-y-is-z x y z &key :start-marker)
Alternative form of x-is-the-y-of-z.
(x-is-a-<role-chain> x role-chain &key :start-marker)
Find or create the node specified by role-chain. This should be a type-node or map of a type-node. Check whether it is legal for x to be of this type. If so, create and return an is-a link from x to this type-role.
(x-is-a-y-of-z x y z &key :start-marker)
This is just x-is-a-<role-chain> for the common case of a chain with two elements – a single role relation.
(<role-chain>-is-an-x role-chain x &key :start-marker)
The x argument should be a type-node. Find or create the node specified by role-chain. If it is legal for this role node to be of type x, create and return an is-a link stating that this is so.
(the-x-of-y-is-a-z x y z &key :start-marker)
This is just x-is-a-<role-chain> for the common case of a chain with two elements – a single role relation.
(convert-parent-wire-to-link e)
E is an element. Convert its parent-wire to a full-fledged is-a link, returning the new link element.
(convert-context-wire-to-link e)
E is an element. Convert its context-wire to a full-fledged has-a link, returning the new link element.
Beware: Wholesale removal of elements can leave the knowledge base in a bad state, with lots of dangling connections that give rise to anomalous behavior. However, it is sometimes necessary to remove a few elements to repair some error or problem in the knowledge base. It is usually safe to remove the last N elements created, since these refer to earlier elements but earlier elements usually don’t refer to them. Note, however, that the deferred-connection mechanism can create such references.
Disconnects all wires leading to element e and removes it from the knowledge base. If e is part of a type-set pair, remove both elements. Returns the number of elements remaining in the KB.
(remove-last-element &optional n)
Removes the last element in the knowledge-base, or the last type/set pair. If the n argument (an integer) is supplied, remove n elements or pairs. Returns the number of elements remaining in the KB.
Remove all the elements added by the last loaded file (and any elements added by hand after that bile was loaded). Returns the number of elements remaining in the KB.
These are convenience functions for various domains, created by files that are part of the “common” base-load of knowledge.
(set-cardinality type-node integer)
This is defined in the “general” KB file. Find the set-node associated with the type-node argument. Create a new integer-node representing the integer argument. Equate the “cardinality” role of the set-node to this integer-node, returning the new EQ-link.
(add-measure number unit &key :parent)
This is defined in the “physics” KB file. It creates and returns an individual of type “measure”, which is the combination of a number and a unit – something like “16.0 ton”. If the number argument is an integer, it will first be converted to a float. The :parent argument, if supplied, indicates that the new measure is some specific kind of measure, such as “mass measure”.
Note that the unit should be a singular form: “ton” rather than “tons”. We don’t yet have the machinery to deal smoothly with plurals.
One long-term goal for Scone is to interface it to a natural-language input-output system so that users can augment or query the knowledge base in English (or in the natural language of their choice). At present, the system has only a simple facility for associating words or phrases with various elements in the knowledge base. We refer to these words and phrases collectively as “external names” to distinguish them from the internal element-names described above.
While most of the natural-language machinery is external to Scone, it makes sense to maintain the dictionary mapping names to elements and elements to names as part of the Scone system. When a KB file full of new concepts is loaded into the knowledge base, the external names for these concepts can be loaded at the same time.
The mapping between external names and Scone elements is many-to-many. Scone provides a simple interface by which an external natural-language system can ask for all the Scone elements (if any) that correspond to a given name or all the names (if any) that correspond to a given element. In some cases, a name may map into a Lisp function that is to be called, rather than mapping directly into a Scone element.
Note that the name strings are matched exactly (at present there is no spelling correction or “find an approximate match” capability) and the string matching is case-sensitive. In general, external name strings should be lower-case, except in the case of proper nouns like “Iraq”. In multiple-word strings, the words should be separated by a single space.
Each mapping of a name-string to an element has an associated name-type. This is a Lisp keyword that corresponds roughly to the part of speech. At present, the following name-types are supported: :name, :role-name, and :adjective.
A :name is an English common or proper noun, which will generally be associated with a Scone type-node or indv-node. “Clyde” and “elephant” are names.
A :role-name is the label for some Scone role-name (an indv-role or a type-role). Usually this will be followed by preposition “of”. Examples of role-names are “mother”, “parent”, and “president”. Note that a role-name is sometimes used like a regular name: we ask the knowledge base for “the mother of Clyde” or ask for a list of “all mothers”.
As the name implies, an :adjective corresponds roughly to an English adjective. For example, “adult” or “invisible” define Scone classes that are normally named with adjectives. Typically the adjective is combined with the name of some superior or sibling type-node to designate the entity, as in “adult elephant” or “invisible man”. Note that some adjectives, such as “big” represent complex predicates, not simple classes.
(register-element-name element name name-type)
Associates name with element, with the given name-type.
(unregister-all-names element)
Remove all the names (of all name-types) associated with element. This is useful when removing the element or to correct errors.
(lookup string &optional type-name)
Returns all the values associated with string. A value will be of the form (name-type . element). If a type-name argument is supplied, return a list containing only values of the specified type.
(mark-named-elements string m &optional type-name)
Like lookup, but instead of returning a list of the elements associated with string, we mark them with marker m.
(get-names element &optional type-name)
Returns all the names associated with element. A name will be of the form (name-type . string). If a type-name argument is supplied, return a list containing only names of the specified type.
This function prints out every entry in the external-name dictionary. If the KB is very large, this may print more than you expect. This is used mainly for debugging.
(synonyms name-string &optional name-type)
Finds all the elements that are registered as meaning of name-string in the KB. For each of these, creates a list of synonym name-strings. Returns a list, each element of which has a meaning-element as the first element and a list of synonym strings as the second element. If type-name is non-null, we look only at elements whose connection to name-string is of this type.
(show-synonyms name-string &optional name-type)
Like synonyms, but prints out the synonyms it finds instead of returning a list.
(add-name element name)
(add-names element name-list)
The add-name call associates element with name, which must be a string. The name-type is :name. In the add-names call, name-list is a list of name-strings, each of which is registered as a name for element.
(add-role-name element name)
(add-role-names element name-list)
Like add-name or add-names, but the name-type is :role-name.
(add-adjective element name)
(add-adjectives element name-list)
Like add-name or add-names, but the element-type is :adjective.
Often is it convenient to register the internal name of a newly created node as its external name (or as one of its external names, if you later add more). This behavior is controlled by the :xname argument to the various add-functions that create nodes.
The :xname argument may be nil (indicating that the iname should not be registered as an external name), or it may be any of the name-type constants listed above: :name, :property-name, and so on. If one of these is supplied, the internal name is registered as an external name of the node being created by the add-function. A value of t is also allowed; this is a shorthand equivalent for :name.
If no :xname argument is supplied, the default for type-nodes and indv-nodes is as if :name were supplied as the :xname argument. However, the default for a set node created at the same time as its type-node is nil. Set nodes rarely have external names. The default for split-nodes and map-nodes is also nil.
For the add-indv-role and add-type-role functions, the default value for :xname is :role-name.
Links so rarely have external names that the various add-link functions do not take an :xname argument, though you can register an external name for a link using the functions in the previous section. The same is true for elements of type integer, real-number, and word.
Note that in add-split-subtypes and other functions that create a number of new nodes, a non-null :xname argument says that the internal name of each new node in the set should be used as its external name as well, with the specified name-type. The default :xname argument for these functions is :name.
Pre-assigned Markers
context-marker
result-marker
temp-marker
Loading KB Files
(load-kb filename &key :verbose)
(run-script
filename
&key :verbose)
Simple Functions
(in-context c)
(marker-count m)
(get-set-node e)
(get-type-node e)
(get-context e)
Internal Names
(lookup-element e)
(full-iname e)
(extract-namespace string)
(extract-name string)
(make-namespace name &key :include)
(in-namespace name &key :include)
(process-deferred-connections)
Display Functions
(marker-counts)
(element-counts)
(show-elements &key :n :all :last)
(show-marked m &key :n :all :last)
(show-not-marked m &key :n :all :last)
(mark-supertypes e m)
(show-supertypes e &key :n :all :last)
(mark-subtypes e m)
(show-subtypes e &key :n :all :last)
(mark-instances e m)
(show-instances e &key :n :all :last)
(mark-children e m)
(show-children e)
(mark-roles e m &key :type-roles :indv-roles :set-roles
:maps
:n :all :last)
(show-roles e &key :type-roles :indv-roles :set-roles :maps
:n :all :last)
(mark-lowermost m1 m2)
(show-lowermost m &key :n :all :last)
(show-most-specific m &key :n :all :last)
Marker Operations and Scans
(cancel-marker m)
(marker-on? e m)
(marker-off? e m)
(marker-count m)
(mark e m)
(mark-all m)
(unmark e m)
(clear-marker m)
(clear-marker-pair m)
(clear-all-markers)
(mark-boolean m must-be-set must-be-clear)
(unmark-boolean m must-be-set must-be-clear)
(upscan start-element m &key :owner-marker :augment)
(downscan start-element m &key :augment)
(eq-scan start-element m &key :augment)
(any-violated-splits? m)
Predicates
(is-x-a-y? x y)
(can-x-be-a-y? x y)
Roles and Role-Chains
(does-x-have-a-y? x y)
(mark-the-x-of-y x y &key :start-marker :predicate)
(show-the-x-of-y x y &key :start-marker)
(can-x-be-the-y-of-z? x y z &key :start-marker)
(can-x-be-a-y-of-z? x y z &key :start-marker)
(mark-<role-chain> role-chain &key :start-marker
:predicate)
(show-<role-chain> role-chain &key :start-marker)
(can-x-be-<role-chain>? x role-chain &key
:start-marker)
(can-x-be-a-<role-chain>? x role-chain &key
start-marker)
Accessing Relations
(statement-true? a rel b :start-marker)
(mark-rel a rel mb)
(mark-rel-inverse ma rel b)
(show-rel a rel &key :start-marker :n :all :last)
(show-rel-inverse rel b &key :start-marker :n :all :last)
Single-Element Add-Functions
(add-indv iname &key :parent :context)
(add-integer value
&key :parent :context)
(add-real value &key :parent :context)
(add-word value &key :parent :context)
(add-type-node iname &key :parent :context :set)
(add-relation-node iname domain range &key :parent :context :xname
:reflexive :symmetric :transitive)
(add-split-node iname &key :parent :context :set)
(add-map role owner &key :iname)
(add-is-a a b &key :iname :parent :context)
(add-eq a b &key :iname :parent :context)
(add-has-a a b &key :iname :parent :context)
(add-is-not-a a b &key :iname :parent :context)
(add-cancel a b &key :iname :parent :context)
(add-statement a rel b &key :iname :context)
Higher-Level Add-Functions
(add-type iname &key :parent :context :set-node-parent)
(add-relation iname domain range &key :parent :context
:set-node-parent :xname :reflexive
:symmetric :transitive)
(add-split iname &key :parent :context)
(add-split-subtypes parent items &key :context)
(add-members parent items &key :context)
(add-complete-type iname &key :parent :context)
(add-complete-split iname &key :parent :context)
(add-complete-split-subtypes parent items &key :context)
(add-complete-members parent items &key :context)
(type-is-complete type-node &key :context)
(split-under parent items &key :context)
Adding Roles and Fillers
(add-indv-role iname owner &key :parent)
(add-type-role iname owner &key :parent :n)
(create-the-x-of-y x y &key :start-marker)
(x-is-the-y-of-z x y z &key :start-marker)
(the-x-of-y-is-z x y z &key :start-marker)
(x-is-a-y-of-z x y z &key :start-marker)
(the-x-of-y-is-a-z x y z &key :start-marker)
(create-<role-chain> role-chain &key :start-marker)
(x-is-<role-chain> x role-chain &key :start-marker)
(<role-chain>-is-x role-chain x &key :start-marker)
(x-is-a-<role-chain> x role-chain &key
:start-marker)
(<role-chain>-is-an-x role-chain x &key
:start-marker)
Converting Wires to Links
(convert-parent-wire-to-link
e)
(convert-context-wire-to-link e)
Removing Elements
(remove-element e)
(remove-last-element &optional n)
(remove-last-loaded-file)
Domain-Specific Creation Functions
(set-cardinality type-node integer)
(add-measure number unit &key :parent)
External Names
(register-element-name element name name-type)
(unregister-all-names element)
(lookup string &optional type-name)
(mark-named-elements string m &optional type-name)
(get-names element &optional type-name)
(show-dictionary)
(synonyms name-string &optional name-type)
(show-synonyms name-string &optional name-type)
(add-name element name)
(add-names element name-list)
(add-role-name element name)
(add-role-names element name-list)
(add-adjective element name)
(add-adjectives element name-list)