def decompose_oi(classifier, max_width, algo, only_exact=False, max_num_groups=None): """ Decomposes given classifier into a set of order-independent subclassifiers. Args: classifier: Initial classifier. max_width: Maximal allowed classification width in the resulting subclassifiers. algo: Algorithm to use (Possible values 'icnp_oi', 'incp_blockers', 'min_similarity') only_exact: Whether only exact bits should be allowed (False by default). max_num_groups: Maximal allowed number of subclassifiers. Returns: Pair of subclassifiers list and classifier with leftover rules. """ p4t_native.log("OI decomposition has started: exact={:s}".format( str(only_exact))) subclassifiers = [] while (max_num_groups is None or len(subclassifiers) < max_num_groups) and len(classifier) > 0: bits, indices = p4t_native.best_subgroup(classifier, max_width, only_exact, algo) subclassifiers.append(classifier.subset(indices).reorder(bits)) classifier = classifier.subset( set(range(len(classifier))) - set(indices)) p4t_native.log("OI decomposition has completed") return subclassifiers, classifier
def decompose_oi(classifier, max_width, algo, max_num_groups, *, only_exact=False, max_candidate_groups=None, max_oi_algo='top_down'): """ Decomposes given classifier into a set of order-independent subclassifiers. Args: classifier: Initial classifier. max_width: Maximal allowed classification width in the resulting subclassifiers. algo: Algorithm to use (Possible values 'icnp_oi', 'incp_blockers', 'min_similarity') only_exact: Whether only exact bits should be allowed (False by default). max_num_groups: Maximal allowed number of subclassifiers. max_candidate_groups: The total number of candidate groups. max_oi_algo: The algorithm used for maximal OI (Possible values 'top_down' and 'min_degree'). Returns: Pair of subclassifiers list and classifier with leftover rules. """ assert (max_candidate_groups is None or max_num_groups <= max_candidate_groups) if max_candidate_groups is None: max_candidate_groups = max_num_groups p4t_native.log(f"OI decomposition has started: exact={only_exact}") indices = list(range(len(classifier))) oi_groups = [] while len(oi_groups) < max_candidate_groups and len(indices) > 0: oi_bits, oi_indices = p4t_native.best_subgroup(classifier.subset(indices), max_width, only_exact, algo, max_oi_algo) current_indices = [indices[i] for i in oi_indices] oi_groups.append(OIGroupInfo( classifier=classifier.subset(oi_indices).reorder(oi_bits), indices=current_indices )) indices = sorted(set(indices) - set(current_indices)) p4t_native.log("OI decomposition has completed") oi_groups.sort(key=lambda x: len(x.indices), reverse=True) selected_groups = oi_groups[:max_num_groups] rest_indices = sorted(set(range(len(classifier))) - reduce( lambda x, y: x | y, (set(x.indices) for x in selected_groups), set() )) return ([x.classifier for x in selected_groups], classifier.subset(rest_indices))
def try_boolean_minimization(cls, use_resolution=False): """ Applies boolean minimization to a given classifier Args: cls: classifier to minimize use_resolution: use resolution technique (slower) Returns: possibly minimized version of the given classifier """ p4t_native.log(f"Starting boolean minimization for {len(cls)} rules") result = cls.subset([]) fill_from_native(result, p4t_native.try_boolean_minimization(cls, use_resolution)) return result
def weight_action_obstruction(cls): """ Given a classifier, weight its actions according to obstruction Args: cls: classifier Returns: a dictionary mapping actions to their weights """ p4t_native.log( f'running weight obstruction calculation for {len(cls)} rules') result = p4t_native.calc_obstruction_weights(cls) for e in cls: if e.action not in result: result[e.action] = 0 return result
def test_incremental(classifier, max_width, max_num_groups, max_traditional, *, algo='icnp_blockers', max_oi_algo='min_degree', max_candidate_groups=None): """ Test incremental updates for a classifier Args: classifier: Test classifier. max_width: Maximal allowed classification width. max_num_groups: Maximal allowed number of groups. max_traditional: Maximal allowed size of traditional representation. algo: Algorithm to use (Possible values 'icnp_oi', 'incp_blockers', 'min_similarity') max_candidate_groups: The total number of candidate groups max_oi_algo: The algorithm used for maximal OI (Possible values 'top_down' and 'min_degree'). Returns: A list of Incremental Batch Stats """ p4t_native.log("Running incremental updates...") incremental_stats = [] num_added, lpm_groups, traditional = 0, [], classifier.subset([]) while num_added < len(classifier): incremental, traditional = p4t_native.incremental_updates( classifier.subset(range(num_added, len(classifier))), lpm_groups, traditional, max_traditional) incremental_stats.append(IncrementalBatchStats(incremental, traditional)) num_added += incremental + traditional p4t_native.log( f"... incremental: {incremental}, traditional: {traditional}" f" total: {num_added}, ... {max_traditional}") if num_added < len(classifier): lpm_groups, traditional = minimize_oi_lpm( classifier.subset(range(num_added)), max_width, algo, max_num_groups, max_oi_algo=max_oi_algo, max_candidate_groups=max_candidate_groups) return incremental_stats
def minimize_oi_lpm(classifier, max_width, algo, max_num_groups, *, max_expanded_bits=None, provide_non_expanded=False, max_candidate_groups=None, max_oi_algo='top_down'): """ Minimizes the number of subclassifiers, which are both LPM and OI. Args: classifier: Initial classifier. max_width: Maximal allowed classification width in the resulting subclassifiers. algo: Algorithm to use (Possible values 'icnp_oi', 'incp_blockers', 'min_similarity') max_num_groups: Maximal allowed number of subclassifiers. max_expanded_bits: Maximal allowed number of expanded bits. provide_non_expanded: Whether non-expanded versions should be returned. max_candidate_groups: The total number of candidate groups max_oi_algo: The algorithm used for maximal OI (Possible values 'top_down' and 'min_degree'). Returns: Pair of subclassifiers list and classifier with leftover rules. If provide_non_expanded is True, then the third element in a tuple is a list of non-expanded classifiers. """ assert max_expanded_bits is not None or not provide_non_expanded assert (max_candidate_groups is None or max_num_groups <= max_candidate_groups) if max_candidate_groups is None: max_candidate_groups = max_num_groups indices = list(range(len(classifier))) lpm_groups = [] while len(indices) > 0 and len(lpm_groups) < max_candidate_groups: p4t_native.log(f"OI-LPM has started for group #{len(lpm_groups) + 1}") oi_bits, oi_indices = p4t_native.best_subgroup( classifier.subset(indices), max_width, False, algo, max_oi_algo ) oi_classifier = classifier.subset( indices[i] for i in oi_indices ).reorder(oi_bits) if max_expanded_bits is None: [[bitchain]], [[lpm_indices]] = p4t_native.min_bmgr( [oi_classifier], 1 ) group = oi_classifier.subset(lpm_indices).reorder( _chain2bits(bitchain, oi_classifier.bit_width) ) nexp_group = None else: bitchain, lpm_indices, expansions = p4t_native.min_bmgr1_w_expansions( oi_classifier, max_expanded_bits) group = oi_classifier.subset([]) for i, exp in zip(lpm_indices, expansions): for entry in expand(oi_classifier[i], exp): group.vmr.append(entry) if provide_non_expanded: nexp_group = oi_classifier.subset(lpm_indices).reorder( _chain2bits(bitchain, oi_classifier.bit_width) ) else: nexp_group = None current_indices = [indices[oi_indices[i]] for i in lpm_indices] lpm_groups.append(LPMGroupInfo( classifier=group, nexp_classifier=nexp_group, indices=current_indices )) indices = sorted(set(indices) - set(current_indices)) p4t_native.log("OI decomposition has finished") lpm_groups.sort(key = lambda x: len(x.indices), reverse=True) selected_groups = lpm_groups[:max_num_groups] rest_indices = sorted(set(range(len(classifier))) - reduce( lambda x, y: x | y, (set(x.indices) for x in selected_groups), set() )) if provide_non_expanded: return ( [x.classifier for x in selected_groups], classifier.subset(rest_indices), [x.nexp_classifier for x in selected_groups] ) else: return ( [x.classifier for x in selected_groups], classifier.subset(rest_indices) )
def minimize_oi_lpm(classifier, max_width, algo, max_num_groups, max_expanded_bits=None, provide_non_expanded=False): """ Minimizes the number of subclassifiers, which are both LPM and OI. Args: classifier: Initial classifier. max_width: Maximal allowed classification width in the resulting subclassifiers. algo: Algorithm to use (Possible values 'icnp_oi', 'incp_blockers', 'min_similarity') max_num_groups: Maximal allowed number of subclassifiers. max_expanded_bits: Maximal allowed number of expanded bits. provide_non_expanded: Whether non-expanded versions should be returned. Returns: Pair of subclassifiers list and classifier with leftover rules. If provide_non_expanded is True, then the third element in a tuple is a list of non-expanded classifiers. """ assert max_expanded_bits is not None or not provide_non_expanded subclassifiers = [] non_expanded_subclassifiers = [] while len(classifier) > 0 and len(subclassifiers) < max_num_groups: p4t_native.log("OI-LPM has started for group #{:d}".format( len(subclassifiers) + 1)) oi_bits, oi_indices = p4t_native.best_subgroup(classifier, max_width, False, algo) oi_classifier = classifier.subset(oi_indices).reorder(oi_bits) if max_expanded_bits is None: [[bitchain]], [[lpm_indices] ] = p4t_native.min_bmgr([oi_classifier], 1) subclassifiers.append( oi_classifier.subset(lpm_indices).reorder( _chain2bits(bitchain, oi_classifier.bit_width))) else: bitchain, lpm_indices, expansions = p4t_native.min_bmgr1_w_expansions( oi_classifier, max_expanded_bits) expanded = oi_classifier.subset([]) for i, exp in zip(lpm_indices, expansions): for entry in expand(oi_classifier[i], exp): expanded.vmr.append(entry) if provide_non_expanded: non_expanded_subclassifiers.append( oi_classifier.subset(lpm_indices).reorder( _chain2bits(bitchain, oi_classifier.bit_width))) subclassifiers.append( expanded.reorder(_chain2bits(bitchain, oi_classifier.bit_width))) classifier = classifier.subset( set(range(len(classifier))) - set(oi_indices[i] for i in lpm_indices)) p4t_native.log("OI decomposition has finished") if provide_non_expanded: return subclassifiers, classifier, non_expanded_subclassifiers else: return subclassifiers, classifier