RuleWorks

Knowledge Representation and Working Memory

Working memory is the dynamic, global database which encodes the problem a RuleWorks program is to solve and the current state of the solution. Different aspects of the problem are organized into different classes of information in working memory. Classes which store related information can be grouped into an inheritance hierarchy.

Each class contains a set of named attributes with their associated attribute structure and default values. Attribute structure is scalar (atomic) or multi-valued (compound). Each instance of a class is called a working-memory object (WMO).

As a RuleWorks program runs, WMOs are continually created, changed, and deleted. In OPS5, every rule in a program could "see" (potentially match, change, or delete) every object in working memory. The advantage with RuleWorks is that when a given entry block is executing, the rules in that block are only able to see working memory which is in scope.

Object Class Hierarchy

RuleWorks allows classes directly to represent hierarchical categories of data. Object classes can inherit attribute names, attribute structure, and default values from a parent class. A parent class can have many subclasses, but each subclass can have only one immediate parent. This is called single inheritance as opposed to multiple. You can think of this graphically as a tree with branches and leaves. The figure shows the inheritance tree of the object class PART in the sample program KIWI.

Inheritance from a parent class is declared in the INHERITS-FROM clause of the OBJECT-CLASS declaration of the subclass:

(OBJECT-CLASS subclass-name
(INHERITS-FROM parent-class-name))

Because objects inherit membership in the parent class, objects match condition elements not only of their own class, but also of their ancestor object classes (their parents, and their parents' parents, and so on). Thus, a condition element can ask for an object of a high-level class (such as OPTION), and be matched by instances of low-level subclasses (such as KIWICALC, BW-MONITOR, or MOUSE).

Figure 2-1. Example of a Single Inheritance Hierarchy

Table 2-1 lists all the characteristics of object classes and attributes that can be inherited by a subclass. Default values can be redefined by a subclass; the declaration in the subclass overrides the declaration in the parent class for that subclass and for all classes inheriting from it. Attribute structure cannot be redefined; it is a compile-time error to try to redefine a scalar attribute to be compound, or vice-versa.

Table 2-1. Inherited Characteristics

Characteristic Can be Redefined ? Details in section...
Membership in the parent class No Object Class Hierarchy
Attribute names No Attributes
Attribute structure No Compound Attributes
Data type of each attribute No Data Type Declarations
Default value of each attribute Yes Default Value Declarations

RuleWorks provides an implicit top-level object class called $ROOT. Any object class that you declare without an explicit INHERITS-FROM clause is treated as though you declared the parent class $ROOT. Figure 2-2 shows the implied hierarchy for the top-level user-declared object classes in the sample program KIWI.RUL.

The specificity of a class is measured by its distance from $ROOT in the inheritance hierarchy. Any top-level user-declared object class, such as those shown in figure 2-2 has class specificity of one. Object classes SOFTWARE-OPTION and HARDWARE-OPTION, shown in figure 2-1 have class specificity of three. Object classes HD-30 and HD-200 have class specificity of six.

Figure 2-2. Inheritance from $ROOT

Working Memory Objects

A working-memory object (WMO), sometimes called simply an object, is a collection of attributes each with some value (one or more atoms, or units of data) that represents a thing or concept and its associated characteristics. In addition to attributes with values, each object has a class name, an identifier, and a time-tag. Figure 2-3 illustrates how objects are stored in working memory.

Figure 2-3. Conceptual Model of a Working-Memory Object

Note: The left-to-right order of the attributes shown in The figures do not necessarily reflect the actual internal representation. In RuleWorks you cannot depend upon the position of attributes in a WMO.

All WMOs inherit two read-only attributes from the built-in top-level class $ROOT: ^$ID and ^$INSTANCE-OF. These attributes store the instance identifier and class name of the WMO (see Object Identifiers and Class Name)

RuleWorks displays WMOs in the following format:

#id time-tag( class-name [rule-name] [ {^attribute-name attribute-value} ...])

For example:

#297 2529 (PERSON [INIT-DB] ^GIVEN-NAME LARRY ^LAST-NAME KIRK ^CHILDREN (COMPOUND ALEXANDER ALICIA ANDREW) ^SPOUSE NINA)

RuleWorks does not include the names ^$ID and ^$INSTANCE-OF when it displays a WMO, but it does include the values of these attributes. RuleWorks does not display scalar attributes whose value is NIL, nor compound attributes that have no elements, unless you have declared a default value for the attribute (see Default Value Declarations for more on default values).

Object Identifiers

RuleWorks associates a unique identifier with each object when it is created. This object identifier has the data type INSTANCE-ID. Unlike the time-tag, which is updated each time the object is modified, the object identifier is guaranteed to remain unchanged during the life of the object. Object identifiers are stored in a built-in, read-only attribute called ^$ID. It is an error to attempt to modify ^$ID.

A variable bound to an INSTANCE-ID value is called an ID variable.

ID variables can be compared for identity or non-indentity, as in the following rule, but nothing else:

Example 2-1. Comparing Object Identifiers


(rule verify-configuration:kiwindows-needs-2-memory-cards-found-one

;This rule matches when there is exactly one object of class MEMORY

(active-context ^name verify-configuration)

(kiwindows)

(memory ^$id <mem-id>) ; there is one MEMORY

- (memory ^$id <> <mem-id>) ; but there isn't another one

-->

(make error ^severity warning ^message |Insufficient memory|)

(write (crlf) |Caution: KiWindows requires two memory cards,|
(crlf) | but you have only one memory card.|
(crlf) | Fixup: adding another memory card to your order.|
(crlf))

(make memory)

)

Class Name

A class name is a symbol that identifies the class of which the object is an instance. Objects that are instances of the same class always have the same attributes, but the values of the attributes can be different. The INSTANCE-ID and time-tag of each object are unique to that object.

Figure 2-4 shows seven different objects with the class name CONTEXT. Each of the seven has the same attribute, ^NAME, but each has a different value for that attribute.

Figure 2-4. Multiple Objects of One Class

The class name of an object is stored in a built-in, read-only attribute named ^$INSTANCE-OF. You can use this attribute on the LHS of a rule, but you cannot modify it.

Time-Tags

Time-tags are integers that the run-time system uses to determine recency during conflict resolution. You cannot match or modify time-tags. The run-time system assigns a unique time-tag to each object in working memory. The time-tag is automatically updated whenever the object is modified. Therefore, the object with the largest time-tag is the most recently created or changed.

Attributes

An attribute consists of the attribute operator (^) followed by an attribute name that describes a characteristic of the object class. Attribute names are symbols and are declared in the OBJECT-CLASS declaration.

Attributes can be declared in a parent class or in any inheriting subclass. For example, all components of the Kiwi-9200 share some attributes by virtue of being a PART. These attributes (^PART-NUMBER, ^NAME, ^PRICE, and ^IS-EXPANDED) are specified in the OBJECT-CLASS declaration of PART and propagated down from PART to OPTION, to HARDWARE-OPTION and SOFTWARE-OPTION, and so on, by inheritance. The attributes that distinguish hardware from software are specified in separate OBJECT-CLASS declarations. (See Example 2-2)

Example 2-2. Declaring Additional Attributes

;The OBJECT-CLASS HARDWARE-OPTION. Hardware-options
;have to know whether they take up a slot in the
;CPU box, whether they have been placed in a slot,
;and what slot they are in.

(OBJECT-CLASS hardware-option
(inherits-from option)
^takes-slot
^is-placed
^in-slot

)


;The OBJECT-CLASS SOFTWARE-OPTION. All software
;options have a media type as well as the
;other attributes inherited from OPTION and PART.
;
(OBJECT-CLASS software-option
(inherits-from option)
^media-type

)

Suppose you want to specify an object that has the class name KIWICALC, is distributed on a 3.5-inch floppy disk, has part number S-CA-9200, name KiwiCalc Spreadsheet Software, price $29.95, and a status flag whose value is YES. You can specify this object as shown in the example.

Example 2-3. Specifying an Object

(kiwicalc ^media-type FD-35

^part-number S-CA-9200

^name |KiwiCalc Spreadsheet Software|

^price 29.95

^is-expanded yes

)

You can declare this object class as follows:

(OBJECT-CLASS KiwiCalc

(inherits-from KIWOS-APPLICATION))

In this example, all the attributes are inherited. For a detailed description of the OBJECT-CLASS declaration, see Chapter 3 of this manual.

Attributes are local to an object class and its descendants; you can use the same attribute name in unrelated object classes. For example, the attribute ^NAME is used in both of the following objects:

(CONTEXT ^NAME INPUT-ORDER)

(KIWICALC ^MEDIA-TYPE FD-35 ^NAME KIWICALC)

Scalar Attributes

The value of a scalar attribute is a single atom (see the section Atoms). For example:

^NAME |KiwiCalc Spreadsheet Software|

^PRICE 29.95

The values of these attributes are the symbolic atom |KiwiCalc Spreadsheet Software| and the floating-point atom 29.95. The figure shows how the run-time system stores the scalar attribute values for a KIWICALC object.

Figure 2-5. Storing the Values of Scalar Attributes

Compound Attributes

A compound attribute contains a dynamically-sized, ordered collection of scalar values. Such a collection is called a compound value. When a compound attribute is bound to a variable, the variable is called a compound variable and has a compound value. The individual scalar atoms are called elements of the compound value.

Compound values are created (and displayed) with the COMPOUND function. For example:

(COMPOUND MEMORY MEMORY KEYBOARD FD-35)

RuleWorks does not have multilevel lists like LISP; the result of concatenating the compound value (COMPOUND A B C), the atom D, and the compound value (COMPOUND E F) is the compound value (COMPOUND A B C D E F), not the multilevel list ((A B C) D (E F)).

Figure 2-6 shows a conceptual model of an object class that has two compound attributes.

Figure 2-6. Conceptual Model of Compound Attributes

The object in figure 2-6 has the class name BOX; the scalar attributes ^PART-NUMBER, ^NAME, ^PRICE, and ^IS-EXPANDED; and the compound attributes ^CARD-IN-SLOT and ^CARD-IN-SLOT-OBJ-ID. Note that the left-to-right order of the attributes in the figure does not necessarily reflect the actual internal representation of an object. The top-down order of the values in the compound attributes, however, does match the actual representation. Compound attributes are indexed starting at 1 (not 0). The first element of ^CARD-IN-SLOT has the value MEMORY; the fourth and last has the value FD-35.

There is no preset limit on the number of atoms allowed in compound attributes; their size is limited only by memory available. You do not need to allocate memory for them. The size of a compound value varies during a program's execution. See Chapter 3 for information on matching compound attributes; see Chapter 4 for information on creating and changing compound values.

Use the COMPOUND keyword in an OBJECT-CLASS declaration to define the name of a compound attribute.

Example 2-4. OBJECT-CLASS

(OBJECT-CLASS box
(INHERITS-FROM part)
^card-in-slot COMPOUND
^card-in-slot-obj-id COMPOUND)

The compound attribute declaration is local to the object class (and its inheriting subclasses), so it is possible for an attribute name to be compound in one object class and scalar in another. It is not possible for an attribute to be compound in the parent class but scalar in a subclass, or vice-versa.

Data Type Declarations

By default, the value of a scalar attribute can have any valid atomic data type, and the value of a compound attribute can be a mixture of data types. RuleWorks uses a weak typing system for attribute values. That is, data type declarations are not required but if you provide one, RuleWorks enforces it.

Data types can be added to the OBJECT-CLASS declaration after the attribute name whose type you want to specify as shown in example 2-5.

Figure 2-5. Adding Data Types to an OBJECT-CLASS Declaration

(OBJECT-CLASS class-name {^attribute-name [COMPOUND] data-type }... )

 

For example:

(object-class person

^name symbol

^age integer

^children compound symbol)

Table 2-2 shows the symbols that can be used in data type declarations for attributes.

Table 2-2. Data Types for Attributes

Data Type Description Default value
INTEGER Integer number 0
FLOAT Floating point number 0.0
NUMBER Either kind of number 0
SYMBOL Symbolic atom NIL
INSTANCE-ID Object identifier #0
OPAQUE Virtual address %x0
ANY Any of the above NIL

RuleWorks does not perform automatic coercion on values assigned to typed attributes, not even between integers and floats. To ensure that a value has the correct type, use one of the built-in coercion functions: FLOAT, INTEGER, or SYMBOL.

Default Value Declarations

When you create an object, you do not need to specify a value for each attribute. RuleWorks supplies a default value for attributes whose values were not specified. You can specify the default value for each attribute, or use RuleWorks's default.

If an attribute does not have a data-type declaration, the default value is the NIL for scalar attributes and the empty list ((COMPOUND)) for compound attributes. If an attribute has a data type declaration but does not have default value declaration, its default value depends on its data type (see Table 2-2).

You specify a default value for an attribute with the DEFAULT keyword in the OBJECT-CLASS declaration. The following example shows the declaration of the PART class from the sample configuration program.

Example 2-6. Declaration of the PART Class

(OBJECT-CLASS PART

^PART-NUMBER
^NAME
^PRICE ^IS-EXPANDED (DEFAULT NO))

When any object that inherits from class PART is made, and no new value for the ^IS-EXPANDED attribute is specified, the value of that attribute is NO.

For a compound attribute, the specified default value is itself a compound value that will be used as the initial contents of the attribute, if none is specified in the MAKE action. The following example declares an initial value for the hypothetical compound attribute ^TASKS:

Example 2-7. Declaring an Initial Value for ^TASKS

(OBJECT-CLASS AGENDA
^TASKS COMPOUND SYMBOL
(DEFAULT (COMPOUND INPUT-ORDER
CONVERT-TO-PARTS
VERIFY-CONFIGURATION
EXPAND-PART-SKELETONS
CHOOSE-SLOTS
MODIFY-SOFTWARE-MEDIA
OUTPUT-NEW-ORDER)))

Fill Value Declarations

You can modify a compound attribute by specifying a new value for any element. Thus, it is possible to modify a compound attribute "beyond" the last assigned element. The question arises, what value do elements between the former end and the new end of the compound now have? To answer this question, RuleWorks allows you to declare a fill, or placeholder, value for the compound attribute.

RuleWorks' default fill value depends on the data type of the compound attribute, if any. You specify a different fill value in the OBJECT-CLASS declaration, after the COMPOUND keyword. For example, consider the declaration shown below:

(OBJECT-CLASS AGENDA

^TASKS COMPOUND

(DEFAULT (COMPOUND INPUT VERIFY OUTPUT))

(FILL NO-OP))

When an AGENDA object is made, the default value of its ^TASKS attribute is (COMPOUND INPUT VERIFY OUTPUT). Suppose the following MODIFY action is executed:

(modify <my-agenda> ^tasks[5] clean-up)

The value of ^TASKS is now (COMPOUND INPUT VERIFY OUTPUT NO-OP CLEAN-UP).

Default Value Declarations explains how to specify a default value for the entire compound attribute.

Value Expressions

In RuleWorks an attribute value can be set by using a constant, a variable, an arithmetic expression, a function call, or an expression that contains any of these things.

Atoms

The smallest unit of data in a RuleWorks program is called an atom. Atoms must be one of the data types listed in table 2-2.

Symbolic and Quoted Atoms

A symbolic atom is one that does not have a numeric value. A symbol consists of from 0 to 256 printable ASCII characters on a single line. For example:

c

PLANT

?-c

10-14

RuleWorks supports the eight-bit DEC Multinational Character Set. The table lists the characters that cannot be used in unquoted RuleWorks symbols.

Table 2-3. Special Characters

Character Description
Parenthesis (()) Enclose rules, actions, and so on.
Braces ({}) Indicate conjunctions.
Brackets ([]) Index compound attributes.
Caret (^) The attribute operator.
Semicolon (;) The comment character.
Vertical bar ( | ) The quote character.
Pound sign (#) Indicates an instance identifier.
Percent sign (%) Indicates an opaque address.
White space Tokens (space, tab, line feed, form feed, vertical tab).
Ampersand (&) Reserved for future use.
Double-quote (") Reserved for future use.
Tilde (~) Reserved for future use.

Note: RuleWorks does not support null characters in any symbol, quoted or not.

To include special characters or white space in an atom, quote the atom by enclosing it in vertical bars ( | | ). The text between the two vertical bars is considered to be one symbolic atom. For example, the compiler and run-time system recognize THIS IS AN ATOM; THIS IS NOT to be four symbolic atoms followed by a comment, which they ignore. However, they recognize | THIS IS AN ATOM; THIS IS NOT | to be a single symbolic atom.

Quoted atoms are symbolic atoms; therefore,| 1.2 | is a symbol, not a floating-point number, and arithmetic operations cannot be performed on it. The opening and closing vertical bars, which can enclose as many as 256 characters, must appear on the same line in the code. The empty symbol are vertical bars enclosing nothing (||).

To quote a vertical bar itself, double it. For example:

| This is a vertical bar - ||. |

When this atom is displayed or printed, it includes only one vertical bar:

This is a vertical bar - |.

When reading unquoted symbols, RuleWorks automatically turns lowercase characters into uppercase. An unquoted symbol printed by RuleWorks may appear to be different from the symbol read by RuleWorks. If you want the print form to be the same as the read form, you must use quoted symbols. The table shows the read and print forms of several atoms.

Table 2-4. Read Forms and Print Forms

Read Form Print Form Data Type
A A SYMBOL
a A SYMBOL
|a| a SYMBOL
|a b| a b one SYMBOL
a b A B two SYMBOLs
|A B| A B one SYMBOL

All atoms that are not symbols have read forms that are identical to their print forms.

Integer Atoms

Integer atoms consist of the following:

The following are examples of integer atoms:

0

25.

-14

-5.

Integer atoms can represent the same range of values as a "long" in the C language. On Digital UNIX for Alpha, integers are 64 bits. That is, the valid range for integer atoms is - 2 63 to 2 63 - 1. On all other systems, integers are 32 bits or 2 31 to 2 31 -1

Floating-Point Atoms Floating-Point Atoms

A floating-point atom is composed of the following:

An exponent consists of the letter "E" followed by a signed or unsigned integer and represents a power of 10 by which a preceding number is to be multiplied. For example, E-8 represents the value 10 raised to the power -8.

Note: A floating-point atom must include a decimal point followed by a digit or an exponent, or both.

The following are examples of floating-point atoms:

0.0

.25

10.05e-14

-5.e10

Floating-point numbers are stored in double-precision and can represent the same range of values as a "double" in the C language. (On VAX systems, this is D_float data; see the VAX Architecture Handbook. On Alpha systems, this is G_float data; see the Alpha Architecture Handbook.)

Instance Identifier Atoms Instance Identifier Atoms

Values of type INSTANCE-ID are used to identify objects. RuleWorks displays (and you type) an INSTANCE-ID atom as a number sign (#) followed by an integer.

#1

#7955

Note: INSTANCE-IDs are not integers. Arithmetic operations cannot be applied to values of type INSTANCE-ID, and they cannot be compared except for identity or nonidentity.

More information on variables bound to atoms of type INSTANCE-ID is found in Chapter 3.

Virtual Address Atoms Virtual Address Atoms

Values of type OPAQUE store addresses of functions or external data structures. An OPAQUE value is an "atomic address" or in C terms, a "void *".

The print form of an OPAQUE atom is a percent sign and an x (%x) followed by a string of hexadecimal digits. The size of OPAQUE atoms depends on the machine architecture you are using. Note that OPAQUE may or may not be the same size as the INTEGER data type.

There is no way in RuleWorks directly to create a constant of type OPAQUE, except for the null pointer which is a RuleWorks constant. For example:

%x0 ; the null pointer

An OPAQUE constant can be passed into RuleWorks from an external routine, or as an input argument to an entry block, and then passed out or returned.

Note: OPAQUE atoms are not numbers. Arithmetic operations cannot be applied to values of type OPAQUE, and they cannot be compared except for identity or nonidentity.

Variables

A variable is any unquoted symbol enclosed in angle brackets (< >). An example is <PRICE>.

Note: In RuleWorks, the symbols <=> and <-> represent the same-type and different-type operators. They cannot be used as variables.

You can use a variable as an argument to an action if the variable is bound to a value. A variable can be bound to a value in a condition element or in a BIND action.

The following WRITE action has two arguments: a quoted symbolic atom and a bound variable.

(write |The value found was:| <x>)

Arithmetic Expressions

Arithmetic expressions can be used anywhere a numeric constant may be used. An arithmetic expression can contain numbers, variables bound to numbers, functions which return numbers, and arithmetic operators. The expression must be enclosed in parentheses, except when indexing into a compound value. For example:

^price = (<item-1> + <item-2>) ; add two bound variables

^card-in-slot[<n> + 1] memory ; add a variable and a constant

The valid operators are:

Table 2-5. RuleWorks Operators

Operator Description
+ Addition
- Subtraction
* Multiplication
/ Division
\ Modulus

If an expression contains both integers and floating-point numbers, the result is a floating-point number. If an expression contains integers only, the result is an integer (by truncation). For example, 7.0 (float) divided by 4 (integer) is 1.75 (float), but 7 (integer) divided by 4 (integer) is 1 (integer).

Note: Use the modulus operator only with integer operands.

Use infix notation in RuleWorks arithmetic expressions, that is, place operators between operands. Separate each operator and operand with a space. Surround the entire expression with parentheses. For example:

(2 + <X>)

Spaces are not required around any of the special characters in the first five rows of the table.

Expressions are evaluated from left to right. Precedence of operators follows the normal conventions:

- ; unary negation, highest precedence

* / \ ; multiplication, division, and modulus

+ - ; addition and subtraction, lowest precedence

For example, the following expression evaluates to 12, not 20:

(2 + 2 * 5)

To override the normal precedence, use more parentheses. The following expression does evaluate to 20:

((2 + 2)* 5)

Consider the following rule:

(rule choose-slots:place-memory

(active-context ^name choose-slots)

(box ^$ID <the-box> ^card-in-slot { [=] <len> [<] 6 } )

(memory ^$ID <the-mem> ^is-placed NIL ^takes-slot YES)

-->

(modify <the-box>

^card-in-slot [ ( <len> + 1 ) ] memory

^card-in-slot-obj-id [ ( <len> + 1 ) ] <the-mem> )

(modify <the-mem> ^is-placed YES ^in-slot ( <len> + 1 ) )

)

The MODIFY actions in this rule use numeric expressions for the index into a compound attribute and for the value of a scalar attribute.

Note: RuleWorks does not have complex mathematical functions built in. For example, RuleWorks has no ARC-COSINE function. For such complex mathematical functions, a RuleWorks program can call external routines written in other languages that are optimized for algorithmic coding.

Function Calls

The format for specifying a function call is:

(function-name argument-1 argument-2 ..)

Use the same format whether the function is built-in or user-defined (as an external function or an entry block that returns a value).

Note: If a function requires no arguments, you must still enclose the function name in parentheses.

You can represent a value with a call to a built-in RuleWorks function. Consider the following MAKE action:

(make input-thing ^item (accept-atom infil))

First, the run-time system evaluates the call to the ACCEPT-ATOM function. The MAKE action then stores the value returned by the ACCEPT-ATOM function in the ^ITEM attribute of the INPUT-THING object it creates.

You can also represent a value with a call to a user-defined function, written in RuleWorks or some other language. In either case, you must declare the function before you call it. The following BIND action calls the external function SQUARE_ROOT:

(BIND <STD_DEVIATION> (SQUARE_ROOT <VARIANCE>))