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