Number theory
Ivy is based on the idea that the automated parts of a proof should be expressed in a decidable logical fragment so that the automated prover will behave in a more predictable and stable way. Ivy’s fragment checker provides feedback when a proof goal falls outside the decidable fragment to help in fixing the problem. Typically, to stay within the decidable realm, we break the proof into lemmas, or we manually instantiate quantifiers.
There are cases, however, where this becomes difficult. Ivy checks
that proof obligations fall into the FAU fragment, which is
decidable if the theories used are decidable for formulas without
quantifiers. This holds true for the theory of linear integer
arithmetic (LIA). A formula is on LIA if in every term of the form
X*Y
, either X
or Y
is a numeric constant. If, however, we
allow non-linear terms, then validity of even quantifier-free
formulas is undecidable.
The fragment checker does not reject non-linear formulas in FAU. This is because Z3 can sometimes provide proofs about non-linear arithmetic and manually reducing proofs about arithmetic to linear subgoals using axioms is very tedious. This mean is that, for non-linear proof goals, we have to accept the fact the Z3 will be unpredictable and unstable. We will frequently get timeouts or incorrect counter-examples. We can mitigate this problem by pushing the arithmetic reasoning into simple generic theorems that don’t need frequent revisions.
In this file, we develop some theorems of elementary number theory, to illustrate how Z3 can be used to help prove simple arithmetic facts. We will cover some facts about divisibility, greatest common divisors and prime numbers and end with a proof that the square root of two is irrational. We will still use the normal strategy of manually instantiating quantifiers that the fragment check complains about. However, in some cases we will also have to provide some “hints” to help Z3 prove simple arithmetic facts.
The theorems developed here are based on the presentation in the online textbook Logic and Proof by Leonardo DeMoura.
First, we instantiate a type nat
corresponding to the natural numbers.
include arith_nat
instance nat : arith_nat
The arith_nat
module comes with some basic facts about arithmetic,
including induction principles.
As an example of a very simple arithmetic fact that Z3 cannot prove (at the time of this writing) consider the following:
theorem [triv1] {
property B:nat > 0
property A:nat * B = C
property A:nat = C / B
}
You can wait quite a while for Z3 to try to prove this triviality.
Try commenting out the proof below to test this out. The problem is
that, since it doesn’t produce a result, we get no help in figuring
out how to make the proof easier. Our approach is simply to go in
small steps. First we divide through by B
, then we regroup terms:
proof {
property (A:nat * B) / B = C / B proof {}
property A:nat * (B/B) = C / B proof {}
}
Each of these two steps can be proved by Z3, as can the conclusion. However, the first step alone is not sufficient. Having finished the proof, we can try deleting some steps to see if Z3 can do without them. In fact, the first step is not necessary (for the version of Z3 used at this writing). Try commenting it out to see. In the proofs below, we’ve deleted the unnecessary hints, on the theory that they might make the proofs more fragile and less re-usable. However, when developing proofs with non-linear arithmetic, it’s better to go in small steps to reduce confusion. Fortunately, although Z3 can’t handle the above very trivial proof, it can often handle substantially more complex proof goals. The point is that, with undecidable theories, we sometimes fail on even very small problems and we need some strategy to recover from these failures.
Quotients and remainders
We prove some useful properties of quotients and remainders of division. First we need a couple of lemmas about multiplication and division to use later.
Lemma (Multiplication expanding).
If X
is positive, then MX >= M
.
Luckily, Z3 gets this by itself.
theorem [mul_expand] {
property X:nat > 0
property M:nat * X >= M
}
Lemma (Division by subtraction).
If M
positive and N >= M
then N:nat/M = (N - M) / M + 1
Proof. Z3 can’t prove this directly. We have to tell it to distribute the division over the subtraction (note, integer division does not always distribute, but it does in this case). This is another example of proceeding in small steps to help out the non-linear arithmetic procedure in Z3.
theorem [div_rec] {
property M:nat > 0 & N:nat >= M
property N:nat/M = (N - M) / M + 1
}
proof {
property (N:nat - M) / M = N/M - M/M proof {}
}
Now we define the notion of quotient and remainder. It is convenient
to create a macro quot_rem
for this, so we don’t have to keep
writing this formula.
Definition (Quotient/Remainder).
A quotient/remainder pair (Q,R) for division of N by M is such that
R < M
and N = Q*M + R
.
relation quot_rem(N:nat,M:nat,Q:nat,R:nat)
explicit definition quot_rem(N,M,Q,R) = R < M & N = Q * M + R
Theorem (Quotient/Remainder existence).
Let N,M
be natural numbers with M > 0
. There exists a quotient/remainder
pair (Q,R)
for division of N by M.
Proof by general induction on N
. If N
is less than M
, then
Q=0
and R=N
. Else let _Q,_R
be the quotient and remainder for
N-M
, by the inductive hypothesis. Then Q=_Q+1
and R=_R
.
To convince the prover of this, we start by unfolding the definition
of quot_rem
, then apply the induction schema with N
substituted for
the induction variable X
in the schema. This is sufficient to match
the conclusion p(X)
in the schema to our proof goal. We must now
prove our property for arbitrary x
, assuming the it holds for all
numbers less than x
(the induction hypothesis). We then split
cases on whether x < M
. This is easy because the quotient must be
0 and the remainder must be x
. We ‘forget’ the induction
hypothesis, since it isn’t needed and it has a quantifier. Z3 does
the rest.
In the remaining case, x >= M
, we subtract M
from x
. The
inductive hypothesis gives us a quotient/remainder pair (_Q,_R)
for x-M
. Adding one to the quotient, we get a quotient/remainder
pair for x
.
explicit property [quot_rem_ex] M:nat > 0 -> exists Q,R. quot_rem(N,M,Q,R)
proof {
unfold with quot_rem
apply nat.gen_ind with X=N
apply exmid with q = (x < M)
proof [pos] {
instantiate with Q=0:nat,R=x
forget ind_hyp
}
proof [neg] {
instantiate ind_hyp with Y = x - M
tactic skolemize
instantiate with Q = _Q + 1, R = _R
}
}
Theorem (Quotient/Remainder uniqueness).
Let N,M
be natural numbers with M > 0
and let Q,R,Q1,R1
be such that
quot_rem(N,M,Q,R)
and quot_rem(N,M,Q1,R1)
. Then Q1=Q
and R1=R
.
Proof. Assume Q1~=Q
and consider the case Q < Q1
. We have R
= M:nat * (Q1-Q) + R1
and, since Q1-Q
is positive, we have M * (Q:nat -Q1) >= M
.
Thus, R1 >= M
, a contradiction. The case Q > Q1
is symmetric. Thus
by contradiction Q1=Q
and hence R1=R
.
Notice in the proof, the cases Q < Q1
and Q > Q1
are not
quite symmetric. This is because in the latter case, Z3 doesn’t
get the fact that M * (Q:nat -Q1) >= M
and we have to prove it
manually. This is characteristic of non-linear arithmetic using
Z3: sometimes there are very simple facts that is doesn’t get,
and there is some guesswork involved in finding the missing fact
that it needs. Although the proof below looks simple, it was
quite time-consuming to produce because Z3 diverged or produced
an “unknown” result without giving good feedback as to the
source of the problem.
explicit property [quot_rem_uniq]
M:nat > 0 & quot_rem(N,M,Q,R) & quot_rem(N,M,Q1,R1) -> Q1=Q & R1=R
proof {
unfold with quot_rem
apply introImp
apply exmid with q = (Q=Q1)
proof [neg] {
apply exmid with q = (Q < Q1)
proof [pos] {
property R = M:nat * (Q1-Q) + R1 proof {}
}
proof [neg] {
property R1 = M:nat * (Q-Q1) + R proof {}
property M * (Q:nat -Q1) >= M proof {apply mul_expand}
}
}
}
Lemma (Division).
If M > 0
then for some R
, quot_rem(N,M,N/M,R)
.
Since R
is unique, this could be seen as a definition of the
division operator. However, we can’t define /
because it is
already interpreted in the natural number theory. Instead, we
prove it from the theory. Unfortunately, Z3 can’t prove it
because of the existential quantifier. The proof follows very
closely the proof of quote_rem_ex
above, where the unknown
quotient Q
is replaced by the term N/M
.
theorem [div_lem] {
property M:nat > 0
property exists R. quot_rem(N,M,N/M,R)
}
proof {
unfold with quot_rem
apply nat.gen_ind with X=N
apply exmid with q = (x < M)
proof [pos] {
instantiate with R = x
forget ind_hyp
}
proof [neg] {
instantiate ind_hyp with Y = x - M
tactic skolemize
property x/M = (x - M) / M + 1 proof {apply div_rec}
instantiate with R = _R
}
}
Greatest Common Divisors and Prime numbers
We start by defining some elementary concepts from number theory, including ‘divisor’ and ‘common divisor’.
Definition (Divisor).
Relation dvds(X,Y)
is true if X is a divisor of Y. We define it
here in terms of the integer division operator instead of saying the
there exists a number N
such that N * Y = Y
. This is convenient
because it lets Z3’s non-linear arithmetic capability do some work
for us. Later, though, we’ll need to introduce the more traditional
definition as an alternative.
Because this definition uses arithmetic and the variables X
and
Y
are implicitly universally quantified, it might lead to proof
goals that are outside the decidable fragment. For this reason, we
make it an ‘explicit’ definition, meaning that it is not used by the
default tactic and we have to instantiate it explicitly.
relation dvds(X:nat,Y:nat)
explicit definition dvds(X,Y) = (X > 0 & (Y/X) * X = Y)
Definition (Common Divisor).
Relation commdiv(Z,X,Y)
is true if Z is a common divisor of X and
of Y.
function commdiv(Z,X,Y) = dvds(Z,X) & dvds(Z,Y)
Definition (GCD).
Definition of greatest common divisor. Z is a GCD of X,Y if Z is a common divisor of X and Y and if no lesser number is so.
Notice that this is also an explicit definition. There are two reasons that using it by default might lead to undecidability. First, it has a quantified symbol under arithmetic. Second, it has a quantifier alternation from type nat to type nat.
relation is_gcd(Z:nat,X:nat,Y:nat)
definition {is_gcd(Z,X,Y) =
commdiv(Z,X,Y) & forall W. commdiv(W,X,Y) -> Z >= W}
Definition (GCD function).
We can now define a function gcd(X,Y)
that returns the GCD when X
and Y are positive and is otherwise undefined. We first prove a
lemma [lem] that, under the assumption, that X
and Y
are
positive, a GCD exists. This is done by unfolding the definition of
GCD and matching the greatest element principle provided by
arith_nat
. This says that any non-empty, upper-bounded set of
natural numbers has a maximum element. As the upper bound n
, we
use X, since a divisor of X cannot be greater than X.
This generates two subgoals: that there exists a common divisor
[prem1] and that there is no common divisor greater than X
[prem2]. The first premise we witness with 1, which is always a
common divisor. The second Z3 can prove by itself. From the lemma,
Z3 can prove the goal. This amounts to just moving the condition ‘X
and Y are positive’ inside the existential quantifier.
By showing the existence of Z, we can define a function gcd(X,Y)
that yields some Z satisfying the property. This gives us the
property X > 0 & Y > 0 -> is_gcd(gcd(X,Y),X,Y)
. When X or Y is
zero, gcd(X,Y)
can be any natural number.
explicit property [gcd_prop]
exists Z. X > 0 & Y > 0 -> is_gcd(Z,X,Y)
named gcd(X,Y)
proof {
theorem [lem] {
property X:nat>0 & Y:nat>0
property exists Z. is_gcd(Z,X,Y)
}
proof {
unfold with is_gcd,commdiv,dvds
apply nat.gep with n=X
proof [prem1] {
instantiate with Z=1:nat
}
}
}
We prove some useful properties of GCD.
Theorem (GCD with self)
If X is positive, the GCD of X and itself is X.
Since X
is a common divisor, the GCD must be greater than or equal
to X
. Since no divisor of X
is greater that X
, X
must be the
GCD. To get Z3 to prove this, we instantiate the defining property
of GCD. Unfolding the definitions of is_gcd
, commdiv
and
dvds
, we get a formula of arithmetic saying that every divisor W
of X
is <= the GCD. In particular, by plugging in X
for W
, we
get that the CGD is <= X. Now the proof goal is
quantifier-free. Amazingly, Z3 can prove it with no further help.
However, notice that it was necessary to instantiate W
manually,
to avoid applying interpreted arithmetic operators to a quantified
variable.
explicit property [gcd_self] X > 0 -> gcd(X,X) = X
proof {
instantiate [g] gcd_prop with X=X, Y=X
unfold g with is_gcd,commdiv,dvds
instantiate g with W=X
}
Theorem (GCD symmetric).
If X and Y are positive, then gcd(X,Y) = gcd(Y,X)
.
This follows from commutativity of the ‘and’ operator in
the definition of is_gcd
. We don’t have to unfold the
definition of dvds
.
explicit property [gcd_symm]
forall X,Y. X>0 & Y>0 -> gcd(X,Y) = gcd(Y,X)
proof {
tactic skolemize
instantiate [g1] gcd_prop with X=_X, Y=_Y
instantiate [g2] gcd_prop with X=_Y, Y=_X
unfold g1 with is_gcd,commdiv
unfold g2 with is_gcd,commdiv
}
Definition (prime number).
A natural number X > 1
is prime if its divisors are only one and
itself.
relation prime(X:nat)
explicit definition prime(X) = X > 1 & forall Y. dvds(Y,X) -> Y=1 | Y=X
Two positive numbers are ‘co-prime’ if their GCD is one.
Lemma (Prime/Coprime).
If N>0
is a natural number and P
is a prime number, then either
N
and P
are coprime or P
divides N
.
To prove this, we instantiate the GCD property, the definition
is_gcd
, the definition of prime number and the definition of
common divisor. The idea is that since gcd(N,P)
divides P
and
P
is prime, it must equal 1
or P
. In the first case we have
our conclusion directly. in the second case, we have the
dvds(P,N)
by the definition of common divisor.
theorem [lemma_prime_coprime] {
property N:nat > 0
property [prem] prime(P)
property gcd(N,P) = 1 | dvds(P,N)
}
proof {
unfold prem with prime
instantiate prem with Y=gcd(N,P)
instantiate [gp] gcd_prop with X=N,Y=P
unfold gp with is_gcd
instantiate gp with W = N
instantiate commdiv with Z=gcd(N,P),X=N,Y=P
}
Lemma (Divisor, alternate definition).
Natural number X
is a divisor of Y
iff X
is positive and
there exists a Z
such that X * Z = Y
.
This could be considered the definition of dvds
. Here, we prove it as a lemma.
For the forward implication, we give Y/X
as the witness for Z
, and unfold
the definition of divides. Z3 can do the rest.
In the reverse case, we have to prove (Y / X) * X = Y
from Z * X
= Y
. Sadly, Z3 can’t do this. As a first step, we divide through by X
and
regroup to get _Z * (_X/_X) = _Y/_X
. Z3 can do the rest (i.e., substituting
Y/X
for Z
to get the conclusion).
explicit property [dvds_alt] forall X,Y. dvds(X,Y) <-> (X > 0 & exists Z. Z * X = Y)
proof {
tactic skolemize
apply introIff
proof [fwd] {
instantiate with Z = _Y/_X
unfold prem with dvds
}
proof [rev] {
tactic skolemize
unfold with dvds
property _Z * (_X/_X) = _Y/_X proof {}
}
}
The next lemma is the basis for Euclid’s algorithm for computing the
GCD. That is, provided N > M
, gcd(N,M)
is preserved by
subtracting M
from N
.
Lemma (GCD/Euclid).
If N>M>0
then gcd(M,N) = gcd(M,N-M)
We start by proving that the common divisors of M
and N
are exactly
the common divisors of M
and M-N
. The forward and reverse directions of this
bi-implication are similar, but not quite identical. In the forward case, suppose
M = A * Z
and N = B * Z
. Then N-M = Z * (B-A)
. Thus, Z is a common divisor of
M
and N-M
.
Notice that in the formal proof, something funny is happening at line
[*]
. That is, we take the premise commdiv(Z,M,N)
and unfold
commdiv
to get dvds(Z,M) & dvds(A,N)
. Then we unfold with
dvds_alt
which is dvds(X,Y) <-> (X > 0 & exists Z. Z * X =
Y)
. Since there are two instances of dvds
in our formula, we
would get two quantified subformulas, exists Z_a. Z_a * Z = M
and
exists Z_b. Z_b * Z = N
where the bound variable Z
in dvds_alt
has been renamed respectively to Z_a
and Z_b
to avoid capturing
the existing variable Z
. There is nothing logically wrong with
this, but the two generated names Z_a
and Z_b
(corresponding to
A
and B
above) might be hard to predict and might later change
if the proof context changes. For this reason, it’s best to avoid
generated names. In the unfold
command, we give two
alpha-renamings <A/Z>
and <B/Z>
. The first is used for the first
(left-most) unfolding of dvds_alt
and the second is used for the
second unfolding. This eliminates the variable name clash and gives
us two distinct names we can refer to later. In particular, after Skolemization,
these become _A
and _B
, which we can use to instantiate the existentials in
the conclusion, at line [**]
.
For the reverse direction of the proof, suppose M = A * Z
and N-M
= B * Z
. Then N = Z * (B+A)
. Thus, Z is a common divisor of M
and N-M
.
Once we know the common divisors are the same, we can just unfold the definition of GCD in terms of common divisors and let Z3 do the rest of the proof, which is purely first-order reasoning.
explicit property [gcd_euclid] N > M & M > 0 -> gcd(M,N) = gcd(M,N-M)
proof {
apply introImp
property [lem] forall Z. (commdiv(Z,M,N) <-> commdiv(Z,M,N-M))
proof {
tactic skolemize
apply introIff
proof [fwd] {
unfold prem with commdiv,dvds_alt<A/Z><B/Z> # [*]
unfold with commdiv,dvds_alt<A1/Z><B1/Z>
tactic skolemize
instantiate with B1 = (_B - _A), A1 = _A # [**]
}
proof [rev] {
unfold prem with commdiv,dvds_alt<A/Z><B/Z>
unfold with commdiv,dvds_alt<A1/Z><B1/Z>
tactic skolemize
instantiate with B1 = (_B + _A), A1 = _A
}
}
instantiate [g1] gcd_prop with X=M,Y=N
instantiate [g2] gcd_prop with X=M,Y=N-M
unfold g1 with is_gcd
unfold g2 with is_gcd
}
We can use the above to prove Bezout’s lemma, an important fact about GCD’s that we will apply to help us prove facts about prime factorizations.
Theorem (Bezout’s lemma).
If S,T
are positive, then there exist A,B
such that A*S-B*T =
gcd(S,T)
.
Proof. By induction over max(S,T)
using the GCD/Euclid lemma.
To prove this, we first restate the theorem with an additional
premise M = max(S,T)
. We can then match the general induction
schema with the induction variable X=M
. We then, in effect,
Skolemize the quantifiers on S,T
using the introA
rule and we
split cases on whether S > T
, S=T
or S<T
. In the first case,
we take the inductive hypothesis for gcd(S-T,T)
. That is, since
S>T
we know that max(S-T,T) < max(S,T)
, so the theorem holds by
induction on max(S,T)
. There is a bit of a technicality here, as
we have to plug in max(S,S-T)
as the induction parameter Y
of
the induction hypothesis. Now by the GCD/Euclid result, we get the
theorem by plugging in A+B
for B
.
In the case S=T
, the assignment A=1,B=0
proves the theorem and
we can forget the induction hypothesis (this is essentially the
termination case for Euclid’s algorithm). Finally, the case S > T
is symmetric to S < T
.
Having proved the lemma for all values of max(S,T)
, we eliminate
the induction variable M
by instantiating our lemma with
M=max(S,T)
(i.e., witnessing the fact that every pair M,T
has a
max).
explicit property [bezout_lemma]
S:nat > 0 & T:nat > 0 -> exists A,B. A*S - B*T = gcd(S,T)
proof {
property [bezout_lemma_pre]
forall S:nat,T. S > 0 & T > 0 & M = (S if S > T else T)
-> exists A,B. A*S - B*T = gcd(S,T)
proof {
apply nat.gen_ind with X=M
apply introA<_S/x>
apply introA<_T/x>
apply exmid with q = (_S >= _T)
assume gcd_symm
proof [pos] {
apply exmid with q = _S > _T
proof [pos] {
instantiate ind_hyp with S=_S-_T, T=_T, Y = _S-_T if _S-_T > _T else _T
instantiate gcd_euclid with M=_T, N=_S
tactic skolemize
instantiate with A = _A, B = _A+_B
}
proof [neg] {
instantiate gcd_self with X=_S
instantiate with A = 1:nat , B = 0:nat
forget ind_hyp
}
}
proof [neg] {
instantiate ind_hyp with S=_S, T=_T-_S, Y = _T-_S if _T-_S > _S else _S
instantiate gcd_euclid with M=_S, N=_T
tactic skolemize
instantiate with A = _A+_B, B = _B
}
}
instantiate bezout_lemma_pre with S=S,T=T,M = (S if S > T else T)
tactic skolemize
instantiate with A=_A,B=_B
}
Here is a useful consequence of Bezout’s lemma.
Lemma (Coprime/Product).
For any positive P
, N
and M
, if N
and P
are co-prime and P
divides N * M
then P
divides M
.
Proof. By Bezout’s lemma, we have gcd(T,P) = A*T - B*P
. Now assume
that P*_Z=N*M
. It follows that P*(A*_Z-B*M) = M
.
theorem [coprime_product] {
property N:nat > 0 & P:nat > 0 & M:nat > 0
property gcd(N,P) = 1
property [prem] dvds(P,N*M)
property dvds(P,M)
}
proof {
instantiate bezout_lemma with S=N, T=P
tactic skolemize
unfold prem with dvds_alt
unfold with dvds_alt
tactic skolemize
instantiate with Z = _A * _Z - _B * M
}
Also, using Bezout’s lemma, we can show the following.
Lemma (Divisor/GCD).
If a
and b
are positive, and if c
is a common divisor of
a,b
, then c
is a divisor of gcd(a,b)
.
Proof. By Bezout’s lemma, we have gcd(a,b) = A*a - B*b
. Moreover,
we have C * c = a
and D * c = b
. It follows that `c * (_A * _Z
- _B * _Z_a) = gcd(a,b)`.
theorem [dvds_gcd] {
individual a : nat
individual b : nat
individual c : nat
property a > 0 & b > 0
property [prem1] dvds(c,a)
property [prem2] dvds(c,b)
property dvds(c,gcd(a,b))
}
proof {
instantiate bezout_lemma with S=a, T=b
unfold prem1 with dvds_alt<C/Z>
unfold prem2 with dvds_alt<D/Z>
unfold with dvds_alt
tactic skolemize
instantiate with Z = _A * _C - _B * _D
}
A consequence the Coprime/Product lemma is that if a prime divides a product, it divides one of the factors.
Lemma (Prime/Product).
For any prime P
and N
and M
, if P
divides N * M
then P
divides N
or M
.
Proof. Use the Prime/Coprime lemma, the Coprime/Product theorem and the definition of prime.
Something subtle happens here in the instantiation of
lemma_prime_coprime
. That is, we use premise prem1
of our
theorem to supply premise prem
of lemma_prime_coprime
, saying
that P
is prime. As an alternative to this, we could have unfolded the
definition of prime in lemma_prime_coprime
. This way can sometimes work out
better, however, if the definition contains a quantifier.
Generally speaking, if you want to use a premise you already have to supply a premise of a schema you are instantiating, you should state this in the substitution.
theorem [prime_dvds_product] {
property N:nat > 0
property M:nat > 0
property [prem1] prime(P)
property [prem2] dvds(P,M*N)
property dvds(P,M) | dvds(P,N)
}
proof {
instantiate lemma_prime_coprime with P=P,N=N,prem=prem1
instantiate coprime_product with P=P,M=M,N=N
unfold prem1 with prime
}
Squares and square roots
Now we will use our properties of primes and GCD to prove a fact known to the Pythagorean School of ancient Greece: no rational number is a square root of two.
First, for convenience, we define the squaring function:
Definition (Squaring).
function squared(X:nat) : nat
explicit definition squared(X) = X * X
This is a useful special case of the Prime/Product lemma that we’ll use twice in the proof:
Lemma (Prime/Square).
If a prime divides squared(A)
then it divides A
.
Proof. Unfold squared
and apply the Prime/Product lemma.
theorem [prime_square] {
individual a : nat
individual b : nat
property [prem1] dvds(b,squared(a))
property prime(b)
property dvds(b,a)
}
proof {
unfold prem1 with squared
instantiate prime_dvds_product with P = b, N=a, M=a
}
Now we’re ready to prove that there is no rational square root of 2.
Actually, we prove the closet thing we can in integer arithmetic,
that is, there is no reduced fraction a/b
such that squared(a/b)
= 2
. We state this as follows:
Theorem (Square root of 2 irrational).
Given positive natural numbers a,b
such that gcd(a,b) = 1
, it is not the
case that squared(a) = 2 * squared(b)
.
Proof. 2 is prime. Moreover, 2 is a divisor of a
, witnessed by squared(b)
.
By the Prime/Square lemma, it follows that 2 is a divisor of a
, thus there is a _Z
such that _Z * 2 = a
. Thus, squared(a) = 4 * squared(_Z) = 2 * squared(b)
.
By the Prime/Squared lemma, 2 is a divisor of b
. This contradicts gcd(a,b) = 1
.
In the formal proof, we took three steps in the above argument as
lemmas: 2 is prime, squared(a)
is even, and squared(a) = 4 *
squared(_Z)
. The proof of these lemmas and the final result was
done by instantiating properties and definitions. This still left a
fair amount of reasoning to do. For example, to get the conclusion
from squared(a) = 4 * squared(_Z)
we need to apply transitivity of
equality, divide through by two and apply the definition of
dvds
. This was done automatically by Z3. Finding the needed steps in the proof
can be a process of trial and error. If we failed to prove a lemma, we break it into
smaller steps.
Also, notice that we used an ‘isolate’ to help structure the proof.
This allowed us to say in the ‘with’ clause that the proof of all
the subgoals should apply the full definition of dvds
. This is
safe in terms of decidability because the definition has no
quantifiers, and we only apply dvds
to ground terms.
isolate sqrt2irrat_iso = {
theorem [sqrt2irrat] {
individual a : nat
individual b : nat
property a > 0 & b > 0
property [co] gcd(a,b) = 1
property squared(a) = 2 * squared(b)
property false
}
proof {
property [prime2] prime(2) proof {
unfold with prime,commdiv,dvds
}
property [a2_even] dvds(2,squared(a)) proof {
unfold with dvds_alt
instantiate with Z=squared(b)
}
instantiate [dp1] prime_square with a=a,b=2:nat,prem1=a2_even
unfold dp1 with dvds_alt
tactic skolemize
property squared(a) = 4 * squared(_Z) proof {unfold with squared}
instantiate [dp2] prime_square with a=b,b=2:nat
instantiate dvds_gcd with a=a,b=b,c=2:nat
}
} with nat,dvds