9 Beliefset Relations
9.1 Introduction
Beliefsets are used in JACK to maintain an agent’s beliefs about the world.
An agent’s beliefset can be stored as either an OpenWorld or a ClosedWorld class. The beliefset represents these beliefs in a first order, tuple-based relational model. The logical consistency of the beliefs contained in the beliefset is automatically maintained. Hence, for example, if an agent adds a belief that contradicts a belief it already has, the beliefset detects this and automatically removes the old belief.
The beliefset is not the only way that an agent can represent information. Agents can also include ordinary data members and other data structures that have been implemented in Java. However, the advantage of using a beliefset over normal Java data structures is that beliefsets have been designed specifically to work within the agent-oriented programming paradigm. Therefore, it is fully integrated with the other JACK Agent Language classes, and provides facilities not available with other data storage techniques. In particular, a JACK beliefset provides:
- Automatic maintenance of logical consistency and key constraints.
- Either Open World or Closed World logic semantics for maintaining these beliefs.
- The ability to post events automatically when changes are made to the beliefset, and hence initiate action within the agent based on a change of beliefs.
- The ability to support beliefset cursor statements, providing a distinct tuple that unifies with the cursor’s query expression each time the cursor attempts to rebind the query (in a complex logical expression).
Each beliefset class definition that an agent uses is called a beliefset relation . It describes a set of beliefs that the agent may have in terms of fields. When the agent wants to adopt a new belief, it specifies values for each of these fields and adds this belief to the relation. This creates a tuple for the relation. Every belief that an agent currently has stored in a given beliefset relation is represented as a tuple.
Tuples can either be true or false. This models the ‘belief’ aspect of the tuple. If the tuple is true, the agent believes that it is a true statement. If it is false, the agent believes that it is a false statement. For example, an agent may have a tuple to represent the statement that Mr Important is Prime Minister and member of the Sensible Party. If this tuple is stored as being true, this indicates that the agent will consider the statement true. If it is stored as false, the agent will assume that this statement is false.
The fact that beliefset relations represents their data as beliefs rather than ‘absolute truths’ distinguishes them from most other programming storage mechanisms and allows agents to more realistically exhibit rational behaviour. Agents do not treat what they know as absolute truth, but rather as beliefs that reflect what they have learned or have been told about the world to this moment. Like people, they will operate on the assumption that these beliefs are true until more information comes to light, but if something new is uncovered that contradicts them, they will update these beliefs accordingly.
9.2 Beliefset Definition
A beliefset definition uses a relational model to specify an agent’s knowledge capacity. This knowledge capacity is expressed as a relation that the agent can use to express beliefs with. Each belief is represented by a specific set of values for each of the relation’s fields. The general format for a beliefset relation’s definition takes one of the two forms shown below:
beliefset RelationName extends ClosedWorld
{
// Zero or more #key field declarations.
// These describe the key attributes of each belief.
// Zero of more #value field declarations
// These describe the data attributes of each belief.
}
beliefset RelationName extends OpenWorld
{
// Zero or more #key field declarations.
// These describe the key attributes of each belief.
// Zero of more #value field declarations
// These describe the data attributes of each belief.
}
Each component of this definition is explained in the following table:
Syntax Term | Description |
beliefset | JACK Agent Language keyword, identifies a beliefset relation’s declaration. |
RelationName | Used to identify the beliefset relation. Whenever an agent wants to query or modify this relation’s tuples, it does so by using this name. |
extends ClosedWorld | Identifies the beliefset relation as a Closed World relation. Closed World relations are relations that store true tuples and assume any tuple not found is false. |
extends OpenWorld | Identifies the beliefset relation as an Open World relation. Open World relations are relations that store true and false tuples and assume any tuple not found is unknown. |
Table 9-1: Components of a beliefset definition
For an agent to be able to use a beliefset, its agent definition file must include a data declaration. The data declaration describes the type of beliefset required by the agent and specifies an external name (BeliefType) and an internal name (beliefName) for the beliefset. The external name of the beliefset maps to a beliefset definition file of the same name.
Each of an agent’s plans that makes use of a beliefset must contain a declaration specifying read-only or read-write access to the beliefset. This is achieved by using either a #reads data, a #modifies data or a #uses data plan declaration.
9.2.1 Closed World Relations
Closed World relations assume that the agent is operating in a world where every tuple that the relation can express is stored in the beliefset at all times as being either true or false. This means that there is no query the agent can make for which it does not have an answer because, theoretically, every possible tuple is always represented in the beliefset. All that the agent can change is whether it believes the tuple to be true or false.
Of course, most tuple fields have an almost infinite range of values, and hence in practical terms the beliefset cannot store every possible tuple. Instead, only those tuples that the agent believes to be true are stored. Any tuple that is not stored, therefore, is assumed to be false.
In a Closed World relation, adding a tuple to the beliefset causes the agent to believe what was false to now be true. Similarly, removing a tuple causes the agent to believe what was true is now false.
Closed World relations do not often occur in the real world, but are still useful in many applications. For example, consider an agent that plays chess. It needs to know the positions of the pieces on the board. This information could be modelled using Closed World relations.
9.2.2 Open World Relations
Open World relations model knowledge and beliefs as most people in the real world experience them: for any given set of beliefs, only some of the answers are known to the agent. Some things may be known to be true, others known to be false and still others unknown.
Unlike the Closed World example, therefore, Open World relations store both true and false tuples. This models the situation where the agent does not know what something is, but does know what it is not. For example, consider a detective in a classic murder mystery. This detective may not know who the murderer is, but may believe that the murderer is definitely not Ms Scarlet (due to some prior investigation). To reflect this knowledge, the agent will store the statement, “Ms Scarlet is the murderer” in the beliefset as a false tuple – one that is believed not to be the case.
Because Open World relations record both true and false tuples, any tuple that is not stored in the beliefset is assumed to be unknown. That is, the agent does not know whether the tuple is true or not.
Unlike Closed World relations, therefore, Open World relations effectively work with three logic values: true, false and unknown.
9.3 Beliefset Members and Methods
Just like the other JACK Agent Language classes, beliefsets provide a number of base members and methods that you can access. These members and methods are described in the following subsections.
Beliefset Construction
JACK supports three types of beliefsets; private, agent and global. Only private beliefsets can be populated by a beliefset constructor and manipulated by plans that use the add() and remove() methods. Beliefsets that are specified in an agent declaration as being agent or global are read-only, so after they are populated by their constructor they can only be queried. As the default constructor creates an empty beliefset when the agent that uses it is instantiated, it only makes sense to use the default constructor for private beliefsets.
A beliefset can be populated with an initial set of tuples by either writing a constructor that reads the required data from a file and explicitly adding the records to the beliefset or by using JACOB Object Modelling.
The following example shows a beliefset constructor which reads data from a file and explicitly adds tuples to the beliefset.
import java.io.*;
import java.text.*;
beliefset Foo extends ClosedWorld
{
#key field int tag;
#value field double stamp;
#indexed query get(int t, logical double s);
static MessageFormat format =
new MessageFormat("(foo {0, number} {1, number))");
Foo(String name)
{
try
{
BufferedReader in =
new BufferedReader(new FileReader(name));
for (String line; ((line = in.readLine()) != null); )
{
try
{
Object [] data = format.parse(line);
int t = ((Number)data[0]).intValue();
double s = ((Number)data[1]).doubleValue();
add(t, s);
}
catch (ParseException e) { }
}
}
catch (IOException e)
{
System.err.println("Problems loading file "+name+".");
}
catch (BeliefSetException e)
{
System.err.println("Loading of file "+name+" failed.");
}
}
}
If multiple constructors have been defined, the JACK kernel determines which beliefset constructor to use on the basis of the number and type of arguments supplied.
An alternative method is to initialise tuple objects using JACOB Object Modelling. JACOB is described in more detail in the JACOB Manual. Beliefsets have a read() method which takes the name of a file as its argument. The read() method populates the beliefset according to the contents of the file which should contain data in JACOB format.
Beliefsets also have a write(String filename) method that can be used to write the beliefset contents in the appropriate JACOB form to the given filename.
The following example illustrates how a beliefset can be initialised in this way.
Given the beliefset
public beliefset BookData extends ClosedWorld {
#key field String title;
#key field String author;
#value field double price;
indexed query get(String t, String a, logical double p);
}
and an agent containing the declaration:
#private data BookData books();
The beliefset could be populated using the read() method as illustrated below:
books.read("book.dat");
The data file book.dat could contain data similar to the following:
<TupleTable
:tuples (
<BookData__Tuple
:title "Reading is Fun"
:author "Walter Fox"
:price 23.75
>
<BookData__Tuple
:title "Spelling is Fun"
:author "Walter Fox"
:price 23.75
>
)
>
where TupleTable is a pre-defined object for the purpose of initialising beliefsets in this way. Also note that in the above example BookData__Tuple has two underscores.
Note: An OpenWorld beliefset would be initialised by two consecutive TupleTable objects; the first for true beliefs and the second for false beliefs.
In addition, beliefset classes have a constructor which takes a filename as an argument and uses the read method to populate the beliefset.
This means that by declaring the beliefset as follows:
#private data BookData books("book.dat");
the tuples are initialised with the data contained in book.dat.
void postEvent(Event e)
BeliefSet modification callbacks use this method to post events when changes are made to a beliefset relation. Therefore, when registering any beliefset callbacks with a beliefset relation, the relevant events using this method must be posted.
void add(parameters)
This method is automatically generated from the beliefset definition file by the JACK compiler – the key and value fields of the relation become arguments of the add() method, as illustrated by the following example.
beliefset Foo extends ClosedWorld
{
#key field int a;
#value field boolean b;
...
}
This results in the following methods being generated in the class Foo:
add (int __v0, boolean __v1);
This method is used to add tuples to Foo. It assumes that the tuple to be added has a belief state of true.
add (int __v0, boolean __v1, BeliefState __d);
This method could also be used to add tuples to Foo. However, given that Foo has Closed World semantics, it is only valid to add tuples with a belief state of true. Attempts to add tuples with a belief state of false or unknown will result in a BeliefSetException being thrown. If Foo had been defined to have Open World semantics, it would have been valid to add tuples with belief states of either true or false. Attempts to add tuples with an unknown belief state into a beliefset with Open World semantics will cause a BeliefSetException to be thrown.
The add() methods allow an agent to add tuples to any of its private relations, but not to any of its agent or global relations (as these relations cannot have their tuple set changed after creation).
void remove(parameters)
This method is automatically generated from the beliefset definition file by the JACK compiler – the key and value fields of the relation become arguments of the remove() method as illustrated by the following example.
beliefset Foo extends ClosedWorld
{
#key field int a;
#value field boolean b;
...
}
This results in the following methods being generated in the class Foo;
remove (int __v0, boolean __v1);
This method is used to remove tuples from Foo. It assumes that the tuple to be removed has a belief state of true.
remove (int __v0, boolean __v1, BeliefState __d);
This method could also be used to remove tuples from Foo. Given that Foo has been defined to have Closed World semantics, this form of the method should only be used to remove tuples with a belief state of true. Attempting to remove tuples with a belief state of false or unknown from a beliefset with Closed World semantics will result in a BeliefSetException being thrown.
If Foo had been defined to have Open World semantics, it would have been valid to remove tuples with belief states of either true or false. Attempting to remove tuples with an unknown belief state from a beliefset with Open World semantics will cause a BeliefSetException to be thrown.
The remove() methods allow an agent to remove tuples from any of its private relations, but not from any of its agent or global relations (as these relations cannot have their tuple set changed after creation).
public int nFacts()
This relation method returns the number of tuples stored in the relation at the time of calling. This includes all tuple instances that physically appear in the relation; therefore, the meaning and results are different depending on whether the relation follows an Open World or Closed World logical model.
For Closed World relations, this returns a count of the number of True tuples that are currently stored for this relation. For Open World relations, this returns a count of both the number of True and False tuples that are currently stored for this relation. For example, if a relation stores tuples to represent statements of, “The tie is blue”, “The tie is not green” and “The tie is not red”, calling nFacts() on this relation will return 3: one tuple representing true information and two tuples representing false information.
9.4 Beliefset Declarations
BeliefSet definitions can include the following declarations:
#key field FieldType field_name;
#value field FieldType field_name;
#indexed query methodName(parameters);
#linear query methodName(parameters);
#complex query methodName(parameters) <statements>
#function query return-type methodName(parameters) <statements>
#posts event EventType handle;
#propagates changes [EventType];
Each of these declarations are described in the following sections.
#key field FieldType field_name
This declaration is used to describe a beliefset relation’s key fields. Key fields describe attributes that uniquely identify the object or entity that the tuple is referring to. Each belief that is expressed using a beliefset tuple is a belief about something. Hence, the agent needs some way of knowing whether two tuples refer to the same thing or not.
The relation’s key fields are used for this purpose. They describe a uniquely identifying characteristic of the thing that the tuple refers to.
For example, suppose an agent had a beliefset relation to represent bank accounts. When a tuple is added to the beliefset, how does the agent know which bank account it refers to? How does it know whether this new information contradicts what it already believes about bank accounts?
With bank account information, this is normally done using an account number. By definition, the account number uniquely identifies a given bank account. Therefore, if the beliefset already contains a tuple stating that account 54321 contains $100, adding a (true) tuple that says account 54321 contains $200 contradicts and replaces this belief. Similarly, adding a tuple that says account 12378 has $200 has no effect on this tuple, because the agent knows from its key that this tuple refers to a different bank account.
A #key field declaration takes the following form:
#key field FieldType field_name;
Each component of this declaration is described in the following table:
Component | Meaning |
#key field | Adds a key field to the beliefset relation. Values given for this field in tuples will be used to identify the object that the tuple is referring to, and hence to determine whether this tuple’s data clashes with an existing tuple and needs to replace it. |
FieldType | The field’s data type. A beliefset relation’s key fields are constrained to be of type String, any scalar type or any type that implements aos.jak.beliefset.Immutable. If the type is a user defined type, the user may need to override the equals() and hashCode() methods of its parent class – refer to the java.util.Hashtable documentation. |
field_name | Used to identify the key field. |
Table 9-2: Components of a #key field declaration
In the last example, the bank accounts had a single key field. This is not necessarily the case in all situations. Sometimes a relation may have multiple key fields (for example in a relation that describes geographic location, where the key might require two key fields: a site’s latitude and its longitude). Similarly, a relation might have no key fields. When a relation has no key fields, this means that there is only ever one object that the relation refers to (hence, it does not need to be specified).
Note: When a beliefset relation has no key fields, this does not necessarily mean that the beliefset will only ever hold one tuple. If the relation is Open World, the agent may store multiple false tuples about the object. For example, there may be only one winner. However, if the agent does not know who the winner is but knows that it is not Mr Important, adding the fact that it is not Ms Action will not contradict its existing belief.
The knockout of existing tuples due to key constraints occurs for true tuples only. As the example above demonstrates, an Open World beliefset can have many negative tuples about something without having them contradicting one another. In fact, if the beliefset contains a positive tuple and the agent adds a negative tuple that doesn’t contradict it, the two tuples will coexist in the beliefset as well (“Ms Action is the Minister for Sport” and “Ms Action is not the Prime Minister”, though redundant, do not contradict one another). The only way negative tuples will knock out positive ones is if the two are directly contradictory (for example, “Ms Action is the Minister for Sport” and “Ms Action is not the Minister for Sport”).
#value field FieldType field_name
This declaration is used to specify a relation’s data fields. Unlike key fields, data fields do not identify the object that the tuple is describing. Instead, they represent information about this object that the agent needs to know. To a certain extent, the value fields are the reason why the agent has the relation in the first place – because it wants to know this information about some kind of object.
A #value field declaration takes the following form:
#value field FieldType field_name;
Each component of this declaration is described in the following table:
Component | Meaning |
#value field | Adds a value field to the beliefset relation. Data assigned to these fields is used to represent attributes about the object of which the agent needs to be aware. |
FieldType | The field’s data type. A beliefset relation’s value fields can be of any type. |
fieldName | Used to identify the value field. |
Table 9-3: Components of a #value field declaration
For example, a bank account’s key field is its account number. However, the account number does not provide any information about the account that an agent will want to record. Its purpose is purely to distinguish one account from another. The sorts of things that the agent might want to know about the account are its balance, owner’s name, credit limit, etc. Each of there attributes would be described using value fields.
#indexed query methodName(parameters)
Once a beliefset relation has been defined and tuples have been added to the beliefset, the agent will need to access this data. It does so by performing a query on the relation. There are two kinds of query that an agent can perform on a relation:
- an indexed query (defined by the #indexed query declaration); or
- a linear query (defined by the #linear query declaration).
Both these queries produce the same results (search for the tuple(s) concerned). The only difference is in implementation. Indexed queries maintain a hash index of tuples, and can usually locate them more quickly, whereas linear queries do not maintain an index; thus the only way that matching tuples can be found is through a linear search.
Indexed queries occupy slightly more space (which is required for the index) and are slightly slower to update tuples (since the index must be updated as well), but are much quicker to query in most circumstances. Therefore, unless memory and update speed is at an absolute premium, you should use indexed queries for all but the smallest of relations (i.e. those that will hold at least 10 tuples in the beliefset).
When a query is performed and most of the fields are unified with unbound logical variables, the agent may not have enough information to use the index effectively. In this case, the indexed query would be just as slow as the linear one.
An indexed query’s definition takes the following form:
#indexed query methodName(parameters);
Each component of this definition is described in the following table:
Component | Meaning |
#indexed query | Defines an indexed query, namely one that builds and maintains an internal hash table index for query optimisation. |
methodName | The name of the query. |
(parameters) | The list of parameters used by the query. These parameters are matched in order with the relation’s tuples. Parameters that are defined as normal members are input parameters. Parameters that are defined as logical members are output parameters. |
Table 9-4: Components of an #indexed query definition
Only the prototype needs to be declared for each indexed query. The actual query class will be a derived class of BeliefSetCursor generated by the JACK compiler. The indexing is done using only the non-logical parameters.
For example, suppose a beliefset relation is defined as shown below:
beliefset Politician extends OpenWorld
{
#key field String name;
#value field String party;
#value field String portfolio;
#indexed query
getPortfolio (String n,String p,logical String port);
}
This beliefset relation has an indexed query, which takes name and party as input parameters, and if successful will return the matching tuple’s portfolio.
A beliefset relation may have many query methods, each specifying different input and output parameters.
Beliefset queries take a number of parameters which are either data values (which can either be literal, normal members or logical members that have already been bound to a specific value) or unbound logical members. The query attempts to match the given parameters against the relation’s tuples, using the unbound logical members as ‘wild cards’. If a match can be found, the logical members are bound to the tuple’s values.
It is possible to overload the query methods by providing different parameter lists. When such polymorphic query methods are defined, the compiler will select the definition that best matches the parameters provided.
For example, consider the following beliefset relation definition:
beliefset Job extends OpenWorld
{
#key field String name;
#value field String employment;
#indexed query jobQuery(String n, String e);
#indexed query jobQuery(String n, logical String e);
#indexed query jobQuery(logical String n, String e);
#indexed query jobQuery(logical String n, logical String e);
}
The same query name has been defined with all different combinations of input and output parameters. This means that any combination of String and logical variables can be queried.
#linear query methodName(parameters)
The #linear query method is identical to the #indexed query method in all respects, other than the way queries search the beliefset for matching tuples. The #indexed query builds an index that allows for search speed optimisation while the #linear query is more efficient in terms of memory usage.
Each component of a #linear query is described in the following table:
Component | Meaning |
#linear query | Defines a linear query – namely one that the agent executes by attempting to unify with each tuple in the beliefset in turn. |
methodName | The name of the query. |
(parameters) | This list of parameters must be passed in a query. These parameters are matched in order with the relation’s tuples.
|
Table 9-5: Components of a #linear query definition
In all other respects, linear queries are identical to indexed queries in terms of how they are defined and how they are used.
#complex query name(parameters) <body>
Complex queries provide a way to combine simple queries (as described above) into one entity that can be used in the same way as a simple query. Suppose that there is a beliefset which contains only parent(parent, child) relations and a parent(parent, child) query has already been defined. A grandparent() query could be written as follows, avoiding the need to add specific grandparent(grandparent, grandchild) relations to the beliefset.
beliefset Ancestors extends OpenWorld
{
#key field String parent ;
#key field String child ;
#indexed query parent( String p , logical String c );
#indexed query parent( logical String p , logical String c );
#complex query
grandparent(logical String a, logical String c)
{
logical String b;
return parent(a, b) && parent(b, c);
}
}
Note: the return value expression of a complex query incurs an implicit .next() in the same way as a condition expression in a #reasoning method
A #complex query declaration takes the following form:
#complex query name(parameters) <statements>
Each component of this declaration is described in the following table:
Component | Meaning |
#complex query | Declares that the following method is a complex query. |
name(parameters) | name is the name of the query. Parameters can be of any type. |
<statements> | The code which constitutes the query. The method body can contain arbitrary Java code, but the method must return a Cursor. |
Table 9-6: Components of a #complex query definition
#function query ReturnType name(params) <body>
In JACK, the code for indexed and linear queries is generated automatically – the user only provides the function prototypes. With a function query the user provides the entire function definition so queries can be constructed which use arbitrary Java code. A function query can contain logical member definitions, so it can be used to query a beliefset from JACK entities that do not support logical members. In the following example, the function queries #function query String parent(String b) and #function query int numChildren( String p ) could be used from within an agent method.
beliefset Ancestorship extends OpenWorld
{
#key field String parent ;
#key field String child ;
#indexed query parent( logical String p , String c );
#indexed query parent( logical String p , logical String c );
#function query String parent(String b)
{
logical String a;
parent(a,b).next();
return a.as_string();
}
#function query int numChildren( logical String p )
{
logical String child ;
int i = 0;
Cursor c = parent(p,child);
while(c.next())
i++;
return i;
}
#function query int numChildren( String p )
{
logical String lp;
lp.unify( p );
return numChildren( lp );
}
}
Note:
- There is a requirement for an explicit .next() in the function queries. Implicit .next() only occurs inside reasoning methods and in the return statement of a complex query.
- For the purposes of method overloading, it is important to note that the JACK compiler converts all logical variables into variables of type aos.jack.jak.logic.Variable. So in the example above, no ambiguity exists between the two numChildren() queries.
A #function query declaration takes the following form:
#function query ReturnType name(parameters) <statements>
Each component of this declaration is described in the following table:
Component | Meaning |
#function query | Declares that the following method is a function query. |
ReturnType | Unlike a #complex query, a #function query can return any type. |
name(parameters) | name is the name of the query. Parameters can be of any type. |
<statements> | The code which constitutes the query. The method body can contain arbitrary Java code. |
Table 9-7: Components of a #function query definition
#posts event EventType handle
Beliefset relations are able to post events when changes are made to their tuples. This is done by posting an event within a beliefset callback. Beliefset callbacks are described in more detail below, but essentially they are methods that will be called when a particular beliefset change occurs. This declaration specifies the event types that may be posted from within any of the callbacks defined for this beliefset type.
A #posts event declaration takes the following form:
#posts event EventType handle;
Each component of this declaration is described in the following table:
Component | Meaning |
#posts event | Identifies that the beliefset relation can post an event. Typically, the details of when this event is posted will be specified in a beliefset callback. |
EventType | The type of event that this beliefset relation can post via callbacks. |
handle | A handle on this event, so that the event’s posting methods can be accessed. |
Table 9-8: Components of a #posts event declaration
#propagates changes [EventType]
#propagates changes marks that a beliefset may be a source beliefset in a team belief connection, and it provides an implementation of the connection dynamics, so that changes to the beliefset are propagated correctly. Belief propagation is only available when using JACK Teams. Refer to the Teams Manual for more details.
This propagation includes filtering when the #propagates changes declaration is used with an optional event type. This is discussed in more detail in the Teams Manual.
9.5 Beliefset Callbacks
A beliefset relation will only post an event if the specific event-posting callback has been written. Hence, a beliefset relation will only need to include #posts event declarations if such callbacks are going to be written. These callbacks are defined in the beliefset super-classes OpenWorld and ClosedWorld. Their prototypes are listed below:
- public void addfact(Tuple t, BeliefState is);
This callback is executed whenever an attempt is made to add a Tuple t with the is BeliefState into the agent’s beliefset, regardless of whether the tuple is already present.
The BeliefState can either be Cursor.TRUE if the tuple is meant to be true, or Cursor.FALSE if the tuple is meant to be false (this only applies to OpenWorld relations). - public void newfact(Tuple t, BeliefState is, BeliefState was);
This callback is executed whenever a Tuple t with the BeliefState is is added to the beliefset.
The was BeliefState is bound to the BeliefState that the tuple had in the beliefset previously.- If the tuple was not present in any form and the beliefset relation is ClosedWorld, was will be Cursor.FALSE.
- If the tuple was not present in any form and the beliefset relation is OpenWorld, was will be Cursor.UNKNOWN.
- public void delfact(Tuple t, BeliefState was);
This callback is executed whenever a Tuple t with BeliefState was is removed from the agent’s beliefset.
The was BeliefState can either be Cursor.TRUE if the tuple is meant to be true, or Cursor.FALSE if the tuple is meant to be false (this only applies to OpenWorld relations).
- public void endfact (Tuple t, BeliefState was, BeliefState is);
This callback is executed whenever a Tuple t with BeliefState was is removed from the agent’s beliefset. This removal can take place either through an explicit remove, the adding of its negation or through key constraint knockouts.
The is BeliefState is bound to the BeliefState that the tuple has after being removed. This may be Cursor.FALSE if the tuple was negated or the beliefset relation is ClosedWorld, or it may be Cursor.UNKNOWN if the tuple is removed completely and the relation is OpenWorld. - public void modfact (Tuple t, BeliefState is, Tuple knocked, Tuple negated);
This callback is executed just before a Tuple t gets added to the relation’s beliefset and changes its BeliefState to is. If the change knocks out another tuple due to key constraints, this tuple is assigned to the knocked parameter, and if the change knocks out another tuple due to inconsistency (negation), this tuple is assigned to the negated parameter. - public void moddb();
This is a catch-all callback. It is called whenever the state of the beliefset changes due to an add() or remove() method call.
Note: This is called after the change has been made to the beliefset.
When any of these callbacks are included in a beliefset relation’s definition, it is imperative to declare any events that the callback posts in #posts event declarations.
For example, consider the following beliefset definition:
beliefset Politician extends OpenWorld
{
#key field String name;
#value field String party;
#value field String portfolio;
#indexed query
getPortfolio(String n,String p,logical String port);
#indexed query
getWho(logical String n,String p,String port);
#posts event electedEvent electref;
public void newfact(Tuple t,BeliefState is,BeliefState was)
{
// Note that Politician__Tuple contains two underscores
Politician__Tuple pt = (Politician__Tuple) t;
if (pt.portfolio.equals("Prime Minister"))
{
// code to post the elected event. This code will
// be executed whenever a new prime minister is
// elected. For example:
postEvent(electref.newElection(pt.name, pt.party,
pt.portfolio));
...
}
}
}
In this example, the beliefset relation includes a callback that should be executed whenever a new prime minister is elected. When a new tuple is added to the beliefset whose portfolio field is ‘Prime Minister’ the newfact() callback is executed. The name member will be bound to the new prime minister’s name and the party member to the new prime minister’s party. It is up to the callback’s author to implement how the event should be posted when these circumstances arise. Because an event of type electedEvent will be posted, the #posts event electedEvent electref declaration is required. The newfact() callback method can use this event’s electref handle to access the event’s posting methods.
9.6 Manipulating Beliefset Relations
The beliefset OpenWorld and ClosedWorld classes provide two base methods for manipulating the tuples in an agent’s beliefset. These are add(), which is for adding information to the beliefset, and remove(), which is for removing information from the beliefset. Each of these methods are provided for both Closed World and Open World relations.
Beliefset cursors also provide a base method called removeAll() that can be used to remove a set of tuples from an agent’s beliefset relation. When called, this method removes all tuples from the relation that unify with the beliefset cursor’s query expression.
For example, suppose an agent uses a private relation called politician() (of type Politician). Suppose also that the current set of tuples for this relation are as shown in the following table:
Name | Party | Portfolio |
Mr Important | Sensible Party | Prime Minister |
Ms Action | Sensible Party | Minister for Sport |
Mr Knockout | Silly Party | Minister for Sport |
Table 9-9: Tuples in the Politician beliefset
If the agent executes the following code, this will remove the first two tuples from the agent’s private politician() relation.
logical String name;
String party;
String portfolio;
party.unify("Sensible Party");
politician.getWho(name, party, portfolio).removeAll();
That is, it will remove all tuples that unify with the parameters provided to the relation’s get who indexed query.
One simple way to clear a private relation for an agent is to use the removeAll() method on a beliefset cursor expression with unbound logical members for all query parameters. For example, to completely clear the above agent’s politician relation of tuples, regardless of how many it has, an agent could execute the following reasoning method code:
logical String name;
logical String party;
logical String portfolio;
politician.who(name, party, portfolio).removeAll();
Since every tuple in the relation will unify with this query expression, every tuple will be removed.
9.7 Beliefset Iteration
A JACK beliefset is neither an array nor a list of records. It is structured in a way that permits efficient query-based information retrieval, and this is how it is best used. To access data linearly, it may be more appropriate to use a Java data structure rather than a JACK beliefset. Nevertheless, it is sometimes useful to be able to retrieve all the tuples from a JACK beliefset.
A JACK beliefset query returns a Cursor and it is possible to iterate over all the matching tuples using this cursor. For example, suppose we have a plan with:
//code that iterates through the beliefset
#reads data BeliefsetType bel;
logical something x, y, z;
for ( Cursor c = bel.get(x,y,z) ; c.next() ; )
{
// process x,y,z with new bindings each time
}
The logic machinery knows of cursors as objects that can provide bindings for logical variables and that they carry information, so that when the next() method is called on the cursor, it will renew the bindings, if possible. The first call to next() provides the first binding, the second call provides the second binding, and so on, until all alternative bindings have been provided, at which point the next() resets bindings to the original input state and returns false.
However, note that if the subsequent processing changes the beliefset, or the beliefset is changed by some other task, the next c.next() call will result in a BeliefSetException.
Note that the code below does not iterate through the beliefset (as one may initially expect), but instead repeatedly performs the same processing while a certain beliefset state holds. This is because there is no call to next() on the cursor returned by bel.get() to renew the bindings:
//code that does NOT iterate through the beliefset
logical something x, y, z;
while ( bel.get(x,y,z) )
{
// process x,y,z
}
9.8 Extending the OpenWorld or ClosedWorld classes
If you wish to create your own extension of the OpenWorld or ClosedWorld classes they must be marked as abstract.
It will also be necessary to create a Cursor class for your extended class. An example is shown below:
public abstract class MyOpenWorld
extends aos.jack.jak.beliefset.OpenWorld {
// Any constructors or methods
// you wish to override go here.
}
public abstract class MyOpenWorldCursor
extends aos.jack.jak.beliefset.OpenWorldCursor {
// You need this class whether or not
// you plan to override anything in it.
}
The JACK compiler will define the required abstract methods for each specific type of beliefset that you define in your JACK application.
转载于:https://www.cnblogs.com/6DAN_HUST/archive/2012/04/14/2447567.html
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/110429.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...