Package madgraph :: Package various :: Module diagram_symmetry
[hide private]
[frames] | no frames]

Source Code for Module madgraph.various.diagram_symmetry

  1  ################################################################################ 
  2  # 
  3  # Copyright (c) 2010 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  """Module for calculation of symmetries between diagrams, by 
 17  evaluating amp2 values for permutations of momenta.""" 
 18   
 19  from __future__ import division 
 20   
 21  import array 
 22  import copy 
 23  import fractions 
 24  import itertools 
 25  import logging 
 26  import math 
 27  import os 
 28  import re 
 29  import signal 
 30   
 31  import aloha.aloha_writers as aloha_writers 
 32  import aloha.create_aloha as create_aloha 
 33   
 34  import madgraph.iolibs.export_python as export_python 
 35  import madgraph.iolibs.group_subprocs as group_subprocs 
 36  import madgraph.iolibs.helas_call_writers as helas_call_writer 
 37  import models.import_ufo as import_ufo 
 38  import madgraph.iolibs.save_load_object as save_load_object 
 39   
 40  import madgraph.core.base_objects as base_objects 
 41  import madgraph.loop.loop_base_objects as loop_base_objects 
 42  import madgraph.core.helas_objects as helas_objects 
 43  import madgraph.loop.loop_helas_objects as loop_helas_objects 
 44   
 45  import madgraph.core.color_algebra as color 
 46  import madgraph.core.color_amp as color_amp 
 47  import madgraph.core.helas_objects as helas_objects 
 48  import madgraph.core.diagram_generation as diagram_generation 
 49   
 50  import madgraph.various.process_checks as process_checks 
 51  import madgraph.various.misc as misc 
 52   
 53  from madgraph import MG5DIR 
 54   
 55  import models.model_reader as model_reader 
 56  import aloha.template_files.wavefunctions as wavefunctions 
 57  from aloha.template_files.wavefunctions import \ 
 58       ixxxxx, oxxxxx, vxxxxx, sxxxxx 
 59   
 60  #=============================================================================== 
 61  # Logger for process_checks 
 62  #=============================================================================== 
 63   
 64  logger = logging.getLogger('madgraph.various.diagram_symmetry') 
65 66 #=============================================================================== 67 # find_symmetry 68 #=============================================================================== 69 70 -def find_symmetry(matrix_element):
71 """Find symmetries between amplitudes by comparing diagram tags 72 for all the diagrams in the process. Identical diagram tags 73 correspond to different external particle permutations of the same 74 diagram. 75 76 Return list of positive number corresponding to number of 77 symmetric diagrams and negative numbers corresponding to the 78 equivalent diagram (for e+e->3a, get [6, -1, -1, -1, -1, -1]), 79 list of the corresponding permutations needed, and list of all 80 permutations of identical particles.""" 81 82 if isinstance(matrix_element, group_subprocs.SubProcessGroup): 83 return find_symmetry_subproc_group(matrix_element) 84 85 nexternal, ninitial = matrix_element.get_nexternal_ninitial() 86 87 # diagram_numbers is a list of all relevant diagram numbers 88 diagram_numbers = [] 89 # Prepare the symmetry vector with non-used amp2s (due to 90 # multiparticle vertices) 91 symmetry = [] 92 permutations = [] 93 ident_perms = [] 94 process = matrix_element.get('processes')[0] 95 base_model = process.get('model') 96 97 if isinstance(matrix_element, loop_helas_objects.LoopHelasMatrixElement): 98 # For loop induced processes we consider only the loops (no R2) and 99 # the shrunk diagram instead of the lcut one. 100 FDStructRepo = loop_base_objects.FDStructureList([]) 101 base_diagrams = base_objects.DiagramList( 102 [(d.get_contracted_loop_diagram(base_model,FDStructRepo) if 103 isinstance(d,loop_base_objects.LoopDiagram) else d) for d in 104 matrix_element.get('base_amplitude').get('loop_diagrams') \ 105 if d.get('type')>0]) 106 diagrams = matrix_element.get_loop_diagrams() 107 else: 108 diagrams = matrix_element.get('diagrams') 109 base_diagrams = matrix_element.get_base_amplitude().get('diagrams') 110 111 vert_list = [max(diag.get_vertex_leg_numbers()) for diag in diagrams if \ 112 diag.get_vertex_leg_numbers()!=[]] 113 min_vert = min(vert_list) if vert_list!=[] else 0 114 115 for diag in diagrams: 116 diagram_numbers.append(diag.get('number')) 117 permutations.append(range(nexternal)) 118 if diag.get_vertex_leg_numbers()!=[] and \ 119 max(diag.get_vertex_leg_numbers()) > min_vert: 120 # Ignore any diagrams with 4-particle vertices 121 symmetry.append(0) 122 else: 123 symmetry.append(1) 124 125 # Check for matrix elements with no identical particles 126 if matrix_element.get("identical_particle_factor") == 1: 127 return symmetry, \ 128 permutations,\ 129 [range(nexternal)] 130 131 logger.info("Finding symmetric diagrams for process %s" % \ 132 matrix_element.get('processes')[0].nice_string().\ 133 replace("Process: ", "")) 134 135 # diagram_tags is a list of unique tags 136 diagram_tags = [] 137 # diagram_classes is a list of lists of diagram numbers belonging 138 # to the different classes 139 diagram_classes = [] 140 perms = [] 141 for diag, base_diagram in zip(diagrams, base_diagrams): 142 if any([vert > min_vert for vert in 143 diag.get_vertex_leg_numbers()]): 144 # Only 3-vertices allowed in configs.inc 145 continue 146 147 tag = diagram_generation.DiagramTag(base_diagram) 148 try: 149 ind = diagram_tags.index(tag) 150 except ValueError: 151 diagram_classes.append([diag.get('number')]) 152 perms.append([tag.get_external_numbers()]) 153 diagram_tags.append(tag) 154 else: 155 diagram_classes[ind].append(diag.get('number')) 156 perms[ind].append(tag.get_external_numbers()) 157 158 for inum, diag_number in enumerate(diagram_numbers): 159 if symmetry[inum] == 0: 160 continue 161 idx1 = [i for i, d in enumerate(diagram_classes) if \ 162 diag_number in d][0] 163 idx2 = diagram_classes[idx1].index(diag_number) 164 if idx2 == 0: 165 symmetry[inum] = len(diagram_classes[idx1]) 166 else: 167 symmetry[inum] = -diagram_classes[idx1][0] 168 # Order permutations according to how to reach the first perm 169 permutations[inum] = diagram_generation.DiagramTag.reorder_permutation(perms[idx1][idx2], 170 perms[idx1][0]) 171 # ident_perms ordered according to order of external momenta 172 perm = diagram_generation.DiagramTag.reorder_permutation(perms[idx1][0], 173 perms[idx1][idx2]) 174 if not perm in ident_perms: 175 ident_perms.append(perm) 176 177 return (symmetry, permutations, ident_perms)
178
179 -def find_symmetry_by_evaluation(matrix_element, evaluator, max_time = 600):
180 """Find symmetries between amplitudes by comparing the squared 181 amplitudes for all permutations of identical particles. 182 183 Return list of positive number corresponding to number of 184 symmetric diagrams and negative numbers corresponding to the 185 equivalent diagram (for e+e->3a, get [6, -1, -1, -1, -1, -1]), 186 list of the corresponding permutations needed, and list of all 187 permutations of identical particles. 188 max_time gives a cutoff time for finding symmetries (in s).""" 189 190 #if isinstance(matrix_element, group_subprocs.SubProcessGroup): 191 # return find_symmetry_subproc_group(matrix_element, evaluator, max_time) 192 193 assert isinstance(matrix_element, helas_objects.HelasMatrixElement) 194 195 # Exception class and routine to handle timeout 196 class TimeOutError(Exception): 197 pass
198 def handle_alarm(signum, frame): 199 raise TimeOutError 200 201 (nexternal, ninitial) = matrix_element.get_nexternal_ninitial() 202 vert_list = [max(diag.get_vertex_leg_numbers()) for diag in \ 203 matrix_element.get('diagrams') if diag.get_vertex_leg_numbers()!=[]] 204 min_vert = min(vert_list) if vert_list!=[] else 0 205 # Prepare the symmetry vector with non-used amp2s (due to 206 # multiparticle vertices) 207 symmetry = [] 208 for diag in matrix_element.get('diagrams'): 209 # It used to be hardcoded to three instead of min_vert. Need to check 210 # if it is ok to use the general min_vert instead. 211 if diag.get_vertex_leg_numbers()!=[] and \ 212 max(diag.get_vertex_leg_numbers()) > min_vert: 213 # Ignore any diagrams with 4-particle vertices 214 symmetry.append(0) 215 else: 216 symmetry.append(1) 217 218 # Check for matrix elements with no identical particles 219 if matrix_element.get("identical_particle_factor") == 1: 220 return symmetry, \ 221 [range(nexternal)]*len(symmetry),\ 222 [range(nexternal)] 223 224 logger.info("Finding symmetric diagrams for process %s" % \ 225 matrix_element.get('processes')[0].nice_string().\ 226 replace("Process: ", "")) 227 228 process = matrix_element.get('processes')[0] 229 base_model = process.get('model') 230 equivalent_process = base_objects.Process({\ 231 'legs': base_objects.LegList([base_objects.Leg({ 232 'id': wf.get('pdg_code'), 233 'state': wf.get('leg_state')}) \ 234 for wf in matrix_element.get_external_wavefunctions()]), 235 'model': base_model}) 236 237 # Get phase space point 238 p, w_rambo = evaluator.get_momenta(equivalent_process) 239 240 # Check matrix element value for all permutations 241 amp2start = [] 242 final_states = [l.get('id') for l in \ 243 equivalent_process.get('legs')[ninitial:]] 244 nperm = 0 245 perms = [] 246 ident_perms = [] 247 248 # Set timeout for max_time 249 signal.signal(signal.SIGALRM, handle_alarm) 250 signal.alarm(max_time) 251 try: 252 for perm in itertools.permutations(range(ninitial, nexternal)): 253 if [equivalent_process.get('legs')[i].get('id') for i in perm] != \ 254 final_states: 255 # Non-identical particles permutated 256 continue 257 ident_perms.append([0,1]+list(perm)) 258 nperm += 1 259 new_p = p[:ninitial] + [p[i] for i in perm] 260 261 res = evaluator.evaluate_matrix_element(matrix_element, new_p) 262 if not res: 263 break 264 me_value, amp2 = res 265 # Make a list with (8-pos value, magnitude) to easily compare 266 amp2sum = sum(amp2) 267 amp2mag = [] 268 for a in amp2: 269 a = a*me_value/max(amp2sum, 1e-30) 270 if a > 0: 271 amp2mag.append(int(math.floor(math.log10(abs(a))))) 272 else: 273 amp2mag.append(0) 274 amp2 = [(int(a*10**(8-am)), am) for (a, am) in zip(amp2, amp2mag)] 275 276 if not perms: 277 # This is the first iteration - initialize lists 278 # Initiate symmetry with all 1:s 279 symmetry = [1 for i in range(len(amp2))] 280 # Store initial amplitudes 281 amp2start = amp2 282 # Initialize list of permutations 283 perms = [range(nexternal) for i in range(len(amp2))] 284 continue 285 286 for i, val in enumerate(amp2): 287 if val == (0,0): 288 # If amp2 is 0, just set symmetry to 0 289 symmetry[i] = 0 290 continue 291 # Only compare with diagrams below this one 292 if val in amp2start[:i]: 293 ind = amp2start.index(val) 294 # Replace if 1) this amp is unmatched (symmetry[i] > 0) or 295 # 2) this amp is matched but matched to an amp larger 296 # than ind 297 if symmetry[ind] > 0 and \ 298 (symmetry[i] > 0 or \ 299 symmetry[i] < 0 and -symmetry[i] > ind + 1): 300 symmetry[i] = -(ind+1) 301 perms[i] = [0, 1] + list(perm) 302 symmetry[ind] += 1 303 except TimeOutError: 304 # Symmetry canceled due to time limit 305 logger.warning("Cancel diagram symmetry - time exceeded") 306 307 # Stop the alarm since we're done with this process 308 signal.alarm(0) 309 310 return (symmetry, perms, ident_perms) 311
312 #=============================================================================== 313 # DiagramTag class to identify matrix elements 314 #=============================================================================== 315 316 -class IdentifySGConfigTag(diagram_generation.DiagramTag):
317 """DiagramTag daughter class to identify diagrams giving the same 318 config. Need to compare state, spin, mass, width, and color. 319 Warning: If changing this tag, then also CanonicalConfigTag in 320 helas_objects.py must be changed! 321 """ 322 323 @staticmethod 344 345 @staticmethod
346 - def vertex_id_from_vertex(vertex, last_vertex, model, ninitial):
347 """Returns the info needed to identify symmetric configs: 348 interaction color, mass, width.""" 349 350 inter = model.get_interaction(vertex.get('id')) 351 352 if last_vertex: 353 return (0,) 354 else: 355 part = model.get_particle(vertex.get('legs')[-1].get('id')) 356 try: 357 QCD = inter.get('orders')['QCD'] 358 except Exception, error: 359 QCD = 0 360 361 return ((part.get('color'), 362 part.get('mass'), part.get('width'), QCD),)
363
364 -def find_symmetry_subproc_group(subproc_group):
365 """Find symmetric configs by directly comparing the configurations 366 using IdentifySGConfigTag.""" 367 368 assert isinstance(subproc_group, group_subprocs.SubProcessGroup),\ 369 "Argument to find_symmetry_subproc_group has to be SubProcessGroup" 370 371 # diagram_numbers is a list of all relevant diagram numbers 372 diagram_numbers = [] 373 # Prepare the symmetry vector with non-used amp2s (due to 374 # multiparticle vertices) 375 symmetry = [] 376 permutations = [] 377 diagrams = subproc_group.get('mapping_diagrams') 378 nexternal, ninitial = \ 379 subproc_group.get('matrix_elements')[0].get_nexternal_ninitial() 380 model = subproc_group.get('matrix_elements')[0].get('processes')[0].\ 381 get('model') 382 vert_list = [max(diag.get_vertex_leg_numbers()) for diag in diagrams if \ 383 diag.get_vertex_leg_numbers()!=[]] 384 min_vert = min(vert_list) if vert_list!=[] else 0 385 386 for idiag,diag in enumerate(diagrams): 387 diagram_numbers.append(idiag+1) 388 permutations.append(range(nexternal)) 389 if diag.get_vertex_leg_numbers()!=[] and \ 390 max(diag.get_vertex_leg_numbers()) > min_vert: 391 # Ignore any diagrams with 4-particle vertices 392 symmetry.append(0) 393 else: 394 symmetry.append(1) 395 396 logger.info("Finding symmetric diagrams for subprocess group %s" % \ 397 subproc_group.get('name')) 398 399 # diagram_tags is a list of unique tags 400 diagram_tags = [] 401 # diagram_classes is a list of lists of diagram numbers belonging 402 # to the different classes 403 diagram_classes = [] 404 perms = [] 405 for idiag, diag in enumerate(diagrams): 406 if diag.get_vertex_leg_numbers()!=[] and \ 407 max(diag.get_vertex_leg_numbers()) > min_vert: 408 # Only include vertices up to min_vert 409 continue 410 tag = IdentifySGConfigTag(diag, model) 411 try: 412 ind = diagram_tags.index(tag) 413 except ValueError: 414 diagram_classes.append([idiag + 1]) 415 perms.append([tag.get_external_numbers()]) 416 diagram_tags.append(tag) 417 else: 418 diagram_classes[ind].append(idiag + 1) 419 perms[ind].append(tag.get_external_numbers()) 420 for inum, diag_number in enumerate(diagram_numbers): 421 if symmetry[inum] == 0: 422 continue 423 idx1 = [i for i, d in enumerate(diagram_classes) if \ 424 diag_number in d][0] 425 idx2 = diagram_classes[idx1].index(diag_number) 426 # Note that for subproc groups, we want symfact to be 1 427 if idx2 > 0: 428 symmetry[inum] = -diagram_classes[idx1][0] 429 # Order permutations according to how to reach the first perm 430 permutations[inum] = diagram_generation.DiagramTag.reorder_permutation(perms[idx1][idx2], 431 perms[idx1][0]) 432 return (symmetry, permutations, [permutations[0]])
433
434 435 -def old_find_symmetry_subproc_group(subproc_group):
436 """Find symmetries between the configs in the subprocess group. 437 For each config, find all matrix elements with maximum identical 438 particle factor. Then take minimal set of these matrix elements, 439 and determine symmetries based on these.""" 440 441 assert isinstance(subproc_group, group_subprocs.SubProcessGroup),\ 442 "Argument to find_symmetry_subproc_group has to be SubProcessGroup" 443 444 matrix_elements = subproc_group.get('matrix_elements') 445 446 contributing_mes, me_config_dict = \ 447 find_matrix_elements_for_configs(subproc_group) 448 449 nexternal, ninitial = matrix_elements[0].get_nexternal_ninitial() 450 451 all_symmetry = {} 452 all_perms = {} 453 454 for me_number in contributing_mes: 455 diagram_config_map = dict([(i,n) for i,n in \ 456 enumerate(subproc_group.get('diagram_maps')[me_number]) \ 457 if n > 0]) 458 symmetry, perms, ident_perms = find_symmetry(matrix_elements[me_number]) 459 460 # Go through symmetries and remove those for any diagrams 461 # where this ME is not supposed to contribute 462 for isym, sym_config in enumerate(symmetry): 463 if sym_config == 0 or isym not in diagram_config_map: 464 continue 465 config = diagram_config_map[isym] 466 if config not in me_config_dict[me_number] or \ 467 sym_config < 0 and diagram_config_map[-sym_config-1] not in \ 468 me_config_dict[me_number]: 469 symmetry[isym] = 1 470 perms[isym]=range(nexternal) 471 if sym_config < 0 and diagram_config_map[-sym_config-1] in \ 472 me_config_dict[me_number]: 473 symmetry[-sym_config-1] -= 1 474 475 # Now update the maps all_symmetry and all_perms 476 for isym, (perm, sym_config) in enumerate(zip(perms, symmetry)): 477 if sym_config in [0,1] or isym not in diagram_config_map: 478 continue 479 config = diagram_config_map[isym] 480 481 all_perms[config] = perm 482 483 if sym_config > 0: 484 all_symmetry[config] = sym_config 485 else: 486 all_symmetry[config] = -diagram_config_map[-sym_config-1] 487 488 # Fill up all_symmetry and all_perms also for configs that have no symmetry 489 for iconf in range(len(subproc_group.get('mapping_diagrams'))): 490 all_symmetry.setdefault(iconf+1, 1) 491 all_perms.setdefault(iconf+1, range(nexternal)) 492 # Since we don't want to multiply by symmetry factor here, set to 1 493 if all_symmetry[iconf+1] > 1: 494 all_symmetry[iconf+1] = 1 495 496 symmetry = [all_symmetry[key] for key in sorted(all_symmetry.keys())] 497 perms = [all_perms[key] for key in sorted(all_perms.keys())] 498 499 return symmetry, perms, [perms[0]]
500
501 502 -def find_matrix_elements_for_configs(subproc_group):
503 """For each config, find all matrix elements with maximum identical 504 particle factor. Then take minimal set of these matrix elements.""" 505 506 matrix_elements = subproc_group.get('matrix_elements') 507 508 n_mes = len(matrix_elements) 509 510 me_config_dict = {} 511 512 # Find the MEs with maximum ident factor corresponding to each config. 513 # Only include MEs with identical particles (otherwise no contribution) 514 for iconf, diagram_list in \ 515 enumerate(subproc_group.get('diagrams_for_configs')): 516 # Check if any diagrams contribute to config 517 if set(diagram_list) == set([0]): 518 continue 519 # Add list of MEs with maximum ident factor contributing to this config 520 max_ident = max([matrix_elements[i].get('identical_particle_factor') \ 521 for i in range(n_mes) if diagram_list[i] > 0]) 522 max_mes = [i for i in range(n_mes) if \ 523 matrix_elements[i].get('identical_particle_factor') == \ 524 max_ident and diagram_list[i] > 0 and max_ident > 1] 525 for me in max_mes: 526 me_config_dict.setdefault(me, [iconf+1]).append(iconf + 1) 527 528 # Make set of the configs 529 for me in me_config_dict: 530 me_config_dict[me] = sorted(set(me_config_dict[me])) 531 532 # Sort MEs according to 1) ident factor, 2) number of configs they 533 # contribute to 534 def me_sort(me1, me2): 535 return (matrix_elements[me2].get('identical_particle_factor') \ 536 - matrix_elements[me1].get('identical_particle_factor'))\ 537 or (len(me_config_dict[me2]) - len(me_config_dict[me1]))
538 539 sorted_mes = sorted([me for me in me_config_dict], me_sort) 540 541 # Reduce to minimal set of matrix elements 542 latest_me = 0 543 checked_configs = [] 544 while latest_me < len(sorted_mes): 545 checked_configs.extend(me_config_dict[sorted_mes[latest_me]]) 546 for me in sorted_mes[latest_me+1:]: 547 me_config_dict[me] = [conf for conf in me_config_dict[me] if \ 548 conf not in checked_configs] 549 if me_config_dict[me] == []: 550 del me_config_dict[me] 551 # Re-sort MEs 552 sorted_mes = sorted([me for me in me_config_dict], me_sort) 553 latest_me += 1 554 555 return sorted_mes, me_config_dict 556