ATLAS Offline Software
Loading...
Searching...
No Matches
ChainMerging Namespace Reference

Functions

 mergeChainDefs (listOfChainDefs, chainDict, perSig_lengthOfChainConfigs=None)
 check_leg_lengths (perSig_lengthOfChainConfigs)
 mergeParallel (chainDefList, offset, leg_numbering=None, perSig_lengthOfChainConfigs=None)
 getMergedEmptyStepName (alignmentGroup, stepNumber, multiplicity, signature)
 getEmptySeqName (signature, step_number, alignGroup, order)
 isFullScanRoI (inputL1Nav)
 getCurrentAG (chainStep)
 serial_zip (allSteps, chainName, chainDefList, legOrdering)
 mergeSerial (chainDefList, chainDefListOrdering)
 checkStepContent (parallel_steps)
 makeCombinedStep (parallel_steps, stepNumber, chainDefList, allSteps=None, currentChainSteps=None, leg_numbering=None, alignment_group="")
 zip_longest_parallel (AllSteps, multiplicity, fillvalue=None)
 build_empty_sequences (emptyChainDicts, step_mult, caller, L1decisions, seqNames, chainName)

Variables

 log = logging.getLogger( __name__ )

Function Documentation

◆ build_empty_sequences()

ChainMerging.build_empty_sequences ( emptyChainDicts,
step_mult,
caller,
L1decisions,
seqNames,
chainName )

Definition at line 635 of file ChainMerging.py.

635def build_empty_sequences(emptyChainDicts, step_mult, caller, L1decisions, seqNames, chainName):
636 emptySequences = []
637 for ileg in range(len(L1decisions)):
638 is_fs_string = 'FS' if isFullScanRoI(L1decisions[ileg]) else ''
639 sname = seqNames[ileg]+is_fs_string
640 log.debug("[%s] adding %s empty sequenc with name %s", caller, is_fs_string, sname)
641
642 thisEmpty = createEmptyMenuSequenceCfg(flags=None, name=sname)
643 emptySequences += [functools.partial(thisEmpty, flags=None, name=sname)]
644
645
646 log.verbose("[%s] emptyChainDicts %s", caller, emptyChainDicts)
647 log.debug("[%s] %s has %d empty sequences and %d empty legs in stepDicts",
648 caller, chainName, len(emptySequences), len(emptyChainDicts))
649 if len(emptySequences) != len(emptyChainDicts):
650 log.error("[%s] %s has a different number of empty sequences/legs %d than stepDicts %d",
651 caller, chainName, len(emptySequences), len(emptyChainDicts))
652
653 raise Exception(f"[{caller}] Cannot create this chain step, exiting.")
654
655 for sd in emptyChainDicts:
656 if sd['signature'] == 'Jet' or sd['signature'] == 'Bjet':
657 step_mult += [1]
658 elif len(sd['chainParts']) != 1:
659 log.error("[%s] %s has chainParts has length != 1 within a leg! chain dictionary for this step: \n %s",
660 caller, chainName, sd)
661 raise Exception(f"[{caller}] Cannot create this chain step, exiting.")
662 else:
663 step_mult += [int(sd['chainParts'][0]['multiplicity'])]
664
665 if len(emptySequences) != len(step_mult):
666 log.error("[%s] %s has a different number of empty sequences/legs %d than multiplicities %d",
667 caller, chainName, len(emptySequences), len(step_mult))
668 raise Exception(f"[{caller}] Cannot create this chain step, exiting.")
669
670 log.verbose('[%s] step multiplicity %s',caller, step_mult)
671
672
673 return emptySequences

◆ check_leg_lengths()

ChainMerging.check_leg_lengths ( perSig_lengthOfChainConfigs)

Definition at line 91 of file ChainMerging.py.

91def check_leg_lengths(perSig_lengthOfChainConfigs):
92 if not perSig_lengthOfChainConfigs: #default is None
93 return "", -1
94 leg_length_dict = {}
95 for leg_lengths, leg_grps in perSig_lengthOfChainConfigs:
96 for grp, length in zip(leg_grps,leg_lengths):
97 if grp in leg_length_dict:
98 leg_length_dict[grp] += [length]
99 else:
100 leg_length_dict[grp] = [length]
101 found_mismatch = False
102 max_length = -1
103 mismatched_ag = ""
104 log.debug("[check_leg_lengths] leg lengths: %s",leg_length_dict)
105 for grp,lengths in leg_length_dict.items():
106 if len(set(lengths)) > 1: #a mismatch!
107 log.debug("[check_leg_lengths] found mismatch for %s given %s", grp, lengths)
108 if found_mismatch:
109 log.error("[check_leg_lengths] Second mismatch in the same chain! I don't know how to deal with this, please resolve. Chain leg lengths: %s",perSig_lengthOfChainConfigs)
110 log.error("[check_leg_lengths] Second mismatch in the same chain! lengths,grp: %s,%s",lengths, grp)
111 raise Exception("[are_lengths_mismatched] Cannot merge chain, exiting.")
112 found_mismatch = True
113 max_length = max(lengths)
114 mismatched_ag = grp
115
116 return mismatched_ag, max_length
117
118
#define max(a, b)
Definition cfImp.cxx:41
STL class.

◆ checkStepContent()

ChainMerging.checkStepContent ( parallel_steps)
return True if any step contains a real Sequence

Definition at line 428 of file ChainMerging.py.

428def checkStepContent(parallel_steps):
429 """
430 return True if any step contains a real Sequence
431 """
432 for step in parallel_steps:
433 if step is None or step.isEmpty:
434 continue
435 for seq in step.sequenceGens:
436 if not isEmptySequenceCfg(seq):
437 return True
438 return False
439

◆ getCurrentAG()

ChainMerging.getCurrentAG ( chainStep)

Definition at line 264 of file ChainMerging.py.

264def getCurrentAG(chainStep):
265 filled_seq_ag = []
266 for iseq,seq in enumerate(chainStep.sequenceGens):
267 # In the case of dummy configs, they are all empty
268 if isEmptySequenceCfg(seq):
269 continue
270 else:
271 # get the alignment group of the leg that is running a non-empty sequence
272 # if we double-serial merge enough this will have to be recursive. Throw an error here for now
273 # if the length is greater than one. I don't think this will ever come up
274 if len(set(cp['alignmentGroup'] for cp in chainStep.stepDicts[iseq]['chainParts'])) > 1:
275 log.error("[getCurrentAG] The leg has more than one chainPart (%s). Either the alignmentGroup property is bad or this is an unimplemented situation.",chainStep.stepDicts[iseq]['chainParts'])
276 raise Exception("[getCurrentAG] Not sure what is happening here, but I don't know what to do.")
277 filled_seq_ag += [chainStep.stepDicts[iseq]['chainParts'][0]['alignmentGroup']]
278
279 if len(filled_seq_ag) == 0:
280 log.error("[getCurrentAG] No non-empty sequences were found in %s", chainStep.sequenceGens)
281 log.error("[getCurrentAG] The chainstep is %s", chainStep)
282 raise Exception("[getCurrentAG] Cannot find the current alignment group for this chain")
283 elif len(set(filled_seq_ag)) > 1:
284 log.error("[getCurrentAG] Found more than one alignment group for this step %s", filled_seq_ag)
285 raise Exception("[getCurrentAG] Cannot find the current alignment group for this chain")
286 else:
287 return filled_seq_ag[0]
288

◆ getEmptySeqName()

ChainMerging.getEmptySeqName ( signature,
step_number,
alignGroup,
order )

Definition at line 251 of file ChainMerging.py.

251def getEmptySeqName(signature, step_number, alignGroup,order):
252 seqName = 'Empty'+ alignGroup +'Seq'+str(step_number)+ '_'+ str(order) + signature
253 return seqName
254
255

◆ getMergedEmptyStepName()

ChainMerging.getMergedEmptyStepName ( alignmentGroup,
stepNumber,
multiplicity,
signature )

Definition at line 246 of file ChainMerging.py.

246def getMergedEmptyStepName(alignmentGroup, stepNumber, multiplicity, signature):
247 currentStepName = 'Empty' + alignmentGroup +'Align'+str(stepNumber)+'_'+ str(multiplicity) + signature
248 return currentStepName
249
250

◆ isFullScanRoI()

ChainMerging.isFullScanRoI ( inputL1Nav)

Definition at line 256 of file ChainMerging.py.

256def isFullScanRoI(inputL1Nav):
257 fsRoIList = ['HLTNav_L1FSNOSEED','HLTNav_L1MET','HLTNav_L1J']
258 if inputL1Nav in fsRoIList:
259 return True
260 else:
261 return False
262
263

◆ makeCombinedStep()

ChainMerging.makeCombinedStep ( parallel_steps,
stepNumber,
chainDefList,
allSteps = None,
currentChainSteps = None,
leg_numbering = None,
alignment_group = "" )

Definition at line 440 of file ChainMerging.py.

440def makeCombinedStep(parallel_steps, stepNumber, chainDefList, allSteps = None, currentChainSteps = None, leg_numbering = None, alignment_group = ""):
441 # default mutable values must be initialized to None
442 if allSteps is None: allSteps = []
443 if currentChainSteps is None: currentChainSteps = []
444 if leg_numbering is None: leg_numbering =[]
445
446 stepName = 'merged' #we will renumber all steps after chains are aligned #Step' + str(stepNumber)
447
448 log.debug("[makeCombinedStep] stepNumber %d, alignment_group %s, %d steps: [%s], %d chain list: [%s], alignment groups: [%s]", stepNumber, alignment_group, len(parallel_steps), ', '.join([step.name if step is not None else "EMPTY" for step in parallel_steps ]), len(chainDefList), ', '.join([chain.name for chain in chainDefList]), ', '.join([chain.alignmentGroups[0] for chain in chainDefList]))
449
450
451 stepDicts = []
452 comboHypoTools = []
453 comboHypo = None
454
455 leg_counter = 0
456 currentStepName = ''
457 # if *all* the steps we're trying to merge are either empty sequences or empty steps
458 # we need to create a single empty step instead.
459 hasNonEmptyStep = checkStepContent(parallel_steps)
460 log.debug("hasNonEmptyStep %d", hasNonEmptyStep)
461
462 if not hasNonEmptyStep:
463 # only empty steps here
464 if len(parallel_steps)>=len(chainDefList) and all(step is None for step in parallel_steps[len(chainDefList):]):
465 # We need to remove manually here the None steps exceeding the len of chainDefList. The right solution
466 # would be to make sure that these cases don't happen upstream, but I am not confident enough with this
467 # code to make such a large (and dangerous) change. But it would be nice to do that in future if possible..
468 parallel_steps=parallel_steps[:len(chainDefList)]
469 log.debug("[makeCombinedStep] removing empty steps exceeding chainDefList size. The new steps are now %s ", parallel_steps)
470
471 for chain_index, step in enumerate(parallel_steps):
472 # every step is empty but some might have empty sequences and some might not
473 if step is None or step.isEmpty:
474 new_stepDicts = deepcopy(chainDefList[chain_index].steps[-1].stepDicts)
475 nLegs = chainDefList[chain_index].steps[-1].nLegs
476 currentStepName = getMergedEmptyStepName(chainDefList[chain_index].alignmentGroups[0], stepNumber, nLegs, new_stepDicts[0]['signature'])
477 log.debug('[makeCombinedStep] step has no sequences, making empty step %s', currentStepName)
478
479 # we need a chain dict here, use the one corresponding to this leg of the chain
480 for new_stepDict in new_stepDicts:
481 oldLegName = new_stepDict['chainName']
482 if re.search('^leg[0-9]{3}_',oldLegName):
483 oldLegName = oldLegName[7:]
484 new_stepDict['chainName'] = legName(oldLegName,leg_counter)
485 log.debug("[makeCombinedStep] stepDict naming old: %s, new: %s", oldLegName, new_stepDict['chainName'])
486 stepDicts.append(new_stepDict)
487 leg_counter += 1
488
489 else:
490 # Standard step with empty sequence(s)
491 currentStepName = step.name
492 #remove redundant instances of StepN_ and merged_ (happens when merging already merged chains)
493
494 if currentStepName.startswith('merged_'):
495 currentStepName = currentStepName[7:]
496
497 # update the chain dict list for the combined step with the chain dict from this step
498 log.debug('[makeCombinedStep] adding step dictionaries %s',step.stepDicts)
499
500 for new_stepDict in deepcopy(step.stepDicts):
501 oldLegName = new_stepDict['chainName']
502 if re.search('^leg[0-9]{3}_',oldLegName):
503 oldLegName = oldLegName[7:]
504 if len(leg_numbering) > 0:
505 leg_counter = leg_numbering[chain_index]
506 new_stepDict['chainName'] = legName(oldLegName,leg_counter)
507 log.debug("[makeCombinedStep] stepDict naming old: %s, new: %s", oldLegName, new_stepDict['chainName'])
508 stepDicts.append(new_stepDict)
509 leg_counter += 1
510
511 stepName += '_' + currentStepName
512
513 theChainStep = ChainStep(stepName, chainDicts = stepDicts, isEmpty = True)
514 log.debug("[makeCombinedStep] Merged empty step: %s", theChainStep.name)
515 return theChainStep
516
517 stepSeq = []
518 alignLegsInStep = []
519 # count the number of legs inside this chain part/step (inner legs)
520 # this happens if the step is already the result of a merging, due to the alignemnt, and can have more than one leg
521 # use the alignmentGroups here, which is stored by grouping the legs per alignemnt group
522 # TODO: can be extracted from stepDict['chainParts'][0]['multiplicity']?
523 for num, chain in enumerate(chainDefList):
524 alignLegsInStep.append(len(chain.alignmentGroups))
525 assert(len(alignLegsInStep) == len(parallel_steps))
526
527 for chain_index, step in enumerate(parallel_steps): #this is a horizontal merge!
528
529 if step is None or (hasNonEmptyStep and step.isEmpty):
530 # this happens for merging chains with different numbers of steps, we need to "pad" out with empty sequences to propogate the decisions
531 # all other chain parts' steps should contain an empty sequence
532
533 log.debug("[makeCombinedStep] step %s is Empty and has %d alignemnt group legs", step.name if step is not None else "None", alignLegsInStep[chain_index])
534 if alignment_group == "":
535 alignment_group = chainDefList[0].alignmentGroups[0]
536
537 # loop over the inner legs of this sub-chain and create one empty sequence per each inner leg
538 for innerLeg in range(alignLegsInStep[chain_index]):
539 new_stepDict = deepcopy(chainDefList[chain_index].steps[-1].stepDicts[-1])
540 seqName = getEmptySeqName( new_stepDict['signature'], stepNumber, alignment_group, innerLeg)
541 log.debug("[makeCombinedStep] creating Empty sequence %s", seqName)
542 signature=new_stepDict['signature']
543 is_fs_string = 'FS' if isFullScanRoI(chainDefList[chain_index].L1decisions[0]) else ''
544 seqName=seqName+is_fs_string
545 signature=new_stepDict['signature']+is_fs_string
546 thisEmpty = createEmptyMenuSequenceCfg(flags=None, name=seqName)
547 stepSeq.append(functools.partial(thisEmpty, flags=None, name=seqName))
548 oldLegName = new_stepDict['chainName']
549 if re.search('^leg[0-9]{3}_',oldLegName):
550 oldLegName = oldLegName[7:]
551 new_stepDict['chainName'] = legName(oldLegName,leg_counter)
552 stepDicts.append(new_stepDict)
553 leg_counter += 1
554
555 nLegs = alignLegsInStep[chain_index]
556 currentStepName = getMergedEmptyStepName(alignment_group, stepNumber, nLegs, signature)
557
558 log.debug("[makeCombinedStep] found empty step to be merged, step number: %d chain_index: %s, step name: %s, made new empty sequence name: %s", stepNumber, chain_index, currentStepName, seqName)
559
560
561 else:
562 # Standard step, append it to the combined step
563 log.debug("[makeCombinedStep] step %s with nLegs = %s", step.name, str(step.nLegs))
564 if len(step.sequenceGens):
565 log.debug("[makeCombinedStep] with sequences = [%s]", ', '.join(map(str, [seq.func.__name__ for seq in step.sequenceGens])))
566
567 # this function only works if the input chains are single-object chains (one menu seuqnce)
568 if len(step.sequenceGens) > 1:
569 log.debug("[makeCombinedStep] combining in an already combined chain")
570
571 comboHypo = step.comboHypoCfg
572 currentStepName = step.name
573 #remove redundant instances of StepN_ and merged_ (happens when merging already merged chains)
574 if currentStepName.startswith('merged_'):
575 currentStepName = currentStepName[7:]
576 stepSeq.extend(step.sequenceGens)
577 comboHypoTools.extend(step.comboToolConfs)
578 # update the chain dict list for the combined step with the chain dict from this step
579 log.debug('[makeCombinedStep] adding step dictionaries %s',step.stepDicts)
580 log.debug('[makeCombinedStep] my leg_numbering is: %s, for chain_index %s',leg_numbering, chain_index)
581 for new_stepDict in deepcopy(step.stepDicts):
582 oldLegName = new_stepDict['chainName']
583 if re.search('^leg[0-9]{3}_',oldLegName):
584 oldLegName = oldLegName[7:]
585 if len(leg_numbering) > 0:
586 leg_counter = leg_numbering[chain_index]
587 new_stepDict['chainName'] = legName(oldLegName,leg_counter)
588 log.debug("[makeCombinedStep] stepDict naming old: %s, new: %s", oldLegName, new_stepDict['chainName'])
589 stepDicts.append(new_stepDict)
590 leg_counter += 1
591
592
593 # the step naming for combined chains needs to be revisted!!
594 stepName += '_' + currentStepName
595 log.debug('[makeCombinedStep] current step name %s, with %d sequences',stepName, len(stepSeq))
596 # for merged steps, we need to update the name to add the leg name
597
598 comboHypoTools = list(set(comboHypoTools))
599 theChainStep = ChainStep(stepName, SequenceGens = stepSeq, chainDicts = stepDicts,
600 comboHypoCfg = comboHypo, comboToolConfs = comboHypoTools)
601 log.debug("[makeCombinedStep] Merged step index %d: \n %s", stepNumber, theChainStep)
602
603
604 return theChainStep
605
606# modified version of zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-, which takes into account the multiplicity of the steps
STL class.

◆ mergeChainDefs()

ChainMerging.mergeChainDefs ( listOfChainDefs,
chainDict,
perSig_lengthOfChainConfigs = None )
 function to merge chain definitions for all legs, used also by signature code 
chainDefList is a list of Chain() objects
one for each part (leg) in the chain 

Definition at line 15 of file ChainMerging.py.

15def mergeChainDefs(listOfChainDefs, chainDict, perSig_lengthOfChainConfigs = None):
16 """ function to merge chain definitions for all legs, used also by signature code
17 chainDefList is a list of Chain() objects
18 one for each part (leg) in the chain """
19
20 strategy = chainDict["mergingStrategy"]
21 offset = chainDict["mergingOffset"]
22 log.debug("[mergeChainDefs] %s: Combine by using %s merging with %d chain defs (legs)", chainDict['chainName'], strategy, len(listOfChainDefs))
23
24 leg_numbering = []
25
26 if 'Bjet' in chainDict['signatures'] and 'Jet' in chainDict['signatures']:#and chainDict['Signature'] == 'Bjet':
27 leg_numbering = [it for it,s in enumerate(chainDict['signatures'])]# if s != 'Jet']
28
29 if strategy=="parallel":
30 return mergeParallel(listOfChainDefs, offset)
31 elif strategy=="serial":
32 return mergeSerial(listOfChainDefs)
33
34 elif strategy=="auto":
35 ordering = getAlignmentGroupOrdering()
36 merging_dict = {} # maps the chain alignment group with the alignment order dict
37 for ich,cConfig in enumerate(listOfChainDefs):
38 chain_ag = cConfig.alignmentGroups[0]
39 if chain_ag not in ordering:
40 log.error("[mergeChainDefs] Alignment group %s can't be auto-merged because it's not in the grouping list!",chain_ag)
41 if chain_ag in merging_dict:
42 merging_dict[chain_ag] += [ich]
43 else:
44 merging_dict[chain_ag] = [ich]
45
46 tmp_merged = []
47 tmp_merged_ordering = []
48 for ag in merging_dict:
49 if len(merging_dict[ag]) > 1:
50 log.debug("[mergeChainDefs] parallel merging")
51 new_chain_defs, perSig_lengthOfChainConfigs = mergeParallel(list( listOfChainDefs[i] for i in merging_dict[ag] ), offset, leg_numbering, perSig_lengthOfChainConfigs)
52 tmp_merged += [new_chain_defs]
53 tmp_merged_ordering += [ordering.index(ag)]
54 else:
55 log.debug("[mergeChainDefs] don't need to parallel merge")
56 tmp_merged += [listOfChainDefs[merging_dict[ag][0]]]
57 tmp_merged_ordering += [ordering.index(ag)]
58
59 #reset the ordering to index from zero (padding comes later!)
60 merged_ordering = [-1]*len(tmp_merged_ordering)
61 copy_ordering = tmp_merged_ordering.copy()
62 tmp_val = 0
63 while len(copy_ordering) > 0:
64 min_index = tmp_merged_ordering.index(min(copy_ordering))
65 copy_ordering.pop(copy_ordering.index(min(copy_ordering)))
66 merged_ordering[min_index] = tmp_val
67 tmp_val += 1
68
69 # only serial merge if necessary
70 if len(tmp_merged) == 1:
71 if perSig_lengthOfChainConfigs is None:
72 log.debug("[mergeChainDefs] tmp merged has length 1, returning 0th element")
73 return tmp_merged[0]
74 else:
75 log.debug("[mergeChainDefs] tmp merged has length 1, returning 0th element and perSig list")
76 return tmp_merged[0], perSig_lengthOfChainConfigs
77
78 if perSig_lengthOfChainConfigs is None:
79 log.debug("[mergeChainDefs] serial merging first")
80 return mergeSerial(tmp_merged, merged_ordering) #shouldn't need to modify it here!
81 else:
82 log.debug("[mergeChainDefs] returning mergeSerial result and perSig_lengthOfChainConfigs %s",perSig_lengthOfChainConfigs)
83 return mergeSerial(tmp_merged, merged_ordering), perSig_lengthOfChainConfigs #shouldn't need to modify it here!
84
85 else:
86 log.error("[mergeChainDefs] Merging failed for %s. Merging strategy '%s' not known.", (listOfChainDefs, strategy))
87 return -1
88
89
90
#define min(a, b)
Definition cfImp.cxx:40

◆ mergeParallel()

ChainMerging.mergeParallel ( chainDefList,
offset,
leg_numbering = None,
perSig_lengthOfChainConfigs = None )
Performs merging of steps with the same step number 

Definition at line 119 of file ChainMerging.py.

119def mergeParallel(chainDefList, offset, leg_numbering = None, perSig_lengthOfChainConfigs = None):
120 ''' Performs merging of steps with the same step number '''
121 # default mutable values must be initialized to None
122 if leg_numbering is None: leg_numbering = []
123
124 if offset != -1:
125 log.error("[mergeParallel] Offset for parallel merging not implemented.")
126 raise Exception("[mergeParallel] Cannot merge this chain, exiting.")
127
128 log.debug(f"[mergeParallel] Parallel merging for chain alignments {[cConfig.alignmentGroups for cConfig in chainDefList]}")
129 allSteps = []
130 allStepsMult = []
131 nSteps = []
132 chainName = ''
133 l1Decisions = []
134 alignmentGroups = []
135 vertical_alignment_groups = []
136
137 for iConfig, cConfig in enumerate(chainDefList):
138 if chainName == '':
139 chainName = cConfig.name
140 elif chainName != cConfig.name:
141 log.error("[mergeParallel] Something is wrong with the combined chain name: cConfig.name = %s while chainName = %s", cConfig.name, chainName)
142 raise Exception("[mergeParallel] Cannot merge this chain, exiting.")
143
144 if len(set(cConfig.alignmentGroups)) == 1:
145 alignmentGroups.append(cConfig.alignmentGroups[0])
146 elif len(cConfig.alignmentGroups) > 1:
147 log.debug("[mergeParallel] Parallel merging an already merged chain with different alignment groups? This is odd! %s",cConfig.alignmentGroups)
148 log.debug("...let's look at the config: %s", perSig_lengthOfChainConfigs)
149 # if the length the matching group in the pre-merged part is shorter than the full one,
150 # we need to patch it up to the full length by adding empty steps so that when
151 # we merge, the longer leg doesn't merge onto the second alignment group
152 align_grp_to_lengthen, max_length = check_leg_lengths(perSig_lengthOfChainConfigs)
153 if max_length > -1:
154 current_leg_ag_length = -1
155 index_modified_leg = -1
156 leg_lengths, leg_ags = perSig_lengthOfChainConfigs[iConfig]
157 for ileg, (length, ag) in enumerate(zip(leg_lengths, leg_ags)):
158 if ag == align_grp_to_lengthen:
159 current_leg_ag_length = length
160 index_modified_leg = ileg
161 log.debug("[mergeParallel] ileg %s, length %s, ag %s: ",ileg, length, ag)
162 break
163 # it's already merged so even if there is more than one in this chain
164 # they had better be the same length already
165
166 n_new_steps = max_length - current_leg_ag_length
167
168 previous_step_dicts = cConfig.steps[current_leg_ag_length-1].stepDicts
169 for i in range(1,n_new_steps+1):
170 step_mult = []
171 sigNames = []
172
173 for ileg,stepDict in enumerate(previous_step_dicts):
174 is_fs_string = 'FS' if isFullScanRoI(cConfig.L1decisions[ileg]) else ''
175 sigNames += [stepDict['chainParts'][0]['signature'] + is_fs_string]
176
177 seqMultName = '_'.join([sigName for sigName in sigNames])
178 nLegs = 1 # TODO, make it follow the real multiplicity of the step
179 # nLegs = cConfig.steps[current_leg_ag_length-1].nLegs
180 seqStepName = getMergedEmptyStepName(align_grp_to_lengthen, current_leg_ag_length+i, nLegs, seqMultName)
181 seqNames = [getEmptySeqName(previous_step_dicts[iSeq]['signature'], current_leg_ag_length+i, align_grp_to_lengthen,i) for iSeq in range(len(sigNames))]
182
183 emptySequences = build_empty_sequences(previous_step_dicts, step_mult, 'mergeParallel', cConfig.L1decisions, seqNames, chainName)
184 # insert a step with an empty sequence
185 cConfig.steps.insert(current_leg_ag_length + i - 1, #-1 to go to indexed from zero
186 ChainStep( seqStepName, SequenceGens = emptySequences,
187 chainDicts = previous_step_dicts)
188 )
189
190
191 # edited the lengths, so need to update the leg length dict the code we did so!
192 perSig_lengthOfChainConfigs[iConfig][0][index_modified_leg] = max_length
193 else:
194 log.debug("[mergeParallel] Alignment groups are empty for this combined chain")
195
196 allSteps.append(cConfig.steps)
197 #TODO: instead of the real step multiplicy (len(cConfig.steps[0].nLegs))), we set allStepsMult=[1] because the zip doesn't need it: when a step is missing in one leg, one None step is added, not multiple steps. I think we can remove the allStepsMult in the zip_longest below
198 allStepsMult.append(1)
199 nSteps.append(len(cConfig.steps))
200 l1Decisions.extend(cConfig.L1decisions)
201
202 # Use zip_longest_parallel so that we get None in case one chain has more steps than the other
203 orderedSteps = list(zip_longest_parallel(allSteps, allStepsMult))
204
205 if perSig_lengthOfChainConfigs is not None and len(perSig_lengthOfChainConfigs) > 0:
206 in_chain_ag_lengths = {}
207 ag_ordering = getAlignmentGroupOrdering()
208
209 for ag in ag_ordering:
210 for ag_lengths, sig_ags in perSig_lengthOfChainConfigs:
211 for ag_length, sig_ag in zip(ag_lengths, sig_ags):
212 if (sig_ag in in_chain_ag_lengths and in_chain_ag_lengths[sig_ag] < ag_length) or sig_ag not in in_chain_ag_lengths:
213 in_chain_ag_lengths[sig_ag] = ag_length
214 for ag, ag_length in in_chain_ag_lengths.items():
215 vertical_alignment_groups += [ag]*ag_length
216 else:
217 #it's all one alignment group in this case
218 vertical_alignment_groups = [alignmentGroups[0]]*len(orderedSteps)
219
220
221 log.debug("[mergeParallel] alignment groups horizontal: %s", alignmentGroups)
222 log.debug("[mergeParallel] alignment groups vertical: %s", vertical_alignment_groups)
223
224 combChainSteps =[]
225 log.debug("[mergeParallel] len(orderedSteps): %d", len(orderedSteps))
226 for chain_index in range(len(chainDefList)):
227 log.debug('[mergeParallel] Chain object to merge (i.e. chainDef) %s', chainDefList[chain_index])
228
229 for step_index, (steps, step_ag) in enumerate(zip(orderedSteps,vertical_alignment_groups)):
230 mySteps = list(steps)
231 log.debug(f"[mergeParallel] Merging step {step_index+1} with alignment group = {step_ag}")
232
233 combStep = makeCombinedStep(mySteps, step_index+1, chainDefList, orderedSteps, combChainSteps, leg_numbering, step_ag)
234 combChainSteps.append(combStep)
235
236
237 combinedChainDef = Chain(chainName, ChainSteps=combChainSteps, L1decisions=l1Decisions,
238 nSteps = nSteps, alignmentGroups = alignmentGroups)
239
240 log.debug("[mergeParallel] Parallel merged chain %s with these steps:", chainName)
241 for step in combinedChainDef.steps:
242 log.debug('\n %s', step)
243
244 return combinedChainDef, perSig_lengthOfChainConfigs
245

◆ mergeSerial()

ChainMerging.mergeSerial ( chainDefList,
chainDefListOrdering )

Definition at line 380 of file ChainMerging.py.

380def mergeSerial(chainDefList, chainDefListOrdering):
381 allSteps = []
382 legOrdering = []
383 nSteps = []
384 chainName = ''
385 l1Decisions = []
386 alignmentGroups = []
387 log.debug('[mergeSerial] Merge chainDefList:')
388 log.debug(chainDefList)
389 log.debug('[mergeSerial] wth ordering %s:',chainDefListOrdering)
390
391 for ic,cOrder in enumerate(chainDefListOrdering):
392
393 #put these in order of alignment
394 cConfig = chainDefList[chainDefListOrdering.index(ic)]
395 leg_order = chainDefListOrdering.index(ic) #but keep track of where it came from
396
397 if chainName == '':
398 chainName = cConfig.name
399 elif chainName != cConfig.name:
400 log.error("[mergeSerial] Something is wrong with the combined chain name: cConfig.name = %s while chainName = %s", cConfig.name, chainName)
401 raise Exception("[mergeSerial] Cannot merge this chain, exiting.")
402
403 #allSteps is ordered such that the first entry in the list is what we want to *run* first
404 allSteps.append(cConfig.steps)
405 legOrdering.append(leg_order)
406 nSteps.extend(cConfig.nSteps)
407 l1Decisions.extend(chainDefList[ic].L1decisions) # do not invert L1Decisions because they follow the order of the sequences inside teh steps
408 alignmentGroups.extend(cConfig.alignmentGroups)
409
410 serialSteps = serial_zip(allSteps, chainName, chainDefList, legOrdering)
411 combChainSteps =[]
412 for chain_index in range(len(chainDefList)):
413 log.debug('[mergeSerial] Chain object to merge (i.e. chainDef) %s', chainDefList[chain_index])
414 for step_index, steps in enumerate(serialSteps):
415 mySteps = list(steps)
416 combStep = makeCombinedStep(mySteps, step_index+1, chainDefList)
417 combChainSteps.append(combStep)
418
419 combinedChainDef = Chain(chainName, ChainSteps=combChainSteps, L1decisions=l1Decisions,
420 nSteps = nSteps, alignmentGroups = alignmentGroups)
421
422 log.debug("[mergeSerial] Serial merged chain %s with number of steps/leg %s with these steps:", chainName, combinedChainDef.nSteps)
423 for step in combinedChainDef.steps:
424 log.debug(' %s', step)
425
426 return combinedChainDef
427

◆ serial_zip()

ChainMerging.serial_zip ( allSteps,
chainName,
chainDefList,
legOrdering )

Definition at line 289 of file ChainMerging.py.

289def serial_zip(allSteps, chainName, chainDefList, legOrdering):
290
291 #note: allSteps and chainDefList do not have the legs in the same order
292 #the legOrdering is a mapping between the two:
293 # chainDefList[legOrdering[0]] <=> allSteps[0]
294
295 legs_per_part = [chainDefList[stepPlacement].steps[0].nLegs for stepPlacement in legOrdering]
296 n_parts = len(allSteps)
297 log.debug('[serial_zip] configuring chain with %d parts with multiplicities %s', n_parts, legs_per_part)
298 log.debug('[serial_zip] and leg ordering %s', legOrdering)
299 newsteps = []
300
301 #per-part (horizontal) iteration by alignment ordering
302 #i.e. if we run muon then electron, allSteps[0] = muon steps, allSteps[1] = electron steps
303 #leg ordering tells us where it was ordered in the chain name, so e_mu in this case would
304 #have legOrdering = [1,0]
305 for chain_index, (chainSteps, stepPlacement) in enumerate(zip(allSteps, legOrdering)):
306
307 for step_index, step in enumerate(chainSteps): #serial step iteration
308 if step_index == 0:
309 prev_ag_step_index = step_index
310 previousAG = getCurrentAG(step)
311 log.debug('[serial_zip] chain_index: %s step_index: %s, alignment group: %s', chain_index, step_index+1, previousAG)
312 # create list of correct length (chainSteps in parallel)
313 stepList = [None]*n_parts
314
315 # put the step from the current sub-chain into the right place
316 stepList[stepPlacement] = step
317 log.debug('[serial_zip] Put step: %s', step.name)
318
319 # all other chain parts' steps should contain an empty sequence
320 for chain_index2, (nLegs, stepPlacement2) in enumerate(zip(legs_per_part, legOrdering)): #more per-leg iteration
321 emptyStep = stepList[stepPlacement2]
322 if emptyStep is None:
323 if chain_index2 == chain_index:
324 log.error("chain_index2 = chain_index, but the stepList still has none!")
325 raise Exception("[serial_zip] duplicating existing leg, why has this happened??")
326
327 #this WILL NOT work for jets!
328 step_mult = []
329 emptyChainDicts = []
330 if chain_index2 < chain_index:
331 emptyChainDicts = allSteps[chain_index2][-1].stepDicts
332 else:
333 emptyChainDicts = allSteps[chain_index2][0].stepDicts
334
335 log.debug("[serial_zip] nLegs: %s, len(emptyChainDicts): %s, len(L1decisions): %s", nLegs, len(emptyChainDicts), len(chainDefList[stepPlacement2].L1decisions))
336 sigNames = []
337 for ileg,(emptyChainDict,_) in enumerate(zip(emptyChainDicts,chainDefList[stepPlacement2].L1decisions)):
338 is_fs_string = 'FS' if isFullScanRoI(chainDefList[stepPlacement2].L1decisions[ileg]) else ''
339 sigNames +=[emptyChainDict['chainParts'][0]['signature']+is_fs_string]
340
341
342 seqMultName = '_'.join([sigName for sigName in sigNames])
343 currentAG = ''
344
345 #now we need to know what alignment group this step is in to properly name the empty sequence
346 if len(set(chainDefList[stepPlacement].alignmentGroups)) == 1:
347 currentAG = chainDefList[stepPlacement].alignmentGroups[0]
348 ag_step_index = step_index+1
349 else:
350 # this happens if one of the bits to serial merge is already serial merged.
351 currentAG = getCurrentAG(step)
352 if currentAG == previousAG:
353 ag_step_index = prev_ag_step_index + 1
354 prev_ag_step_index = ag_step_index
355 else:
356 ag_step_index = 1
357 previousAG = currentAG
358 prev_ag_step_index = 1
359
360 seqStepName = getMergedEmptyStepName(currentAG, ag_step_index, nLegs, seqMultName)
361
362 seqNames = [getEmptySeqName(emptyChainDicts[iSeq]['signature'], ag_step_index, currentAG,iSeq) for iSeq in range(nLegs)]
363
364 log.verbose("[serial_zip] step name for this leg: %s", seqStepName)
365 log.verbose("[serial_zip] created empty sequence(s): %s", seqNames)
366 log.verbose("[serial_zip] L1decisions %s ", chainDefList[stepPlacement2].L1decisions)
367
368 emptySequences = build_empty_sequences(emptyChainDicts, step_mult, 'serial_zip', chainDefList[stepPlacement2].L1decisions, seqNames, chainName)
369
370 stepList[stepPlacement2] = ChainStep( seqStepName, SequenceGens = emptySequences,
371 chainDicts = emptyChainDicts)
372
373 newsteps.append(stepList)
374 log.debug('After serial_zip')
375 for s in newsteps:
376 log.debug( ', '.join(map(str, [step.name for step in s]) ) )
377 return newsteps
378
379

◆ zip_longest_parallel()

ChainMerging.zip_longest_parallel ( AllSteps,
multiplicity,
fillvalue = None )

Definition at line 607 of file ChainMerging.py.

607def zip_longest_parallel(AllSteps, multiplicity, fillvalue=None):
608
609 iterators = [iter(it) for it in AllSteps]
610 inactives =set()
611 if len(iterators)==0:
612 return
613 while True:
614 values = []
615 for i, it in enumerate(iterators): #Here we loop over the different chain parts
616 try:
617 value = next(it)
618 except StopIteration:
619 if i not in inactives:
620 #We want to add the inactive iterator to the list of inactives iterators
621 inactives.add(i)
622 if len(inactives)>=len(iterators):
623 #We want to exit the while True if we reached the end of all iterators.
624 return
625 iterators[i] = repeat(fillvalue, int(multiplicity[i]))
626 value = fillvalue
627 values.append(value)
628 if int(multiplicity[i]) > 1 and value == fillvalue:
629 values.extend([fillvalue]*int(multiplicity[i]-1))
630
631 yield tuple(values)
632
633
634

Variable Documentation

◆ log

ChainMerging.log = logging.getLogger( __name__ )

Definition at line 13 of file ChainMerging.py.