Beispiel #1
0
def main_cross_validation(fname_examples: str, fname_settings: str, fname_background: str,
                          dir_fold_files: str, fname_prefix_fold: str, fold_start_index: int, nb_folds: int,
                          fold_suffix: str, dir_output_files: str,
                          filter_out_unlabeled_examples=False,
                          debug_printing_example_parsing=False,
                          debug_printing_tree_building=False,
                          debug_printing_tree_pruning=False,
                          debug_printing_program_conversion=False,
                          debug_printing_get_classifier=False,
                          debug_printing_classification=False):
    engine = DefaultEngine()
    engine.unknown = 1

    fd = FoldData.build_fold_data(fname_examples, fname_settings, fname_background,
                                  dir_fold_files, fname_prefix_fold, fold_start_index, nb_folds, fold_suffix,
                                  dir_output_files,
                                  filter_out_unlabeled_examples,
                                  debug_printing_example_parsing,
                                  debug_printing_tree_building,
                                  debug_printing_tree_pruning,
                                  debug_printing_program_conversion,
                                  debug_printing_get_classifier,
                                  debug_printing_classification,
                                  engine=engine
                                  )

    # take one key set as test, the others as training
    for fold_index, test_key_set in enumerate(fd.all_key_sets):
        do_one_fold(fold_index, test_key_set, fd)

    do_all_examples(fd)
Beispiel #2
0
def run_eval_neg(filename, **other):
    from .data import read_data, concat, Interpretations, Instance
    from problog.program import PrologString
    # from problog.logic import AnnotatedDisjunction, Clause
    print("starting eval")
    data = read_data(filename)

    rules = concat(data['RULES'])

    engine = DefaultEngine()
    engine.prepare(PrologString(':- unknown(fail).'))

    background_pl = concat(data.get('BACKGROUND', []))

    examples = data.get('!', [])
    examples_db = [
        engine.prepare(PrologString(background_pl + example_pl))
        for example_pl in examples
    ]
    instances = Interpretations(
        [Instance(example_db) for example_db in examples_db],
        PrologString(background_pl))

    for rule in PrologString(rules):
        clause = Clause.from_logic(rule)
        print('Evaluation of rule:', clause)
        if not clause.validate(instances, engine):
            print('\tRule is invalid')
            #for ex, success in enumerate(clause.successes):
            #    if not success:
            #        print('\t\tExample %s:' % (ex + 1), success)

        else:
            print('\tRule is valid.')
 def __init__(self,
              pos_examples,
              neg_examples,
              extra_terms=[],
              target_name='target'):
     # Define the language of terms
     self.target = Term(target_name)
     self.equal = Term('equal')
     self.pos_examples = pos_examples
     self.neg_examples = neg_examples
     self.examples = pos_examples + neg_examples
     self.extra_terms = extra_terms
     #TODO: check extra terms arity, if greater than target arity, create more variables
     n_target_variables = len(self.examples[0])
     target_variables_names = [
         'X' + str(i) for i in range(1, n_target_variables + 1)
     ]
     self.X = list(map(Var, target_variables_names))
     constants = set()
     for example in self.examples:
         constants.update(example)
     self.c = list(map(Term, [str(constant) for constant in constants]))
     # Initialize the logic program
     self.pl = SimpleProgram()
     self.pl += self.equal(self.X[0], self.X[0])
     self.pl += self.target(*tuple(self.X))
     for extra_term in self.extra_terms:
         self.pl += PrologString(extra_term)
     self.predicates = [self.equal]  # + list(extra_terms.keys())
     self.engine = DefaultEngine()
     self.db = self.engine.prepare(self.pl)
     self.original_rule = list(self.pl)[1]
     self.new_body_literals = []
     print(list(self.pl))
Beispiel #4
0
def main():
    p = PrologString("""
    mother_child(trude, sally).
    
    father_child(tom, sally).
    father_child(tom, erica).
    father_child(mike, tom).
    
    sibling(X, Y) :- parent_child(Z, X), parent_child(Z, Y).
    
    parent_child(X, Y) :- father_child(X, Y).
    parent_child(X, Y) :- mother_child(X, Y).
    """)

    sibling = Term('sibling')
    query_term = sibling(None, None)
    engine = DefaultEngine()

    # prepare the model for querying
    model_db = engine.prepare(p)  # This compiles the Prolog model into an internal format.
    # This step is optional, but it might be worthwhile if you
    #  want to query the same model multiple times.

    times_query = test_query_method1(engine, model_db, query_term)
    times_query_extended = test_query_method2(engine, model_db, query_term)

    print("average duration query:", statistics.mean(times_query), "seconds")
    print("average duration query:", statistics.mean(times_query_extended), "seconds")
def _run_tests():
    from problog.program import PrologString
    from problog.engine import DefaultEngine
    p = PrologString("""
    coin(c1). coin(c2).
    0.4::heads(C); 0.6::tails(C) :- coin(C).
    win :- heads(C).
    """)

    engine = DefaultEngine()
    db = engine.prepare(p)

    # tasks = [
    #     ([Term("win")], []),
    #     ([Term("win")], [(Term("heads", Term("c1")), True)]),
    #     ([Term("win")], [(Term("heads", Term("c1")), False)]),
    # ]
    # for q,e in tasks:
    #     qs.prepare_query(q, e)
    # print(qs.evaluate_queries())

    qs = QuerySession(engine, db)
    inline_queries = [" win | heads(c1).", "win | \+heads(c1).", "win."]
    for iq in inline_queries:
        q, e = qs.transform_inline_query(PrologString(iq)[0])
        qs.prepare_query(q, e)

    result = qs.evaluate_queries()
    print(result)
Beispiel #6
0
 def __init__(self, *sources):
     self._database = DefaultEngine().prepare(sources[0])
     # pdb.set_trace()
     for source in sources[1:]:
         for clause in source:
             # pdb.set_trace()
             self._database += clause
Beispiel #7
0
def sample( filename, N=1, with_facts=False, oneline=False ) :
    pl = PrologFile(filename)
    
    engine = DefaultEngine()
    db = engine.prepare(pl)
    
    for i in range(0, N) :
        result = engine.ground_all(db, target=SampledFormula())
        print ('====================')
        print (result.toString(db, with_facts, oneline))
Beispiel #8
0
def init_engine(**kwdargs):
    engine = DefaultEngine(**kwdargs)
    engine.add_builtin("sample", 2, builtin_sample)
    engine.add_builtin("value", 2, builtin_sample)
    engine.add_builtin("previous", 2, builtin_previous)
    engine.previous_result = None
    return engine
Beispiel #9
0
def init_engine(**kwdargs):
    engine = DefaultEngine(**kwdargs)
    engine.add_builtin('sample', 2, builtin_sample)
    engine.add_builtin('value', 2, builtin_sample)
    engine.add_builtin('previous', 2, builtin_previous)
    engine.previous_result = None
    return engine
Beispiel #10
0
def do_inference(a, b, terms, program):
    engine = DefaultEngine()
    xs = engine.prepare(program)

    print("[%s]是[%s]的 --> " % (b, a), end="")
    for key in terms:
        query_term = terms[key](a, b)
        res = engine.query(xs, query_term)
        if bool(res):
            print(terms[key], end=" ")
    print("")
Beispiel #11
0
    def test_compare(self) :
        """Comparison operator"""

        program = """
            morning(Hour) :- Hour >= 6, Hour =< 10.
        """

        engine = DefaultEngine()
        db = engine.prepare( PrologString(program) )

        self.assertEqual( list(map(list,engine.query(db, Term('morning', Constant(8)) ))), [[8]])
Beispiel #12
0
def extract_evidence(pl):
    engine = DefaultEngine()
    atoms = engine.query(pl, Term('evidence', None, None))
    atoms1 = engine.query(pl, Term('evidence', None))
    atoms2 = engine.query(pl, Term('observe', None))
    for atom in atoms1 + atoms2:
        atom = atom[0]
        if atom.is_negated():
            atoms.append((-atom, Term('false')))
        else:
            atoms.append((atom, Term('true')))
    return [(at, str2bool(vl)) for at, vl in atoms]
Beispiel #13
0
 def __init__(self, program, fformat):
     #GlobalEngine
     self._engine = DefaultEngine()
     gdl_parser = GDLIIIParser()
     self._model = gdl_parser.output_model(program, fformat)
     self._baseModelFile = self._model.as_problog()
     self._playerList = []
     self._randomIdentifier = Constant(0) #Hardcoded to give the random player a specific constant id as we apply some special rules to the random player
     worlds = self._initialiseKB()
     self._cur_node = GDLNode(worlds, GameData(self._playerList, self._randomIdentifier))
     self._moveList = dict([(i,None) for i in self._playerList])
     self.terminal = False
    def learn(self):
        '''Construisce gli oggetti problog per la valutazione dell'inferenza'''
        try:
            knowledge_str = ''
            for predicate in self.conoscenza_prob:
                knowledge_str += predicate + '\n'

            knowledge_str = PrologString(knowledge_str)
            self.problog = DefaultEngine()
            self.knowledge_database = self.problog.prepare(knowledge_str)

        except Exception as e:
            print(e)
Beispiel #15
0
def get_labels_single_example_models(example: SimpleProgram,
                                     rules: SimpleProgram,
                                     possible_labels: Iterable[str],
                                     background_knowledge=None,
                                     debug_printing=False) -> List[str]:
    """


    Classifies a single example and returns a list of its labels
    :param example:
    :param rules:
    :param possible_labels:
    :return:
    """
    eng = DefaultEngine()
    eng.unknown = 1

    if background_knowledge is not None:
        db = eng.prepare(background_knowledge)
        for statement in example:
            db += statement
        for rule in rules:
            db += rule
    else:
        db = eng.prepare(rules)
        for statement in example:
            db += statement

    if debug_printing:
        print('\nQueried database:')
        for statement in db:
            print('\t' + str(statement))
            # print('\n')

    result_list = []
    for label in possible_labels:
        db_to_query = db.extend()
        db_to_query += Term('query')(label)
        start_time = time.time()
        result = problog.get_evaluatable().create_from(db_to_query,
                                                       engine=eng).evaluate()
        end_time = time.time()
        print("call time:", end_time - start_time)

        if result[label] > 0.5:
            result_list.append(label)

    return result_list
Beispiel #16
0
class LabelCollector:
    """"

    IMPORTANT:
        * ONLY for KEYS format
        * has a lot of overlap with Classifier
    """
    def __init__(self,
                 predicate_to_query,
                 index_of_label_var,
                 background_knowledge=None,
                 debug_printing=False,
                 engine: GenericEngine = None):
        if engine is None:
            self.engine = DefaultEngine()
            self.engine.unknown = 1
        else:
            self.engine = engine
        if background_knowledge is not None:
            self.db = self.engine.prepare(background_knowledge)
        else:
            self.db = None
        self.predicate_to_query = predicate_to_query
        self.labels = set()  # type: Set[Label]
        self.index_of_label_var = index_of_label_var
        self.debug_printing = debug_printing

    def get_labels(self) -> Set[Label]:
        return self.labels

    def extract_labels(self, example_collection: ExampleCollection):
        raise NotImplementedError('Abstract method')
Beispiel #17
0
 def __init__(self, engine: GenericEngine = None):
     if engine is None:
         self.engine = DefaultEngine()
         self.engine.unknown = 1
     else:
         self.engine = engine
     self.to_query = Term('to_query')
Beispiel #18
0
    def __init__(self,
                 fname_prefix_fold,
                 nb_folds,
                 dir_output_files,
                 debug_printing_example_parsing=False,
                 debug_printing_tree_building=False,
                 debug_printing_tree_pruning=False,
                 debug_printing_program_conversion=False,
                 debug_printing_get_classifier=False,
                 debug_printing_classification=False,
                 engine: GenericEngine = None):

        self.fname_prefix_fold = fname_prefix_fold
        self.nb_folds = nb_folds
        self.dir_output_files = dir_output_files
        self.dir_output_files = dir_output_files

        self.debug_printing_example_parsing = debug_printing_example_parsing
        self.debug_printing_tree_building = debug_printing_tree_building
        self.debug_printing_tree_pruning = debug_printing_tree_pruning
        self.debug_printing_program_conversion = debug_printing_program_conversion
        self.debug_printing_get_classifier = debug_printing_get_classifier
        self.debug_printing_classification = debug_printing_classification

        if engine is None:
            self.engine = DefaultEngine()
            self.engine.unknown = 1
        else:
            self.engine = engine
Beispiel #19
0
    def test_cycle_goodcode(self):
        N = 20
        program = self.program_v1[:]

        for i in range(0, N):
            seed = str(random.random())[2:]
            random.seed(seed)
            random.shuffle(program)
            txt = "\n".join(program)
            f = DefaultEngine(label_all=True).ground_all(PrologString(txt))
            paths = list(list_paths(f))

            edges = set()
            for p in paths:
                for i in range(0, len(p) - 1):
                    edges.add((int(p[i]), int(p[i + 1])))
            edges = list(sorted(edges))

            # if (edges != self.edges) :
            #     with open('cycle_error.pl', 'w') as f :
            #         print(txt, file=f)
            #     with open('cycle_error.dot', 'w') as f :
            #         print('digraph CycleError {', file=f)
            #         for edge in edges :
            #             print('%s -> %s;' % edge, file=f)
            #         print('}', file=f)

            self.assertCollectionEqual(self.edges,
                                       edges,
                                       msg="Test failed for random seed %s" %
                                       seed)
Beispiel #20
0
    def run(self):
        global DEBUG_FLAG
        data = self.preprocess_metafunctions()
        if DEBUG_FLAG:
            self.display_preprocessed_code(data)
        model = PrologString(data)
        if DEBUG_FLAG:
            print '=' * 80
            print "BEFORE ESCAPING"
            print '=' * 80
            for elem in model: print clause2str(elem)
            print '=' * 80

        model = escape_metafunctions(model)

        if DEBUG_FLAG:
            print '=' * 80
            print "AFTER ESCAPING"
            print '=' * 80
            for elem in model: print clause2str(elem)
            print '=' * 80

        engine = DefaultEngine(label_all=True)
        engine.add_builtin(METAMETAFUNCTION_FUNCTOR, 2, BooleanBuiltIn(self.builtin_metafunction))
        engine.add_builtin('declare', 2, BooleanBuiltIn(self.builtin_declare))
        engine.add_builtin('declare', 3, BooleanBuiltIn(self.builtin_declare))

        db = engine.prepare(model)
        if DEBUG_FLAG:
            print "=" * 80
            print "DATABASE"
            print "=" * 80
            for elem in db: print elem
            print "=" * 80

        gp = LogicFormula(
            keep_all=True,
            keep_order=True,
            keep_duplicates=True,
            avoid_name_clash=True
        )

        gp = engine.ground_all(db, target=gp)
        if DEBUG_FLAG:
            print "=" * 80
            print "GROUND PROGRAM (GROUNDER)"
            print "=" * 80
            for elem in gp.enum_clauses():
                print elem
            print "=" * 80
        clauses = []
        facts = []
        for clause in unescape_metafunctions(gp.enum_clauses()):
            if isinstance(clause, Clause):
                clauses.append(clause)
            else:
                facts.append(clause)

        query_atoms = gp._names['query']
        return self.builtin_declare.declarations, clauses + facts, query_atoms
Beispiel #21
0
    def get_full_background_knowledge_clausedb(self, engine=None) -> ClauseDB:
        if self.full_background_knowledge_clausedb is not None:
            return self.full_background_knowledge_clausedb
        else:
            if engine is None:
                engine = DefaultEngine()
                engine.unknown = 1

            full_bg_kw = self.get_full_background_knowledge_simple_program()

            if full_bg_kw is not None:
                self.full_background_knowledge_clausedb = engine.prepare(
                    full_bg_kw)  # ClauseDB
                return self.full_background_knowledge_clausedb
            else:
                raise Exception(
                    "No sense in making an empty ClauseDB for an empty background knowledge"
                )
Beispiel #22
0
    def test_nonground_query_ad(self):
        """Non-ground call to annotated disjunction"""

        program = """
            0.1::p(a); 0.2::p(b).
            query(p(_)).
        """

        engine = DefaultEngine()
        db = engine.prepare(PrologString(program))

        result = None
        for query in engine.query(db, Term("query", None)):
            result = engine.ground(db, query[0], result, label="query")

        found = [str(x) for x, y in result.queries()]

        self.assertCollectionEqual(found, ["p(a)", "p(b)"])
Beispiel #23
0
    def test_anonymous_variable(self) :
        """Anonymous variables are distinct"""

        program = """
            p(_,X,_) :- X = 3.

            q(1,2,3).
            q(1,2,4).
            q(2,3,5).
            r(Y) :- q(_,Y,_).

        """

        engine = DefaultEngine()
        db = engine.prepare( PrologString(program) )
        self.assertEqual( list(map(list,engine.query(db, Term('p', Constant(1), Constant(3), Constant(2) )))), [[Constant(1),Constant(3),Constant(2)]])

        self.assertEqual(list(map(list,engine.query(db, Term('r', None )))), [[2], [3]])
Beispiel #24
0
    def test_functors(self) :
        """Calls with functors"""

        program = """
            p(_,f(A,B),C) :- A=y, B=g(C).
            a(X,Y,Z) :- p(X,f(Y,Z),c).
        """
        pl = PrologString(program)

        r1 = DefaultEngine().query(pl, Term('a',Term('x'),None,Term('g',Term('c'))))
        r1 = [ list(map(str,sol)) for sol in r1  ]
        self.assertCollectionEqual( r1, [['x', 'y', 'g(c)']])

        r2 = DefaultEngine().query(pl, Term('a',Term('x'),None,Term('h',Term('c'))))
        self.assertCollectionEqual( r2, [])

        r3 = DefaultEngine().query(pl, Term('a',Term('x'),None,Term('g',Term('z'))))
        self.assertCollectionEqual( r3, [])
Beispiel #25
0
def estimate( filename, N=1 ) :
    from collections import defaultdict
    pl = PrologFile(filename)
    
    engine = DefaultEngine()
    db = engine.prepare(pl)
    
    estimates = defaultdict(float)
    counts = 0.0
    for i in range(0, N) :
        result = engine.ground_all(db, target=SampledFormula())
        for k, v in result.queries() :
            if v == 0 :
                estimates[k] += 1.0
        counts += 1.0

    for k in estimates :
        estimates[k] = estimates[k] / counts
    return estimates
Beispiel #26
0
def get_labels_single_example_probabilistic_models(
        example: SimpleProgram,
        rules: SimpleProgram,
        possible_labels: Iterable[str],
        background_knowledge=None,
        debug_printing=False) -> List[str]:
    """
    Classifies a single example and returns a list of its labels
    :param example:
    :param rules:
    :param possible_labels:
    :return:
    """
    eng = DefaultEngine()
    eng.unknown = 1

    if background_knowledge is not None:
        db = eng.prepare(background_knowledge)
        for statement in example:
            db += statement
        for rule in rules:
            db += rule
    else:
        db = eng.prepare(rules)
        for statement in example:
            db += statement

    if debug_printing:
        print('\nQueried database:')
        for statement in db:
            print('\t' + str(statement))
            # print('\n')

    query_terms = [Term('query')(label) for label in possible_labels]

    db_to_query = db.extend()
    for query_term in query_terms:
        db_to_query += query_term

    query_results = problog.get_evaluatable().create_from(
        db_to_query, engine=eng).evaluate()

    return query_results
Beispiel #27
0
 def __init__(self,
              predicate_to_query,
              index_of_label_var,
              background_knowledge=None,
              debug_printing=False,
              engine: GenericEngine = None):
     if engine is None:
         self.engine = DefaultEngine()
         self.engine.unknown = 1
     else:
         self.engine = engine
     if background_knowledge is not None:
         self.db = self.engine.prepare(background_knowledge)
     else:
         self.db = None
     self.predicate_to_query = predicate_to_query
     self.labels = set()  # type: Set[Label]
     self.index_of_label_var = index_of_label_var
     self.debug_printing = debug_printing
Beispiel #28
0
    def _run_sl_operators_on_semiring(self, givensemiring, program=None):
        engine = DefaultEngine()
        if program == None:
            program = self._slproblog_program

        db = engine.prepare(PrologString(program))
        semiring = givensemiring
        knowledge = get_evaluatable(None, semiring=semiring)
        formula = knowledge.create_from(db, engine=engine, database=db)
        res = formula.evaluate(semiring=semiring)

        ret = {}
        for k, v in res.items():
            if isinstance(semiring, BetaSemiring):
                ret[k] = moment_matching(semiring.parse(v))
            else:
                ret[k] = semiring.parse(v)

        return self._order_dicts(ret)
Beispiel #29
0
def get_example_databases(
        simple_program_examples: Iterable[SimpleProgramExampleWrapper],
        background_knowledge: Optional[LogicProgram] = None,
        models=False,
        engine=None) -> List[ClauseDBExampleWrapper]:
    if engine is None:
        engine = DefaultEngine()
        engine.unknown = 1

    clausedb_examples = []  # type: List[ClauseDBExampleWrapper]

    if background_knowledge is not None:
        db = engine.prepare(background_knowledge)  # type: ClauseDB
        for example in simple_program_examples:
            db_example = db.extend()  # type: ClauseDB
            for statement in example:
                db_example += statement

            example_wrapper = ClauseDBExampleWrapper(logic_program=db_example)
            clausedb_examples.append(example_wrapper)

            if example.classification_term is not None:
                example_wrapper.classification_term = example.classification_term
            if example.key is not None:
                example_wrapper.key = example.key
            if models:
                example_wrapper.label = example.label
    else:
        for example in simple_program_examples:
            db_example = engine.prepare(
                example.logic_program)  # type: ClauseDB

            example_wrapper = ClauseDBExampleWrapper(logic_program=db_example)
            clausedb_examples.append(example_wrapper)

            if example.classification_term is not None:
                example_wrapper.classification_term = example.classification_term
            if example.key is not None:
                example_wrapper.key = example.key
            if models:
                example_wrapper.label = example.label
    return clausedb_examples
Beispiel #30
0
 def __init__(self, *sources):
     self._database = DefaultEngine().prepare(sources[0])
     self._databaseEn = EnStructure()
     for source in sources[1:]:
         for clause in source:
             self._database += clause
     for source in sources:
         for clause in source:
             predicate = str(clause.functor)
             args = [str(x) for x in clause.args]
             if predicate == 'base':
                 base = str(clause.args[0].functor)
                 types = [str(x) for x in clause.args[0].args]
                 self._databaseEn.add_base(base, types)
             elif predicate not in [
                     'option', 'target', 'mode', 'learn', 'example_mode'
             ]:
                 if clause.probability == None or float(
                         clause.probability) != 0.0:
                     self._databaseEn.add_tuple(predicate, args)
Beispiel #31
0
    def get_default_decision_tree_builder(language,
                                          prediction_goal) -> TreeBuilder:
        engine = DefaultEngine()
        engine.unknown = 1

        test_evaluator = SimpleProgramQueryEvaluator(engine=engine)

        test_generator_builder = ProbLogTestGeneratorBuilder(
            language=language, query_head_if_keys_format=prediction_goal)
        splitter = Splitter(split_criterion_str=split_criterion(),
                            test_evaluator=test_evaluator,
                            test_generator_builder=test_generator_builder)
        # splitter = ProblogSplitter(language=language,split_criterion_str='entropy', test_evaluator=test_evaluator,
        #                            query_head_if_keys_format=prediction_goal)
        leaf_builder = LeafBuilder()
        stop_criterion = StopCriterion()
        tree_builder = TreeBuilder(splitter=splitter,
                                   leaf_builder=leaf_builder,
                                   stop_criterion=stop_criterion)
        return tree_builder
Beispiel #32
0
 def __init__(self, program):
     self._engine = DefaultEngine()
     self._db = self._engine.prepare(PrologString(program))
     self._gp = None
     self._knowledge = None
Beispiel #33
0
class Engine(object):
    """
    Adapter class to Problog grounding and query engine.

    :param program: a valid MDP-ProbLog program
    :type program: str
    """

    def __init__(self, program):
        self._engine = DefaultEngine()
        self._db = self._engine.prepare(PrologString(program))
        self._gp = None
        self._knowledge = None

    def declarations(self, declaration_type):
        """
        Return a list of all terms of type `declaration_type`.

        :param declaration_type: declaration type.
        :type declaration_type: str
        :rtype: list of problog.logic.Term
        """
        return [t[0] for t in self._engine.query(self._db, Term(declaration_type, None))]

    def assignments(self, assignment_type):
        """
        Return a dictionary of assignments of type `assignment_type`.

        :param assignment_type: assignment type.
        :type assignment_type: str
        :rtype: dict of (problog.logic.Term, problog.logic.Constant) items.
        """
        return dict(self._engine.query(self._db, Term(assignment_type, None, None)))

    def get_instructions_table(self):
        """
        Return the table of instructions separated by instruction type
        as described in problog.engine.ClauseDB.

        :rtype: dict of (str, list of (node,namedtuple))
        """
        instructions = {}
        for node, instruction in enumerate(self._db._ClauseDB__nodes):
            instruction_type = str(instruction)
            instruction_type = instruction_type[:instruction_type.find('(')]
            if instruction_type not in instructions:
                instructions[instruction_type] = []
            assert(self._db.get_node(node) == instruction)  # sanity check
            instructions[instruction_type].append((node, instruction))
        return instructions

    def add_fact(self, term, probability=None):
        """
        Add a new `term` with a given `probability` to the program database.
        Return the corresponding node number.

        :param term: a predicate
        :type term: problog.logic.Term
        :param probability: a number in [0,1]
        :type probability: float
        :rtype: int
        """
        return self._db.add_fact(term.with_probability(Constant(probability)))

    def get_fact(self, node):
        """
        Return the fact in the table of instructions corresponding to `node`.

        :param node: identifier of fact in table of instructions
        :type node: int
        :rtype: problog.engine.fact
        """
        fact = self._db.get_node(node)
        if not str(fact).startswith('fact'):
            raise IndexError('Node `%d` is not a fact.' % node)
        return fact

    def add_rule(self, head, body):
        """
        Add a new rule defined by a `head` and `body` arguments
        to the program database. Return the corresponding node number.

        :param head: a predicate
        :type head: problog.logic.Term
        :param body: a list of literals
        :type body: list of problog.logic.Term or problog.logic.Not
        :rtype: int
        """
        b = body[0]
        for term in body[1:]:
            b = b & term
        rule = head << b
        return self._db.add_clause(rule)

    def get_rule(self, node):
        """
        Return the rule in the table of instructions corresponding to `node`.

        :param node: identifier of rule in table of instructions
        :type node: int
        :rtype: problog.engine.clause
        """
        rule = self._db.get_node(node)
        if not str(rule).startswith('clause'):
            raise IndexError('Node `%d` is not a rule.' % node)
        return rule

    def add_assignment(self, term, value):
        """
        Add a new utility assignment of `value` to `term` in the program database.
        Return the corresponding node number.

        :param term: a predicate
        :type term: problog.logic.Term
        :param value: a numeric value
        :type value: float
        :rtype: int
        """
        args = (term.with_probability(None), Constant(1.0 * value))
        utility = Term('utility', *args)
        return self._db.add_fact(utility)

    def get_assignment(self, node):
        """
        Return the assignment in the table of instructions corresponding to `node`.

        :param node: identifier of assignment in table of instructions
        :type node: int
        :rtype: pair of (problog.logic.Term, problog.logic.Constant)
        """
        fact = self._db.get_node(node)
        if not (str(fact).startswith('fact') and fact.functor == 'utility'):
            raise IndexError('Node `%d` is not an assignment.' % node)
        return (fact.args[0], fact.args[1])

    def add_annotated_disjunction(self, facts, probabilities):
        """
        Add a new annotated disjunction to the program database from
        a list of `facts` and its `probabilities`.
        Return a list of choice nodes.

        :param facts: list of probabilistic facts
        :type  facts: list of problog.logic.Term
        :param probabilities: list of valid individual probabilities
                              such that the total probability is less
                              than or equal to 1.0
        :type probabilities: list of float in [0.0, 1.0]
        :rtype: list of int
        """
        disjunction = [ f.with_probability(Constant(p)) for f, p in zip(facts, probabilities) ]
        self._db += AnnotatedDisjunction(heads=disjunction, body=Constant('true'))

        choices = []
        for node, term in enumerate(self._db._ClauseDB__nodes):
            if str(term).startswith('choice'):
                choices.append((term, node))

        nodes = []
        for term in disjunction:
            term = term.with_probability(None)
            for choice, node in choices:
                if term in choice.functor.args:
                    nodes.append(node)
        return nodes

    def get_annotated_disjunction(self, nodes):
        """
        Return the list of choice nodes in the table of instructions
        corresponding to `nodes`.

        :param nodes: list of node identifiers
        :type nodes: list of int
        :rtype: list of problog.engine.choice
        """
        choices = [ self._db.get_node(node) for node in nodes ]
        for choice in choices:
            if not str(choice).startswith('choice'):
                raise IndexError('Node `%d` is not a choice node.' % choice)
        return choices

    def relevant_ground(self, queries):
        """
        Create ground program with respect to `queries`.

        :param queries: list of predicates
        :type queries: list of problog.logic.Term
        """
        self._gp = self._engine.ground_all(self._db, queries=queries)

    def compile(self, terms=[]):
        """
        Create compiled knowledge database from ground program.
        Return mapping of `terms` to nodes in the compiled knowledge database.

        :param terms: list of predicates
        :type terms: list of problog.logic.Term
        :rtype: dict of (problog.logic.Term, int)
        """
        self._knowledge = get_evaluatable(None).create_from(self._gp)
        term2node = {}
        for term in terms:
            term2node[term] = self._knowledge.get_node_by_name(term)
        return term2node

    def evaluate(self, queries, evidence):
        """
        Compute probabilities of `queries` given `evidence`.

        :param queries: mapping of predicates to nodes
        :type queries: dict of (problog.logic.Term, int)
        :param evidence: mapping of predicate and evidence weight
        :type evidence: dictionary of (problog.logic.Term, {0, 1})
        :rtype: list of (problog.logic.Term, [0.0, 1.0])
        """
        evaluator = self._knowledge.get_evaluator(semiring=None, evidence=None, weights=evidence)
        return [ (query, evaluator.evaluate(queries[query])) for query in sorted(queries, key=str) ]