enriquepablo / nl (http://bitbucket.org/enriquepablo/nl/wiki/Home)

nl is a python library that provides a production system with a declarative API modelled on the natural language.

Clone this repository (size: 536.4 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/enriquepablo/nl/

nl

Introduction

nl is a python library, that exposes a declarative API that allows us to build sentences and rules. These are used as input for a knowledge base built on the CLIPS production system. CLIPS builds a Rete network with the rules and sentences, which can then be queried for the consecuences of those in a most efficient way.

The main claim of nl is to offer a syntax that can accommodate any coherent theory that we may build with the natural language (in the same sense as something like the semantic web's OWL-Full would), while at the same time being based on a simple finite domain first order theory. This theory is NL, a discussion of which can be found here. This discussion is probably required reading to understand the breadth and the limits of nl, but not to start using it.

There is a mailing list for nl.

Installation

For installation instructions, you can see the INSTALL.txt file in the distribution. Basically, just (with python2.5):

 $ easy_install nl

Usage

The API consists of a few classes, basically Thing , State , Fact , and Rule , and a few functions, basically, tell , extend and ask . Thing and State are subclassed to define, respectively, nouns and verbs, which are instantiated to build nominal and verb phrases, which in turn are used to instantiate Fact and Rule to build sentences. tell is used to enter these sentences into the system, extend is used to obtain all logical consecuences of a set of sentences, and ask is used to query the system.

A more elaborate example can be found in http://bitbucket.org/enriquepablo/nl/src/tip/src/nl/examples/cms2.py, combined with http://bitbucket.org/enriquepablo/nl/src/tip/src/nl/test/functional_test.py.

Thing

Thing is subclassed to make what will be used like common nouns, e.g., "person". The derived classes are instantiated with a string [1] corresponding to a proper noun.

1
2
3
4
5
6
7
8
>>> from nl import Thing, State, Number, Fact, Rule, kb

>>> class Person(Thing):
...     pass

>>> john = Person('john')

>>> kb.tell(john)

These instances can be used in two ways. They can be passed directly to tell , and that will be like asserting a copular sentence, "John is a person". Once they have been added to the system in such a manner, they can also be used in facts, as subject or modifier in the predicate; more on this later.

[1] These strings have to be legal python names.

State

State is subclassed to make what will be used as verbs. (The word "state" is probably not a good choice to lend it's spelling to this class; it does not seem an ancestor of all verbs, and indeed, it is not a verb. "To be" might seem more approriate, but "to be" corresponds to predicates in NL, and, in nl, it is implied in the class structure: as we saw above, the fact that john is an instance of Man is interpreted as "john is a man".) And Verb is the name of State 's metaclass.

The subclasses of state are defined with a dictionary mods of strings to classes, which have to be subclasses of Thing, Noun, State, Verb , or Number (also from the nl namespace). Noun and Verb are the metaclasses of Thing and State. This mods dict represents the possible modifiers that the verb can have when instantiated as a predicate of ln. The subclasses of State can optionally take an attribute named subject , of class Thing .

1
2
3
4
5
6
7
8
9
>>> class Wants(State):
...     subject = Person
...     mods = {'what': State}

>>> class Content(Thing): pass

>>> class Publish(State):
...     subject = Person
...     mods = {'content': Content}

Classes derived from State are instantiated as verb phrases within facts, and are given, as **kwargs , a dictionary equivalent to the mods of the class, but with instances instead of classes, as we shall see in the next section.

Fact

Fact is instantiated to make facts, with three positional args representing subject, predicate, and (optional) time. As subject, it requires a Thing instance, as predicate a State instance, and as time, a Instant or Duration instance.

1
2
3
4
5
6
7
>>> class Document(Content): pass

>>> doc1 = Document('doc1')

>>> p1 = Fact( john, Wants( what=Publish( content=doc1 )))

>>> kb.tell(doc1, p1)

It can also take an additional named parameter truth of type boolean, that indicates whether the sentence is affirmed or denied, and defaults to True.

Rule

Rule is instantiated to build rules. The constuctor takes as positional arguments two lists of facts and things, to act as premises and as consecuences. What CLIPS will do with all this is to seek for instances (stand alone facts and things) that match the premises, and once it covers all the premises in a rule, adds the consecuences as stand-alone sentences.

Variables can be used within rules. They are given by a string composed by an upper case letter followed by digits. They can be used in any place where intances of Thing, State, Noun, Verb, or Number might be used. Their scope is the rule where they appear, and their only role is to match with names, when present in the premises, and to be substituted by the match when present in the consecuences.

1
2
3
4
5
6
7
>>> r1 = Rule([
...            Fact( Person('P1'), Wants( what=State('X1')))
...            ],[
...            Fact( Person('P1'), State('X1'))])


>>> kb.tell(r1)

extend and ask

Now, we can extend the database to all its logical consecuences. With the previous sentences, there is only one consecuence: that john publishes doc1:

1
2
3
4
5
>>> assert not kb.ask(Fact( john, Publish( content=doc1 ), 0 ))

>>> kb.extend()

>>> assert kb.ask(Fact( john, Publish( content=doc1 ), 0 ))

Arithmetics

Modifiers in predicates can have a numeric value, specified in the verb definition as Number . In stand alone facts, numbers are just numbers, integers or floats. In a rule, they can be arithmetic operations in which variables take part. The syntax for these operations is (<op> <arg1> <arg2>) . At the moment, only binary operations are allowed. Of course, any of the arguments can itself be an operation. The operators can be any of the arithmetic operators allowed in clips.

Additionally, we can include arithmetic sentences, with truth value instead of numeric value, as premises in a rule. The allowed predicates are the arithmetic predicates allowed in clips, and the syntax is the same as that of operations.

An example of the use of arithmetics can be found in http://bitbucket.org/enriquepablo/nl/src/tip/src/nl/examples/physics-22.py

Class variables

We also can use verbs and nouns as individuals, and, in rules, have variables ranging over them, as was implied by the inclussion of Noun and Verb in the discussion above. Within a fact, we can use a verb or a noun as subject, or as a modifier in the predicate. Within a rule, we can use a noun or verb variable anywhere a noun or a verb might be used.

Hooks

We can add a method in_fact to subclasses of State, that will be called whenever a new fact with that verb in the predicate is added to the system (by the user or by extend ). This method takes as argument the fact added, as an instance of Fact.

Licence

This software is licenced under the GPL v3.


Enrique Pérez Arnaud © 2008-2009 <enriquepablo at google's mail domain>


This revision is from 2009-12-23 22:39