Package madgraph :: Package iolibs :: Module ufo_expression_parsers
[hide private]
[frames] | no frames]

Source Code for Module madgraph.iolibs.ufo_expression_parsers

  1  ################################################################################ 
  2  # 
  3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
  4  # 
  5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
  6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
  7  # high-energy processes in the Standard Model and beyond. 
  8  # 
  9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
 10  # distribution. 
 11  # 
 12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
 13  # 
 14  ################################################################################ 
 15   
 16  """Parsers for algebraic expressions coming from UFO, outputting into 
 17  different languages/frameworks (Fortran and Pythia8). Uses the PLY 3.3 
 18  Lex + Yacc framework""" 
 19   
 20  import logging 
 21  import os 
 22  import re 
 23  import sys 
 24   
 25  root_path = os.path.split(os.path.dirname(os.path.realpath( __file__ )))[0] 
 26  sys.path.append(os.path.join(root_path, os.path.pardir)) 
 27   
 28  from madgraph import MadGraph5Error 
 29  import vendor.ply.lex as lex 
 30  import vendor.ply.yacc as yacc 
 31  import models.check_param_card as check_param_card 
 32   
 33  logger = logging.getLogger('madgraph.ufo_parsers') 
 34   
 35  # PLY lexer class 
 36   
37 -class ModelError(MadGraph5Error):
38 """Appropriate Error for a wrong parsing"""
39
40 -class UFOExpressionParser(object):
41 """A base class for parsers for algebraic expressions coming from UFO.""" 42 43 parsed_string = "" 44 logical_equiv = {} 45
46 - def __init__(self, **kw):
47 """Initialize the lex and yacc""" 48 49 modname = self.__class__.__name__ 50 self.debugfile = os.path.devnull 51 self.tabmodule = os.path.join(root_path, "iolibs", modname + "_" + "parsetab.py") 52 lex.lex(module=self, debug=0) 53 self.y=yacc.yacc(module=self, debug=0, debugfile=self.debugfile, 54 tabmodule=self.tabmodule)
55
56 - def parse(self, buf):
57 """Parse the string buf""" 58 self.y.parse(buf) 59 return self.parsed_string
60 61 # List of tokens and literals 62 tokens = ( 63 'LOGICAL','LOGICALCOMB','POWER', 'CSC', 'SEC', 'ACSC', 'ASEC', 64 'SQRT', 'CONJ', 'RE', 'RE2', 'IM', 'PI', 'COMPLEX', 'FUNCTION', 'IF','ELSE', 65 'VARIABLE', 'NUMBER','COND','REGLOG', 'ARG' 66 ) 67 literals = "=+-*/()," 68 69 # Definition of tokens 70
71 - def t_CSC(self, t):
72 r'(?<!\w)csc(?=\()' 73 return t
74 - def t_SEC(self, t):
75 r'(?<!\w)sec(?=\()' 76 return t
77 - def t_ACSC(self, t):
78 r'(?<!\w)acsc(?=\()' 79 return t
80 - def t_ASEC(self, t):
81 r'(?<!\w)asec(?=\()' 82 return t
83 - def t_REGLOG(self, t):
84 r'(?<!\w)reglog(?=\()' 85 return t
86 - def t_COND(self, t):
87 r'(?<!\w)cond(?=\()' 88 return t
89 - def t_ARG(self,t):
90 r'(?<!\w)arg(?=\()'
91 - def t_IF(self, t):
92 r'(?<!\w)if\s' 93 return t
94 - def t_ELSE(self, t):
95 r'(?<!\w)else\s' 96 return t
97 - def t_LOGICAL(self, t):
98 r'==|!=|<=|>=|<|>' 99 return t
100 - def t_LOGICALCOMB(self, t):
101 r'(?<!\w)and(?=[\s\(])|(?<!\w)or(?=[\s\(])' 102 return t
103 - def t_SQRT(self, t):
104 r'cmath\.sqrt' 105 return t
106 - def t_PI(self, t):
107 r'cmath\.pi' 108 return t
109 - def t_CONJ(self, t):
110 r'complexconjugate' 111 return t
112 - def t_IM(self, t):
113 r'(?<!\w)im(?=\()' 114 return t
115 - def t_RE(self, t):
116 r'(?<!\w)re(?=\()' 117 return t
118 - def t_RE2(self, t):
119 r'\.real|\.imag' 120 return t
121
122 - def t_COMPLEX(self, t):
123 r'(?<!\w)complex(?=\()' 124 return t
125 - def t_FUNCTION(self, t):
126 r'(cmath\.){0,1}[a-zA-Z_][0-9a-zA-Z_]*(?=\()' 127 return t
128 - def t_VARIABLE(self, t):
129 r'[a-zA-Z_][0-9a-zA-Z_]*' 130 return t
131 132 t_NUMBER = r'([0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)([eE][+-]{0,1}[0-9]+){0,1}j{0,1}' 133 t_POWER = r'\*\*' 134 135 t_ignore = " \t" 136 137 re_cmath_function = re.compile("cmath\.(?P<name>[0-9a-zA-Z_]+)") 138
139 - def t_newline(self, t):
140 r'\n+' 141 t.lexer.lineno += t.value.count("\n")
142
143 - def t_error(self, t):
144 logger.error("Illegal character '%s'" % t.value[0]) 145 t.lexer.skip(1)
146 147 # Build the lexer
148 - def build(self,**kwargs):
149 self.lexer = lex.lex(module=self, **kwargs)
150 151 # Definitions for the PLY yacc parser 152 # Parsing rules 153 precedence = ( 154 ('right', 'LOGICALCOMB'), 155 ('right', 'LOGICAL'), 156 ('right','IF'), 157 ('right','ELSE'), 158 ('left','='), 159 ('left','+','-'), 160 ('left','*','/'), 161 ('left', 'RE2'), 162 ('right','UMINUS'), 163 ('left','POWER'), 164 ('right','REGLOG'), 165 ('right','ARG'), 166 ('right','CSC'), 167 ('right','SEC'), 168 ('right','ACSC'), 169 ('right','ASEC'), 170 ('right','SQRT'), 171 ('right','CONJ'), 172 ('right','RE'), 173 ('right','IM'), 174 ('right','FUNCTION'), 175 ('right','COMPLEX'), 176 ('right','COND'), 177 ) 178 179 # Dictionary of parser expressions
180 - def p_statement_expr(self, p):
181 'statement : expression' 182 self.parsed_string = p[1]
183
184 - def p_expression_binop(self, p):
185 '''expression : expression '=' expression 186 | expression '+' expression 187 | expression '-' expression 188 | expression '*' expression 189 | expression '/' expression''' 190 p[0] = p[1] + p[2] + p[3]
191
192 - def p_expression_logical(self, p):
193 '''boolexpression : expression LOGICAL expression''' 194 if p[2] not in self.logical_equiv: 195 p[0] = p[1] + p[2] + p[3] 196 else: 197 p[0] = p[1] + self.logical_equiv[p[2]] + p[3]
198
199 - def p_expression_logicalcomb(self, p):
200 '''boolexpression : boolexpression LOGICALCOMB boolexpression''' 201 if p[2] not in self.logical_equiv: 202 p[0] = p[1] + p[2] + p[3] 203 else: 204 p[0] = p[1] + self.logical_equiv[p[2]] + p[3]
205
206 - def p_expression_uminus(self, p):
207 "expression : '-' expression %prec UMINUS" 208 p[0] = '-' + p[2]
209
210 - def p_group_parentheses(self, p):
211 "group : '(' expression ')'" 212 p[0] = '(' + p[2] +')'
213
214 - def p_group_parentheses_boolexpr(self, p):
215 "boolexpression : '(' boolexpression ')'" 216 p[0] = '(' + p[2] +')'
217
218 - def p_expression_group(self, p):
219 "expression : group" 220 p[0] = p[1]
221
222 - def p_expression_function1(self, p):
223 "expression : FUNCTION '(' expression ')'" 224 p1 = p[1] 225 re_groups = self.re_cmath_function.match(p1) 226 if re_groups: 227 p1 = re_groups.group("name") 228 p[0] = p1 + '(' + p[3] + ')'
229
230 - def p_expression_function2(self, p):
231 "expression : FUNCTION '(' expression ',' expression ')'" 232 p1 = p[1] 233 re_groups = self.re_cmath_function.match(p1) 234 if re_groups: 235 p1 = re_groups.group("name") 236 p[0] = p1 + '(' + p[3] + ',' + p[5] + ')'
237
238 - def p_error(self, p):
239 if p: 240 raise ModelError("Syntax error at '%s' (%s)." %(p.value,p)) 241 else: 242 logger.error("Syntax error at EOF") 243 self.parsed_string = "Error"
244
245 -class UFOExpressionParserFortran(UFOExpressionParser):
246 """A parser for UFO algebraic expressions, outputting 247 Fortran-style code.""" 248 249 # The following parser expressions need to be defined for each 250 # output language/framework 251 252 logical_equiv = {'==':'.EQ.', 253 '>=':'.GE.', 254 '<=':'.LE.', 255 '!=':'.NE.', 256 '>':'.GT.', 257 '<':'.LT.', 258 'or':'.OR.', 259 'and':'.AND.'} 260
261 - def p_expression_number(self, p):
262 "expression : NUMBER" 263 if p[1].endswith('j'): 264 p[0] = ('DCOMPLX(0d0, %e)' % float(p[1][:-1])).replace('e', 'd') 265 else: 266 p[0] = ('%e' % float(p[1])).replace('e', 'd')
267
268 - def p_expression_variable(self, p):
269 "expression : VARIABLE" 270 p[0] = p[1].lower()
271
272 - def p_expression_power(self, p):
273 'expression : expression POWER expression' 274 try: 275 p3 = float(p[3].replace('d','e')) 276 # Chebck if exponent is an integer 277 if p3 == int(p3): 278 p3 = str(int(p3)) 279 p[0] = p[1] + "**" + p3 280 else: 281 p[0] = p[1] + "**" + p[3] 282 except Exception: 283 p[0] = p[1] + "**" + p[3]
284
285 - def p_expression_if(self,p):
286 "expression : expression IF boolexpression ELSE expression " 287 p[0] = 'CONDIF(%s,DCMPLX(%s),DCMPLX(%s))' % (p[3], p[1], p[5])
288
289 - def p_expression_ifimplicit(self,p):
290 "expression : expression IF expression ELSE expression " 291 p[0] = 'CONDIF(DCMPLX(%s).NE.(0d0,0d0),DCMPLX(%s),DCMPLX(%s))'\ 292 %(p[3], p[1], p[5])
293
294 - def p_expression_cond(self, p):
295 "expression : COND '(' expression ',' expression ',' expression ')'" 296 p[0] = 'COND(DCMPLX('+p[3]+'),DCMPLX('+p[5]+'),DCMPLX('+p[7]+'))'
297
298 - def p_expression_complex(self, p):
299 "expression : COMPLEX '(' expression ',' expression ')'" 300 p[0] = '(' + p[3] + ',' + p[5] + ')'
301
302 - def p_expression_func(self, p):
303 '''expression : CSC group 304 | SEC group 305 | ACSC group 306 | ASEC group 307 | RE group 308 | IM group 309 | ARG group 310 | SQRT group 311 | CONJ group 312 | REGLOG group''' 313 if p[1] == 'csc': p[0] = '1d0/cos' + p[2] 314 elif p[1] == 'sec': p[0] = '1d0/sin' + p[2] 315 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')' 316 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')' 317 elif p[1] == 're': p[0] = 'dble' + p[2] 318 elif p[1] == 'im': p[0] = 'dimag' + p[2] 319 elif p[1] == 'arg': p[0] = 'arg(DCMPLX'+p[2]+')' 320 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2] 321 elif p[1] == 'complexconjugate': p[0] = 'conjg(DCMPLX' + p[2]+')' 322 elif p[1] == 'reglog': p[0] = 'reglog(DCMPLX' + p[2] +')'
323 324
325 - def p_expression_real(self, p):
326 ''' expression : expression RE2 ''' 327 328 if p[2] == '.real': 329 if p[1].startswith('('): 330 p[0] = 'dble' +p[1] 331 else: 332 p[0] = 'dble(%s)' % p[1] 333 elif p[2] == '.imag': 334 if p[1].startswith('('): 335 p[0] = 'dimag' +p[1] 336 else: 337 p[0] = 'dimag(%s)' % p[1]
338
339 - def p_expression_pi(self, p):
340 '''expression : PI''' 341 p[0] = 'pi'
342
343 -class UFOExpressionParserMPFortran(UFOExpressionParserFortran):
344 """A parser for UFO algebraic expressions, outputting 345 Fortran-style code for quadruple precision computation.""" 346 347 mp_prefix = check_param_card.ParamCard.mp_prefix 348 349 # The following parser expressions need to be defined for each 350 # output language/framework 351
352 - def p_expression_number(self, p):
353 "expression : NUMBER" 354 355 if p[1].endswith('j'): 356 p[0] = 'CMPLX(0.000000e+00_16, %e_16 ,KIND=16)' % float(p[1][:-1]) 357 else: 358 p[0] = '%e_16' % float(p[1])
359
360 - def p_expression_variable(self, p):
361 "expression : VARIABLE" 362 # All the multiple_precision variables are defined with the prefix _MP_" 363 p[0] = (self.mp_prefix+p[1]).lower()
364
365 - def p_expression_power(self, p):
366 'expression : expression POWER expression' 367 try: 368 p3 = float(p[3].replace('_16','')) 369 # Check if exponent is an integer 370 if p3 == int(p3): 371 p3 = str(int(p3)) 372 p[0] = p[1] + "**" + p3 373 else: 374 p[0] = p[1] + "**" + p[3] 375 except Exception: 376 p[0] = p[1] + "**" + p[3]
377
378 - def p_expression_if(self,p):
379 "expression : expression IF boolexpression ELSE expression " 380 p[0] = 'MP_CONDIF(%s,CMPLX(%s,KIND=16),CMPLX(%s,KIND=16))' % (p[3], p[1], p[5])
381
382 - def p_expression_ifimplicit(self,p):
383 "expression : expression IF expression ELSE expression " 384 p[0] = 'MP_CONDIF(CMPLX(%s,KIND=16).NE.(0.0e0_16,0.0e0_16),CMPLX(%s,KIND=16),CMPLX(%s,KIND=16))'\ 385 %(p[3], p[1], p[5])
386
387 - def p_expression_cond(self, p):
388 "expression : COND '(' expression ',' expression ',' expression ')'" 389 p[0] = 'MP_COND(CMPLX('+p[3]+',KIND=16),CMPLX('+p[5]+\ 390 ',KIND=16),CMPLX('+p[7]+',KIND=16))'
391
392 - def p_expression_func(self, p):
393 '''expression : CSC group 394 | SEC group 395 | ACSC group 396 | ASEC group 397 | RE group 398 | IM group 399 | ARG group 400 | SQRT group 401 | CONJ group 402 | REGLOG group''' 403 if p[1] == 'csc': p[0] = '1e0_16/cos' + p[2] 404 elif p[1] == 'sec': p[0] = '1e0_16/sin' + p[2] 405 elif p[1] == 'acsc': p[0] = 'asin(1e0_16/' + p[2] + ')' 406 elif p[1] == 'asec': p[0] = 'acos(1e0_16/' + p[2] + ')' 407 elif p[1] == 're': p[0] = 'real' + p[2] 408 elif p[1] == 'im': p[0] = 'imag' + p[2] 409 elif p[1] == 'arg': p[0] = 'mp_arg(CMPLX(' + p[2] + ',KIND=16))' 410 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2] 411 elif p[1] == 'complexconjugate': p[0] = 'conjg(CMPLX(' + p[2] + ',KIND=16))' 412 elif p[1] == 'reglog': p[0] = 'mp_reglog(CMPLX(' + p[2] +',KIND=16))'
413
414 - def p_expression_real(self, p):
415 ''' expression : expression RE2 ''' 416 417 if p[2] == '.real': 418 if p[1].startswith('('): 419 p[0] = 'real' +p[1] 420 else: 421 p[0] = 'real(%s)' % p[1] 422 elif p[2] == '.imag': 423 if p[1].startswith('('): 424 p[0] = 'imag' +p[1] 425 else: 426 p[0] = 'imag(%s)' % p[1]
427 428
429 - def p_expression_pi(self, p):
430 '''expression : PI''' 431 p[0] = self.mp_prefix+'pi'
432
433 -class UFOExpressionParserCPP(UFOExpressionParser):
434 """A parser for UFO algebraic expressions, outputting 435 C++-style code.""" 436 437 logical_equiv = {'==':'==', 438 '>=':'>=', 439 '<=':'<=', 440 '!=':'!=', 441 '>':'>', 442 '<':'<', 443 'or':'||', 444 'and':'&&'} 445 446 # The following parser expressions need to be defined for each 447 # output language/framework 448
449 - def p_expression_number(self, p):
450 'expression : NUMBER' 451 452 if p[1].endswith('j'): 453 p[0] = 'std::complex<double>(0., %e)' % float(p[1][:-1]) 454 else: 455 p[0] = ('%e' % float(p[1])).replace('e', 'd') 456 457 458 p[0] = p[1] 459 # Check number is an integer, if so add "." 460 if float(p[1]) == int(float(p[1])) and float(p[1]) < 1000: 461 p[0] = str(int(float(p[1]))) + '.'
462
463 - def p_expression_variable(self, p):
464 'expression : VARIABLE' 465 p[0] = p[1]
466
467 - def p_expression_if(self,p):
468 "expression : expression IF boolexpression ELSE expression " 469 p[0] = '(%s ? %s : %s)' % (p[3], p[1], p[5])
470
471 - def p_expression_ifimplicit(self,p):
472 "expression : expression IF expression ELSE expression " 473 p[0] = '(%s ? %s : %s)' % (p[3], p[1], p[5])
474
475 - def p_expression_cond(self, p):
476 "expression : COND '(' expression ',' expression ',' expression ')'" 477 p[0] = 'COND('+p[3]+','+p[5]+','+p[7]+')'
478
479 - def p_expression_power(self, p):
480 'expression : expression POWER expression' 481 p1=p[1] 482 p3=p[3] 483 if p[1][0] == '(' and p[1][-1] == ')': 484 p1 = p[1][1:-1] 485 if p[3][0] == '(' and p[3][-1] == ')': 486 p3 = p[3][1:-1] 487 p[0] = 'pow(' + p1 + ',' + p3 + ')'
488
489 - def p_expression_complex(self, p):
490 "expression : COMPLEX '(' expression ',' expression ')'" 491 p[0] = 'std::complex<double>(' + p[3] + ',' + p[5] + ')'
492
493 - def p_expression_func(self, p):
494 '''expression : CSC group 495 | SEC group 496 | ACSC group 497 | ASEC group 498 | RE group 499 | IM group 500 | ARG group 501 | SQRT group 502 | CONJ group 503 | REGLOG group ''' 504 if p[1] == 'csc': p[0] = '1./cos' + p[2] 505 elif p[1] == 'sec': p[0] = '1./sin' + p[2] 506 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')' 507 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')' 508 elif p[1] == 're': p[0] = 'real' + p[2] 509 elif p[1] == 'im': p[0] = 'imag' + p[2] 510 elif p[1] == 'arg':p[0] = 'arg' + p[2] 511 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2] 512 elif p[1] == 'complexconjugate': p[0] = 'conj' + p[2] 513 elif p[1] == 'reglog': p[0] = 'reglog' + p[2]
514
515 - def p_expression_real(self, p):
516 ''' expression : expression RE2 ''' 517 518 if p[2] == '.real': 519 if p[1].startswith('('): 520 p[0] = 'real' +p[1] 521 else: 522 p[0] = 'real(%s)' % p[1] 523 elif p[2] == '.imag': 524 if p[1].startswith('('): 525 p[0] = 'imag' +p[1] 526 else: 527 p[0] = 'imag(%s)' % p[1]
528 529
530 - def p_expression_pi(self, p):
531 '''expression : PI''' 532 p[0] = 'M_PI'
533 534 535 536 # Main program, allows to interactively test the parser 537 if __name__ == '__main__': 538 539 if len(sys.argv) == 1: 540 print "Please specify a parser: fortran, mpfortran or c++" 541 exit() 542 if sys.argv[1] == "fortran": 543 calc = UFOExpressionParserFortran() 544 elif sys.argv[1] == "mpfortran": 545 calc = UFOExpressionParserMPFortran() 546 elif sys.argv[1] == "c++": 547 calc = UFOExpressionParserCPP() 548 elif sys.argv[1] == "aloha": 549 calc = UFOExpressionParserCPP() 550 else: 551 print "Please specify a parser: fortran, mpfortran or c++" 552 print "You gave", sys.argv[1] 553 exit() 554 555 while 1: 556 try: 557 s = raw_input('calc > ') 558 except EOFError: 559 break 560 if not s: continue 561 print calc.parse(s) 562