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

Source Code for Module madgraph.core.color_amp

  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, methods and functions required to write QCD color information  
 17  for a diagram and build a color basis, and to square a QCD color string for 
 18  squared diagrams and interference terms.""" 
 19   
 20  import copy 
 21  import fractions 
 22  import operator 
 23  import re 
 24  import array 
 25   
 26  import madgraph.core.color_algebra as color_algebra 
 27  import madgraph.core.diagram_generation as diagram_generation 
 28  import madgraph.core.base_objects as base_objects 
29 30 #=============================================================================== 31 # ColorBasis 32 #=============================================================================== 33 -class ColorBasis(dict):
34 """The ColorBasis object is a dictionary created from an amplitude. Keys 35 are the different color structures present in the amplitude. Values have 36 the format (diag,(index c1, index c2,...), coeff, is_imaginary, Nc_power) 37 where diag is the diagram index, (index c1, index c2,...) the list of 38 indices corresponding to the chose color parts for each vertex in the 39 diagram, coeff the corresponding coefficient (a fraction), is_imaginary 40 if this contribution is real or complex, and Nc_power the Nc power.""" 41 42 # Dictionary to save simplifications already done in a canonical form 43 _canonical_dict = {} 44 45 # Dictionary store the raw colorize information 46 _list_color_dict = [] 47 48
49 - class ColorBasisError(Exception):
50 """Exception raised if an error occurs in the definition 51 or the execution of a color basis object.""" 52 pass
53
54 - def colorize(self, diagram, model):
55 """Takes a diagram and a model and outputs a dictionary with keys being 56 color coefficient index tuples and values a color string (before 57 simplification).""" 58 59 # The smallest value used to create new summed indices 60 min_index = -1000 61 # The dictionary to be output 62 res_dict = {} 63 # The dictionary for book keeping of replaced indices 64 repl_dict = {} 65 66 for i, vertex in enumerate(diagram.get('vertices')): 67 min_index, res_dict = self.add_vertex(vertex, diagram, model, 68 repl_dict, res_dict, min_index) 69 70 # if the process has no QCD particles 71 # Return a list filled with ColorOne if all entries are empty ColorString() 72 empty_colorstring = color_algebra.ColorString() 73 if all(cs == empty_colorstring for cs in res_dict.values()): 74 res_dict = dict((key, color_algebra.ColorString( 75 [color_algebra.ColorOne()])) for key in res_dict) 76 77 return res_dict
78 79 80
81 - def add_vertex(self, vertex, diagram, model, 82 repl_dict, res_dict, min_index, id0_rep=[]):
83 """Update repl_dict, res_dict and min_index for normal vertices. 84 Returns the min_index reached and the result dictionary in a tuple. 85 If the id0_rep list is not None, perform the requested replacement on the 86 last leg number before going further.""" 87 88 # Create a list of (color,leg number) pairs for the vertex, where color 89 # can be negative for anti particles 90 91 color_num_pairs = [] 92 pdg_codes = [] 93 94 for index, leg in enumerate(vertex.get('legs')): 95 curr_num = leg.get('number') 96 curr_part = model.get('particle_dict')[leg.get('id')] 97 curr_color = curr_part.get_color() 98 curr_pdg = curr_part.get_pdg_code() 99 100 # If this is the next-to-last vertex and the last vertex is 101 # the special identity id=0, start by applying the replacement rule 102 # on the last vertex. 103 if index == len(vertex.get('legs')) - 1 and \ 104 curr_num in id0_rep: 105 curr_num = id0_rep[id0_rep.index(curr_num) - 1] 106 107 # If this is the last leg and not the last vertex 108 # flip color. If it is not the last, AND not the next-to-last 109 # before an id=0 vertex, replace last index by a new summed index. 110 if index == len(vertex.get('legs')) - 1 and \ 111 vertex != diagram.get('vertices')[-1]: 112 curr_color = curr_part.get_anti_color() 113 curr_pdg = curr_part.get_anti_pdg_code() 114 if not id0_rep: 115 if not ( diagram.get('vertices')[-1].get('id')==-1 and \ 116 vertex == diagram.get('vertices')[-2]): 117 repl_dict[curr_num] = min_index 118 min_index = min_index - 1 119 else: 120 repl_dict[curr_num] = \ 121 max(l.get('number') for l in \ 122 diagram.get('vertices')[-1].get('legs')) 123 124 # Take into account previous replacements 125 try: 126 curr_num = repl_dict[curr_num] 127 except KeyError: 128 pass 129 130 color_num_pairs.append((curr_color, curr_num)) 131 pdg_codes.append(curr_pdg) 132 133 if vertex != diagram.get('vertices')[-1]: 134 # Put the resulting wavefunction first, to make 135 # wavefunction call more natural 136 last_color_num = color_num_pairs.pop(-1) 137 color_num_pairs.insert(0, last_color_num) 138 last_pdg = pdg_codes.pop(-1) 139 pdg_codes.insert(0, last_pdg) 140 141 # Order the legs according to the interaction particles 142 if vertex.get('id')!=-1: 143 interaction_pdgs = [p.get_pdg_code() for p in \ 144 model.get_interaction(vertex.get('id')).\ 145 get('particles')] 146 else: 147 interaction_pdgs = [l.get('id') for l in vertex.get('legs')] 148 149 sorted_color_num_pairs = [] 150 #print "interactions_pdg=",interaction_pdgs 151 #print "pdg_codes=",pdg_codes 152 for i, pdg in enumerate(interaction_pdgs): 153 index = pdg_codes.index(pdg) 154 pdg_codes.pop(index) 155 sorted_color_num_pairs.append(color_num_pairs.pop(index)) 156 157 if color_num_pairs: 158 raise base_objects.PhysicsObject.PhysicsObjectError 159 160 color_num_pairs = sorted_color_num_pairs 161 162 # Create a list of associated leg number following the same order 163 list_numbers = [p[1] for p in color_num_pairs] 164 165 # ... and the associated dictionary for replacement 166 match_dict = dict(enumerate(list_numbers)) 167 168 if vertex['id'] == -1: 169 return (min_index, res_dict) 170 171 # Update the result dict using the current vertex ColorString object 172 # If more than one, create different entries 173 inter_color = model.get_interaction(vertex['id'])['color'] 174 inter_indices = [i for (i,j) in \ 175 model.get_interaction(vertex['id'])['couplings'].keys()] 176 177 # For colorless vertices, return a copy of res_dict 178 # Where one 0 has been added to each color index chain key 179 if not inter_color: 180 new_dict = {} 181 for k, v in res_dict.items(): 182 new_key = tuple(list(k) + [0]) 183 new_dict[new_key] = v 184 # If there is no result until now, create an empty CS... 185 if not new_dict: 186 new_dict[(0,)] = color_algebra.ColorString() 187 return (min_index, new_dict) 188 189 new_res_dict = {} 190 for i, col_str in \ 191 enumerate(inter_color): 192 193 # Ignore color string if it doesn't correspond to any coupling 194 if i not in inter_indices: 195 continue 196 197 # Build the new element 198 assert type(col_str) == color_algebra.ColorString 199 mod_col_str = col_str.create_copy() 200 201 # Replace summed (negative) internal indices 202 list_neg = [] 203 for col_obj in mod_col_str: 204 list_neg.extend([ind for ind in col_obj if ind < 0]) 205 internal_indices_dict = {} 206 # This notation is to remove duplicates 207 for index in list(set(list_neg)): 208 internal_indices_dict[index] = min_index 209 min_index = min_index - 1 210 mod_col_str.replace_indices(internal_indices_dict) 211 212 # Replace other (positive) indices using the match_dic 213 mod_col_str.replace_indices(match_dict) 214 215 # If we are considering the first vertex, simply create 216 # new entries 217 218 if not res_dict: 219 new_res_dict[tuple([i])] = mod_col_str 220 #... otherwise, loop over existing elements and multiply 221 # the color strings 222 else: 223 for ind_chain, col_str_chain in res_dict.items(): 224 new_col_str_chain = col_str_chain.create_copy() 225 new_col_str_chain.product(mod_col_str) 226 new_res_dict[tuple(list(ind_chain) + [i])] = \ 227 new_col_str_chain 228 229 return (min_index, new_res_dict)
230 231
232 - def update_color_basis(self, colorize_dict, index):
233 """Update the current color basis by adding information from 234 the colorize dictionary (produced by the colorize routine) 235 associated to diagram with index index. Keep track of simplification 236 results for maximal optimization.""" 237 import madgraph.various.misc as misc 238 # loop over possible color chains 239 for col_chain, col_str in colorize_dict.items(): 240 # Create a canonical immutable representation of the the string 241 canonical_rep, rep_dict = col_str.to_canonical() 242 try: 243 # If this representation has already been considered, 244 # recycle the result. 245 col_fact = self._canonical_dict[canonical_rep].create_copy() 246 except KeyError: 247 # If the representation is really new 248 249 # Create and simplify a color factor for the considered chain 250 col_fact = color_algebra.ColorFactor([col_str]) 251 col_fact = col_fact.full_simplify() 252 253 # Here we need to force a specific order for the summed indices 254 # in case we have K6 or K6bar Clebsch Gordan coefficients 255 for colstr in col_fact: colstr.order_summation() 256 257 # Save the result for further use 258 canonical_col_fact = col_fact.create_copy() 259 canonical_col_fact.replace_indices(rep_dict) 260 # Remove overall coefficient 261 for cs in canonical_col_fact: 262 cs.coeff = cs.coeff / col_str.coeff 263 self._canonical_dict[canonical_rep] = canonical_col_fact 264 else: 265 # If this representation has already been considered, 266 # adapt the result 267 # Note that we have to replace back 268 # the indices to match the initial convention. 269 col_fact.replace_indices(self._invert_dict(rep_dict)) 270 # Since the initial coeff of col_str is not taken into account 271 # for matching, we have to multiply col_fact by it. 272 for cs in col_fact: 273 cs.coeff = cs.coeff * col_str.coeff 274 # Must simplify up to two times at NLO (since up to two traces 275 # can appear with a loop) to put traces in a canonical ordering. 276 # If it still causes issue, just do a full_simplify(), it would 277 # not bring any heavy additional computational load. 278 col_fact = col_fact.simplify().simplify() 279 280 # Here we need to force a specific order for the summed indices 281 # in case we have K6 or K6bar Clebsch Gordan coefficients 282 for colstr in col_fact: colstr.order_summation() 283 284 # loop over color strings in the resulting color factor 285 for col_str in col_fact: 286 immutable_col_str = col_str.to_immutable() 287 # if the color structure is already present in the present basis 288 # update it 289 basis_entry = (index, 290 col_chain, 291 col_str.coeff, 292 col_str.is_imaginary, 293 col_str.Nc_power, 294 col_str.loop_Nc_power) 295 try: 296 self[immutable_col_str].append(basis_entry) 297 except KeyError: 298 self[immutable_col_str] = [basis_entry]
299
300 - def create_color_dict_list(self, amplitude):
301 """Returns a list of colorize dict for all diagrams in amplitude. Also 302 update the _list_color_dict object accordingly """ 303 304 list_color_dict = [] 305 306 for diagram in amplitude.get('diagrams'): 307 colorize_dict = self.colorize(diagram, 308 amplitude.get('process').get('model')) 309 list_color_dict.append(colorize_dict) 310 311 self._list_color_dict = list_color_dict 312 313 return list_color_dict
314
315 - def build(self, amplitude=None):
316 """Build the a color basis object using information contained in 317 amplitude (otherwise use info from _list_color_dict). 318 Returns a list of color """ 319 320 if amplitude: 321 self.create_color_dict_list(amplitude) 322 for index, color_dict in enumerate(self._list_color_dict): 323 self.update_color_basis(color_dict, index)
324
325 - def __init__(self, *args):
326 """Initialize a new color basis object, either empty or filled (0 327 or 1 arguments). If one arguments is given, it's interpreted as 328 an amplitude.""" 329 330 assert len(args) < 2, "Object ColorBasis must be initialized with 0 or 1 arguments" 331 332 333 dict.__init__(self) 334 335 # Dictionary to save simplifications already done in a canonical form 336 self._canonical_dict = {} 337 338 # Dictionary store the raw colorize information 339 self._list_color_dict = [] 340 341 342 if args: 343 assert isinstance(args[0], diagram_generation.Amplitude), \ 344 "%s is not a valid Amplitude object" % str(args[0]) 345 346 self.build(*args)
347
348 - def __str__(self):
349 """Returns a nicely formatted string for display""" 350 351 my_str = "" 352 for k, v in self.items(): 353 for name, indices in k: 354 my_str = my_str + name + str(indices) 355 my_str = my_str + ': ' 356 for contrib in v: 357 imag_str = '' 358 if contrib[3]: 359 imag_str = 'I' 360 my_str = my_str + '(diag:%i, chain:%s, coeff:%s%s, Nc:%i) ' % \ 361 (contrib[0], contrib[1], contrib[2], 362 imag_str, contrib[4]) 363 my_str = my_str + '\n' 364 return my_str
365
366 - def _invert_dict(self, mydict):
367 """Helper method to invert dictionary dict""" 368 369 return dict([v, k] for k, v in mydict.items())
370 371 @staticmethod
372 - def get_color_flow_string(my_color_string, octet_indices):
373 """Return the color_flow_string (i.e., composed only of T's with 2 374 indices) associated to my_color_string. Take a list of the external leg 375 color octet state indices as an input. Returns only the leading N 376 contribution!""" 377 # Create a new color factor to allow for simplification 378 my_cf = color_algebra.ColorFactor([my_color_string]) 379 380 # Add one T per external octet 381 for indices in octet_indices: 382 if indices[0] == -6: 383 # Add a K6 which contracts the antisextet index to a 384 # pair of antitriplets 385 my_cf[0].append(color_algebra.K6(indices[1], 386 indices[2], 387 indices[3])) 388 if indices[0] == 6: 389 # Add a K6Bar which contracts the sextet index to a 390 # pair of triplets 391 my_cf[0].append(color_algebra.K6Bar(indices[1], 392 indices[2], 393 indices[3])) 394 if abs(indices[0]) == 8: 395 # Add a T which contracts the octet to a 396 # triplet-antitriplet pair 397 my_cf[0].append(color_algebra.T(indices[1], 398 indices[2], 399 indices[3])) 400 # Simplify the whole thing 401 my_cf = my_cf.full_simplify() 402 403 # If the result is empty, just return 404 if not my_cf: 405 return my_cf 406 407 # Return the string with the highest N coefficient 408 # (leading N decomposition), and the value of this coeff 409 max_coeff = max([cs.Nc_power for cs in my_cf]) 410 411 res_cs = [cs for cs in my_cf if cs.Nc_power == max_coeff] 412 413 # If more than one string at leading N... 414 if len(res_cs) > 1 and any([not cs.near_equivalent(res_cs[0]) \ 415 for cs in res_cs]): 416 raise ColorBasis.ColorBasisError, \ 417 "More than one color string with leading N coeff: %s" % str(res_cs) 418 419 res_cs = res_cs[0] 420 421 # If the result string does not contain only T's with two indices 422 # and Epsilon/EpsilonBar objects 423 for col_obj in res_cs: 424 if not isinstance(col_obj, color_algebra.T) and \ 425 not col_obj.__class__.__name__.startswith('Epsilon'): 426 raise ColorBasis.ColorBasisError, \ 427 "Color flow decomposition %s contains non T/Epsilon elements" % \ 428 str(res_cs) 429 if isinstance(col_obj, color_algebra.T) and len(col_obj) != 2: 430 raise ColorBasis.ColorBasisError, \ 431 "Color flow decomposition %s contains T's w/o 2 indices" % \ 432 str(res_cs) 433 434 return res_cs
435
436 - def color_flow_decomposition(self, repr_dict, ninitial):
437 """Returns the color flow decomposition of the current basis, i.e. a 438 list of dictionaries (one per color basis entry) with keys corresponding 439 to external leg numbers and values tuples containing two color indices 440 ( (0,0) for singlets, (X,0) for triplet, (0,X) for antitriplet and 441 (X,Y) for octets). Other color representations are not yet supported 442 here (an error is raised). Needs a dictionary with keys being external 443 leg numbers, and value the corresponding color representation.""" 444 445 # Offsets used to introduce fake quark indices for gluons 446 offset1 = 1000 447 offset2 = 2000 448 offset3 = 3000 449 450 res = [] 451 452 for col_basis_entry in sorted(self.keys()): 453 454 res_dict = {} 455 fake_repl = [] 456 457 # Rebuild a color string from a CB entry 458 col_str = color_algebra.ColorString() 459 col_str.from_immutable(col_basis_entry) 460 for (leg_num, leg_repr) in repr_dict.items(): 461 # By default, assign a (0,0) color flow 462 res_dict[leg_num] = [0, 0] 463 464 # Raise an error if external legs contain non supported repr 465 if abs(leg_repr) not in [1, 3, 6, 8]: 466 raise ColorBasis.ColorBasisError, \ 467 "Particle ID=%i has an unsupported color representation" % leg_repr 468 469 # Build the fake indices replacements for octets 470 if abs(leg_repr) == 8: 471 fake_repl.append((leg_repr, leg_num, 472 offset1 + leg_num, 473 offset2 + leg_num)) 474 # Build the fake indices for sextets 475 elif leg_repr in [-6, 6]: 476 fake_repl.append((leg_repr, leg_num, 477 offset1 + leg_num, 478 offset3 + leg_num)) 479 480 # Get the actual color flow 481 col_str_flow = self.get_color_flow_string(col_str, fake_repl) 482 483 # Offset for color flow 484 offset = 500 485 486 for col_obj in col_str_flow: 487 if isinstance(col_obj, color_algebra.T): 488 # For T, all color indices should be the same 489 offset = offset + 1 490 for i, index in enumerate(col_obj): 491 if isinstance(col_obj, color_algebra.Epsilon): 492 # Epsilon contracts with antitriplets, 493 i = 0 494 # ...and requires all different color indices 495 offset = offset+1 496 elif isinstance(col_obj, color_algebra.EpsilonBar): 497 # EpsilonBar contracts with antitriplets 498 i = 1 499 # ...and requires all different color indices 500 offset = offset+1 501 if index < offset1: 502 res_dict[index][i] = offset 503 elif index > offset1 and index < offset2: 504 res_dict[index - offset1][i] = offset 505 elif index > offset2 and index < offset3: 506 res_dict[index - offset2][i] = offset 507 elif index > offset3: 508 # For color sextets, use negative triplet 509 # number to reperesent antitriplet and vice 510 # versa, allowing for two triplet or two 511 # antitriplet numbers representing the color 512 # sextet. 513 res_dict[index - offset3][1-i] = -offset 514 515 # Reverse ordering for initial state to stick to the (weird) 516 # les houches convention 517 518 for key in res_dict.keys(): 519 if key <= ninitial: 520 res_dict[key].reverse() 521 522 res.append(res_dict) 523 524 return res
525
526 527 528 529 #=============================================================================== 530 # ColorMatrix 531 #=============================================================================== 532 -class ColorMatrix(dict):
533 """A color matrix, meaning a dictionary with pairs (i,j) as keys where i 534 and j refer to elements of color basis objects. Values are Color Factor 535 objects. Also contains two additional dictionaries, one with the fixed Nc 536 representation of the matrix, and the other one with the "inverted" matrix, 537 i.e. a dictionary where keys are values of the color matrix.""" 538 539 _col_basis1 = None 540 _col_basis2 = None 541 col_matrix_fixed_Nc = {} 542 inverted_col_matrix = {} 543
544 - def __init__(self, col_basis, col_basis2=None, 545 Nc=3, Nc_power_min=None, Nc_power_max=None):
546 """Initialize a color matrix with one or two color basis objects. If 547 only one color basis is given, the other one is assumed to be equal. 548 As options, any value of Nc and minimal/maximal power of Nc can also be 549 provided. Note that the min/max power constraint is applied 550 only at the end, so that it does NOT speed up the calculation.""" 551 552 self.col_matrix_fixed_Nc = {} 553 self.inverted_col_matrix = {} 554 555 self._col_basis1 = col_basis 556 if col_basis2: 557 self._col_basis2 = col_basis2 558 self.build_matrix(Nc, Nc_power_min, Nc_power_max) 559 else: 560 self._col_basis2 = col_basis 561 # If the two color basis are equal, assumes the color matrix is 562 # symmetric 563 self.build_matrix(Nc, Nc_power_min, Nc_power_max, is_symmetric=True)
564
565 - def build_matrix(self, Nc=3, 566 Nc_power_min=None, 567 Nc_power_max=None, 568 is_symmetric=False):
569 """Create the matrix using internal color basis objects. Use the stored 570 color basis objects and takes Nc and Nc_min/max parameters as __init__. 571 If is_isymmetric is True, build only half of the matrix which is assumed 572 to be symmetric.""" 573 574 canonical_dict = {} 575 576 for i1, struct1 in \ 577 enumerate(sorted(self._col_basis1.keys())): 578 for i2, struct2 in \ 579 enumerate(sorted(self._col_basis2.keys())): 580 # Only scan upper right triangle if symmetric 581 if is_symmetric and i2 < i1: 582 continue 583 584 # Fix indices in struct2 knowing summed indices in struct1 585 # to avoid duplicates 586 new_struct2 = self.fix_summed_indices(struct1, struct2) 587 588 # Build a canonical representation of the two immutable struct 589 canonical_entry, dummy = \ 590 color_algebra.ColorString().to_canonical(struct1 + \ 591 new_struct2) 592 593 try: 594 # If this has already been calculated, use the result 595 result, result_fixed_Nc = canonical_dict[canonical_entry] 596 except KeyError: 597 # Otherwise calculate the result 598 result, result_fixed_Nc = \ 599 self.create_new_entry(struct1, 600 new_struct2, 601 Nc_power_min, 602 Nc_power_max, 603 Nc) 604 # Store both results 605 canonical_dict[canonical_entry] = (result, result_fixed_Nc) 606 607 # Store the full result... 608 self[(i1, i2)] = result 609 if is_symmetric: 610 self[(i2, i1)] = result 611 612 # the fixed Nc one ... 613 self.col_matrix_fixed_Nc[(i1, i2)] = result_fixed_Nc 614 if is_symmetric: 615 self.col_matrix_fixed_Nc[(i2, i1)] = result_fixed_Nc 616 # and update the inverted dict 617 if result_fixed_Nc in self.inverted_col_matrix.keys(): 618 self.inverted_col_matrix[result_fixed_Nc].append((i1, 619 i2)) 620 if is_symmetric: 621 self.inverted_col_matrix[result_fixed_Nc].append((i2, 622 i1)) 623 else: 624 self.inverted_col_matrix[result_fixed_Nc] = [(i1, i2)] 625 if is_symmetric: 626 self.inverted_col_matrix[result_fixed_Nc] = [(i2, i1)]
627
628 - def create_new_entry(self, struct1, struct2, 629 Nc_power_min, Nc_power_max, Nc):
630 """ Create a new product result, and result with fixed Nc for two color 631 basis entries. Implement Nc power limits.""" 632 633 # Create color string objects corresponding to color basis 634 # keys 635 col_str = color_algebra.ColorString() 636 col_str.from_immutable(struct1) 637 638 col_str2 = color_algebra.ColorString() 639 col_str2.from_immutable(struct2) 640 641 # Complex conjugate the second one and multiply the two 642 col_str.product(col_str2.complex_conjugate()) 643 644 # Create a color factor to store the result and simplify it 645 # taking into account the limit on Nc 646 col_fact = color_algebra.ColorFactor([col_str]) 647 result = col_fact.full_simplify() 648 649 # Keep only terms with Nc_max >= Nc power >= Nc_min 650 if Nc_power_min is not None: 651 result[:] = [col_str for col_str in result \ 652 if col_str.Nc_power >= Nc_power_min] 653 if Nc_power_max is not None: 654 result[:] = [col_str for col_str in result \ 655 if col_str.Nc_power <= Nc_power_max] 656 657 # Calculate the fixed Nc representation 658 result_fixed_Nc = result.set_Nc(Nc) 659 660 return result, result_fixed_Nc
661
662 - def __str__(self):
663 """Returns a nicely formatted string with the fixed Nc representation 664 of the current matrix (only the real part)""" 665 666 mystr = '\n\t' + '\t'.join([str(i) for i in \ 667 range(len(self._col_basis2))]) 668 669 for i1 in range(len(self._col_basis1)): 670 mystr = mystr + '\n' + str(i1) + '\t' 671 mystr = mystr + '\t'.join(['%i/%i' % \ 672 (self.col_matrix_fixed_Nc[(i1, i2)][0].numerator, 673 self.col_matrix_fixed_Nc[(i1, i2)][0].denominator) \ 674 for i2 in range(len(self._col_basis2))]) 675 676 return mystr
677
678 - def get_line_denominators(self):
679 """Get a list with the denominators for the different lines in 680 the color matrix""" 681 682 den_list = [] 683 for i1 in range(len(self._col_basis1)): 684 den_list.append(self.lcmm(*[\ 685 self.col_matrix_fixed_Nc[(i1, i2)][0].denominator for \ 686 i2 in range(len(self._col_basis2))])) 687 return den_list
688
689 - def get_line_numerators(self, line_index, den):
690 """Returns a list of numerator for line line_index, assuming a common 691 denominator den.""" 692 693 return [self.col_matrix_fixed_Nc[(line_index, i2)][0].numerator * \ 694 den / self.col_matrix_fixed_Nc[(line_index, i2)][0].denominator \ 695 for i2 in range(len(self._col_basis2))]
696 697 @classmethod
698 - def fix_summed_indices(self, struct1, struct2):
699 """Returns a copy of the immutable Color String representation struct2 700 where summed indices are modified to avoid duplicates with those 701 appearing in struct1. Assumes internal summed indices are negative.""" 702 703 # First, determines what is the smallest index appearing in struct1 704 #list2 = reduce(operator.add,[list(elem[1]) for elem in struct1]) 705 list2 = sum((list(elem[1]) for elem in struct1),[]) 706 if not list2: 707 min_index = -1 708 else: 709 min_index = min(list2) - 1 710 711 # Second, determines the summed indices in struct2 and create a 712 # replacement dictionary 713 repl_dict = {} 714 #list2 = reduce(operator.add, 715 # [list(elem[1]) for elem in struct1]) 716 for summed_index in list(set([i for i in list2 \ 717 if list2.count(i) == 2])): 718 repl_dict[summed_index] = min_index 719 min_index -= 1 720 721 # Three, create a new immutable struct by doing replacements in struct2 722 return_list = [] 723 for elem in struct2: 724 fix_elem = [elem[0], []] 725 for index in elem[1]: 726 try: 727 fix_elem[1].append(repl_dict[index]) 728 except Exception: 729 fix_elem[1].append(index) 730 return_list.append((elem[0], tuple(fix_elem[1]))) 731 732 return tuple(return_list)
733 734 @staticmethod
735 - def lcm(a, b):
736 """Return lowest common multiple.""" 737 return a * b // fractions.gcd(a, b)
738 739 @staticmethod
740 - def lcmm(*args):
741 """Return lcm of args.""" 742 if args: 743 return reduce(ColorMatrix.lcm, args) 744 else: 745 return 1
746