예제 #1
0
 def setup_net(self):
     if not self.setup:
         self.setup = True
         self.net = Net(*SimpleParser(open(self.sisc_file)).parse(), **self.options)
         for c,i in enumerate(self.net.inputs):
             self.net.inputs[i] = self.inputs[c] == 1
         for c,i in enumerate(self.net.outputs):
             self.net.outputs[i] = self.outputs[c] == 1
         self.components = TwoWayDict(dict(enumerate(self.net.gates.keys())))
예제 #2
0
 def __init__(self, specification, trace, **options):
     super(SimpleHittingSetDescription, self).__init__([], **options)
     self.spec = specification
     self.trace = trace
     enc_options = {'ab_predicates': True}
     if options.get('variable_typos', False) is True:
         enc_options['variable_typos'] = True
     self.variant = options.get('encoding_variant_class', 'DirectEncoding')
     self.de = eval(self.variant)(self.spec, self.trace, **enc_options)
     self.components = TwoWayDict(
         dict(enumerate(get_all_subformulas(self.spec, Type.ANY))))
     self.scs = []
예제 #3
0
 def __init__(self, specification, trace, **options):
     super(FaultModeHittingSetDescription, self).__init__([], **options)
     self.spec = specification
     self.trace = trace
     self.variant = options.get('encoding_variant_class', 'DirectEncoding')
     self.de = eval(self.variant)(self.spec, self.trace,
                                  **options.get('fault_mode_opts',
                                                {'all': True}))
     self.components = TwoWayDict(
         dict(enumerate(get_all_subformulas_list(self.spec, Type.ANY))))
     self.fault_modes = None
     self.scs = set()
     self.sat_solver = options.get('sat_solver', 'picosat')
예제 #4
0
    def setup_system_model(self):
        config = self.net_generator.get_config_copy()

        if not config:
            return False

        if not config.nodes or not config.observers:
            return False

        if not self.setup:
            self.setup = True
            self.net.set_model(config)
            self.nodes = TwoWayDict(dict(enumerate(self.net.nodes)))

        self.net.set_observations(self.observations)
        return True
예제 #5
0
 def __init__(self, specification, trace, **options):
     super(FaultModeHittingSetDescription, self).__init__([], **options)
     self.spec = specification
     self.trace = trace
     self.variant = options.get('encoding_variant_class', 'DirectEncoding')
     self.de = eval(self.variant)(self.spec, self.trace, **options.get('fault_mode_opts', {'all':True}))
     self.components = TwoWayDict(dict(enumerate(get_all_subformulas_list(self.spec, Type.ANY))))
     self.fault_modes = None
     self.scs = set()
     self.sat_solver = options.get('sat_solver', 'picosat')
예제 #6
0
 def __init__(self, specification, trace, **options):
     super(SimpleHittingSetDescription, self).__init__([], **options)
     self.spec = specification
     self.trace = trace
     enc_options = {'ab_predicates':True}
     if options.get('variable_typos',False) is True:
         enc_options['variable_typos'] = True
     self.variant = options.get('encoding_variant_class', 'DirectEncoding')
     self.de = eval(self.variant)(self.spec, self.trace, **enc_options)
     self.components = TwoWayDict(dict(enumerate(get_all_subformulas(self.spec, Type.ANY))))
     self.scs = []
예제 #7
0
 def __init__(self, sisc_file, inputs, mutated_gates, **options):
     super(IscasOracleOld, self).__init__(None, [], [], **options)
     self.sisc_file = sisc_file
     self.mutated_gates = mutated_gates
     self.net = Net(*SimpleParser(open(sisc_file)).parse(), **options)
     for c,i in enumerate(self.net.inputs):
         self.net.inputs[i] = inputs[c] == 1
     orig_gates = self.net.mutate_net(mutated_gates)
     self.net.calculate_outputs()
     self.net.mutate_net(orig_gates)
     self.comp_calls = 0
     self.check_calls = 0
     self.comp_time = 0
     self.check_time = 0
     self.scs = set()
     self.components = TwoWayDict(dict(enumerate(self.net.gates.keys())))
예제 #8
0
class IscasOracle(Description):
    
    def __init__(self, sisc_file, inputs, outputs, **options):
        super(IscasOracle, self).__init__([], **options)
        self.sisc_file = sisc_file
        self.comp_calls = 0
        self.check_calls = 0
        self.comp_time = 0
        self.check_time = 0
        self.scs = set()
        self.setup = False
        self.inputs = inputs
        self.outputs = outputs
        self.net = None
        
    def setup_net(self):
        if not self.setup:
            self.setup = True
            self.net = Net(*SimpleParser(open(self.sisc_file)).parse(), **self.options)
            for c,i in enumerate(self.net.inputs):
                self.net.inputs[i] = self.inputs[c] == 1
            for c,i in enumerate(self.net.outputs):
                self.net.outputs[i] = self.outputs[c] == 1
            self.components = TwoWayDict(dict(enumerate(self.net.gates.keys())))
            
    def set_options(self, **options):
        super(IscasOracle, self).set_options(**options)
        if self.net is not None:
            self.net.set_options(**options)
    
    def get_num_components(self):
        self.setup_net()
        return len(self.components)
        
    def get_conflict_set(self, h):
        self.setup_net()
        self.comp_calls += 1
        t0 = time.time()
        cs = self.net.calculate_conflicts(self.numbers_to_gates(h))
        t1 = time.time()
        self.comp_time += t1-t0
        if cs:
            self.scs.add(frozenset(cs))
            return self.gates_to_numbers(cs)
        else:
            return None
    
    def check_consistency(self, h):
        self.setup_net()
        self.check_calls += 1
        t0 = time.time()
        sat = self.net.check_consistency(self.numbers_to_gates(h))
        t1 = time.time()
        self.check_time += t1-t0
        return sat

    def get_next_diagnosis(self, previous_diagnoses, max_card=None):
        self.setup_net()
        self.comp_calls += 1
        t0 = time.time()
#        print "get_next_diagnosis(%s,%d)"%(previous_diagnoses, max_card)
        diag = self.net.calculate_next_diagnosis(map(self.numbers_to_gates,previous_diagnoses), max_card=max_card)
#        print "solution:", diag
        t1 = time.time()
        self.comp_time += t1-t0
        if diag:
            return self.gates_to_numbers(diag)
        else:
            return None
        
    def get_all_diagnoses(self, previous_diagnoses, max_card=None, max_solutions=None):
        self.setup_net()
        self.comp_calls += 1
        t0 = time.time()
        diag = self.net.calculate_next_diagnosis(map(self.numbers_to_gates,previous_diagnoses), max_card=max_card, find_all_sols=True)
        t1 = time.time()
        self.comp_time += t1-t0
        if diag:
            return map(self.gates_to_numbers, diag)
        else:
            return None
        

    def finished(self):
        self.net.finished()
        
    def gates_to_numbers(self, gates):
        return frozenset(map(lambda g: self.components.key(g), gates))
        
    def numbers_to_gates(self, numbers):
        return frozenset(map(lambda n: self.components[n], numbers))
예제 #9
0
class TUGDescriptionOracle(Description):
    def __init__(self, **options):
        super(TUGDescriptionOracle, self).__init__([], **options)
        self.comp_calls = 0
        self.check_calls = 0
        self.comp_time = 0
        self.check_time = 0
        self.scs = set()
        self.model_ready = False
        self.observations = ()
        self.net = Model(**self.options)
        self.net_generator = ModelGenerator()
        self.setup = False
        self.nodes = None

    def setup_system_model(self):
        config = self.net_generator.get_config_copy()

        if not config:
            return False

        if not config.nodes or not config.observers:
            return False

        if not self.setup:
            self.setup = True
            self.net.set_model(config)
            self.nodes = TwoWayDict(dict(enumerate(self.net.nodes)))

        self.net.set_observations(self.observations)
        return True

    def set_options(self, **options):
        super(TUGDescriptionOracle, self).set_options(**options)
        if self.net is not None:
            self.net.set_options(**options)

    def is_real_node(self, node_name):
        return self.net.is_real_node(node_name)

    def get_num_nodes(self):
        if not self.setup_system_model():
            return 0
        return len(self.nodes)

    def get_conflict_set(self, h):
        if not self.setup_system_model():
            return None
        self.comp_calls += 1
        t0 = time.time()
        cs = self.net.calculate_conflicts(self.numbers_to_nodes(h))
        t1 = time.time()
        self.comp_time += t1 - t0
        if cs:
            self.scs.add(frozenset(cs))
            return self.nodes_to_numbers(cs)
        else:
            return None

    def check_consistency(self, h):
        if not self.setup_system_model():
            return None
        self.check_calls += 1
        t0 = time.time()
        sat = self.net.check_consistency(self.numbers_to_nodes(h))
        t1 = time.time()
        self.check_time += t1 - t0
        return sat

    def finished(self):
        self.net.finished()

    def nodes_to_numbers(self, nodes):
        return frozenset(map(lambda g: self.nodes.key(g), nodes))

    def numbers_to_nodes(self, numbers):
        return frozenset(map(lambda n: self.nodes[n], numbers))
예제 #10
0
class SimpleHittingSetDescription(Description):
    """
    An oracle/description object for a non-fault-mode based computation (i.e. only binary health variables). 
    In this case, the conflict sets are translated to sets of integer numbers, where each number represents 
    a subformula. This is to make the diagnosis algorithm easier to debug and avoids all problems that might 
    arise from using (complex) node objects inside the hitting set algorithms. The mapping from and to those 
    integers is done in the numbers_to_nodes and nodes_to_numbers methods, respectively, for each call 
    of check_consistency and get_conflict_set. By using the two-way dictionary, this should not impose any 
    performance problems. 
    """
    def __init__(self, specification, trace, **options):
        super(SimpleHittingSetDescription, self).__init__([], **options)
        self.spec = specification
        self.trace = trace
        enc_options = {'ab_predicates':True}
        if options.get('variable_typos',False) is True:
            enc_options['variable_typos'] = True
        self.variant = options.get('encoding_variant_class', 'DirectEncoding')
        self.de = eval(self.variant)(self.spec, self.trace, **enc_options)
        self.components = TwoWayDict(dict(enumerate(get_all_subformulas(self.spec, Type.ANY))))
        self.scs = []
    
    def get_first_conflict_set(self):
        return self.get_conflict_set(set())
    
    def check_consistency(self, h):
#        print "check_consistency, h=%s" % map(lambda n: "n"+str(n.id), self.numbers_to_nodes(h))
        t0 = time.time()
        self.check_calls += 1
        r = self.de.check(abnormal_subformulae=self.numbers_to_nodes(h), sat_solver=self.options.get('sat_solver', 'picosat'), config=self.options.get('config',{}))
        t1 = time.time()
        self.check_time += t1-t0
#        print "->", r
        return r

    def get_conflict_set(self, h):
#        print "get_conflict_set, h=%s" % map(lambda n: "n"+str(n.id), self.numbers_to_nodes(h))
        t0 = time.time()
        self.comp_calls += 1
        r, cs = self.de.get_conflict(abnormal_subformulae=self.numbers_to_nodes(h), sat_solver=self.options.get('sat_solver', 'picosat'), config=self.options.get('config',{}))
        if r:
            return None
        else:
#            print cs
            if not self.de.check(abnormal_subformulae=cs, sat_solver=self.options.get('sat_solver', 'picosat'), config=self.options.get('config',{})):
                print("WARNING! Result is inconsistent, i.e. not a real conflict set!")
            
            t1 = time.time()
            self.comp_time += t1-t0
            self.scs.append(frozenset(cs))
#            print "->", map(lambda n: "n"+str(n.id), cs)
            return self.nodes_to_numbers(cs)
        
    def get_next_diagnosis(self, previous_diagnoses, max_card=None):
        self.comp_calls += 1
        t0 = time.time()
#        print "get_next_diagnosis(%s,%d)"%(previous_diagnoses, max_card)
        diag = self.de.calculate_next_diagnosis(map(self.numbers_to_nodes,previous_diagnoses), max_card=max_card, sat_solver=self.options.get('sat_solver', 'simple-yices'), config=self.options.get('config',{}))
#        print "solution:", diag
        t1 = time.time()
        self.comp_time += t1-t0
        if diag:
            return self.nodes_to_numbers(diag)
        else:
            return None
        
    def get_all_diagnoses(self, previous_diagnoses, max_card=None, max_solutions=None):
        self.comp_calls += 1
        t0 = time.time()
        diag = self.de.calculate_next_diagnosis(map(self.numbers_to_nodes,previous_diagnoses), max_card=max_card, find_all_sols=True, sat_solver=self.options.get('sat_solver', 'simple-yices'), config=self.options.get('config',{}))
        t1 = time.time()
        self.comp_time += t1-t0
        if diag:
            return map(self.nodes_to_numbers, diag)
        else:
            return None
    
    def nodes_to_numbers(self, gates):
        return frozenset(map(lambda g: self.components.key(g), gates))
        
    def numbers_to_nodes(self, numbers):
        return frozenset(map(lambda n: self.components[n], numbers))
예제 #11
0
class FaultModeHittingSetDescription(Description):
    """
    Oracle/Description used for fault mode diagnosis runs. Again the subformulae are mapped to integer 
    numbers, which together with the fault mode number form tuples for the conflict sets (component,faultmode).
    A conflict set is sometimes called mode assignment in this class (i.e. a mode combination that leads to a 
    conflict).  
    """
    def __init__(self, specification, trace, **options):
        super(FaultModeHittingSetDescription, self).__init__([], **options)
        self.spec = specification
        self.trace = trace
        self.variant = options.get('encoding_variant_class', 'DirectEncoding')
        self.de = eval(self.variant)(self.spec, self.trace, **options.get('fault_mode_opts', {'all':True}))
        self.components = TwoWayDict(dict(enumerate(get_all_subformulas_list(self.spec, Type.ANY))))
        self.fault_modes = None
        self.scs = set()
        self.sat_solver = options.get('sat_solver', 'picosat')
    
    def get_first_conflict_set(self):
#        self.check_calls += 1
        return self.get_conflict_set(set())
    
    def check_consistency(self, h):
#        print "check_consistency, h=%s" % h
#        for k,v in self.tuples_to_mode_assignment(h).iteritems():
#            print k, v, self.de.fault_modes[k][v]
        self.check_calls += 1
        tuples = self.tuples_to_mode_assignment(h)
        t0 = time.time()
        r = self.de.check_with_modes(mode_assignment=tuples,sat_solver=self.sat_solver)
        t1 = time.time()
        self.check_time += t1-t0
        return r

    def get_fault_modes_for(self, component):
        """
        In the diagnosis algorithm (HS-DAG), this method is used to determine which fault 
        modes should be used to expand a node, e.g. if component 3 has fault modes 0,1,2,False
        (False meaning deactivated), then this method would return 1,2. The result of this 
        computation is cached such that it can be used for the next query again. 
        """
        if not self.fault_modes:
            self.fault_modes = defaultdict(list)
            for num,sf in self.components.iteritems():
                if sf in self.de.fault_modes:
                    for i,fm in enumerate(self.de.fault_modes[sf]):
                        if fm != False and fm != sf:
                            self.fault_modes[num].append((num,i))
#            print sum(map(len,self.fault_modes.values()))
#            print self.components
        return self.fault_modes[component]

    def get_conflict_set(self, h):
#        print "get_conflict_set, h=%s" % h
        t0 = time.time()
        self.comp_calls += 1
        r, cs = self.de.get_conflict_with_modes(mode_assignment=self.tuples_to_mode_assignment(h), sat_solver=self.sat_solver)
        if r:
            return None
        else:
#            if not self.de.check_with_modes(mode_assignment=cs):
#                print("WARNING! Result is inconsistent, i.e. not a real conflict set!")
            
            t1 = time.time()
            self.comp_time += t1-t0
#            print map(str, cs)
#            print "--> CS:", self.nodes_to_tuples(cs)
#            print
            result = self.nodes_to_tuples(cs) # - self.nodes_to_tuples(self.tuples_to_nodes(h))
            self.scs.add(frozenset(result)) 
            return result

    def get_next_diagnosis(self, previous_diagnoses, max_card=None):
        self.comp_calls += 1
        t0 = time.time()
#        print "get_next_diagnosis(%s,%d)"%(previous_diagnoses, max_card)
        diag = self.de.calculate_next_diagnosis(map(self.tuples_to_mode_assignment,previous_diagnoses), max_card=max_card, sat_solver=self.options.get('sat_solver', 'simple-yices'))
#        print "solution:", diag
        t1 = time.time()
        self.comp_time += t1-t0
        if diag:
            return self.nodes_to_tuples(diag)
        else:
            return None
        
    def get_all_diagnoses(self, previous_diagnoses, max_card=None, max_solutions=None):
        self.comp_calls += 1
        t0 = time.time()
        diag = self.de.calculate_next_diagnosis(map(self.tuples_to_mode_assignment,previous_diagnoses), max_card=max_card, find_all_sols=True, sat_solver=self.options.get('sat_solver', 'simple-yices'))
        t1 = time.time()
        self.comp_time += t1-t0
        if diag:
            return map(self.nodes_to_tuples, diag)
        else:
            return None

    def tuples_to_mode_assignment(self, tuples):
        return dict((self.components[t[0]],t[1]) for t in tuples)
    
    def nodes_to_tuples(self, nodes):
        result = set()
        for n,m in nodes:
            result.add((self.components.key(n), m))
#        for n in nodes:
#            for f in xrange(1,len(self.de.fault_modes[n])):
#                if self.de.fault_modes[n][f] != False:
#                    result.add((self.components.key(n),f))
        return frozenset(result)
        
#    def tuples_to_nodes(self, tuples):
#        return frozenset(map(lambda n: self.components[n[0]], tuples))
    
    def hittingset_to_formula(self, tuples):
        """
        this method allows to convert a hitting set back to a fully-fledged LTL formula again, 
        which means that starting from the original formula, the modes in the hitting set are 
        used to replace the corresponding subformulae with their fault mode. Note that this is 
        just for debugging and output purposes, so some small hacks are acceptable :)
        """
        mode_assignment = dict((self.components[t[0]].id,t[1]) for t in tuples)
        f_mapping = {}
        for sf in get_all_subformulas(self.spec, Type.ANY):
            f_mapping[sf.id] = sf
        f = self.spec.clone()
        for sf in get_all_subformulas(f, Type.ANY):
            if sf.id in mode_assignment:
                try:
                    new = self.de.fault_modes[f_mapping[sf.id]][mode_assignment[sf.id]]
                except:
                    print "whowooo"
                if type(new) == Node:
                    # this is a HACK!
                    orig = self.de.fault_modes[f_mapping[sf.id]][0]
                    perm = [orig.children.index(v) for v in new.children]
                    sf.type = new.type
                    sf.children = [sf.children[v] for v in perm]
                    sf.value = new.value
                    sf.instmode = new.instmode
                elif new == True:
                    sf.type = Type.Constant
                    sf.children = []
                    sf.value = "True"
        return f
    
    def print_mapping(self):
        for i in self.components:
            print i, self.components[i]
            if self.components[i] in self.de.fault_modes:
                for j,f in enumerate(self.de.fault_modes[self.components[i]]):
                    print "\t", j, f
예제 #12
0
class SimpleHittingSetDescription(Description):
    """
    An oracle/description object for a non-fault-mode based computation (i.e. only binary health variables). 
    In this case, the conflict sets are translated to sets of integer numbers, where each number represents 
    a subformula. This is to make the diagnosis algorithm easier to debug and avoids all problems that might 
    arise from using (complex) node objects inside the hitting set algorithms. The mapping from and to those 
    integers is done in the numbers_to_nodes and nodes_to_numbers methods, respectively, for each call 
    of check_consistency and get_conflict_set. By using the two-way dictionary, this should not impose any 
    performance problems. 
    """
    def __init__(self, specification, trace, **options):
        super(SimpleHittingSetDescription, self).__init__([], **options)
        self.spec = specification
        self.trace = trace
        enc_options = {'ab_predicates': True}
        if options.get('variable_typos', False) is True:
            enc_options['variable_typos'] = True
        self.variant = options.get('encoding_variant_class', 'DirectEncoding')
        self.de = eval(self.variant)(self.spec, self.trace, **enc_options)
        self.components = TwoWayDict(
            dict(enumerate(get_all_subformulas(self.spec, Type.ANY))))
        self.scs = []

    def get_first_conflict_set(self):
        return self.get_conflict_set(set())

    def check_consistency(self, h):
        #        print "check_consistency, h=%s" % map(lambda n: "n"+str(n.id), self.numbers_to_nodes(h))
        t0 = time.time()
        self.check_calls += 1
        r = self.de.check(abnormal_subformulae=self.numbers_to_nodes(h),
                          sat_solver=self.options.get('sat_solver', 'picosat'),
                          config=self.options.get('config', {}))
        t1 = time.time()
        self.check_time += t1 - t0
        #        print "->", r
        return r

    def get_conflict_set(self, h):
        #        print "get_conflict_set, h=%s" % map(lambda n: "n"+str(n.id), self.numbers_to_nodes(h))
        t0 = time.time()
        self.comp_calls += 1
        r, cs = self.de.get_conflict(
            abnormal_subformulae=self.numbers_to_nodes(h),
            sat_solver=self.options.get('sat_solver', 'picosat'),
            config=self.options.get('config', {}))
        if r:
            return None
        else:
            #            print cs
            if not self.de.check(abnormal_subformulae=cs,
                                 sat_solver=self.options.get(
                                     'sat_solver', 'picosat'),
                                 config=self.options.get('config', {})):
                print(
                    "WARNING! Result is inconsistent, i.e. not a real conflict set!"
                )

            t1 = time.time()
            self.comp_time += t1 - t0
            self.scs.append(frozenset(cs))
            #            print "->", map(lambda n: "n"+str(n.id), cs)
            return self.nodes_to_numbers(cs)

    def get_next_diagnosis(self, previous_diagnoses, max_card=None):
        self.comp_calls += 1
        t0 = time.time()
        #        print "get_next_diagnosis(%s,%d)"%(previous_diagnoses, max_card)
        diag = self.de.calculate_next_diagnosis(
            map(self.numbers_to_nodes, previous_diagnoses),
            max_card=max_card,
            sat_solver=self.options.get('sat_solver', 'simple-yices'),
            config=self.options.get('config', {}))
        #        print "solution:", diag
        t1 = time.time()
        self.comp_time += t1 - t0
        if diag:
            return self.nodes_to_numbers(diag)
        else:
            return None

    def get_all_diagnoses(self,
                          previous_diagnoses,
                          max_card=None,
                          max_solutions=None):
        self.comp_calls += 1
        t0 = time.time()
        diag = self.de.calculate_next_diagnosis(
            map(self.numbers_to_nodes, previous_diagnoses),
            max_card=max_card,
            find_all_sols=True,
            sat_solver=self.options.get('sat_solver', 'simple-yices'),
            config=self.options.get('config', {}))
        t1 = time.time()
        self.comp_time += t1 - t0
        if diag:
            return map(self.nodes_to_numbers, diag)
        else:
            return None

    def nodes_to_numbers(self, gates):
        return frozenset(map(lambda g: self.components.key(g), gates))

    def numbers_to_nodes(self, numbers):
        return frozenset(map(lambda n: self.components[n], numbers))
예제 #13
0
class FaultModeHittingSetDescription(Description):
    """
    Oracle/Description used for fault mode diagnosis runs. Again the subformulae are mapped to integer 
    numbers, which together with the fault mode number form tuples for the conflict sets (component,faultmode).
    A conflict set is sometimes called mode assignment in this class (i.e. a mode combination that leads to a 
    conflict).  
    """
    def __init__(self, specification, trace, **options):
        super(FaultModeHittingSetDescription, self).__init__([], **options)
        self.spec = specification
        self.trace = trace
        self.variant = options.get('encoding_variant_class', 'DirectEncoding')
        self.de = eval(self.variant)(self.spec, self.trace,
                                     **options.get('fault_mode_opts',
                                                   {'all': True}))
        self.components = TwoWayDict(
            dict(enumerate(get_all_subformulas_list(self.spec, Type.ANY))))
        self.fault_modes = None
        self.scs = set()
        self.sat_solver = options.get('sat_solver', 'picosat')

    def get_first_conflict_set(self):
        #        self.check_calls += 1
        return self.get_conflict_set(set())

    def check_consistency(self, h):
        #        print "check_consistency, h=%s" % h
        #        for k,v in self.tuples_to_mode_assignment(h).iteritems():
        #            print k, v, self.de.fault_modes[k][v]
        self.check_calls += 1
        tuples = self.tuples_to_mode_assignment(h)
        t0 = time.time()
        r = self.de.check_with_modes(mode_assignment=tuples,
                                     sat_solver=self.sat_solver)
        t1 = time.time()
        self.check_time += t1 - t0
        return r

    def get_fault_modes_for(self, component):
        """
        In the diagnosis algorithm (HS-DAG), this method is used to determine which fault 
        modes should be used to expand a node, e.g. if component 3 has fault modes 0,1,2,False
        (False meaning deactivated), then this method would return 1,2. The result of this 
        computation is cached such that it can be used for the next query again. 
        """
        if not self.fault_modes:
            self.fault_modes = defaultdict(list)
            for num, sf in self.components.iteritems():
                if sf in self.de.fault_modes:
                    for i, fm in enumerate(self.de.fault_modes[sf]):
                        if fm != False and fm != sf:
                            self.fault_modes[num].append((num, i))
#            print sum(map(len,self.fault_modes.values()))
#            print self.components
        return self.fault_modes[component]

    def get_conflict_set(self, h):
        #        print "get_conflict_set, h=%s" % h
        t0 = time.time()
        self.comp_calls += 1
        r, cs = self.de.get_conflict_with_modes(
            mode_assignment=self.tuples_to_mode_assignment(h),
            sat_solver=self.sat_solver)
        if r:
            return None
        else:
            #            if not self.de.check_with_modes(mode_assignment=cs):
            #                print("WARNING! Result is inconsistent, i.e. not a real conflict set!")

            t1 = time.time()
            self.comp_time += t1 - t0
            #            print map(str, cs)
            #            print "--> CS:", self.nodes_to_tuples(cs)
            #            print
            result = self.nodes_to_tuples(
                cs)  # - self.nodes_to_tuples(self.tuples_to_nodes(h))
            self.scs.add(frozenset(result))
            return result

    def get_next_diagnosis(self, previous_diagnoses, max_card=None):
        self.comp_calls += 1
        t0 = time.time()
        #        print "get_next_diagnosis(%s,%d)"%(previous_diagnoses, max_card)
        diag = self.de.calculate_next_diagnosis(
            map(self.tuples_to_mode_assignment, previous_diagnoses),
            max_card=max_card,
            sat_solver=self.options.get('sat_solver', 'simple-yices'))
        #        print "solution:", diag
        t1 = time.time()
        self.comp_time += t1 - t0
        if diag:
            return self.nodes_to_tuples(diag)
        else:
            return None

    def get_all_diagnoses(self,
                          previous_diagnoses,
                          max_card=None,
                          max_solutions=None):
        self.comp_calls += 1
        t0 = time.time()
        diag = self.de.calculate_next_diagnosis(
            map(self.tuples_to_mode_assignment, previous_diagnoses),
            max_card=max_card,
            find_all_sols=True,
            sat_solver=self.options.get('sat_solver', 'simple-yices'))
        t1 = time.time()
        self.comp_time += t1 - t0
        if diag:
            return map(self.nodes_to_tuples, diag)
        else:
            return None

    def tuples_to_mode_assignment(self, tuples):
        return dict((self.components[t[0]], t[1]) for t in tuples)

    def nodes_to_tuples(self, nodes):
        result = set()
        for n, m in nodes:
            result.add((self.components.key(n), m))
#        for n in nodes:
#            for f in xrange(1,len(self.de.fault_modes[n])):
#                if self.de.fault_modes[n][f] != False:
#                    result.add((self.components.key(n),f))
        return frozenset(result)


#    def tuples_to_nodes(self, tuples):
#        return frozenset(map(lambda n: self.components[n[0]], tuples))

    def hittingset_to_formula(self, tuples):
        """
        this method allows to convert a hitting set back to a fully-fledged LTL formula again, 
        which means that starting from the original formula, the modes in the hitting set are 
        used to replace the corresponding subformulae with their fault mode. Note that this is 
        just for debugging and output purposes, so some small hacks are acceptable :)
        """
        mode_assignment = dict(
            (self.components[t[0]].id, t[1]) for t in tuples)
        f_mapping = {}
        for sf in get_all_subformulas(self.spec, Type.ANY):
            f_mapping[sf.id] = sf
        f = self.spec.clone()
        for sf in get_all_subformulas(f, Type.ANY):
            if sf.id in mode_assignment:
                try:
                    new = self.de.fault_modes[f_mapping[sf.id]][
                        mode_assignment[sf.id]]
                except:
                    print "whowooo"
                if type(new) == Node:
                    # this is a HACK!
                    orig = self.de.fault_modes[f_mapping[sf.id]][0]
                    perm = [orig.children.index(v) for v in new.children]
                    sf.type = new.type
                    sf.children = [sf.children[v] for v in perm]
                    sf.value = new.value
                    sf.instmode = new.instmode
                elif new == True:
                    sf.type = Type.Constant
                    sf.children = []
                    sf.value = "True"
        return f

    def print_mapping(self):
        for i in self.components:
            print i, self.components[i]
            if self.components[i] in self.de.fault_modes:
                for j, f in enumerate(self.de.fault_modes[self.components[i]]):
                    print "\t", j, f