Use inheritance for my first program (cpp)

Who is the intruder ?

Find the intruder

Now that we know how to launch our program, it's time to add a touch of intelligence

getUp

Let us begin by making our program understand the meaning of each word. To do that, we will use the getUp function. This function applied to an individual returns us all the classes of which our individual inherits.

std::vector<std::vector<std::string> > getWordsInheritance(std::vector<std::string> words)
{
std::vector<std::vector<std::string> > inheritances;
for(auto& word : words)
{
std::vector<std::string> inheritance = onto_->individuals.getUp(word);
inheritances.push_back(inheritance);
}

return inheritances;
}

This function, therefore, applies the getUp method to each word found in the "words" vector and it returns a vector for which each cell corresponds to the vector returned by getUp.

std::vector<std::string> inheritance = onto_->individuals.getUp(word);

We also notice that we are going to look for the getUp function among the individuals because we know that the input words will be only individuals.

In order to see the effect of this function, we will add the following lines in our main function, just after the call to the "say" function.

std::vector<std::vector<std::string> > inheritances = getWordsInheritance(names_);
for(size_t i = 0; i < inheritances.size(); i++)
{
std::cout << names_[i] << " is a : ";
for(auto& type : inheritances[i])
std::cout << type << " ";
std::cout << std::endl;
}

We can now recompile our project and launch it!

catkin_make

roslaunch ontologenius_tutorial intruder.launch

In another terminal we will publish a few words to see the result:

rostopic pub -1 /intruder/input std_msgs/String "data: 'bob'"
rostopic pub -1 /intruder/input std_msgs/String "data: 'alice'"
rostopic pub -1 /intruder/input std_msgs/String "data: 'kevin'"
rostopic pub -1 /intruder/input std_msgs/String "data: 'start'"

At the end of our program, you should have this:

[SAY]on bob alice kevin
bob is a : human man agent
alice is a : human woman agent
kevin is a : human man agent
[SAY]Let's play!

Our program knows what each word means

We can represent this part of the ontology as follow:

tutorial 1 inheritance graph

Delete the same

We will now create a function that will simply remove classes from which all words inherit. If we look at the previous example, we can see that Alice, Bob, and Kevin all inherit active and human classes. That is to say that the agent and human classes can not allow us to determine the intruder and that we can therefore remove them from our reasoning. We will not analyze this code together because it is not the center of this tutorial.

void deleteTheSame(std::vector<std::vector<std::string> >& inheritances)
{
if(inheritances.size() > 1)
{
std::vector<std::string> sames;
for(size_t words = 0; words < inheritances[0].size(); words++)
{
bool is_same = true;
for(size_t i = 1; i < inheritances.size(); i++)
{
std::vector<std::string>::iterator it = find(inheritances[i].begin(), inheritances[i].end(), inheritances.front()[words]);
if(it == inheritances[i].end())
is_same = false;
}

if(is_same)
sames.push_back(inheritances.front()[words]);
}

for(auto& same : sames)
for(auto& inheritance : inheritances)
{
std::vector<std::string>::iterator it = find(inheritance.begin(), inheritance.end(), same);
inheritance.erase(it);
}
}
}

Add the call to this function in the main function just after the getWordsInheritance call.

std::vector<std::vector<std::string> > inheritances = getWordsInheritance(names_);
deleteTheSame(inheritances);

Compile this and run the program, then in another terminal re-query the programs on the words: bob, alice, and kevin.

This time, you should have this:

[SAY]on bob alice kevin
bob is a : man
alice is a : woman
kevin is a : man
[SAY]Let's play!

Find the intruder

Now that we have removed the classes from which all individuals inherit, we are not going to look for the individual who is the only one to inherit from a class but rather the class of which all but one inherit.

That's exactly what the next function will do.

Let's start by defining a structure that will represent an intruder with his name and the reason he is the intruder.

struct intruder_t
{
std::string name_;
std::string why_;
};

We will finally code algorithms to find intruders. Indeed, in the function, it is sometimes possible to find several intruders for different reasons.

Once again, we will not analyze the following algorithm because it does not use ontologenius directly.

To still be able to analyze it for yourself, here are some key elements:

  • We go through all the words of each heritage vector. For each of these words, we seek to know if it exists in all other vectors except one.
  • The intruder_index variable represents the index of the vector in which the search word does not exist. This variable is set to -1 as long as we have not found or found more than one vector that does not have the word.
  • In the case where we found an intruder, we add it to our intruder vector, only if it does not already exist for the same reason.
std::vector<struct intruder_t> find_intruder(std::vector<std::string>& names, std::vector<std::vector<std::string> >& inheritances)
{
std::vector<struct intruder_t> intruders;

for(size_t vect_i = 0; vect_i < inheritances.size(); vect_i++)
for(size_t class_i = 0; class_i < inheritances[vect_i].size(); class_i++)
{
int intruder_index = -1;

for(size_t vect_j = 0; vect_j < inheritances.size(); vect_j++)
if(vect_j != vect_i)
{
std::vector<std::string>::iterator it = find(inheritances[vect_j].begin(), inheritances[vect_j].end(), inheritances[vect_i][class_i]);
if(it == inheritances[vect_j].end())
{
if(intruder_index == -1)
intruder_index = vect_j;
else
{
intruder_index = -1;
break;
}
}
}

if(intruder_index != -1)
{
struct intruder_t intruder;
intruder.name_ = names[intruder_index];
intruder.why_ = inheritances[vect_i][class_i];

bool exist = false;
for(auto& intruder_i : intruders)
if((intruder_i.name_ == intruder.name_) && (intruder_i.why_ == intruder.why_))
exist = true;

if(exist == false)
intruders.push_back(intruder);
}
}

return intruders;
}

We can therefore call this new function in our main function and replace our old display with the intruder display.

deleteTheSame(inheritances);
std::vector<struct intruder_t> intruders = find_intruder(names_, inheritances);
for(auto& intruder : intruders)
say("the intruder is " + intruder.name_ + " because it is the only one that is not: " + intruder.why_);

Compile this and run the program, then in another terminal re-query the programs on the words: bob, alice, and kevin.

This time, you should have this:

[SAY]on bob alice kevin
[SAY]the intruder is alice because it is the only one that is not: man
[SAY]Let's play!

IT WORKS!

Now is the time to have a little fun. Test for example with the words banana, lemon, apple, and carrot or only with the words banana, lemon, and carrot.

You should have this:

[SAY]on banana lemon apple carrot
[SAY]the intruder is carrot because it is the only one that is not: fruit
[SAY]Let's play!
[SAY]on banana lemon carrot
[SAY]the intruder is carrot because it is the only one that is not: yellow
[SAY]the intruder is carrot because it is the only one that is not: fruit
[SAY]Let's play!

Do not hesitate to look at all the available words and see how our algorithm reacts.