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', '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_COMPLEX(self, t):
119 r'(?<!\w)complex(?=\()' 120 return t
121 - def t_FUNCTION(self, t):
122 r'(cmath\.){0,1}[a-zA-Z_][0-9a-zA-Z_]*(?=\()' 123 return t
124 - def t_VARIABLE(self, t):
125 r'[a-zA-Z_][0-9a-zA-Z_]*' 126 return t
127 128 t_NUMBER = r'([0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)([eE][+-]{0,1}[0-9]+){0,1}' 129 t_POWER = r'\*\*' 130 131 t_ignore = " \t" 132 133 re_cmath_function = re.compile("cmath\.(?P<name>[0-9a-zA-Z_]+)") 134
135 - def t_newline(self, t):
136 r'\n+' 137 t.lexer.lineno += t.value.count("\n")
138
139 - def t_error(self, t):
140 logger.error("Illegal character '%s'" % t.value[0]) 141 t.lexer.skip(1)
142 143 # Build the lexer
144 - def build(self,**kwargs):
145 self.lexer = lex.lex(module=self, **kwargs)
146 147 # Definitions for the PLY yacc parser 148 # Parsing rules 149 precedence = ( 150 ('right', 'LOGICALCOMB'), 151 ('right', 'LOGICAL'), 152 ('right','IF'), 153 ('right','ELSE'), 154 ('left','='), 155 ('left','+','-'), 156 ('left','*','/'), 157 ('right','UMINUS'), 158 ('left','POWER'), 159 ('right','REGLOG'), 160 ('right','ARG'), 161 ('right','CSC'), 162 ('right','SEC'), 163 ('right','ACSC'), 164 ('right','ASEC'), 165 ('right','SQRT'), 166 ('right','CONJ'), 167 ('right','RE'), 168 ('right','IM'), 169 ('right','FUNCTION'), 170 ('right','COMPLEX'), 171 ('right','COND'), 172 ) 173 174 # Dictionary of parser expressions
175 - def p_statement_expr(self, p):
176 'statement : expression' 177 self.parsed_string = p[1]
178
179 - def p_expression_binop(self, p):
180 '''expression : expression '=' expression 181 | expression '+' expression 182 | expression '-' expression 183 | expression '*' expression 184 | expression '/' expression''' 185 p[0] = p[1] + p[2] + p[3]
186
187 - def p_expression_logical(self, p):
188 '''boolexpression : expression LOGICAL expression''' 189 if p[2] not in self.logical_equiv: 190 p[0] = p[1] + p[2] + p[3] 191 else: 192 p[0] = p[1] + self.logical_equiv[p[2]] + p[3]
193
194 - def p_expression_logicalcomb(self, p):
195 '''boolexpression : boolexpression LOGICALCOMB boolexpression''' 196 if p[2] not in self.logical_equiv: 197 p[0] = p[1] + p[2] + p[3] 198 else: 199 p[0] = p[1] + self.logical_equiv[p[2]] + p[3]
200
201 - def p_expression_uminus(self, p):
202 "expression : '-' expression %prec UMINUS" 203 p[0] = '-' + p[2]
204
205 - def p_group_parentheses(self, p):
206 "group : '(' expression ')'" 207 p[0] = '(' + p[2] +')'
208
209 - def p_group_parentheses_boolexpr(self, p):
210 "boolexpression : '(' boolexpression ')'" 211 p[0] = '(' + p[2] +')'
212
213 - def p_expression_group(self, p):
214 "expression : group" 215 p[0] = p[1]
216
217 - def p_expression_function1(self, p):
218 "expression : FUNCTION '(' expression ')'" 219 p1 = p[1] 220 re_groups = self.re_cmath_function.match(p1) 221 if re_groups: 222 p1 = re_groups.group("name") 223 p[0] = p1 + '(' + p[3] + ')'
224
225 - def p_expression_function2(self, p):
226 "expression : FUNCTION '(' expression ',' expression ')'" 227 p1 = p[1] 228 re_groups = self.re_cmath_function.match(p1) 229 if re_groups: 230 p1 = re_groups.group("name") 231 p[0] = p1 + '(' + p[3] + ',' + p[5] + ')'
232
233 - def p_error(self, p):
234 if p: 235 raise ModelError("Syntax error at '%s' (%s)." %(p.value,p)) 236 else: 237 logger.error("Syntax error at EOF") 238 self.parsed_string = "Error"
239
240 -class UFOExpressionParserFortran(UFOExpressionParser):
241 """A parser for UFO algebraic expressions, outputting 242 Fortran-style code.""" 243 244 # The following parser expressions need to be defined for each 245 # output language/framework 246 247 logical_equiv = {'==':'.EQ.', 248 '>=':'.GE.', 249 '<=':'.LE.', 250 '!=':'.NE.', 251 '>':'.GT.', 252 '<':'.LT.', 253 'or':'.OR.', 254 'and':'.AND.'} 255
256 - def p_expression_number(self, p):
257 "expression : NUMBER" 258 p[0] = ('%e' % float(p[1])).replace('e', 'd')
259
260 - def p_expression_variable(self, p):
261 "expression : VARIABLE" 262 p[0] = p[1].lower()
263
264 - def p_expression_power(self, p):
265 'expression : expression POWER expression' 266 try: 267 p3 = float(p[3].replace('d','e')) 268 # Chebck if exponent is an integer 269 if p3 == int(p3): 270 p3 = str(int(p3)) 271 p[0] = p[1] + "**" + p3 272 else: 273 p[0] = p[1] + "**" + p[3] 274 except Exception: 275 p[0] = p[1] + "**" + p[3]
276
277 - def p_expression_if(self,p):
278 "expression : expression IF boolexpression ELSE expression " 279 p[0] = 'CONDIF(%s,DCMPLX(%s),DCMPLX(%s))' % (p[3], p[1], p[5])
280
281 - def p_expression_ifimplicit(self,p):
282 "expression : expression IF expression ELSE expression " 283 p[0] = 'CONDIF(DCMPLX(%s).NE.(0d0,0d0),DCMPLX(%s),DCMPLX(%s))'\ 284 %(p[3], p[1], p[5])
285
286 - def p_expression_cond(self, p):
287 "expression : COND '(' expression ',' expression ',' expression ')'" 288 p[0] = 'COND(DCMPLX('+p[3]+'),DCMPLX('+p[5]+'),DCMPLX('+p[7]+'))'
289
290 - def p_expression_complex(self, p):
291 "expression : COMPLEX '(' expression ',' expression ')'" 292 p[0] = '(' + p[3] + ',' + p[5] + ')'
293
294 - def p_expression_func(self, p):
295 '''expression : CSC group 296 | SEC group 297 | ACSC group 298 | ASEC group 299 | RE group 300 | IM group 301 | ARG group 302 | SQRT group 303 | CONJ group 304 | REGLOG group''' 305 if p[1] == 'csc': p[0] = '1d0/cos' + p[2] 306 elif p[1] == 'sec': p[0] = '1d0/sin' + p[2] 307 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')' 308 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')' 309 elif p[1] == 're': p[0] = 'dble' + p[2] 310 elif p[1] == 'im': p[0] = 'dimag' + p[2] 311 elif p[1] == 'arg': p[0] = 'arg(DCMPLX'+p[2]+')' 312 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2] 313 elif p[1] == 'complexconjugate': p[0] = 'conjg(DCMPLX' + p[2]+')' 314 elif p[1] == 'reglog': p[0] = 'reglog(DCMPLX' + p[2] +')'
315
316 - def p_expression_pi(self, p):
317 '''expression : PI''' 318 p[0] = 'pi'
319
320 -class UFOExpressionParserMPFortran(UFOExpressionParserFortran):
321 """A parser for UFO algebraic expressions, outputting 322 Fortran-style code for quadruple precision computation.""" 323 324 mp_prefix = check_param_card.ParamCard.mp_prefix 325 326 # The following parser expressions need to be defined for each 327 # output language/framework 328
329 - def p_expression_number(self, p):
330 "expression : NUMBER" 331 p[0] = '%e_16' % float(p[1])
332
333 - def p_expression_variable(self, p):
334 "expression : VARIABLE" 335 # All the multiple_precision variables are defined with the prefix _MP_" 336 p[0] = (self.mp_prefix+p[1]).lower()
337
338 - def p_expression_power(self, p):
339 'expression : expression POWER expression' 340 try: 341 p3 = float(p[3].replace('_16','')) 342 # Check if exponent is an integer 343 if p3 == int(p3): 344 p3 = str(int(p3)) 345 p[0] = p[1] + "**" + p3 346 else: 347 p[0] = p[1] + "**" + p[3] 348 except Exception: 349 p[0] = p[1] + "**" + p[3]
350
351 - def p_expression_if(self,p):
352 "expression : expression IF boolexpression ELSE expression " 353 p[0] = 'MP_CONDIF(%s,CMPLX(%s,KIND=16),CMPLX(%s,KIND=16))' % (p[3], p[1], p[5])
354
355 - def p_expression_ifimplicit(self,p):
356 "expression : expression IF expression ELSE expression " 357 p[0] = 'MP_CONDIF(CMPLX(%s,KIND=16).NE.(0.0e0_16,0.0e0_16),CMPLX(%s,KIND=16),CMPLX(%s,KIND=16))'\ 358 %(p[3], p[1], p[5])
359
360 - def p_expression_cond(self, p):
361 "expression : COND '(' expression ',' expression ',' expression ')'" 362 p[0] = 'MP_COND(CMPLX('+p[3]+',KIND=16),CMPLX('+p[5]+\ 363 ',KIND=16),CMPLX('+p[7]+',KIND=16))'
364
365 - def p_expression_func(self, p):
366 '''expression : CSC group 367 | SEC group 368 | ACSC group 369 | ASEC group 370 | RE group 371 | IM group 372 | ARG group 373 | SQRT group 374 | CONJ group 375 | REGLOG group''' 376 if p[1] == 'csc': p[0] = '1e0_16/cos' + p[2] 377 elif p[1] == 'sec': p[0] = '1e0_16/sin' + p[2] 378 elif p[1] == 'acsc': p[0] = 'asin(1e0_16/' + p[2] + ')' 379 elif p[1] == 'asec': p[0] = 'acos(1e0_16/' + p[2] + ')' 380 elif p[1] == 're': p[0] = 'real' + p[2] 381 elif p[1] == 'im': p[0] = 'imag' + p[2] 382 elif p[1] == 'arg': p[0] = 'mp_arg(CMPLX(' + p[2] + ',KIND=16))' 383 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2] 384 elif p[1] == 'complexconjugate': p[0] = 'conjg(CMPLX(' + p[2] + ',KIND=16))' 385 elif p[1] == 'reglog': p[0] = 'mp_reglog(CMPLX(' + p[2] +',KIND=16))'
386
387 - def p_expression_pi(self, p):
388 '''expression : PI''' 389 p[0] = self.mp_prefix+'pi'
390
391 -class UFOExpressionParserCPP(UFOExpressionParser):
392 """A parser for UFO algebraic expressions, outputting 393 C++-style code.""" 394 395 logical_equiv = {'==':'==', 396 '>=':'>=', 397 '<=':'<=', 398 '!=':'!=', 399 '>':'>', 400 '<':'<', 401 'or':'||', 402 'and':'&&'} 403 404 # The following parser expressions need to be defined for each 405 # output language/framework 406
407 - def p_expression_number(self, p):
408 'expression : NUMBER' 409 p[0] = p[1] 410 # Check number is an integer, if so add "." 411 if float(p[1]) == int(float(p[1])) and float(p[1]) < 1000: 412 p[0] = str(int(float(p[1]))) + '.'
413
414 - def p_expression_variable(self, p):
415 'expression : VARIABLE' 416 p[0] = p[1]
417
418 - def p_expression_if(self,p):
419 "expression : expression IF boolexpression ELSE expression " 420 p[0] = '(%s ? %s : %s)' % (p[3], p[1], p[5])
421
422 - def p_expression_ifimplicit(self,p):
423 "expression : expression IF expression ELSE expression " 424 p[0] = '(%s ? %s : %s)' % (p[3], p[1], p[5])
425
426 - def p_expression_cond(self, p):
427 "expression : COND '(' expression ',' expression ',' expression ')'" 428 p[0] = 'COND('+p[3]+','+p[5]+','+p[7]+')'
429
430 - def p_expression_power(self, p):
431 'expression : expression POWER expression' 432 p1=p[1] 433 p3=p[3] 434 if p[1][0] == '(' and p[1][-1] == ')': 435 p1 = p[1][1:-1] 436 if p[3][0] == '(' and p[3][-1] == ')': 437 p3 = p[3][1:-1] 438 p[0] = 'pow(' + p1 + ',' + p3 + ')'
439
440 - def p_expression_complex(self, p):
441 "expression : COMPLEX '(' expression ',' expression ')'" 442 p[0] = 'std::complex<double>(' + p[3] + ',' + p[5] + ')'
443
444 - def p_expression_func(self, p):
445 '''expression : CSC group 446 | SEC group 447 | ACSC group 448 | ASEC group 449 | RE group 450 | IM group 451 | ARG group 452 | SQRT group 453 | CONJ group 454 | REGLOG group ''' 455 if p[1] == 'csc': p[0] = '1./cos' + p[2] 456 elif p[1] == 'sec': p[0] = '1./sin' + p[2] 457 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')' 458 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')' 459 elif p[1] == 're': p[0] = 'real' + p[2] 460 elif p[1] == 'im': p[0] = 'imag' + p[2] 461 elif p[1] == 'arg':p[0] = 'arg' + p[2] 462 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2] 463 elif p[1] == 'complexconjugate': p[0] = 'conj' + p[2] 464 elif p[1] == 'reglog': p[0] = 'reglog' + p[2]
465
466 - def p_expression_pi(self, p):
467 '''expression : PI''' 468 p[0] = 'M_PI'
469 470 471 472 # Main program, allows to interactively test the parser 473 if __name__ == '__main__': 474 475 if len(sys.argv) == 1: 476 print "Please specify a parser: fortran, mpfortran or c++" 477 exit() 478 if sys.argv[1] == "fortran": 479 calc = UFOExpressionParserFortran() 480 elif sys.argv[1] == "mpfortran": 481 calc = UFOExpressionParserMPFortran() 482 elif sys.argv[1] == "c++": 483 calc = UFOExpressionParserCPP() 484 elif sys.argv[1] == "aloha": 485 calc = UFOExpressionParserCPP() 486 else: 487 print "Please specify a parser: fortran, mpfortran or c++" 488 print "You gave", sys.argv[1] 489 exit() 490 491 while 1: 492 try: 493 s = raw_input('calc > ') 494 except EOFError: 495 break 496 if not s: continue 497 print calc.parse(s) 498