YARP
Yet Another Robot Platform
binexparser.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * All rights reserved.
4  *
5  * This software may be modified and distributed under the terms of the
6  * BSD-3-Clause license. See the accompanying LICENSE file for details.
7  */
8 
10 #include <yarp/os/Log.h>
11 #include <cstdio>
12 #include <cstring>
13 #include <iostream>
14 #include <fstream>
15 #include <string>
16 #include <algorithm>
17 #include <cmath>
18 #include <cinttypes>
19 #include <climits>
20 #include <cstddef>
21 
22 
23 #define EXPNOT '~'
24 #define EXPAND '&'
25 #define EXPOR '|'
26 #define IMPLY ':'
27 
28 #define PRECISION(max_value) sizeof(max_value) * 8
29 
30 
31 using namespace std;
32 using namespace yarp::manager;
33 
34 
35 BinaryExpParser::BinaryExpParser() = default;
36 
37 BinaryExpParser::~BinaryExpParser() = default;
38 
39 bool BinaryExpParser::parse(string _exp)
40 {
41  expression = _exp;
42  string strexp = expression;
43  if(!checkExpression(strexp))
44  return false;
45 
46  binTree.clear();
47  operands.clear();
48  BinaryNodePtr root = nullptr;
49  parseExpression(strexp, root);
50  if(root == nullptr)
51  {
52  ErrorLogger* logger = ErrorLogger::Instance();
53  string msg = "BinaryExpParser: failed to parse the expression";
54  logger->addError(msg);
55  return false;
56  }
57  if(invalidOperands.size())
58  {
59  ErrorLogger* logger = ErrorLogger::Instance();
60  string msg = "Invalid operands";
61  for(const auto& invalidOperand : invalidOperands)
62  msg += " '" + invalidOperand + "'";
63  logger->addError(msg);
64  return false;
65  }
66 
67  // creating a truth table for inputs and outputs
68  createTruthTable(operands.size());
69  int n = truthTable.size();
70  for(int x = 0; x < (1 << (n-1)); ++x)
71  {
72  auto itr = operands.begin();
73  for(int y = 0; y < (n-1); ++y)
74  (*itr++).second = (truthTable[y][x] != 0);
75  truthTable[n-1][x] = evalTree(root, operands);
76  }
77  //printTruthTable(leftOpr);
78  return true;
79 }
80 
81 bool BinaryExpParser::exportDotGraph(const char* szFileName)
82 {
83  ofstream dot;
84  dot.open(szFileName);
85  if(!dot.is_open())
86  return false;
87 
88  dot<<"digraph G {"<<endl;
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];"<<endl;
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=\"\"];"<<endl;
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];"<<endl;
112  break;
113  }
114  default:
115  break;
116  };
117  }
118  dot<<"}"<<endl;
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 {
165  ErrorLogger* logger = ErrorLogger::Instance();
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  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) != 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 != 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  if(bError)
226  {
227  msg<<"Incorrect expression format of '~' at "<<(int)n;
228  logger->addError(msg.str().c_str());
229  return false;
230  }
231  n = dummy.find(EXPNOT, n+1);
232  }
233 
234  // checking '| &'
235  n = dummy.find_first_of("&|");
236  while(n != string::npos)
237  {
238  OSTRINGSTREAM msg;
239  bool bError = ((n+1) == dummy.length()); // empty operand after & or |
240  if((n+1) < dummy.length())
241  {
242  bError |= (dummy[n+1] == EXPAND); // operator & after & or |
243  bError |= (dummy[n+1] == EXPOR); // operand | after & or |
244  }
245  bError |= (n == 0); // empty operand before & or |
246  if(n != 0)
247  {
248  bError |= (dummy[n-1] == EXPAND); // operator & before & or |
249  bError |= (dummy[n-1] == EXPOR); // operand | before & or |
250  bError |= (dummy[n-1] == EXPOR); // operand ~ before & or |
251  }
252  if(bError)
253  {
254  msg<<"Incorrect expression format of '&' or '|' at "<<(int)n;
255  logger->addError(msg.str().c_str());
256  return false;
257  }
258  n = dummy.find_first_of("&|", n+1);
259  }
260 
261  // at the end
262  strexp.erase(0, strexp.find(IMPLY)+1);
263  return true;
264 }
265 
266 void BinaryExpParser::parseExpression(std::string &strexp, BinaryNodePtr& node)
267 {
268  string op;
269  BinaryNodePtr rightNode = nullptr;
270  parseNot(strexp, node);
271  while(!strexp.empty() &&
272  ((*strexp.begin() == EXPAND) ||
273  (*strexp.begin() == EXPOR)))
274  {
275  op = *strexp.begin();
276  strexp.erase(strexp.begin());
277  parseNot(strexp, rightNode);
278  BinaryNode tmpNode(op.c_str(), node, rightNode);
279  node = (BinaryNodePtr) binTree.addNode(&tmpNode);
280  }
281 }
282 
283 
284 void BinaryExpParser::parseNot(std::string &strexp, BinaryNodePtr& node) {
285  string op;
286  BinaryNodePtr rightNode;
287  if(*strexp.begin() != EXPNOT)
288  parseFactor(strexp, node);
289  while(!strexp.empty() &&
290  (*strexp.begin() == EXPNOT))
291  {
292  op = *strexp.begin();
293  strexp.erase(strexp.begin());
294  parseFactor(strexp, rightNode);
295  BinaryNode tmpNode(op.c_str(), rightNode, nullptr);
296  node = (BinaryNodePtr) binTree.addNode(&tmpNode);
297  }
298 }
299 
300 void BinaryExpParser::parseFactor(std::string &strexp, BinaryNodePtr& node) {
301  if(!strexp.empty() && (*strexp.begin() != '('))
302  {
303  string op = popNextOperand(strexp);
304  BinaryNode tmpNode(op.c_str());
305  node = (BinaryNodePtr) binTree.addNode(&tmpNode);
306  operands[op] = false;
307  if(validOperands.size())
308  {
309  if(std::find(validOperands.begin(),
310  validOperands.end(), op) == validOperands.end())
311  invalidOperands.push_back(op);
312  }
313  }
314  else
315  {
316  strexp.erase(strexp.begin()); //skip '('
317  parseExpression(strexp, node);
318  strexp.erase(strexp.begin()); //skip ')'
319  }
320 }
321 
322 
323 std::string BinaryExpParser::getNextOperand(std::string &strexp) {
324  string token;
325  std::string::iterator it;
326  for (it=strexp.begin(); it!=strexp.end(); ++it)
327  if((*it != EXPAND) && (*it != EXPOR) && (*it != EXPNOT) &&
328  (*it != ')') && (*it != '('))
329  token.push_back(*it);
330  else
331  break;
332  return token;
333 }
334 
335 std::string BinaryExpParser::popNextOperand(std::string &strexp) {
336  string token;
337  std::string::iterator it;
338  for (it=strexp.begin(); it!=strexp.end(); ++it)
339  if((*it != EXPAND) && (*it != EXPOR) && (*it != EXPNOT) &&
340  (*it != ')') && (*it != '('))
341  token.push_back(*it);
342  else
343  {
344  strexp.erase(strexp.begin(), it);
345  break;
346  }
347  return token;
348 }
349 
350 void BinaryExpParser::createTruthTable(const int n)
351 {
352  yAssert((n-1) > 0);
353  yAssert((unsigned)(n-1) < PRECISION(INT_MAX));
354  yAssert(1 <= (INT_MAX >> (n-1)));
355 
356  truthTable.clear();
357  // n input + one output
358  truthTable.resize(n+1);
359  for(int i=0; i<n+1; i++)
360  truthTable[i].resize(1 << n);
361  int num_to_fill = 1 << (n - 1);
362  for(int col = 0; col < n; ++col, num_to_fill >>= 1)
363  {
364  for(int row = num_to_fill; row < (1 << n); row += (num_to_fill * 2))
365  {
366  std::fill_n(&truthTable[col][row], num_to_fill, 1);
367  }
368  }
369 }
370 
371 void BinaryExpParser::printTruthTable(std::string lopr)
372 {
373  int n = truthTable.size();
374 
375  yAssert((n-1) > 0);
376  yAssert((unsigned)(n-1) < PRECISION(INT_MAX));
377  yAssert(1 <= (INT_MAX >> (n-1)));
378 
379  map<string, bool>::iterator itr;
380  for(itr=operands.begin(); itr!=operands.end(); itr++)
381  cout<<(*itr).first<<"\t";
382  cout<<lopr<<endl;
383  for(int i=0; i<(int)operands.size()*8+1; i++)
384  cout<<"-";
385  cout<<endl;
386 
387  for(int x = 0; x < (1<<(n-1)); ++x)
388  {
389  for(int y = 0; y < n; ++y)
390  std::cout<< truthTable[y][x] << "\t";
391  std::cout<<std::endl;
392  }
393 }
394 
395 
396 bool LinkTrainer::train(const std::vector<std::vector<int> >& truthTable)
397 {
398  // resetting weights
399  int n = truthTable.size();
400  errors.clear();
401  alphas.clear();
402  alphas.resize(n-1);
403  std::fill(alphas.begin(), alphas.end(), 0.0);
404  bias = 0.0;
405  double sumerr = 1.0;
406  for(int i=0; (i<maxIteration) && (sumerr !=0); i++)
407  {
408  sumerr = 0;
409  // number of training set : (1 << (n-1)
410  for(int x = 0; x < (1 << (n-1)); ++x)
411  {
412  // computing output of one set
413  double P = truthTable[n-1][x];
414  double out = bias;
415  for(int y = 0; y < n-1; ++y)
416  out = out + ((double)truthTable[y][x] * alphas[y]);
417  out = (out>0) ? 1 : 0;
418  double err = P - out;
419  sumerr += fabs(err);
420  //cout<<P<<"\t"<<out<<"\terr:"<<err<<endl;
421  bias = bias + trainRate * err;
422  for(int y = 0; y < (n-1); ++y)
423  alphas[y] = alphas[y] + (trainRate * err * (double)truthTable[y][x]);
424  }
425  errors.push_back(sumerr);
426  }
427  return ((int)errors.size() < maxIteration);
428 }
EXPAND
#define EXPAND
Definition: binexparser.cpp:24
EXPOR
#define EXPOR
Definition: binexparser.cpp:25
yarp::manager::BinaryNode::getName
const char * getName()
Definition: binexparser.h:85
yarp::manager::GraphIterator
Class GraphIterator.
Definition: graph.h:70
yarp::manager::OSTRINGSTREAM
std::stringstream OSTRINGSTREAM
Definition: utility.h:52
yarp::manager
Definition: application.h:24
yarp::manager::ErrorLogger
Singleton class ErrorLogger.
Definition: utility.h:60
yarp::manager::BinaryNodePtr
BinaryNode * BinaryNodePtr
Definition: binexparser.h:95
yarp::math::dot
double dot(const yarp::sig::Vector &a, const yarp::sig::Vector &b)
Scalar product between vectors (defined in Math.h).
Definition: math.cpp:461
Log.h
yarp::manager::Node::isLeaf
bool isLeaf()
Definition: node.h:91
yarp::manager::OPERAND
@ OPERAND
Definition: binexparser.h:26
yarp::manager::ErrorLogger::addError
void addError(const char *szError)
Definition: utility.cpp:121
yarp::manager::BinaryNode::setValue
void setValue(bool val)
Definition: binexparser.h:83
yarp::manager::exportDotGraph
bool exportDotGraph(Graph &graph, const char *szFileName)
Definition: utility.cpp:337
yarp::manager::OPERATOR
@ OPERATOR
Definition: binexparser.h:25
yarp::manager::BinaryNode::getValue
bool getValue()
Definition: binexparser.h:82
yarp::manager::Node::getLinkAt
Link & getLinkAt(int index)
Definition: node.h:99
yarp::manager::BinaryNode
Definition: binexparser.h:33
IMPLY
#define IMPLY
Definition: binexparser.cpp:26
binexparser.h
PRECISION
#define PRECISION(max_value)
Definition: binexparser.cpp:28
yAssert
#define yAssert(x)
Definition: Log.h:297
EXPNOT
#define EXPNOT
Definition: binexparser.cpp:23
isParentheses
bool isParentheses(char c)
Definition: binexparser.cpp:158