예제 #1
0
def test_synthesize_for_model(debug=False):
    all_models1 = [{'x': False},
                   {'x': True}]
    all_models2 = [{'p': False, 'q': False},
                   {'p': False, 'q': True},
                   {'p': True,  'q': False},
                   {'p': True,  'q': True}]
    all_models3 = [{'r1': False, 'r12': False, 'p37': False},
                   {'r1': False, 'r12': False, 'p37': True},
                   {'r1': False, 'r12': True, 'p37': False},
                   {'r1': False, 'r12': True, 'p37': True},
                   {'r1': True, 'r12': False, 'p37': False},
                   {'r1': True, 'r12': False, 'p37': True},
                   {'r1': True, 'r12': True, 'p37': False},
                   {'r1': True, 'r12': True, 'p37': True}]

    for all_models in [all_models1, all_models2, all_models3]:
        for idx in range(len(all_models)):
            if debug:
                print('Testing synthesis of formula for model', all_models[idx])
            f = synthesize_for_model(frozendict(all_models[idx]))
            assert type(f) is Formula, 'Expected a formula, got ' + str(f)
            assert is_clause(f), str(f) + ' should be a clause'
            all_values = [False] * len(all_models)
            all_values[idx] = True
            for model,value in zip(all_models, all_values):
                assert evaluate(f, frozendict(model)) == value
예제 #2
0
def test_evaluate_inference(debug=False):
    from propositions.proofs import InferenceRule

    # Test 1
    rule1 = InferenceRule([Formula.parse('p'), Formula.parse('q')],
                          Formula.parse('r'))
    for model in all_models(['p', 'q', 'r']):
        if debug:
            print('Testing evaluation of inference rule', rule1, 'in model',
                  model)
        assert evaluate_inference(rule1, frozendict(model)) == \
               (not model['p']) or (not model['q']) or model['r']

    # Test 2
    rule2 = InferenceRule([Formula.parse('(x|y)')],
                          Formula.parse('x'))
    for model in all_models(['x', 'y']):
        if debug:
            print('Testing evaluation of inference rule', rule2, 'in model',
                  model)
        assert evaluate_inference(rule2, frozendict(model)) == \
               (not model['y']) or model['x']

    # Test 3
    rule3 = InferenceRule([Formula.parse(s) for s in ['(p->q)', '(q->r)']],
                           Formula.parse('r'))
    for model in all_models(['p', 'q', 'r']):
        if debug:
            print('Testing evaluation of inference rule', rule3, 'in model',
                  model)
        assert evaluate_inference(rule3, frozendict(model)) == \
               (model['p'] and not model['q']) or \
               (model['q'] and not model['r']) or model['r']
예제 #3
0
def test_merge_specialization_maps(debug=False):
    for d1, d2, d in [
        ({}, {}, {}),
        ({}, None, None),
        (None, {}, None),
        (None, None, None),
        ({'p':'q'}, {'r':'s'}, {'p':'q', 'r':'s'}),
        ({'p':'q'}, {}, {'p':'q'}),
        ({}, {'p':'q'}, {'p':'q'}),
        ({'p':'q'}, {'p':'r'}, None),
        ({'p':'q'}, None, None),
        (None, {'p':'q'}, None),
        ({'x':'p1', 'y':'p2'}, {'x':'p1', 'z':'p3'},
         {'x':'p1', 'y':'p2', 'z':'p3'}),
        ({'x':'p1', 'y':'p2'}, {'x':'p1', 'y':'p3'}, None),
        ({'x':'p1', 'y':'p2'}, {'x':'p1', 'y':'p2', 'z':'p3'},
         {'x':'p1', 'y':'p2', 'z':'p3'}),
        ({'x':'p1', 'y':'p2', 'z':'p3'}, {'x':'p1', 'y':'p2'},
         {'x':'p1', 'y':'p2', 'z':'p3'})]:
        if debug:
            print('Testing merging of dictionaries', d1, d2)
        dd = InferenceRule.merge_specialization_maps(
            frozendict({v: Formula.parse(d1[v]) for v in d1}) if d1 is not None
            else None,
            frozendict({v: Formula.parse(d2[v]) for v in d2}) if d2 is not None
            else None)
        assert dd == ({v: Formula.parse(d[v]) for v in d}
                      if d is not None else None), "got " + dd
예제 #4
0
def __test_synthesize_clause(clause_synthesizer, for_model, debug):
    all_models1 = [{'x': False},
                   {'x': True}]
    all_models2 = [{'p': False, 'q': False},
                   {'p': False, 'q': True},
                   {'p': True, 'q': False},
                   {'p': True, 'q': True}]
    all_models3 = [{'r1': False, 'r12': False, 'p37': False},
                   {'r1': False, 'r12': False, 'p37': True},
                   {'r1': False, 'r12': True, 'p37': False},
                   {'r1': False, 'r12': True, 'p37': True},
                   {'r1': True, 'r12': False, 'p37': False},
                   {'r1': True, 'r12': False, 'p37': True},
                   {'r1': True, 'r12': True, 'p37': False},
                   {'r1': True, 'r12': True, 'p37': True}]

    for all_models in [all_models1, all_models2, all_models3]:
        for idx in range(len(all_models)):
            if debug:
                print('Testing', clause_synthesizer.__qualname__, 'for model',
                      all_models[idx])
            f = clause_synthesizer(frozendict(all_models[idx]))
            assert type(f) is Formula, 'Expected a formula, got ' + str(f)
            if for_model:
                assert is_conjunctive_clause(f), \
                    str(f) + ' should be a conjunctive clause'
                all_values = [False] * len(all_models)
                all_values[idx] = True
            else:
                assert is_disjunctive_clause(f), \
                    str(f) + ' should be a disjunctive clause'
                all_values = [True] * len(all_models)
                all_values[idx] = False
            for model, value in zip(all_models, all_values):
                assert evaluate(f, frozendict(model)) == value
예제 #5
0
    def evaluate_formula(
        self, formula: Formula, assignment: Mapping[str,
                                                    T] = frozendict()) -> bool:
        """Calculates the truth value of the given formula in the current model,
        for the given assignment of values to free occurrences of variables
        names.

        Parameters:
            formula: formula to calculate the truth value of, for the constants,
                functions, and relations of which the current model has
                meanings.
            assignment: mapping from each variable name that has a free
                occurrence in the given formula to a universe element to which
                it is to be evaluated.

        Returns:
            The truth value of the given formula in the current model, for the
            given assignment of values to free occurrences of variable names.
        """
        assert formula.constants().issubset(self.constant_meanings.keys())
        assert formula.free_variables().issubset(assignment.keys())
        for function, arity in formula.functions():
            assert function in self.function_meanings and \
                   self.function_arities[function] == arity
        for relation, arity in formula.relations():
            assert relation in self.relation_meanings and \
                   self.relation_arities[relation] in {-1, arity}
        # Task 7.8
        return self.evaluate_helper(assignment, formula)
예제 #6
0
    def evaluate_term(
        self, term: Term, assignment: Mapping[str, T] = frozendict()) -> T:
        """Calculates the value of the given term in the current model, for the
        given assignment of values to variables names.

        Parameters:
            term: term to calculate the value of, for the constants and
                functions of which the current model has meanings.
            assignment: mapping from each variable name in the given term to a
                universe element to which it is to be evaluated.

        Returns:
            The value (in the universe of the current model) of the given
            term in the current model, for the given assignment of values to
            variable names.
        """
        assert term.constants().issubset(self.constant_meanings.keys())
        assert term.variables().issubset(assignment.keys())
        for function, arity in term.functions():
            assert function in self.function_meanings and \
                   self.function_arities[function] == arity
        # Decide based on the term type
        term_type = term.get_type()
        if term_type == Term.TermType.T_CONST:
            return self.constant_meanings[term.root]
        elif term_type == Term.TermType.T_VARIABLE:
            return assignment[term.root]
        elif term_type == Term.TermType.T_FUNCTION:
            # Evaluate the arguments
            args = [self.evaluate_term(t, assignment) for t in term.arguments]
            return self.function_meanings[term.root][tuple(args)]
예제 #7
0
def test_evaluate_all_operators(debug=False):
    infix1 = '(p+q7)'
    models_values1 = [
        ({'p': True,  'q7': False}, True),
        ({'p': False, 'q7': False}, False),
        ({'p': True,  'q7': True},  False)
    ]
    infix2 = '~(p<->q7)'
    models_values2 = [
        ({'p': True,  'q7': False}, True),
        ({'p': False, 'q7': False}, False),
        ({'p': True,  'q7': True},  False)
    ]
    infix3 = '~((x-&x)-|(y-&y))'
    models_values3 = [
        ({'x': True,  'y': False}, True),
        ({'x': False,  'y': False}, True),
        ({'x': True,  'y': True}, False)
    ]
    for infix,models_values in [[infix1, models_values1],
                                [infix2, models_values2],
                                [infix3, models_values3]]:
        formula = Formula.parse(infix)
        for model,value in models_values:
            if debug:
                print('Testing evaluation of formula', formula, 'in model',
                      model)
            assert evaluate(formula, frozendict(model)) == value
예제 #8
0
def test_reduce_assumption(debug=False):
    for f, m, v in [ ('(y->x)', {'x':True}, 'y'),
                     ('(p->p)', {}, 'p'),
                     ('(p->(r->q))', {'p':True, 'q':True}, 'r')]:
        f = Formula.parse(f)
        m[v]=True
        pt = prove_in_model(f, frozendict(m))
        m[v]=False
        pf = prove_in_model(f, frozendict(m))
        if debug:
            print("testing reduce assumption on", pt.statement, "and",
                  pf.statement)
        p = reduce_assumption(pt,pf)
        assert p.statement.conclusion  == pf.statement.conclusion
        assert p.statement.assumptions[:] == pt.statement.assumptions[:-1]
        assert p.rules == AXIOMATIC_SYSTEM
        assert p.is_valid(), offending_line(p)
예제 #9
0
def test_formulas_capturing_model(debug=False):
    for q,a in [({'p':True},['p']),
                ({'q7':False}, ['~q7']),
                ({'x1':True, 'x2':False, 'x3':True}, ['x1', '~x2', 'x3']),
                ({'q3':False, 'p13':False, 'r':True}, ['~p13', '~q3', 'r'])]:
        if debug:
            print("Testing formulas_capturing_model on", q)
        aa = [Formula.parse(f) for f in a]
        assert formulas_capturing_model(frozendict(q)) == aa
예제 #10
0
def test_prove_in_model_full(debug=False):
    for (f, m, a, cp) in [ ('x', {'x':True}, ['x'], ''),
                           ('x',{'x':False}, ['~x'], '~'),
                           ('~x', {'x':False}, ['~x'], ''),
                           ('~x', {'x':True}, ['x'], '~'),
                           ('x', {'x':True, 'z5':False}, ['x', '~z5'], ''),
                           ('(p->~p)', {'p':True}, ['p'], '~'),
                           ('(p->~p)', {'p':False}, ['~p'], ''),
                           ('(p->q)', {'p':True, 'q':True}, ['p','q'], ''),
                           ('(p->q)', {'p':True, 'q':False}, ['p','~q'], '~'),
                           ('(p->q)', {'p':False, 'q':True}, ['~p','q'], ''),
                           ('(p->q)', {'p':False, 'q':False}, ['~p', '~q'], ''),
                           ('~~~~y7', {'y7':True}, ['y7'], ''),
                           ('~~~~y7', {'y7':False}, ['~y7'], '~'),
                           ('~(~p->~q)', {'p':True, 'q':True}, ['p','q'], '~'),
                           ('~(~p->~q)', {'p':False, 'q':True}, ['~p','q'], ''),
                           ('((p1->p2)->(p3->p4))',
                            {'p1':True, 'p2':True, 'p3':True, 'p4':True},
                            ['p1','p2','p3','p4'], ''),
                           ('((p1->p2)->(p3->p4))',
                            {'p1':True, 'p2':True, 'p3':True, 'p4':False},
                            ['p1','p2','p3','~p4'], '~'),
                           ('((p1->p2)->(p3->p4))',
                            {'p1':True, 'p2':False, 'p3':True, 'p4':False},
                            ['p1','~p2','p3','~p4'], ''),
                           ('(~(~x->~~y)->(z->(~x->~~~z)))',
                            {'z':True, 'x':False, 'y':False},
                            ['~x','~y','z'], '~'),
                           ('T', {}, [], ''),
                           ('F', {}, [], '~'),
                           ('(p|q)', {'p': True, 'q': True}, ['p', 'q'], ''),
                           ('(p|q)', {'p': True, 'q': False}, ['p', '~q'], ''),
                           ('(p|q)', {'p': False, 'q': True}, ['~p', 'q'], ''),
                           ('(p|q)',
                            {'p': False, 'q': False}, ['~p', '~q'], '~'),
                           ('(p&q)', {'p': True, 'q': True}, ['p', 'q'], ''),
                           ('(p&q)', {'p': True, 'q': False}, ['p', '~q'], '~'),
                           ('(p&q)', {'p': False, 'q': True}, ['~p', 'q'], '~'),
                           ('(p&q)',
                            {'p': False, 'q': False}, ['~p', '~q'], '~'),
                           ('~(~(q|p)&(r->~(s|q)))',
                            {'p': False, 'q': False, 'r': False, 's': False},
                            ['~p', '~q', '~r', '~s'], '~'),
                           ('~(~(q|p)&(r->~(s|q)))',
                            {'p': False, 'q': False, 'r': True, 's': True},
                            ['~p', '~q', 'r', 's'], '')
                           ]:
        c = Formula.parse(cp+f)
        f = Formula.parse(f)
        a = (Formula.parse(v) for v in a)
        if debug:
            print('Testing prove_in_model_full on formula',f, 'in model', m)
        p = prove_in_model_full(f, frozendict(m))
        assert p.statement == InferenceRule(a, c)
        assert p.rules == AXIOMATIC_SYSTEM_FULL
        assert p.is_valid(), offending_line(p)
예제 #11
0
def prove_tautology(tautology: Formula, model: Model = frozendict()) -> Proof:
    """Proves the given tautology from the formulas that capture the given
    model.

    Parameters:
        tautology: tautology that contains no constants or operators beyond
            ``'->'`` and ``'~'``, to prove.
        model: model over a (possibly empty) prefix (with respect to the
            alphabetical order) of the variables of `tautology`, from whose
            formulas to prove.

    Returns:
        A valid proof of the given tautology from the formulas that capture the
        given model, in the order returned by
        `formulas_capturing_model`\ ``(``\ `model`\ ``)``, via
        `~propositions.axiomatic_systems.AXIOMATIC_SYSTEM`.

    Examples:
        >>> proof = prove_tautology(Formula.parse('(~(p->p)->q)'),
        ...                         {'p': True, 'q': False})
        >>> proof.is_valid()
        True
        >>> proof.statement.conclusion
        (~(p->p)->q)
        >>> proof.statement.assumptions
        (p, ~q)
        >>> proof.rules == AXIOMATIC_SYSTEM
        True

        >>> proof = prove_tautology(Formula.parse('(~(p->p)->q)'))
        >>> proof.is_valid()
        True
        >>> proof.statement.conclusion
        (~(p->p)->q)
        >>> proof.statement.assumptions
        ()
        >>> proof.rules == AXIOMATIC_SYSTEM
        True
    """
    assert is_tautology(tautology)
    assert tautology.operators().issubset({'->', '~'})
    assert is_model(model)
    assert sorted(tautology.variables())[:len(model)] == sorted(model.keys())
    if len(model) == len(tautology.variables()):
        return prove_in_model(tautology, model)
    variables_not_in_model = sorted(
        [v for v in tautology.variables() if v not in model])
    check_on_var = variables_not_in_model[0]
    variables_not_in_model.remove(check_on_var)
    new_model = {check_on_var: True}
    new_model.update(model)
    aff_proof = prove_tautology(tautology, new_model)
    new_model[check_on_var] = False
    neg_proof = prove_tautology(tautology, new_model)
    return reduce_assumption(aff_proof, neg_proof)
예제 #12
0
def __test_synthesize(synthesizer, dnf, debug):
    all_variables1 = ['p']
    all_models1 = [{'p': False}, {'p': True}]
    value_lists1 = [(False, False), (False, True), (True, False), (True, True)]

    all_variables2 = ['p', 'q']
    all_models2 = [{'p': False, 'q': False},
                   {'p': False, 'q': True},
                   {'p': True, 'q': False},
                   {'p': True, 'q': True}]
    value_lists2 = [(True, False, False, True),
                    (True, True, True, True),
                    (False, False, False, False)]

    all_variables3 = ['r1', 'r12', 'p37']
    all_models3 = [{'r1': False, 'r12': False, 'p37': False},
                   {'r1': False, 'r12': False, 'p37': True},
                   {'r1': False, 'r12': True, 'p37': False},
                   {'r1': False, 'r12': True, 'p37': True},
                   {'r1': True, 'r12': False, 'p37': False},
                   {'r1': True, 'r12': False, 'p37': True},
                   {'r1': True, 'r12': True, 'p37': False},
                   {'r1': True, 'r12': True, 'p37': True}]
    value_lists3 = [(True, False, True, True, False, True, False, True),
                    (True, True, True, True, True, True, True, True),
                    (False, False, False, False, False, False, False, False)]

    for all_variables, all_models, value_lists in [
        [all_variables1, all_models1, value_lists1],
        [all_variables2, all_models2, value_lists2],
        [all_variables3, all_models3, value_lists3]]:
        for all_values in value_lists:
            if debug:
                print('Testing', synthesizer.__qualname__, 'for variables',
                      all_variables, 'and model-values', all_values)
            formula = synthesizer(tuple(all_variables), all_values)
            assert type(formula) is Formula, \
                'Expected a formula, got ' + str(formula)
            if dnf:
                assert is_dnf(formula), str(formula) + ' should be a DNF'
            else:
                assert is_cnf(formula), str(formula) + ' should be a CNF'
            assert formula.variables().issubset(set(all_variables))
            for model, value in zip(all_models, all_values):
                assert evaluate(formula, frozendict(model)) == value, \
                    str(formula) + ' does not evaluate to ' + str(value) + \
                    ' on ' + str(model)
예제 #13
0
def prove_tautology(tautology: Formula, model: Model = frozendict()) -> Proof:
    """Proves the given tautology from the formulae that capture the given
    model.

    Parameters:
        tautology: tautology that contains no constants or operators beyond
            ``'->'`` and ``'~'``, to prove.
        model: model over a (possibly empty) prefix (with respect to the
            alphabetical order) of the variables of `tautology`, from whose
            formulae to prove.

    Returns:
        A valid proof of the given tautology from the formulae that capture the
        given model, in the order returned by
        `formulae_capturing_model`\ ``(``\ `model`\ ``)``, via
        `~propositions.axiomatic_systems.AXIOMATIC_SYSTEM`.

    Examples:
        If the given model is the empty dictionary, then the returned proof is
        of the given tautology from no assumptions.
    """
    assert is_tautology(tautology)
    assert tautology.operators().issubset({'->', '~'})
    assert is_model(model)
    assert sorted(tautology.variables())[:len(model)] == sorted(model.keys())
    # Task 6.3a

    statement = InferenceRule(formulae_capturing_model(model), tautology)

    # rules are AXIOMATIC_SYSTEM
    list_of_var_in_formula = list(tautology.variables())
    list_of_var_in_formula.sort()
    for var in list_of_var_in_formula:
        if var not in model:
            new_model = dict(model)
            new_model[var] = True
            # proof 1 is with that var with value True
            proof1 = prove_tautology(tautology, new_model)
            new_model[var] = False
            # proof 2 is with that var with value False
            proof2 = prove_tautology(tautology, new_model)
            # proof without that var
            return reduce_assumption(proof1, proof2)

    # if the model contains all the var in the tautology formula
    return prove_in_model(tautology, model)
예제 #14
0
def prove_tautology(tautology: Formula, model: Model = frozendict()) -> Proof:
    """Proves the given tautology from the formulae that capture the given
    model.

    Parameters:
        tautology: tautology that contains no constants or operators beyond
            ``'->'`` and ``'~'``, to prove.
        model: model over a (possibly empty) prefix (with respect to the
            alphabetical order) of the variables of `tautology`, from whose
            formulae to prove.

    Returns:
        A valid proof of the given tautology from the formulae that capture the
        given model, in the order returned by
        `formulae_capturing_model`\ ``(``\ `model`\ ``)``, via
        `~propositions.axiomatic_systems.AXIOMATIC_SYSTEM`.

    Examples:
        If the given model is the empty dictionary, then the returned proof is
        of the given tautology from no assumptions.
    """
    assert is_tautology(tautology)
    assert tautology.operators().issubset({'->', '~'})
    assert is_model(model)
    assert sorted(tautology.variables())[:len(model)] == sorted(model.keys())
    # Task 6.3a
    if model is not None and len(tautology.variables()) == len(model):
        return prove_in_model(tautology, model)
    else:
        variables = sorted(tautology.variables())
        new_model_1 = dict()
        if model is not None:
            for var in model.keys():
                new_model_1[var] = model[var]
        new_model_2 = deepcopy(new_model_1)
        for var in variables:
            if var not in model.keys():
                new_model_1[var] = True
                new_model_2[var] = False
                antecedent_proof_1 = prove_tautology(tautology, new_model_1)
                antecedent_proof_2 = prove_tautology(tautology, new_model_2)
                return reduce_assumption(antecedent_proof_1, antecedent_proof_2)
예제 #15
0
def test_specialize(debug=False):
    for t in substitutions:
        d = t[0]
        if debug:
            print('Testing substitition dictionary', d)
        d = frozendict({k: Formula.parse(d[k]) for k in d})
        cases = [ [Formula.parse(c[0]), Formula.parse(c[1])] for c in t[1:]]
        for case in cases:
            if debug:
                print('...checking that', case[0], 'specializes to', case[1])
            general = InferenceRule([],case[0])
            special = InferenceRule([],case[1])
            assert general.specialize(d) == special, \
                   "got " + str(general.specialize(d).conclusion)
        if debug:
            print('...now checking all together in a single rule')
            general = InferenceRule([case[0] for case in cases[1:]],cases[0][0])
            special = InferenceRule([case[1] for case in cases[1:]],cases[0][1])
            assert general.specialize(d) == special, \
                   "got " + str(general.specialize(d))      
예제 #16
0
def test_substitute_variables(debug=False):
    #           f         d              result
    tests = [('v', {}, 'v'),
             ('v', {'v': 'p'}, 'p'),
             ('(F->v12)', {'v12': 'v11'}, '(F->v11)'),
             ('v', {'q': 'r', 'z': 'w'}, 'v'),
             ('p', {'p': '(q|q)'}, '(q|q)'),
             ('~v', {'v': '(q|q)'}, '~(q|q)'),
             ('(~v|v)', {'v': '(q|q)'}, '(~(q|q)|(q|q))'),
             ('(q12->w)', {'q12': 'T', 'w': 'x'}, '(T->x)'),
             ('(v->w)', {'v': 'T', 'w': 'v'}, '(T->v)'),
             ('((~v&w)|(v->u))', {'v': '(~p->q)', 'u': '~~F'}, '((~(~p->q)&w)|((~p->q)->~~F))'),
             ('v2', {'v': 'p'}, 'v2')]
    for f, d, r in tests:
        if debug:
            print("Testing substituting variables according to", d, "in formula", f)
        f = Formula.parse(f)
        d = {k: Formula.parse(d[k]) for k in d}
        a = str(f.substitute_variables(frozendict(d)))
        assert a == r, "Incorrect answer:" + a
예제 #17
0
def prove_tautology(tautology: Formula, model: Model = frozendict()) -> Proof:
    """Proves the given tautology from the formulae that capture the given
    model.

    Parameters:
        tautology: tautology that contains no constants or operators beyond
            ``'->'`` and ``'~'``, to prove.
        model: model over a (possibly empty) prefix (with respect to the
            alphabetical order) of the variables of `tautology`, from whose
            formulae to prove.

    Returns:
        A valid proof of the given tautology from the formulae that capture the
        given model, in the order returned by
        `formulae_capturing_model`\ ``(``\ `model`\ ``)``, via
        `~propositions.axiomatic_systems.AXIOMATIC_SYSTEM`.

    Examples:
        If the given model is the empty dictionary, then the returned proof is
        of the given tautology from no assumptions.
    """
    assert is_tautology(tautology)
    assert tautology.operators().issubset({'->', '~'})
    assert is_model(model)
    assert sorted(tautology.variables())[:len(model)] == sorted(model.keys())
    tautology_variables = list(sorted(tautology.variables()))
    if len(model) == len(tautology_variables):
        return prove_in_model(tautology, model)
    else:
        #craete a model with added assumption, assigned to be True, and another model
        #with the same added assumption assigned to be False
        model_with_negation =  dict(model)
        model_with_negation[tautology_variables[len(model)]] = False
        model_with_affirmation = dict(model)
        model_with_affirmation[tautology_variables[len(model)]] = True
        proof_from_affirmation = prove_tautology(tautology, model_with_affirmation)
        proof_from_negation = prove_tautology(tautology, model_with_negation)
        return reduce_assumption(proof_from_affirmation, proof_from_negation)