def build_tree_plan(self, pattern: Pattern, statistics: Dict): """ Creates a tree-based evaluation plan for the given pattern. """ statistics_copy = deepcopy(statistics) pattern_positive_args = pattern.get_top_level_structure_args( positive_only=True) pattern_positive_statistics = TreePlanBuilder.extract_positive_statistics( pattern, statistics_copy) if any(not isinstance(arg, PrimitiveEventStructure) for arg in pattern_positive_args): # the pattern contains nested parts and should be treated accordingly nested_topology, _ = self.__create_nested_topology( pattern, pattern_positive_statistics) positive_root = TreePlanBuilder.__adjust_nested_indices( pattern, nested_topology) else: # the pattern is entirely flat positive_root = self._create_tree_topology( pattern, pattern_positive_statistics, self.__init_tree_leaves(pattern)) if isinstance(pattern.positive_structure, UnaryStructure): # an edge case where the topmost operator is a unary operator positive_root = self._instantiate_unary_node( pattern, positive_root) root = self.__negation_algorithm.handle_pattern_negation( pattern, statistics_copy, positive_root) pattern_condition = deepcopy( pattern.condition ) # copied since apply_condition modifies its input parameter root.apply_condition(pattern_condition) return TreePlan(root)
def __create_selectivity_matrix_for_nested_operators( pattern: Pattern, statistics: Dict): """ This function creates a selectivity matrix that fits the root operator (kind of flattening the selectivity of the nested operators, if exists). """ selectivity_matrix = statistics[StatisticsTypes.SELECTIVITY_MATRIX] if pattern.count_primitive_events( positive_only=True) != len(selectivity_matrix): raise Exception("size mismatch") nested_selectivity_matrix = [] primitive_sons_list = [] # event_names are all the events in this pattern (including those under nested operators) event_names = [ name for name in pattern.positive_structure.get_all_event_names() ] for arg in pattern.get_top_level_structure_args(positive_only=True): # This is a list with size of the number of args, where each entry in the list is the events' name of the # primitive events under this arg. primitive_sons_list.append( [name for name in arg.get_all_event_names()]) for i, row_entry in enumerate(primitive_sons_list): nested_selectivity_matrix.append([]) for col_entry in primitive_sons_list: # Building the new matrix, which is (#args x #args), where each entry is calculated based on the events # under the specific args respectively nested_selectivity = TreePlanBuilder.__calculate_nested_selectivity( event_names, selectivity_matrix, row_entry, col_entry) nested_selectivity_matrix[i].append(nested_selectivity) return nested_selectivity_matrix
def __init_tree_leaves(pattern: Pattern, nested_topologies: List[TreePlanNode] = None, nested_args: List[PatternStructure] = None, nested_cost: List[float] = None): """ Initializes the leaves of the tree plan. If the nested parameters are given, creates nested nodes instead of regular leaves where necessary. """ leaves = [] pattern_positive_args = pattern.get_top_level_structure_args( positive_only=True) for i, arg in enumerate(pattern_positive_args): if nested_topologies is None or nested_topologies[i] is None: # the current argument can either be a PrimitiveEventStructure or an UnaryOperator surrounding it event_structure = arg if isinstance( arg, PrimitiveEventStructure) else arg.child new_leaf = TreePlanLeafNode(i, event_structure.type, event_structure.name) else: nested_topology = nested_topologies[i].sub_tree_plan \ if isinstance(nested_topologies[i], TreePlanNestedNode) else nested_topologies[i] new_leaf = TreePlanNestedNode(i, nested_topology, nested_args[i], nested_cost[i]) if isinstance(arg, UnaryStructure): new_leaf = TreePlanBuilder._instantiate_unary_node( TreePlanBuilder.__create_dummy_subpattern(pattern, arg), new_leaf) leaves.append(new_leaf) return leaves
def __adjust_nested_indices(pattern: Pattern, root, offset=0): """ After building a tree plan, this function will correct the indexes of sub trees to continue the indices of the main tree, instead of restarting in each sub tree(which was important while building the plan) """ for index, arg in enumerate( pattern.get_top_level_structure_args(positive_only=True)): node = TreePlanBuilder.__get_node_by_index(root, index) if isinstance(node, TreePlanLeafNode): node.event_index += offset elif isinstance(node, TreePlanNestedNode): nested_pattern = TreePlanBuilder.__create_dummy_subpattern( pattern, arg) TreePlanBuilder.__adjust_nested_indices( nested_pattern, node.sub_tree_plan, node.nested_event_index + offset) offset += nested_pattern.count_primitive_events( positive_only=True) - 1 return root
def __extract_nested_pattern(self, pattern: Pattern, statistics: Dict): """ This function is done recursively, to support nested pattern's operators (i.e. And(Seq,Seq)). When encounters KleeneClosure or CompositeStructure, it computes this operator's tree plan (recursively...) and uses the returned tree plan's root as a new "simple" (primitive) event (with its statistics updated according to its subtree nodes), such that all nested Kleene/Composite operators can be treated as "simple" ones. For every "flat" pattern, it invokes an algorithm (to be implemented by subclasses) that builds an evaluation order of the operands, and converts it into a left-deep tree topology. """ if isinstance(pattern.positive_structure, PrimitiveEventStructure): return pattern, statistics, None, None, None # a nested structure nested_topologies = [] nested_args = [] nested_cost = [] nested_arrival_rates = [] modified_statistics = {} if StatisticsTypes.SELECTIVITY_MATRIX in statistics: # If we have a selectivity matrix, than we need to create new one, that has the size of the "flat" root # operator and all its entries are computed according to the nested operators in each composite/kleene # structure. modified_statistics[StatisticsTypes.SELECTIVITY_MATRIX] = \ TreePlanBuilder.__create_selectivity_matrix_for_nested_operators(pattern, statistics) for arg in pattern.get_top_level_structure_args(positive_only=True): # This loop creates (recursively) all the nested subtrees if not isinstance(arg, PrimitiveEventStructure): # If we are here than this structure is composite or unary. # And first create new pattern that fits the nested operator's stats and structure. pattern, nested_topologies, nested_arrival_rates, nested_cost, nested_args = \ self.__handle_nested_operator(arg, pattern, statistics, nested_topologies, nested_arrival_rates, nested_cost, nested_args) else: # If we are here, than this structure is primitive pattern, nested_topologies, nested_arrival_rates, nested_cost, nested_args = \ TreePlanBuilder.__handle_primitive_event(pattern, statistics, nested_topologies, nested_arrival_rates, nested_cost, nested_args) modified_statistics[ StatisticsTypes.ARRIVAL_RATES] = nested_arrival_rates return pattern, modified_statistics, nested_topologies, nested_args, nested_cost