Library RelationAlgebra.fhrel
Require Import Setoid Morphisms.
Require Import kat normalisation rewriting kat_tac comparisons rel relalg boolean.
From mathcomp Require Import all_ssreflect.
Set Implicit Arguments.
Unset Printing Implicit Defensive.
Unset Strict Implicit.
Set Bullet Behavior "Strict Subproofs".
Require Import kat normalisation rewriting kat_tac comparisons rel relalg boolean.
From mathcomp Require Import all_ssreflect.
Set Implicit Arguments.
Unset Printing Implicit Defensive.
Unset Strict Implicit.
Set Bullet Behavior "Strict Subproofs".
fhrel: The model of finite heterogeneous relations
#[export] Instance: Proper (iff ==> iff ==> iff ==> iff) and3. firstorder. Qed.
#[export] Instance: Proper (iff ==> iff ==> iff ==> iff ==> iff) and4. firstorder. Qed.
#[export] Instance: Proper (iff ==> iff ==> iff ==> iff ==> iff ==> iff) and5. firstorder. Qed.
#[export] Instance: Proper (iff ==> iff ==> iff ==> iff) or3. firstorder. Qed.
#[export] Instance: Proper (iff ==> iff ==> iff ==> iff ==> iff) or4. firstorder. Qed.
Ltac to_prop :=
repeat first
[ setoid_rewrite <- (rwP andP)
| setoid_rewrite <- (rwP orP)
| setoid_rewrite <- (rwP and3P)
| setoid_rewrite <- (rwP existsP)
| setoid_rewrite <- (rwP forallP)
| setoid_rewrite <- (rwP eqP)
|
setoid_rewrite <- (rwP implyP)].
Use phantom types to trigger inference of finType structures
Section FHRelType.
Variables A B : finType.
Definition fhrel_type := A → B → bool.
Definition fhrel_of of phant A & phant B := fhrel_type.
End FHRelType.
Notation "{ 'fhrel' A & B }" := (fhrel_of (Phant A) (Phant B))
(at level 0, format "{ 'fhrel' A & B }") : type_scope.
Variables A B : finType.
Definition fhrel_type := A → B → bool.
Definition fhrel_of of phant A & phant B := fhrel_type.
End FHRelType.
Notation "{ 'fhrel' A & B }" := (fhrel_of (Phant A) (Phant B))
(at level 0, format "{ 'fhrel' A & B }") : type_scope.
Heterogeneous binary relations hrel X Y (i.e., X → Y → Prop) are
already declared as a model of relation algebra in the module
rel. Thus, we only need to provide the model for finite and
decidable relations between finite types {fhrel A & B}
Lattice Operations
Section LatticeOps. Variables (A B : finType) (e1 e2 : {fhrel A & B}). Implicit Types (x : A) (y : B). Definition fhrel_weq := e1 =2 e2. Definition fhrel_leq := forall x y, e1 x y -> e2 x y. Definition fhrel_cup := fun x y => e1 x y || e2 x y. Definition fhrel_cap := fun x y => e1 x y && e2 x y. Definition fhrel_neg := fun x y => ~~ (e1 x y). Definition fhrel_top := fun x y => true. Definition fhrel_bot := fun x y => false. End LatticeOps.
Canonical Structure fhrel_lattice_ops A B :=
lattice.mk_ops {fhrel A & B} lattice.leq weq cup cap neg bot top.
Arguments lattice.leq : simpl never.
Arguments lattice.weq : simpl never.
Obtain the lattice laws using the standard powerset construction
#[export] Instance fhrel_lattice_laws A B:
lattice.laws (BL+STR+CNV+DIV) (fhrel_lattice_ops A B) := pw_laws _.
Lemma fhrel_leq_dec A B (e1 e2 : {fhrel A & B}) : decidable (e1 ≦ e2).
Proof.
apply: (@decP _ [∀ x, ∀ y, e1 x y ==> e2 x y]).
apply: (equivP idP); to_prop; reflexivity.
Qed.
Lemma fhrel_weq_dec A B (e1 e2 : {fhrel A & B}) : decidable (e1 ≡ e2).
Proof.
apply: (@decP _ [∀ x, ∀ y, e1 x y == e2 x y]).
apply: (equivP idP); to_prop; reflexivity.
Qed.
lattice.laws (BL+STR+CNV+DIV) (fhrel_lattice_ops A B) := pw_laws _.
Lemma fhrel_leq_dec A B (e1 e2 : {fhrel A & B}) : decidable (e1 ≦ e2).
Proof.
apply: (@decP _ [∀ x, ∀ y, e1 x y ==> e2 x y]).
apply: (equivP idP); to_prop; reflexivity.
Qed.
Lemma fhrel_weq_dec A B (e1 e2 : {fhrel A & B}) : decidable (e1 ≡ e2).
Proof.
apply: (@decP _ [∀ x, ∀ y, e1 x y == e2 x y]).
apply: (equivP idP); to_prop; reflexivity.
Qed.
Enable rewriting of lattice.leq and weq using Ssreflect's rewrite tactic
#[export] Instance leq_rewrite_relation ops : RewriteRelation (@lattice.leq ops).
Qed.
#[export] Instance weq_rewrite_relation ops : RewriteRelation (@weq ops).
Qed.
Qed.
#[export] Instance weq_rewrite_relation ops : RewriteRelation (@weq ops).
Qed.
Monoid Operations
Section MonoidOps.
Variables (A B C : finType).
Definition fhrel_dot (e1 : {fhrel A & B}) (e2 : {fhrel B & C}) : {fhrel A & C} :=
fun x y ⇒ [∃ z, e1 x z && e2 z y].
Definition fhrel_cnv (e : {fhrel A & B}) : {fhrel B & A} :=
fun x y ⇒ e y x.
Definition fhrel_str (e : {fhrel A & A}) : {fhrel A & A} :=
connect e.
Definition fhrel_one : {fhrel A & A} :=
@eq_op A.
Definition fhrel_rdv (e1 : {fhrel B & A}) (e2 : {fhrel C & A}) : {fhrel C & B} :=
fun j i ⇒ [∀ k, e1 i k ==> e2 j k].
Definition fhrel_ldv (e1 : {fhrel A & B}) (e2 : {fhrel A & C}) : {fhrel B & C} :=
fun i j ⇒ [∀ k, e1 k i ==> e2 k j].
Definition fhrel_inj (p : pred A) :=
fun x y ⇒ (x == y) && p x.
End MonoidOps.
Definition fhrel_itr A (e : {fhrel A & A}) : {fhrel A & A} :=
fhrel_dot e (connect e).
Canonical fhrel_monoid_ops :=
monoid.mk_ops finType fhrel_lattice_ops
fhrel_dot
fhrel_one
fhrel_itr
fhrel_str
fhrel_cnv
fhrel_ldv
fhrel_rdv.
Ensure that the fhrel_× definitions simplify, given enough arguments.
Arguments fhrel_dot {_ _ _} _ _ /.
Arguments fhrel_cnv {_ _} _ _ /.
Arguments fhrel_one {_} _ _ /.
Arguments fhrel_str {_} _ _ /.
Arguments fhrel_ldv {_} _ _ /.
Arguments fhrel_rdv {_} _ _ /.
Arguments fhrel_inj {_} _ _ _ /.
Lemma connect_iter (A : ob fhrel_monoid_ops) (e : fhrel_monoid_ops A A) (x y : A):
connect e x y ↔ (∃ u : nat, rel.iter A e u x y).
Proof.
split.
- case/connectP ⇒ p pth_p lst_p. ∃ (size p).
elim: p x pth_p lst_p ⇒ /= [x _ → // | z p IHp x /andP [xz pth_p] lst_p].
∃ z ⇒ //. exact: IHp.
- case ⇒ n. elim: n x ⇒ /= [x <- // |n IHn x [z xz H]].
exact: connect_trans (connect1 xz) (IHn _ _).
Qed.
Arguments fhrel_cnv {_ _} _ _ /.
Arguments fhrel_one {_} _ _ /.
Arguments fhrel_str {_} _ _ /.
Arguments fhrel_ldv {_} _ _ /.
Arguments fhrel_rdv {_} _ _ /.
Arguments fhrel_inj {_} _ _ _ /.
Lemma connect_iter (A : ob fhrel_monoid_ops) (e : fhrel_monoid_ops A A) (x y : A):
connect e x y ↔ (∃ u : nat, rel.iter A e u x y).
Proof.
split.
- case/connectP ⇒ p pth_p lst_p. ∃ (size p).
elim: p x pth_p lst_p ⇒ /= [x _ → // | z p IHp x /andP [xz pth_p] lst_p].
∃ z ⇒ //. exact: IHp.
- case ⇒ n. elim: n x ⇒ /= [x <- // |n IHn x [z xz H]].
exact: connect_trans (connect1 xz) (IHn _ _).
Qed.
We obtain the monoid laws using a faithful functor to the hrel model
Definition hrel_of (A B : finType) (e : {fhrel A & B}) : hrel A B := fun x y ⇒ e x y.
Ltac hrel_prop := do ! move ⇒ ?; rewrite /hrel_of /=; to_prop; by firstorder.
Lemma hrel_of_morphism (A B : finType) : morphism (BDL+STR+CNV+DIV) (@hrel_of A B).
Proof.
split; try done; try hrel_prop.
move ⇒ e1 e2 H x y. apply/eq_bool_iff. exact: H.
Qed.
Lemma hrel_of_functor : functor (BDL+STR+CNV+DIV) hrel_of.
Proof.
apply (@Build_functor (BDL+STR+CNV+DIV) fhrel_monoid_ops hrel_monoid_ops id hrel_of).
all: try done. all: try hrel_prop.
- apply: hrel_of_morphism.
- move ⇒ _ A e x y. rewrite /hrel_of/= /fhrel_itr /hrel_itr /=. to_prop.
setoid_rewrite connect_iter. by firstorder.
- move ⇒ _ A e x y. exact: connect_iter.
Qed.
Lemma fhrel_monoid_laws_BDL: monoid.laws (BDL+STR+CNV+DIV) fhrel_monoid_ops.
Proof.
eapply (laws_of_faithful_functor hrel_of_functor) ⇒ //.
move ⇒ A B e1 e2 H x y. apply/eq_bool_iff. exact: H.
Qed.
#[export] Instance hrel_monoid_laws: monoid.laws (BL+STR+CNV+DIV) fhrel_monoid_ops.
Proof.
case fhrel_monoid_laws_BDL ⇒ ×.
split; try assumption. exact: fhrel_lattice_laws.
Qed.
Ltac hrel_prop := do ! move ⇒ ?; rewrite /hrel_of /=; to_prop; by firstorder.
Lemma hrel_of_morphism (A B : finType) : morphism (BDL+STR+CNV+DIV) (@hrel_of A B).
Proof.
split; try done; try hrel_prop.
move ⇒ e1 e2 H x y. apply/eq_bool_iff. exact: H.
Qed.
Lemma hrel_of_functor : functor (BDL+STR+CNV+DIV) hrel_of.
Proof.
apply (@Build_functor (BDL+STR+CNV+DIV) fhrel_monoid_ops hrel_monoid_ops id hrel_of).
all: try done. all: try hrel_prop.
- apply: hrel_of_morphism.
- move ⇒ _ A e x y. rewrite /hrel_of/= /fhrel_itr /hrel_itr /=. to_prop.
setoid_rewrite connect_iter. by firstorder.
- move ⇒ _ A e x y. exact: connect_iter.
Qed.
Lemma fhrel_monoid_laws_BDL: monoid.laws (BDL+STR+CNV+DIV) fhrel_monoid_ops.
Proof.
eapply (laws_of_faithful_functor hrel_of_functor) ⇒ //.
move ⇒ A B e1 e2 H x y. apply/eq_bool_iff. exact: H.
Qed.
#[export] Instance hrel_monoid_laws: monoid.laws (BL+STR+CNV+DIV) fhrel_monoid_ops.
Proof.
case fhrel_monoid_laws_BDL ⇒ ×.
split; try assumption. exact: fhrel_lattice_laws.
Qed.
In ssreflect /= is used pervasively, so we block the
simplification of relation operators. Unfold selectively by using
rewrite /dot/=
Arguments dot : simpl never.
Arguments cnv : simpl never.
Arguments str : simpl never.
Arguments itr : simpl never.
Arguments lattice.leq : simpl never.
Arguments lattice.weq : simpl never.
Arguments monoid.one : simpl never.
Hint Unfold monoid.one : core.
Arguments cnv : simpl never.
Arguments str : simpl never.
Arguments itr : simpl never.
Arguments lattice.leq : simpl never.
Arguments lattice.weq : simpl never.
Arguments monoid.one : simpl never.
Hint Unfold monoid.one : core.
Lemma hrel_oneE (n : Set) a b : (1 : hrel n n) a b ↔ a = b.
Proof. reflexivity. Qed.
Lemma fhrel_oneE A a b : (1 : {fhrel A & A}) a b = (a == b).
Proof. reflexivity. Qed.
Definition oneE := (hrel_oneE,fhrel_oneE).
Proof. reflexivity. Qed.
Lemma fhrel_oneE A a b : (1 : {fhrel A & A}) a b = (a == b).
Proof. reflexivity. Qed.
Definition oneE := (hrel_oneE,fhrel_oneE).
The following is required since top' x y tries to infer an
hrel instance and then fails with a universe inconsistency as finite
types to not necessarily live in Set
Definition ftop_def (A B : finType) of phant A & phant B :=
(@top (@mor fhrel_monoid_ops A B)).
Notation ftop A B := (ftop_def (Phant A) (Phant B)).
Definition fzero_def (A B : finType) of phant A & phant B :=
(@bot (@mor fhrel_monoid_ops A B)).
Notation fzero A B := (fzero_def (Phant A) (Phant B)).
Definition fone_def (A : finType) of phant A :=
(@one fhrel_monoid_ops A).
Notation fone A := (fone_def (Phant A)).
Arguments ftop_def A B /.
Arguments fzero_def A B /.
Arguments fone_def A /.
(@top (@mor fhrel_monoid_ops A B)).
Notation ftop A B := (ftop_def (Phant A) (Phant B)).
Definition fzero_def (A B : finType) of phant A & phant B :=
(@bot (@mor fhrel_monoid_ops A B)).
Notation fzero A B := (fzero_def (Phant A) (Phant B)).
Definition fone_def (A : finType) of phant A :=
(@one fhrel_monoid_ops A).
Notation fone A := (fone_def (Phant A)).
Arguments ftop_def A B /.
Arguments fzero_def A B /.
Arguments fone_def A /.
Definition fdset: ob fhrel_monoid_ops → lattice.ops := pw_ops bool_lattice_ops.
Canonical Structure fhrel_kat_ops :=
kat.mk_ops fhrel_monoid_ops fdset (@fhrel_inj).
Global Instance fhrel_kat_laws: kat.laws fhrel_kat_ops.
Proof.
split.
- by eapply lower_laws.
- move ⇒ A. by eapply lower_lattice_laws.
- move ⇒ A.
have H : Proper (lattice.leq ==> lattice.leq) (@fhrel_inj A).
{ move ⇒ e1 e2 H x y /=. by case: (_ == _) ⇒ //=. }
split ⇒ //=.
+ move ⇒ x y. rewrite !weq_spec. by intuition.
+ move ⇒ _ f g x y /=. by case: (_ == _); case (f x); case (g x).
+ move ⇒ _ x y /=. by rewrite andbF.
- move ⇒ A x y /=. by rewrite andbT.
- move ⇒ A p q x y /=. rewrite /dot/=.
apply/eq_bool_iff. to_prop. by firstorder;subst.
Qed.
Canonical Structure fhrel_kat_ops :=
kat.mk_ops fhrel_monoid_ops fdset (@fhrel_inj).
Global Instance fhrel_kat_laws: kat.laws fhrel_kat_ops.
Proof.
split.
- by eapply lower_laws.
- move ⇒ A. by eapply lower_lattice_laws.
- move ⇒ A.
have H : Proper (lattice.leq ==> lattice.leq) (@fhrel_inj A).
{ move ⇒ e1 e2 H x y /=. by case: (_ == _) ⇒ //=. }
split ⇒ //=.
+ move ⇒ x y. rewrite !weq_spec. by intuition.
+ move ⇒ _ f g x y /=. by case: (_ == _); case (f x); case (g x).
+ move ⇒ _ x y /=. by rewrite andbF.
- move ⇒ A x y /=. by rewrite andbT.
- move ⇒ A p q x y /=. rewrite /dot/=.
apply/eq_bool_iff. to_prop. by firstorder;subst.
Qed.
Cardinality
For finite relations we obtain cardinality operation by coercing heterogeneous relations to prediactes on pairsDefinition fhrel_pred (aT rT: finType) (e : {fhrel aT & rT}) :=
[pred x : aT × rT | e x.1 x.2].
Canonical fhrelPredType (aT rT: finType) :=
@PredType (aT × rT) ({fhrel aT & rT}) (@fhrel_pred _ _).
Coercion fhrel_pred : fhrel_of >-> simpl_pred.
Section CardinalityBase.
Variables (aT rT : finType).
Implicit Types (e : {fhrel aT & rT}).
Lemma leq_card e e': e ≦ e' → #|e| ≤ #|e'|.
Proof. move ⇒ A. apply: subset_leq_card. apply/subsetP ⇒ x. exact: A. Qed.
Lemma weq_card e e' : e ≡ e' → #|e| = #|e'|.
Proof. rewrite weq_spec (rwP eqP) eqn_leq ⇒ [[A B]]. by rewrite !leq_card. Qed.
Lemma fhrel_card0 : #|fzero aT rT| = 0%N.
Proof. exact: card0. Qed.
Lemma fhrel_cardT : #|ftop aT rT| = #|aT| × #|rT|.
Proof. exact: eq_card_prod. Qed.
Lemma fhrel_card1 : #|fone aT| = #|aT|.
Proof.
rewrite -(on_card_preimset (f := (fun x ⇒ (x,x)))).
- rewrite eq_cardT ?cardT //= ⇒ x. by rewrite !inE oneE eqxx.
- ∃ fst ⇒ //= [[x y]]. by rewrite inE oneE /= ⇒ /eqP→.
Qed.
Lemma card_cnv e : #|e°| = #|e|.
Proof.
rewrite -(on_card_preimset (f := (fun x ⇒ (x.2,x.1)))).
- apply: eq_card ⇒ [[x y]]. by rewrite !inE.
- apply: onW_bij. apply: (Bijective (g := fun x ⇒ (x.2,x.1))); by case.
Qed.
Lemma card_cup e1 e2 : #|e1 + e2| = #|e1| + #|e2| - #|e1 ∩ e2|.
Proof. by rewrite (rwP eqP) -cardUI -addnBA // subnn addn0. Qed.
Lemma card_capL e1 e2 : #|e1 ∩ e2| ≤ #|e1|.
Proof. apply: leq_card. by lattice. Qed.
Lemma card_capR e1 e2 : #|e1 ∩ e2| ≤ #|e2|.
Proof. apply: leq_card. by lattice. Qed.
Definition dom e := [pred x | [∃ y, e x y]].
Definition ran e := [pred y | [∃ x, e x y]].
Lemma card_dom e : #|dom e| = #|e ⋅ ftop rT unit|.
Proof.
rewrite -[in RHS](on_card_preimset (f := (fun x : aT ⇒ (x,tt)))).
- apply: eq_card ⇒ x. rewrite !inE /dot /=.
by apply/eq_bool_iff;to_prop; firstorder.
- apply: onW_bij. by apply: (Bijective (g := fst)) ⇒ [|[? []]].
Qed.
End CardinalityBase.
Lemma ran_dom {A B} {e : {fhrel A & B}} : ran e =i dom e°.
Proof. move ⇒ x. by rewrite !inE. Qed.
Lemma card_ran A B (e : {fhrel A & B}) :
#|ran e| = #|ftop unit _⋅e|.
Proof.
rewrite (eq_card ran_dom) card_dom -card_cnv.
apply: weq_card. rewrite /ftop_def.
by rewrite cnvdot cnvtop cnv_invol.
Qed.
Lemma fhrel_surjective A B (e : {fhrel A & B}) : is_surjective e ↔ ∀ y, ∃ x, e x y.
Proof.
split ⇒ [H y|H y ?].
- case/exists_inP : (H y y (eqxx _)) ⇒ x. by ∃ x.
- move/eqP<-. apply/exists_inP. case: (H y) ⇒ x exy. by ∃ x.
Qed.
Lemma surjective_card A B (e : {fhrel A & B}) :
is_surjective e → #|B| ≤ #|e|.
Proof.
move/fhrel_surjective ⇒ E.
pose f y := (xchoose (E y),y).
have inj_f : injective f by move ⇒ x y [].
rewrite -(card_codom inj_f).
apply: subset_leq_card. apply/subsetP ⇒ [[x y]] /codomP ⇒ [[y'] [-> <-]].
rewrite inE /=. exact: (xchooseP (E y)).
Qed.
Lemma fhrel_injective A B (e : {fhrel A & B}) :
is_injective e ↔ ∀ x x' y, e x y → e x' y → x = x'.
Proof.
rewrite /is_injective; split ⇒ [H x x' y E1 E2|H x x' E].
- apply/eqP. apply: (H x x'). by apply/exists_inP;∃ y.
- apply/eqP. case/exists_inP : E. exact: H.
Qed.
Lemma injective_card A B (e : {fhrel A & B}) :
is_injective e → #|e| ≤ #|B|.
Proof.
move/fhrel_injective ⇒ inj_e.
rewrite -(card_in_imset (f := @snd A B)) ?max_card //.
move ⇒ [x y] [x' y']. rewrite !inE /= ⇒ E1 E2 ?; subst.
by rewrite (inj_e _ _ _ E1 E2).
Qed.
Lemma fhrel_univalent A B (e : {fhrel A & B}) :
is_univalent e ↔ ∀ x y y', e x y → e x y' → y = y'.
Proof.
split ⇒ [/@injective_cnv/fhrel_injective|H].
- rewrite /cnv/= ⇒ H ? ? ?; exact: H.
- rewrite /is_univalent. cnv_switch. rewrite cnv1 cnvdot.
rewrite -/(is_injective _) fhrel_injective ⇒ ? ? ?. exact: H.
Qed.
Lemma univalent_card A B (e : {fhrel A & B}) :
is_univalent e → #|e| ≤ #|A|.
Proof. move ⇒ ?. rewrite -card_cnv. exact: injective_card. Qed.
Lemma total_card A B (e : {fhrel A & B}) :
is_total e → #|A| ≤ #|e|.
Proof. move ⇒ ?. rewrite -card_cnv. exact: surjective_card. Qed.
End FHRel.
Notation ftop A B := (ftop_def (Phant A) (Phant B)).
Notation fzero A B := (fzero_def (Phant A) (Phant B)).
Notation fone A := (fone_def (Phant A)).
Ltac fold_fhrel := ra_fold fhrel_monoid_ops.
Tactic Notation "fold_fhrel" "in" hyp_list(H) := ra_fold fhrel_monoid_ops in H.
Tactic Notation "fold_fhrel" "in" "*" := ra_fold fhrel_monoid_ops in ×.
Note that, subrel e f (which is convertible to e ≦ f if e
and f are homogeneous relations over some finite type) and e =2 f
(which is convertible to e ≡ f even in the heterogeneous case) are
used pervasively in MathComp. Consequently, fold_fhrel can be used
to convert their statements into relation algebra statements. See
below for an examle:
This is connect_sub
Goal ∀ (T : finType) (e e' : rel T), subrel e (connect e') → subrel (connect e) (connect e').
move ⇒ T e e'. fold_fhrel. move ⇒ H. rewrite H. ka. Abort.
move ⇒ T e e'. fold_fhrel. move ⇒ H. rewrite H. ka. Abort.
This is connect_eq