Esempio n. 1
0
    def update_blackboard(self, B):
        """
        Saturates a Blackboard B with additive inferences.
        """
        timer.start(timer.PADD)
        messages.announce_module('polyhedron additive module')

    #    learn_additive_sign_info(blackboard)

        comparisons = get_additive_information(B)

        h_matrix = lrs_util.create_h_format_matrix(comparisons, B.num_terms)
        messages.announce('Halfplane matrix:', messages.DEBUG)
        messages.announce(h_matrix, messages.DEBUG)
        v_matrix, v_lin_set = lrs_util.get_vertices(h_matrix)
        messages.announce('Vertex matrix:', messages.DEBUG)
        #messages.announce(str(v_matrix), messages.DEBUG)
        for l in v_matrix:
            messages.announce(str(l), messages.DEBUG)
        messages.announce('Linear set:', messages.DEBUG)
        messages.announce(str(v_lin_set), messages.DEBUG)

        new_comparisons = get_2d_comparisons(v_matrix, v_lin_set)

        for c in new_comparisons:
            B.assert_comparison(c)

        timer.stop(timer.PADD)
Esempio n. 2
0
    def update_blackboard(self, B):
        """
        Instantiates all stored axioms with respect to B and asserts new clauses.
        """
        timer.start(timer.FUN)
        messages.announce_module('axiom module')
        n = B.num_terms + len(B.equalities.keys()) + len(B.zero_equalities)
        #if n == self.num_eqs:
        #    messages.announce("No new information for the axiom module to use.", messages.DEBUG)
        #    timer.stop(timer.FUN)
        #    return
        self.num_eqs = n

        for a in self.axioms:
            messages.announce("Instantiating axiom: {}".format(a),
                              messages.DEBUG)
            if a.unifiable:
                clauses = instantiate(a, self.used_envs, B)
                for c in clauses:
                    B.assert_clause(*c)
            else:
                clauses = instantiate_triggerless(a, self.used_envs, B)
                for c in clauses:
                    B.assert_clause(*c)
        timer.stop(timer.FUN)
Esempio n. 3
0
 def update_blackboard(self, B):
     """
     Learns equalities and inequalities from additive information in B, and asserts them
     to B.
     """
     timer.start(timer.FMADD)
     messages.announce_module('Fourier-Motzkin additive module')
     eqs, comps = get_additive_information(B)
     for i in range(B.num_terms):
         # at this point, eqs and comps have all comparisons with indices >= i
         i_eqs, i_comps = eqs, comps
         # determine all comparisons with IVar(i) and IVar(j) with j >= i
         for j in range(i + 1, B.num_terms):
             # at this point, i_eqs and i_comps have all comparisons with i and indices >= j
             ij_eqs, ij_comps = i_eqs, i_comps
             # determine all comparisons between IVar(i) and IVar(j)
             for k in range(j + 1, B.num_terms):
                 ij_eqs, ij_comps = elim(ij_eqs, ij_comps, k)
             assert_comparisons_to_blackboard(ij_eqs, ij_comps, B)
             # done with IVar(j)
             i_eqs, i_comps = elim(i_eqs, i_comps, j)
         # add this point, i_eqs and i_comps contain only comparisons with IVar(i) alone
         assert_comparisons_to_blackboard(i_eqs, i_comps, B)
         # done with IVar(i)
         eqs, comps = elim(eqs, comps, i)
     timer.stop(timer.FMADD)
Esempio n. 4
0
 def update_blackboard(self, B):
     """
     Learns sign information and equalities and inequalities from multiplicative information in
     B, and asserts them to B.
     """
     timer.start(timer.FMMUL)
     messages.announce_module('Fourier-Motzkin multiplicative module')
     mul_util.derive_info_from_definitions(B)
     mul_util.preprocess_cancellations(B)
     eqs, comps = get_multiplicative_information(B)
     # t0 = 1; ignore
     for i in range(1, B.num_terms):
         # at this point, eqs and comps have all comparisons with indices >= i
         i_eqs, i_comps = eqs, comps
         # determine all comparisons with IVar(i) and IVar(j) with j >= i
         for j in range(i + 1, B.num_terms):
             # at this point, i_eqs and i_comps have all comparisons with i and indices >= j
             ij_eqs, ij_comps = i_eqs, i_comps
             # determine all comparisons between IVar(i) and IVar(j)
             for k in range(j + 1, B.num_terms):
                 ij_eqs, ij_comps = elim(ij_eqs, ij_comps, k)
             #print 'comps:', ij_comps
             assert_comparisons_to_blackboard(ij_eqs, ij_comps, B)
             # done with IVar(j)
             i_eqs, i_comps = elim(i_eqs, i_comps, j)
         # add this point, i_eqs and i_comps contain only comparisons with IVar(i) alone
         assert_comparisons_to_blackboard(i_eqs, i_comps, B)
         # done with IVar(i)
         eqs, comps = elim(eqs, comps, i)
     timer.stop(timer.FMMUL)
Esempio n. 5
0
    def update_blackboard(self, B):
        """
        Saturates a Blackboard B with additive inferences.
        """
        timer.start(timer.PADD)
        messages.announce_module('polyhedron additive module')

    #    learn_additive_sign_info(blackboard)

        comparisons = get_additive_information(B)

        h_matrix = lrs_util.create_h_format_matrix(comparisons, B.num_terms)
        messages.announce('Halfplane matrix:', messages.DEBUG)
        messages.announce(h_matrix, messages.DEBUG)
        v_matrix, v_lin_set = lrs_util.get_vertices(h_matrix)
        messages.announce('Vertex matrix:', messages.DEBUG)
        #messages.announce(str(v_matrix), messages.DEBUG)
        for l in v_matrix:
            messages.announce(str(l), messages.DEBUG)
        messages.announce('Linear set:', messages.DEBUG)
        messages.announce(str(v_lin_set), messages.DEBUG)

        new_comparisons = get_2d_comparisons(v_matrix, v_lin_set)

        for c in new_comparisons:
            B.assert_comparison(c)

        timer.stop(timer.PADD)
Esempio n. 6
0
    def update_blackboard(self, B):
        """
        Adds axioms corresponding to each nth root function present.
        """
        messages.announce_module('nth root value module')
        timer.start(timer.ROOT)

        #nth_roots = []

        def is_nth_root_term(i):
            """
            Returns True if t_i is of the form abs(t_j).
            """
            return (isinstance(B.term_defs[i], terms.FuncTerm)
                        and isinstance(B.term_defs[i].func, terms.NthRoot))

        def root_degree(i):
            """
            If t_i is of the form NthRoot(n, t_j), returns n.
            """
            return B.term_defs[i].func.n

        nth_roots = set([root_degree(i) for i in range(B.num_terms) if is_nth_root_term(i)])
        x = terms.Var('x')
        for n in nth_roots:
            if n % 2 == 0:
                self.am.add_axiom(formulas.Forall([x], formulas.Implies(x >= 0,
                                                        terms.root(n, x) ** n == x)))
                self.am.add_axiom(formulas.Forall([x], formulas.Implies(x >= 0,
                                                        terms.root(n, x) >= 0)))
            else:
                self.am.add_axiom(formulas.Forall([x], terms.root(n, x) ** n == x))

        timer.stop(timer.ROOT)
Esempio n. 7
0
 def update_blackboard(self, B):
     """
     Learns sign information and equalities and inequalities from multiplicative information in
     B, and asserts them to B.
     """
     timer.start(timer.FMMUL)
     messages.announce_module('Fourier-Motzkin multiplicative module')
     mul_util.derive_info_from_definitions(B)
     mul_util.preprocess_cancellations(B)
     eqs, comps = get_multiplicative_information(B)
     # t0 = 1; ignore
     for i in range(1, B.num_terms):
         # at this point, eqs and comps have all comparisons with indices >= i
         i_eqs, i_comps = eqs, comps
         # determine all comparisons with IVar(i) and IVar(j) with j >= i
         for j in range(i + 1, B.num_terms):
             # at this point, i_eqs and i_comps have all comparisons with i and indices >= j
             ij_eqs, ij_comps = i_eqs, i_comps
             # determine all comparisons between IVar(i) and IVar(j)
             for k in range(j + 1, B.num_terms):
                 ij_eqs, ij_comps = elim(ij_eqs, ij_comps, k)
             #print 'comps:', ij_comps
             assert_comparisons_to_blackboard(ij_eqs, ij_comps, B)
             # done with IVar(j)
             i_eqs, i_comps = elim(i_eqs, i_comps, j)
         # add this point, i_eqs and i_comps contain only comparisons with IVar(i) alone
         assert_comparisons_to_blackboard(i_eqs, i_comps, B)
         # done with IVar(i)
         eqs, comps = elim(eqs, comps, i)
     timer.stop(timer.FMMUL)
Esempio n. 8
0
    def update_blackboard(self, B):
        """
        Saturates a Blackboard B with multiplicative inferences.
        """
        timer.start(timer.PMUL)
        messages.announce_module('polyhedron multiplicative module')
        mul_util.derive_info_from_definitions(B)

        mul_util.preprocess_cancellations(B)

        m_comparisons = mul_util.get_multiplicative_information(B)
        # Each ti in m_comparisons really represents |t_i|.

        p = add_of_mul_comps(m_comparisons, B.num_terms)
        a_comparisons, prime_of_index, num_terms = p
        a_comparisons = [terms.comp_eval[c[1]](c[0], 0) for c in a_comparisons]

        h_matrix = lrs_util.create_h_format_matrix(a_comparisons, num_terms)
        messages.announce('Halfplane matrix:', messages.DEBUG)
        messages.announce(h_matrix, messages.DEBUG)
        v_matrix, v_lin_set = lrs_util.get_vertices(h_matrix)
        messages.announce('Vertex matrix:', messages.DEBUG)
        for l in v_matrix:
            messages.announce(str(l), messages.DEBUG)
        messages.announce('Linear set:', messages.DEBUG)
        messages.announce(str(v_lin_set), messages.DEBUG)

        new_comparisons = get_mul_comparisons(v_matrix, v_lin_set, B.num_terms,
                                              prime_of_index)

        for m1, m2, coeff, comp in new_comparisons:
            c = mul_util.process_mul_comp(m1, m2, coeff, comp, B)
            if c is not None:
                B.assert_comparison(c)
        timer.stop(timer.PMUL)
Esempio n. 9
0
 def update_blackboard(self, B):
     """
     Asserts identities about minm terms
     """
     messages.announce_module('minimum module')
     timer.start(timer.MINM)
     for i in range(B.num_terms):
         if isinstance(
                 B.term_defs[i],
                 terms.FuncTerm) and B.term_defs[i].func_name == 'minm':
             # t_i is of the form minm(...)
             args = B.term_defs[i].args
             # assert that t_i is le all of its arguments
             for a in args:
                 B.assert_comparison(terms.IVar(i) <= a)
             # see if we can infer the sign
             # TODO: optimize
             if all(B.implies_comparison(a > 0) for a in args):
                 B.assert_comparison(terms.IVar(i) > 0)
             elif all(B.implies_comparison(a >= 0) for a in args):
                 B.assert_comparison(terms.IVar(i) >= 0)
             if any(B.implies_comparison(a < 0) for a in args):
                 B.assert_comparison(terms.IVar(i) < 0)
             elif any(B.implies_comparison(a <= 0) for a in args):
                 B.assert_comparison(terms.IVar(i) <= 0)
             # see if any multiple of another problem term is known to be less than all the
             # arguments.
             for j in range(B.num_terms):
                 if j != i:
                     comp_range = geometry.ComparisonRange(
                         geometry.neg_infty, geometry.infty, True, True,
                         True)
                     for a in args:
                         new_comp_range = B.le_coeff_range(
                             j, a.term.index, a.coeff)
                         comp_range = comp_range & new_comp_range
                         if comp_range.is_empty():
                             break
                     if not comp_range.is_empty():
                         if comp_range.lower.type == geometry.VAL:
                             c = comp_range.lower.val
                             if comp_range.lower_strict:
                                 B.assert_comparison(
                                     c * terms.IVar(j) < terms.IVar(i))
                             else:
                                 B.assert_comparison(
                                     c * terms.IVar(j) <= terms.IVar(i))
                         if comp_range.upper.type == geometry.VAL:
                             c = comp_range.upper.val
                             if comp_range.upper_strict:
                                 B.assert_comparison(
                                     c * terms.IVar(j) < terms.IVar(i))
                             else:
                                 B.assert_comparison(
                                     c * terms.IVar(j) <= terms.IVar(i))
     timer.stop(timer.MINM)
Esempio n. 10
0
 def update_blackboard(self, B):
     """
     Asserts identities about exp and log terms found in B.
     """
     timer.start(timer.EXP)
     messages.announce_module('exponential module')
     if any(isinstance(t, terms.FuncTerm) and t.func == terms.exp for t in B.term_defs.values()):
         B.assert_comparison(terms.exp(0) == 1)
     if any(isinstance(t, terms.FuncTerm) and t.func == terms.log for t in B.term_defs.values()):
         B.assert_comparison(terms.log(1) == 0)
     exp_factor_constant(B)
     exp_factor_sum(B)
     log_factor_exponent(B)
     log_factor_product(B)
     timer.stop(timer.EXP)
Esempio n. 11
0
 def update_blackboard(self, B):
     """
     Asserts identities about minm terms
     """
     messages.announce_module('minimum module')
     timer.start(timer.MINM)
     for i in range(B.num_terms):
         if isinstance(B.term_defs[i], terms.FuncTerm) and B.term_defs[i].func_name == 'minm':
             # t_i is of the form minm(...)
             args = B.term_defs[i].args
             # assert that t_i is le all of its arguments
             for a in args:
                 B.assert_comparison(terms.IVar(i) <= a)
             # see if we can infer the sign
             # TODO: optimize
             if all(B.implies_comparison(a > 0) for a in args):
                 B.assert_comparison(terms.IVar(i) > 0)
             elif all(B.implies_comparison(a >= 0) for a in args):
                 B.assert_comparison(terms.IVar(i) >= 0)
             if any(B.implies_comparison(a < 0) for a in args):
                 B.assert_comparison(terms.IVar(i) < 0)
             elif any(B.implies_comparison(a <= 0) for a in args):
                 B.assert_comparison(terms.IVar(i) <= 0)
             # see if any multiple of another problem term is known to be less than all the
             # arguments.
             for j in range(B.num_terms):
                 if  j != i:
                     comp_range = geometry.ComparisonRange(geometry.neg_infty, geometry.infty,
                                                           True, True, True)
                     for a in args:
                         new_comp_range = B.le_coeff_range(j, a.term.index, a.coeff)
                         comp_range = comp_range & new_comp_range
                         if comp_range.is_empty():
                             break
                     if not comp_range.is_empty():
                         if comp_range.lower.type == geometry.VAL:
                             c = comp_range.lower.val
                             if comp_range.lower_strict:
                                 B.assert_comparison(c * terms.IVar(j) < terms.IVar(i))
                             else:
                                 B.assert_comparison(c * terms.IVar(j) <= terms.IVar(i))
                         if comp_range.upper.type == geometry.VAL:
                             c = comp_range.upper.val
                             if comp_range.upper_strict:
                                 B.assert_comparison(c * terms.IVar(j) < terms.IVar(i))
                             else:
                                 B.assert_comparison(c * terms.IVar(j) <= terms.IVar(i))
     timer.stop(timer.MINM)
Esempio n. 12
0
 def update_blackboard(self, B):
     """
     Asserts identities about exp and log terms found in B.
     """
     timer.start(timer.EXP)
     messages.announce_module('exponential module')
     if any(isinstance(t, terms.FuncTerm) and t.func == terms.exp for t in B.term_defs.values()):
         B.assert_comparison(terms.exp(0) == 1)
     if any(isinstance(t, terms.FuncTerm) and t.func == terms.log for t in B.term_defs.values()):
         B.assert_comparison(terms.log(1) == 0)
     exp_factor_constant(B)
     exp_factor_sum(B)
     #exp_factor_both(B)
     log_factor_exponent(B)
     log_factor_product(B)
     timer.stop(timer.EXP)
Esempio n. 13
0
    def update_blackboard(self, B):
        """
        Adds axioms corresponding to each nth root function present.
        """
        messages.announce_module('nth root value module')
        timer.start(timer.ROOT)

        #nth_roots = []

        def is_nth_root_term(i):
            """
            Returns True if t_i is of the form abs(t_j).
            """
            return (isinstance(B.term_defs[i], terms.FuncTerm)
                    and isinstance(B.term_defs[i].func, terms.NthRoot))

        def root_degree(i):
            """
            If t_i is of the form NthRoot(n, t_j), returns n.
            """
            return B.term_defs[i].func.n

        nth_roots = set([
            root_degree(i) for i in range(B.num_terms) if is_nth_root_term(i)
        ])
        x = terms.Var('x')
        for n in nth_roots:
            if n % 2 == 0:
                self.am.add_axiom(
                    formulas.Forall([x],
                                    formulas.Implies(x >= 0,
                                                     terms.root(n,
                                                                x)**n == x)))
                self.am.add_axiom(
                    formulas.Forall([x],
                                    formulas.Implies(x >= 0,
                                                     terms.root(n, x) >= 0)))
            else:
                self.am.add_axiom(
                    formulas.Forall([x],
                                    terms.root(n, x)**n == x))

        timer.stop(timer.ROOT)
Esempio n. 14
0
    def update_blackboard(self, B):
        """
        Checks the blackboard B for function terms with equal arguments, and asserts that the
        function terms are equal.
        """
        def eq_func_terms(f1, f2):
            """
            Returns true if f1 and f2 have the same name and arity, and all args are equal.
            """
            if f1.func_name != f2.func_name or len(f1.args) != len(f2.args):
                return False
            for i in range(len(f1.args)):
                arg1, arg2 = f1.args[i], f2.args[i]
                if arg1.coeff == 0:
                    eq = B.implies(arg2.term.index, terms.EQ, 0,
                                   0) or arg2.coeff == 0
                else:
                    eq = B.implies(arg1.term.index, terms.EQ,
                                   fractions.Fraction(arg2.coeff, arg1.coeff),
                                   arg2.term.index)
                if not eq:
                    return False
            return True

        timer.start(timer.CCM)
        messages.announce_module('congruence closure module')

        func_classes = {}
        for i in (d for d in range(B.num_terms)
                  if isinstance(B.term_defs[d], terms.FuncTerm)):
            name = B.term_defs[i].func_name
            func_classes[name] = func_classes.get(name, []) + [i]

        for name in func_classes:
            tinds = func_classes[name]
            for (i, j) in itertools.combinations(tinds, 2):
                # ti and tj are function terms with the same symbols. check if they're equal.
                f1, f2 = B.term_defs[i], B.term_defs[j]
                if eq_func_terms(f1, f2):
                    B.assert_comparison(terms.IVar(i) == terms.IVar(j))
        timer.stop(timer.CCM)
Esempio n. 15
0
    def update_blackboard(self, B):
        """
        Saturates a Blackboard B with multiplicative inferences.
        """
        timer.start(timer.PMUL)
        messages.announce_module('polyhedron multiplicative module')
        mul_util.derive_info_from_definitions(B)

        mul_util.preprocess_cancellations(B)

        m_comparisons = mul_util.get_multiplicative_information(B)
        # Each ti in m_comparisons really represents |t_i|.

        p = add_of_mul_comps(m_comparisons, B.num_terms)
        a_comparisons, prime_of_index, num_terms = p
        a_comparisons = [terms.comp_eval[c[1]](c[0], 0) for c in a_comparisons]

        h_matrix = lrs_util.create_h_format_matrix(a_comparisons, num_terms)
        messages.announce('Halfplane matrix:', messages.DEBUG)
        messages.announce(h_matrix, messages.DEBUG)
        v_matrix, v_lin_set = lrs_util.get_vertices(h_matrix)
        messages.announce('Vertex matrix:', messages.DEBUG)
        for l in v_matrix:
            messages.announce(str(l), messages.DEBUG)
        messages.announce('Linear set:', messages.DEBUG)
        messages.announce(str(v_lin_set), messages.DEBUG)

        new_comparisons = get_mul_comparisons(v_matrix, v_lin_set,
                                              B.num_terms, prime_of_index)


        for m1, m2, coeff, comp in new_comparisons:
            c = mul_util.process_mul_comp(m1, m2, coeff, comp, B)
            if c is not None:
                B.assert_comparison(c)
        timer.stop(timer.PMUL)
Esempio n. 16
0
    def update_blackboard(self, B):
        """
        Instantiates all stored axioms with respect to B and asserts new clauses.
        """
        timer.start(timer.FUN)
        messages.announce_module('axiom module')
        n = B.num_terms + len(B.equalities.keys()) + len(B.zero_equalities)
        #if n == self.num_eqs:
        #    messages.announce("No new information for the axiom module to use.", messages.DEBUG)
        #    timer.stop(timer.FUN)
        #    return
        self.num_eqs = n

        for a in self.axioms:
            messages.announce("Instantiating axiom: {}".format(a), messages.DEBUG)
            if a.unifiable:
                clauses = instantiate(a, self.used_envs, B)
                for c in clauses:
                    B.assert_clause(*c)
            else:
                clauses = instantiate_triggerless(a, self.used_envs, B)
                for c in clauses:
                    B.assert_clause(*c)
        timer.stop(timer.FUN)
Esempio n. 17
0
    def update_blackboard(self, B):
        """
        Adds variations on the triangle inequality.
        Looks for expressions abs(c1 * t1 + ... + ck * tk) for which each ti either
        occurs in an abs term, or has a known sign. In that case, adds inequalities corresponding
        to

            abs(c1 * t1 + ... + ck * tk) <= abs(c1 * t1) + ... + abs(ck * tk)
            abs(c1 * t1 + ... + ck * tk) >= abs(cj * tj) - (abs(c1 * t1) + ... + abs(ck * tk))

        """
        messages.announce_module('absolute value module')
        timer.start(timer.ABS)

        def is_abs_term(i):
            """
            Returns True if t_i is of the form abs(t_j).
            """
            return (isinstance(B.term_defs[i], terms.FuncTerm)
                                                      and B.term_defs[i].func_name == 'abs')

        def abs_arg_index(i):
            """
            If t_i is of the form abs(t_j), returns j.
            """
            return B.term_defs[i].args[0].term.index

        def is_sum_term(i):
            """
            Return True if t_i is a sum.
            """
            return isinstance(B.term_defs[i], terms.AddTerm)

        def sum_args(i):
            """
            Assuming t_i is a sum, returns the arguments.
            """
            return B.term_defs[i].args

        # indices i of terms t_i of the form abs(t_j)
        abs_indices = [i for i in range(B.num_terms) if is_abs_term(i)]
        # indices j of terms occurring in the context abs(t_j)
        abs_wrapped_indices = [abs_arg_index(i) for i in abs_indices]
        # indices of terms of the form abs(c1 * t1 + ... + ck * tk)
        abs_sum_indices = [i for i in abs_indices if is_sum_term(abs_arg_index(i))]

        def abs_present(i):
            """
            Returns True if t_i occurs in the context abs(t_i) or has known sign, which is to say,
            something equivalent to abs(t_i) is already a problem term.
            """
            return i in abs_wrapped_indices or B.weak_sign(i) == 1 or B.weak_sign(i) == -1

        def abs_of(i):
            """
            Assuming abs_present(i), returns an expression equal to abs(t_i).
            """
            if B.weak_sign(i) == 1:
                return terms.IVar(i)
            elif B.weak_sign(i) == -1:
                return terms.IVar(i) * -1
            else:
                return terms.IVar(next(j for j in abs_indices if abs_arg_index(j) == i))

        for i in abs_sum_indices:
            args = sum_args(abs_arg_index(i))
            if all(abs_present(a.term.index) for a in args):
                abs_of_args = [a.coeff * abs_of(a.term.index) for a in args]
                sum_of_abs_of_args = reduce(lambda x, y: x + y, abs_of_args, 0)
                B.assert_comparison(terms.IVar(i) <= sum_of_abs_of_args)
                for a in abs_of_args:
                    B.assert_comparison(terms.IVar(i) >= a * 2 - sum_of_abs_of_args)

        timer.stop(timer.ABS)
Esempio n. 18
0
    def update_blackboard(self, B):
        """
        Adds variations on the triangle inequality.
        Looks for expressions abs(c1 * t1 + ... + ck * tk) for which each ti either
        occurs in an abs term, or has a known sign. In that case, adds inequalities corresponding
        to

            abs(c1 * t1 + ... + ck * tk) <= abs(c1 * t1) + ... + abs(ck * tk)
            abs(c1 * t1 + ... + ck * tk) >= abs(cj * tj) - (abs(c1 * t1) + ... + abs(ck * tk))

        """
        messages.announce_module('absolute value module')
        timer.start(timer.ABS)

        def is_abs_term(i):
            """
            Returns True if t_i is of the form abs(t_j).
            """
            return (isinstance(B.term_defs[i], terms.FuncTerm)
                    and B.term_defs[i].func_name == 'abs')

        def abs_arg_index(i):
            """
            If t_i is of the form abs(t_j), returns j.
            """
            return B.term_defs[i].args[0].term.index

        def is_sum_term(i):
            """
            Return True if t_i is a sum.
            """
            return isinstance(B.term_defs[i], terms.AddTerm)

        def sum_args(i):
            """
            Assuming t_i is a sum, returns the arguments.
            """
            return B.term_defs[i].args

        # indices i of terms t_i of the form abs(t_j)
        abs_indices = [i for i in range(B.num_terms) if is_abs_term(i)]
        # indices j of terms occurring in the context abs(t_j)
        abs_wrapped_indices = [abs_arg_index(i) for i in abs_indices]
        # indices of terms of the form abs(c1 * t1 + ... + ck * tk)
        abs_sum_indices = [
            i for i in abs_indices if is_sum_term(abs_arg_index(i))
        ]

        def abs_present(i):
            """
            Returns True if t_i occurs in the context abs(t_i) or has known sign, which is to say,
            something equivalent to abs(t_i) is already a problem term.
            """
            return i in abs_wrapped_indices or B.weak_sign(
                i) == 1 or B.weak_sign(i) == -1

        def abs_of(i):
            """
            Assuming abs_present(i), returns an expression equal to abs(t_i).
            """
            if B.weak_sign(i) == 1:
                return terms.IVar(i)
            elif B.weak_sign(i) == -1:
                return terms.IVar(i) * -1
            else:
                return terms.IVar(
                    next(j for j in abs_indices if abs_arg_index(j) == i))

        for i in abs_sum_indices:
            args = sum_args(abs_arg_index(i))
            if all(abs_present(a.term.index) for a in args):
                abs_of_args = [a.coeff * abs_of(a.term.index) for a in args]
                sum_of_abs_of_args = reduce(lambda x, y: x + y, abs_of_args, 0)
                B.assert_comparison(terms.IVar(i) <= sum_of_abs_of_args)
                for a in abs_of_args:
                    B.assert_comparison(
                        terms.IVar(i) >= a * 2 - sum_of_abs_of_args)

        timer.stop(timer.ABS)