Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Discussion: Knowledge Representation for AI applications

Luciftias Neurocam
Ecosystem Design
Join date: 13 Oct 2005
Posts: 742
02-23-2006 21:19
I don't have a catchy name for this script, but I think its part of a parcel of scripts necessary to bring useful AI applications to SL. Specifically, this script represents an "ontology", a network of concepts and their implications accessible by other scripts.

Basically, I've implemented a mini-scripting language to facilitate the representation of concepts in SL scripts.

So say you wanted to describe the possible relationships entailed in the concept "dog", you'd have the following dialogue snippet with the object containing this script.

CODE
You: ADD $$ dog
Object: OK


Now the concept dog is embedded in a network of relationships:
A dog is an animal, so...

CODE
You: ADD $$ animal
Object: OK
You: ENTAILS $$ dog $$ animal
Object: OK


And an animal is also, say, a "lifeform"

CODE
You: ADD $$ lifeform
Object: OK
You: ENTAILS $$ animal $$ lifeform
Object: OK



A dog is also a pet

CODE
You: ADD $$ pet
Object: OK
You: ENTAILS $$ dog $$ pet
Object: OK


Let's check if the object knows what we think we've told it (is a dog an animal):

CODE
You: QENTAILS $$ dog $$ animal 
Object: TRUE


Is a dog a lifeform?

CODE
You: QENTAILS $$ dog $$ lifeform
Object: TRUE



Show the next concept(s) entailed by "dog"

CODE
You: SHOWENTAILS $$ dog
Object: dog>>animal
Object: dog>>pet


Show me what concept(s) "animal" contains:

CODE
You: SHOWCONTAINS $$ animal
Object: animal<<dog


Does the concept "pet" contain the concept "dog":

CODE
QCONTAINS $$ pet $$ dog
Object: TRUE





Here's the script:

CODE
list gOntology;
list gMembership;
list gQualities;
string gName="startup";
integer gLine;
key gQueryID;
list FindInstanceList(list inlist,string data)
{
list outlist;
integer i;
for(i=0;i<llGetListLength(inlist);i++)
{
if(llList2String(inlist,i)==data)
{
outlist=outlist+;
}
}
return outlist;
}

default
{
state_entry()
{
llListen(0,"","","";);
gQueryID = llGetNotecardLine(gName, gLine);
}


dataserver(key query_id, string data) {
if (query_id == gQueryID) {
if (data != EOF && data !="EMPTY";) { // not at the end of the notecard

list temp=llParseString2List(data,[" "],[]);
gOntology=gOntology+llList2List(temp,0,0);
gMembership=gMembership+llList2List(temp,1,1);
++gLine; // increase line count
gQueryID = llGetNotecardLine(gName, gLine); // request next line
}
if(data==EOF)
llSay(0,"Done loading ontology!";);
if(data=="EMPTY";)
llSay(0,"No ontology to load...enter manually.";);
}
}
listen(integer channel,string name,key id,string message)
{
//
integer stopcondition;
integer findmatch;

if(channel==0 && llGetOwner()==id)
{
string command=llList2String(llParseString2List(message,[" $$ "],[]),0);
if(command=="ADD";)
{
//check first if exists
integer ind=llListFindList(gOntology,[llList2String(llParseString2List(message,[" $$ "],[]),1)]);
if(ind==-1)
{
gOntology=gOntology+[llList2String(llParseString2List(message,[" $$ "],[]),1)];
gMembership=gMembership+["~"];
llSay(0,"OK";);
}
else
llSay(0,"already a listed element, add instances with ENTAILS command";);
}
if(command=="RESET";)
llResetScript();
if(command=="ENTAILS";)
{

integer ind=llListFindList(gOntology,[llList2String(llParseString2List(message,[" $$ "],[]),1)]);
integer ind2=llListFindList(gOntology,[llList2String(llParseString2List(message,[" $$ "],[]),2)]);
if(ind!=-1 && ind2!=-1)
{
//find out if already in ENTAILS relationship. Then add new instance to end of list with accompanying superset name
if(llList2String(gMembership,ind)=="~";)
{
gMembership=llListReplaceList(gMembership,[llList2String(llParseString2List(message,[" $$ "],[]),2)],ind,ind);
llSay(0,"OK";);

}
else
{
gMembership=gMembership+[llList2String(llParseString2List(message,[" $$ "],[]),2)];
gOntology=gOntology+[llList2String(llParseString2List(message,[" $$ "],[]),1)];
llSay(0,"OK";);
}
}

else
{
if(ind==-1)
llSay(0,llList2String(llParseString2List(message,[" $$ "],[]),1)+" is not an ontology list member";);
if(ind2==-1)
llSay(0,llList2String(llParseString2List(message,[" $$ "],[]),2)+" is not a membership class";);
}
}
if(command=="QENTAILS";)
{
findmatch==0;
integer iter=0;
string start= llList2String(llParseString2List(message,[" $$ "],[]),1);
string firststop=llList2String(llParseString2List(message,[" $$ "],[]),2);
string firststart=start;

//make sure all elements in approrpriate lists
integer OK;
if(llListFindList(gOntology,[firststart])!=-1 && llListFindList(gMembership,[firststop])!=-1)
{
OK=1;
}
else
{
OK=0;
if(llListFindList(gOntology,[firststart])==-1)
llSay(0,firststart+" not in ontology list.";);
if(llListFindList(gMembership,[firststop])==-1)
llSay(0,firststop+" not in membership list.";);
}

string stop=":";
stopcondition=0;
while(OK==1 && findmatch!=1 && iter<llGetListLength(gOntology))// && stopcondition!=1)
{
iter++;
//integer ind=iter;//llListFindList(gOntology,[start]);
gOntology=gOntology+llList2List(gOntology,0,0);
gOntology=llDeleteSubList(gOntology,0,0);
gMembership=gMembership+llList2List(gMembership,0,0);
gMembership=llDeleteSubList(gMembership,0,0);
string start= llList2String(llParseString2List(message,[" $$ "],[]),1);
string firststop=llList2String(llParseString2List(message,[" $$ "],[]),2);
string firststart=start;
string stop=":";
stopcondition=0;
while(stopcondition==0)
{

integer ind=llListFindList(gOntology,[start]);
start=llList2String(gOntology,ind);

stop = llList2String( gMembership,ind);

if(stop==firststop)
{
stopcondition=1;
findmatch=1;
}
if(stop=="~";)
{
stopcondition=1;
findmatch=0;
}
start=stop;
}

}
if(findmatch==1)
{
llSay(0,"TRUE";);
// llSay(0,stop+" "+firststop);
}
else
llSay(0,"FALSE";);
}



if(command=="SHOWENTAILS";)
{
list indices=FindInstanceList(gOntology,llList2String(llParseString2List(message,[" $$ "],[]),1));
integer i;
integer index;
string chain;
for(i=0;i<llGetListLength(indices);i++)
{

chain=llList2String(gOntology,(integer)llList2String(indices,i)) +">>"+llList2String(gMembership,(integer)llList2String(indices,i));



llSay(0,chain);
}

}


if(command=="SHOWCONTAINS";)
{
list indices=FindInstanceList(gMembership,llList2String(llParseString2List(message,[" $$ "],[]),1));
integer i;
integer index;
string chain;
for(i=0;i<llGetListLength(indices);i++)
{

chain=llList2String(gMembership,(integer)llList2String(indices,i)) +"<<"+llList2String(gOntology,(integer)llList2String(indices,i));



llSay(0,chain);
}

}
if(command=="DUMP";)
{
integer i;
for(i=0;i<llGetListLength(gOntology);i++)
{
string s1=llList2String(gOntology,i);
string s2=llList2String(gMembership,i);
llOwnerSay(s1+" "+s2);
}
}
if(command=="QCONTAINS";)
{
findmatch==0;
integer iter=0;
string start= llList2String(llParseString2List(message,[" $$ "],[]),1);
string firststop=llList2String(llParseString2List(message,[" $$ "],[]),2);
string firststart=start;
integer OK;
if(llListFindList(gMembership,[firststart])!=-1 && llListFindList(gOntology,[firststop])!=-1)
{
OK=1;
}
else
{
OK=0;
if(llListFindList(gMembership,[firststart])==-1)
llSay(0,firststart+" not in membership list.";);
if(llListFindList(gOntology,[firststop])==-1)
llSay(0,firststop+" not in ontology list.";);
}
string stop=":";
stopcondition=0;
while(OK==1 && findmatch!=1 && iter<llGetListLength(gOntology))// && stopcondition!=1)
{
iter++;
//integer ind=iter;//llListFindList(gOntology,[start]);
gOntology=gOntology+llList2List(gOntology,0,0);
gOntology=llDeleteSubList(gOntology,0,0);
gMembership=gMembership+llList2List(gMembership,0,0);
gMembership=llDeleteSubList(gMembership,0,0);
string start= llList2String(llParseString2List(message,[" $$ "],[]),1);
string firststop=llList2String(llParseString2List(message,[" $$ "],[]),2);
string firststart=start;
string stop=":";
stopcondition=0;
while(stopcondition==0)
{

integer ind=llListFindList(gMembership,[start]);
start=llList2String(gMembership,ind);

stop = llList2String( gOntology,ind);
// start=stop;

//if(stop==firststart)
//{
// stopcondition=1;
// findmatch=1;
//}

if(stop==firststop)
{
stopcondition=1;
findmatch=1;
}
if(start=="~";)
{
stopcondition=1;
findmatch=0;
}
if(ind==-1)
{
stopcondition=1;
findmatch=0;
}
start=stop;
}

}
if(findmatch==1)
{
llSay(0,"TRUE";);
// llSay(0,stop+" "+firststop);
}
else
llSay(0,"FALSE";);
}

}
}
}


To run the script, just place it in an object with a notecard named "startup".
The notecard should, initially, only contain the word "EMPTY", in all caps.

In order to back up ontologies, you can get the script to dump the contents of its knowledge base to text simply by typing "DUMP". The script will use llOwnerSay to dump 2 columns of data to chat channel 0. the first column represents the actually ontology, while the second column the entailed concepts by the elements in column A. Cut and paste the columns from the chat window and replace the word "EMPTY" in the startup card. Make sure you're only including the 2 columns and not the "Object: " (or whatever your script container is called) preceding the first column. Then say RESET (all caps) and the saved ontology will be loaded into memory. If startup only contains "EMPTY", RESET will simply prompt you to enter a new ontology.
Nada Epoch
The Librarian
Join date: 4 Nov 2002
Posts: 1,423
Original Thread
02-24-2006 08:01
/15/0e/90014/1.html
_____________________
i've got nothing. ;)
Patch Lamington
Blumfield SLuburban
Join date: 2 Nov 2005
Posts: 188
02-24-2006 09:36
Not something Im likely to use myself, but... you can remove the need to edit the object name out of the chat log by having it change its name to whatever text should be in the first column and then only saying the second column.

Voila, two columns with a : delimiter.

It will be slower to dump the log tho :)
_____________________
Blumfield - a regular everyday kind of 'burb in an irregular world.
This notice brought to you by the Blumfield Visitors and Residents Bureau.
Luciftias Neurocam
Ecosystem Design
Join date: 13 Oct 2005
Posts: 742
02-24-2006 10:05
From: Patch Lamington
Not something Im likely to use myself


There's about 2 users here who are :)

From: someone
but... you can remove the need to edit the object name out of the chat log by having it change its name to whatever text should be in the first column and then only saying the second column.

Voila, two columns with a : delimiter.


Neat idea. :)
Mack Echegaray
Registered Snoozer
Join date: 15 Dec 2005
Posts: 145
02-24-2006 16:09
Mmm, I somehow doubt you're going to fit a useful knowldge base in 16kb of of memory. Considering SL doesn't even have dogs themselves right now trying to make an ontology about them makes me think 'overkill' ;) still pretty cool, check out NARS if you haven't already, predicate logic is so 1998
Luciftias Neurocam
Ecosystem Design
Join date: 13 Oct 2005
Posts: 742
02-24-2006 17:51
From: Mack Echegaray
Mmm, I somehow doubt you're going to fit a useful knowldge base in 16kb of of memory. Considering SL doesn't even have dogs themselves right now trying to make an ontology about them makes me think 'overkill' ;) still pretty cool, check out NARS if you haven't already, predicate logic is so 1998


You'd be surprised what you can fit when mulitple domains of an ontology are broken up into several communicating/cooperating objects. Don't try and cram an entire domain of useful knowledge into a single object or script.

That's the trick I used for creating useful conversation templates for my version of AIML.

on edit: Don't tell this:
From: someone
Considering SL doesn't even have dogs themselves right now

to Gurgon Grumby :)