Details
on Context Binding and Unification
by
Adil KABBAJ
Creating a binding context that implements BindingContext Interface
Identifying the binding context as a "primitive concept" in Amine algebraic level constitutes a relevant feature and a key step toward more genericity:
It is relevant since generic structures (structures with variables) are essential in any programming context and they are essential in Amine. Also, the notions of variable binding and binding context where binding resolution is performed are clearly presented and specified in Amine.
Binding context in Amine constitutes a key step toward more genericity since BindingContext is defined in Amine as a Java interface, i.e. as a specification or a contract between Amine and any Java class that pretends to implement a binding context. In this way, Amine is not committed to a specific binding context; any Java class that implements BindingContext interface can considered by Amine as a binding context. For instance, the two Amine's programming languages PROLOG+CG and SYNERGY will implement BindingContext. In the present document, we present an example where a simple binding context is defined and used.
We start by defining a simple binding context (or environment) that implements BindingContext interface. Then we consider examples of Term, AmineList, Concept, Relation, and CG unification that use this simple binding context. All these examples are extracted from the Java class BindingContextTest.
Creating a binding context that implements BindingContext Interface
This section presents a specific binding context that implements BindingContext interface. This specific and simple context is defined as a Java class: SimpleBindingContext, provided in the source code at aminePlatform.test.util. It is also available from the web site at BindingContextTest. Parts of the code of SimpleBindingContext is reported here to give the reader a precise idea of what is meant by binding context and how it can be implemented.
SimpleBindingContext is a simple context: binding (association of a value to a variable) occurs in general due to an explicit assignment of a value to a variable or due to an unification of two structures. During unification, elements of both structures can be binded. And since both structures can contain variables with the same identifier, our solution here is to use two HashMaps (varsOfObj1 and varsOfObj2), one for each structure and each hashMap records the binding of the variables of the corresponding structure.
Hence, SimpleBindingContext is a binding context that is composed of two HashMaps, with the implementation of the BindingContext interface, i.e. the implementation of the two methods getValue() and setValue():
- getValue(Object obj, Object bindInf): the first parameter (obj) can be a variable or any other Amine object. If obj is not a variable, it is returned as such. The second parameter (bindInf) represents the binding information concerning obj. In the current example, bindInf is simply a Boolean that indicates to which structure obj belongs: if bindInf is true, then obj belongs to the first structure and so, getValue() has to search the value of obj in the first HashMap varsOfObj1. If bindInf is false then obj belongs to the second structure and so, getValue() has to search the value in the second HashMap varsOfObj2.
- setValue(Variable variable, Object value, Object bindInf): associates the specified value to the specified variable. The new association will be inserted in the HashMap varsOfObj1 or in the HashMap varsOfObj2 depending on the value of bindInf.
Specific methods of SimpleBindingContext are:
- getResultSet(Object bindInf): return HashMap varsOfObj1 or varsOfObj2 depending on the value of bindInf (true or false),
- printResultSet(HashMap resultSet): print the content of the specified HashMap. Another variant of this method has Lexicon as a second parameter.
- clear(): clear the content of the two HashMaps.
public class
SimpleBindingContext implements
BindingContext {
private HashMap varsOfObj1 = new HashMap();
private HashMap varsOfObj2 = new HashMap();
/***************** Implement BindingContext Interface
*******/
public ObjectWithBindInf getValue(Object
obj, Object bindInf) {
Object value = null;
if (!Variable.isVariable(obj))
value = obj;
else if (((Boolean) bindInf).booleanValue())
value = varsOfObj1.get(obj);
else value = varsOfObj2.get(obj);
if (value == null)
return null;
else return new ObjectWithBindInf(value, bindInf);
}
public void setValue(Variable variable,
Object value, Object bindInf) {
if (((Boolean) bindInf).booleanValue())
varsOfObj1.put(variable, value);
else varsOfObj2.put(variable, value);
}
// ... other methods proper to
SimpleBindingContext
}
SimpleBindingContext is used as the BindingContext in BindingContextTest where unification of Term, AmineList, Concept, Relation, and CG are illustrated. Following sections are based on excerpts from BindingContextTest.
-> Example 1: unification of two simple terms
Here is an excerpt from BindingContextTest where two terms (term1 and term2) are parsed, a SimpleBindingContext is created, and then unification operation is applied on term1 and term2. This SimpleBindingContext will be used as the (common) binding context, for all the examples.
If unification of term1 and term2 succeeds, we print the result ; associations variable/value. In this example, only term2 that contains variables. Thus we are concerned by associations for its variables only.
Term term1 = (Term) Term.parse("pere(Ahmad,
Karim)");
System.out.println("\nA simple term term1 : " + term1);
Term term2 = (Term) Term.parse("pere(x,
Karim)");
System.out.println("\nA simple term term2 : " + term2);
SimpleBindingContext bindContext = new
SimpleBindingContext();
Boolean bindInf1 = new Boolean(true);
Boolean bindInf2 = new Boolean(false);
boolean unify = term1.unify(bindContext,
bindInf1, term2, bindInf2);
if (unify)
bindContext.printResultSet(bindContext.getResultSet(bindInf2));
Result of the code above is:
A simple term term1 : pere(Ahmad, Karim)
A simple term term2 : pere(x, Karim)
x = Ahmad
-> Example 2: unification of complex terms
Let us assume the next two terms:
term1: pere(Ahmad, x1, address(15, road_casa, rabat))
term2: pere(y1, Karim, y2)
And the following statements (note that we use the same bindContext, but we clear previous bindings. Also, we print the binding for variables of the two terms):
bindContext.clear();
// use the same binding context,
but clear previous bindings
unify = term1.unify(bindContext,
bindInf1, term2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1));
bindContext.printResultSet(bindContext.getResultSet(bindInf2));
}
Result of the code above is:
x1 = Karim
y2 = address(15, road_casa, rabat)
y1 = Ahmad
-> Example 3: unsuccessful unification
Let us assume the next two terms:
term1: pere(Ahmad, x1, address(15, road_casa, rabat))
term2: pere(y1, Karim, y1)
And the following statements:
bindContext.clear();
// use the same binding context,
but clear previous bindings
unify = term1.unify(bindContext,
bindInf1, term2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1));
bindContext.printResultSet(bindContext.getResultSet(bindInf2));
}
Result of the code above is:
The two terms can not be unified
The unification of term1 and term2 is not possible since y1 is involved in two contradictory binding: y1 = Ahmed and y1 = address(15, road_casa, rabat).
-> Example 4: unification of terms, lists with variable constructor, and sets
Let us assume the next two terms (note the use of variable constructor in term1):
term1: term(Ahmad, [1, candy, hello|x], address({rabat, 15, road_casa}))
term2: term(y1, [1, candy, hello, 45, sos], address(y2))
And the following statements:
bindContext.clear();
// use the same binding context,
but clear previous bindings
unify = term1.unify(bindContext,
bindInf1, term2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1));
bindContext.printResultSet(bindContext.getResultSet(bindInf2));
}
Result of the code above is:
x = [45, sos]
y2 = {rabat, 15, road_casa}
y1 = Ahmad
-> Example 5: unification of terms, lists with variables constructors, and sets
Let us assume now the next two terms:
term1: term(Ahmad, [1, candy, hello|x], address({rabat, 15, road_casa}))
term2: term(y1, [1,y2,hello,45,sos|y3], address({15,road_casa,rabat,maroc}))
And the following statements:
bindContext.clear();
// use the same binding context,
but clear previous bindings
unify = term1.unify(bindContext,
bindInf1, term2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1));
bindContext.printResultSet(bindContext.getResultSet(bindInf2));
}
Result of the code above is:
x = [45, sos|y3]
y2 = candy
y1 = Ahmad
-> Example 6: Terms, lists, and concepts unification
We provide the whole code for this example. Note that an ontology is loaded first; it is required to treat concepts. In this example, first argument of the two terms are concepts: [Man :x1 =descr(45, teacher)] and [Person :imad = y1] respectively.
//
Load the Lexicons/Ontology that is stored in the specified file.
Ontology ontology = Ontology.load("C:/ManOntology.ont");
Lexicon mainLexicon = ontology.getMainLexicon();
CG.setCanonicity(false);
term1 = (Term) Term.parse(
"term([Man
:x1 =descr(45, teacher)], [1, candy, hello|x2])",
mainLexicon);
System.out.println("\nA simple term term1 : " +
term1.toString(mainLexicon));
term2 = (Term) Term.parse(
"term([Person
:imad = y1], [1, y2, hello, 45, sos|y3])",
mainLexicon);
System.out.println("\nA simple term term2 : " +
term2.toString(mainLexicon));
bindContext.clear();
unify = term1.unify(bindContext,
bindInf1, term2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two terms can not be unified");
Result of the code above is:
A simple term term1 :
term([Man :x1 = descr(45, teacher)], [1, candy, hello|x2])
A simple term term2 :
term([Human :imad = y1], [1, y2, hello, 45, sos|y3])
x2 = [45, sos|y3]
x1 = imad
y2 = candy
y1 = descr(45, teacher)
Unification of the two concepts [Man :x1 =descr(45,teacher)] and [Person :imad =y1] is possible with x1 = imad and y1 = descr(45, teacher).
-> Example 7: unification of two lists of
concepts
Let us assume the next two lists of concepts (note that we specify the pseudo-designator #0 in order to indicate that [Man] is a concept, not a list):
list1 : [[Man:x], [Man #0], [Woman:Sue], [Woman:Sue]]
list2: [[Man:imad], [Man:imad], [Woman:y], [Woman #0]]
And the following statements:
bindContext.clear();
unify = list1.unify(bindContext,
bindInf1, list2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two lists can not be unified");
Result of the code above is:
x = imad
y = Sue
This example illustrates various cases of concept's unification. For instance, unification of [Man #0] (which is equivalent to [Man]) with [Man:imad] is possible. A concept with no designator (like [Man]) is equivalent to a concept with an anonymous variable.
-> Example 8: unsuccessful unification of two concepts
We provide the whole code for this example (in order to recall how it is simple in Amine to parse a concept or inversely, to formulate a concept in text). Unification concerns the concept [Woman:Sue] with the concept [Woman :Mary].
Concept conc1 = Concept.parse("[Woman:Sue]",
mainLexicon);
System.out.println("\nA concept conc1 : " +
conc1.toString(mainLexicon));
Concept conc2 = Concept.parse("[Woman
:Mary]", mainLexicon);
System.out.println("\nA concept conc2 : " +
conc2.toString(mainLexicon));
bindContext.clear();
unify = conc1.unify(bindContext,
bindInf1, conc2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two concepts can not be unified");
Result of the code above is:
The two concepts can not be unified
The two concepts [Woman:Sue] and [Woman :Mary] can not be unified; the individual Sue can not be unified with the individual Mary.
-> Example 9: relation unification
We provide the whole code for this example (in order to recall how it is simple in Amine to parse a relation or inversely, to formulate a relation in text). Unification concerns the relation (agnt [Work] [Man :karim]) with the relation [y4 :y5]<-agnt-[Action]. Note that the first relation is expressed in CGIF and the second in LF. Note also the use of variable in the place of concept type in [y4 :y5].
Relation rel1 = Relation.parse("(agnt
[Work] [Man :karim])", mainLexicon);
System.out.println("\nRelation rel1 : " + rel1.toString(mainLexicon));
Relation rel2 = Relation.parse("[y4
:y5]<-agnt-[Action]", mainLexicon);
System.out.println("\nRelation rel2 : " + rel2.toString(mainLexicon));
bindContext.clear();
unify = rel1.unify(bindContext,
bindInf1, rel2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two relations can not be unified");
Result of the code above is:
Relation rel1 : (agnt [Work] [Man
:karim])
Relation rel2 : (agnt [Action] [y4 :y5])
y5 = karim
y4 = Man
-> Example 10: unification of simple CG
We provide the whole code for this example (in order to recall how it is simple in Amine to parse a CG or inversely, to formulate a CG in text).
CG cg1 = CG.parse("[Drive]-\n"
+
"-obj->[Car],\n" +
"-agnt->[Person: x]", mainLexicon);
System.out.println("\nA simple CG cg1 : \n" + cg1.toLF(mainLexicon));
CG cg2 = CG.parseLF("[Drive]-\n" +
"-obj->[Vehicle],\n" +
"-agnt->[Man : imad]-ageOf->[Age = 25],\n" +
"-manr->[Fast]", mainLexicon);
System.out.println("\nA simple CG cg2 : \n" + cg2.toLF(mainLexicon));
bindContext.clear();
unify = cg1.unify(bindContext,
bindInf1, cg2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two CGs can not be unified");
Result of the code above is:
A simple CG cg1 :
[Drive] -
-obj->[Car],
-agnt->[Human :x]
A simple CG cg2 :
[Drive] -
-obj->[Vehicle],
-agnt->[Man :imad]-ageOf->[Age = 25],
-manr->[Fast]
x = imad
-> Example 11: unsuccessful unification
Let us assume now the following two CGs:
CG cg1:
[Drive] -
-obj->[Car],
-agnt->[Man :imad]-ageOf->[Age = 25]
CG cg2:
[Drive] -
-obj->[Car],
-agnt->[Human :x],
-manr->[Fast]
And the following statements:
bindContext.clear();
unify = cg1.unify(bindContext,
bindInf1, cg2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two CGs can not be unified");
Result of the code above is:
The two CGs can not be unified
Unification failed because cg1 can not be projected in cg2, modulo unification of concepts: relation [Man :imad]-ageOf->[Age = 25] from cg1 was not matched with a relation in cg2.
-> Example 12: unification followed by maximalJoin
This example illustrates a "step toward CG programming": we call unification on two CGs cg1 and cg2 which produces variables binding, then we call maximalJoin on cg1 and a CG cg3. The relevant point here is that maximalJoin will take into account the binding of variables that result from the unification (by sharing the same bindingContext).
Here is the three CGs used in this example:
CG cg1:
[Drive] -
-obj->[Car],
-agnt->[Human :x]
CG cg2:
[Drive] -
-obj->[Vehicle],
-agnt->[Man :imad]-ageOf->[Age = 25],
-manr->[Fast]
CG cg3:
[Action] -
-obj->[Car],
-agnt->[Human]-ageOf->[Age = 25],
-manr->[Fast]
And consider the following statements:
bindContext.clear();
unify = cg1.unify(bindContext,
bindInf1, cg2, bindInf2);
if (unify) {
bindContext.printResultSet(bindContext.getResultSet(bindInf1),
mainLexicon);
bindContext.printResultSet(bindContext.getResultSet(bindInf2),
mainLexicon);
}
else System.out.println("The two CGs can not be unified");
CG cgResult = (CG) cg1.maximalJoin(bindContext,
bindInf1,
cg3, bindInf2);
System.out.println("\nResult of maximal join: \n" +
cgResult.toLF(mainLexicon));
Result of the code above is:
Result of maximal join:
[Drive] -
-obj->[Car],
-agnt->[Human :imad]-ageOf->[Age = 25],
-manr->[Fast]
From the unification of cg1 with cg2 and especially the unification of the concept [Human:x] in cg1 with [Man:imad] in cg2, the variable x is bind to "imad". Then, by calling maximalJoin on cg1 and cg3 with the same bindingContext that is used by unification, the operation is able to consider variable's value.