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 return ((part.get('color'), 365 part.get('mass'), part.get('width'), QCD),)
366
367 -def find_symmetry_subproc_group(subproc_group):
368 """Find symmetric configs by directly comparing the configurations 369 using IdentifySGConfigTag.""" 370 371 assert isinstance(subproc_group, group_subprocs.SubProcessGroup),\ 372 "Argument to find_symmetry_subproc_group has to be SubProcessGroup" 373 374 # diagram_numbers is a list of all relevant diagram numbers 375 diagram_numbers = [] 376 # Prepare the symmetry vector with non-used amp2s (due to 377 # multiparticle vertices) 378 symmetry = [] 379 permutations = [] 380 diagrams = subproc_group.get('mapping_diagrams') 381 nexternal, ninitial = \ 382 subproc_group.get('matrix_elements')[0].get_nexternal_ninitial() 383 model = subproc_group.get('matrix_elements')[0].get('processes')[0].\ 384 get('model') 385 vert_list = [max(diag.get_vertex_leg_numbers()) for diag in diagrams if \ 386 diag.get_vertex_leg_numbers()!=[]] 387 min_vert = min(vert_list) if vert_list!=[] else 0 388 389 for idiag,diag in enumerate(diagrams): 390 diagram_numbers.append(idiag+1) 391 permutations.append(list(range(nexternal))) 392 if diag.get_vertex_leg_numbers()!=[] and \ 393 max(diag.get_vertex_leg_numbers()) > min_vert: 394 # Ignore any diagrams with 4-particle vertices 395 symmetry.append(0) 396 else: 397 symmetry.append(1) 398 399 logger.info("Finding symmetric diagrams for subprocess group %s" % \ 400 subproc_group.get('name')) 401 402 # diagram_tags is a list of unique tags 403 diagram_tags = [] 404 # diagram_classes is a list of lists of diagram numbers belonging 405 # to the different classes 406 diagram_classes = [] 407 perms = [] 408 for idiag, diag in enumerate(diagrams): 409 if diag.get_vertex_leg_numbers()!=[] and \ 410 max(diag.get_vertex_leg_numbers()) > min_vert: 411 # Only include vertices up to min_vert 412 continue 413 tag = IdentifySGConfigTag(diag, model) 414 try: 415 ind = diagram_tags.index(tag) 416 except ValueError: 417 diagram_classes.append([idiag + 1]) 418 perms.append([tag.get_external_numbers()]) 419 diagram_tags.append(tag) 420 else: 421 diagram_classes[ind].append(idiag + 1) 422 perms[ind].append(tag.get_external_numbers()) 423 for inum, diag_number in enumerate(diagram_numbers): 424 if symmetry[inum] == 0: 425 continue 426 idx1 = [i for i, d in enumerate(diagram_classes) if \ 427 diag_number in d][0] 428 idx2 = diagram_classes[idx1].index(diag_number) 429 # Note that for subproc groups, we want symfact to be 1 430 if idx2 > 0: 431 symmetry[inum] = -diagram_classes[idx1][0] 432 # Order permutations according to how to reach the first perm 433 permutations[inum] = diagram_generation.DiagramTag.reorder_permutation(perms[idx1][idx2], 434 perms[idx1][0]) 435 return (symmetry, permutations, [permutations[0]])
436
437 438 -def old_find_symmetry_subproc_group(subproc_group):
439 """Find symmetries between the configs in the subprocess group. 440 For each config, find all matrix elements with maximum identical 441 particle factor. Then take minimal set of these matrix elements, 442 and determine symmetries based on these.""" 443 444 assert isinstance(subproc_group, group_subprocs.SubProcessGroup),\ 445 "Argument to find_symmetry_subproc_group has to be SubProcessGroup" 446 447 matrix_elements = subproc_group.get('matrix_elements') 448 449 contributing_mes, me_config_dict = \ 450 find_matrix_elements_for_configs(subproc_group) 451 452 nexternal, ninitial = matrix_elements[0].get_nexternal_ninitial() 453 454 all_symmetry = {} 455 all_perms = {} 456 457 for me_number in contributing_mes: 458 diagram_config_map = dict([(i,n) for i,n in \ 459 enumerate(subproc_group.get('diagram_maps')[me_number]) \ 460 if n > 0]) 461 symmetry, perms, ident_perms = find_symmetry(matrix_elements[me_number]) 462 463 # Go through symmetries and remove those for any diagrams 464 # where this ME is not supposed to contribute 465 for isym, sym_config in enumerate(symmetry): 466 if sym_config == 0 or isym not in diagram_config_map: 467 continue 468 config = diagram_config_map[isym] 469 if config not in me_config_dict[me_number] or \ 470 sym_config < 0 and diagram_config_map[-sym_config-1] not in \ 471 me_config_dict[me_number]: 472 symmetry[isym] = 1 473 perms[isym]=list(range(nexternal)) 474 if sym_config < 0 and diagram_config_map[-sym_config-1] in \ 475 me_config_dict[me_number]: 476 symmetry[-sym_config-1] -= 1 477 478 # Now update the maps all_symmetry and all_perms 479 for isym, (perm, sym_config) in enumerate(zip(perms, symmetry)): 480 if sym_config in [0,1] or isym not in diagram_config_map: 481 continue 482 config = diagram_config_map[isym] 483 484 all_perms[config] = perm 485 486 if sym_config > 0: 487 all_symmetry[config] = sym_config 488 else: 489 all_symmetry[config] = -diagram_config_map[-sym_config-1] 490 491 # Fill up all_symmetry and all_perms also for configs that have no symmetry 492 for iconf in range(len(subproc_group.get('mapping_diagrams'))): 493 all_symmetry.setdefault(iconf+1, 1) 494 all_perms.setdefault(iconf+1, list(range(nexternal))) 495 # Since we don't want to multiply by symmetry factor here, set to 1 496 if all_symmetry[iconf+1] > 1: 497 all_symmetry[iconf+1] = 1 498 499 symmetry = [all_symmetry[key] for key in sorted(all_symmetry.keys())] 500 perms = [all_perms[key] for key in sorted(all_perms.keys())] 501 502 return symmetry, perms, [perms[0]]
503
504 505 -def find_matrix_elements_for_configs(subproc_group):
506 """For each config, find all matrix elements with maximum identical 507 particle factor. Then take minimal set of these matrix elements.""" 508 509 matrix_elements = subproc_group.get('matrix_elements') 510 511 n_mes = len(matrix_elements) 512 513 me_config_dict = {} 514 515 # Find the MEs with maximum ident factor corresponding to each config. 516 # Only include MEs with identical particles (otherwise no contribution) 517 for iconf, diagram_list in \ 518 enumerate(subproc_group.get('diagrams_for_configs')): 519 # Check if any diagrams contribute to config 520 if set(diagram_list) == set([0]): 521 continue 522 # Add list of MEs with maximum ident factor contributing to this config 523 max_ident = max([matrix_elements[i].get('identical_particle_factor') \ 524 for i in range(n_mes) if diagram_list[i] > 0]) 525 max_mes = [i for i in range(n_mes) if \ 526 matrix_elements[i].get('identical_particle_factor') == \ 527 max_ident and diagram_list[i] > 0 and max_ident > 1] 528 for me in max_mes: 529 me_config_dict.setdefault(me, [iconf+1]).append(iconf + 1) 530 531 # Make set of the configs 532 for me in me_config_dict: 533 me_config_dict[me] = sorted(set(me_config_dict[me])) 534 535 # Sort MEs according to 1) ident factor, 2) number of configs they 536 # contribute to 537 def me_sort(me1, me2): 538 return (matrix_elements[me2].get('identical_particle_factor') \ 539 - matrix_elements[me1].get('identical_particle_factor'))\ 540 or (len(me_config_dict[me2]) - len(me_config_dict[me1]))
541 542 sorted_mes = sorted([me for me in me_config_dict], key=misc.cmp_to_key(me_sort)) 543 544 # Reduce to minimal set of matrix elements 545 latest_me = 0 546 checked_configs = [] 547 while latest_me < len(sorted_mes): 548 checked_configs.extend(me_config_dict[sorted_mes[latest_me]]) 549 for me in sorted_mes[latest_me+1:]: 550 me_config_dict[me] = [conf for conf in me_config_dict[me] if \ 551 conf not in checked_configs] 552 if me_config_dict[me] == []: 553 del me_config_dict[me] 554 # Re-sort MEs 555 sorted_mes = sorted([me for me in me_config_dict], me_sort) 556 latest_me += 1 557 558 return sorted_mes, me_config_dict 559