Hence an index may carry the association and we can respond to the query:
"Who is the mother of Arnold?" by scanning the quot;mother of" column, picking
the Index 3 at the entry "Arnold" and returning "Mary" from the "3 position" of
NAME.

Such retrieval systems are obvious and are in general use. What, then, are
the faults? Part of the problem lies in the relative paucity of information-the
large number of blanks in the tables. Other problems occur at the time of
search, for the tables are not properly ordered. In fact, the "best order"
depends on what question is expected to be asked. For the question: "Who is the
mother of...?" the order 4,3,2,1 is preferred, whereas "Who is the father
of...?" prefers the order 2,1,3,4 or 1,2,3,4. Hence there is no order that is
optimal for all questions. Another major problem of tabular storage is its size
limitation. To be efficient, the table sizes must be pre-specified, and hence a
sudden request for extra space is potentially catastrophic.

The need for easy addition and deletion led to the list processor, and many
information systems stem from the ideas of IPL-V and SLIP. The former elucidated
and refined the method of pointers and lists, and the ideas of association lists
(Figure 2). The use of lists makes possible dynamically extended tables. A
second use of lists is with association explicitly defined for certain objects.
Thus the illustration of Figure 2b could be written:
<Attribute
A> of <object α> = <value A>
<Son> of
<Arnold> is <James> etc.
Here we see that the question "Who is
the mother of Arnold?" is difficult to answer, because it was not explicitly
stored on Arnold's association list. This question may be answered by searching
all association lists, until one is found which has the pair {Mother, Arnold}.

SLIP was the first embedding of a list-processing capability within a
higher-level language and was a formative ring structure. The idea of rings was
crystalized by Sutherland, and Roberts, and used with data systems designed
primarily for graphics and computer-aided design. Roberts has also developed a
language to refer to rings (Class Oriented Ring Associative Language: CORAL). In
such languages, the associations are built into the structure by allowing blocks
of information to be threaded by rings which carry the associations between the
blocks of data. This is illustrated by Figure 3. Dodd has implemented a similar
structure within PL/I. The duality of certain relationships, such as: "defined
by" and "defines" or "to the left of" and "to the right of," etc., led to the
need for a connector block, here illustrated by the three NUBS.

In essence, the NUB represents a two-way switch for transferring out of one
ring and into another. The subroutines or macros pass along the ring until they
arrive at a NUB. They "switch" it, and pass into the other ring, passing along
the second ring (and others as found) picking up information until they return
to the original NUB and re-enter the first ring. This allows answers to
questions such as "Who is the mother of Arnold?" as well as "Who is the son of
Mary?" One of the major disadvantages of these structures occurs on adding a
new, not previously anticipated, association. The operation either is
impossible, requiring a complete recompilation, or else clumsy, patching on
additional blocks (Figure 4) and requiring sophisticated garbage collectors. A
recent survey by Gray describes these and similar structures.

Probably the first conception of a true relational data structure was
Kochen's AMNIPS . He dealt with the problem of logical inference rather than
with data structures, but he recognized that conventional memories were
inadequate, and turned to the relational structure, which unfortunately was
never fully realized.

A considerable amount of work has been done on various "question-answering
machines." Among the better early machines were Lindsay's SAD SAM which can
digest English statements about family relationships and construct a family
tree, and the BASEBALL program of Green, et al. which answers English queries
about facts taken from a stylized baseball "yearbook." These investigations use
conventional data structures, and their real contributions were to the analysis
of the English language and logical inference by computer. Simmons provides the
most complete survey to date of this work.

Maron and Levien have designed one of the most extensive and complete systems
depending on a relational data file. They deal with binary relations as their
building blocks, i.e., each association has three components: one relation and
two operands. In addition, they allow the naming of the entire association
(triple) giving rise to a fourth component. Reference can be made using any of
the four components, and there are four copies of each association-one copy for
each word which can reference it.

Feldman has recently used the ideas of hash coding for association tables.
The associations are, of course, carried by a new table, referenced by a "double
hash" technique. Feldman's language, AL , is designed to be compiled rather than
interpreted. AL has been expanded to be three languages in one: a true
ALGOL-type algebraic language with full numerical computation facilities; an
associative processor; and a language which operates on sets as its basic
entities with a full complement of set operations. The language now allows for
certain kinds of restricted composition of associations. Specifically, if a
sentence is a triple <A,0,V>, then 0 or V may itself be another triple.
This allows for generating N-place relations out of the basic binary relations.
However, the language has, instead of dynamic inference, a DO loop which is
slightly more cumbersome, and far less economical of storage, than are strict
logical inference capabilities.

Naturally, the simplicity in using data systems depends on the retrieval
language. We have already suggested that the problem is partially a function of
explicit versus implicit storage. If the family relationships are taken in the
binary male lineage tree of four generations (Figure 5), we have: 16
great-grandfather-son; 24 grandfather-son; 28 father-son; 14 brother; 24
uncle-nephew; 48 cousins, etc. Obviously, a large number of relationships must
be explicitly stored for rapid access, compared with the cost of an implied
relationship search with small storage requirement.


The "relational" part of the TRAMP system is the means for retrieving implied
relationships, while the "associative" part deals with the explicit
relations.


Extract: TRAMP

III. TRAMP


TRAMP (Timeshared Relational Associative Memory Program) is two packages of
functions: the first-the data structure-may be used to enter, retrieve, and
generally manipulate an associative data structure; the second-the relational
memory-places an artificial structure on the "associative triples," viz., the
relational structure. The relational package allows logical inference to be
performed on the data within the associative structure. Specifically, rules may
be entered; these will be followed by TRAMP, effectively expanding a "minimal"
set of data to a workably large set; the number of associations that must be
explicitly stored is thereby drastically reduced. For example: by defining the
relation "HUSBAND OF" to be the converse of "WIFE OF," the user need only store
marital relations in one direction, while effectively having them available in
both directions. More detailed examples and the rules for using the relational
package appear later.


These machine-coded functional packages are presently embedded in the UMIST
interpreter on the IBM/360 model 67. Although this existing union has proved
most fruitful, the data structure is totally independent of the interpreter and
actually relies on it only for I/O. The relational package is also independent,
except that it relies on the type of recursion that the interpreter provides.
The relational package is totally


The Associative Data Structure


Feldman's initial work was a strong motivation in the design of this system,
and led us to adopt his notation, viz., the generic entity:
A (0) =
V
<Attribute> of <Object> equals <Value>
Thus the
Associative Triple is: lt;A,O,V>. Each of the three components is a non-empty
set. To the data structure this is an ordered triple, but no interpretation or
meaning is attached to the ordering, and all three are treated equally, giving
none a priority.


By appropriately designating the three components as being constant or
variable, we can ask eight "questions" of the data structure. Again using
Feldman's notation, with a slight re-ordering, they are:

TRAMP(ID:5341/tra016)

Associative memory language 


for Timeshared Relational Associative Memory Program

relational memory with an associative base

Places
Related languages
LEAP => TRAMP   Influence
UMIST => TRAMP   Extension of
TRAMP => Buckle data structure language   Incorporated some features of
TRAMP => FORAL   Influence
TRAMP => Graphical TRAMP   Extension of

References:
  • Sibley, E H. The Computer as Symbol Manipulator and Evaluator for Engineering, A Ph.D. Dissertation at the Massachusetts Institute of Technology, Cambridge, 1967. view details
  • Ash, William L. and Sibley, Edgar H. "TRAMP: a relational memory with an associative base" Technical Report No 5 Concomp Project The University of Michigan Ann Arbor June 1968 view details Extract: INTRODUCTION
    I. INTRODUCTION

    In recent years, the need for a content-addressable computer memory has become increasingly clear. Larger and larger programs are being written which require a structured data base to operate with any efficiency. Many of these could well benefit by replacing tedious searches with a fast, efficient, "content addressable" access of the data store. A good example is the "key-word" library search. If we ask for a list of the books written by J. von Neumann, we do not expect the system to look at each title in its store and save only those written by von Neumann. And, if there happens to be a catalog prepared, designed to answer this particular question, we do not want to have to do a binary search to find the correct section of the catalog-we want to retrieve the answer directly!

    There are many other problems which might find content- addressability advantageous. Examples abound in Artificial Intelligence, where prohibitively large tree searches are encountered; question answering machines; logical inference systems; graphic systems; and most conversational (timeshared) systems, which require immediate, direct access to a large data store to interact effectively. To date, most investigations into content-addressable memories have been concerned with hardware; such memories have not yet proved to be economically feasible. Even if they had, it is not clear that the obvious gain in speed would compensate for the loss in generality and flexibility. For the moment, it can be said that software simulations are a stopgap measure. They are. But it is not certain that they will be completely replaced by hardware in even the relatively distant future.

    Another function of an artificial language is to permit the programmer to phrase his problem in a natural manner. For many problems information is most naturally described as "relational triples"; e.g., in a graphics system, one might want to say: is ; or is . The associative processor approach to content addressability allows this.

    Before proceeding, we shall explain some potentially ambiguous terms.

    The essential feature of an associative processor is that it has, in the conventional sense, no explicit addresses. Reference to storage is made by specifying all or any part of an associative cell, and all cells which match this field(s) are referenced. The conventional computer store may be thought of as a special (degenerate) case of an associative memory, in that the association is between the physical address and its contents. However, reference can be made only by specifying the address-one cannot ask directly for all cells which are zero! The true associative memory is accessed by specifying any of the N participants in the association. Associative memories are often referred to as relational data structures. This is because an association between N + 1 "objects" is most easily thought of, talked about, and manipulated by calling it an N-place relation.

    The following example demonstrates why an associative processor can effectively be employed as an application of content addressability. Suppose we wish to know the phone number of Clark Kent. It is simple to look it up in the local phone book. It is, however, quite a different matter to find out whose number is 764-6148 (using the same directory). An associative processor would find both tasks equal. In this example, the "association" is between a subscriber's name and his phone number. In translating this to a two-place relation, "phone number of" could be the relation, and using the format we would say: is . This is a type of associativity wherein we may now directly reference this triple by any of its content-addressable components or combination thereof. If we use only the first component, phone number, in a search, what will be referenced is the entire book. If we specify two components: phone number and 764-6148, then we are referencing directly all associations containing those two components, viz., the associations containing the name(s) of the person(s) having the phone number 764-6148.

    We are, of course, working with a conventional computer memory. The general strategy used to effect the simulation of an associative processor and an approximation to content addressability was that of hash-coding.

    For those unfamiliar with the term, hash-coding is simply a technique whereby an arithmetic transformation is applied to an external name to generate an internal address. Hash-coding by itself provides a restricted but significant approximation to content addressability, but hashing alone does not provide any kind of associative, and there is always the problem of the "collision," i.e., when two distinct names hash to the same internal address:X &neq; Y ; H(X) = H(Y) . Hashing partitions the space of names into equivalence classes. Hopefully, each class has only one element, but two or more names may be equivalent under this partition.

    By providing an interpretive language with an associative data structure it is possible to achieve great flexibility. To this end, we decided to use an existing interpreter and give it a new data structure, rather than start at the bottom by designing a special purpose interpreter. Principally, we were concerned with the data structure, and the vehicle for it was initially felt to be unimportant, since the data structure relies on the host only superficially. In considering the question of the interpreter, we were faced with very little choice. A major consideration was that of availability; fortunately this consideration led us to TRAC T-64 Language. It has proven to be a most elegant host, and credit for the power of the resulting system must be shared by both the interpreter and the associative memory given to it. However, we feel that the additional primitives are excellent vehicles which change the original processor into an efficient language for writing man-machine and machine-machine communication systems. Familiarity with the TRAC language may be helpful in reading this article, but it is not prerequisite. A brief summary of the basic components of the language is found in Appendix A.

    Extract: BACKGROUND

    II. BACKGROUND


    An associative processor is one possible tool for information storage and
    retrieval, and its history should be discussed relative to such systems.
    Unfortunately, adequate comparisons of different types of data systems are
    difficult to make because they are predicated on different rules. Thus the prime
    method of storage may vary from cards or paper tape, through magnetic discs and
    drums, to prime computer memory; at the same time, the storage may be either
    random or ordered according to some schema; finally, the retrieval of the
    information may be gross, such as the use of a mechanical sort based on some
    algorithm, or simple because the data were stored for such answers or because
    there is a well-developed language to address the stored data.


     

    Thus we have three possible criteria for comparison of systems: the type of
    storage, the way of entering data into that storage, and the language for
    addressing that storage. The spectrum of potential systems is therefore large
    and varied. We will consider only those which use main computer memory as the
    storage device (including virtual or paged memory).


    One inherent bias of computer design affects the storage of data: the use of
    sequential storage, where the data are placed in numbered or ordered cells.
    Because this organization system allows the automatic indexing of information by
    means of some automatically varied register, the preferred method of storage is
    tabular. Fortunately, tables are excellent ways of storing information, for the
    parallel entries are ways of expressing associations between objects (see Figure
    1).



      
      
        
        
        
        
        
      
        
        
        
        
        
      
        
        
        
        
        
      
        
        
        
        
        
      
        
        
        
        
        
    ITEM#NAMEFATHER OFBROTHER OFMOTHER OF
    1JOHNEDITH; ARNOLDSAM;JOAN-
    2ARNOLDJAMESEDITH-
    3MARY--ARNOLD
    4EDITH- -MELISSA

  • Figure 1.
    Association by Tables.

      
      
        
        
      
        
        
      
        
        
      
        
        
      
        
        
      
        
        
      
        
        
      
        
        
    F0A (O) = V
    F1A (O) = x
    F2A (x) = V
    F3A (x) = Y
    F4x (O) = V
    F5x (O) = Y
    F6x (y) = V
    F7x (Y) = Z


    where [ A,O,V ] represent constants, and [ x,y,z ] are variables. Question F7
    is not a question at all but a request for a dump of the associative memory, and
    in TRAMP such a dump is given. Question FO simply asks: "Does A (O) = V?" and
    the answer is a kind of truth value. In the case where A, O, and V are all
    singletons, the truth value is a straightforward 1 or O denoting whether or not
    the specified association can be verified by the data. The interpretation is
    slightly ambiguous, however, when one or more of the three sets has cardinality
    greater than one. To illustrate, assuming that the association
    COLOR (CAR) =
    RED; GREEN


    has been stored, these five questions have the following truth values:


      
      
        
        
        
      
        
        
        
      
        
        
        
      
        
        
        
      
        
        
        
    1.COLOR (CAR) = BLUEO
    2.COLOR (CAR) = RED; GREEN1
    3.COLOR (CAR) = RED; BLUE?
    4.COLOR (CAR) = RED1
    5.COLOR (CAR) = RED;GREEN; BLUE?
    Questions 1 and 2 are clearly false and true
    respectively, but questions 3 and 5 are each partially true and partially false;
    question 4 is only half true. The interpretation which seemed most natural, and
    the one adopted by TRAMP, gives the truth values as shown, namely:
    if ALL
    associations implied by the question are resident in memory, or derivable
    therefrom, the value is "1"
    if none, the value is "0"
    if some, but not
    all, the value returned is "?"


    Questions F1-F6 simply ask the system to "fill in the blank(s)," i.e., to
    replace the variable with the set that is the answer to the question. For
    example, Question F1 asks for the set of all Vs that A (0) equals. Question F3
    asks for the sets of all 0s and Vs that have a first component "A." Because of
    the recursive nature of TRAC, questions F1-F6 may be nested in any way, to any
    desired depth. One may ask: "How many fingers on a hand?"; "What figures are
    pointed to by the arrows in Window Q?"; "How old are the fathers of the wives of
    Mary's brothers?"; or any questions composed in any way compatible with the
    stored data, nested to any level.


    For those totally unfamiliar with TRAC language, for this section it is
    necessary to know only the syntax of a function call. The sharp sign (#) signals
    the start of a function call, with the call itself enclosed in an immediately
    following pair of parentheses. The arguments are separated by commas, and the
    first argument is the name of the function.
    #(sub,ARG)
    is therefore
    analagous to the FORTRAN
    CALL SUB(ARG)


    Data Structure Storage


    The name of the storage function is dr and the syntax of the call is:
    #(dr,A,0,V) . Again, the three arguments to dr are each non-empty sets. Each
    point in the cartesian product of the three sets is stored, i.e., each element
    of each set is grouped with each pair of elements of the other two sets, and the
    resulting triple is stored. Thus a single call on dr stores as many associations
    as the product of the cardinalities of the three sets. The storage
    declaration:
    M(dr,AGE,JOHN;MARY,64)


    would therefore store:
    AGE (JOHN) = 64
    AGE (MARY) = 64
    The actual
    storage is accomplished by pairing each A and 0 to point to a list of Vs ; each
    A and V point to a list of Os , etc. These answer' lists are, strictly speaking,
    un-ordered, except that they retain the order in which they were stored. That
    is, asking the question:
    "Whose age is 64?"
    would yield the
    answer:
    JOHN; MARY not MARY; JOHN


    It should be noted that this is a pure data structure, and it does not deal
    with semantics; dr simply inserts associations into memory in a way that they
    can be quickly retrieved. TRAMP is not a question-answering system that checks
    for redundancies or inconsistencies of data.



    Data Retrieval


    The primary retrieval function has the name rl The syntax of the function
    call is identical to that of dr except for variable specification. A variable in
    TRAMP is denoted by enclosing a name, possibly null, within asterisks (*) .
    Thus, #(rl,A,O,V) has no variables and asks whether A (O) = V ; #(rl,A,O,*X*)
    asks: "What does A (O) equal?" If the variable is Named, i.e., there is a name
    within the asterisks, then the function is Null Valued and the answer is stored
    in TRAC language form storage labeled by the Name. #(rl,A,O,*ANS*) would store
    the set of Vs which A (O) equals, under the label "ANS." If the variable is not
    Named, #(rl,A,O,**) , then the answer is the Value of the function.
    #(rl,A,*SETO*,**) is an example of a two-variable question with one Named and
    one Unnamed variable. The result in this case would be that the set of Os is
    placed in form storage under the label "SETO" while the set of Vs is returned as
    the Value of the function.


    The two-variable questions (F3, F5, F6) simply use the Name table of one of
    the variables and index through that table, internally always asking the
    one-variable questions. Since the data structure does not assign any priority to
    the three components, questions F3, F5, and F6, although considerably slower
    that the one-variable questions, are all equal among themselves. The process of
    answering a two-variable question is less efficient because it must iterate on
    the one-variable questions, the number of iterations being a linear function of
    the size of memory.

    The speed with which the one-variable questions are answered is not
    significantly affected by the size of memory! The three-variable question,
    #(rl,**,**,**), is, of course, the slowest of all and it is a full dump of the
    associative memory. Alternatively, one can call: #(dump).


    Going back to the earlier example of JOHN and MARY, the question:
    #(rl,AGE,JOHN;MARY,**) should have as its value: 64;64. That is, redundancies
    can be valid and should be reported. But there are certain times, particularly
    in the two-variable questions, when redundancies become quite a nuisance (and
    even threaten to overflow the interpreter). Therefore, the function rl will
    always return an answer set with all redundancies deleted. A second entry point
    is provided, with the name rlr, which is identical except that it does not check
    for redundancies but returns the answer set as it finds it.


    rl generates the union of the answer sets. That is, the question:
    #(rl,AGE,JOHN;MARY,**) has two answer sets: the AGE of JOHN and the AGE of MARY.
    rl simply forms the union of however many sets there might be. int is the
    function (yet another entry point to the same routine) which generates the
    intersection of the several answer sets. Thus, #(int,SOUTH;WEST,AUGUSTA,**)
    generates the set of all things both south and west of Augusta. #(rl,SOUTH;WEST,
    AUGUSTA,**), on the other hand, would generate the set of all things either
    south or west of Augusta.


    Data Structure-General Strategy


    As stated in the introduction, hash-coding is the technique most basic to the
    data structure design. A brief description of the use of hash-coding in TRAMP
    follows. Section V gives a more technical and detailed description.


    The data structure uses three Name tables and three Association tables, one
    each for each of the three components of the association. When the declaration:
    #(dr,WIFE,JOHN,MARY) is made, each name that appears must be stored somewhere in
    memory. The full name must be present so that it can be retrieved, and so that,
    when it is referenced, a collision can be identified and resolved. The first
    hash, H1, then is applied to the "A" component, "WIFE," to generate a
    displacement from the A Name Table. The designated table entry is then
    inspected. If the entry is zero, then there is no collision and WIFE has never
    appeared before as an "A" component. Accordingly, the table entry is now made to
    point to the Header for the name WIFE, see Figure 6. If the table entry is not
    zero, the Header to which it points is inspected to see if it is the Header for
    WIFE. If so, the A name has been processed and we move on to the 0 component,
    otherwise there is a collision. For a collision, instead of a single Header,
    there is an alphabetical list of Headers.

    Thus, Name Table collisions are not really special cases: if there is no
    collision, then there is a list consisting of a single Header, otherwise the
    list contains two or more Headers in alphabetical order.


    If the above process did not find the name, before it is actually placed in
    storage, a further check is made on the other Name Tables, thus avoiding
    redundant storage. Any name will appear at most once in memory, with up to three
    Headers pointing to it.

    The same procedure is applied to "JOHN" and "MARY," the O and V of this
    example, on their respective Name Tables. As a result of the Name Table
    processing, a unique pointer is associated with each of A, O , and V , namely
    the pointer in the Header which points to the location of the actual name. It is
    this unique pointer that will be used for the second hash, H2. "WIFE"
    must now be placed on the A Association Table. To do this, the O and V pointers
    are hashed together to generate a displacement from the Association Table. To be
    able to identify collisions, both pointers that were used to generate the hash
    are stored in the table entry designated by the hash. Collisions are again
    resolved by ordered lists. The Association Table entry has three pointers: the
    first two are the pointers used to generate the displacement; the third points
    to the Answer List, i.e., the list of A's (in this case) with which O and V have
    been associated. Thus, "WIFE" is appended to the Answer List by placing the
    unique pointer to it at the end of the list. Note that H1 is a
    function of the actual name, while H2 is only a function of where the
    name is stored and is independent of the name itself. Figure 7 shows the
    Association Tables, both for the collision case [ 7b ] , and for the normal case
    [ 7a ] .


    Extract: LOGICAL INFERENCE PACKAGE

    IV. LOGICAL INFERENCE PACKAGE


    The associative memory accomplishes a kind of content addressability by using
    two quick hashes to address data, and the access time is essentially independent
    of the size of storage. But for most, if not all, applications, many
    associations will be implied by a single associative sentence. This poses two
    real problems:


    To alleviate this, TRAMP provides the facility to define, in a characteristic
    way, what other associations may be derived from a given association. This
    permits all of the information that might be contained in a single association
    or sequence of associations to be utilized instead of having to enter the same
    information redundantly in each of the several ways that it might be referenced.
    The name of the function which makes the definition is ddr. The syntax of the
    function call is: #(ddr,(R = EXP)) , where R is the relation ("A"component) to
    be defined, and "EXP" is a logical expression which is the definition.


    Before presenting examples of the use of ddr, two relational operators must
    be defined:
    The first is converse, denoted in TRAMP by ".CON." Converse
    simply inverts the order of the two relational arguments:
    R(x,y)
    ↔ .CON. R(y,x)
    Thus "CHILD OF" is the converse of
    "PARENT OF"; "WIFE OF" is the converse of "HUSBAND OF"; "SPOUSE" is its own
    converse; any symmetric relation is its own converse. (The relational notation
    used by TRAMP is derived from the format "R,x,y" by enclosing the relational
    arguments in parentheses. This is a slight distortion of the associative
    notation: A(0) = V , but the order is preserved: R(x,y) means that it(x) = y. )

    Relative Product: The relative product or composition of two relations is
    commonly denoted by R1/R2, and this is the notation used
    by TRAMP.
    ∀x ∀y &exists;z [ (R1/R2) (x,y)
    ↔ R1(x,z) Λ R2(z,y) ]
    Less
    rigorously, but more specifically,
    #(ddr,(R3 =
    R1/R2))
    would tell TRAMP that R3(x,y) if a
    "z"can be found such that Rl(x,z) and R2(z,y).
    Besides
    these two relational operators, three logical operators are available: .A.
    (conjunction); .V. (disjunction); .N. (negation). Finally, there are six
    equality operators: .EQ.; .NE.; .GE.; .LE.; .GT.; .LT. , with obvious meanings.

    Examples of TRAMP relational definitions are:


      
    #(ddr,(BIGGER = BIGGER / BIGGER))
      
    Bigger is transitive
      
    #(ddr,(BIGGER(A,B) = BIGGER(A,Q) .A. BIGGER(Q,B)))
      
    exact same definition using expanded format-specifying dummy arguments.
      
    #(ddr, (SIB = BRO .V. SIS .V. .CON.SIB))
      
    a sibling is a brother or a sister and it is symmetric.
      
    #(ddr,(HUSBAND = .CON.WIFE))
      
    Husband is the converse of Wife.
      
    #(ddr, (BIGGER = LARGER)
      
    Bigger and Larger are synonomous.
      
    #(ddr,(BRO(CAIN,ABEL) = SIB(CAIN,ABEL).A. SEX(ABEL,"MALE")))
      
    a brother is a male sibling. Note that constants are denoted by enclosing
      them within double quotes.
      
    #(ddr,(MALE(X) = SEX(X,"MALE")))
      
    defined the unary relation MALE
      
    #(ddr,(BRO(X,Y) = FATHER(X,Z) .A. FATHER(Y,Z) .A. MALE(Y) .A. X.NE.Y))
      
    a brother is a male offspring of the same father, other than oneself.
      
    #(ddr, (STEPMOTHER = FATHER / SPOUSE .A. .N.MOTHER))
      
    a stepmother is the spouse of the father who is not the mother.
      
    #(ddr, (NEPHEW = SIBLING / SON))
      
    a nephew is the composition of sibling and son.
      
    #(ddr, (UNCLE = .CON.(SIBLING/SON)))
      
    in a male world, uncle is the converse of nephew and may be defined as the
      converse of the definition of nephew.
      
    #(ddr,(UNCLE = .CON.NEPHEW))
      
    or simply as the converse of nephew.

    More complex examples will arise, and TRAMP is prepared to handle definitions
    of the above form to a level of complexity virtually unlimited. One major
    constraint is placed on the definitions: relations must be defined so that at
    least one set is generated. This generated set can then be intersected with or
    joined with another set, or otherwise manipulated. The intent of this constraint
    is that there be at least one reference set. The "whole space" may never be used
    as a reference set.


      
    #(ddr,(R3 = .N.R1 .V. R2))
      
    is illegal since it specifies a global complement (of R1),
      i.e., it references the "whole space."
      
    #(ddr,(R3 = .N.R1.A. R2))
      
    is legal because it specifies a relative rather than a global complement,
      i.e., R1 places a constraint on R2 not on the whole
      space.

    Extract: TRAMP INTERNAL ORGANIZATION
    TRAMP INTERNAL ORGANIZATION

    In effect, TRAMP employs a triple storage technique to be able to reference
    an association in three different ways. Thus, the association A (O) = V is
    stored on each of the A , O , and V Association Tables. This makes the answers
    to questions F1, F2, and F4 equally accessible and optimizes retrieval time.

    TRAMP uses eight principal blocks of core. Though it is designed to run
    under a timesharing supervisor which continually swaps TRAMP on and off a drum,
    TRAMP itself makes no explicit use of drums, discs, or other secondary storage
    devices; that is, such use by the system is transparent to TRAMP as well as to
    the user. The blocks of virtual core are: four Name Tables [ A, 0, V, and a Name
    Table for Defined Relations ] ; three Association Tables; and a General Storage
    list [ GS ] (commonly called "Available Space" in many list processors). GS
    provides all the working space for TRAMP and is by far the largest of the eight.
    In addition to storing all of the information indexed by the seven tables, GS
    resolves any overflow (via collisions) from the tables.

    For purposes of illustration, let us follow the interpretation
    of:
    #(dr,HUSBAND,EVE,ADAM)
    First the Name Tables are processed. "HUSBAND"
    is hashed to produce a displacement from the "A" Name Table. The actual hashing
    scheme for H1 is to form a full word (4 bytes) by concatenating the
    first, last, and middle two characters of the name, in that order. A single
    character may play only one of those roles, i.e., a name consisting of one
    character has no last or middle characters. Any missing components are filled
    with hexadecimal zeroes. Thus HUSBAND yields "HDBA"; EVE yields "EEVo" and ADAM
    yields "AMDA." The full word so generated is then transformed, with the
    transformation being little more than squaring and masking.

    A list is generated in GS to hold the EBCDIC representation of the name: 6
    characters (bytes) per double word and a 2-byte pointer to the next unit in the
    list. All units in GS are double words (64 bits). Each name list is terminated
    with a stop meta character. All lists in TRAMP are terminated with a stop
    pointer, though the stop pointer is superfluous in the name lists because of the
    stop meta. In the case of HUSBAND, two double word units will be needed: the
    first will hold the 6 characters H-U-S-B-A-N, and a two-byte pointer to the next
    unit which will hold the character "D," the stop meta charcter, and the stop
    pointer, with four bytes left over. Since HUSBAND is used here as an
    "attribute," we are concerned with the A Name Table. We look at the entry in
    this table designated by the hash. If this entry is empty, i.e., zero, there is
    no collision, and HUSBAND has not been used previously as an attribute. If not
    empty, we look at the list of Headers pointed to by the entry. This list is
    alphabetical, so we need look only until we find HUSBAND or its proper
    alphabetical position on the list. Proceeding down this list of Headers, we
    compare the list generated above with the sublists pointed to by the headers. If
    a match is found, simply return this temporary list to GS and increment the USE
    count for HUSBAND in its header. If it is not found, insert it ... after
    checking the 0 and V tables for its occurrence. Previously HUSBAND may have been
    used as, say, an "object": #(dr,SEX,HUSBAND,MALE). In this case the HUSBAND name
    list would already be resident in GS. We therefore return the copy of it
    generated above, and insert a pointer to the first list on the A table header
    list. Thus a name never appears in core more than once, though many pointers may
    point to it, including up to four headers if a name appears on all four Name
    Tables.

    The above process is done for each HUSBAND, EVE, and ADAM. The final pointer
    to the one name sublist of each is saved to generate the Association Table
    hash-H2. Let us follow the processing of the O Association Table.
    HUSBAND and ADAM (A and V) are hashed together (multiplied and masked) to
    produce a displacement in the Association Table. The actual hash is performed on
    the two unique pointers found during Name Table processing. The designated
    Association Table entry is examined. If zero, there is no collision, and HUSBAND
    and ADAM have never appeared together with another value. The unique pointer to
    HUSBAND is placed in the first 2 bytes of the 6-byte entry. The pointer to ADAM
    is inserted in the middle 2 bytes. A double word unit is picked off GS to be the
    start of the "Answer List," and the pointer to this Answer List is placed in the
    last 2 bytes of the table entry. The Answer List elements consist of three
    2-byte pointers to name sublists, the answers, and a 2-byte pointer to the next
    list element. Accordingly, the pointer to EVE is inserted in the Answer List.

    If the table entry was not zero, compare the first 4 bytes of it with the two
    pointers that would go there. If a match is made, then just add EVE to the end
    of the already started Answer List (polygamy is fine here), starting a new unit
    if necessary. This is a simple unordered list, with new elements always being
    added to the right-hand end. If the first four bytes of the table entry do not
    match the pointers, is there a collision flag in the entry? If not, a collision
    list is begun. This is an ordered list, with the ordering being the numerical
    value of the full word obtained by concatenation of the A and V pointers (the
    first 4 bytes of the table entry). Each element of the list is a double word as
    always. The first 6 bytes of this double word are identical to the 6-byte table
    entry. The last 2 bytes point to the next element on the list. When a collision
    occurs, the first 4 bytes of the table entry are so flagged and the last two
    bytes point to the list which resolves it.

    The entries of all the tables, as well as all list pointers within GS , point
    to double word units in GS . All pointers are 2 bytes long (16 bits), but are
    capable of addressing 128 pages of GS. (1 page = 4096 bytes; 128 pages =
    219 bytes.) For some applications this size is more than adequate,
    and for others (e.g., artificial intelligence) not nearly enough. With its
    present scheme (addressing 219 bytes with only 16 bits) TRAMP has an
    upper limit of 128 pages, which is a usable size for the majority of cases,
    including many AI applications. There is obviously a trade-off here since the
    more core that a pointer can address, the less percentage (though not
    proportionately less) of that core is available! There is a second trade-off
    because the size of the units which must be addressed determines the number of
    bits needed to address them-the larger the unit, the fewer bits required, but
    generally, the less efficiently it is used. We arbitrarily decided that the
    half-word pointers that TRAMP uses to address double words are, in a sense,
    optimal. Should more experience prove us wrong, or if some special application
    should require much greater capacity, the structure could be augmented, e.g., to
    incorporate full 32-bit addresses, with little more trouble than alteration of
    an assembly parameter. At this time it is not anticipated that explicit use will
    be made of any peripheral storage devices, other than the transparent swapping
    performed by the timesharing supervisor.

    The sizes of the various Name and Association tables are another assembly
    parameter. Currently the 7 tables occupy 4 pages of core. This figure was
    arrived at arbitrarily and will remain in force pending feedback which indicates
    that it is inappropriate.


    TRAMP is initially loaded into core with all of its tables, a one-page PSECT
    and 8 pages of GS . Thereafter, when more space is needed (GS is the only unit
    that will require more space, since overflow from the tables is placed in GS),
    TRAMP requests it of the system in blocks of 8 pages until the maximum of 128 is
    reached, or the system is unable to comply with the request.


    TRAMP is continually generating temporary lists which are immediately
    returned to GS when no longer needed. As well, when an association is destroyed,
    or a relational definition erased (KR and KDR , Appendix B), as much storage as
    is being released is at that time returned to GS. Thus, unused units are never
    left lying about core: a unit is returned, not discarded, eliminating formal
    garbage collections by ensuring that garbage is never created.


  • Ash, William L. and Sibley, Edgar H. "Tramp: An interpretive associative processor with deductive capabilities" view details Abstract: In recent years; it has become increasingly clear that there is need for a content-addressable computer memory. Larger and larger programs are being written which require a structured data base to operate with any efficiency. Many of these could well benefit by replacing tedious searches with a fast, efficient, "content-addressable" access of the data store. A good example is the "key-word" library search. If one asks for a list of the books written by J. von Neumann, we do not expect the system to look at each title in its store and save only those written by von Neumann. And, if there happens to be a catalog prepared, designed to answer this particular question, we do not want to have to do a binary search to find the correct section of the catalog—we want to retrieve the answer directly!

          in Proceedings of the 23rd ACM national conference January 1968 view details
  • Ash, William L. "A Compiler For An Associative Object Machine"University of Michigan, Ann Arbor, MI 1969 view details Abstract: This paper discusses the design and construction of a compiler whose source language consists of sentences of a restricted predicate calculus, and whose output object code operates on a simulated associative target machine. The source programs are characterizations of relations, used for deriving one relation from others, and for completing relations. A program realizes the completion of a relation when it defines that relation in terms of itself, i.e., when the definition is recursive.
    The most significant feature of such a compiler is that there are two levels of operators and operands. The first level operators are the logical connectives whose operands are relations, which in turn have as their "operands" relational arguments. The lower level operands deter- mine the context-dependent interpretation of the higher level operators. The parse is further encumbered by the fact that the logical connectives in such a framework do not lend themselves to a production grammar. The situation is resolved by constructing a digraph to represent the definition of the relation. This construction proceeds using a back-up technique. The output of the compiler is an object program in the form of a macro definition, to be expanded for an interpretive associative target machine.

    Extract: INTRODUCTION

    1. INTRODUCTION

    An associative computer memory can effectively be employed to approximate "content addressability." By an associative memory we mean a memory that stores information in ordered n-tuples, called associations, which can be referenced by specifying any of the components of the association.  We refer to an association by its contents (components), rather than by any address; indeed, it is the lack of explicit addresses that characterizes an associative machine.  The term content-addressable becomes clearer when we see that we reference an association by its contents.  More precisely, by a content-addressable memory, we essentially mean one in which the name of a datum contains a dynamic cue to the relevant information about that datum. Content addressability obviates table lookups, binary search, etc. An associative processor provides a useful approximation to content addressability.

    The associative processor being used in the work reported herein is TRAMP1,2, a software simulation of an associative machine.  TRAMP associations are 3-tuples3

    <A, O, V>
    <Attribute> of <Object> equals <Value>: A (O) = V
    This associative processor provides a semblance of content addressability and can be used to store and retrieve efficiently large amounts of data.  However, any association, or combination of associations, will in general imply many more associations.  For example:

    [ Husband (Mary) = John ] ⟹ [ Wife (John) = Mary ]
    Father (Norm) = Harry
    Brother (Harry) = Sam
    ⟹ Uncle (Norm) = Sam
    Nephew (Sam) = Norm
    Brother (Sam) = Harry
    Son (Harry) = Norm

    This situation can be resolved by requiring that the user redundantly enter his information in all the various ways that he might want to access it; or, more realistically, the user can define a relation, whereby he specifies what inference rules may be used in deriving the implied associations.  Now the important distinction, once we have decided to admit relations, is whether their definitions will be extensional or intensional.  We can extensionally define a relation by going through the data structure and generating and storing all implied associations. This might be done with an iteration loop:
    FOR HUSBAND (A) = B, LET WIFE (B) = A
    That is, all the ordered pairs which comprise the (binary) relation are generated and stored.  This is the approach taken by many associative processors.  The result is that all the implied information can in fact be made available, but there are two serious drawbacks:
    1) In general, an extensional definition will gobble up extensive amounts or core, rendering it operationally uneconomical, or, in the extreme, infeasible.
    2) Unless these iteration loops are entered frequently and regularly, their time-dependency renders the extension incomplete, and/or inaccurate.

    The alternative to an extensional definition is the intensional definition: where we characterize the relation, rather than exhaustively storing all of its ordered pairs.  As an example, if we were to characterize the relation "WIFE" [ of ] as:
    WIFE = Converse of HUSBAND
    and we now want to ask who is the WIFE of Harry, we could ask:
    WIFE (HARRY) = ?
    and the system, using the characterization given above, would expand the question to be:
    WIFE (HARRY) = ? or HUSBAND (?) = HARRY
    It would ask both questions since it doesn't know if the desired association appears implicitly, explicitly, neither, or both.

    This is the operational strategy of TRAMP intensional relational definitions. The user enters the definition as a sentence of a modified predicate calculus, e.g.,
    (WIFE = .CON.  HUSBAND)
    This is the source program for a compiler whose output is an object program written in the associative language.  This object program will then effect the "expansion" of questions to extract from the data those relevant associations that an be inferred from associations explicitly in core4 and the intensional definitions of correspondences.


    1.  Ash, W.L.  and Sibley, E.H., TRAMP: An Interpretive Associative Processor with Deductive Capabilities, ACM National Conference, Las Vegas, August 1968, pp.  143-156.

    2.  Ash, W.L.  and Sibley, E.H.  TRAMP: A Relational Memory with an Associative Base, Technical Report 5, Concomp Project, University of Michigan, Ann Arbor, June 1968.

    3. Feldman, J.A., Aspects of Associative Processing, Technical Note 1965-13, Lincoln Laboratory, Massachusetts Institute of Technology, Lexington, April 1965.

    4. The goal then is only to reduce the number of associative sentences necessary to represent information, rather than to provide sophisticated heuristic search procedures for a question-answering system.


    Extract: THE ASSOCIATIVE OBJECT LANGUAGE: TRAMP

    2. THE ASSOCIATIVE OBJECT LANGUAGE: TRAMP

    TRAMP is fully documented in (1,2), but a brief summary of it must be given here.  As stated in the introduction, TRAMP works with 3-tuples, or associative triples: A, O, V, which can be read as: A(O) = V.  By specifying an association with the various components of the triple being either constant or "variable," there are eight questions that can be asked:

    F(O) A (O) = V
    F1 A (O) = ?
    F2 A (?) = V
    F3 A (?) = ?
    F4 ? (O) = V
    F5 ? (O) = ?
    F6 ? (?) = V
    F7 ? (?) = ?
    where "?" represents a variable.  Question FO has no variables; it simply asks if A (O) = V, and expects a truth value. The other questions all have one or more variables, and the variable is expected to take on, as its value, the "answer set," i.e., the set which completes the association.

    TRAMP is currently embedded in the UMIST3.  UMIST is a dialect of the TRAC T-64 language4 locally implemented at the University of Michigan, on the IBM 360/67. interpreter.  Since the object programs of the compiler are TRAMP programs, they must obviously work within the syntax of UMIST, a macro-generator language. Procedures are defined by the user as UMIST macro definitions. When a "function" is called, if the function is a user-defined macro (procedure), then it is at that time expanded; if the function is a pre-defined primitive of the language, then that routine is invoked.  The pound sign (#) signals the start of a function call, with the call itself enclosed in an immediately following pair of parentheses. The arguments are delimited by commas, and the first argument is the name of the function (or macro). The arguments may themselves be function calls, with nesting depth effectively unlimited.  UMIST functions are evaluated recursively from the inside out.  All of the arguments to a function must themselves have been evaluated, with the value, possibly null, replacing the call, before the function can be called. A UMIST function call might look like:
    #(ds,X,Y)
    where ds is the name of the function, and X and Y are the arguments to ds.

    This paper assumes no knowledge of UMIST (TRAC), except for the syntax described above, and the recursive manner of replacing a function by its value, which will be shown more clearly in subsequent examples.

    The TRAMP system is an addition to UMIST of primitives which create and manipulate an associative structure.  The full facilities of TRAMP are described in (5); following are

    brief descriptions of the primitives which constitute a level O object language for the compiler.
    NAME:
    RL
    PROTOTYPE:
    #(RL,A,O,V)
    DESCRIPTION:
    This is the primary retrieval function in TRAMP, and as well as being used in compiler-generated programs, it is the function which when called by the user invokes the object programs of the compiler.

    Variables are specified by two asterisks (*). The answer set is the value of the function.  For example,
    #(RL,COLOR,CAR,**)
    asks "What color is the car?" and expects the answer to be the value of this function.  Two-variable questions generate two answer sets, rather than a set of ordered pairs.  There is one special case of the two-variable question which is significant to the compiler-produced code. This case is where one of the variables is denoted by '*@*', i.e., an at-sign between the two asterisks.  The'@' signifies that although that component of the triple is to be considered arbitrary, the corresponding answer set is not desired, and should not be generated.  The question #(RL,COLOR,*@*,**) would find ALL things which appear as the third component of associations in which "color" is the first component.

    There are several variations of this function and the compiler uses some of them.  The function named RL@ is the entry point which specifies that any programs previously put out by the compiler are NOT to be executed, i.e., only explicitly stored associations are to be retrieved.  This function is always used by the compiler.  The other variations that the compiler uses do not differ from each other in ways significant to the discussion in this paper and can be considered implementational details. In all examples, only these two forms will be shown, although in practice others are used.  There are certain other minor points which will differ in actuality from what is shown in the examples, but they are details whose explanations are not warranted.
    NAME:
    INT
    PROTOTYPE:
    #(INT,SET1,SET2)
    DESCRIPTION:
    This function forms the set intersection of its two arguments, and returns the intersection as its value.  TRAMP sets are unordered collections of things, separated by semicolons(;) (necessitated by the crucial role that the comma plays in UMIST) and possibly containing redundancies, i.e., the same element may appear several times in the same TRAMP "set."
    NAME:
    RCOM
    PROTOTYPE:
    #(RCOM,SET1,SET2)
    DESCRIPTION:
    This function similarly forms the relative complement of its two arguments. The second argument is logically subtracted from the first, with the result being the value of the function.
    NAME:
    @@
    PROTOTYPE:
    #(@@,STRING,NAME)
    DESCRIPTION:
    This function can be considered an internal function, since it was written specifically for the compiler, is not generally available to the user of TRAMP, and does not appear in the TRAMP reference manual.

    This is the "clean-up" function.  The programs generated by the compiler are always the first argument, STRING.  Because of the program structure, as we shall see, several "paths" specified by the definition may lead to the same point, causing redundancies; and some paths may lead to dead ends, resulting in dangling set element delimiters. The function @@ checks for these two conditions and corrects them if necessary.  The second argument, NAME, specifies the disposition of the result.  If the argument is given, then the cleaned-up set is stored, labeled by the NAME, and the function itself is null-valued.  If this argument is omitted, the cleaned-up set is the value of the function.


    1.  Ash, W.L.  and Sibley, E.H., TRAMP: An Interpretive Associative Processor with Deductive Capabilities, ACM National Conference, Las Vegas, August 1968, pp.  143-156.  

    2.  Ash, W.L. and Sibley, E.H.  TRAMP: A Relational Memory with an Associative Base, Technical Report 5, Concomp Project, University of Michigan, Ann Arbor, June 1968.

    3. Pinkerton, T.B., "UMIST Manual," The University of Michigan Terminal System Manual, 2nd edition, Vol.  II, The University of Michigan Computing Center, Ann Arbor, December 1967, pp. 717-764.

    4. Mooers, C.N., "TRAC, a Procedure-Describing Language for the Reactive Typewriter," Comm. ACM, 9, March 1966, pp. 215-219. (registered trademark of the Rockford Research Institute,  Cambridge, Mass.)

    5. Ash, W.L.  and Sibley, E.H. TRAMP: A Relational Memory with an Associative Base, Technical Report 5, Concomp Project, University of Michigan, Ann Arbor, June 1968.



          in Proceedings of the 23rd ACM national conference January 1968 view details
  • Barter, C.J. "Data Structure and Question Answering" view details
          in Kaneff, S. (ed) Picture Language Machines: Proceedings of a Conference held at the Australian National University, Canberra on 24-28 February, 1969 view details
  • Sibley, Edgar H. "The Use of a Graphic Language to Generate Graphic Procedures" view details
          in Faiman, M. and J. Nievergelt, eds. "Pertinent Concepts in Computer Graphics" University of Illinois Urbana, 1969 view details
  • Codd, E.F. "A database sublanguage founded on the relational calculus" pp35-68 view details
          in [ACM] Proceedings on the ACM SIGFIDET Workshop on Data Description, Access, and Control, San Diego, California (November 1971) view details
  • Leavenworth, Burt M.; Sammet, Jean E. "An overview of nonprocedural languages" pp1-12 view details Abstract: This paper attempts to describe some of the basic characteristics and issues involving the class of programming languages commonly referred to as ?nonprocedural? or ?very high level?. The paper discusses major issues such as terminology, relativeness, and arbitrary sequencing. Five features of nonprocedural languages are described, and a number of specific languages are discussed briefly. A short history of the subject is included.

          in Proceedings of the ACM SIGPLAN symposium on Very high level languages, March 28-29, 1974, Santa Monica, California, United States view details