Package madgraph :: Package core :: Module color_algebra
[hide private]
[frames] | no frames]

Source Code for Module madgraph.core.color_algebra

   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  """Classes and methods required for all calculations related to SU(N) color  
  17  algebra.""" 
  18   
  19  from __future__ import absolute_import 
  20  import array 
  21  import copy 
  22  import fractions 
  23  import itertools 
  24  import madgraph.various.misc as misc 
  25  from six.moves import map 
  26  from six.moves import range 
  27  from six.moves import zip 
28 29 #=============================================================================== 30 # ColorObject 31 #=============================================================================== 32 -class ColorObject(array.array):
33 """Parent class for all color objects like T, Tr, f, d, ... Any new color 34 object MUST inherit from this class!""" 35
36 - def __new__(cls, *args):
37 """Create a new ColorObject, assuming an integer array""" 38 try: 39 return super(ColorObject, cls).__new__(cls, 'i', args) 40 except TypeError: 41 assert args[0] == 'i' #happens when unpacking pickle with py3 42 return super(ColorObject, cls).__new__(cls, 'i', args[1])
43 - def __reduce__(self):
44 """Special method needed to pickle color objects correctly""" 45 return (self.__class__, tuple([i for i in self]))
46
47 - def __str__(self):
48 """Returns a standard string representation.""" 49 50 return '%s(%s)' % (self.__class__.__name__, 51 ','.join([str(i) for i in self]))
52 53 __repr__ = __str__ 54
55 - def simplify(self):
56 """Simplification rules, to be overwritten for each new color object! 57 Should return a color factor or None if no simplification is possible""" 58 return None
59
60 - def pair_simplify(self, other):
61 """Pair simplification rules, to be overwritten for each new color 62 object! Should return a color factor or None if no simplification 63 is possible""" 64 return None
65
66 - def complex_conjugate(self):
67 """Complex conjugation. By default, the ordering of color index is 68 reversed. Can be overwritten for specific color objects like T,...""" 69 70 self.reverse() 71 return self
72
73 - def replace_indices(self, repl_dict):
74 """Replace current indices following the rules listed in the replacement 75 dictionary written as {old_index:new_index,...}. Deals correctly with 76 the replacement by allowing only one single replacement.""" 77 78 for i, index in enumerate(self): 79 try: 80 self[i] = repl_dict[index] 81 except KeyError: 82 continue
83
84 - def create_copy(self):
85 """Return a real copy of the current object.""" 86 return globals()[self.__class__.__name__](*self)
87 88 __copy__ = create_copy
89
90 91 #=============================================================================== 92 # Tr 93 #=============================================================================== 94 -class Tr(ColorObject):
95 """The trace color object""" 96
97 - def simplify(self):
98 """Implement simple trace simplifications and cyclicity, and 99 Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c))""" 100 101 # Tr(a)=0 102 if len(self) == 1: 103 col_str = ColorString() 104 col_str.coeff = fractions.Fraction(0, 1) 105 return ColorFactor([col_str]) 106 107 # Tr()=Nc 108 if len(self) == 0: 109 col_str = ColorString() 110 col_str.Nc_power = 1 111 return ColorFactor([col_str]) 112 113 # Always order starting from smallest index 114 if self[0] != min(self): 115 pos = self.index(min(self)) 116 new = self[pos:] + self[:pos] 117 return ColorFactor([ColorString([Tr(*new)])]) 118 119 # Tr(a,x,b,x,c) = 1/2(Tr(a,c)Tr(b)-1/Nc Tr(a,b,c)) 120 for i1, index1 in enumerate(self): 121 for i2, index2 in enumerate(self[i1 + 1:]): 122 if index1 == index2: 123 a = self[:i1] 124 b = self[i1 + 1:i1 + i2 + 1] 125 c = self[i1 + i2 + 2:] 126 col_str1 = ColorString([Tr(*(a + c)), Tr(*b)]) 127 col_str2 = ColorString([Tr(*(a + b + c))]) 128 col_str1.coeff = fractions.Fraction(1, 2) 129 col_str2.coeff = fractions.Fraction(-1, 2) 130 col_str2.Nc_power = -1 131 return ColorFactor([col_str1, col_str2]) 132 133 return None
134
135 - def pair_simplify(self, col_obj):
136 """Implement Tr product simplification: 137 Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) and 138 Tr(a,x,b)T(c,x,d,i,j) = 1/2(T(c,b,a,d,i,j)-1/Nc Tr(a,b)T(c,d,i,j))""" 139 140 # Tr(a,x,b)Tr(c,x,d) = 1/2(Tr(a,d,c,b)-1/Nc Tr(a,b)Tr(c,d)) 141 if isinstance(col_obj, Tr): 142 for i1, index1 in enumerate(self): 143 for i2, index2 in enumerate(col_obj): 144 if index1 == index2: 145 a = self[:i1] 146 b = self[i1 + 1:] 147 c = col_obj[:i2] 148 d = col_obj[i2 + 1:] 149 col_str1 = ColorString([Tr(*(a + d + c + b))]) 150 col_str2 = ColorString([Tr(*(a + b)), Tr(*(c + d))]) 151 col_str1.coeff = fractions.Fraction(1, 2) 152 col_str2.coeff = fractions.Fraction(-1, 2) 153 col_str2.Nc_power = -1 154 return ColorFactor([col_str1, col_str2]) 155 156 # Tr(a,x,b)T(c,x,d,i,j) = 1/2(T(c,b,a,d,i,j)-1/Nc Tr(a,b)T(c,d,i,j)) 157 if isinstance(col_obj, T): 158 for i1, index1 in enumerate(self): 159 for i2, index2 in enumerate(col_obj[:-2]): 160 if index1 == index2: 161 a = self[:i1] 162 b = self[i1 + 1:] 163 c = col_obj[:i2] 164 d = col_obj[i2 + 1:-2] 165 ij = col_obj[-2:] 166 col_str1 = ColorString([T(*(c + b + a + d + ij))]) 167 col_str2 = ColorString([Tr(*(a + b)), T(*(c + d) + ij)]) 168 col_str1.coeff = fractions.Fraction(1, 2) 169 col_str2.coeff = fractions.Fraction(-1, 2) 170 col_str2.Nc_power = -1 171 return ColorFactor([col_str1, col_str2]) 172 173 return None
174
175 #=============================================================================== 176 # ColorOne 177 #=============================================================================== 178 -class ColorOne(ColorObject):
179 """The one of the color object""" 180
181 - def __init__(self, *args):
182 """Check for no index""" 183 184 assert len(args) == 0 , "ColorOne objects must have no index!" 185 186 super(ColorOne, self).__init__()
187
188 - def simplify(self):
189 """""" 190 assert len(self)==0, "There is argument(s) in color object ColorOne." 191 col_str = ColorString() 192 col_str.coeff = fractions.Fraction(1, 1) 193 return ColorFactor([col_str])
194 195
196 - def pair_simplify(self, col_obj):
197 """Implement ColorOne product simplification""" 198 199 if any(isinstance(col_obj, c_type) for c_type in [Tr,T,f,d,ColorOne]): 200 col_str = ColorString([col_obj]) 201 return ColorFactor([col_str]) 202 return None
203
204 205 #=============================================================================== 206 # T 207 #=============================================================================== 208 -class T(ColorObject):
209 """The T color object. Last two indices have a special meaning""" 210
211 - def __init__(self, *args):
212 """Check for at least two indices""" 213 214 assert len(args) > 1 , "T objects must have at least two indices!" 215 216 super(T, self).__init__()
217
218 - def simplify(self):
219 """Implement T(a,b,c,...,i,i) = Tr(a,b,c,...) and 220 T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j))""" 221 222 # T(a,b,c,...,i,i) = Tr(a,b,c,...) 223 if self[-2] == self[-1]: 224 return ColorFactor([ColorString([Tr(*self[:-2])])]) 225 226 # T(a,x,b,x,c,i,j) = 1/2(T(a,c,i,j)Tr(b)-1/Nc T(a,b,c,i,j)) 227 for i1, index1 in enumerate(self[:-2]): 228 for i2, index2 in enumerate(self[i1 + 1:-2]): 229 if index1 == index2: 230 a = self[:i1] 231 b = self[i1 + 1:i1 + i2 + 1] 232 c = self[i1 + i2 + 2:-2] 233 ij = self[-2:] 234 col_str1 = ColorString([T(*(a + c + ij)), Tr(*b)]) 235 col_str2 = ColorString([T(*(a + b + c + ij))]) 236 col_str1.coeff = fractions.Fraction(1, 2) 237 col_str2.coeff = fractions.Fraction(-1, 2) 238 col_str2.Nc_power = -1 239 return ColorFactor([col_str1, col_str2]) 240 241 return None
242
243 - def pair_simplify(self, col_obj):
244 """Implement T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k) 245 and T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j) 246 -1/Nc T(a,b,i,j)T(c,d,k,l)).""" 247 248 if isinstance(col_obj, T): 249 ij1 = self[-2:] 250 ij2 = col_obj[-2:] 251 252 # T(a,...,i,j)T(b,...,j,k) = T(a,...,b,...,i,k) 253 if ij1[1] == ij2[0]: 254 return ColorFactor([ColorString([T(*(self[:-2] + \ 255 col_obj[:-2] + \ 256 array.array('i', [ij1[0], 257 ij2[1]])))])]) 258 259 # T(a,x,b,i,j)T(c,x,d,k,l) = 1/2(T(a,d,i,l)T(c,b,k,j) 260 # -1/Nc T(a,b,i,j)T(c,d,k,l)) 261 for i1, index1 in enumerate(self[:-2]): 262 for i2, index2 in enumerate(col_obj[:-2]): 263 if index1 == index2: 264 a = self[:i1] 265 b = self[i1 + 1:-2] 266 c = col_obj[:i2] 267 d = col_obj[i2 + 1:-2] 268 col_str1 = ColorString([T(*(a + d + \ 269 array.array('i', 270 [ij1[0], ij2[1]]))), 271 T(*(c + b + \ 272 array.array('i', 273 [ij2[0], ij1[1]])))]) 274 col_str2 = ColorString([T(*(a + b + \ 275 array.array('i', 276 [ij1[0], ij1[1]]))), 277 T(*(c + d + \ 278 array.array('i', 279 [ij2[0], ij2[1]])))]) 280 col_str1.coeff = fractions.Fraction(1, 2) 281 col_str2.coeff = fractions.Fraction(-1, 2) 282 col_str2.Nc_power = -1 283 return ColorFactor([col_str1, col_str2])
284
285 - def complex_conjugate(self):
286 """Complex conjugation. Overwritten here because the two last indices 287 should be treated differently""" 288 289 # T(a,b,c,i,j)* = T(c,b,a,j,i) 290 l1 = self[:-2] 291 l1.reverse() 292 l2 = self[-2:] 293 l2.reverse() 294 self[:] = l1 + l2 295 return self
296
297 #=============================================================================== 298 # f 299 #=============================================================================== 300 -class f(ColorObject):
301 """The f color object""" 302
303 - def __init__(self, *args):
304 """Ensure f and d objects have strictly 3 indices""" 305 306 # for py3 from pickle 307 if len(args) !=3 and args[0] == 'i': 308 args = args[1] 309 assert len(args) == 3, "f and d objects must have three indices!" 310 311 super(f, self).__init__()
312 313
314 - def simplify(self):
315 """Implement only the replacement rule 316 f(a,b,c)=-2ITr(a,b,c)+2ITr(c,b,a)""" 317 318 indices = self[:] 319 col_str1 = ColorString([Tr(*indices)]) 320 indices.reverse() 321 col_str2 = ColorString([Tr(*indices)]) 322 323 col_str1.coeff = fractions.Fraction(-2, 1) 324 col_str2.coeff = fractions.Fraction(2, 1) 325 326 col_str1.is_imaginary = True 327 col_str2.is_imaginary = True 328 329 return ColorFactor([col_str1, col_str2])
330
331 #=============================================================================== 332 # d 333 #=============================================================================== 334 -class d(f):
335 """The d color object""" 336
337 - def simplify(self):
338 """Implement only the replacement rule 339 d(a,b,c)=2Tr(a,b,c)+2Tr(c,b,a)""" 340 341 indices = self[:] 342 col_str1 = ColorString([Tr(*indices)]) 343 indices.reverse() 344 col_str2 = ColorString([Tr(*indices)]) 345 346 col_str1.coeff = fractions.Fraction(2, 1) 347 col_str2.coeff = fractions.Fraction(2, 1) 348 349 return ColorFactor([col_str1, col_str2])
350
351 #=============================================================================== 352 # Epsilon and EpsilonBar, the totally antisymmetric tensors of three triplet 353 # (antitriplet) indices 354 #=============================================================================== 355 -class Epsilon(ColorObject):
356 """Epsilon_ijk color object for three triplets""" 357
358 - def __init__(self, *args):
359 """Ensure e_ijk objects have strictly 3 indices""" 360 361 super(Epsilon, self).__init__() 362 assert len(args) == 3, "Epsilon objects must have three indices!"
363 364 @staticmethod
365 - def perm_parity(lst, order=None):
366 '''\ 367 Given a permutation of the digits 0..N in order as a list, 368 returns its parity (or sign): +1 for even parity; -1 for odd. 369 ''' 370 lst = lst[:] 371 sort =lst[:] 372 if not order: 373 order = lst[:] 374 order.sort() 375 parity = 1 376 for i in range(0,len(lst)-1): 377 if lst[i] != order[i]: 378 parity *= -1 379 mn = lst.index(order[i]) 380 lst[i],lst[mn] = lst[mn],lst[i] 381 return parity
382
383 - def simplify(self):
384 """Implement epsilon(i,k,j) = -epsilon(i,j,k) i<j<k""" 385 386 387 388 # epsilon(i,k,j) = -epsilon(i,j,k) i<j<k 389 order_list = list(self[:]) 390 order_list.sort() 391 392 if list(self[:]) != order_list: 393 col_str1 = ColorString([Epsilon(*order_list)]) 394 col_str1.coeff = self.perm_parity(self[:], order_list) 395 return ColorFactor([col_str1])
396
397 - def pair_simplify(self, col_obj):
398 """Implement e_ijk ae_ilm = T(j,l)T(k,m) - T(j,m)T(k,l) and 399 e_ijk T(l,k) = e_ikl""" 400 401 # e_ijk ae_ilm = T(j,l)T(k,m) - T(j,m)T(k,l) 402 if isinstance(col_obj, EpsilonBar): 403 404 incommon = False 405 eps_indices = self[:] 406 aeps_indices = col_obj[:] 407 for i in self: 408 if i in col_obj: 409 incommon = True 410 com_index_eps = self.index(i) 411 com_index_aeps = col_obj.index(i) 412 413 if incommon: 414 eps_indices = self[com_index_eps:] + self[:com_index_eps] 415 aeps_indices = col_obj[com_index_aeps:] + col_obj[:com_index_aeps] 416 col_str1 = ColorString([T(eps_indices[1], aeps_indices[1]), 417 T(eps_indices[2], aeps_indices[2])]) 418 col_str2 = ColorString([T(eps_indices[1], aeps_indices[2]), 419 T(eps_indices[2], aeps_indices[1])]) 420 421 col_str2.coeff = fractions.Fraction(-1, 1) 422 423 return ColorFactor([col_str1, col_str2]) 424 425 # e_ijk T(l,k) = e_ikl 426 if isinstance(col_obj, T) and len(col_obj) == 2 and col_obj[1] in self: 427 428 com_index = self.index(col_obj[1]) 429 new_self = copy.copy(self) 430 new_self[com_index] = col_obj[0] 431 432 return ColorFactor([ColorString([new_self])])
433 434
435 - def complex_conjugate(self):
436 """Complex conjugation. Overwritten here because complex conjugation 437 interchange triplets and antitriplets.""" 438 439 return EpsilonBar(*self)
440
441 442 -class EpsilonBar(ColorObject):
443 """Epsilon_ijk color object for three antitriplets""" 444
445 - def __init__(self, *args):
446 """Ensure e_ijk objects have strictly 3 indices""" 447 448 super(EpsilonBar, self).__init__() 449 assert len(args) == 3, "EpsilonBar objects must have three indices!"
450
451 - def pair_simplify(self, col_obj):
452 """Implement ebar_ijk T(k,l) = e_ikl""" 453 454 # ebar_ijk T(k,l) = ebar_ijl 455 if isinstance(col_obj, T) and len(col_obj) == 2 and col_obj[0] in self: 456 457 com_index = self.index(col_obj[0]) 458 new_self = copy.copy(self) 459 new_self[com_index] = col_obj[1] 460 461 return ColorFactor([ColorString([new_self])])
462
463 - def simplify(self):
464 """Implement epsilon(i,k,j) = -epsilon(i,j,k) i<j<k""" 465 466 # epsilon(i,k,j) = -epsilon(i,j,k) i<j<k 467 order_list = list(self[:]) 468 order_list.sort() 469 470 if list(self[:]) != order_list: 471 col_str1 = ColorString([EpsilonBar(*order_list)]) 472 col_str1.coeff = Epsilon.perm_parity(self[:], order_list) 473 return ColorFactor([col_str1])
474
475 - def complex_conjugate(self):
476 """Complex conjugation. Overwritten here because complex conjugation 477 interchange triplets and antitriplets.""" 478 479 return Epsilon(*self)
480
481 482 #=============================================================================== 483 # Color sextet objects: K6, K6Bar, T6 484 # Note that delta3 = T, delta6 = T6, delta8 = 2 Tr 485 # This 2 Tr is weird and should be check why it is not the expected 1/2. 486 #=============================================================================== 487 488 -class K6(ColorObject):
489 """K6, the symmetry clebsch coefficient, mapping into the symmetric 490 tensor.""" 491
492 - def __init__(self, *args):
493 """Ensure sextet color objects have strictly 3 indices""" 494 495 super(K6, self).__init__() 496 assert len(args) == 3, "sextet color objects must have three indices!"
497
498 - def pair_simplify(self, col_obj):
499 """Implement the replacement rules 500 K6(m,i,j)K6Bar(m,k,l) = 1/2(delta3(l,i)delta3(k,j) 501 + delta3(k,i)delta3(l,j)) 502 = 1/2(T(l,i)T(k,j) + T(k,i)T(l,j)) 503 K6(m,i,j)K6Bar(n,j,i) = delta6(m,n) 504 K6(m,i,j)K6Bar(n,i,j) = delta6(m,n) 505 delta3(i,j)K6(m,i,k) = K6(m,j,k) 506 delta3(i,k)K6(m,j,i) = K6(m,j,k).""" 507 508 if isinstance(col_obj, K6Bar): 509 510 m = self[0] 511 n = col_obj[0] 512 513 ij1 = self[-2:] 514 ij2 = col_obj[-2:] 515 516 # K6(m,i,j)K6Bar(m,k,l) = 1/2(T(l,i)T(k,j) 517 # + T(k,i)T(l,j) 518 if m == n: 519 col_str1 = ColorString([T(ij2[1], ij1[0]), 520 T(ij2[0], ij1[1])]) 521 col_str2 = ColorString([T(ij2[0], ij1[0]), 522 T(ij2[1], ij1[1])]) 523 col_str1.coeff = fractions.Fraction(1, 2) 524 col_str2.coeff = fractions.Fraction(1, 2) 525 526 return ColorFactor([col_str1, col_str2]) 527 528 # K6(m,i,j)K6Bar(n,j,i) = delta6(m,n) 529 if ij1[1] == ij2[0] and ij1[0] == ij2[1]: 530 return ColorFactor([ColorString([T6(m, n)])]) 531 532 # K6(m,i,j)K6Bar(n,i,j) = delta6(m,n) 533 if ij1[0] == ij2[0] and ij1[1] == ij2[1]: 534 return ColorFactor([ColorString([T6(m, n)])]) 535 536 if isinstance(col_obj, T) and len(col_obj) == 2: 537 # delta3(i,j)K6(m,i,k) = K6(m,j,k) 538 # delta3(i,k)K6(m,j,i) = K6(m,j,k) 539 if col_obj[0] in self[-2:]: 540 index1 = self[-2:].index(col_obj[0]) 541 return ColorFactor([ColorString([K6(self[0], 542 self[2-index1], 543 col_obj[1])])])
544
545 - def complex_conjugate(self):
546 """Complex conjugation. By default, the ordering of color index is 547 reversed. Can be overwritten for specific color objects like T,...""" 548 549 return K6Bar(*self)
550
551 552 -class K6Bar(ColorObject):
553 """K6Bar, the barred symmetry clebsch coefficient, mapping into the symmetric 554 tensor.""" 555
556 - def __init__(self, *args):
557 """Ensure sextet color objects have strictly 3 indices""" 558 559 super(K6Bar, self).__init__() 560 assert len(args) == 3, "sextet color objects must have three indices!"
561
562 - def pair_simplify(self, col_obj):
563 """Implement the replacement rules 564 delta3(i,j)K6Bar(m,j,k) = K6Bar(m,i,k) 565 delta3(k,j)K6Bar(m,i,j) = K6Bar(m,i,k).""" 566 567 if isinstance(col_obj, T) and len(col_obj) == 2: 568 # delta3(i,j)K6Bar(m,j,k) = K6Bar(m,i,k) 569 # delta3(k,j)K6Bar(m,i,j) = K6Bar(m,i,k) 570 if col_obj[1] in self[-2:]: 571 index1 = self[-2:].index(col_obj[1]) 572 return ColorFactor([ColorString([K6Bar(self[0], 573 self[2-index1], 574 col_obj[0])])])
575
576 - def complex_conjugate(self):
577 """Complex conjugation. By default, the ordering of color index is 578 reversed. Can be overwritten for specific color objects like T,...""" 579 580 return K6(*self)
581
582 -class T6(ColorObject):
583 """The T6 sextet trace color object.""" 584 585 new_index = 10000 586
587 - def __init__(self, *args):
588 """Check for exactly three indices""" 589 590 super(T6, self).__init__() 591 assert len(args) >= 2 and len(args) <= 3, \ 592 "T6 objects must have two or three indices!"
593
594 - def simplify(self):
595 """Implement delta6(i,i) = 1/2 Nc(Nc+1), 596 T6(a,i,j) = 2(K6(i,ii,jj)T(a,jj,kk)K6Bar(j,kk,ii))""" 597 598 # delta6(i,i) = Nc 599 if len(self) == 2 and self[0] == self[1]: 600 col_str1 = ColorString() 601 col_str1.Nc_power = 2 602 col_str1.coeff = fractions.Fraction(1, 2) 603 col_str2 = ColorString() 604 col_str2.Nc_power = 1 605 col_str2.coeff = fractions.Fraction(1, 2) 606 return ColorFactor([col_str1, col_str2]) 607 608 if len(self) == 2: 609 return 610 611 # Set new indices according to the Mathematica template 612 ii = T6.new_index 613 jj = ii + 1 614 kk = jj + 1 615 T6.new_index += 3 616 # Create the resulting color objects 617 col_string = ColorString([K6(self[1], ii, jj), 618 T(self[0], jj, kk), 619 K6Bar(self[2], kk, ii)]) 620 col_string.coeff = fractions.Fraction(2, 1) 621 return ColorFactor([col_string])
622
623 - def pair_simplify(self, col_obj):
624 """Implement the replacement rules 625 delta6(i,j)delta6(j,k) = delta6(i,k) 626 delta6(m,n)K6(n,i,j) = K6(m,i,j) 627 delta6(m,n)K6Bar(m,i,j) = K6Bar(n,i,j).""" 628 629 if len(self) == 3: 630 return 631 632 if isinstance(col_obj, T6) and len(col_obj) == 2: 633 #delta6(i,j)delta6(j,k) = delta6(i,k) 634 if col_obj[0] == self[1]: 635 return ColorFactor([ColorString([T6(self[0], 636 col_obj[1])])]) 637 638 if isinstance(col_obj, K6): 639 # delta6(m,n)K6(n,i,j) = K6(m,i,j) 640 if col_obj[0] == self[1]: 641 return ColorFactor([ColorString([K6(self[0], 642 col_obj[1], 643 col_obj[2])])]) 644 645 646 if isinstance(col_obj, K6Bar): 647 # delta6(m,n)K6Bar(m,i,j) = K6Bar(n,i,j).""" 648 if col_obj[0] == self[0]: 649 return ColorFactor([ColorString([K6Bar(self[1], 650 col_obj[1], 651 col_obj[2])])])
652
653 #=============================================================================== 654 # ColorString 655 #=============================================================================== 656 -class ColorString(list):
657 """A list of ColorObjects with an implicit multiplication between, 658 together with a Fraction coefficient and a tag 659 to indicate if the coefficient is real or imaginary. ColorStrings can be 660 simplified, by simplifying their elements.""" 661 662 coeff = fractions.Fraction(1, 1) 663 is_imaginary = False 664 Nc_power = 0 665 # The loop_NC_power attribute is the power of Nc that comes from the 666 # possible color trace that appear in loop diagrams. It is typically 667 # equal to 1 if the loop diagrams is a closed fermion loop and 0 otherwise. 668 loop_Nc_power = 0 669 canonical = None 670 immutable = None 671
672 - def __init__(self, init_list=[], 673 coeff=fractions.Fraction(1, 1), 674 is_imaginary=False, Nc_power=0, loop_Nc_power=0):
675 """Overrides norm list constructor to implement easy modification 676 of coeff, is_imaginary and Nc_power""" 677 678 if init_list: 679 for obj in init_list: 680 assert type(obj) != array.array 681 self.extend(init_list) 682 self.coeff = coeff 683 self.is_imaginary = is_imaginary 684 self.Nc_power = Nc_power 685 self.loop_Nc_power = loop_Nc_power
686
687 - def __str__(self):
688 """Returns a standard string representation based on color object 689 representations""" 690 691 coeff_str = str(self.coeff) 692 if self.is_imaginary: 693 coeff_str += ' I' 694 if self.Nc_power > 0: 695 coeff_str += ' Nc^%i' % self.Nc_power 696 elif self.Nc_power < 0: 697 coeff_str += ' 1/Nc^%i' % abs(self.Nc_power) 698 return '%s %s' % (coeff_str, 699 ' '.join([str(col_obj) for col_obj in self]))
700 701 __repr__ = __str__ 702
703 - def product(self, other):
704 """Multiply self with other.""" 705 706 self.coeff = self.coeff * other.coeff 707 708 self.Nc_power = self.Nc_power + other.Nc_power 709 710 # Complex algebra 711 if self.is_imaginary and other.is_imaginary: 712 self.is_imaginary = False 713 self.coeff = -self.coeff 714 elif self.is_imaginary or other.is_imaginary: 715 self.is_imaginary = True 716 717 # Reset "canonical", so don't get wrong result from comparison 718 self.canonical = None 719 self.immutable = None 720 721 self.extend(other)
722
723 - def simplify(self):
724 """Simplify the current ColorString by applying simplify rules on 725 each element and building a new ColorFactor to return if necessary""" 726 727 # First, try to simplify element by element 728 for i1, col_obj1 in enumerate(self): 729 res = col_obj1.simplify() 730 # If a simplification possibility is found... 731 if res: 732 # Create a color factor to store the answer... 733 res_col_factor = ColorFactor() 734 # Obtained my multiplying the initial string minus the color 735 # object to simplify with all color strings in the result 736 for second_col_str in res: 737 first_col_str = copy.copy(self) 738 del first_col_str[i1] 739 first_col_str.product(second_col_str) 740 # This sort is necessary to ensure ordering of ColorObjects 741 # remains the same for comparison 742 first_col_str.sort() 743 res_col_factor.append(first_col_str) 744 745 return res_col_factor 746 747 # Second, try to simplify pairs 748 for i1, col_obj1 in enumerate(self): 749 750 for i2, col_obj2 in enumerate(self[i1 + 1:]): 751 res = col_obj1.pair_simplify(col_obj2) 752 # Try both pairing 753 if not res: 754 res = col_obj2.pair_simplify(col_obj1) 755 if res: 756 res_col_factor = ColorFactor() 757 for second_col_str in res: 758 first_col_str = copy.copy(self) 759 del first_col_str[i1] 760 del first_col_str[i1 + i2] 761 first_col_str.product(second_col_str) 762 first_col_str.sort() 763 res_col_factor.append(first_col_str) 764 return res_col_factor 765 766 return None
767
768 - def add(self, other):
769 """Add string other to current string. ONLY USE WITH SIMILAR STRINGS!""" 770 771 self.coeff = self.coeff + other.coeff
772
773 - def complex_conjugate(self):
774 """Returns the complex conjugate of the current color string""" 775 776 compl_conj_str = ColorString([], self.coeff, self.is_imaginary, 777 self.Nc_power) 778 for col_obj in self: 779 compl_conj_str.append(col_obj.complex_conjugate()) 780 if compl_conj_str.is_imaginary: 781 compl_conj_str.coeff = -compl_conj_str.coeff 782 783 return compl_conj_str
784
785 - def to_immutable(self):
786 """Returns an immutable object summarizing the color structure of the 787 current color string. Format is ((name1,indices1),...) where name is the 788 class name of the color object and indices a tuple corresponding to its 789 indices. An immutable object, in Python, is built on tuples, strings and 790 numbers, i.e. objects which cannot be modified. Their crucial property 791 is that they can be used as dictionary keys!""" 792 793 if self.immutable: 794 return self.immutable 795 796 ret_list = [(col_obj.__class__.__name__, tuple(col_obj)) \ 797 for col_obj in self] 798 799 if not ret_list and self.coeff: 800 ret_list=[("ColorOne",tuple([]))] 801 802 ret_list.sort() 803 self.immutable = tuple(ret_list) 804 805 return self.immutable
806
807 - def from_immutable(self, immutable_rep):
808 """Fill the current object with Color Objects created using an immutable 809 representation.""" 810 811 del self[:] 812 813 for col_tuple in immutable_rep: 814 self.append(globals()[col_tuple[0]](*col_tuple[1]))
815
816 - def replace_indices(self, repl_dict):
817 """Replace current indices following the rules listed in the replacement 818 dictionary written as {old_index:new_index,...}, does that for ALL 819 color objects.""" 820 821 list(map(lambda col_obj: col_obj.replace_indices(repl_dict), self))
822
823 - def create_copy(self):
824 """Returns a real copy of self, non trivial because bug in 825 copy.deepcopy""" 826 res = ColorString() 827 for col_obj in self: 828 assert type(col_obj) != array.array 829 res.append(col_obj.create_copy()) 830 res.coeff = self.coeff 831 res.is_imaginary = self.is_imaginary 832 res.Nc_power = self.Nc_power 833 res.loop_Nc_power = self.loop_Nc_power 834 835 return res
836 837 __copy__ = create_copy 838
839 - def set_Nc(self, Nc=3):
840 """Returns a tuple, with the first entry being the string coefficient 841 with Nc replaced (by default by 3), and the second one being True 842 or False if the coefficient is imaginary or not. Raise an error if there 843 are still non trivial color objects.""" 844 845 if self: 846 raise ValueError("String %s cannot be simplified to a number!" % str(self)) 847 848 if self.Nc_power >= 0: 849 return (self.coeff * fractions.Fraction(\ 850 int(Nc ** self.Nc_power), 1), 851 self.is_imaginary) 852 else: 853 return (self.coeff * fractions.Fraction(\ 854 1, int(Nc ** abs(self.Nc_power))), 855 self.is_imaginary)
856
857 - def order_summation(self, immutable=None):
858 """Force a specific order for the summation indices 859 in case we have Clebsch Gordan coefficients K6's or K6Bar's 860 This is necessary to correctly recognize later on the equivalent 861 color strings (otherwise the color basis is degenerate). 862 The new ordering is as follow: 863 1. put K and KBar Clebsch Gordan coefficients at the end of the list of color factors 864 the other factors are re-arranged in the reversed order compared with immutable 865 2. rename the summation indices so that they are increasing (starting from 10000) 866 from left to right 867 3. finally, after the summation indices have been renamed, replace 868 K6(a,i,j) by K6(a,j,i) and K6Bar(a,i,j) by K6Bar(a,j,i) IF j>i 869 """ 870 871 if not immutable: 872 immutable = self.to_immutable() 873 874 # STEP 1: first scan to see whether there are some K's or KBar's, 875 # and put them at the en 876 immutable_order2=[] 877 go_further=0 878 for elem in immutable: 879 if elem[0]=="K6" or elem[0]=="K6Bar" : 880 immutable_order2.append(elem) 881 go_further=1 882 else: immutable_order2.insert(0,elem) 883 884 if go_further==0: return 885 886 # STEP 2: rename the summation indices so that they are increasing (starting from 10000) 887 # from left to right 888 replaced_indices = {} 889 curr_ind = 10000 890 return_list = [] 891 892 for elem in immutable_order2: 893 can_elem = [elem[0], []] 894 for index in elem[1]: 895 if index>9999: # consider only summation indices 896 try: 897 new_index = replaced_indices[index] 898 except KeyError: 899 new_index = curr_ind 900 curr_ind += 1 901 replaced_indices[index] = new_index 902 else: new_index=index 903 can_elem[1].append(new_index) 904 # STEP 3. replace K6(a,i,j) by K6(a,j,i) and K6Bar(a,i,j) by K6Bar(a,j,i) IF j>i 905 if (can_elem[0]=="K6" or can_elem[0]=="K6Bar"): 906 if can_elem[1][2]>can_elem[1][1]: can_elem[1]=[can_elem[1][0], can_elem[1][2], can_elem[1][1] ] 907 return_list.append((can_elem[0], tuple(can_elem[1]))) 908 return_list.sort() 909 910 self.from_immutable(return_list) 911 self.immutable=None # don't use the information self.immutable later on in the code, 912 # since the summation indices have been modified 913 return
914
915 - def to_canonical(self, immutable=None):
916 """Returns the canonical representation of the immutable representation 917 (i.e., first index is 1, ...). This allow for an easy comparison of 918 two color strings, i.e. independently of the actual index names (only 919 relative positions matter). Also returns the conversion dictionary. 920 If no immutable representation is given, use the one build from self.""" 921 922 if not immutable: 923 immutable = self.to_immutable() 924 925 if self.canonical: 926 return self.canonical 927 928 replaced_indices = {} 929 curr_ind = 1 930 return_list = [] 931 932 for elem in immutable: 933 can_elem = [elem[0], []] 934 for index in elem[1]: 935 try: 936 new_index = replaced_indices[index] 937 except KeyError: 938 new_index = curr_ind 939 curr_ind += 1 940 replaced_indices[index] = new_index 941 can_elem[1].append(new_index) 942 return_list.append((can_elem[0], tuple(can_elem[1]))) 943 944 return_list.sort() 945 946 self.canonical = (tuple(return_list), replaced_indices) 947 return self.canonical
948
949 - def __eq__(self, col_str):
950 """Check if two color strings are equivalent by checking if their 951 canonical representations and the coefficients are equal.""" 952 953 return self.coeff == col_str.coeff and \ 954 self.Nc_power == col_str.Nc_power and \ 955 self.is_imaginary == col_str.is_imaginary and \ 956 self.to_canonical() == col_str.to_canonical()
957
958 - def __ne__(self, col_str):
959 """Logical opposite of ea""" 960 961 return not self.__eq__(col_str)
962
963 - def is_similar(self, col_str):
964 """Check if two color strings are similar by checking if their 965 canonical representations and Nc/I powers are equal.""" 966 967 return self.Nc_power == col_str.Nc_power and \ 968 self.is_imaginary == col_str.is_imaginary and \ 969 self.to_canonical() == col_str.to_canonical()
970
971 - def near_equivalent(self, col_str):
972 """Check if two color strings are equivalent looking only at 973 the color objects (used in color flow string calculation)""" 974 975 if len(self.to_canonical()) != len(col_str.to_canonical()): 976 return False 977 978 return all([co1[0] == co2[0] and sorted(co1[1]) == sorted(co2[1]) \ 979 for (co1,co2) in zip(self.to_canonical()[0], 980 col_str.to_canonical()[0])])
981
982 #=============================================================================== 983 # ColorFactor 984 #=============================================================================== 985 -class ColorFactor(list):
986 """ColorFactor objects are list of ColorString with an implicit summation. 987 They can be simplified by simplifying all their elements.""" 988
989 - def __str__(self):
990 """Returns a nice string for printing""" 991 992 return '+'.join(['(%s)' % str(col_str) for col_str in self])
993
994 - def append_str(self, new_str):
995 """Special append taking care of adding new string to strings already 996 existing with the same structure.""" 997 998 for col_str in self: 999 # Check if strings are similar, this IS the optimal way of doing 1000 # it. Note that first line only compare the lists, not the 1001 # properties associated 1002 if col_str.is_similar(new_str): 1003 # Add them 1004 col_str.add(new_str) 1005 return True 1006 1007 # If no correspondence is found, append anyway 1008 self.append(new_str) 1009 return False
1010
1011 - def extend_str(self, new_col_fact):
1012 """Special extend taking care of adding new strings to strings already 1013 existing with the same structure.""" 1014 1015 # Reset "canonical", so don't get wrong result from comparison 1016 self.canonical = None 1017 self.immutable = None 1018 1019 for col_str in new_col_fact: 1020 self.append_str(col_str)
1021
1022 - def simplify(self):
1023 """Returns a new color factor where each color string has been 1024 simplified once and similar strings have been added.""" 1025 1026 new_col_factor = ColorFactor() 1027 # Simplify 1028 for col_str in self: 1029 res = col_str.simplify() 1030 if res: 1031 new_col_factor.extend_str(res) 1032 else: 1033 new_col_factor.append_str(col_str) 1034 1035 # Only returns non zero elements 1036 return ColorFactor([col_str for col_str in \ 1037 new_col_factor if col_str.coeff != 0])
1038
1039 - def full_simplify(self):
1040 """Simplify the current color factor until the result is stable""" 1041 1042 result = copy.copy(self) 1043 while(True): 1044 ref = copy.copy(result) 1045 result = result.simplify() 1046 if result == ref: 1047 return result
1048
1049 - def set_Nc(self, Nc=3):
1050 """Returns a tuple containing real and imaginary parts of the current 1051 color factor, when Nc is replaced (3 by default).""" 1052 1053 return (sum([cs.set_Nc(Nc)[0] for cs in self if not cs.is_imaginary]), 1054 sum([cs.set_Nc(Nc)[0] for cs in self if cs.is_imaginary]))
1055 1056
1057 - def replace_indices(self, repl_dict):
1058 """Replace current indices following the rules listed in the replacement 1059 dictionary written as {old_index:new_index,...}, does that for ALL 1060 color strings.""" 1061 1062 list(map(lambda col_str:col_str.replace_indices(repl_dict), self))
1063
1064 - def create_copy(self):
1065 """Returns a real copy of self, non trivial because bug in 1066 copy.deepcopy""" 1067 1068 res = ColorFactor() 1069 for col_str in self: 1070 res.append(col_str.create_copy()) 1071 1072 return res
1073 1074 __copy__ = create_copy
1075