def testPrintReal(self): basic.load_theory('real') m = Var("m", RealType) test_data = [ (real.zero, "(0::real)"), (real.one, "(1::real)"), (Real(2), "(2::real)"), (Real(3), "(3::real)"), (real.plus(m, real.one), "m + 1"), (real.plus(real.one, Real(2)), "(1::real) + 2"), ] for t, s in test_data: self.assertEqual(printer.print_term(t), s)
def get_proof_term(self, t): if t.get_type() != RealType: return refl(t) simp_t = Real(real_eval(t)) if simp_t == t: return refl(t) return ProofTerm('real_eval', Eq(t, simp_t))
def handle_leq_stage2(self, pt_upper_bound, pts, delta): # get ⊢ x_i ≤ -δ, for i = 1...n leq_pt = [] pt_b = pt_upper_bound for i in range(len(pts)): if i != len(pts) - 1: pt = logic.apply_theorem("both_leq_max", pt_b) pt_1, pt_2 = logic.apply_theorem("conjD1", pt), logic.apply_theorem( "conjD2", pt) else: pt_2 = pt_b ineq = pt_2.prop if ineq.arg1.is_minus() and ineq.arg1.arg.is_number(): num = ineq.arg1.arg expr = less_eq(ineq.arg1.arg1, num - delta) else: expr = less_eq(ineq.arg1, Real(0) - delta) pt_eq_comp = ProofTerm("real_eq_comparison", Eq(ineq, expr)) leq_pt.insert(0, pt_2.on_prop(replace_conv(pt_eq_comp))) if i != len(pts) - 1: pt_b = pt_1 return leq_pt
def handle_geq_stage2(self, pt_lower_bound, pts, delta): # get ⊢ x_i ≥ δ, i = 1...n geq_pt = [] pt_a = pt_lower_bound d = set() for i in range(len(pts)): if i != len(pts) - 1: pt = logic.apply_theorem("both_geq_min", pt_a) pt_1, pt_2 = logic.apply_theorem("conjD1", pt), logic.apply_theorem( "conjD2", pt) else: pt_2 = pt_a ineq = pt_2.prop if ineq.arg1.is_minus() and ineq.arg1.arg.is_number(): # move all constant term from left to right in pt_2's prop num = ineq.arg1.arg expr = greater_eq(ineq.arg1.arg1, num + delta) else: expr = greater_eq(ineq.arg1, Real(0) + delta) pt_eq_comp = ProofTerm("real_eq_comparison", Eq(ineq, expr)) geq_pt.insert(0, pt_2.on_prop(replace_conv(pt_eq_comp))) if i != len(pts) - 1: pt_a = pt_1 return geq_pt
def get_proof_term(self, t): a, p = t.args # Exponent is an integer: apply rpow_pow if p.is_number() and p.is_comb('of_nat', 1) and p.arg.is_binary(): return refl(t).on_rhs( arg_conv(rewr_conv('real_of_nat_id', sym=True)), rewr_conv('rpow_pow')) if not (a.is_number() and p.is_number()): raise ConvException a, p = a.dest_number(), p.dest_number() if a <= 0: raise ConvException # Case 1: base is a composite number factors = factorint(a) keys = list(factors.keys()) if len(keys) > 1 or (len(keys) == 1 and keys[0] != a): b1 = list(factors.keys())[0] b2 = a // b1 eq_th = refl(Real(b1) * b2).on_rhs(real_eval_conv()) pt = refl(t).on_rhs(arg1_conv(rewr_conv(eq_th, sym=True))) pt = pt.on_rhs(rewr_conv('rpow_mul')) return pt # Case 2: exponent is not between 0 and 1 if isinstance(p, Fraction) and p.numerator // p.denominator != 0: div, mod = divmod(p.numerator, p.denominator) eq_th = refl(Real(div) + Real(mod) / p.denominator).on_rhs( real_eval_conv()) pt = refl(t).on_rhs(arg_conv(rewr_conv(eq_th, sym=True))) a_gt_0 = auto.auto_solve(Real(a) > 0) pt = pt.on_rhs(rewr_conv('rpow_add', conds=[a_gt_0])) return pt return refl(t)
def handle_geq_stage1(self, pts): if not pts: return None, None, None # ⊢ min(min(...(min(x_1, x_2), x_3)...), x_n-1), x_n) > 0 min_pos_pt = functools.reduce( lambda pt1, pt2: logic.apply_theorem("min_greater_0", pt1, pt2), pts[1:], pts[0]) # ⊢ 0 < 2 two_pos_pt = ProofTerm("real_compare", Real(0) < Real(2)) # ⊢ min(...) / 2 > 0 min_divides_two_pos = logic.apply_theorem( "real_lt_div", min_pos_pt.on_prop(rewr_conv("real_ge_to_le")), two_pos_pt).on_prop(rewr_conv("real_ge_to_le", sym=True)) # ⊢ 2 ≥ 1 two_larger_one = ProofTerm("real_compare", Real(2) >= Real(1)) # ⊢ min(...) ≥ min(...) / 2 larger_half_pt = logic.apply_theorem("real_divides_larger_1", two_larger_one, min_pos_pt) # ⊢ min(...) / 2 = δ_1 delta_1 = Var("δ_1", RealType) pt_delta1_eq = ProofTerm.assume(Eq(larger_half_pt.prop.arg, delta_1)) # ⊢ min(...) ≥ δ_1 larger_half_pt_delta = larger_half_pt.on_prop( top_conv(replace_conv(pt_delta1_eq))) # ⊢ δ_1 > 0 delta_1_pos = min_divides_two_pos.on_prop( arg1_conv(replace_conv(pt_delta1_eq))) return larger_half_pt_delta, delta_1_pos, pt_delta1_eq
def handle_leq_stage1(self, pts): if not pts: return None, None, None # ⊢ max(max(...(max(x_1, x_2), x_3)...), x_n-1), x_n) < 0 max_pos_pt = functools.reduce( lambda pt1, pt2: logic.apply_theorem("max_less_0", pt1, pt2), pts[1:], pts[0]) # ⊢ 0 < 2 two_pos_pt = ProofTerm("real_compare", Real(2) > Real(0)) # ⊢ max(...) / 2 < 0 max_divides_two_pos = logic.apply_theorem("real_neg_div_pos", max_pos_pt, two_pos_pt) # ⊢ 2 ≥ 1 two_larger_one = ProofTerm("real_compare", Real(2) >= Real(1)) # ⊢ max(...) ≤ max(...) / 2 less_half_pt = logic.apply_theorem("real_neg_divides_larger_1", two_larger_one, max_pos_pt) # ⊢ max(...) / 2 = -δ delta_2 = Var("δ_2", RealType) pt_delta_eq = ProofTerm.assume(Eq(less_half_pt.prop.arg, -delta_2)) # ⊢ δ > 0 delta_pos_pt = max_divides_two_pos.on_prop( rewr_conv("real_le_gt"), top_conv(replace_conv(pt_delta_eq)), auto.auto_conv()) # max(...) ≤ -δ less_half_pt_delta = less_half_pt.on_prop( arg_conv(replace_conv(pt_delta_eq))) return less_half_pt_delta, delta_pos_pt, pt_delta_eq
def from_mono(m): """Convert a monomial to a term.""" assert isinstance(m, poly.Monomial), "from_mono: input is not a monomial" factors = [] for base, power in m.factors: assert isinstance(base, Term), "from_mono: base is not a Term" baseT = base.get_type() if baseT != RealType: base = Const('of_nat', TFun(baseT, RealType))(base) if power == 1: factors.append(base) else: factors.append(nat_power(base, Nat(power))) if m.coeff != 1: factors = [Real(m.coeff)] + factors return Prod(RealType, factors)
def testReal(self): basic.load_theory('real') x = Var('x', RealType) y = Var('y', RealType) n = Var('n', NatType) test_data = [ (x + y, "x + y"), (x * y, "x * y"), (x - y, "x - y"), (-x, "-x"), (x - (-y), "x - -y"), (-(-x), "--x"), (-(x - y), "-(x - y)"), (-(x**n), "-(x ^ n)"), ((-x)**n, "-x ^ n"), (x + real.of_nat(Nat(2)), "x + of_nat 2"), (x + Real(1) / 0, "x + 1 / 0"), ] for t, s in test_data: self.assertEqual(printer.print_term(t), s)
def get_nat_power_bounds(pt, n): """Given theorem of the form t Mem I, obtain a theorem of the form t ^ n Mem J. """ a, b = get_mem_bounds(pt) if not n.is_number(): raise NotImplementedError if eval_hol_expr(a) >= 0 and is_mem_closed(pt): pt = apply_theorem('nat_power_interval_pos_closed', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(a) >= 0 and is_mem_open(pt): pt = apply_theorem('nat_power_interval_pos_open', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(a) >= 0 and is_mem_lopen(pt): pt = apply_theorem('nat_power_interval_pos_lopen', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(a) >= 0 and is_mem_ropen(pt): pt = apply_theorem('nat_power_interval_pos_ropen', auto.auto_solve(real_nonneg(a)), pt, inst=Inst(n=n)) elif eval_hol_expr(b) <= 0 and is_mem_closed(pt): int_n = n.dest_number() if int_n % 2 == 0: even_pt = nat_as_even(int_n) pt = apply_theorem('nat_power_interval_neg_even_closed', auto.auto_solve(real_nonpos(b)), even_pt, pt) else: odd_pt = nat_as_odd(int_n) pt = apply_theorem('nat_power_interval_neg_odd_closed', auto.auto_solve(real_nonpos(b)), odd_pt, pt) elif eval_hol_expr(b) <= 0 and is_mem_open(pt): int_n = n.dest_number() if int_n % 2 == 0: even_pt = nat_as_even(int_n) pt = apply_theorem('nat_power_interval_neg_even_open', auto.auto_solve(real_nonpos(b)), even_pt, pt) else: odd_pt = nat_as_odd(int_n) pt = apply_theorem('nat_power_interval_neg_odd_open', auto.auto_solve(real_nonpos(b)), odd_pt, pt) elif is_mem_closed(pt): # Closed interval containing 0 t = pt.prop.arg1 assm1 = hol_set.mk_mem(t, real.closed_interval(a, Real(0))) assm2 = hol_set.mk_mem(t, real.closed_interval(Real(0), b)) pt1 = get_nat_power_bounds(ProofTerm.assume(assm1), n).implies_intr(assm1) pt2 = get_nat_power_bounds(ProofTerm.assume(assm2), n).implies_intr(assm2) x = Var('x', RealType) pt = apply_theorem('split_interval_closed', auto.auto_solve(real.less_eq(a, Real(0))), auto.auto_solve(real.less_eq(Real(0), b)), pt1, pt2, pt, inst=Inst(x=t, f=Lambda(x, x**n))) subset_pt = interval_union_subset(pt.prop.arg) pt = apply_theorem('subsetE', subset_pt, pt) elif is_mem_open(pt): # Open interval containing 0 t = pt.prop.arg1 assm1 = hol_set.mk_mem(t, real.open_interval(a, Real(0))) assm2 = hol_set.mk_mem(t, real.ropen_interval(Real(0), b)) pt1 = get_nat_power_bounds(ProofTerm.assume(assm1), n).implies_intr(assm1) pt2 = get_nat_power_bounds(ProofTerm.assume(assm2), n).implies_intr(assm2) x = Var('x', RealType) pt = apply_theorem('split_interval_open', auto.auto_solve(real.less_eq(a, Real(0))), auto.auto_solve(real.less_eq(Real(0), b)), pt1, pt2, pt, inst=Inst(x=t, f=Lambda(x, x**n))) subset_pt = interval_union_subset(pt.prop.arg) pt = apply_theorem('subsetE', subset_pt, pt) else: raise NotImplementedError return norm_mem_interval(pt)
def real_nonpos(a): return real.less_eq(a, Real(0))
def real_neg(a): return real.less(a, Real(0))
def real_nonneg(a): return real.greater_eq(a, Real(0))
def real_pos(a): return real.greater(a, Real(0))
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