PROLOG+CG
by
Adil KABBAJ
Using/Calling JAVA from PROLOG+CG program
Object-based side of PROLOG+CG
An application: Natural Language Processing (prototype)
The language PROLOG+CG has been developed by A. Kabbaj in his Ph.D. thesis (1992-1996) [1] in order to extend PROLOG language in two main directions :
a conceptual extension allowing the representation of goals with CG and the manipulation of simple and compound CGs (including type hierarchy, operations on conceptual graphs, etc.) and,
an object oriented extension allowing the manipulation of objects (object specification, inheritance mechanisms, etc.).
The initial definition and implementation of Prolog+CG (Prolog+CG 1.0) has been extended to include an interface with Java [2], leading to the next version of Prolog+CG (Prolog+CG 2.0). This integration benefits from the fact that both Prolog+CG 2 and its development environment were implemented in Java. Hence and thanks to this new interface, Prolog+CG 2.0 can be called from a Java code and inversely, Java classes can be manipulated from a Prolog+CG program. The integration of Java, object-oriented Prolog and the manipulation of CGs provides a powerful development environment for the creation of knowledge-based applications and their integration on the web. Java allows the development of multi-platform applications as well as the capabilities of object-oriented languages. Object-oriented Prolog provides the full power of a logic programming language well suited for natural language processing, inference and symbolic manipulations. CGs provide the expressive power of an advanced knowledge representation language (advanced semantic nets, type hierarchy, schemas, notion of context, etc.).
For more detail on Prolog+CG 2.0, see the ICCS2001 paper or Prolog+CG 2 web site (Ulrik Petersen created a sourceforge project for Prolog+CG 2 and he is currently the maintener of this old version of Prolog+CG). An excellent online course on CG theory and some aspects of Prolog+CG 2 is provided by the group of Henrik Scharfe and Peter Ohrstrom from Aalborg University, Denmark.
Then, Prolog+CG has been integrated to Amine platform, leading to the development of new versions of Prolog+CG. To introduce the changes involved by such an integration, let us recall three key features of previous versions of Prolog+CG (Prolog+CG 1.0 and 2.0):
CG (simple and compound CGs) is a basic and primitive structure in Prolog+CG, like list and term. And like a term, a CG can be used as a structure and/or as a representation of a goal. CG matching-based operations are provided as primitive operations.
By a supplementary indexation mechanism of rules, Prolog+CG offers an object based extension of Prolog. Consider for instance, the following fragment of a Prolog+CG program: rules are indexed by identifiers (Leopard, Mammal, Carnivorous, Fact). All the rules with the same index, like the two rules for Carnivorous, belong to the same object. Note that the index can be a term, not only an identifier. A goal in the tail of a rule can be prefixed by such an index (like : Mammal::[Animal : x]-isA->[Mammal]). In this case, the goal is resolved (interpreted) according to the content of the object with the same index. This supplementary indexation is advantagous when several rules start with CG as their head. It plays the same role as the signature of a term in Prolog.
Leopard::[Animal : x]-isA->[Leopard] :-
Mammal::[Animal : x]-isA->[Mammal],
Carnivorous::[Animal : x]-isA->[Carnivorous],
Fact::[Animal : x]-colorOf->[Color]-attr->[Wild],
Fact::[Animal : x]-partOf->[Component]-attr->[Dark].
Mammal::[Animal : x]-isA->[Mammal] :-
Fact::[Animal : x]-poss->[Hair].
Carnivorous::[Animal : x]-isA->[Carnivorous] :-
Fact::[Animal : x]<-agnt-[Eat]-obj->[Meat].
Carnivorous::[Animal : x]-isA->[Carnivorous] :-
Fact::[Animal : x]-poss->[Teeth]-attr->[Sharp],
Fact::[Animal : x]-poss->[Claw],
Fact::[Animal : x]-has->[Eyes]-attr->[Forward].
Prolog+CG offers an interface with Java: Java objects can be created and methods can be called from a Prolog+CG program. Also, Prolog+CG can be activated from Java classes.
These three key features of Prolog+CG are still present in the new versions of Prolog+CG, but the re-engineering of Prolog+CG 2.0, which was necessary for its integration in Amine platform, involved some changes in the language. Four main changes are of interest:
Type hierarchy and Conceptual Structures (CSs) are no more described in a Prolog+CG program. Now, Prolog+CG programs are interpreted according to an ontology that should be loaded first. Type hierarchy and CSs can be looked for directly from the ontology. Also, Prolog+CG attempts first to interpret each identifier in a program according to the current lexicon of the current ontology. If no such identifier is found, then the identifier is considered as a simple identifier (without any underlying semantic).
The notion of project is introduced: user can consult several programs (not only one) that share the same ontology. For instance, Figure 6 gives a snapshot of a natural language application composed of four programs (lexicon, Analysis, Utilities, QuestionAnswering).
Prolog+CG inherits the first two layers of Amine: all Amine structures and operations are also Prolog+CG structures and operations. And of course, user can manipulate the current ontology and the associated lexicons according to their APIs. Operations, implemented as methods, can be used thanks to the new interface between Prolog+CG and Java.
The interface between Prolog+CG and Java is more simple and more “naturel”: the call of a method in Prolog+CG is very similar to the call in Java and it is very simple to call Prolog+CG from Java.
These changes, from the old to the new versions of Prolog+CG, includes also the architecture and the implementation aspects: the architecture is more modular and the implementation more simple. As noted in the previous section, Expert System Mode and Object rules Inheritance are eliminated from the new versions of Prolog+CG. Also, some bugs have been fixed (in unification for instance) and also the set of primitives is reduced. The reason of this reduction is that an access to Java (and to Amine APIs which are Java packages) is now very easy.
This document presents the last version: Prolog+CG 5.5
Organization of this document
Following sections introduce basic parts of the new versions of Prolog+CG. We start by a brief introduction of the structure of a Prolog+CG program which is similar to standard Prolog program. Elementary elements (Identifier, Variable, Number, String, etc.) and composed structures (AmineSet, AmineList, Term, CG) are introduced. We insist on the fact that Prolog+CG is now fully integrated to Amine: structures, operations and components of Amine can be easily used by/from Prolog+CG programs. In fact, Prolog+CG parser is implemented as an extension of ObjectParsing; a class that parses Amine Objects (elementary and composed structures). So, Prolog+CG is a continuation or an extension of the Kernel and Algebraic layers of Amine (with the addition of rules).
Then we introduce a new possibility (in comparison to old versions of Prolog+CG): Prolog+CG treats infix formulation of expression (instead of prefix notation that was adopted in previous versions). Assignation operator "is" is also introduced. We present also the possibility to use global objects (global variables). Then, we present the current list of Prolog+CG primitive goals.
The remaining sections introduce the interface between Prolog+CG and Java: a) call of (Amine and Java) methods from Prolog+CG program, b) Creation and use of Java objects from Prolog+CG program, and c) calling Prolog+CG from Java classes.
We present also the possibility to organize a program as a collection of objects (of rules). In a separate section, we present some examples to illustrate programming with Prolog+CG. Then we present the syntax of PROLOG+CG and some implementation details on Prolog+CG components.
Prolog+CG is an extension of Prolog language. This document is not an introduction of Prolog. We assume that the user is familiar to this language. So, only a brief description of the Prolog part is presented here. Our focus will be on the specific features of Prolog+CG.
A PROLOG+CG program is a collection of rules, a rule is composed of a head and optionally a tail. The head is separated from the tail by the symbol ":-" (symbol ":-" can be read "if". Syntax of PROLOG+CG is similar to several dialects of Prolog). If the symbol ":-" and the tail are not specified, the rule represents a fact. Prolog+CG rule terminates with a dot ".".
The "head of a rule" corresponds to a goal and the "tail of a rule" is a sequence of Goals separated by comma. A Goal is either an identifier, a variable, a string, a term, a CG, a "Java" message, or a Prolog+CG message. However, the head of a rule can't be a variable, a "Java" message or a Prolog+CG Message. Identifier, Variable, String, Term and CG are Amine elements or structures. Next sections provide brief recall of these elements and structures. See Algebraic Layer for more detail about these elements/structures. Also and for simplicity, we ignore for the moment the possibilities of "Java" message and Prolog+CG message, as well as the possibility to organize a program in a collection of objects (of rules). These possibilities/features are introduced later.
Examples:
mother(Kathie, Jane). // this is a fact; a fact that Kathie is the mother of Jane
mother(Jane, Mary). // this is another fact: Jane is the mother of Mary
grandMother(x, y) :- mother(x, z), mother(z, y). // this is a rule: x is grandMother of y IF x is mother of z, and z is mother of y
Elementary elements
Elementary elements of Prolog+CG are those of Amine: Identifier, Variable, AmineInteger, AmineDouble, AmineBoolean and String.
Identifier. An identifier starts with at least two letters and can be followed by characters. An identifier is either specified in the current lexicon of the current ontology and so it refeers to a CS (Type, Individual or RelationType) in the ontology, or it is an unknown identifier and it is considered as an elementary object (without any underlying semantic).
Variable. A variable identifier should start with a letter followed optionally by a digit or underscore. After the digit or the underscore, a variable can have any sequence of characters. A variable identifier can begin also with an underscore followed optionally by any sequence of characters. There is also special cases of variables (they are variables of the language !): "super", "this", "x_source", and "y_target" .
Composed Structures: List, Term, CG, and other Amine structures and operations
Composed structures of Prolog+CG are those of Amine: AmineSet, AmineList, Term and CG. An element in AmineSet, AmineList or Term is an Amine object. See Algebraic Layer for definition and examples of these structures.
CG is considered as a "data structure" in the same sense as a list or a term. Like the other structures of Amine, CG can contain variables: a concept type, a concept designator, a concept descriptor and a relation type can be a variable. This is very important in the context of a programming language. With this full integration of CG in PROLOG+CG, the latter enhances its capabilities as a rule-based and pattern-matching programming language (CG enables the formulation of complex patterns). List and Term (or predicate) can be used also to represent patterns. See CG for differences between CG and predicates (or terms).
Examples:
AmineSet: {34, Man, "good answer"}
AmineList: [Karl, v, isFather(x, Martha), [67, "ok"], 78.6 | L]
Note that unlike lists in previous versions of Prolog+CG where a list is enclosed by parenthesis "(" and ")", the new versions of Prolog+CG use "[" and "]".
Term: father(Karine, x)
CG (note the use of variables: x as a designator, T as a concept type, R as a Relation Type):
[Man:x]<-agnt-[Eat]-R->[T]-Color->[Color: red]
Note that a concept descriptor in a CG is an Amine object: it can be any elementary or composed Amine object. For instance: [Man:Karim]<-pat-[Born]-date->[Date = [11, 06, 1960]]
Example: a Prolog+CG program (of facts)
Consider this simple Prolog+CG program, composed of a paquet of three facts with signature cg/1; it provides an idea of the "natural" use of CG as Prolog+CG data structure:
Note: To run the following examples, load first the Ontology "samples/ontology/ManOntology2.xml".
cg([Man:karim]<-agnt-[Eat]-obj->[Apple]).
cg([Man:imad]<-agnt-[Drive]-obj->[Car]).
cg([Man:karim]<-agnt-[Drink]-obj->[Water]).
And the following request: "What actions are done by Man Karim ?"
?- cg([Man:karim]<-agnt-[x]).
{x = Eat};
{x = Drink};
no
?-
Expression and assign primitive (is)
Previous versions of PROLOG+CG were limited to prefix notation for expressions. For instance the expression:
x * 34 - y was represented in Prolog+CG 2 as sub(mul(x, 34), y). In the new versions (since Prolog+CG 3), an expression should be represented by an infix notation: x * 34 - y. The value of an expression can be assigned to a variable or to a global object (introduced in the next section). The assignation operation "is" should be used for this purpose (it replaces the primitive val() in the previous versions of Prolog+CG).
Example:
compute(x, y, z, r) :-
w is x + y * z,
r is w - 34.
?- compute(425, 65, 2, r).
{r = 521}
The operator = (= corresponds to the call to unify operation)
?- x = "bonjour".
{x = "bonjour"}
?- x = "bonjour", x = "bour".
no
Global Objects
Prolog+CG provides a possibility to create and manipulate global objects; objects that are not local to a rule. Indeed, if the user assigns a value to an identifier instead of a variable, then the identifier becomes an identifier for a global object (the value assigned).
Example:
Consider the rules below: since the value of the expression "w - 34" is assigned to the identifier result, the latter is considered as a global object and it is added to the HashMap of GlobalObjects. In the second rule, the global object result is used in an expression (like an identifier of a variable which represents a local object).
compute(x, y, z) :-
w is x + y * z,
result is w - 34 .
useGlobal(x, r) :-
r is result + x .
?- compute(45, 76, 2), useGlobal(13, r).
{r = 176}
yes
?-
Of course, like for a variable, any value (a list, a term, a CG, etc.) can be associated to a global object.
Prolog+CG primitives
The set of primitive goals is currently very small (see below). The main reason is that most of the primitives in the previous version of Prolog+CG are now methods in Amine APIs (like type hierarchy operations and CG operations) and these methods are easily accessible via the interface between Prolog+CG and Java (as illustrated in next sections). Beside Amine APIs, the user has also an access to Java APIs and of course, he/she may develop his/her own classes and use them in his/her Prolog+CG programs. Of course, it is always possible to extend the small set of Prolog+CG primitives (if necessary).
Here is the list of the current Prolog+CG primitives (Figure 1):
Figure 1: Primitives of Prolog+CG
! : the cut operator
fail: resolution will fail by definition, involving a backtracking
read/1: read an Amine object from the input dialog and unify this object to the specified argument (see examples below)
readSentence/1: read a sentence; a sequence of words, then construct a list of strings (one string per word) and then unify the list with the specified argument
write/1 and writeln/1: write the value of the specified argument followed by new line (for writeln)
concOfCG/3: concOfCG(C, P, G) : find a concept C in the CG G that unifies with the concept P. P represents the pattern that should be satisfied by concepts to search. Resatisfaction of this primitive will return all concepts in G that satisfy the pattern P (see example below).
branchOfCG/3: branchOfCG(B, P, G) : find a branch B (Relation with its relation type, source concept and target concept) in the CG G that unifies with the branch P. P represents the pattern of branches to search. Resatisfaction of this primitive will return all branches in G that satisfy the pattern P (see example below).
asserta/2 and assertz/2: assert a rule (or a fact if the second argument is an empty list []) at the top or at the bottom of the related paquet
retract/1: retract, from the related paquet, a rule with the head that unifies with the specified argument
suppress/1: suppress all the rules of the paquet with the specified signature (formulated as a string). See example below.
load_db/1: load the content of the "db" file specified in argument.
save_db/2: store in the "db" file specified in the second argument the paquet with signature specified in the first argument.
list/1 and listAll: list the rules of the paquet with the specified signature
free/1: check if the specified variable is a free variable
destroy/1: remove the specified global object from the GlobalObjects HashMap
destroyAll: clear the content of the GlobalObjects HashMap
getOntology/1: associate to the specified argument the reference to the current ontology (see example below)
getLexicon/1: associate to the specified argument the reference to the current lexicon
getLanguage/1: associate to the specified argument the reference to the current language
setLanguage/1: change the current language to the specified language (specified as a String).
Examples:
Example #1: some primitives can/should be defined by the user. For instance:
eq(x, x).
dif(x, y) :- eq(x, y), !, fail.
dif(x, y).
sup(x, y) :- true is x > y.
inf(x, y) :- true is x < y.
or(p, q) :- p, !.
or(p, q) :- q.
member(x, [x|_]).
member(x, [_|L]) :- member(x, L).
?- consult(["exple1.pcg"]).
yes
?- eq(5, 4).
no
?- eq(7, 7).
yes
?- dif(toto, titi).
yes
?- sup(7, 5).
yes
?- sup(5, 7).
no
?- inf(5, 7).
yes
?- inf(7, 5).
no
?- or(inf(5, 7), inf(7, 5)).
yes
?- or(inf(7, 5), sup(5, 7)).
no
?- member(3, [5, 6, 7, 8]).
no
?- member(3, [5, 6, 7, 3, 8]).
yes
?- member(x, [5, 6, 7, 3, 8]).
{x = 5};
{x = 6};
{x = 7};
{x = 3};
{x = 8};
no
?-
The user can define all these primitives in a Prolog+CG program and then use it in all his projects (see section of Project in Prolog+CG).
Examples #2: read, readSentence and write
The execution of primitives read and readSentence initiate the appearance of a read_frame where the user is asked to write his/her data (Figure 1). By selection of the button "Read", the frame is closed and the input data is unified to the argument of read (or readSentence/1). Figure 1 illustrates this point.
(a) (b)
Figure 1: Input Frame
The user gets the same read_frame for readSentence/1. If the user enters the sentence "John drive to the store.", then the response will be:
?- readSentence(x).
|: John drive to the store.
{x = ["John", "drive", "to", "the", "store", "."]}
yes
It is also
possible to specify directly the sentence as a first argument of
ReadSentence/2:
?- readSentence("John drive to the store.", L).
{L = ["John", "drive", "to", "the", "store", "."]}
yes
Examples of
write:
?- eq(x, 45), write([8, x, 78]).
[8, 45, 78]{x = 45};
no
?- eq(x, [Man]<-agnt-[Eat]-obj->[Apple]), write(x).
[Eat #0] -
-obj->[Apple],
-agnt->[Man]{x = [Eat #0] -
-obj->[Apple],
-agnt->[Man]}
yes
?-
Examples #3: concOfCG and branchOfCG
Note: in the following request, [Mammal #0] is specified with pseudo-designator "#0" instead of [Mammal] because, used outside a CG (i.e. as an independent Amine object), the concept [Mammal] will not be interpreted as a concept but as a list with one element. To force its interpretation as a concept, we specify a pseudo-designator (of course it adds nothing, its use here is to resolve the ambiguity). Note also how the second argument of concOfCG/3 plays the role of a pattern to search all concepts that unify with the pattern.
Note: Whenever a Prolog+CG program contains CG, the required ontology, which constitutes the conceptual background for the CG, should be loaded first. This constraint holds also when the Prolog+CG program contains elements that refeers to elements in an ontology.
For this example, you should load first the Ontology "samples/ontology/ManOntology2.xml".
?- concOfCG(c, [Mammal #0], [Cat]<-agnt-[Eat]-obj->[Mouse]).
{c = [Cat]};
{c = [Mouse]};
no
?- concOfCG(c, [Cat #0], [Mammal]<-agnt-[Eat]-obj->[Mouse]).
{c = [Mammal]};
no
?-
Let us consider now a more complex example, where the CG corresponds to a complex structure with embedded contexts (concepts with CG as descriptors). The CG to look in is "stored" as an argument of the term cg/1. The complex structure is about a girl Karen, who is a kid with blond hair and blue eyes. Karen has the goal to get a piece of Pizza. Her plan is to ask her father, Mike who is a grown-up man with brown hair and Green eyes, to give her a piece of Pizza. Mike accepts and give the piece of Pizza to Karen. The complex structure, represented by a compound CG, is an "intentional structure"; it concerns a situation of plannification where two agents are involved with different goals and plans.
cg(
[Human :Karen ?k_karen = [Human: Karen]-
-Age->[Age:Kid],
-Hair->[Hair]-Color->[Color:Blond],
-Eyes->[Eyes]-Color->[Color:Blue]
]<-actor-[Goal = [Goal]-
-actor->[Human :Karen *k_karen],
-goal->[Poss_By *s_state],
-plan->[Plan *p_plan],
-outcome->[Success_Goal_Outcome]
]-
-outcome->[Poss_By #2 ?s_state = [Poss_By]-
-actor->[Human: Karen *k_karen],
-object->[Pizza: pizza001],
-State_Value->[State_Value:Yes_State_Value]
],
-achievedBy->[ATrans #1 ?a_atrans = [ATrans]-
-actor->[Human:Mike *m_mike],
-object->[Pizza:Pizza001],
-to->[Human:Karen *k_karen]
],
-intendedBy->[Plan ?p_plan = [Plan]-
-actor->[Human: Karen *k_karen],
-RealizedBy->[MTrans *m_trans]
]-realizedBy->[MTrans ?m_trans = [MTrans]-
-actor->[Human: Karen *k_karen],
-to->[Human : Mike *m_mike]<-Father-[Human: Karen *k_karen],
-object->[ATrans *a_atrans]
]-motivates->
[Goal = [Goal]-
-actor->[Human :Mike *m_mike],
-goal->[ATrans *a_atrans],
-plan->[Plan *p2_plan],
-outcome->[Success_Goal_Outcome]
]-
-actor->[Human : Mike ?m_mike = [Human:Mike]-
-Age->[Age: Grown_Up],
-Hair->[Hair]-Color->[Color:Brown],
-Eyes->[Eyes]-Color->[Color:Green]
],
-achievedBy->[ATrans #1],
-intendedBy->[Plan ?p2_plan = [Plan]-
-actor->[Human: Mike *m_mike],
-RealizedBy->[ATrans *a_atrans]
]-realizedBy->[ATrans #1],
-outcome->[Poss_By #2]
).
The following goal (exple2/2) is defined to access the fact cg/1 to get the CG G and then to get a concept c in G that unifies with the specified concept p:
exple2(c, p) :-
cg(g),
concOfCG(c, p, g).
Our request now is to get goals, from the stored CG, that unify with the specific pattern (a goal that has Human as actor and does succeed):
?- exple2(c, [Goal = [Success_Goal_Outcome]<-outcome-[Goal]-actor->[Human]]).
{c = [Goal = [Goal #0] -
-Actor->[Human :Karen *k_karen],
-Goal->[Poss_By *s_state],
-Plan->[Plan *p_plan],
-outcome->[Success_Goal_Outcome]
]};
{c = [Goal = [Goal #0] -
-Actor->[Human :Mike *m_mike],
-Goal->[ATrans *a_atrans],
-Plan->[Plan *p2_plan],
-outcome->[Success_Goal_Outcome]
]};
no
?-
Let us consider now the use of branchOfCG/3. Please note that branchOfCG/3 returns a relation object, not the relation type neither a CG composed of the relation, but a relation (relation type with the source and target concepts):
exple3(b, p) :-
cg(g),
branchOfCG(b, p, g).
We want to search "what motivates a goal ?":
?- exple3(b, [T]-motivates->[Goal]).
{b = (motivates [MTrans ?m_trans = [MTrans #0] [Human :Karen *k_karen] [Human :Mike *m_mike]
(Father Karen Mike) (to #0 Mike) (Object #0 [ATrans *a_atrans]) (Actor #0 Karen) ] [Goal = [Goal #0]
(outcome #0 [Success_Goal_Outcome]) (Goal #0 [ATrans *a_atrans]) (Actor #0 [Human :Mike *m_mike])
(Plan #0 [Plan *p2_plan]) ]), T = MTrans}
The result is a relation (branch) expressed in CGIF: (relationType SourceConcept TargetConcept). Here is the above result indented:
{b = (motivates
[MTrans ?m_trans = [MTrans #0] [Human :Karen *k_karen] [Human :Mike *m_mike]
(Father Karen Mike) (to #0 Mike) (Object #0 [ATrans *a_atrans]) (Actor #0 Karen)
]
[Goal = [Goal #0]
(outcome #0 [Success_Goal_Outcome]) (Goal #0 [ATrans *a_atrans]) (Actor #0 [Human :Mike *m_mike])
(Plan #0 [Plan *p2_plan])
]
), T = MTrans}
Example #4: load_db and save_db.
Figure 2.a shows a simple Prolog+CG program with load_db and store_DB primitives. Figure 2.b shows the content of the "db" file "ex1" before the execution of the program. Figure 2.c shows the execution of the program and Figure 2.d shows the content of the file "ex1" after the execution.
(a) (b)
(c) (d)
Figure 2: Example for load_db and save_db primitives
Example #5: asserta/2, assertz/2, retract/1, suppress/1 and list/1
car(1, "bmw").
car(2, "gfdgfd").
assertMetaPredicates :-
assertz(car(3, "peugeot"), []),
asserta(car(0, "honda"), []).
removeMetaPredicates :-
car(x,y),
check(x),
retract(car(x,y)),
fail.
removeMetaPredicates.
check(1).
check(2).
We ask for a listing of the rules that compose the paquet with signature car/2 (provided as a string), then we call assertMetaPredicates goal, then we call list primitive to show the assertion of the two facts, then we call removeMetaPredicates goal to remove two facts that validate a simple constraint, then we list the result. And we terminate by calling the primitive suppress/1 to suppress the paquet.
?- list("car/2").
Car(1, "bmw").
Car(2, "gfdgfd").
yes
?- assertMetaPredicates.
yes
?- list("car/2").
Car(0, "honda").
Car(1, "bmw").
Car(2, "gfdgfd").
Car(3, "peugeot").
yes
?- removeMetaPredicates.
yes
?- list("car/2").
Car(0, "honda").
Car(3, "peugeot").
yes
?- suppress("car/2").
yes
?- list("car/2").
no
?- car(x, y).
no
?-
The other primitives will be illustrated in the next sections.
Using/Calling JAVA from PROLOG+CG program
"Java" Messages: Calling methods of Java objects from Prolog+CG program
As noted before, it is very easy in the new versions of Prolog+CG to call methods of Java objects (from Amine APIs or from Java APIs). In the introduction, we provided the following definition of Goal: a goal is either an identifier, a variable, a string, a term or a CG. Now we extend this definition of Goal with a new case :
A Prolog+CG goal can be also a "Java" message.
"Java" message in Prolog+CG has the form Object:Method where Object is either an Identifier, a Variable or a String, and Method is either a Term or a Variable. We use the term Java surrounded by double quotes ("Java") to stress the fact that there are some differences between "Java" Message in Prolog+CG and message in Java. For instance, in "Java" message, Object is separated from Method by ":", not by "." as in Java. Despite this difference (and others), the interpretation (execution) of a "Java" message in Prolog+CG leads to a call/execution of a message in Java.
Note: We use the expression "Java" message instead of "Prolog+CG message" because this expression is used for another type of message in Prolog+CG.
Here is the syntax of (Java/Prolog+CG) message :
"Java" Message = (Identifier | Variable | String) ":" (Term | Variable)
Identifier can correspond to an Identifier specified in the current lexicon of the current ontology and in this case, the identifier refeers to a CS object. Identifier can be a global object. In general, when the receiver of the message is a String, it represents the complete name of a Java class and the method corresponds to a static method. If a variable is used in the formulation of a message (as the object of the message or as its method), it should be bound (an object should be associated to it) at the execution time. Note also that the value of the variable should be a term when used as a method. At the execution time, the method is applied on the object.
Examples:
Example #1: In the following example, the "Java" message is L:size() . At execution time, L is associated to a list ([3, 4, 5, 6] for instance) which corresponds to an AmineList object. Thus, the method size() is applied on an AmineList object. The value returned by the method is assigned to a variable. Note that since AmineList extends the Java class ArrayList, it inherits the method size(). Previous version of Prolog+CG provided a primitive for the length of a list. It is no more necessary; a simple "Java" message does the same Job !
length(L, s) :- s is L:size().
?- length([3, 4, 5, 6], x).
{x = 4}
yes
?- length([], x).
{x = 0}
yes
?-
Example #2: Let us consider now another example: it concerns a CG operation; the maximalJoin of two CG G1 and G2, the result is returned in G3. The message in this example is: G1:maximalJoin(G2) . At execution time, G1 is associated to a CG (an Amine object). Thus, the method maximalJoin() is applied on the CG associated to the variable G1. The value returned by the method is assigned to a variable (G3 in our example). Again, it is no more necessary to provide maximalJoin as a primitive; a simple "Java" message is sufficient !
cg(cg1, [Man]<-agnt-[Eat]).
cg(cg2, [Person:karim]<-agnt-[Eat]-obj->[Apple]).
maxJoin(G1, G2, G3) :- G3 is G1:maximalJoin(G2).
?- cg(cg1, G1), cg(cg2, G2), maxJoin(G1, G2, G3).
{G1 = [Man]<-agnt-[Eat],
G2 = [Eat #0] -
-obj->[Apple],
-agnt->[Human :karim],
G3 = [Eat #0] -
-agnt->[Man :karim],
-obj->[Apple]};
no
?-
Remark (optional): To understand the correspondance between the method (signature) in a "Java" message (i.e. maximalJoin/1 like maximalJoin(G)) and the corresponding method (signature) in Amine APIs (i.e. maximalJoin/4 like maximalJoin(BindingContext, BindInf, G, BindInfOfG) and other variants), please note that the method of a "Java" message: methodIdent(arg1, arg2, ..., argN) is reformulated by Prolog+CG interpreter as methodIdent(BindingContext, BindInf, arg1, BindInfArg1, arg2, BindInf2, ..., argN, BindInfN); BindingContext is added as a parameter as well as the binding information for each argument. Using reflection possibilities of Java, Prolog+CG interpreter searches a method with the same signature. If no such a method exists, it searches a method with the initial signature: methodIdent(arg1, arg2, ..., argN). See implementation details to get more information on this issue.
Example #3: This example illustrates the case where the object of the message is an Identifier (Man in our example) specified in the current lexicon of the current ontology. As noted before, in this case, the object of the message is not the Identifier itself but the Conceptual Structure (CS) object that is refeered by it, and the method will be applied on this CS object. In the first request, we ask for the maximal common subtype of Man and Woman. They have no maximal common subtype, the result is null. In the second request, we get the result Human. The messages in this example are Man:getMaxComSubType(Woman) and Man:getMinComSuperType(Woman).
?- x is Man:getMaxComSubType(Woman).
{x = null};
no
?- x is Man:getMinComSuperType(Woman).
{x = Human};
no
?-
Example #4: Here an example of a static method (that returns the specified value as a string). The message is: "java.lang.String":valueOf(345). The object of the message is a string that represents the complete name of the class: "java.lang.String"
?- v is "java.lang.String":valueOf(345).
{v = "345"}
yes
?-
Creation and use of Java objects from Prolog+CG program
The user can create and then use a Java object from his/her Prolog+CG program. The "Java" message (in Prolog+CG) for the creation of a Java object has the form: CompleteClassName:new(arg1, ..., argN) .
For instance, the following "Java" message "java.util.Stack":new() is equivalent to the following Java code:
new Stack().
Of course, a constructor (in Java) with arguments is represented by the special Prolog+CG method new() with the same arguments. Once created, methods of the object can be activated normally.
Example :
createAStack(r) :-
v is "java.util.Stack":new(),
v:push(one),
v:push(two),
r is v:peek().
?- createAStack(r).
{r = two}
yes
?-
The above example illustrates also the case where the method doesn't return a value (or the programmer is not interested by its value), like the method push().
Use of Other components of Amine from a Prolog+CG program
The following example illustrates how other components of Amine, like dynamic integration process and information retrieval process can be called easily from a Prolog+CG program.
// call the method integrateSituation() to integrate, in the current ontology, a situation with pertinent types
integrateSituation(G, L) :-
getOntology(_ontology), // getOntology() is a primitive goal in Prolog+CG to get the current ontology
"aminePlatform.engines.dynamicOntology.DynamicOntology":integrateSituation(_ontology, G, L), !.
ask(G, L) :- // call the method ask()which returns an AmineList of results that represents the neighbourhood
getOntology(_ontology),
_rslt is "aminePlatform.engines.dynamicOntology.DynamicOntology":ask(_ontology, G, L),
writeRslt(_rslt), !.
Some requests, submitted to Prolog+CG via its console:
?- integrateSituation([Robot]<-agnt-[Wash]-thme->[Car], [Robot, Wash, Car]).
yes
?- ask([Robot]<-agnt-[Wash]-thme->[Inanimate], [Wash]).
The description is : EQUAL to/than the known
[Wash #0] -
-Agnt->[Robot],
-Thme->[Inanimate]
The description is : MORE_GENERAL to/than the known
[Wash #0] -
-Thme->[Truck],
-Agnt->[Robot]
The description is : MORE_GENERAL to/than the known
[Wash #0] -
-Thme->[Car],
-Agnt->[Robot]
yes
?-
The call of Prolog+CG (interpreter) from a Java Class is very simple:
create an instance of a Prolog+CG interpreter, with the specification of the path for the ontology to use and the list of paths for the Prolog+CG files to consult, and
call the method findAllSolutions(Request) of the interpreter to return all the solutions for the specified request.
Here is an example (an excerpt from a Java code):
Interpreter interpreter = new Interpreter(ontologyFilePath, ppcgFilePaths);
ArrayList allSolutions =
interpreter.findAllSolutions("[CITIZEN : x]<-memberOf-[COUNTRY : Oz].");
Here is a more detailed fragment from the Java class PrologPlusCGFromJava:
//
The File Path of the Ontology to use in this example.
String ontologyFilePath = AmineFrame.ontologyDirPath +
"ManOntology2.xml";
// Specify the List of Prolog+CG source files
that will be used.
// In this example, we will use only one file : /CGPrograms/citizenExple.prlg,
// So, we construct the full path for the file and we create an array of
String
// with this filePath (as the only element).
String filePath = PrologPlusCGGUIFrame.ppcgDirPath +
System.getProperty("file.separator") + "CGPrograms" + System.getProperty("file.separator")+"citizenExple.prlg";
String[] ppcgFilePaths = new String[1];
ppcgFilePaths[0] = filePath;
// Create an Instance of Prolog+CG Interpreter,
with the ontology file path
// and the array of Prolog+CG file paths. This creation will be concerned
by
// the parsing of all the specified files, in order to be ready for
answering
// requests.
Interpreter interpreter = new Interpreter(ontologyFilePath, ppcgFilePaths);
Lexicon lexicon = interpreter.getLexicon();
// Step 7 : Ask the interpreter to resolve the
desired request and
// to return all the solutions. The request is specified in a String.
// First request
ArrayList allSolutions = interpreter.findAllSolutions("[CITIZEN : x]<-memberOf-[COUNTRY : Oz].");
// Do whatever with the solutions. Here we print
the result.
txtArea.setText("");
HashMap aSolution;
if (allSolutions != null) {
for (Iterator i = allSolutions.iterator(); i.hasNext();) {
aSolution = (HashMap) i.next();
writeASolution(aSolution);
txtArea.append("\n");
}
System.out.println(txtArea.getText());
// Second request
allSolutions = interpreter.findAllSolutions("analogy(s, t1, t2).");
...
This code is taken from the Java class
PrologPlusCGFromJava. The complete path to the Java file is:
aminePlatform.samples.prologPlusCG.PrologPlusCGFromJava
Object-based side of Prolog+CG
An object of rules, called also a Prolog+CG object, is a collection of rules, that could belong to different paquets, and prefixed by goals with the same signature, the latter corresponds to the signature of the Object (of rules). Object of rules is a supplementary indexation mechanism of rules. The general form of a Prolog+CG Object (an Object of rules) is:
goal1::Rule1.
goal2::Rule2.
...
goalN::RuleN.
Note: goal1 to goalN have the same signature.
Now we can add a new kind of goal: Object Message or Prolog+CG Message (in contrast with "Java" message). An object message has the general form: goalA::goalB where goalA and goalB are two goals: goalB is resolved (interpreted) according to the content of the object (of rules) identified by goalA:
Object message = goalA::goalB
Consider for instance, the following Prolog+CG program: rules are indexed by identifiers (Leopard, Mammal, Carnivorous, Fact). All the rules with the same index, like the two rules for Carnivorous, belong to the same object. Note that the index can be a term (or even a CG), not only an identifier. A goal in the tail of a rule can be prefixed by such an index (like : Mammal::[Animal : x]-isA->[Mammal]). In this case, the goal is resolved (interpreted) according to the content of the object with the same index. This supplementary indexation is advantagous when several rules start with CG as their head. It plays the same role as the signature of a term in Prolog.
Note: To run this example, load the Ontology "samples/ontology/ManOntology2.xml".
Leopard::[Animal : x]-isA->[Leopard] :-
Mammal::[Animal : x]-isA->[Mammal],
Carnivorous::[Animal : x]-isA->[Carnivorous],
Fact::[Animal : x]-colorOf->[Color]-attr->[Wild],
Fact::[Animal : x]-partOf->[Component]-attr->[Dark].
Mammal::[Animal : x]-isA->[Mammal] :-
Fact::[Animal : x]-poss->[Hair].
Carnivorous::[Animal : x]-isA->[Carnivorous] :-
Fact::[Animal : x]<-agnt-[Eat]-obj->[Meat].
Carnivorous::[Animal : x]-isA->[Carnivorous] :-
Fact::[Animal : x]-poss->[Teeth]-attr->[Sharp],
Fact::[Animal : x]-poss->[Claw],
Fact::[Animal : x]-has->[Eyes]-attr->[Forward].
Fact::[Animal : Yala]-
<-pat-[BelongTo]-bnfcre->[Man : Robert],
-colorOf->[Color]-attr->[Wild],
-poss->[Teeth]-attr->[Sharp],
-has->[Eyes]-attr->[Forward].
Fact::[Animal : Yala]-poss->[Claw].
?- Carnivorous::[Animal : x]-isA->[Carnivorous].
{x = Yala};
no
?- Mammal::[Animal : x]-isA->[Mammal].
no
?- Leopard::[Animal : x]-isA->[Leopard].
no
?-
Prolog+CG Project: a collection of Prolog+CG Programs
A Prolog+CG project is a collection of Prolog+CG programs. Thus, instead of "putting" all the rules in one program, the user can express his/her application as a Prolog+CG project. Prolog+CG project is specified by the operator (re)consult which (re)consults a list of Prolog+CG programs and constitute one integrated program. The next section presents a prolog+CG application which is implemented as a Prolog+CG project.
An application: Natural Language Processing with Prolog+CG (prototype)
This section presents a small natural language processing application using Prolog+CG. It is composed of four Prolog+CG programs: a) Utilities where some "utility" goals are defined, b) Lexicon where the lexicon is defined, c) Analysis where integrated analysis (syntaxico-semantic analysis) of simple sentences is defined, and d) QuestionAnswering where question interpretation is treated. Figure 2 presents a snapshot of this application, using Prolog+CG GUI. Note that the above programs can be used in other applications. For instance, programs Utilities and Analysis can be used in other applications (like the application SHRDLU).
Figure 2: Prolog+CG GUI and Prolog+CG project
Note: To run this example, load the Ontology "samples/ontology/ManOntology2.xml".
Let us consider first some relevant points concerning our implementation of this NLP application: Integrated Analysis and the dynamic between syntax and semantic: The semantic of the whole sentence (that will be expressed as a CG G) is composed by successive jointure of "semantic fragments" (represented by CGs) that represent the semantic of the phrase component. So we start with an "empty" CG G and as parts of the input sentence are analyzed and their semantics (represented by CGs) are produced; these "semantic fragments" will specialize G. It is the same CG that is updated each time. The situation is similar to an incrementation: we have the same variable that is incremented (updated) several times. Specialize operation performs this update (update by maximal jointure) while maximalJoin operation returns a new CG as a result of the jointure of two CGs. That is why we use specialize operation instead of maximalJoin. By using specialize, we save space.
Let us consider now how this progressive semantic composition is guided (and constrained) by syntax. We will illustrate this process with the case of declarative sentence analysis.
In general, the semantic of the verb constitutes the kernel for semantic composition; it provides the "main pattern" that will be specialized by the jointure of successive "semantic fragments" as they are produced by analysis of sentence parts. Consider the general pattern (a case frame) for the semantic of a verb (Figure 3): Figure 3 presents a case of sentence composed of a first NP (Noun Phrase), followed by a verb, then by a second NP, then by a preposition and then by third NP. NP1 plays the role of subject, NP2 the role of COD and NP3 of a complement (the nature of this complement depends on the preposition and on the meaning of the verb and the complement). Each NP has a header which is the main noun in the NP. The header for a VP is its main verb. This notion of header is important in linguistic and also in computational linguistic (what we are doing now !): it helps to determine how to join "semantic fragments" that were produced from the analysis of sentence parts. In our case, where CG is used to represent semantic, headers are used as entry points for the jointure operation (see below). Figure 3 presents also the associated syntaxic structure of the sentence (a tree) with highlighting the sub-trees of the three NPs. It presents also the general structure of the semantic structure of the sentence, expressed as a CG.
Figure 3: Integrated Analysis of a simple declarative sentence
When the first NP1 is read, its sub-tree is constructed, as well as its semantic representation (a CG). The NP1 is interpreted as the subject (in the syntaxic tree) and as the agent of the action (that corresponds to the verb). When the verb is read and its semantic structure is brought, the latter should be joined with the semantic of NP1. But how ? it should be join as a subject/agent; the "semantic of NP1" corresponds to the agent of the action. So we have to locate the concept that represents the action and then follow the relation agent to locate the concept E_Agent which corresponds to the semantic representation (a concept) of the header of NP1 (Figure 4). In this way, we will have two entry points for the jointure: the first entry point (E_NP1) which corresponds to the semantic representation (a concept) of the header of NP1 (this concept is in the CG that represents the semantic of NP1) and the second entry point is E_Agent. Figure 4 illustrates this treatment.
Figure 4: Entry points identification and jointure of semantic fragments
A similar treatment is done for the integrated analysis of NP2 (COD) and NP3 (Complement) and for other components (like other complements). Now, to complete the presentation, let us consider how the above treatment is implemented in Prolog+CG (and in this application in particular):
From grammatical/syntaxical rule to Prolog+CG rule(s). Syntaxical rule specifies only the syntaxical structure of a syntaxical category (S, NP, VP, etc.), the corresponding Prolog+CG rules will add to this syntaxical structure two other functions: recognition of the category (from the input sentence) and the composition/construction of its semantic (expressed in general as a CG). An example will illustrate (better) this point:
The syntaxical rule: interrogative_sentence = "why" + Aux + DeclarativeSentence + QuestionMark.
The corresponding Prolog+CG goal (the associated rules are provided below):
interrogative_sentence(["why", _aux |P], [T_TypeOfSentence = G]-modalityOf->[Modality = interrogative]-attr->[Why])
Note the formulation of the goal parameters (which are patterns): the first parameter is a pattern of the input sentence, expressed as a list of words. This pattern specifies what and how words will be recognized and "consumed" by the current goal. The second parameter specifies a general pattern for the semantic of the sentence, expressed as CG. It specifies the type of the sentence (Modality = interrogative) and in the case of interrogative sentence, the type of the request (here, the type is: Why). The kernel of the request is expressed by the concept [T_TypeOfSentence = G] in the CG pattern. The variable T_TypeOfSentence will specify the type of the kernel (state or action) and the variable G the content, expressed as a CG.
The form of NLP goals and the notion of Header. Let us consider the goal NP(P, P1, G_NP, E_NP) which is typical in this application. The function of NP/4 is to recognize (consume) an NP and to return its semantic expressed in the two parameters: G_NP, E_NP. P represents (what remains from) the input sentence and P1 represents what remain from the input sentence minus the NP (P = NP + P1). G_NP represents the semantic of the NP expressed as a CG and E_NP represents a concept in G_NP that corresponds to the Header of the NP.
Consider now the following rule for the goal ActiveVP(P, P1, G_SUBJ, E_SUBJ, G) which receives the semantic of the subject (NP1): G_SUBJ, E_SUBJ . The function of ActiveVP/5 is to analyze the rest of the sentence; analyze the verb, the COD (if any) and the complement(s). This is done by the goal ActiveVP(P, P1, G) which returns (a CG G) the semantic of this part. Then, it has to join the semantic of the subject (G_SUBJ with entry point E_SUBJ) with the semantic of the rest of the sentence (G). To get the entry point for G, we use the primitive goal branchOfCG to locate, in G, the branch B_Branch with relation type agent (the Figure illustrates the localization of the branch).
Once located (variable B_Branch refers to the branch), we use "Java" message B_Branch:getTargetConcept() to get its target concept (E_G_SUBJ). This concept is then used as the entry point for G in the jointure of G with G_SUBJ (note that the jointure is made by specialize, not maximalJoin).
ActiveVP(P, P1, G_SUBJ, E_SUBJ, G) :-
ActiveVP(P, P1, G),
branchOfCG(B_Branch, [Action]-agnt->[X], G),
E_G_SUBJ is B_Branch:getTargetConcept(),
G:specialize(E_G_SUBJ, G_SUBJ, E_SUBJ).
After this brief introduction of the approach used to implement NLP using Prolog+CG, we provide now the code of the three Prolog+CG files (that compose the NLP application) with some comments.
The content of the program Lexicon: The lexicon is simple. Our goal is to provide an idea (a prototype) of how a NLP can be implemented using Prolog+CG, not a complete specification of a NLP lexicon.
// lexicon(word, syntaxic_category, type_or_concept_or_canon_or_situation)
lexicon("push", verb, [Push]-
-agnt->[Human],
-obj->[Object],
-on->[Object]).
lexicon("create", verb, [Create]-obj->[Object]-colorOf->[Color]).
lexicon("drive", verb, [Human]<-agnt-[Drive]-dest->[Place]).
// several meaning of "open"
lexicon("open", verb, [Human]<-agnt-[Open]-obj->[OpenableObject]).
lexicon("open", verb, [Key]<-agnt-[Open]-obj->[Door]).
lexicon("open", verb, [Open_Box]<-agnt-[Open]-obj->[Box]).
lexicon("open", verb, [Shop]<-pat-[Open]-
-obj->[Door],
-ptime->[Time]).
lexicon("is", aux_verb, _).
lexicon("did", aux_verb, _).
lexicon("pyramid", noun, Pyramid).
lexicon("cube", noun, Cube).
lexicon("sphere", noun, Sphere).
lexicon("man", noun, Man).
lexicon("woman", noun, Woman).
lexicon("john", noun, [Man : John]).
lexicon("store", noun, Store).
lexicon("openbox", noun, Open_Box).
lexicon("key", noun, Key).
lexicon("box", noun, Box).
lexicon("door", noun, Door).
lexicon("book", noun, Book).
lexicon("shop", noun, Shop).
lexicon("store", noun, Shop).
lexicon("why", noun, Why).
lexicon("twelve", noun, [Time = twelve]).
// lexion(word, adj, relation_type, Type, Value)
lexicon("small", adj, sizeOf, Size, small).
lexicon("big", adj, sizeOf, Size, big).
lexicon("red", adj, colorOf, Color, red).
lexicon("blue", adj, colorOf, Color, blue).
// lexion(word, prep, relation_type)
lexicon("on", prep, on).
lexicon("under", prep, under).
lexicon("left", prep, left).
lexicon("right", prep, right).
lexicon("to", prep, dest).
// lexicon(word, art, variable). Article is not treated in detail in this prototype
lexicon("the", art, x).
lexicon("a", art, x).
lexicon(".", punctuation, _).
lexicon("!", punctuation, _).
lexicon("?", punctuation, _).
Note: In this implementation of lexicon, meaning of words is specified in a Prolog+CG program (Lexicon). It is also possible (and maybe better) to specify the meaning in the ontology. Here is an implementation of this second approach:
lexicon("push", verb, Push). // only the concept type is specified
lexicon("create", verb, Create).
lexicon("open", verb, Open).
...
The goal lexicon/4, defined below, searches the lexicon entry (in the above paquet) for the word according to its syntaxic category in order to get its type. For the type T, the goal getSemantic/2 will get, from the ontology, a "meaning" for T (i.e. to get Conceptual Structures associated to T like canon and situations):
lexicon(_verb, _syntaxicCategory, _Type, _Sem) :-
lexicon(_verb, _syntaxicCategory, _Type),
getSemantic(_Type, _Sem).
getSemantic/2 tries first to get the canon of the type, from the ontology. It will get also situation(s) of the type. The first is done with the "Java" message: _Type:getCanon() and the second with the "Java" message: _Type:getSituationsDescription(). Note that both methods getCanon() and getSituationsDescription() are methods of the Conceptual Structure Type (see Ontology). getSituationsDescription() returns null (if there is no situation) or an Enumeration. We use another "Java" message to convert the Enumeration into an AmineList. Then we use member to get one situation at a time.
Note the "openess", with "Java" message, of Prolog+CG to Amine APIs and to Java.
getSemantic(_Type, _Sem) :-
_Sem is _Type:getCanon(),
dif(_Sem, null).
getSemantic(_Type, _Sem) :-
_EnumSitDescr is _Type:getSituationsDescription(),
dif(_EnumSitDescr, null),
_ListSitDescr is "aminePlatform.util.AmineObjects":enumeration2AmineList(_EnumSitDescr),
member(_Sem, _ListSitDescr).
The content of the program Analysis: Analysis performs an integrated analysis (syntaxico-semantic analysis) of simple sentences: imperative, interrogative (just one specific type) and declarative sentences. For the latter, we differentiate between stative (description of a state) from active (description of an action) sentences.
nlp :- // read a sentence P and write its "meaning" expressed as a CG G
readSentence(P),
analyze_sentence(P, G),
writeln(G), !.
analyze_sentence(P, G) :-
imperative_sentence(P, G), !.
analyze_sentence(P, G) :-
interrogative_sentence(P, G), !.
analyze_sentence(P, G) :-
declarative_sentence(P, G).
// interrogative_sentence = "why" + Aux + DeclarativeSentence + QuestionMark. % only a single case is considered here
interrogative_sentence(["why", _aux |P], [T_TypeOfSentence = G]-modalityOf->[Modality = interrogative]-attr->[Why]) :-
lexicon(_aux, aux_verb, _),
NP(P, P1, G_NP, E_NP),
stativeOrActiveVP(P1, ["?"], G_NP, E_NP, T_TypeOfSentence, G).
imperative_sentence(P, [Action = G]-modalityOf->[Modality = imperative]) :- // imperative_sentence = ActiveVP
ActiveVP(P, G).
// declarative_sentence = NP stativeOrActiveVP
declarative_sentence(P, [T_TypeOfSentence = G]-modalityOf->[Modality = declarative]) :-
NP(P, P1, G_NP, E_NP),
stativeOrActiveVP(P1, ["."], G_NP, E_NP, T_TypeOfSentence, G).
stativeOrActiveVP(P, P1, G_NP, E_NP, State, G) :- // stativeOrActiveVP = (isVerb StativePart) | ActivePart
isVerb(P, R),
stativePart(R, P1, G_NP, E_NP, G), !.
stativeOrActiveVP(P, P1, G_NP, E_NP, Action, G) :-
ActiveVP(P, P1, G_NP, E_NP, G).
ActiveVP(P, P1, G_SUBJ, E_SUBJ, G) :-
ActiveVP(P, P1, G),
branchOfCG(B_Branch, [Action]-agnt->[X], G),
E_G_SUBJ is B_Branch:getTargetConcept(),
G:specialize(E_G_SUBJ, G_SUBJ, E_SUBJ).
ActiveVP([V|P1], P3, G) :-
Verb(V, G_lexicon),
G is G_lexicon:copy(),
cod(P1, P2, G),
complement(P2, P3, G).
cod(P1, P2, G) :-
NP(P1, P2, G_COD, E_NP1),
branchOfCG(B_Branch, [T_Verb]-obj->[X], G),
E_GV_COD is B_Branch:getTargetConcept(),
G:specialize(E_GV_COD, G_COD, E_NP1), !.
cod(P, P, G).
complement([v], [v], G) :- !.
complement(P, P2, G) :-
Prep(P, P1, s_prep), // the "semantic" of a preposition is a relation type, represented by the variable s_prep
NP(P1, P2, G_NP, E_G_NP),
branchOfCG(B_Branch, [T_Verb]-s_prep->[X], G), // note the use of the variable s_prep to represent the relation type
E_COD is B_Branch:getTargetConcept(),
G:specialize(E_COD, G_NP, E_G_NP).
// stativePart = Adjective. example: Ahmed is black
// stativePart([A|P1], P1, G_NP, E_NP, G): check if the first word A is an adjective and returns the rest P1. It receives also the semantic of the NP analyzed before (G_NP and its header E_NP). This definition of stativePart has to join the semantic of the adjective with the semantic of the previous NP. First, it constructs the semantic of the adjective, then the jointure is done.
stativePart([A|P1], P1, G_NP, E_NP, G) :-
Adj(A, R1, T1, V1), !, // recognize an adjective and returns its semantic: Relation_Type, Type, Value
eq(E_NP, [N : A1]), // Get the type and the designator of the concept E_NP
eq(G, [N : A1]-R1->[T1 = V1]), // construct a CG that represents the semantic of the adjective.
ex: [Man:Ahmed]-colorOf->[Color = Black]. The noun ([Man:Ahmed]) corresponds to the header of
the previous NP: E_NP = [N : A1]
branchOfCG(B, [N : A1]-R1->[T1 = V1], G),
E_N is B:getSourceConcept(),
G:specialize(E_N, G_NP, E_NP).
// stativePart = NP. example: Ahmed is a big man
stativePart(P, P1, G_NP, E_NP, G_NP) :-
NP(P, P1, G_NP1, E_NP1),
G_NP:specialize(E_NP, G_NP1, E_NP1).
// NP = Article + (Adjective)* + Noun + (Adjective)*.
NP(P, P1, G, E) :-
Art(P, P2, A1),
AdjsSynt(P2, P3, L_Adjs),
Noun(P3, P4, N),
suiteNP(P4, P1, N, A1, L_Adjs, G, E), !.
suiteNP([N1|P1], P1, N, A1, L_Adjs, G, E) :-
not(lexicon(N1, _, _)),
not(lexicon(N1, _, _, _, _)),
SemAdjs(L_Adjs, N, N1, G, E), !.
suiteNP(P4, P1, N, A1, L_Adjs, G, E) :-
SemAdjs(L_Adjs, N, A1, S, E1),
AdjsSynt(P4, P1, L_Adjs2),
SemAdjs(L_Adjs2, N, A1, S1, E11),
maximalJoin(S, E1, S1, E11, G, E).
maximalJoin(G1, E1, G2, E2, G3, E3) :-
_resMatchCG is G1:maximalJoin(E1, G2, E2),
G3 is _resMatchCG:getCG(),
E3 is _resMatchCG:getConcept().
AdjsSynt([A|P], P1, [A|L_Adjs]) :-
lexicon(A, adj, _, _, _),
AdjsSynt(P, P1, L_Adjs), !.
AdjsSynt(P, P, []).
SemAdjs(L_Adjs, [T:D], A, S, E) :-
SemAdjsBis(L_Adjs, T, D, S, E), !.
SemAdjs(L_Adjs, N, A, S, E) :-
SemAdjsBis(L_Adjs, N, A, S, E).
SemAdjsBis([A|P], N, A1, S, E_N_S) :-
Adj(A, R1, T1, V1),
eq(G, [N : A1]-R1->[T1 = V1]),
branchOfCG(B, [N : A1]-R1->[T1 = V1], G),
E_N is B:getSourceConcept(),
SemAdjs2(P, G, E_N, N, A1, S, E_N_S), !.
SemAdjsBis([], N, A1, G, E) :-
eq(C, [N : A1]),
G is C:toCG(),
concOfCG(E, [N : A1], G).
SemAdjs2([A|P], G, E_N, N, A1, S, E_S) :-
Adj(A, R, T, V),
eq(G1, [N : A1]-R->[T = V]),
branchOfCG(B, [N : A1]-R->[T = V], G1),
E_N1 is B:getSourceConcept(),
maximalJoin(G, E_N, G1, E_N1, G2, E_N2),
SemAdjs2(P, G2, E_N2, N, A1, S, E_S), !.
SemAdjs2([], G, E, _, _, G, E).
Verb(v, G) :- lexicon(v, verb, G).
isVerb(["is"|P], P).
Prep([v|P], P, T) :- lexicon(v, prep, T).
Art([v|P], P, T) :- lexicon(v, art, T), !.
Art(P, P, undefined).
Noun([v|P], P, T) :- lexicon(v, noun, T).
Adj(A, R, T, V) :- lexicon(A, adj, R, T, V).
Here is some examples of execution (note that the program QuestionAnswering is not necessary for this execution) (Figure 6):
Figure 6: Examples of Execution
The content of the program QuestionAnswering:
// The meaning (semantic) of the story is represented by a (compound) CG which is an overlap of three structures (nets) :
// a) temporal structure represented here by "after" relation which specify the temporal succession of actions, events, and states.
// b) causal structure representedd here by "cause" relation
// c) intentional structure represented here by "motivationOf" and "reason" relations.
// Start of the representation of the semantic of the story
story(
[Action #act1 = [Time : Early]<-time-[WakeUp]-pat->[Man: John]]-after->
[State #stt1 = [Hungry]-pat->[Man: John]]-after->
[Action #act2 = [Store]<-dest-[Drive]-agnt->[Man: John]]-after->
[Action #act3 = [Man:John]<-obj-[See]-
-agnt->[Woman: Mary],
-loc->[Store]]-after->
[Action #act4 = [Man:John]<-dest-[WaveAt]-agnt->[Woman: Mary]]-after->
[Action #act5 = [Man:John]<-agnt-[Buy]-obj->[OatMeal]]-after->
[Event #evt1 = [Man:John]<-agnt-[Drop]-
-obj->[OatMeal],
-loc->[Outside]]-after->
[Event #evt2 = [Trunk]<-dest-[Spill]-pat->[OatMeal]]-after->
[Action #act6 = [OatMeal]<-obj-[Put]-
-agnt->[Man: John],
-in->[Container]]-after->
[Action #act7 = [OatMeal]<-obj-[Cook]-agnt->[Man: John]]-after->
[Action #act8 = [OatMeal]<-obj-[Eat]-agnt->[Man: John]]
[State #stt1]<-reason-[Goal = [Action = [Food]<-obj-[Eat]-agnt->[Man: John]]]-
<-reason-[Action #act2],
<-reason-[Action #act5],
<-reason-[Action #act7],
<-reason-[Action #act8]
[Action #act3]<-motivationOf-[Goal = [Action = [Man:John]<-dest-[Greet]-agnt->[Woman: Mary]]]<-reason-[Action #act4]
[Event #evt1]-
-cause->[State #stt2 = [ParkingLot]<-pat-[Slick]],
<-cause-[Event #evt2]
). // The end of the formulation of the semantic of the story (one compound CG as argument of term Story/1).
questionAnswering(_sentence, _answer) :- // Example: questionAnswering("why did john drive to the store ?", _answer).
readSentence(_sentence, _question),
interrogative_sentence(_question, [T = _kernelOfQuest]-modalityOf->[Modality = interrogative]-attr->[_questionType]),
answer(_questionType, _kernelOfQuest, _answer).
// We consider here only one kind of request: why question
answer(Why, _kernelOfQuest, _answer) :-
answerWhy(_kernelOfQuest, _answer).
answerWhy(A, Y) :-
story(_story),
branchOfCG(B, [T = G]<-reason-[T2 = A], _story), // to answer why A, search branch associated to A with relation type "reason"
reason([T = G], Y).
reason(X, X).
reason([T = G], Y) :- // recursive definition of search of reason to explore a path of reasons
story(_story),
branchOfCG(B, [T1 = G1]<-reason-[T = G], _story),
reason([T1 = G1], Y).
Here is an example of execution (Figure 7):
Figure 7: An example of Q/A
Prolog+CG Program = Rule* .
Rule = Goal [":-" Goal {"," Goal}*] "." .
Goal = Java_Message | (SimpleGoal ["::" SimpleGoal]) .
SimpleGoal = Identifier | String | Variable | Term | CG .
Java_Message = (Identifier | String | Variable) ":" (Term | Variable) .
Note: The operator "!" is considered as an Identifier. Categories: Identifier, String, Variable, Term and CG are defined Structures and CG documents. Some constraints hold in the formulation of Goal. For instance, a variable and a Java_Message can not be a header of a rule.
Figure 8 presents the packages and classes in Amine that concern Prolog+CG.
Figure 8: packages and classes in Amine that concern Prolog+CG
Here is some details on components of Prolog+CG language/interpreter and Prolog+CG GUIs, implemented as Java packages.
Prolog+CG language/interpreter components/packages:
Prolog+CG language/interpreter is composed of four components (implemented as packages): util, parser, bindingContext and interpreter.
util. This component is implemented as util package, it contains the definition of basic Prolog+CG structures: rule and paquet of rules. A paquet of rules is implemented by the class Rules which extends Java ArrayList (i.e. an ArrayList of Rules). A rule is implemented by the class Rule which extends Java ArrayList (i.e. an ArrayList of Goals). A goal is either an identifier, a variable, a string, a term, a CG, a "Java" message (implemented as a Term), or a Prolog+CG message (implemented as a Term). The class Goal in package util provides static methods concerning Goal.
parser. This component is implemented as parser package, it contains the class PPCGParser which is responsible for the parsing of Prolog+CG program. This class parses a Prolog+CG program and produces a HashMap of <Key:Signature, Value:Rules>; each Rules represents a paquet of rules and Signature represents the signature of the paquet. PPCGParser extends the class ObjectParsing which is responsible for the parsing of Amine Objects.
bindingContext. This component is implemented as bindingContext package, it contains the class PPCGBindingContext which implements BindingContext interface. The methods of this interface (getValue(), setValue(), isFreeVariable(), etc.) are implemented according to the use of the unification stack. This stack is implemented by UnificationStack class which extends Stack class. An element of this class is an object of UnifRecord class which extends HashMap class (with Key:Variable and Value:LstOfConstraints). A value is an object of LstOfConstraints class which is composed of an object (with its binding information) that represents the value of the variable (once determined) and a list of constraints (an ArrayList), each constraint corresponds to ObjectWithBindInf object (Figure 9).
Figure 9: Unification stack and the associated (sub-)structures
Let us consider now how this organization helps in storing information that result from the unification operation (this is done with the method setValue()) and also how it helps in getting the value of a variable (this is done with the method getValue()).
Resolution process of Prolog+CG adopts sharing of structures and renaming of variables (in the structures to unify). Renaming consists in the association of an index (i.e. a level in the UnificationStack: an Integer) to a variable. Unification of two goals involves (in general) a push of an UnifRecord in UnificationStack. UnifRecord stores for each variable (involved in the unification of the two goals), its value and the LstOfConstraints associated to it. Binding information for an object (especially structured objects like AmineList, Term, CG, Concept and Relation that can contain variables) corresponds, in Prolog+CG programming context, to an index or an integer that represents a level in the Unification stack. Based on these decisions and on the above structures (UnificationStack, UnifRecord, LstOfConstraints, ...), PPCGBindingContext implements methods getValue() and setValue() which are the main methods in BindingContext interface.
setValue(): The unification operation attempts to unify two "elements" (an element can be a variable, a primitive object or a structured object like List, Term and CG). During its execution, unification stores constraints in the unification stack. A constraint represents a binding of a (free) variable with an object or with another (free) variable. If the unification constraint concerns two free variables V1 and V2, then two cases are considered: a) at least one of the two variables V1 and V2 belong to the current UnifRecord. In this case, V2 is added (as a new constraint) in the LstOfConstraints of V1, and V1 is added in the LstOfConstraints of V2, b) none of the two free variables V1 and V2 belong to the current UnifRecord. In this case, a new system variable $V is created in the current UnifRecord and the constraint V1 = V2 is split in two constraints V1 = $V and $V = V2 that are added the unification stack. If the unification constraint concerns a value val and a variable V1 (val = V1), then a new system variable $V is created and a new entry in the current UnifRecord is added (for the variable $V with value val). Variable V1 is added as a constraint in the LstOfConstraints of $V. Then the constraint $V = V1 is considered, i.e. unifRecord for V1 is located and then the entry for V1 (in the located unifRecord) and then the variable $V is added as a constraint in the LstOfConstraints of V1. If the unification constraint concerns a variable V1 and a value val (V1 = val), a similar treatment is done.
For more detail, consult the source code of setValue() and the other methods, in the class PPCGBindingContext.
getValue(): to get the value of a variable V1, we look first at the value associated to V1 (in LstOfConstraints of V1). If value is null then we check if LstOfConstraints of V1 contains a system variable (as constraint). If yes, the entry for this variable is located and the associated value is returned. Otherwise, we apply a recursive call to getValue() on the variables that are bound to V1 (variables contained in LstOfConstraints of V1).
For more detail, consult the source code of getValue() in the class PPCGBindingContext.
interpreter. This component is implemented as interpreter package, it contains the classes Interpreter, Resolution, ResolutionStack, GoalToResolve and other secondary classes. Interpreter class extends the class Resolution: the latter implements a pure Prolog+CG interpreter (the resolution process); without primitives and without "Java" messages. The class Interpreter extends Resolution by adding the interpretation of primitives and the interpretation of "Java" messages.
Resolution process (implemented by the class Resolution) uses three staks: resolution stack that contains goals to resolve (or satisfy), backtrack stack that enables the treatment of backtracking, and unification stack. Resolution starts by pushing the goals that compose the request in the resolution stack. Resolution finds a solution when resolution stack is empty. Resolution process can backtrack to look for other solutions. Resolution is an iterative treatment that considers at each iteration the goal at the top of the resolution stack (such a goal becomes the current goal to resolve). The current goal may be a variable, a primitive goal or a defined goal:
The current goal is a variable. Pop the current goal (the variable) from resolution stack and push it in the backtrack stack, construct a GoalToResolve object for the new goal (which is composed of the value of the variable) and push it in the resolution stack,
The current goal is a primitive. if the primitive is executed with success then pop the current goal (the primitive goal) from resolution stack and push it in the backtrack stack. Otherwise, resolution process will initiate a backtrack (the simple case of this operation is to pop the top of backtrack stack and push it in resolution stack, and also to pop the top of unification stack).
The current goal is a defined goal. If the goal will be satisfied for the first time, then search the paquet that is associated to the signature of the current goal. Resolution process will then scan the rules in the paquet to locate the first rule with a head that can unify the current goal. Thus, unification is called several times to do the test, each time a new UnifRecord is created to store binding information that result from the unification. If it succeeds, then the UnifRecord will remain in the unification stack and the resolution process continue its treatment, otherwise the UnifRecord is destroyed and a new unification test is attempted. So, if the unification of the head of a rule with the current goal succeeds, then the resolution process pop the current goal from the resolution stack and push it in the backtrack stack, and it push goals that compose the tail of the rule in the resolution stack.
For more detail, consult the source code of the class Resolution.
Prolog+CG GUIs components/packages
Prolog+CG GUIs is composed of three components (implemented as packages): Prolog+CG GUI, console and debuger.
Prolog+CG GUI. This component is implemented as prologPlusCGGUI package, it contains classes (i.e. PrologPlusCGGUI, PrologPlusCGGUIFrame, etc.) that are used in the composition of the GUI.
Console. This component is implemented as console package, it contains the class ConsoleFrame for the display and the management of the console, especially the Q/A process.
Debuger. This component is implemented as debuger package, it contains classes (i.e. Debuger, InferenceTree and InfTreeFrame, etc.) that are used in the composition of the Debuger.
More details on the implementation of Prolog+CG components will be provided later (in future releases).
References
[1] Kabbaj A., Un système multi-paradigme pour la manipulation des connaissances utilisant la théorie des graphes conceptuels, Ph.D Thesis, DIRO, U. de Montréal, Juin, 1996.
[2] A. Kabbaj, B. Moulin, J. Gancet, D. Nadeau and O. Rouleau, Uses, improvements and extensions of Prolog+CG : Case studies, in 9th Int. Conf. on Conceptual Structures, ICCS'2001, Springer-Verlag, 2001.
A. Kabbaj, Development of Intelligent Systems and Multi-Agents Systems with Amine Platform, in 15th Int. Conf. on Conceptual Structures, ICCS'2006, Springer-Verlag, 2006.