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