Search code examples
coq

Coq: Nested(?) subtype defining nonzero rational numbers and their reciprocals


I tried to define the reciprocal of a nonzero rational number, imitating the answers in my another question. I tried to delay the proof, but it seems that I misunderstood.

The following is my code:

1) Integers: define z (= a - b) as a pair (a, b) which is in a setoid with the equiv rel (a, b) ~ (c, d) <-> a + d = b + c

Definition Z_eq (z w: integer): Prop :=
  match z with
  | (z1, z2) =>
    match w with
    | (w1, w2) => z1 + w2 = z2 + w1
    end
  end.

Add Parametric Relation:
  integer Z_eq
  reflexivity proved by Z_refl
  symmetry proved by Z_symm
  transitivity proved by Z_tran
  as Z. (** proved; omitting the proof *)

(** addition, subtraction, negation, multiplication are well-defined as a morphism *)
Add Morphism Z_plus with signature Z_eq ==> Z_eq ==> Z_eq as Z_plus_morph.
(** etc. *)

2) Positive integers: {n : nat | 0 <? n }

(** as in answers of the previous question *)
Local Coercion is_true : bool >-> Sortclass.
Definition Z_pos: Set := {n : nat | 0 <? n }.

Definition Z_pos__N (p: Z_pos): nat := proj1_sig p.
Coercion Z_pos__N : Z_pos >-> nat.

Definition Z_pos__Z (p: Z_pos): integer := (proj1_sig p, 0).
Coercion Z_pos__Z : Z_pos >-> integer.

Definition Z_pos_plus : Z_pos -> Z_pos -> Z_pos.
  intros [x xpos%Nat.ltb_lt] [y ypos%Nat.ltb_lt].
  refine (exist  _ (x + y) _).
  now apply Nat.ltb_lt, Nat.add_pos_pos.
Defined.

Definition Z_pos_mult : Z_pos -> Z_pos -> Z_pos.
  intros [x xpos%Nat.ltb_lt] [y ypos%Nat.ltb_lt].
  refine (exist  _ (x * y) _).
  now apply Nat.ltb_lt, Nat.mul_pos_pos.
Defined.

Lemma Z_pos_mult_compat: forall p q: Z_pos, Z_pos__N (Z_pos_mult p q) = Z_pos__N p * Z_pos__N q.
Proof. now intros [x xpos] [y ypos]. Qed.

Definition ZP1: Z_pos.
  refine (exist  _ 1 _). easy.
Defined.

3) Nonzero integers: {z : integer | z <Z> Z0}

(** nonzero integer *)
Definition Z_nonzero: Set := {z : integer | z <Z> Z0}.

(* abs value of a nonzero integer as an obj of type Z_pos *)
Definition Z_nonzero_abs__Z_pos (z: Z_nonzero): Z_pos.
  destruct z as [[z1 z2] z3].
  exists ((z1 - z2) + (z2 - z1))%nat.
Admitted. (** proof ommited *)

(* multiplication b/w two nonzero integers *)
Definition Z_nonzero_mult (z w: Z_nonzero): Z_nonzero.
  exists (proj1_sig z *Z proj1_sig w).
Admitted.

(* sign of n - m *)
Definition N_sgn_diff__Z (n m: nat): integer :=
  if n >? m then Z1 else if n =? m then Z0 else -Z Z1.

(* sign of n - m *)
Definition Z_nonzero_sgn__Z (z: Z_nonzero): Z_nonzero.
  destruct z as [[z1 z2] z3].
  exists (N_sgn_diff__Z z1 z2).
Admitted.

4) Rational numbers: ( a // b ) (a: integer, b: Z_pos)

Inductive rational: Type :=
| prerat : integer -> Z_pos -> rational.

Notation "( x '//' y )" := (prerat x y).

(* equality *)
Definition Q_eq (p q: rational): Prop :=
  match p with
  | (p1 // p2) =>
    match q with
    | (q1 // q2) => (Z_mult p1 (Z_pos__Z q2)) =Z= (Z_mult (Z_pos__Z p2) q1)
    end
  end.

(* numerator of a rational *)
Definition Q_numerator (q: rational) :=
  match q with
  | (iq // rq) => iq
  end.

(* nonzero rationals *)
Definition Q_nonzero: Set := {q : rational | Q_numerator q <Z> Z0}.

(* numerator and denominator *)
Definition Q_nonzero_numerator (q: Q_nonzero): Z_nonzero.
  destruct q as [[q1 q2] q3].
  exists q1.
  simpl in q3. apply q3.
Qed.

Definition Q_nonzero_denominator (q: Q_nonzero): Z_pos.
  destruct q as [[q1 q2] q3].
  exact q2.
Qed.

Definition Q_recip (q: Q_nonzero): Q_nonzero.
  exists ( proj1_sig (Z_nonzero_mult (Z_nonzero_sgn__Z (Q_nonzero_numerator q)) (Z_pos__Z_nonzero (Q_nonzero_denominator q))) // Z_nonzero_abs__Z_pos (Q_nonzero_numerator q) ).
Admitted. (* proved. *)

(* inherited equality *)
Definition Q_nonzero_eq (p q: Q_nonzero): Prop := (proj1_sig p) =Q= (proj1_sig q).

Add Morphism Q_recip with signature Q_nonzero_eq ==> Q_nonzero_eq as Q_recip_morph.
Proof. (* well-definedness of Q_recip *)
  destruct x, y.
  unfold Q_nonzero_eq. simpl. intros.
  unfold Q_recip. (* <----- unfolded result was very long and complex; not     simplified after simpl. *)
Abort.

I think this definitions are somewhat complex, but it was my best try... Now, how can I prove the consistency of the morphism Q_recip?

(View full code)


Solution

  • I think you forgot to add the definition of Q_nonzero in your development, so I cannot run your code. However, I believe that the issue is that you have used Qed instead of Defined in some of your definitions. This question discusses the issue in more detail. (In any case, as Emilio points out, it is usually better to avoid definitions in proof mode, as they tend to be harder to reason about, as you've probably noticed.)

    A note on style: your development mimics common definitions of number systems from first principles in set theory, where we often use equivalence classes to define the integers, the rational numbers, etc. Though elegant, this approach is often inconvenient to use in Coq. The main issue is that, because of the lack of proper quotient types, you are forced to use setoids everywhere, which prevents you from using Coq's much more convenient equality operator =. It is simpler to write these definitions as plain datatypes. For instance, you can define the integers as

    Inductive int :=
    | Posz : nat -> int
    | Negz : nat -> int.
    

    where Posz represents the canonical injection from natural numbers into integers, and Negz maps the natural number n to the integer - n - 1. This is the approach followed in the mathematical components library. Of course, the standard library also has its own definition of the integers (here), so you do not need to replicate it. Defining the rationals is more complicated, but it can still be done: in mathematical components, they are defined as pairs of coprime integers (num, den) such that den > 0 and num.