Java Guide Series
Architecture | Knowledge Base | Networking | Containers | Threads
The MADARA KaRL scripting environment is fast and efficient, but it can also be difficult to debug and work with for programmers more familiar with object-oriented programming in their host languages. To address the needs of object-oriented developers, MADARA also provides a set of containers which present facades into the Knowledge Base and mimic the look and feel of STL containers. These object-oriented containers are meant to be user-friendly and intuitive, but some guidance may be useful for users who do not know where they are and how they may be initialized.
In this section, we breakdown the types of containers. For a complete list of containers, see the files located inside of the com.madara.containers namespaceof your source distribution, or see the Library documentation for the Containers namespace.
Instance variables are facades into single variable locations within the Knowledge Base and are the most efficient of the MADARA containers. Values are set with a set function, available through the container, and retrieved using the get method or with a toInteger (), toDouble (), or toString () function. MADARA currently supports the following instance variables:
Location: ai.madara.knowledge.containers.Integer
Documentation: Integer
Integer is a container that provides interfaces for setting and getting an integer value from the Knowledge Base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.Integer; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); Integer myId = new Integer(); myId.setName(knowledge, ".id"); // set id within the knowledge base to 1 myId.set(1); ... // print the value to stdout System.out.println ("My id is " + myId.get()); // free the C++ resources myId.free(); knowledge.free();
Location: ai.madara.knowledge.containers.Double
Documentation: Double
Double is a container that provides interfaces for setting and getting a double value from the Knowledge Base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.Double; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); Double funds = new Double(); funds.setName(knowledge, ".funds"); // set funds to 300.50 funds.set(300.50); ... // print the value to stdout System.out.println ("My funds available are " + funds.get()); // free the C++ resources funds.free(); knowledge.free();
Location: ai.madara.knowledge.containers.String
Documentation: String
String is a container that provides interfaces for setting and getting a string value from the Knowledge Base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.String; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); String name = new String(); name.setName (knowledge,".name"); // set name to "John Smith" name.set("John Smith"); ... // read the value later // print the value to stdout System.out.println ("My name is " + name.get()); // free the C++ resources name.free(); knowledge.free();
Array containers are facades into arrays of variable locations within the Knowledge Base, and provide O(1) access times to these elements. Unlike with instance variables, the name that is set for an array container is the prefix for variable locations instead of an actual variable name. MADARA currently supports the following vector/array containers:
Location: ai.madara.knowledge.containers.IntegerVector
Documentation: IntegerVector
IntegerVector
is a container that provides interfaces for setting and getting an integer value from the Knowledge Base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.IntegerVector; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); IntegerVector myArray myArray = new IntegerVector(); myArray.setName(knowledge, ".array", 10); // set index 1 of the array to true (1) myArray.set(1, 1); // set index 5 of the array to 30 myArray.set(5, 30); // set index 0 of the array to 17 myArray.set(0, 17); ... // read the values of index 0, 1, and 5 System.out.println("myArray.get(0) is " + myArray.get(0)); System.out.println("myArray.get(1) is " + myArray.get(1)); System.out.println("myArray.get(5) is " + myArray.get(5)); // free the C++ resources myArray.free(); knowledge.free();
Location: ai.madara.knowledge.containers.DoubleVector
Documentation: DoubleVector
Double_Vector
is a container that provides interfaces for setting and getting an array of double values within the Knowledge Base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.DoubleVector; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); DoubleVector myArray myArray = new DoubleVector(); myArray.setName(knowledge, ".array", 10); // set index 1 of the array to 6.5 myArray.set(1, 6.5); // set index 5 of the array to 31.3 myArray.set(5, 31.3); // set index 0 of the array to 15.2 myArray.set(0, 15.2); ... // read the values of index 0, 1, and 5 System.out.println("myArray.get(0) is " + myArray.get(0)); System.out.println("myArray.get(1) is " + myArray.get(1)); System.out.println("myArray.get(5) is " + myArray.get(5)); // free the C++ resources myArray.free(); knowledge.free();
Location: ai.madara.knowledge.containers.StringVector
Documentation: StringVector
StringVector
, also known as String_Array
, is a container that provides interfaces for setting and getting an array of string values within the Knowledge Base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.StringVector; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); StringVector myArray myArray = new StringVector(); myArray.setName(knowledge, ".array", 10); // set the indices of the array to characters in Snow White myArray.set(0, "Snow White"); myArray.set(1, "Doc"); myArray.set(2, "Dopey"); myArray.set(3, "Bashful"); myArray.set(4, "Grumpy"); myArray.set(5, "Sneezy"); myArray.set(6, "Sleepy"); myArray.set(7, "Happy"); myArray.set(8, "Huntsman"); myArray.set(9, "Queen"); ... // print out all elements of the array for (unsigned int i 0; i < 10; ++i) System.out.println("array.get(" + i + ") is " + myArray.get(i)); // free the C++ resources myArray.free(); knowledge.free();
Map containers are facades into string-keyed maps of variable locations within the Knowledge Base. Unlike with instance variables, the name that is set for a map container is the prefix for variable locations instead of an actual variable name, and access times are O(log m), where m is the number of items existing in the Map container.
Location: ai.madara.knowledge.containers.Map
Documentation: Map
The Map class is one of the most versatile container classes available in MADARA. This class is likely to prove invaluable to developers wanting to emulate classes within the knowledge base.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.Map; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a container that points inside it KnowledgeBase knowledge = new KnowledgeBase(); Map map = new Map(); map.setName(knowledge, ".profile.1"); // create profile fields for a man named John Smith at profile.1 map.set("name", "John Smith"); map.set("age", 30); map.set("funds", 300.50); map.set("photo1", "/images/profile/1/headshot.jpg"); // print the profile System.out.println("Name: " + map.get("name")); System.out.println("Age: " + map.get("age")); System.out.println("funds: " + map.get("funds")); System.out.println("Photo: " + map.get("photo1")); /** * in a persistent application, we would actually want to free() whatever is returned by * map.get(...). Otherwise, you'll have a small memory leak. **/ // free the C++ resources map.free(); knowledge.free();
Flexible Maps are abstractions intended to support multi-dimensional arrays and maps. These containers provide both integer-based index and string-based accesses. These functions return other Flexible Maps which can either be set to an arbitrary value (such as an integer, string, double, byte buffer, etc.) or subindexed with integers or strings.
Location: ai.madara.knowledge.containers.FlexMap
Documentation: Map
FlexMap
replaces the old VectorN
class which was less intuitive and useful. FlexMap
is O(1) for subindexing (e.g., [1][2]) and only incurs an O(log n), with n being the number of keys in the knowledge base, lookup on the first access of an element with an accessor or mutator function (e.g. to_string()
or setting the value explicitly). FlexMap essentially always assumes an index is not a variable lookup until you are setting or getting a value explicitly. The keys function is an O(n) operation that must look through all keys in the Knowledge_Base to find all versions with the prefix set in constructor or set_name function.
Though the subindexing is O(1), developers should keep in mind that each index is a concatenation of the container name with a delimiter (by default '.') and the index, which is essentially 2 concatenation operations.
FlexMap
includes to*Container operations that can convert the FlexMap
into any supported container for fast O(1) changes later to String, Integer, Double, etc.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.FlexMap; import ai.madara.knowledge.containers.String; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a multi-dimensional array KnowledgeBase knowledge = new KnowledgeBase(); FlexMap map = new FlexMap(); String cityName; map.setName(knowledge, "cities"); // set location cities.1.2.3.4.name to "St. Louis" map.get(1).get(2).get(3).get(4).get("name").set("St. Louis"); // set location cities.4.2.3.4.name to "San Francisco" map.get(4).get(2).get(3).get(4).get("name").set("San Francisco"); // output the value at [1][2][3][4] System.out.println ("First town is " + map.get(1).get(2).get(3).get(4).get("name").toString()); // output the value at [4][2][3][4] System.out.println ("Second town is " + map.get(4).get(2).get(3).get(4).get("name").toString()); // FlexMap can create containers to its elements for faster access cityName = map.get(4).get(2).get(3).get(4).get("name").toStringContainer (); cityName.set("Ontario"); System.out.println ("Second town is " + cityName.toString());
The above example shows the flexibility of using the FlexMap, but in reality, you will want to call free()
on each return value of get()
on a FlexMap. We do provide finalize
overrides with each container ported to Java, but Java does not guarantee that such methods will ever be called. To make sure you free the underlying C++ memory, it is best practice to call free()
explicitly when you are done using a FlexMap.
Counters are abstractions intended to support aggregate counters across multiple participating agents. Counters provide a solution to the problem of the many writers problem for single variable increments/decrements. What a Counter does under the hood is give each agent a copy of the variable, and the agent only increments/decrements/sets their versions of the variable. When another agent wants to know, the aggregate count, they call toInteger()
or toRecord()
.
Location: ai.madara.knowledge.containers.Counter
Documentation: Map
Updating the Counter takes O(1) time. Retrieving a value from the Counter requires O(N) time, where N is the number of agents participating in the counting operation.
// import containers and KnowledgeBase import ai.madara.knowledge.containers.FlexMap; import ai.madara.knowledge.containers.String; import ai.madara.knowledge.KnowledgeBase; // create a knowledge base and make a multi-dimensional array KnowledgeBase knowledge = new KnowledgeBase(); Counter entries = new Counter(); entries.setName(knowledge, "entries"); // resize our entries counter to have 2 variables (2 agents), // with our id as 0 (the other agent would be 1) entries.resize(0, 2); // add 3 to the number of entries that you are maintaining entries.inc(); entries.inc(2); // other agents across the network may have updated their entry in "entries" System.out.println ("Total entries in network is : " + entries.toString());
When creating containers, each constructor supports a Madara::Knowledge_Engine::Knowledge_Update_Settings
instance that provides control over the updating and retrieving of variables from the context. However, no function in the container classes directly interacts with the transport layer.
Container classes are intended to provide fast, efficient, and convenient facades into the Knowledge Base to support object-oriented programming. In order to send these updates to other reasoning agents in the network, functions will need to be called on the Knowledge_Base. In this section of the Wiki, we'll discuss how the containers are intended to be used and when interactions over the network occur.
sendModifieds
functionIf the developer does not intend to use the evaluate or wait function on the Knowledge Base and intends to explicitly use MADARA containers, then the main option for communicating changes over the network is with the send_modifieds
function. After all relevant changes have been made through the container classes, simply call send_modifieds
.
To showcase this concept, we'll use a complete program that sends a profile over multicast.
import ai.madara.knowledge.KnowledgeBase; import ai.madara.knowledge.transport.QoSTransportSettings; import ai.madara.knowledge.transport.TransportType; import ai.madara.knowledge.containers.Map; //Create transport settings for a multicast transport QoSTransportSettings settings = new QoSTransportSettings (); settings.setHosts(new String[]{"239.255.0.1:4150"}); settings.setType(TransportType.MULTICAST_TRANSPORT); //create a knowledge base with the multicast transport settings KnowledgeBase knowledge = new KnowledgeBase("agent1", settings); // lock the context from any external updates until we're done knowledge.lock(); Map map = new Map(); map.setName(knowledge, ".profile.1"); // create profile fields for a man named John Smith at profile.1 map.set("name", "John Smith"); map.set("age", Integer (30)); map.set("funds", 300.50); map.set("photo1", "/images/profile/1/headshot.jpg"); // unlock the context to allow the network to update our context again knowledge.unlock(); /** * send the changes we've made to profile.1 over the network * These will show up as updates to profile.1.name, profile.1.age, * profile.1.funds, and profile.1.photo1 */ knowledge.sendModifieds(); // free the C++ resources map.free(); knowledge.free(); settings.free();
The more recommended way to use containers is within functions called from an evaluate or wait statement. There are several reasons for doing this:
Variables
or KnowledgeBase
classes, so there is nothing gained by using containers outside of function calls from evaluateIn the next example, we create a function called setProfile
that is called from an evaluate statement within the main function.
~~~~~~import com.madara.KnowledgeBase;
import ai.madara.knowledge.MadaraFunction;
import ai.madara.knowledge.KnowledgeRecord;
import ai.madara.knowledge.KnowledgeList;
import ai.madara.knowledge.Variables;
import someGpsModule;
private class SetProfile extends MadaraFunction
{
KnowledgeRecord execute(KnowledgeList args, Variables variables)
{
KnowledgeRecord result = new KnowledgeRecord();
// create a map with a prefix that points into the context Map profile = new Map(); profile.setName(variables, "profile.1"); // set profile information for John Smith profile.set("name", "John Smith"); profile.set("age", Integer (30)); profile.set("funds", 300.50); profile.set("photo1", "/images/profile/1/headshot.jpg"); profile.free(); return result;
}
}
// from a main function
//Create transport settings for a multicast transport
QoSTransportSettings settings = new QoSTransportSettings ();
settings.setHosts(new String[]{"239.255.0.1:4150"});
settings.setType(TransportType.MULTICAST_TRANSPORT);
//create a knowledge base with the multicast transport settings
KnowledgeBase knowledge = new KnowledgeBase("", settings);
// define the setProfile function within the knowledge base
knowledge.define_function("setProfile", new SetProfile());
// call the set_profile function and immediately send updates
knowledge.evaluate("setProfile()");
// sleep for a few seconds
knowledge.evaluate("#sleep(3)");
// free the C++ resources
knowledge.free();
settings.free();
~~~~~~
The Java module is fully documented with standard java documentation. Please use the API at JavaDocs or tell your editor of choice to load source files from the Git repository.
Java Guide Series
Architecture | Knowledge Base | Networking | Containers | Threads
Wiki: Home
Wiki: JavaInteractingWithTheKnowledgeBase
Wiki: JavaInteractingWithTheTransport
Wiki: JavaMadaraArchitecture
Wiki: JavaWorkingWithThreads