src.serena.analysis.ensemble_analysis
This is the file that holds the code that serena calls that calls all the lower level utilities
1""" 2This is the file that holds the code that serena calls 3that calls all the lower level utilities 4""" 5 6from typing import List 7from dataclasses import dataclass 8 9from serena.utilities.ensemble_structures import Sara2SecondaryStructure, Sara2StructureList 10from serena.utilities.ensemble_groups import MultipleEnsembleGroups 11from serena.utilities.weighted_structures import WeightedEnsembleResult, WeightedStructure 12from serena.utilities.ensemble_variation import EVResult 13from serena.utilities.local_minima_variation import ComparisonLMV, ComparisonLMVResponse, LocalMinimaVariation 14from serena.utilities.comparison_structures import ComparisonNucCounts, ComparisonResult, ComparisonNucResults, ComparisonStructures 15 16from serena.analysis.investigator import ComparisonInvestigator, ComparisonEvalResults, LocalMinimaVariationInvestigator, LMVAssertionResult, SettingsAssertionLMV,InvestigatorResults 17from serena.analysis.judge_pool import AnalysisJudgePool, JudgesResults 18from serena.analysis.scoring import SerenaScoring, BasicScoreResults, AdvancedScoreResults 19from serena.local_minima_variation import RunLocalMinimaVariation 20 21@dataclass 22class ReferenceStructures(): 23 """ 24 Reference structures to use throughout the analysis 25 """ 26 mfe_structure:Sara2SecondaryStructure 27 weighted_structures: WeightedEnsembleResult 28 29 30class ProcessEnsemble(): 31 """ 32 Process a MulitEnsembleGroup representation of the ensemble for various features 33 This information is then given to the investigator to collect the data and hand off 34 to the judges and later on scoring 35 """ 36 def __init__(self) -> None: 37 pass 38 39 def process_ensemble_for_weighted_structures(self, ensemble:MultipleEnsembleGroups) -> WeightedEnsembleResult: 40 """ 41 Finds and returns the weighted structures for each ensemble group and outputs it as a list of 42 weighted structures in sara2secondarystructure form. 43 """ 44 ensemble_weighted_structures: List[Sara2SecondaryStructure] = [] 45 for singel_group in ensemble.groups: 46 structs_list: Sara2StructureList = singel_group.group 47 weighted:WeightedStructure = WeightedStructure() 48 ensemble_weighted_structures.append(weighted.make_weighted_struct(structure_list=structs_list)) 49 50 ensemble_result:WeightedEnsembleResult = WeightedEnsembleResult(structs=ensemble_weighted_structures) 51 return ensemble_result 52 53 def process_ensemble_for_lmv(self, ensemble: MultipleEnsembleGroups, ref_structures:ReferenceStructures)->ComparisonLMVResponse: 54 """ 55 Finds and returns the lmv flavors for the ensemble groups and outputs it as a ComparisonLMVResonse 56 """ 57 #first get mfe lmv then weighted for groups 58 lmv:RunLocalMinimaVariation = RunLocalMinimaVariation() 59 mfe_result:EVResult = lmv.get_mfe_mult_group_lmv(ensemble=ensemble) 60 #now get ref ev 61 rel_result:EVResult = lmv.get_relative_mutli_group_lmv(ensemble=ensemble) 62 63 #now get weightedEV 64 weight_result:EVResult = lmv.get_comp_multi_group_lmv(ensemble=ensemble, 65 weighted_structures=ref_structures.weighted_structures) 66 comparisons_lmv_response: List[ComparisonLMV] = [] 67 for group_index in range(len(ensemble.groups)): 68 lmv_data:ComparisonLMV = ComparisonLMV() 69 lmv_data.lmv_comp = weight_result.ev_values[group_index] 70 lmv_data.lmv_mfe = mfe_result.ev_values[group_index] 71 lmv_data.lmv_rel = rel_result.ev_values[group_index] 72 comparisons_lmv_response.append(lmv_data) 73 74 serena_lmv_respone: ComparisonLMVResponse = ComparisonLMVResponse(lmv_comps=comparisons_lmv_response) 75 return serena_lmv_respone 76 77 # need to feed the ensemble to this and then process it 78 #compaire each weighted struct against the unbound mfe and bound structs 79 def process_ensemble_for_comparison_structures(self, raw_ensemble:MultipleEnsembleGroups, weighted_ensemble:WeightedEnsembleResult)->ComparisonNucResults: 80 """ 81 Find and return the comparison structures for the ensemble groups. This compares the 82 weighted structure for each group with the unbound mfe and the folded mfe to get a compariosn 83 structure for each group and returns ComparisonNucResults 84 """ 85 nuc_count:int = raw_ensemble.groups[0].group.nuc_count 86 87 comparison_nucs_list:List[ComparisonNucCounts]= [] 88 for group_index in range(raw_ensemble.num_groups): 89 unbound_mfe_struct:Sara2SecondaryStructure = raw_ensemble.non_switch_state_structure 90 bound_mfe_struct: Sara2SecondaryStructure = raw_ensemble.switched_state_structure 91 weighted_struct:Sara2SecondaryStructure = weighted_ensemble.structs[group_index] 92 comp:ComparisonStructures = ComparisonStructures() 93 comparison_data:ComparisonResult = comp.compair_structures(unbound_struct=unbound_mfe_struct, 94 bound_struct=bound_mfe_struct, 95 reference_struct=weighted_struct, 96 nuc_count=nuc_count) 97 98 comparison_nuc_counts: ComparisonNucCounts = comparison_data.comp_counts 99 comparison_nucs_list.append(comparison_nuc_counts) 100 101 result: ComparisonNucResults = ComparisonNucResults(comparison_nuc_counts=comparison_nucs_list) 102 return result 103 104@dataclass 105class InvestigateEnsembleResults(): 106 """ 107 Container for all the scores that are returned by the 108 scoring algorithms 109 """ 110 basic_scores:BasicScoreResults 111 advanced_scores:AdvancedScoreResults 112 number_structures:int 113 114class InvestigateEnsemble(): 115 """ 116 Entry point for automated analysis of the ensemble for 117 switchyness scores 118 """ 119 def __init__(self) -> None: 120 pass 121 122 def investigate_and_score_ensemble(self, ensemble:MultipleEnsembleGroups, is_aggressive:bool = False)->InvestigateEnsembleResults: 123 """ 124 Does what it says. Process and investigate the MultipleEnsembleGroup 125 for switchyness and report the score after judging. 126 """ 127 process_ensemble: ProcessEnsemble = ProcessEnsemble() 128 129 #first get weighted structs 130 weighted_result: WeightedEnsembleResult = process_ensemble.process_ensemble_for_weighted_structures(ensemble=ensemble) 131 132 #then get lmv 133 lmv_references:ReferenceStructures = ReferenceStructures(mfe_structure=ensemble.non_switch_state_structure, 134 weighted_structures=weighted_result) 135 lmv_results:ComparisonLMVResponse = process_ensemble.process_ensemble_for_lmv(ensemble=ensemble, 136 ref_structures=lmv_references) 137 138 #now get comparison structures 139 comparison_result:ComparisonNucResults = process_ensemble.process_ensemble_for_comparison_structures(raw_ensemble=ensemble, 140 weighted_ensemble=weighted_result) 141 142 #now do the investigation 143 comparison_investigator:ComparisonInvestigator = ComparisonInvestigator() 144 145 comparison_eval_result: ComparisonEvalResults = comparison_investigator.evalulate_comparison_nucs(comparison_nucs=comparison_result) 146 147 #use default values 148 lmv_eval_settings:SettingsAssertionLMV = SettingsAssertionLMV() 149 150 lmv_investigator:LocalMinimaVariationInvestigator = LocalMinimaVariationInvestigator() 151 lmv_eval_results:LMVAssertionResult = lmv_investigator.evaluate_lmv_for_structure_presence(lmv_data=lmv_results, 152 setting=lmv_eval_settings) 153 154 investigation_results: InvestigatorResults = InvestigatorResults(comparison_eval_results=comparison_eval_result, 155 comp_nuc_counts=comparison_result, 156 lmv_values=lmv_results, 157 lmv_assertions=lmv_eval_results, 158 num_groups=ensemble.num_groups, 159 total_structures_ensemble=ensemble.total_structures) 160 161 #now judge the investigation 162 judges:AnalysisJudgePool = AnalysisJudgePool() 163 judges_decisions: JudgesResults = judges.run_all_judges(investigator=investigation_results, 164 is_aggressive=is_aggressive) 165 166 #now apply scoreing to the decisions 167 scoring:SerenaScoring = SerenaScoring() 168 169 basic_scores:BasicScoreResults = scoring.basic_score_groups(judge_results=judges_decisions, 170 investigator=investigation_results) 171 172 advanced_scores:AdvancedScoreResults = scoring.advanced_score_groups(judge_results=judges_decisions, 173 investigator=investigation_results) 174 175 #add a tweak 176 #if advanced_scores.total_score < 0 and ensemble.total_structures > 1000: 177 # #this is a suspect weak switch so half basic score 178 # half = basic_scores.total_score / 2 179 # basic_scores.penalties = basic_scores.penalties + half 180 # basic_scores.total_score = basic_scores.total_score - half 181 182 analysis_results:InvestigateEnsembleResults = InvestigateEnsembleResults(basic_scores=basic_scores, 183 advanced_scores=advanced_scores, 184 number_structures=ensemble.total_structures, 185 ) 186 187 return analysis_results
22@dataclass 23class ReferenceStructures(): 24 """ 25 Reference structures to use throughout the analysis 26 """ 27 mfe_structure:Sara2SecondaryStructure 28 weighted_structures: WeightedEnsembleResult
Reference structures to use throughout the analysis
31class ProcessEnsemble(): 32 """ 33 Process a MulitEnsembleGroup representation of the ensemble for various features 34 This information is then given to the investigator to collect the data and hand off 35 to the judges and later on scoring 36 """ 37 def __init__(self) -> None: 38 pass 39 40 def process_ensemble_for_weighted_structures(self, ensemble:MultipleEnsembleGroups) -> WeightedEnsembleResult: 41 """ 42 Finds and returns the weighted structures for each ensemble group and outputs it as a list of 43 weighted structures in sara2secondarystructure form. 44 """ 45 ensemble_weighted_structures: List[Sara2SecondaryStructure] = [] 46 for singel_group in ensemble.groups: 47 structs_list: Sara2StructureList = singel_group.group 48 weighted:WeightedStructure = WeightedStructure() 49 ensemble_weighted_structures.append(weighted.make_weighted_struct(structure_list=structs_list)) 50 51 ensemble_result:WeightedEnsembleResult = WeightedEnsembleResult(structs=ensemble_weighted_structures) 52 return ensemble_result 53 54 def process_ensemble_for_lmv(self, ensemble: MultipleEnsembleGroups, ref_structures:ReferenceStructures)->ComparisonLMVResponse: 55 """ 56 Finds and returns the lmv flavors for the ensemble groups and outputs it as a ComparisonLMVResonse 57 """ 58 #first get mfe lmv then weighted for groups 59 lmv:RunLocalMinimaVariation = RunLocalMinimaVariation() 60 mfe_result:EVResult = lmv.get_mfe_mult_group_lmv(ensemble=ensemble) 61 #now get ref ev 62 rel_result:EVResult = lmv.get_relative_mutli_group_lmv(ensemble=ensemble) 63 64 #now get weightedEV 65 weight_result:EVResult = lmv.get_comp_multi_group_lmv(ensemble=ensemble, 66 weighted_structures=ref_structures.weighted_structures) 67 comparisons_lmv_response: List[ComparisonLMV] = [] 68 for group_index in range(len(ensemble.groups)): 69 lmv_data:ComparisonLMV = ComparisonLMV() 70 lmv_data.lmv_comp = weight_result.ev_values[group_index] 71 lmv_data.lmv_mfe = mfe_result.ev_values[group_index] 72 lmv_data.lmv_rel = rel_result.ev_values[group_index] 73 comparisons_lmv_response.append(lmv_data) 74 75 serena_lmv_respone: ComparisonLMVResponse = ComparisonLMVResponse(lmv_comps=comparisons_lmv_response) 76 return serena_lmv_respone 77 78 # need to feed the ensemble to this and then process it 79 #compaire each weighted struct against the unbound mfe and bound structs 80 def process_ensemble_for_comparison_structures(self, raw_ensemble:MultipleEnsembleGroups, weighted_ensemble:WeightedEnsembleResult)->ComparisonNucResults: 81 """ 82 Find and return the comparison structures for the ensemble groups. This compares the 83 weighted structure for each group with the unbound mfe and the folded mfe to get a compariosn 84 structure for each group and returns ComparisonNucResults 85 """ 86 nuc_count:int = raw_ensemble.groups[0].group.nuc_count 87 88 comparison_nucs_list:List[ComparisonNucCounts]= [] 89 for group_index in range(raw_ensemble.num_groups): 90 unbound_mfe_struct:Sara2SecondaryStructure = raw_ensemble.non_switch_state_structure 91 bound_mfe_struct: Sara2SecondaryStructure = raw_ensemble.switched_state_structure 92 weighted_struct:Sara2SecondaryStructure = weighted_ensemble.structs[group_index] 93 comp:ComparisonStructures = ComparisonStructures() 94 comparison_data:ComparisonResult = comp.compair_structures(unbound_struct=unbound_mfe_struct, 95 bound_struct=bound_mfe_struct, 96 reference_struct=weighted_struct, 97 nuc_count=nuc_count) 98 99 comparison_nuc_counts: ComparisonNucCounts = comparison_data.comp_counts 100 comparison_nucs_list.append(comparison_nuc_counts) 101 102 result: ComparisonNucResults = ComparisonNucResults(comparison_nuc_counts=comparison_nucs_list) 103 return result
Process a MulitEnsembleGroup representation of the ensemble for various features This information is then given to the investigator to collect the data and hand off to the judges and later on scoring
40 def process_ensemble_for_weighted_structures(self, ensemble:MultipleEnsembleGroups) -> WeightedEnsembleResult: 41 """ 42 Finds and returns the weighted structures for each ensemble group and outputs it as a list of 43 weighted structures in sara2secondarystructure form. 44 """ 45 ensemble_weighted_structures: List[Sara2SecondaryStructure] = [] 46 for singel_group in ensemble.groups: 47 structs_list: Sara2StructureList = singel_group.group 48 weighted:WeightedStructure = WeightedStructure() 49 ensemble_weighted_structures.append(weighted.make_weighted_struct(structure_list=structs_list)) 50 51 ensemble_result:WeightedEnsembleResult = WeightedEnsembleResult(structs=ensemble_weighted_structures) 52 return ensemble_result
Finds and returns the weighted structures for each ensemble group and outputs it as a list of weighted structures in sara2secondarystructure form.
54 def process_ensemble_for_lmv(self, ensemble: MultipleEnsembleGroups, ref_structures:ReferenceStructures)->ComparisonLMVResponse: 55 """ 56 Finds and returns the lmv flavors for the ensemble groups and outputs it as a ComparisonLMVResonse 57 """ 58 #first get mfe lmv then weighted for groups 59 lmv:RunLocalMinimaVariation = RunLocalMinimaVariation() 60 mfe_result:EVResult = lmv.get_mfe_mult_group_lmv(ensemble=ensemble) 61 #now get ref ev 62 rel_result:EVResult = lmv.get_relative_mutli_group_lmv(ensemble=ensemble) 63 64 #now get weightedEV 65 weight_result:EVResult = lmv.get_comp_multi_group_lmv(ensemble=ensemble, 66 weighted_structures=ref_structures.weighted_structures) 67 comparisons_lmv_response: List[ComparisonLMV] = [] 68 for group_index in range(len(ensemble.groups)): 69 lmv_data:ComparisonLMV = ComparisonLMV() 70 lmv_data.lmv_comp = weight_result.ev_values[group_index] 71 lmv_data.lmv_mfe = mfe_result.ev_values[group_index] 72 lmv_data.lmv_rel = rel_result.ev_values[group_index] 73 comparisons_lmv_response.append(lmv_data) 74 75 serena_lmv_respone: ComparisonLMVResponse = ComparisonLMVResponse(lmv_comps=comparisons_lmv_response) 76 return serena_lmv_respone
Finds and returns the lmv flavors for the ensemble groups and outputs it as a ComparisonLMVResonse
80 def process_ensemble_for_comparison_structures(self, raw_ensemble:MultipleEnsembleGroups, weighted_ensemble:WeightedEnsembleResult)->ComparisonNucResults: 81 """ 82 Find and return the comparison structures for the ensemble groups. This compares the 83 weighted structure for each group with the unbound mfe and the folded mfe to get a compariosn 84 structure for each group and returns ComparisonNucResults 85 """ 86 nuc_count:int = raw_ensemble.groups[0].group.nuc_count 87 88 comparison_nucs_list:List[ComparisonNucCounts]= [] 89 for group_index in range(raw_ensemble.num_groups): 90 unbound_mfe_struct:Sara2SecondaryStructure = raw_ensemble.non_switch_state_structure 91 bound_mfe_struct: Sara2SecondaryStructure = raw_ensemble.switched_state_structure 92 weighted_struct:Sara2SecondaryStructure = weighted_ensemble.structs[group_index] 93 comp:ComparisonStructures = ComparisonStructures() 94 comparison_data:ComparisonResult = comp.compair_structures(unbound_struct=unbound_mfe_struct, 95 bound_struct=bound_mfe_struct, 96 reference_struct=weighted_struct, 97 nuc_count=nuc_count) 98 99 comparison_nuc_counts: ComparisonNucCounts = comparison_data.comp_counts 100 comparison_nucs_list.append(comparison_nuc_counts) 101 102 result: ComparisonNucResults = ComparisonNucResults(comparison_nuc_counts=comparison_nucs_list) 103 return result
Find and return the comparison structures for the ensemble groups. This compares the weighted structure for each group with the unbound mfe and the folded mfe to get a compariosn structure for each group and returns ComparisonNucResults
105@dataclass 106class InvestigateEnsembleResults(): 107 """ 108 Container for all the scores that are returned by the 109 scoring algorithms 110 """ 111 basic_scores:BasicScoreResults 112 advanced_scores:AdvancedScoreResults 113 number_structures:int
Container for all the scores that are returned by the scoring algorithms
115class InvestigateEnsemble(): 116 """ 117 Entry point for automated analysis of the ensemble for 118 switchyness scores 119 """ 120 def __init__(self) -> None: 121 pass 122 123 def investigate_and_score_ensemble(self, ensemble:MultipleEnsembleGroups, is_aggressive:bool = False)->InvestigateEnsembleResults: 124 """ 125 Does what it says. Process and investigate the MultipleEnsembleGroup 126 for switchyness and report the score after judging. 127 """ 128 process_ensemble: ProcessEnsemble = ProcessEnsemble() 129 130 #first get weighted structs 131 weighted_result: WeightedEnsembleResult = process_ensemble.process_ensemble_for_weighted_structures(ensemble=ensemble) 132 133 #then get lmv 134 lmv_references:ReferenceStructures = ReferenceStructures(mfe_structure=ensemble.non_switch_state_structure, 135 weighted_structures=weighted_result) 136 lmv_results:ComparisonLMVResponse = process_ensemble.process_ensemble_for_lmv(ensemble=ensemble, 137 ref_structures=lmv_references) 138 139 #now get comparison structures 140 comparison_result:ComparisonNucResults = process_ensemble.process_ensemble_for_comparison_structures(raw_ensemble=ensemble, 141 weighted_ensemble=weighted_result) 142 143 #now do the investigation 144 comparison_investigator:ComparisonInvestigator = ComparisonInvestigator() 145 146 comparison_eval_result: ComparisonEvalResults = comparison_investigator.evalulate_comparison_nucs(comparison_nucs=comparison_result) 147 148 #use default values 149 lmv_eval_settings:SettingsAssertionLMV = SettingsAssertionLMV() 150 151 lmv_investigator:LocalMinimaVariationInvestigator = LocalMinimaVariationInvestigator() 152 lmv_eval_results:LMVAssertionResult = lmv_investigator.evaluate_lmv_for_structure_presence(lmv_data=lmv_results, 153 setting=lmv_eval_settings) 154 155 investigation_results: InvestigatorResults = InvestigatorResults(comparison_eval_results=comparison_eval_result, 156 comp_nuc_counts=comparison_result, 157 lmv_values=lmv_results, 158 lmv_assertions=lmv_eval_results, 159 num_groups=ensemble.num_groups, 160 total_structures_ensemble=ensemble.total_structures) 161 162 #now judge the investigation 163 judges:AnalysisJudgePool = AnalysisJudgePool() 164 judges_decisions: JudgesResults = judges.run_all_judges(investigator=investigation_results, 165 is_aggressive=is_aggressive) 166 167 #now apply scoreing to the decisions 168 scoring:SerenaScoring = SerenaScoring() 169 170 basic_scores:BasicScoreResults = scoring.basic_score_groups(judge_results=judges_decisions, 171 investigator=investigation_results) 172 173 advanced_scores:AdvancedScoreResults = scoring.advanced_score_groups(judge_results=judges_decisions, 174 investigator=investigation_results) 175 176 #add a tweak 177 #if advanced_scores.total_score < 0 and ensemble.total_structures > 1000: 178 # #this is a suspect weak switch so half basic score 179 # half = basic_scores.total_score / 2 180 # basic_scores.penalties = basic_scores.penalties + half 181 # basic_scores.total_score = basic_scores.total_score - half 182 183 analysis_results:InvestigateEnsembleResults = InvestigateEnsembleResults(basic_scores=basic_scores, 184 advanced_scores=advanced_scores, 185 number_structures=ensemble.total_structures, 186 ) 187 188 return analysis_results
Entry point for automated analysis of the ensemble for switchyness scores
123 def investigate_and_score_ensemble(self, ensemble:MultipleEnsembleGroups, is_aggressive:bool = False)->InvestigateEnsembleResults: 124 """ 125 Does what it says. Process and investigate the MultipleEnsembleGroup 126 for switchyness and report the score after judging. 127 """ 128 process_ensemble: ProcessEnsemble = ProcessEnsemble() 129 130 #first get weighted structs 131 weighted_result: WeightedEnsembleResult = process_ensemble.process_ensemble_for_weighted_structures(ensemble=ensemble) 132 133 #then get lmv 134 lmv_references:ReferenceStructures = ReferenceStructures(mfe_structure=ensemble.non_switch_state_structure, 135 weighted_structures=weighted_result) 136 lmv_results:ComparisonLMVResponse = process_ensemble.process_ensemble_for_lmv(ensemble=ensemble, 137 ref_structures=lmv_references) 138 139 #now get comparison structures 140 comparison_result:ComparisonNucResults = process_ensemble.process_ensemble_for_comparison_structures(raw_ensemble=ensemble, 141 weighted_ensemble=weighted_result) 142 143 #now do the investigation 144 comparison_investigator:ComparisonInvestigator = ComparisonInvestigator() 145 146 comparison_eval_result: ComparisonEvalResults = comparison_investigator.evalulate_comparison_nucs(comparison_nucs=comparison_result) 147 148 #use default values 149 lmv_eval_settings:SettingsAssertionLMV = SettingsAssertionLMV() 150 151 lmv_investigator:LocalMinimaVariationInvestigator = LocalMinimaVariationInvestigator() 152 lmv_eval_results:LMVAssertionResult = lmv_investigator.evaluate_lmv_for_structure_presence(lmv_data=lmv_results, 153 setting=lmv_eval_settings) 154 155 investigation_results: InvestigatorResults = InvestigatorResults(comparison_eval_results=comparison_eval_result, 156 comp_nuc_counts=comparison_result, 157 lmv_values=lmv_results, 158 lmv_assertions=lmv_eval_results, 159 num_groups=ensemble.num_groups, 160 total_structures_ensemble=ensemble.total_structures) 161 162 #now judge the investigation 163 judges:AnalysisJudgePool = AnalysisJudgePool() 164 judges_decisions: JudgesResults = judges.run_all_judges(investigator=investigation_results, 165 is_aggressive=is_aggressive) 166 167 #now apply scoreing to the decisions 168 scoring:SerenaScoring = SerenaScoring() 169 170 basic_scores:BasicScoreResults = scoring.basic_score_groups(judge_results=judges_decisions, 171 investigator=investigation_results) 172 173 advanced_scores:AdvancedScoreResults = scoring.advanced_score_groups(judge_results=judges_decisions, 174 investigator=investigation_results) 175 176 #add a tweak 177 #if advanced_scores.total_score < 0 and ensemble.total_structures > 1000: 178 # #this is a suspect weak switch so half basic score 179 # half = basic_scores.total_score / 2 180 # basic_scores.penalties = basic_scores.penalties + half 181 # basic_scores.total_score = basic_scores.total_score - half 182 183 analysis_results:InvestigateEnsembleResults = InvestigateEnsembleResults(basic_scores=basic_scores, 184 advanced_scores=advanced_scores, 185 number_structures=ensemble.total_structures, 186 ) 187 188 return analysis_results
Does what it says. Process and investigate the MultipleEnsembleGroup for switchyness and report the score after judging.