Adding Common Sense to Applications using ThoughtTreasure's Java-based client API

by Erik T. Mueller

November 30, 1999


ThoughtTreasure is a treasure-house of pieces of common sense such as:
Soda is a drink.
People have fingernails.
The sky is blue.
Dogs bark.
Someone who is 16 years old is called a teenager.
Rough synonyms for food are foodstuffs, groceries, chow, and grub.
Cassette means cassette tape or cassette recorder.
Gold hair is called blond hair.
Though ThoughtTreasure's 100,000 items capture only a small portion of human commonsense knowledge (probably on the order of 100,000,000 items!), a small amount of common sense can be useful in applications.

Previously ThoughtTreasure had to be linked into the application, which was cumbersome. Starting with version 0.00021, ThoughtTreasure can be run as a server. Now any application written in any language can communicate with ThoughtTreasure over a TCP socket connection. Multiple application instances can use a single server, which can run on the same machine or any machine accessible over the network.

This article describes how to use ThoughtTreasure in a calendar manager application. We'll examine two programs written in Java using ThoughtTreasure's Java-based client API. The first program guesses how long an appointment will last, as a default initial value the user can override. The second program determines where an object is typically found, to provide a default location for an appointment or todo item.

To compile and run these programs, you'll first need to obtain ThoughtTreasure and the Java-based client API from Signiform's web site (www.signiform.com). To compile and run the server, you'll need a Unix box—Red Hat Linux 5.2 or later is recommended. The Java programs compile and run under JDK 1.1.6 or later.

Guessing the length of an appointment

Listing One (located at the end of this article) shows the first program, HowManyMinutesIs.java, which guesses the length of an appointment in minutes. The program takes a single command-line argument containing the natural language subject of the appointment as entered by the user. Here are some sample runs of the program:

$ java HowManyMinutesIs "shave"
1.0
$ java HowManyMinutesIs "breakfast"
30.0
$ java HowManyMinutesIs "dog training course"
60.0
$ java HowManyMinutesIs "tennis w/Susanna"
150.0
$ java HowManyMinutesIs "surf the web"
60.0
$ java HowManyMinutesIs "dinner with Alex and Chris"
120.0
$ java HowManyMinutesIs "zzzz"
unknown
$

The main function starts by opening a connection to the ThoughtTreasure server running on a machine called somehost and listening on port 1832. To start the ThoughtTreasure server, you first start ThoughtTreasure and then issue the server command:

$ ../bin/tt
ThoughtTreasure (gcc)
...
19990227T144310: reading ../db/elec.txt
19990227T144312: reading ../db/relation.txt
19990227T144313: Load time = 00:40.
* server -port 1832
19990227T144414: starting ThoughtTreasure server
19990227T144414: server started; port = 1832

The guessEventLength function first invokes the tag API function to map lexical entries to objects.

A lexical entry is a word or phrase such as surf or surf the web. An object represents a meaning of a lexical item. A lexical entry may have several meanings. For example, tag maps the surf the web lexical item to the surf-web meaning, and maps the ambiguous surf to both surf-web and surf-waves.

A meaning may be expressed several ways. The surf-web object can be expressed with surf and surf the web in English, as well as surfer in French. ThoughtTreasure also includes lexical entries for different dialects (American, British, Canadian, regional) and styles (literary, standard, informal, slang).

The guessEventLength function continues by looping over each of the meanings returned by tag, for lexical entries found anywhere in the summary description of the calendar event. It retrieves the typical durations of those meanings, and takes the average. (No attempt is made to disambiguate the lexical entries.)

Here is how a typical duration is found: The retrieve API function is invoked on the retrieval pattern:

duration-of surf-web ?
where surf-web is one of the meanings of a lexical entry found in the input text. The question mark ? is a wild card that matches anything. In this case, the wild card will match the duration value.

The fourth argument to retrieve is "anc", which specifies that the function should move up ThoughtTreasure's object hierarchy on the index 1, as specified by the second argument to retrieve. If the duration of surf-web is not found, the duration of its parent leisure-activity will be returned. If this is not found, the duration of script, the parent of leisure-activity, will be returned.

The first argument to retrieve is 2, which specifies that index 2 of the retrieved assertion should be returned, in this case the duration. -1 can be used here if the entire assertion should be returned.

The result of retrieve is something like:

[NUMBER 3600 secs]
represented as a Java Object made up of Vector, String, and Double. The getMinutes function is used to pick out the duration from this Object and return it as a double in minutes.

In taking the average of the various retrieved duration values, any duration greater than or equal to 720 minutes is thrown out since it probably doesn't apply to a calendar appointment.

The main function prints out the result (or "unknown" if nothing was found) and closes the connection to the ThoughtTreasure server.

Determining where an object is typically found

Listing Two (located at the end of this article) shows the second program, WhereDoIFind.java, which determines where a physical object is typically found. The program takes a single command-line argument containing natural language text describing a physical object. Here are some sample runs of the program:

$ java WhereDoIFind "Tropicana orange juice"
A small city food store.
$ java WhereDoIFind "lettuce"
A small city food store.
$ java WhereDoIFind "minibar"
A hotel room.
$ java WhereDoIFind "runway"
A John F. Kennedy international terminal.
$

The main function opens a connection to ThoughtTreasure and then invokes whereDoIFind, which invokes the tag API function to convert the input text into ThoughtTreasure objects. The whereDoIFindObj function is invoked on each object.

ThoughtTreasure contains a number of ASCII grids that represent stereotypical locations such as a hotel room:

==Park-Plaza-1E//
wwwwwwwwwwww    b:bed
wbbbbb    mw    d:lockable-door
wbbbbb     w    m:minibar
wx        Zw    w:wall
wwwwwwdddwww    x:phone
w               x:night-table
wwwwwwwwwwww    Z.wd:hotel-room

This grid can be taken literally as a particular room in the Park Plaza Hotel, or used to guess how any hotel room might be arranged.

The key on the right specifies which objects are associated with which characters: "w" represents a wall and "b" represents a bed. Contiguous characters in the grid represent single objects. So there is only one bed, which occupies all the cells containing a "b".

The hotel room is defined by filling a region of the grid. "Z.wd" specifies to fill all cells reachable from the "Z" character without going through a "w" (wall) or a "d" (door).

The whereDoIFindObj function starts by determining what grids contain the given object. The retrieve API function is invoked with the pattern:

at-grid minibar
where any descendants of minibar (types or instances of minibar) are allowed to match. Assertions such as the following are returned:
[at-grid minibar1934
         Park-Plaza-1E
         [GRIDSUBSPACE [NUMBER 1 u] [NUMBER 10 u]]]
So far we know that a particular minibar (minibar1934) is located in a particular grid (Park-Plaza-1E) in a particular cell location (row 1, column 10). The whatIsInGridAt function is invoked on that grid and cell location to determine what, if any, other objects in the grid are present in the cell location.

The whatIsInGridAt function starts by retrieving all objects in the given grid. Among them are lamp1924 and hotel-room1952. It invokes isContainedIn on each object.

The isContainedIn function first retrieves the location of a candidate enclosing object. For hotel-room1952 the following assertion is retrieved:

[at-grid hotel-room1952 Park-Plaza-1E [GRIDSUBSPACE ...]]
where GRIDSUBSPACE ... includes all of the cells that constitute the hotel room area. The function then tests whether any of these cells is the same as the cell of the minibar. If so, it returns true and whatIsInGridAt prints out the name of the enclosing object. It invokes the generate API function to convert the object name into English.

Other API functions

In this article I've shown how to use the tag, retrieve, generate, and close methods of the TTConnection class. The following other methods are provided:

For information on how to use these other methods, see the complete Java-based client API documentation available at Signiform's web site (www.signiform.com).

Summing up

As computing becomes more ubiquitous with the advent of the Internet, it becomes more and more important for computers to understand the human world. ThoughtTreasure presents a picture of this world to computer programs. Using the Java-based client API or the socket protocol, it is easy to make use of ThoughtTreasure's knowledge base in applications.


Listing One

/*
 * HowManyMinutesIs.java
 *
 * Uses ThoughtTreasure to guess the length of a calendar event
 * in minutes.
 *
 * Usage: java HowManyMinutesIs "summary of calendar event"
 * 
 */

import java.io.*;
import java.lang.*;
import java.util.*;
import com.signiform.tt.*;

public class HowManyMinutesIs {

protected static final double NA = (double)-999999999.0;
protected static boolean DEBUG = false;

public static void main(String args[])
{
  if (args.length != 1) {
    System.err.println("Usage: java HowManyMinutesIs \"summary\"");
    System.exit(1);
  }
  String summary = args[0];
  try {
    TTConnection tt = new TTConnection("somehost", 1832);
    TTConnection.DEBUG = DEBUG;
    double length = guessEventLength(summary, tt);
    if (length != NA) {
      System.out.println(length);
    } else {
      System.out.println("unknown");
    }
    tt.close();
  } catch (Exception e) {
    System.err.println("Trouble processing \""+summary+"\":");
    e.printStackTrace(System.out);
    System.exit(1);
  }
}

protected static double guessEventLength(String summary, TTConnection tt)
throws IOException
{
  Vector parseNodes = tt.tag(String.valueOf(TT.F_ENGLISH), summary);
  double sum = 0.0, count = 0.0;
  for (Enumeration e1 = parseNodes.elements(); e1.hasMoreElements(); ) {
    TTPNode parseNode = (TTPNode)e1.nextElement();
    if (parseNode.leo == null || parseNode.leo.objname == null) continue;
    String objname = parseNode.leo.objname;
    Vector objects = tt.retrieve(2, 1, -1, "anc", "duration-of "+objname+" ?");
    for (Enumeration e2 = objects.elements(); e2.hasMoreElements(); ) {
      double minutes = getMinutes(e2.nextElement());
      if (minutes != NA) {
        if (DEBUG) System.err.println(objname+": "+minutes);
        if (minutes < 720.0) {
          sum += minutes;
          count += 1.0;
        }
      }
    }
  }
  if (count == 0.0) return NA;
  return sum/count;
}

protected static double getMinutes(Object o)
{
  try {
    return (((Double)((Vector)o).elementAt(1)).doubleValue())/60.0;
  } catch (Exception e) {
    return NA;
  }
}

}

Listing Two

/*
 * WhereDoIFind.java
 *
 * Uses ThoughtTreasure to determine where a physical object is
 * typically found.
 *
 * Usage: java WhereDoIFind "name of a physical object"
 */

import java.io.*;
import java.lang.*;
import java.util.*;
import com.signiform.tt.*;

public class WhereDoIFind {

protected static final double NA = (double)-999999999.0;
protected static boolean DEBUG = false;

public static void main(String args[])
{
  if (args.length != 1) {
    System.err.println("Usage: java WhereDoIFind \"physical object\"");
    System.exit(1);
  }
  String physobj = args[0];
  try {
    TTConnection tt = new TTConnection("somehost", 1832);
    whereDoIFind(physobj, tt);
    tt.close();
  } catch (Exception e) {
    System.err.println("Trouble processing \""+physobj+"\":");
    e.printStackTrace(System.out);
    System.exit(1);
  }
}

protected static void whereDoIFind(String physobj, TTConnection tt)
throws IOException
{
  Vector parseNodes = tt.tag(String.valueOf(TT.F_ENGLISH), physobj);
  for (Enumeration e = parseNodes.elements(); e.hasMoreElements(); ) {
    TTPNode parseNode = (TTPNode)e.nextElement();
    if (parseNode.leo == null || parseNode.leo.objname == null) continue;
    whereDoIFindObj(parseNode.leo.objname, tt);
  }
}

protected static void whereDoIFindObj(String enclosed0, TTConnection tt)
throws IOException
{
  Vector objects = tt.retrieve(-1, -1, 1, "desc", "at-grid "+enclosed0);
  for (Enumeration e = objects.elements(); e.hasMoreElements(); ) {
    try {
      Vector assertion = (Vector)e.nextElement();
      String enclosed = (String)assertion.elementAt(1);
      String grid = (String)assertion.elementAt(2);
      Vector gss = (Vector)assertion.elementAt(3);
      int x = getCoord((Vector)gss.elementAt(1));
      int y = getCoord((Vector)gss.elementAt(2));
      whatIsInGridAt(grid, x, y, enclosed, tt);
    } catch (Exception f) {
      f.printStackTrace(System.out);
    }
  }
}

protected static void whatIsInGridAt(String grid, int x, int y, String enclosed,
                                     TTConnection tt)
throws IOException
{
  Vector objects = tt.retrieve(1, -1, -1, "exact", "at-grid ? "+grid);
  for (Enumeration e = objects.elements(); e.hasMoreElements(); ) {
    try {
      String enclosing = (String)e.nextElement();
      if (enclosing.equals(enclosed)) continue;
      if (isContainedIn(grid, x, y, enclosing, tt)) {
        if (DEBUG) System.out.print(enclosed+" in "+enclosing+": ");
        System.out.println(tt.generate(String.valueOf(TT.F_ENGLISH),
                                       enclosing));
      }
    } catch (Exception f) {
      f.printStackTrace(System.out);
    }
  }
}

protected static boolean isContainedIn(String grid, int x0, int y0,
                                       String enclosing, TTConnection tt)
throws IOException
{
  Vector objects =
    tt.retrieve(-1, -1, 1, "desc", "at-grid "+enclosing+" "+grid);
  for (Enumeration e = objects.elements(); e.hasMoreElements(); ) {
    try {
      Vector assertion = (Vector)e.nextElement();
      Vector gss = (Vector)assertion.elementAt(3);
      int size = gss.size();
      if (size < 4) continue;
      for (int i = 1; i < size; i += 2) {
        int x1 = getCoord((Vector)gss.elementAt(i));
        int y1 = getCoord((Vector)gss.elementAt(i+1));
        if (x0 == x1 && y0 == y1) return true;
      }
    } catch (Exception f) {
      f.printStackTrace(System.out);
    }
  }
  return false;
}

protected static int getCoord(Vector number)
{
  try {
    return ((Double)number.elementAt(1)).intValue();
  } catch (Exception e) {
    e.printStackTrace(System.out);
    return -1;
  }
}

}

The ThoughtTreasure commonsense platform

The ThoughtTreasure natural language and commonsense platform has been under development since 1994. It includes:

Applications of ThoughtTreasure include information extraction, story understanding, story generation, and personal information management.

-E.T.M.


Erik is the creator of ThoughtTreasure. He can be contacted at erik@signiform.com.
ThoughtTreasure documentation | ThoughtTreasure home

Questions or comments? webmaster@signiform.com
Copyright © 1999 Signiform. All Rights Reserved. Terms of use.