algos/utils.py
def add_explanations_to_algorithms(algorithms):
    '''
    Given a list of algorithms, it explains each of the algorithm
    outputs and adds explanations to the corresponding algorithm
    '''

    def check_truth_assignment(truth_assignment, indexes, dataset):
        for combinations, termination in dataset:
            if not (truth_assignment == combinations[:, indexes]).all(axis=-1).any() == termination[0]:
                return False
        return True
    def get_pretty_explanation(indexes, truth_assignment):
        re = []

        for idx in indexes:
            str_i = f'{"" if truth_assignment[idx] else "~"}feature{0:09d}{idx}'
            re.append(str_i)
        return ' & '.join(re)

    for name, algorithm in algorithms.items():
        explanations = {i: {} for i in range(algorithm.output_features)}
        explanations.update({'termination': {}})

        # concatenate concepts/predictions from each batch into one tensor
        all_concepts = torch.cat(algorithm.predictions['concepts'], dim=0)
        all_termination_target = torch.cat(algorithm.actual['terminations'], dim=0)
        all_logits = torch.cat(algorithm.predictions['outputs'], dim=0)
        all_target = torch.cat(algorithm.actual['outputs'], dim=0)
        all_target_from_logits = (all_logits > 0).long if name == 'BFS' else all_logits.argmax(dim=-1)

        # After they are all stacked up
        # explain each class in turn
        for cls in range(max(algorithm.output_features, 2)):
            expl, expls = deep_logic.logic.explain_class(
                algorithm.concept_decoder,
                all_concepts,
                all_target,
                False if name=='parallel_coloring' else True,
                cls,
                simplify=True,
                topk_explanations=5000)
            explanations[cls] = expl

        # and explain termination
        algorithm.explanations = explanations

        # Add rules for termination
        t = TerminationCombinationDataset(algorithm.dataset)
        num_concepts = t.dataset[0][0].shape[1]
        indexes = [i for i in range(num_concepts)]
        explanation = None
        for l in range(1, num_concepts+1):
            for idx_combintion in itertools.combinations(indexes, l):
                for truth_assignment in itertools.product([0, 1], repeat=l):
                    if check_truth_assignment(np.array(truth_assignment), idx_combintion, t.dataset):
                        print("FOUND IT", torch.tensor(idx_combintion), torch.tensor(truth_assignment))
                        explanation = (np.array(truth_assignment), idx_combintion)
                        algorithm.termination_idx_combination = torch.tensor(idx_combintion).to(_DEVICE)
                        algorithm.termination_truth_assignment = torch.tensor(truth_assignment).to(_DEVICE)
                        break
                if explanation is not None:
                    break
            if explanation is not None:
                break

        
        algorithm.explanations['termination'] = get_pretty_explanation(algorithm.termination_idx_combination, algorithm.termination_truth_assignment)

    Given a list of algorithms, it explains each of the algorithm
    outputs and adds explanations to the corresponding algorithm
    
Given a list of algorithms, it explains each of the algorithm
outputs and adds explanations to the corresponding algorithm
Given a list of algorithms it explains each of the algorithm outputs and adds explanations to the corresponding algorithm
def add_explanations_to_algorithms(algorithms):
    def check_truth_assignment(truth_assignment, indexes, dataset):
        for combinations, termination in dataset:
            if not (truth_assignment == combinations[:, indexes]).all(axis=-1).any() == termination[0]:
                return False
        return True
    def get_pretty_explanation(indexes, truth_assignment):
        re = []
        for idx in indexes:
            str_i = f'{"" if truth_assignment[idx] else "~"}feature{0:09d}{idx}'
            re.append(str_i)
        return ' & '.join(re)
    for name, algorithm in algorithms.items():
        explanations = {i: {} for i in range(algorithm.output_features)}
        explanations.update({'termination': {}})
        all_concepts = torch.cat(algorithm.predictions['concepts'], dim=0)
        all_termination_target = torch.cat(algorithm.actual['terminations'], dim=0)
        all_logits = torch.cat(algorithm.predictions['outputs'], dim=0)
        all_target = torch.cat(algorithm.actual['outputs'], dim=0)
        all_target_from_logits = (all_logits > 0).long if name == 'BFS' else all_logits.argmax(dim=-1)
        for cls in range(max(algorithm.output_features, 2)):
            expl, expls = deep_logic.logic.explain_class(
                algorithm.concept_decoder,
                all_concepts,
                all_target,
                False if name=='parallel_coloring' else True,
                cls,
                simplify=True,
                topk_explanations=5000)
            explanations[cls] = expl
        algorithm.explanations = explanations
        t = TerminationCombinationDataset(algorithm.dataset)
        num_concepts = t.dataset[0][0].shape[1]
        indexes = [i for i in range(num_concepts)]
        explanation = None
        for l in range(1, num_concepts+1):
            for idx_combintion in itertools.combinations(indexes, l):
                for truth_assignment in itertools.product([0, 1], repeat=l):
                    if check_truth_assignment(np.array(truth_assignment), idx_combintion, t.dataset):
                        print("FOUND IT", torch.tensor(idx_combintion), torch.tensor(truth_assignment))
                        explanation = (np.array(truth_assignment), idx_combintion)
                        algorithm.termination_idx_combination = torch.tensor(idx_combintion).to(_DEVICE)
                        algorithm.termination_truth_assignment = torch.tensor(truth_assignment).to(_DEVICE)
                        break
                if explanation is not None:
                    break
            if explanation is not None:
                break
        algorithm.explanations['termination'] = get_pretty_explanation(algorithm.termination_idx_combination, algorithm.termination_truth_assignment)
def add_explanations_to_algorithms ( algorithms ) : def check_truth_assignment ( truth_assignment , indexes , dataset ) : for combinations , termination in dataset : if not ( truth_assignment == combinations [ : , indexes ] ) . all ( axis = - 1 ) . any ( ) == termination [ 0 ] : return False return True def get_pretty_explanation ( indexes , truth_assignment ) : re = [ ] for idx in indexes : str_i = f'{"" if truth_assignment[idx] else "~"}feature{0:09d}{idx}' re . append ( str_i ) return ' & ' . join ( re ) for name , algorithm in algorithms . items ( ) : explanations = { i : { } for i in range ( algorithm . output_features ) } explanations . update ( { 'termination' : { } } ) all_concepts = torch . cat ( algorithm . predictions [ 'concepts' ] , dim = 0 ) all_termination_target = torch . cat ( algorithm . actual [ 'terminations' ] , dim = 0 ) all_logits = torch . cat ( algorithm . predictions [ 'outputs' ] , dim = 0 ) all_target = torch . cat ( algorithm . actual [ 'outputs' ] , dim = 0 ) all_target_from_logits = ( all_logits > 0 ) . long if name == 'BFS' else all_logits . argmax ( dim = - 1 ) for cls in range ( max ( algorithm . output_features , 2 ) ) : expl , expls = deep_logic . logic . explain_class ( algorithm . concept_decoder , all_concepts , all_target , False if name == 'parallel_coloring' else True , cls , simplify = True , topk_explanations = 5000 ) explanations [ cls ] = expl algorithm . explanations = explanations t = TerminationCombinationDataset ( algorithm . dataset ) num_concepts = t . dataset [ 0 ] [ 0 ] . shape [ 1 ] indexes = [ i for i in range ( num_concepts ) ] explanation = None for l in range ( 1 , num_concepts + 1 ) : for idx_combintion in itertools . combinations ( indexes , l ) : for truth_assignment in itertools . product ( [ 0 , 1 ] , repeat = l ) : if check_truth_assignment ( np . array ( truth_assignment ) , idx_combintion , t . dataset ) : print ( "FOUND IT" , torch . tensor ( idx_combintion ) , torch . tensor ( truth_assignment ) ) explanation = ( np . array ( truth_assignment ) , idx_combintion ) algorithm . termination_idx_combination = torch . tensor ( idx_combintion ) . to ( _DEVICE ) algorithm . termination_truth_assignment = torch . tensor ( truth_assignment ) . to ( _DEVICE ) break if explanation is not None : break if explanation is not None : break algorithm . explanations [ 'termination' ] = get_pretty_explanation ( algorithm . termination_idx_combination , algorithm . termination_truth_assignment )
Given a list of algorithms, it explains each of the algorithm
outputs and adds explanations to the corresponding algorithm
Given a list of algorithms it explains each of the algorithm outputs and adds explanations to the corresponding algorithm
'''
    Given a list of algorithms, it explains each of the algorithm
    outputs and adds explanations to the corresponding algorithm
    ''' # concatenate concepts/predictions from each batch into one tensor
 # After they are all stacked up
 # explain each class in turn
 # and explain termination
 # Add rules for termination

algorithms