Example #1
0
File: real.py Project: bzhan/holpy
    def get_proof_term(self, t):
        if not is_real_ineq(t):
            return refl(t)

        pt_refl = refl(t).on_rhs(real_norm_comparison())
        left_expr = pt_refl.rhs.arg1
        summands = integer.strip_plus(left_expr)
        first = summands[0]

        if not left_expr.is_plus() or not first.is_constant():
            return pt_refl
        first_value = real_eval(first)
        if pt_refl.rhs.is_greater_eq():
            pt_th = ProofTerm.theorem("real_sub_both_sides_geq")
        elif pt_refl.rhs.is_greater():
            pt_th = ProofTerm.theorem("real_sub_both_sides_gt")
        elif pt_refl.rhs.is_less_eq():
            pt_th = ProofTerm.theorem("real_sub_both_sides_leq")
        elif pt_refl.rhs.is_less():
            pt_th = ProofTerm.theorem("real_sub_both_sides_le")
        else:
            raise NotImplementedError(str(t))

        inst = matcher.first_order_match(pt_th.lhs,
                                         pt_refl.rhs,
                                         inst=matcher.Inst(c=first))
        return pt_refl.transitive(pt_th.substitution(inst=inst)).on_rhs(
            auto.auto_conv())
Example #2
0
    def get_proof_term(self, goal):
        th = theory.get_theorem(self.th_name)

        assum = th.assums[0]
        cond = self.cond
        if cond is None:
            # Find cond by matching with goal.hyps one by one
            for hyp in goal.hyps:
                try:
                    inst = matcher.first_order_match(th.assums[0], hyp,
                                                     self.inst)
                    cond = hyp
                    break
                except matcher.MatchException:
                    pass

        if cond is None:
            raise TacticException('elim: cannot match assumption')

        try:
            inst = matcher.first_order_match(th.concl, goal.prop, inst)
        except matcher.MatchException:
            raise TacticException('elim: matching failed')

        if any(v.name not in inst for v in th.prop.get_svars()):
            raise TacticException('elim: not all variables are matched')

        pt = ProofTerm.theorem(self.th_name).substitution(inst).on_prop(
            beta_norm_conv())
        pt = pt.implies_elim(ProofTerm.assume(cond))
        for assum in pt.assums:
            pt = pt.implies_elim(ProofTerm.sorry(Thm(goal.hyps, assum)))
        return pt
Example #3
0
    def get_proof_term(self, args, pts):
        if self.with_inst:
            name, inst = args
        else:
            name = args
            inst = Inst()
        th = theory.get_theorem(name)
        As, C = th.prop.strip_implies()

        assert len(pts) <= len(As), "apply_theorem: too many prevs."

        # First attempt to match type variables
        svars = th.prop.get_svars()
        for v in svars:
            if v.name in inst:
                v.T.match_incr(inst[v.name].get_type(), inst.tyinst)

        pats = As[:len(pts)]
        ts = [pt.prop for pt in pts]
        inst = matcher.first_order_match_list(pats, ts, inst)

        pt = ProofTerm.theorem(name)
        pt = pt.subst_type(inst.tyinst).substitution(inst)
        if pt.prop.beta_norm() != pt.prop:
            pt = pt.on_prop(beta_norm_conv())
        pt = pt.implies_elim(*pts)

        assert len(pt.prop.get_stvars()) == 0, "apply_theorem: unmatched type variables."
        vars = pt.prop.get_svars()
        for v in reversed(vars):
            pt = pt.forall_intr(v)

        return pt
Example #4
0
    def get_proof_term(self, t):
        if not t.is_compares():
            raise ConvException('%s is not a comparison.' % str(t))

        pt = refl(t)
        pt_norm_form = pt.on_rhs(norm_eq(), arg1_conv(omega_simp_full_conv()))
        summands = strip_plus(pt_norm_form.rhs.arg1)
        coeffs = [
            int_eval(s.arg1) if not s.is_number() else int_eval(s)
            for s in summands
        ]
        g = functools.reduce(gcd, coeffs)
        if g <= 1:
            return pt

        vars = [s.arg for s in summands if not s.is_number()]
        elim_gcd_coeffs = [int(i / g) for i in coeffs]
        if len(vars) < len(coeffs):
            simp_t = sum([
                coeff * v for coeff, v in zip(elim_gcd_coeffs[1:-1], vars[1:])
            ], elim_gcd_coeffs[0] * vars[0]) + Int(elim_gcd_coeffs[-1])
        else:
            simp_t = sum(
                [coeff * v for coeff, v in zip(elim_gcd_coeffs[1:], vars[1:])],
                elim_gcd_coeffs[0] * vars[0])

        simp_t_times_gcd = Int(g) * simp_t
        pt_simp_t_times_gcd = refl(simp_t_times_gcd).on_rhs(
            omega_simp_full_conv()).symmetric()
        pt_c = ProofTerm('int_const_ineq', greater(IntType)(Int(g), Int(0)))
        if t.is_less():
            gcd_pt = ProofTerm.theorem('int_simp_less')
        elif t.is_less_eq():
            gcd_pt = ProofTerm.theorem('int_simp_leq')
        elif t.is_greater():
            gcd_pt = ProofTerm.theorem('int_simp_gt')
        elif t.is_greater_eq():
            gcd_pt = ProofTerm.theorem('int_simp_geq')

        inst1 = matcher.first_order_match(gcd_pt.prop.arg1, pt_c.prop)
        inst2 = matcher.first_order_match(gcd_pt.prop.arg.rhs.arg1,
                                          simp_t,
                                          inst=inst1)

        pt_simp = gcd_pt.substitution(inst2).implies_elim(pt_c).on_lhs(
            arg1_conv(omega_simp_full_conv()))
        return pt_norm_form.transitive(pt_simp).on_rhs(omega_form_conv())
Example #5
0
File: nat.py Project: bzhan/holpy
def ineq_zero_proof_term(n):
    """Returns the inequality n ~= 0."""
    assert n != 0, "ineq_zero_proof_term: n = 0"
    if n == 1:
        return ProofTerm.theorem("one_nonzero")
    elif n % 2 == 0:
        return apply_theorem("bit0_nonzero", ineq_zero_proof_term(n // 2))
    else:
        return apply_theorem("bit1_nonzero", inst=Inst(m=Binary(n // 2)))
Example #6
0
File: nat.py Project: bzhan/holpy
def ineq_one_proof_term(n):
    """Returns the inequality n ~= 1."""
    assert n != 1, "ineq_one_proof_term: n = 1"
    if n == 0:
        return apply_theorem("ineq_sym", ProofTerm.theorem("one_nonzero"))
    elif n % 2 == 0:
        return apply_theorem("bit0_neq_one", inst=Inst(m=Binary(n // 2)))
    else:
        return apply_theorem("bit1_neq_one", ineq_zero_proof_term(n // 2))
Example #7
0
    def get_proof_term(self, args, pts):
        th_name, goal = args
        pt = ProofTerm.theorem(th_name)
        assert pt.prop.is_not(), "resolve_theorem_macro"

        # Match for variables in pt.
        inst = matcher.first_order_match(pt.prop.arg, pts[0].prop)
        pt = pt.subst_type(inst.tyinst).substitution(inst)
        pt = apply_theorem('negE', pt, pts[0])  # false
        return apply_theorem('falseE', pt, concl=goal)
Example #8
0
File: conv.py Project: bzhan/holpy
    def get_proof_term(self, t):
        if not t.is_abs():
            raise ConvException("eta_conv")

        v, body = t.dest_abs()

        if not (body.is_comb() and body.arg == v
                and not body.fun.occurs_var(v)):
            raise ConvException("eta_conv")

        return ProofTerm.theorem('eta_conversion').substitution(f=body.fun)
Example #9
0
    def get_proof_term(self, prevs, goal_lit):
        disj, *lit_pts = prevs
        pt_conj = lit_pts[0]

        for i in range(len(lit_pts)):
            pt = lit_pts[i]
            if not pt.prop.is_not():
                lit_pts[i] = pt.on_prop(rewr_conv('double_neg', sym=True))

        def conj_right_assoc(pts):
            """
            Give a sequence of proof terms: ⊢ A, ⊢ B, ⊢ C,
            return ⊢ A ∧ (B ∧ C)
            """
            if len(pts) == 1:
                return pts[0]
            else:
                return apply_theorem('conjI', pts[0],
                                     conj_right_assoc(pts[1:]))

        # get a /\ b /\ c
        pt_conj = conj_right_assoc(lit_pts)

        other_lits = [
            l.prop.arg if l.prop.is_not() else Not(l.prop) for l in lit_pts
        ]

        # use de Morgan
        pt_conj1 = pt_conj.on_prop(
            bottom_conv(rewr_conv('de_morgan_thm2', sym=True)))

        # if len(other_lits) == 1 and other_lits[0].is_not():
        #     pt_conj1 = pt_conj.on_prop(rewr_conv('double_neg', sym=True))

        # Equality for two disjunctions which literals are the same, but order is different.
        eq_pt = imp_disj_iff(Eq(disj.prop, Or(goal_lit, *other_lits)))
        new_disj_pt = disj.on_prop(top_conv(replace_conv(eq_pt)))

        # A \/ B --> ~B --> A
        pt = ProofTerm.theorem('force_disj_true1')
        A, B = pt.prop.strip_implies()[0]
        C = pt.prop.strip_implies()[1]

        inst1 = matcher.first_order_match(C, goal_lit)
        inst2 = matcher.first_order_match(A,
                                          Or(goal_lit, *other_lits),
                                          inst=inst1)
        inst3 = matcher.first_order_match(B, pt_conj1.prop, inst=inst2)
        pt_implies = apply_theorem('force_disj_true1',
                                   new_disj_pt,
                                   pt_conj1,
                                   inst=inst3)

        return pt_implies.on_prop(try_conv(rewr_conv('double_neg')))
Example #10
0
File: conv.py Project: bzhan/holpy
    def get_proof_term(self, t):
        # If self.eq_pt is not present, produce it from thy, self.pt
        # and self.sym. Decompose into self.As and self.C.
        if self.eq_pt is None:
            if isinstance(self.pt, str):
                self.eq_pt = ProofTerm.theorem(self.pt)
            else:
                self.eq_pt = self.pt

            self.As, self.C = self.eq_pt.prop.strip_implies()

        # The conclusion of eq_pt should be an equality, and the number of
        # assumptions in eq_pt should match number of conditions.
        assert self.C.is_equals(), "rewr_conv: theorem is not an equality."
        if len(self.As) != len(self.conds):
            raise ConvException("rewr_conv: number of conds does not agree")

        inst = Inst()
        ts = [cond.prop for cond in self.conds]
        if not self.sym:
            lhs = self.C.lhs
        else:
            lhs = self.C.rhs
        try:
            inst = matcher.first_order_match_list(self.As, ts, inst)
            inst = matcher.first_order_match(lhs, t, inst)
        except matcher.MatchException:
            raise ConvException("rewr_conv: cannot match left side")

        # Check that every variable in the theorem has an instantiation
        if set(term.get_svars(self.As + [lhs])) != set(
                term.get_svars(self.As + [self.C])):
            raise ConvException("rewr_conv: unmatched vars")

        pt = self.eq_pt
        pt = pt.substitution(inst)
        pt = pt.implies_elim(*self.conds)
        if self.sym:
            pt = pt.symmetric()

        assert pt.th.is_equals(), "rewr_conv: wrong result."

        if pt.th.prop.lhs != t:
            pt = pt.on_prop(beta_norm_conv())

        if pt.th.prop.lhs != t:
            pt = pt.on_prop(eta_conv())

        assert pt.th.prop.lhs == t, "rewr_conv: wrong result. Expected %s, got %s" % (
            str(t), str(pt.th.prop.lhs))
        return pt
Example #11
0
    def get_proof_term(self, goal):
        th = theory.get_theorem(self.th_name)
        try:
            inst = matcher.first_order_match(th.concl, goal.prop, self.inst)
        except matcher.MatchException:
            raise TacticException('rule: matching failed')

        if any(v.name not in inst for v in th.prop.get_svars()):
            raise TacticException('rule: not all variables are matched')

        pt = ProofTerm.theorem(self.th_name).substitution(inst).on_prop(
            beta_norm_conv())
        for assum in pt.assums:
            pt = pt.implies_elim(ProofTerm.sorry(Thm(goal.hyps, assum)))
        return pt
Example #12
0
    def get_proof_term(self, args, pts):
        assert isinstance(args, str), "rewrite_fact_macro: signature"

        th_name = args
        eq_pt = ProofTerm.theorem(th_name)

        assert len(pts) == len(eq_pt.assums) + 1, "rewrite_fact_macro: signature"

        # Check rewriting using the theorem has an effect
        if not has_rewrite(th_name, pts[0].prop, sym=self.sym, conds=pts[1:]):
            raise InvalidDerivationException("rewrite_fact using %s" % th_name)

        cv = then_conv(top_sweep_conv(rewr_conv(eq_pt, sym=self.sym, conds=pts[1:])),
                       beta_norm_conv())
        res = pts[0].on_prop(cv)
        if res == pts[0]:
            raise InvalidDerivationException("rewrite_fact using %s" % th_name)
        return res
Example #13
0
def apply_theorem(th_name, *pts, concl=None, inst=None):
    """Wrapper for apply_theorem and apply_theorem_for macros.

    The function takes optional arguments concl, inst. Matching
    always starts with inst. If conclusion is specified, it is
    matched next. Finally, the assumptions are matched.

    """
    typecheck.checkinstance('apply_theorem', pts, [ProofTerm])
    if concl is None and inst is None:
        # Normal case, can use apply_theorem
        return ProofTerm("apply_theorem", th_name, pts)
    else:
        pt = ProofTerm.theorem(th_name)
        if inst is None:
            inst = Inst()
        if concl is not None:
            inst = matcher.first_order_match(pt.concl, concl, inst)
        for i, prev in enumerate(pts):
            inst = matcher.first_order_match(pt.assums[i], prev.prop, inst)
        return ProofTerm("apply_theorem_for", (th_name, inst), pts)
Example #14
0
    def get_proof_term(self, args, pts):
        assert isinstance(args, tuple) and len(args) == 2 and \
               isinstance(args[0], str) and isinstance(args[1], Term), "rewrite_goal: signature"

        name, goal = args
        eq_pt = ProofTerm.theorem(name)

        if len(pts) == len(eq_pt.assums):
            rewr_cv = rewr_conv(eq_pt, sym=self.sym, conds=pts)
        else:
            assert len(pts) == len(eq_pt.assums) + 1, "rewrite_goal: wrong number of prevs"
            rewr_cv = rewr_conv(eq_pt, sym=self.sym, conds=pts[1:])

        cv = then_conv(top_sweep_conv(rewr_cv), beta_norm_conv())
        pt = cv.get_proof_term(goal)  # goal = th.prop
        pt = pt.symmetric()           # th.prop = goal
        if pt.prop.lhs.is_equals() and pt.prop.lhs.lhs == pt.prop.lhs.rhs:
            pt = pt.equal_elim(refl(pt.prop.lhs.lhs))
        else:
            pt = pt.equal_elim(pts[0])  # goal

        return pt
Example #15
0
 def __init__(self, th_name):
     self.th_name = th_name
     self.pt = ProofTerm.theorem(th_name)
     self.th = self.pt.th
     self.prop = self.pt.prop
     self.incr_sc = 'SIZE'
Example #16
0
    def get_proof_term(self, t):

        if not t.is_conj():
            return refl(t)

        d_pos = dict()
        d_neg = dict()
        qu = deque([ProofTerm.assume(t)])
        # collect each conjunct's proof term in conjuntion
        while qu:
            pt = qu.popleft()
            if pt.prop.is_conj():
                conj1, conj2 = pt.prop.arg1, pt.prop.arg
                pt_conj1, pt_conj2 = apply_theorem('conjD1',
                                                   pt), apply_theorem(
                                                       'conjD2', pt)
                if conj1 == false:
                    th = ProofTerm.theorem("falseE")
                    inst = matcher.first_order_match(th.prop.arg, t)
                    pt_false_implies_conj = th.substitution(inst)
                    return ProofTerm.equal_intr(pt_conj1.implies_intr(t),
                                                pt_false_implies_conj)
                elif conj2 == false:
                    th = ProofTerm.theorem("falseE")
                    inst = matcher.first_order_match(th.prop.arg, t)
                    pt_false_implies_conj = th.substitution(inst)
                    return ProofTerm.equal_intr(pt_conj2.implies_intr(t),
                                                pt_false_implies_conj)
                if conj1.is_conj():
                    qu.appendleft(pt_conj1)
                else:
                    if conj1.is_not():
                        d_neg[conj1] = pt_conj1
                    else:
                        d_pos[conj1] = pt_conj1
                if conj2.is_conj():
                    qu.appendleft(pt_conj2)
                else:
                    if conj2.is_not():
                        d_neg[conj2] = pt_conj2
                    else:
                        d_pos[conj2] = pt_conj2
            else:
                if pt.prop.is_not():
                    d_neg[pt.prop] = pt
                else:
                    d_pos[pt.prop] = pt

        # first check if there are opposite terms in conjunctions, if there exists, return a false proof term
        for key in d_pos:
            if Not(key) in d_neg:
                pos_pt, neg_pt = d_pos[key], d_neg[Not(key)]
                pt_conj_pos_neg = apply_theorem("conjI", pos_pt, neg_pt)
                pt_conj_implies_false = pt_conj_pos_neg.on_prop(
                    rewr_conv("conj_pos_neg")).implies_intr(t)
                th = ProofTerm.theorem("falseE")
                inst = matcher.first_order_match(th.prop.arg, t)
                pt_false_implies_conj = th.substitution(inst)
                return ProofTerm.equal_intr(pt_conj_implies_false,
                                            pt_false_implies_conj)

        d_pos.update(d_neg)
        d = d_pos

        def right_assoc(ts):
            l = len(ts)
            if l == 1:
                return d[ts[0]]
            elif l == 2:
                return apply_theorem('conjI', d[ts[0]], d[ts[1]])
            else:
                return apply_theorem('conjI', d[ts[0]], right_assoc(ts[1:]))

        # pt_right = functools.reduce(lambda x, y: apply_theorem('conjI', x, d[y]), sorted(d.keys()), d[sorted(d.keys())[0]])
        if true not in d:
            sorted_keys = term_ord.sorted_terms(d.keys())
        else:
            d_keys_without_true = term_ord.sorted_terms(
                [k for k in d if k != true])
            sorted_keys = [true] + d_keys_without_true
        sorted_keys_num = len(sorted_keys)
        pt_right = functools.reduce(lambda x, y: apply_theorem('conjI', d[sorted_keys[sorted_keys_num - y - 2]], x), \
                        range(sorted_keys_num - 1), d[sorted_keys[-1]])
        # pt_right = right_assoc(sorted_keys)
        # order implies original
        dd = dict()
        norm_conj = And(*sorted_keys)
        norm_conj_pt = ProofTerm.assume(norm_conj)
        for k in sorted_keys:
            if k != sorted_keys[-1]:
                dd[k] = apply_theorem('conjD1', norm_conj_pt)
                norm_conj_pt = apply_theorem('conjD2', norm_conj_pt)
            else:
                dd[k] = norm_conj_pt

        def traverse(t):
            if not t.is_conj():
                return dd[t]
            else:
                return apply_theorem('conjI', traverse(t.arg1),
                                     traverse(t.arg))

        pt_left = traverse(t)

        pt_final = ProofTerm.equal_intr(pt_right.implies_intr(t),
                                        pt_left.implies_intr(norm_conj))
        if true in d:
            return pt_final.on_rhs(rewr_conv("conj_true_left"),
                                   top_sweep_conv(sort_disj()))
        else:
            return pt_final.on_rhs(top_sweep_conv(sort_disj()))
Example #17
0
File: real.py Project: bzhan/holpy
    def get_proof_term(self, args, prevs=None):
        """
        Let x_i denotes greater comparison, x__i denotes less comparison,
        for the greater comparison, find the smallest number x_min = min(x_1, ..., x_n), since x_min is positive, 
        x_min/2 > 0 ==> x_min >= x_min / 2 ==> x_1, ..., x_n >= x_min / 2 ==> ∃δ. δ > 0 ∧ x_1 >= δ ∧ ... ∧ x_n >= δ.
        for the less comparison, find the largest number x_max = max(x__1, ..., x__n), since x_max is negative, x_max < 
        x_max/2 ==> x__1, ..., x__n <= x_max/2;
        let δ = min(x_min/2, -x_max/2), then all x_i >= δ as well as all x__i <= -δ.
        """
        def need_convert(tm):
            return False if real_eval(tm.arg) != 0 else True

        original_ineq_pts = [ProofTerm.assume(ineq) for ineq in args]

        # record the ineq which rhs is not 0
        need_convert_pt = {arg for arg in args if need_convert(arg)}

        # record the args order
        order_args = {args[i].arg1: i for i in range(len(args))}

        # convert all ineqs to x_i > 0 or x_i < 0
        normal_ineq_pts = [
            pt.on_prop(norm_real_ineq_conv()) if pt.prop.arg != Real(0) else pt
            for pt in original_ineq_pts
        ]

        # dividing less comparison and greater comparison
        greater_ineq_pts = [
            pt for pt in normal_ineq_pts if pt.prop.is_greater()
        ]
        less_ineq_pts = [pt for pt in normal_ineq_pts if pt.prop.is_less()]

        # stage 1: get the max(min) pos bound
        # ⊢ min(...) ≥ δ_1, δ_1 > 0
        # ⊢ max(...) ≤ δ_2, δ_2 < 0
        pt_lower_bound, lower_bound_pos_pt, pt_assert_delta1 = self.handle_geq_stage1(
            greater_ineq_pts)
        pt_upper_bound, upper_bound_neg_pt, pt_assert_delta2 = self.handle_leq_stage1(
            less_ineq_pts)

        delta_1 = Var("δ_1", RealType)
        delta_2 = Var("δ_2", RealType)

        # generate the relaxed inequations
        if pt_lower_bound is None:  # all comparisons are ≤
            pts = self.handle_leq_stage2(pt_upper_bound, less_ineq_pts,
                                         delta_2)
            bound_pt = upper_bound_neg_pt
            delta = delta_2
            pt_asserts = [pt_assert_delta2]
        elif pt_upper_bound is None:  # all comparisons are ≥
            pts = self.handle_geq_stage2(pt_lower_bound, greater_ineq_pts,
                                         delta_1)
            bound_pt = lower_bound_pos_pt
            delta = delta_1
            pt_asserts = [pt_assert_delta1]
        else:  # have both ≥ and ≤
            # ⊢ δ_1 ≥ min(δ_1, δ_2)
            pt_min_lower_bound = logic.apply_theorem("real_greater_min",
                                                     inst=matcher.Inst(
                                                         x=delta_1, y=delta_2))
            # ⊢ -δ_2 ≤ max(-δ_2, -δ_1)
            pt_max_upper_bound = logic.apply_theorem("real_less_max",
                                                     inst=matcher.Inst(
                                                         x=-delta_2,
                                                         y=-delta_1))
            # ⊢ max(-δ_2, -δ_1) = -min(δ_1, δ_2)
            pt_max_min = logic.apply_theorem("max_min",
                                             inst=matcher.Inst(x=delta_1,
                                                               y=delta_2))
            # ⊢ min(...) ≥ min(δ_1, δ_2)
            pt_new_lower_bound = logic.apply_theorem("real_geq_trans",
                                                     pt_lower_bound,
                                                     pt_min_lower_bound)
            # ⊢ -δ_2 ≤ -min(δ_1, δ_2)
            pt_max_upper_bound_1 = pt_max_upper_bound.on_prop(
                arg_conv(replace_conv(pt_max_min)))
            # ⊢ max(...) ≤ -min(δ_1, δ_2)
            pt_new_upper_bound = logic.apply_theorem("real_le_trans",
                                                     pt_upper_bound,
                                                     pt_max_upper_bound_1)
            # ⊢ min(δ_1, δ_2) > 0
            pt_new_lower_bound_pos = logic.apply_theorem(
                "min_pos", lower_bound_pos_pt, upper_bound_neg_pt)
            # ⊢ min(δ_1, δ_2) = δ
            delta = Var("δ", RealType)
            pt_delta_eq = ProofTerm.assume(
                Eq(pt_min_lower_bound.prop.arg, delta))
            pt_asserts = [pt_delta_eq, pt_assert_delta1, pt_assert_delta2]
            # ⊢ min(...) ≥ δ
            pt_new_lower_bound_delta = pt_new_lower_bound.on_prop(
                arg_conv(replace_conv(pt_delta_eq)))
            # ⊢ max(...) ≤ -δ
            pt_new_upper_bound_delta = pt_new_upper_bound.on_prop(
                top_conv(replace_conv(pt_delta_eq)))
            # use new bound
            pts_leq = self.handle_leq_stage2(pt_new_upper_bound_delta,
                                             less_ineq_pts, delta)
            pts_geq = self.handle_geq_stage2(pt_new_lower_bound_delta,
                                             greater_ineq_pts, delta)
            pts = pts_leq + pts_geq
            bound_pt = pt_new_lower_bound_pos.on_prop(
                arg1_conv(replace_conv(pt_delta_eq)))

        # sort_pts = sorted(pts, key=lambda pt: order_args[pt.prop.arg1])

        pt_conj = functools.reduce(
            lambda x, y: logic.apply_theorem("conjI", y, x),
            reversed([bound_pt] + pts))

        # get ⊢∃δ. δ > 0 ∧ x_1 >= δ ∧ ... ∧ x_n >= δ
        th = ProofTerm.theorem("exI")
        inst = matcher.first_order_match(th.prop.arg,
                                         Exists(delta, pt_conj.prop))
        pt_conj_exists = logic.apply_theorem("exI", pt_conj, inst=inst)
        pt_final = pt_conj_exists
        for pt_subst in pt_asserts:
            lhs, rhs = pt_subst.prop.args
            if not rhs.is_uminus():
                pt_final = pt_final.implies_intr(pt_subst.prop).forall_intr(rhs).\
                            forall_elim(lhs).implies_elim(ProofTerm.reflexive(lhs))
            else:
                pt_final = pt_final.implies_intr(pt_subst.prop).forall_intr(rhs.arg).\
                    forall_elim(-lhs).on_prop(top_conv(rewr_conv("real_neg_neg"))).implies_elim(ProofTerm.reflexive(lhs))
        return pt_final