YARP
Yet Another Robot Platform
binexparser.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
7 #include <yarp/os/Log.h>
8 #include <cstdio>
9 #include <cstring>
10 #include <iostream>
11 #include <fstream>
12 #include <string>
13 #include <algorithm>
14 #include <cmath>
15 #include <cinttypes>
16 #include <climits>
17 #include <cstddef>
18 
19 
20 #define EXPNOT '~'
21 #define EXPAND '&'
22 #define EXPOR '|'
23 #define IMPLY ':'
24 
25 #define PRECISION(max_value) sizeof(max_value) * 8
26 
27 
28 using namespace yarp::manager;
29 
30 
32 
34 
35 bool BinaryExpParser::parse(std::string _exp)
36 {
37  expression = _exp;
38  std::string strexp = expression;
39  if (!checkExpression(strexp)) {
40  return false;
41  }
42 
43  binTree.clear();
44  operands.clear();
45  BinaryNodePtr root = nullptr;
46  parseExpression(strexp, root);
47  if(root == nullptr)
48  {
50  std::string msg = "BinaryExpParser: failed to parse the expression";
51  logger->addError(msg);
52  return false;
53  }
54  if(invalidOperands.size())
55  {
57  std::string msg = "Invalid operands";
58  for (const auto& invalidOperand : invalidOperands) {
59  msg += " '" + invalidOperand + "'";
60  }
61  logger->addError(msg);
62  return false;
63  }
64 
65  // creating a truth table for inputs and outputs
66  createTruthTable(operands.size());
67  int n = truthTable.size();
68  for(int x = 0; x < (1 << (n-1)); ++x)
69  {
70  auto itr = operands.begin();
71  for (int y = 0; y < (n - 1); ++y) {
72  (*itr++).second = (truthTable[y][x] != 0);
73  }
74  truthTable[n-1][x] = evalTree(root, operands);
75  }
76  //printTruthTable(leftOpr);
77  return true;
78 }
79 
80 bool BinaryExpParser::exportDotGraph(const char* szFileName)
81 {
82  std::ofstream dot;
83  dot.open(szFileName);
84  if (!dot.is_open()) {
85  return false;
86  }
87 
88  dot<<"digraph G {\n";
89  for(GraphIterator itr=binTree.begin(); itr!=binTree.end(); itr++)
90  {
91  switch((*itr)->getType()) {
92  case OPERATOR: {
93  auto node = (BinaryNodePtr)(*itr);
94  dot<<"\""<<node->getLabel()<<"\"";
95  dot<<" [label=\""<< node->getName()<<"\"";
96  dot<<" shape=circle, fillcolor=lightslategrey, style=filled];\n";
97  for(int i=0; i<node->sucCount(); i++)
98  {
99  Link l = node->getLinkAt(i);
100  auto to = (BinaryNodePtr)l.to();
101  dot<<"\""<<node->getLabel()<<"\" -> ";
102  dot<<"\""<<to->getLabel()<<"\"";
103  dot<<" [label=\"\"];\n";
104  }
105  break;
106  }
107  case OPERAND: {
108  auto node = (BinaryNodePtr)(*itr);
109  dot<<"\""<<node->getLabel()<<"\"";
110  dot<<" [label=\""<< node->getName()<<"\"";
111  dot<<" shape=square];\n";
112  break;
113  }
114  default:
115  break;
116  };
117  }
118  dot << "}\n";
119  dot.close();
120  return true;
121 }
122 
123 
124 bool BinaryExpParser::evalTree(BinaryNodePtr node, std::map<std::string, bool>& opnd)
125 {
126  bool result = false;
127  if(node->isLeaf())
128  {
129  node->setValue(opnd[node->getName()]);
130  result = node->getValue();
131  }
132  else
133  {
134  if(strcmp(node->getName(), "~") == 0)
135  {
136  auto left = (BinaryNodePtr)node->getLinkAt(0).to();
137  result = !evalTree(left, opnd);
138  node->setValue(result);
139  }
140  else if(strcmp(node->getName(), "&") == 0)
141  {
142  auto left = (BinaryNodePtr)node->getLinkAt(0).to();
143  auto right = (BinaryNodePtr)node->getLinkAt(1).to();
144  result = evalTree(left, opnd) && evalTree(right, opnd);
145  node->setValue(result);
146  }
147  else if(strcmp(node->getName(), "|") == 0)
148  {
149  auto left = (BinaryNodePtr)node->getLinkAt(0).to();
150  auto right = (BinaryNodePtr)node->getLinkAt(1).to();
151  result = evalTree(left, opnd) || evalTree(right, opnd);
152  node->setValue(result);
153  }
154  }
155  return result;
156 }
157 
158 bool isParentheses (char c)
159 {
160  return ((c=='(') || (c==')'));
161 }
162 
163 bool BinaryExpParser::checkExpression(std::string& strexp)
164 {
166 
167  if(std::count(strexp.begin(), strexp.end(), '(') !=
168  std::count(strexp.begin(), strexp.end(), ')'))
169  {
170  logger->addError("Incorrect expression format! (parentheses do not match)");
171  return false;
172  }
173  if(std::count(strexp.begin(), strexp.end(), IMPLY) != 1 )
174  {
175  logger->addError("Incorrect expression format! (no implication ':' found)");
176  return false;
177  }
178 
179  // erassing all the sapces
180  strexp.erase(std::remove_if(strexp.begin(), strexp.end(), ::isspace), strexp.end());
181  if(!strexp.size())
182  {
183  logger->addError("Empty expression!");
184  return false;
185  }
186 
187  // making a copy of strexp and checking more
188  std::string dummy = strexp;
189  // removing all pranteses
190  dummy.erase(std::remove_if(dummy.begin(), dummy.end(), isParentheses), dummy.end());
191  leftOpr = dummy.substr(0, dummy.find(IMPLY));
192  if(!leftOpr.size())
193  {
194  logger->addError("Missing operand before ':'");
195  return false;
196  }
197  if(dummy.find(IMPLY) == (dummy.size()-1))
198  {
199  logger->addError("Missing operands after ':'");
200  return false;
201  }
202 
203  dummy.erase(0, dummy.find(IMPLY)+1);
204  if(dummy.find(leftOpr) != std::string::npos)
205  {
206  std::string msg;
207  msg = "recursive assignment of operand '" + leftOpr + "'";
208  logger->addError(msg.c_str());
209  return false;
210  }
211 
212  // checking '~'
213  size_t n = dummy.find(EXPNOT);
214  while(n != std::string::npos)
215  {
216  OSTRINGSTREAM msg;
217  bool bError = ((n+1) == dummy.length()); // empty operand after ~
218  if((n+1) < dummy.length())
219  {
220  bError |= (dummy[n+1] == EXPAND); // operator & after ~
221  bError |= (dummy[n+1] == EXPOR); // operand | after ~
222  }
223  if (n != 0) {
224  bError |= (dummy[n - 1] != EXPAND) && (dummy[n - 1] != EXPOR); // an operand before ~
225  }
226  if(bError)
227  {
228  msg<<"Incorrect expression format of '~' at "<<(int)n;
229  logger->addError(msg.str().c_str());
230  return false;
231  }
232  n = dummy.find(EXPNOT, n+1);
233  }
234 
235  // checking '| &'
236  n = dummy.find_first_of("&|");
237  while(n != std::string::npos)
238  {
239  OSTRINGSTREAM msg;
240  bool bError = ((n+1) == dummy.length()); // empty operand after & or |
241  if((n+1) < dummy.length())
242  {
243  bError |= (dummy[n+1] == EXPAND); // operator & after & or |
244  bError |= (dummy[n+1] == EXPOR); // operand | after & or |
245  }
246  bError |= (n == 0); // empty operand before & or |
247  if(n != 0)
248  {
249  bError |= (dummy[n-1] == EXPAND); // operator & before & or |
250  bError |= (dummy[n-1] == EXPOR); // operand | before & or |
251  bError |= (dummy[n-1] == EXPOR); // operand ~ before & or |
252  }
253  if(bError)
254  {
255  msg<<"Incorrect expression format of '&' or '|' at "<<(int)n;
256  logger->addError(msg.str().c_str());
257  return false;
258  }
259  n = dummy.find_first_of("&|", n+1);
260  }
261 
262  // at the end
263  strexp.erase(0, strexp.find(IMPLY)+1);
264  return true;
265 }
266 
267 void BinaryExpParser::parseExpression(std::string &strexp, BinaryNodePtr& node)
268 {
269  std::string op;
270  BinaryNodePtr rightNode = nullptr;
271  parseNot(strexp, node);
272  while(!strexp.empty() &&
273  ((*strexp.begin() == EXPAND) ||
274  (*strexp.begin() == EXPOR)))
275  {
276  op = *strexp.begin();
277  strexp.erase(strexp.begin());
278  parseNot(strexp, rightNode);
279  BinaryNode tmpNode(op.c_str(), node, rightNode);
280  node = (BinaryNodePtr) binTree.addNode(&tmpNode);
281  }
282 }
283 
284 
285 void BinaryExpParser::parseNot(std::string &strexp, BinaryNodePtr& node) {
286  std::string op;
287  BinaryNodePtr rightNode;
288  if (*strexp.begin() != EXPNOT) {
289  parseFactor(strexp, node);
290  }
291  while(!strexp.empty() &&
292  (*strexp.begin() == EXPNOT))
293  {
294  op = *strexp.begin();
295  strexp.erase(strexp.begin());
296  parseFactor(strexp, rightNode);
297  BinaryNode tmpNode(op.c_str(), rightNode, nullptr);
298  node = (BinaryNodePtr) binTree.addNode(&tmpNode);
299  }
300 }
301 
302 void BinaryExpParser::parseFactor(std::string &strexp, BinaryNodePtr& node) {
303  if(!strexp.empty() && (*strexp.begin() != '('))
304  {
305  std::string op = popNextOperand(strexp);
306  BinaryNode tmpNode(op.c_str());
307  node = (BinaryNodePtr) binTree.addNode(&tmpNode);
308  operands[op] = false;
309  if(validOperands.size())
310  {
311  if (std::find(validOperands.begin(),
312  validOperands.end(),
313  op)
314  == validOperands.end()) {
315  invalidOperands.push_back(op);
316  }
317  }
318  }
319  else
320  {
321  strexp.erase(strexp.begin()); //skip '('
322  parseExpression(strexp, node);
323  strexp.erase(strexp.begin()); //skip ')'
324  }
325 }
326 
327 
328 std::string BinaryExpParser::getNextOperand(std::string &strexp) {
329  std::string token;
330  std::string::iterator it;
331  for (it = strexp.begin(); it != strexp.end(); ++it) {
332  if ((*it != EXPAND) && (*it != EXPOR) && (*it != EXPNOT) && (*it != ')') && (*it != '(')) {
333  token.push_back(*it);
334  } else {
335  break;
336  }
337  }
338  return token;
339 }
340 
341 std::string BinaryExpParser::popNextOperand(std::string &strexp) {
342  std::string token;
343  std::string::iterator it;
344  for (it = strexp.begin(); it != strexp.end(); ++it) {
345  if ((*it != EXPAND) && (*it != EXPOR) && (*it != EXPNOT) && (*it != ')') && (*it != '(')) {
346  token.push_back(*it);
347  } else {
348  strexp.erase(strexp.begin(), it);
349  break;
350  }
351  }
352  return token;
353 }
354 
355 void BinaryExpParser::createTruthTable(const int n)
356 {
357  yAssert((n-1) > 0);
358  yAssert((unsigned)(n-1) < PRECISION(INT_MAX));
359  yAssert(1 <= (INT_MAX >> (n-1)));
360 
361  truthTable.clear();
362  // n input + one output
363  truthTable.resize(n+1);
364  for (int i = 0; i < n + 1; i++) {
365  truthTable[i].resize(1 << n);
366  }
367  int num_to_fill = 1 << (n - 1);
368  for(int col = 0; col < n; ++col, num_to_fill >>= 1)
369  {
370  for(int row = num_to_fill; row < (1 << n); row += (num_to_fill * 2))
371  {
372  std::fill_n(&truthTable[col][row], num_to_fill, 1);
373  }
374  }
375 }
376 
377 void BinaryExpParser::printTruthTable(std::string lopr)
378 {
379  int n = truthTable.size();
380 
381  yAssert((n-1) > 0);
382  yAssert((unsigned)(n-1) < PRECISION(INT_MAX));
383  yAssert(1 <= (INT_MAX >> (n-1)));
384 
385  std::map<std::string, bool>::iterator itr;
386  for (itr = operands.begin(); itr != operands.end(); itr++) {
387  std::cout << (*itr).first << "\t";
388  }
389  std::cout << lopr << '\n';
390  for (int i = 0; i < (int)operands.size() * 8 + 1; i++) {
391  std::cout << "-";
392  }
393  std::cout << '\n';
394 
395  for(int x = 0; x < (1<<(n-1)); ++x)
396  {
397  for (int y = 0; y < n; ++y) {
398  std::cout << truthTable[y][x] << "\t";
399  }
400  std::cout << '\n';
401  }
402 }
403 
404 
405 bool LinkTrainer::train(const std::vector<std::vector<int> >& truthTable)
406 {
407  // resetting weights
408  int n = truthTable.size();
409  errors.clear();
410  alphas.clear();
411  alphas.resize(n-1);
412  std::fill(alphas.begin(), alphas.end(), 0.0);
413  bias = 0.0;
414  double sumerr = 1.0;
415  for(int i=0; (i<maxIteration) && (sumerr !=0); i++)
416  {
417  sumerr = 0;
418  // number of training set : (1 << (n-1)
419  for(int x = 0; x < (1 << (n-1)); ++x)
420  {
421  // computing output of one set
422  double P = truthTable[n-1][x];
423  double out = bias;
424  for (int y = 0; y < n - 1; ++y) {
425  out = out + ((double)truthTable[y][x] * alphas[y]);
426  }
427  out = (out>0) ? 1 : 0;
428  double err = P - out;
429  sumerr += fabs(err);
430  //std::cout<<P<<"\t"<<out<<"\terr:"<<err<<'\n';
431  bias = bias + trainRate * err;
432  for (int y = 0; y < (n - 1); ++y) {
433  alphas[y] = alphas[y] + (trainRate * err * (double)truthTable[y][x]);
434  }
435  }
436  errors.push_back(sumerr);
437  }
438  return ((int)errors.size() < maxIteration);
439 }
#define yAssert(x)
Definition: Log.h:294
#define EXPNOT
Definition: binexparser.cpp:20
bool isParentheses(char c)
#define PRECISION(max_value)
Definition: binexparser.cpp:25
#define EXPAND
Definition: binexparser.cpp:21
#define EXPOR
Definition: binexparser.cpp:22
#define IMPLY
Definition: binexparser.cpp:23
bool parse(std::string _exp)
Definition: binexparser.cpp:35
bool exportDotGraph(const char *szFileName)
Definition: binexparser.cpp:80
const char * getName()
Definition: binexparser.h:82
void setValue(bool val)
Definition: binexparser.h:80
Singleton class ErrorLogger.
Definition: utility.h:57
void addError(const char *szError)
Definition: utility.cpp:118
static ErrorLogger * Instance()
Singleton class ErrorLogger.
Definition: utility.cpp:98
Class GraphIterator.
Definition: graph.h:67
GraphIterator begin()
Definition: graph.cpp:165
Node * addNode(Node *node)
Definition: graph.cpp:17
GraphIterator end()
Definition: graph.cpp:172
bool train(const std::vector< std::vector< int > > &truthTable)
bool isLeaf()
Definition: node.h:88
Link & getLinkAt(int index)
Definition: node.h:96
BinaryNode * BinaryNodePtr
Definition: binexparser.h:92
std::stringstream OSTRINGSTREAM
Definition: utility.h:49
double dot(const yarp::sig::Vector &a, const yarp::sig::Vector &b)
Scalar product between vectors (defined in Math.h).
Definition: math.cpp:475