BC2g: Relacje [TODO]


Require Import FunctionalExtensionality Nat Arith Lia.

Require Import List.
Import ListNotations.


W tym rozdziale zajmiemy się badaniem relacji. Poznamy podstawowe rodzaje relacji, ich właściwości, a także zależności i przekształcenia między nimi. Rozdział będzie raczej matematyczny.

Relacje binarne

Zacznijmy od przypomnienia klasyfikacji zdań, predykatów i relacji:
Istnieje jednak zasadnicza różnica między definiowaniem "zwykłych" funkcji oraz definiowaniem relacji: zwykłe funkcje możemy definiować jedynie przez dopasowanie do wzorca i rekurencję, zaś relacje możemy poza tymi metodami definiować także przez indukcję, dzięki czemu możemy wyrazić więcej konceptów niż za pomocą rekursji.

Definition hrel (A B : Type) : Type := A -> B -> Prop.

Najważniejszym rodzajem relacji są relacje binarne, czyli relacje biorące dwa argumenty. To właśnie im poświęcimy ten rozdział, pominiemy zaś relacje biorące trzy i więcej argumentów. Określenia "relacja binarna" będę używał zarówno na określenie relacji binarnych heterogenicznych (czyli biorących dwa argumnty różnych typów) jak i na określenie relacji binarnych homogenicznych (czyli biorących dwa argumenty tego samego typu).

Identyczność relacji


Definition subrelation {A B : Type} (R S : hrel A B) : Prop :=
  forall (a : A) (b : B), R a b -> S a b.

Notation "R --> S" := (subrelation R S) (at level 40).

Definition same_hrel {A B : Type} (R S : hrel A B) : Prop :=
  subrelation R S /\ subrelation S R.

Notation "R <--> S" := (same_hrel R S) (at level 40).

Zacznijmy od ustalenia, jakie relacje będziemy uznawać za "identyczne". Okazuje się, że używanie równości eq do porównywania zdań nie ma zbyt wiele sensu. Jest tak dlatego, że nie interesuje nas postać owych zdań, a jedynie ich logiczna zawartość.
Z tego powodu właściwym dla zdań pojęciem "identyczności" jest równoważność, czyli <->. Podobnie jest w przypadku relacji: uznamy dwie relacje za identyczne, gdy dla wszystkich argumentów zwracają one równoważne zdania.
Formalnie wyrazimy to nieco na około, za pomocą pojęcia subrelacji. R jest subrelacją S, jeżeli R a b implikuje S a b dla wszystkich a : A i b : B. Możemy sobie wyobrażać, że jeżeli R jest subrelacją S, to w relacji R są ze sobą tylko niektóre pary argumentów, które są w relacji S, a inne nie.

Ćwiczenie


Inductive le' : nat -> nat -> Prop :=
| le'_0 : forall n : nat, le' 0 n
| le'_SS : forall n m : nat, le' n m -> le' (S n) (S m).

Udowodnij, że powyższa definicja le' porządku "mniejszy lub równy" na liczbach naturalnych jest tą samą relacją, co le. Być może przyda ci się kilka lematów pomocniczych.


Lemma le_le'_same : le <--> le'.

Uporawszy się z pojęciem "identyczności" relacji możemy przejść dalej, a mianowicie do operacji, jakie możemy wykonywać na relacjach.

Operacje na relacjach


Definition Rcomp
  {A B C : Type} (R : hrel A B) (S : hrel B C) : hrel A C :=
    fun (a : A) (c : C) => exists b : B, R a b /\ S b c.

Definition Rid {A : Type} : hrel A A := @eq A.

Podobnie jak w przypadku funkcji, najważniejszą operacją jest składanie relacji, a najważniejszą relacją — równość. Składanie jest łączne, zaś równość jest elementem neutralnym tego składania. Musimy jednak zauważyć, że mówiąc o łączności relacji mamy na myśli coś innego, niż w przypadku funkcji.

Lemma Rcomp_assoc :
  forall
    (A B C D : Type) (R : hrel A B) (S : hrel B C) (T : hrel C D),
      Rcomp R (Rcomp S T) <--> Rcomp (Rcomp R S) T.

Lemma Rcomp_eq_l :
  forall (A B : Type) (R : hrel A B),
    Rcomp (@Rid A) R <--> R.

Lemma Rcomp_eq_r :
  forall (A B : Type) (R : hrel A B),
    Rcomp R (@Rid B) <--> R.

Składanie funkcji jest łączne, gdyż złożenie trzech funkcji z dowolnie rozstawionymi nawiasami daje wynik identyczny w sensie eq. Składanie relacji jest łączne, gdyż złożenie trzech relacji z dowolnie rozstawionymi nawiasami daje wynik identyczny w sensie same_hrel.
Podobnie sprawa ma się w przypadku stwierdzenia, że eq jest elementem nautralnym składania relacji.

Definition Rinv {A B : Type} (R : hrel A B) : hrel B A :=
  fun (b : B) (a : A) => R a b.

Rinv to operacja, która zamienia miejscami argumenty relacji. Relację Rinv R będziemy nazywać relacją odwrotną do R.

Lemma Rinv_Rcomp :
  forall (A B C : Type) (R : hrel A B) (S : hrel B C),
    Rinv (Rcomp R S) <--> Rcomp (Rinv S) (Rinv R).

Lemma Rinv_eq :
  forall A : Type, Rinv (@Rid A) <--> @Rid A.

Złożenie dwóch relacji możemy odwrócić, składając ich odwrotności w odwrotnej kolejności. Odwrotnością relacji identycznościowej jest zaś ona sama.

Definition Rnot {A B : Type} (R : hrel A B) : hrel A B :=
  fun (a : A) (b : B) => ~ R a b.

Definition Rand {A B : Type} (R S : hrel A B) : hrel A B :=
  fun (a : A) (b : B) => R a b /\ S a b.

Definition Ror {A B : Type} (R S : hrel A B) : hrel A B :=
  fun (a : A) (b : B) => R a b \/ S a b.

Pozostałe trzy operacje na relacjach odpowiadają spójnikom logicznym — mamy więc negację relacji oraz koniunkcję i dysjunkcję dwóch relacji. Zauważ, że operacje te możemy wykonywać jedynie na relacjach o takich samych typach argumentów.
Sporą część naszego badania relacji przeznaczymy na sprawdzanie, jak powyższe operacj mają się do różnych specjalnych rodzajów relacji. Nim to się stanie, zbadajmy jednak właściwości samych operacji.

Definition RTrue {A B : Type} : hrel A B :=
  fun (a : A) (b : B) => True.

Definition RFalse {A B : Type} : hrel A B :=
  fun (a : A) (b : B) => False.

Zacznijmy od relacjowych odpowiedników True i False. Przydadzą się nam one do wyrażania właściwości Rand oraz Ror.


Lemma Rnot_Rnot :
  forall (A B : Type) (R : hrel A B),
    R --> Rnot (Rnot R).

Lemma Rand_assoc :
  forall (A B : Type) (R S T : hrel A B),
    Rand R (Rand S T) <--> Rand (Rand R S) T.

Lemma Rand_comm :
  forall (A B : Type) (R S : hrel A B),
    Rand R S <--> Rand S R.

Lemma Rand_RTrue_l :
  forall (A B : Type) (R : hrel A B),
    Rand RTrue R <--> R.

Lemma Rand_RTrue_r :
  forall (A B : Type) (R : hrel A B),
    Rand R RTrue <--> R.

Lemma Rand_RFalse_l :
  forall (A B : Type) (R : hrel A B),
    Rand RFalse R <--> RFalse.

Lemma Rand_RFalse_r :
  forall (A B : Type) (R : hrel A B),
    Rand R RFalse <--> RFalse.

Lemma Ror_assoc :
  forall (A B : Type) (R S T : hrel A B),
    Ror R (Ror S T) <--> Ror (Ror R S) T.

Lemma Ror_comm :
  forall (A B : Type) (R S : hrel A B),
    Ror R S <--> Ror S R.

Lemma Ror_RTrue_l :
  forall (A B : Type) (R : hrel A B),
    Ror RTrue R <--> RTrue.

Lemma Ror_RTrue_r :
  forall (A B : Type) (R : hrel A B),
    Ror R RTrue <--> RTrue.

Lemma Ror_RFalse_l :
  forall (A B : Type) (R : hrel A B),
    Ror RFalse R <--> R.

Lemma Ror_RFalse_r :
  forall (A B : Type) (R : hrel A B),
    Ror R RFalse <--> R.

To nie wszystkie właściwości tych operacji, ale myślę, że widzisz już, dokąd to wszystko zmierza. Jako, że Rnot, Rand i Ror pochodzą bezpośrednio od spójników logicznych not, and i or, dziedziczą one po nich wszystkie ich właściwości. Fenomen ten nie jest w żaden sposób specyficzny dla relacji i operacji na nich - z pewnością spotkamy się z nim ponownie w nadchodzących rozdziałach.

Relatory, czyli więcej operacji na relacjach (TODO)

Póki co wszystkie operacje na relacjach, które widzieliśmy, pochodziły po prostu od spójników logicznych. Co jednak z operacjami na typach, takimi jak produkt, suma, opcja, typy funkcyjne, etc.? Czy żyją one w odizolowanym świecie, który nie ma zupełnie żadnego związku ze światem relacji? Oczywiście, że nie: mając relacje na jakichś typach, możemy przekształcić je na relację na produkcie czy sumie tych typów w kanoniczny sposób spełniający pewne właściwości. Operacje, które dokonują tego przekształcenia, nazywać będziemy relatorami.

Class Relator (F : Type -> Type) : Type :=
{
  relate : forall {A : Type}, (A -> A -> Prop) -> (F A -> F A -> Prop);
  relate_id : forall {A : Type} (x y : F A), relate (@eq A) x y <-> eq x y;
}.

Przykładem relatora jest option.

#[export]
#[refine]
Instance Relator_option : Relator option :=
{
  relate := fun A R o1 o2 =>
    match o1, o2 with
    | Some a1, Some a2 => R a1 a2
    | None , None => True
    | _ , _ => False
    end;
}.
Proof.
  intros A [a1 |] [a2 |]; only 4: easy.
  - now split; [intros -> | intros [=]].
  - now split; [| congruence].
  - now split; [| congruence ].
Defined.

Możemy też zdefiniować pojęcia birelatora (od łacińskiego prefiksu "bi" oznaczającego "dwa"), czyli dwuargumentowy relator.

Class Birelator (F : Type -> Type -> Type) : Type :=
{
  birelate : forall {A B : Type}, (A -> A -> Prop) -> (B -> B -> Prop) -> (F A B -> F A B -> Prop);
  birelate_id : forall {A B : Type} (x y : F A B), birelate (@eq A) (@eq B) x y <-> eq x y;
}.

#[export]
#[refine]
Instance Birelator_prod : Birelator prod :=
{
  birelate := fun A B RA RB '(a1, b1) '(a2, b2) => RA a1 a2 /\ RB b1 b2;
}.
Proof.
  intros A B p1 p2; split.
  - now destruct p1, p2; cbn; intros [-> ->].
  - now intros ->; destruct p2.
Defined.

Produkty i sumy są przykładami birelatorów.

#[export]
#[refine]
Instance Birelator_sum : Birelator sum :=
{
  birelate := fun A B RA RB x y =>
    match x, y with
    | inl a1, inl a2 => RA a1 a2
    | inr b1, inr b2 => RB b1 b2
    | _ , _ => False
    end;
}.
Proof.
  intros A B [a1 | b1] [a2 | b2]; cbn; [| easy.. |].
  - now split; congruence.
  - now split; congruence.
Defined.

Relacje homogeniczne (TODO)


Definition rel (A : Type) : Type := hrel A A.

Relacje homogeniczne to takie, których wszystkie argumenty są tego samego typu. Warunek ten pozwala nam na wyrażenie całej gamy nowych właściwości, które relacje takie mogą posiadać.
Uwaga terminologiczna: w innych pracach to, co nazwałem Antireflexive bywa zazwyczaj nazywane Irreflexive. Ja przyjąłem następujące reguły tworzenia nazw różnych rodzajów relacji:

Relacje równoważności (TODO)

Relacje zwrotne


Class Reflexive {A : Type} (R : rel A) : Prop :=
{
  reflexive : forall x : A, R x x
}.

Class Irreflexive {A : Type} (R : rel A) : Prop :=
{
  irreflexive : exists x : A, ~ R x x
}.

Class Antireflexive {A : Type} (R : rel A) : Prop :=
{
  antireflexive : forall x : A, ~ R x x
}.

Relacja R jest zwrotna (ang. reflexive), jeżeli każdy x : A jest w relacji sam ze sobą. Przykładem ze świata rzeczywistego może być relacja "x jest blisko y". Jest oczywiste, że każdy jest blisko samego siebie.

#[export]
Instance Reflexive_Empty :
  forall R : rel Empty_set, Reflexive R.

Okazuje się, że wszystkie relacje na typie pustym (Empty_set) są zwrotne. Nie powinno cię to w żaden sposób zaskakiwać — jest to tzw. pusta prawda (ang. vacuous truth), zgodnie z którą wszystkie zdania kwantyfikowane uniwersalnie po typie pustym są prawdziwe. Wszyscy w pustym pokoju są debilami.

#[export]
Instance Reflexive_eq {A : Type} : Reflexive (@eq A).

#[export]
Instance Reflexive_RTrue :
  forall A : Type, Reflexive (@RTrue A A).

Lemma not_Reflexive_RFalse_inhabited :
  forall A : Type, A -> ~ Reflexive (@RFalse A A).

Najważniejszym przykładem relacji zwrotnej jest równość. eq jest relacją zwrotną, gdyż ma konstruktor eq_refl, który głosi, że każdy obiekt jest równy samemu sobie. Zwrotna jest też relacja RTrue, gdyż każdy obiekt jest w jej przypadku w relacji z każdym, a więc także z samym sobą. Zwrotna nie jest za to relacja RFalse na typie niepustym, gdyż tam żaden obiekt nie jest w relacji z żadnym, a więc nie może także być w relacji z samym sobą.

Lemma eq_subrelation_Reflexive :
  forall (A : Type) (R : rel A),
    Reflexive R -> subrelation (@eq A) R.

Równość jest "najmniejszą" relacją zwrotną w tym sensie, że jest ona subrelacją każdej relacji zwrotnej. Intuicyjnym uzasadnieniem jest fakt, że w definicji eq poza konstruktorem eq_refl, który daje zwrotność, nie ma niczego innego.

Lemma Reflexive_subrelation_eq :
  forall (A : Type) (R : rel A),
    subrelation (@eq A) R -> Reflexive R.

TODO: Zachodzi też twierdzenie odwrotne.

#[export]
Instance Reflexive_Rcomp :
  forall (A : Type) (R S : rel A),
    Reflexive R -> Reflexive S -> Reflexive (Rcomp R S).

#[export]
Instance Reflexive_Rinv :
  forall (A : Type) (R : rel A),
    Reflexive R -> Reflexive (Rinv R).

#[export]
Instance Reflexive_Rand :
  forall (A : Type) (R S : rel A),
    Reflexive R -> Reflexive S -> Reflexive (Rand R S).

#[export]
Instance Reflexive_Ror_l :
  forall (A : Type) (R S : rel A),
    Reflexive R -> Reflexive (Ror R S).

#[export]
Instance Reflexive_Ror_r :
  forall (A : Type) (R S : rel A),
    Reflexive S -> Reflexive (Ror R S).

Jak widać, złożenie, odwrotność i koniunkcja relacji zwrotnych są zwrotne. Dysjunkcja posiada natomiast dużo mocniejszą właściwość: dysjunkcja dowolnej relacji z relacją zwrotną daje relację zwrotną. Tak więc dysjunkcja R z eq pozwala nam łatwo "dodać" zwrotność do R. Słownie dysjunkcja z eq odpowiada zwrotowi "lub równy", który możemy spotkać np. w wyrażeniach "mniejszy lub równy", "większy lub równy".
Właściwością odwrotną do zwrotności jest antyzwrotność. Relacja antyzwrotna to taka, że żaden x : A nie jest w relacji sam ze sobą.

#[export]
Instance Antireflexive_neq :
  forall (A : Type), Antireflexive (fun x y : A => x <> y).

#[export]
Instance Antireflexive_lt : Antireflexive lt.

Typowymi przykładami relacji antyzwrotnych są nierówność <> oraz porządek "mniejszy niż" (<) na liczbach naturalnych. Ze względu na sposób działania ludzkiego mózgu antyzwrotna jest cała masa relacji znanych nam z codziennego życia: "x jest matką y", "x jest ojcem y", "x jest synem y", "x jest córką y", "x jest nad y", "x jest pod y", "x jest za y", "x jest przed y", etc.

Lemma Antireflexive_Empty :
  forall R : rel Empty_set, Antireflexive R.

Lemma not_Antireflexive_eq_inhabited :
  forall A : Type, A -> ~ Antireflexive (@eq A).

Lemma not_Antireflexive_RTrue_inhabited :
  forall A : Type, A -> ~ Antireflexive (@RTrue A A).

#[export]
Instance Antireflexive_RFalse :
  forall A : Type, Antireflexive (@RFalse A A).

Równość na typie niepustym nie jest antyzwrotna, gdyż jest zwrotna (wzajemne związki między tymi dwoma pojęciami zbadamy już niedługo). Antyzwrotna nie jest także relacja RTrue na typie niepustym, gdyż co najmniej jeden element jest w relacji z samym sobą. Antyzwrotna jest za to relacja pusta (RFalse).

Lemma not_Antireflexive_Rcomp :
  exists (A : Type) (R S : rel A),
    Antireflexive R /\ Antireflexive S /\
    ~ Antireflexive (Rcomp R S).

#[export]
Instance Antireflexive_Rinv :
  forall (A : Type) (R : rel A),
    Antireflexive R -> Antireflexive (Rinv R).

#[export]
Instance Antireflexive_Rand :
  forall (A : Type) (R S : rel A),
    Antireflexive R -> Antireflexive (Rand R S).

#[export]
Instance Antireflexive_Ror :
  forall (A : Type) (R S : rel A),
    Antireflexive R -> Antireflexive S -> Antireflexive (Ror R S).

Złożenie relacji antyzwrotnych nie musi być antyzwrotne, ale odwrotność i dysjunkcja już tak, zaś koniunkcja dowolnej relacji z relacją antyzwrotną daje nam relację antyzwrotną. Dzięki temu możemy dowolnej relacji R "zabrać" zwrotność koniunkcjując ją z <>.
Kolejną właściwością jest niezwrotność. Relacja niezwrotna to taka, która nie jest zwrotna. Zauważ, że pojęcie to zasadniczo różni się od pojęcia relacji antyzwrotnej: tutaj mamy kwantyfikator exists, tam zaś forall.

#[export]
Instance Irreflexive_neq_inhabited :
  forall A : Type, A -> Irreflexive (Rnot (@eq A)).

#[export]
Instance Irreflexive_gt : Irreflexive gt.

Typowym przykładem relacji niezwrotnej jest nierówność x <> y. Jako, że każdy obiekt jest równy samemu sobie, to żaden obiekt nie może być nierówny samemu sobie. Zauważ jednak, że typ A musi być niepusty, gdyż w przeciwnym wypadku nie mamy czego dać kwantyfikatorowi exists.
Innym przykładem relacji niezwrotnej jest porządek "większy niż" na liczbach naturalnych. Porządkami zajmiemy się już niedługo.

Lemma not_Irreflexive_Empty :
  forall R : rel Empty_set, ~ Irreflexive R.

Lemma not_Irreflexive_eq_Empty :
  ~ Irreflexive (@eq Empty_set).

Lemma not_Irreflexive_eq_inhabited :
  forall A : Type, A -> ~ Irreflexive (@eq A).

Równość jest zwrotna, a więc nie może być niezwrotna. Zauważ jednak, że musimy podać aż dwa osobne dowody tego faktu: jeden dla typu pustego Empty_set, a drugi dla dowolnego typu niepustego. Wynika to z tego, że nie możemy sprawdzić, czy dowolny typ A jest pusty, czy też nie.

Lemma not_Irreflexive_RTrue_Empty :
  ~ Irreflexive (@RTrue Empty_set Empty_set).

Lemma not_Irreflexive_RTrue_inhabited :
  forall A : Type, A -> ~ Irreflexive (@RTrue A A).

Lemma not_Irreflexive_RFalse_Empty :
  ~ Irreflexive (@RFalse Empty_set Empty_set).

#[export]
Instance Irreflexive_RFalse_inhabited :
  forall A : Type, A -> Irreflexive (@RFalse A A).

Podobnej techniki możemy użyć, aby pokazać, że relacja pełna (RTrue) nie jest niezwrotna. Inaczej jest jednak w przypadku RFalse — na typie pustym nie jest ona niezwrotna, ale na dowolnym typie niepustym już owszem.

Lemma not_Irreflexive_Rcomp :
  exists (A : Type) (R S : rel A),
    Irreflexive R /\ Irreflexive S /\ ~ Irreflexive (Rcomp R S).

Złożenie relacji niezwrotnych nie musi być niezwrotne. Przyjrzyj się uważnie definicji Rcomp, a z pewnością uda ci się znaleźć jakiś kontrprzykład.

#[export]
Instance Irreflexive_Rinv :
  forall (A : Type) (R : rel A),
    Irreflexive R -> Irreflexive (Rinv R).

#[export]
Instance Irreflexive_Rand_l :
  forall (A : Type) (R S : rel A),
    Irreflexive R -> Irreflexive (Rand R S).

#[export]
Instance Irreflexive_Rand_r :
  forall (A : Type) (R S : rel A),
    Irreflexive S -> Irreflexive (Rand R S).

Lemma not_Irreflexive_Ror :
  exists (A : Type) (R S : rel A),
    Irreflexive R /\ Irreflexive S /\ ~ Irreflexive (Ror R S).

Odwrotność relacji niezwrotnej jest niezwrotna. Koniunkcja dowolnej relacji z relacją niezwrotną daje relację niezwrotną. Tak więc za pomocą koniunkcji i dysjunkcji możemy łatwo dawać i zabierać zwrotność różnym relacjom. Okazuje się też, że dysjunkcja nie zachowuje niezwrotności.
Na zakończenie zbadajmy jeszcze, jakie związki zachodzą pomiędzy zwrotnością, antyzwrotnością i niezwrotnością.

#[export]
Instance Reflexive_Rnot :
  forall (A : Type) (R : rel A),
    Antireflexive R -> Reflexive (Rnot R).

#[export]
Instance Antireflexive_Rnot :
  forall (A : Type) (R : rel A),
    Reflexive R -> Antireflexive (Rnot R).

Podstawowa zależność między nimi jest taka, że negacja relacji zwrotnej jest antyzwrotna, zaś negacja relacji antyzwrotnej jest zwrotna.

Lemma Reflexive_Antireflexive_Empty :
  forall R : rel Empty_set, Reflexive R /\ Antireflexive R.

Lemma Reflexive_Antireflexive_inhabited :
  forall (A : Type) (R : rel A),
    A -> Reflexive R -> Antireflexive R -> False.

Każda relacja na typie pustym jest jednocześnie zwrotna i antyzwrotna, ale nie może taka być żadna relacja na typie niepustym.

#[export]
Instance Irreflexive_inhabited_Antireflexive :
  forall (A : Type) (R : rel A),
    A -> Antireflexive R -> Irreflexive R.

Związek między niezwrotnością i antyzwrotnością jest nadzwyczaj prosty: każda relacja antyzwrotna na typie niepustym jest też niezwrotna.

Relacje symetryczne


Class Symmetric {A : Type} (R : rel A) : Prop :=
{
  symmetric : forall x y : A, R x y -> R y x
}.

Class Antisymmetric {A : Type} (R : rel A) : Prop :=
{
  antisymmetric : forall x y : A, R x y -> ~ R y x
}.

Class Asymmetric {A : Type} (R : rel A) : Prop :=
{
  asymmetric : exists x y : A, R x y /\ ~ R y x
}.

Relacja jest symetryczna, jeżeli kolejność podawania argumentów nie ma znaczenia. Przykładami ze świata rzeczywistego mogą być np. relacje "jest blisko", "jest obok", "jest naprzeciwko".

Lemma Symmetric_char :
  forall (A : Type) (R : rel A),
    Symmetric R <-> (Rinv R <--> R).

Alterntywną charakteryzacją symetrii może być stwierdzenie, że relacja symetryczna to taka, która jest swoją własną odwrotnością.

#[export]
Instance Symmetric_Empty :
  forall R : rel Empty_set, Symmetric R.

#[export]
Instance Symmetric_eq :
  forall A : Type, Symmetric (@eq A).

#[export]
Instance Symmetric_RTrue :
  forall A : Type, Symmetric (@RTrue A A).

#[export]
Instance Symmetric_RFalse :
  forall A : Type, Symmetric (@RFalse A A).

Równość, relacja pełna i pusta są symetryczne.

Lemma not_Symmetric_Rcomp :
  exists (A : Type) (R S : rel A),
    Symmetric R /\ Symmetric S /\ ~ Symmetric (Rcomp R S).

Złożenie relacji symetrycznych nie musi być symetryczne.

#[export]
Instance Symmetric_Rinv :
  forall (A : Type) (R : rel A),
    Symmetric R -> Symmetric (Rinv R).

#[export]
Instance Symmetric_Rand :
  forall (A : Type) (R S : rel A ),
    Symmetric R -> Symmetric S -> Symmetric (Rand R S).

#[export]
Instance Symmetric_Ror :
  forall (A : Type) (R S : rel A ),
    Symmetric R -> Symmetric S -> Symmetric (Ror R S).

#[export]
Instance Symmetric_Rnot :
  forall (A : Type) (R : rel A),
    Symmetric R -> Symmetric (Rnot R).

Pozostałe operacje (odwracanie, koniunkcja, dysjunkcja, negacja) zachowują symetrię.
Relacja antysymetryczna to przeciwieństwo relacji symetrycznej — jeżeli x jest w relacji z y, to y nie może być w relacji z x. Sporą klasę przykładów stanowią różne relacje służące do porównywania: "x jest wyższy od y", "x jest silniejszy od y", "x jest bogatszy od y".

Lemma Antisymmetric_Empty :
  forall R : rel Empty_set, Antisymmetric R.

Lemma not_Antisymmetric_eq_inhabited :
  forall A : Type, A -> ~ Antisymmetric (@eq A).

Lemma not_Antisymmetric_RTrue_inhabited :
  forall A : Type, A -> ~ Antisymmetric (@RTrue A A).

#[export]
Instance RFalse_Antiymmetric :
  forall A : Type, Antisymmetric (@RFalse A A).

Każda relacja na typie pustym jest antysymetryczna. Równość nie jest antysymetryczna, podobnie jak relacja pełna (ale tylko na typie niepustym). Relacja pusta jest antysymetryczna, gdyż przesłanka R x y występująca w definicji antysymetrii jest zawsze fałszywa.


Lemma not_Antisymmetric_Rcomp :
  exists (A : Type) (R S : rel A),
    Antisymmetric R /\ Antisymmetric S /\
    ~ Antisymmetric (Rcomp R S).

#[export]
Instance Antisymmetric_Rinv :
  forall (A : Type) (R : rel A),
    Antisymmetric R -> Antisymmetric (Rinv R).

#[export]
Instance Antisymmetric_Rand :
  forall (A : Type) (R S : rel A),
    Antisymmetric R -> Antisymmetric (Rand R S).

Lemma not_Antisymmetric_Ror :
  exists (A : Type) (R S : rel A),
    Antisymmetric R /\ Antisymmetric S /\
    ~ Antisymmetric (Ror R S).

Lemma not_Antisymmetric_Rnot :
  exists (A : Type) (R : rel A),
    Antisymmetric R /\ ~ Antisymmetric (Rnot R).

Lemma not_Asymmetric_Empty :
  forall R : rel Empty_set, ~ Asymmetric R.

Lemma not_Asymmetric_Rcomp :
  exists (A : Type) (R S : rel A),
    Asymmetric R /\ Asymmetric S /\ ~ Asymmetric (Rcomp R S).

#[export]
Instance Asymmetric_Rinv :
  forall (A : Type) (R : rel A),
    Asymmetric R -> Asymmetric (Rinv R).

Lemma not_Asymmetric_Rand :
  exists (A : Type) (R S : rel A),
    Asymmetric R /\ Asymmetric S /\ ~ Asymmetric (Rand R S).

Lemma not_Asymmetric_Ror :
  exists (A : Type) (R S : rel A),
    Asymmetric R /\ Asymmetric S /\ ~ Asymmetric (Ror R S).

Relacje przechodnie


Class Transitive {A : Type} (R : rel A) : Prop :=
{
  transitive : forall x y z : A, R x y -> R y z -> R x z
}.

#[export]
Instance Transitive_RTrue :
  forall A : Type, Transitive (@RTrue A A).

#[export]
Instance Transitive_RFalse :
  forall A : Type, Transitive (@RFalse A A).

#[export]
Instance Transitive_eq :
  forall A : Type, Transitive (@eq A).

#[export]
Instance Transitive_Rinv :
  forall (A : Type) (R : rel A),
    Transitive R -> Transitive (Rinv R).

Lemma not_Transitive_Rcomp :
  exists (A : Type) (R S : rel A),
    Transitive R /\ Transitive S /\ ~ Transitive (Rcomp R S).

Lemma not_Transitive_Rnot :
  exists (A : Type) (R : rel A),
    Transitive R /\ ~ Transitive (Rnot R).

Lemma not_Transitive_Ror :
  exists (A : Type) (R S : rel A),
    Transitive R /\ Transitive S /\ ~ Transitive (Ror R S).

#[export]
Instance Transitive_Rand :
  forall (A : Type) (R S : rel A),
    Transitive R -> Transitive S -> Transitive (Rand R S).

Relacje równoważności


Class Equivalence {A : Type} (R : rel A) : Prop :=
{
  Equivalence_Reflexive :> Reflexive R;
  Equivalence_Symmetric :> Symmetric R;
  Equivalence_Transitive :> Transitive R;
}.

#[export]
Instance Equivalence_RTrue :
  forall A : Type, Equivalence (@RTrue A A).

#[export]
Instance Equivalence_Empty :
  forall R : rel Empty_set, Equivalence R.

Lemma not_Equivalence_RFalse_inhabited :
  forall A : Type,
    A -> ~ Equivalence (@RFalse A A).

#[export]
Instance Equivalence_eq :
  forall A : Type,
    Equivalence (@eq A).

#[export]
Instance Equivalence_Rinv :
  forall (A : Type) (R : rel A),
    Equivalence R -> Equivalence (Rinv R).

Lemma not_Equivalence_Rcomp :
  exists (A : Type) (R S : rel A),
    Equivalence R /\ Equivalence S /\ ~ Equivalence (Rcomp R S).

Lemma not_Equivalence_Rnot :
  exists (A : Type) (R : rel A),
    Equivalence R /\ ~ Equivalence (Rnot R).


Lemma not_Equivalence_Ror :
  exists (A : Type) (R S : rel A),
    Equivalence R /\ Equivalence S /\ ~ Equivalence (Ror R S).

#[export]
Instance Equivalence_Rand :
  forall (A : Type) (R S : rel A),
    Equivalence R -> Equivalence S -> Equivalence (Rand R S).

Relacje porządku (TODO)

Relacje słaboantysymetryczne


Class WeaklyAntisymmetric {A : Type} (R : rel A) : Prop :=
{
  weakly_antisymmetric : forall x y : A, R x y -> R y x -> x = y
}.

#[export]
Instance WeaklyAntisymmetric_Empty :
  forall R : rel Empty_set, WeaklyAntisymmetric R.

#[export]
Instance WeaklyAntisymmetric_hProp :
  forall {A : Type} (R : rel A),
    (forall x y : A, x = y) ->
      WeaklyAntisymmetric R.

Lemma not_WeaklyAntisymmetric_RTrue_doubleton :
  forall {A : Type} {x y : A},
    x <> y -> ~ WeaklyAntisymmetric (@RTrue A A).

#[export]
Instance WeaklyAntisymmetric_RFalse :
  forall A : Type, WeaklyAntisymmetric (@RFalse A A).

#[export]
Instance WeaklyAntisymmetric_Rinv :
  forall (A : Type) (R : rel A),
    WeaklyAntisymmetric R -> WeaklyAntisymmetric (Rinv R).

Lemma not_WeaklyAntisymmetric_Rcomp :
  exists (A : Type) (R S : rel A),
    WeaklyAntisymmetric R /\ WeaklyAntisymmetric S /\
      ~ WeaklyAntisymmetric (Rcomp R S).

Lemma not_WeaklyAntisymmetric_Rnot :
  exists (A : Type) (R : rel A),
    WeaklyAntisymmetric R /\ ~ WeaklyAntisymmetric (Rnot R).

Lemma not_WeaklyAntisymmetric_Ror :
  exists (A : Type) (R S : rel A),
    WeaklyAntisymmetric R /\ WeaklyAntisymmetric S /\
      ~ WeaklyAntisymmetric (Ror R S).

#[export]
Instance WeaklyAntisymmetric_Rand :
  forall (A : Type) (R S : rel A),
    WeaklyAntisymmetric R -> WeaklyAntisymmetric S ->
      WeaklyAntisymmetric (Rand R S).

Relacje totalne


Class Total {A : Type} (R : rel A) : Prop :=
{
  total : forall x y : A, R x y \/ R y x
}.

#[export]
Instance Total_RTrue :
  forall A : Type,
    Total (@RTrue A A).

Lemma Total_RFalse_Empty :
  Total (@RFalse Empty_set Empty_set).

Lemma not_Total_RFalse_inhabited :
  forall A : Type,
    A -> ~ Total (@RFalse A A).

Lemma Total_eq_Empty :
  Total (@eq Empty_set).

Lemma Total_eq_unit :
  Total (@eq unit).

Lemma not_Total_eq_two_elems :
  forall {A : Type} {x y : A},
    x <> y -> ~ Total (@eq A).

#[export]
Instance Total_Rcomp :
  forall (A : Type) (R S : rel A),
    Total R -> Total S -> Total (Rcomp R S).

#[export]
Instance Total_Rinv :
  forall (A : Type) (R : rel A),
    Total R -> Total (Rinv R).

Lemma not_Total_Rnot :
  exists (A : Type) (R : rel A),
    Total R /\ ~ Total (Rnot R).

#[export]
Instance Total_Ror :
  forall (A : Type) (R S : rel A),
    Total R -> Total S -> Total (Ror R S).

Lemma not_Total_Rand :
  exists (A : Type) (R S : rel A),
    Total R /\ Total S /\ ~ Total (Rand R S).

#[export]
Instance Reflexive_Total :
  forall (A : Type) (R : rel A),
    Total R -> Reflexive R.

Lemma Total_Symmetric_char :
  forall {A : Type} (R : rel A),
    Total R -> Symmetric R -> R <--> RTrue.

Porządki


Class Preorder {A : Type} (R : rel A) : Prop :=
{
  Preorder_Reflexive :> Reflexive R;
  Preorder_Transitive :> Transitive R;
}.

Class PartialOrder {A : Type} (R : rel A) : Prop :=
{
  PartialOrder_Preorder :> Preorder R;
  PartialOrder_WeaklyAntisymmetric :> WeaklyAntisymmetric R;
}.

Class TotalOrder {A : Type} (R : rel A) : Prop :=
{
  TotalOrder_PartialOrder :> PartialOrder R;
  TotalOrder_Total :> Total R;
}.

Class TotalPreorder {A : Type} (R : rel A) : Prop :=
{
  TotalPreorder_PartialOrder :> Preorder R;
  TotalPreorder_Total :> Total R;
}.

Relacje gęste


Class Dense {A : Type} (R : rel A) : Prop :=
{
  dense : forall x y : A, R x y -> exists z : A, R x z /\ R z y
}.

#[export]
Instance Dense_RTrue :
  forall A : Type, Dense (@RTrue A A).

#[export]
Instance Dense_RFalse :
  forall A : Type, Dense (@RFalse A A).

#[export]
Instance Dense_eq :
  forall A : Type, Dense (@eq A).

#[export]
Instance Dense_Reflexive :
  forall {A : Type} (R : rel A),
    Reflexive R -> Dense R.

#[export]
Instance Dense_Rinv :
  forall (A : Type) (R : rel A),
    Dense R -> Dense (Rinv R).

Lemma not_Dense_Rcomp :
  exists (A : Type) (R S : rel A),
    Dense R /\ Dense S /\ ~ Dense (Rcomp R S).

Lemma not_Dense_Rnot :
  exists (A : Type) (R : rel A),
    Dense R /\ ~ Dense (Rnot R).

#[export]
Instance Dense_Ror :
  forall (A : Type) (R S : rel A),
    Dense R -> Dense S -> Dense (Ror R S).

Lemma not_Dense_Rand :
  exists (A : Type) (R S : rel A),
    Dense R /\ Dense S /\ ~ Dense (Rand R S).

Domknięcia (TODO)


Require Import Classes.RelationClasses.

Ogólne pojęcie domknięcia

Uwaga, najnowszy pomysł: przedstawić domknięcia w taki sposób, żeby niepostrzeżenie przywyczajały do monad.

Class Closure
  {A : Type}
  (Cl : rel A -> rel A) : Prop :=
{
  pres :
    forall R S : rel A,
      (R --> S) -> Cl R --> Cl S;
  step :
    forall R : rel A,
      forall x y : A, R x y -> Cl R x y;
  join :
    forall R : rel A,
      Cl (Cl R) --> Cl R;
}.

Domknięcie zwrotne


Inductive rc {A : Type} (R : rel A) : rel A :=
| rc_step : forall x y : A, R x y -> rc R x y
| rc_refl : forall x : A, rc R x x.


Ltac rc := compute; repeat split; intros; rel;
repeat match goal with
| H : rc _ _ _ |- _ => induction H; eauto
end; rel.
(* end hide *)

#[export]
Instance Reflexive_rc :
  forall (A : Type) (R : rel A), Reflexive (rc R).

Lemma subrelation_rc :
  forall (A : Type) (R : rel A), subrelation R (rc R).

Lemma rc_smallest :
  forall (A : Type) (R S : rel A),
    subrelation R S -> Reflexive S -> subrelation (rc R) S.

Lemma rc_idempotent :
  forall (A : Type) (R : rel A),
    rc (rc R) <--> rc R.

Lemma rc_Rinv :
  forall (A : Type) (R : rel A),
    Rinv (rc (Rinv R)) <--> rc R.

Domknięcie symetryczne


Inductive sc {A : Type} (R : rel A) : rel A :=
| sc_step : forall x y : A, R x y -> sc R x y
| sc_symm : forall x y : A, R x y -> sc R y x.


Ltac sc := compute; repeat split; intros; rel;
repeat match goal with
| H : sc _ _ _ |- _ => inversion H; eauto
end.
(* end hide *)

#[export]
Instance Symmetric_sc :
  forall (A : Type) (R : rel A), Symmetric (sc R).

Lemma subrelation_sc :
  forall (A : Type) (R : rel A), subrelation R (sc R).

Lemma sc_smallest :
  forall (A : Type) (R S : rel A),
    subrelation R S -> Symmetric S -> subrelation (sc R) S.

Lemma sc_idempotent :
  forall (A : Type) (R : rel A),
    sc (sc R) <--> sc R.

Lemma sc_Rinv :
  forall (A : Type) (R : rel A),
    Rinv (sc (Rinv R)) <--> sc R.

Ćwiczenie (gorsze domknięcie symetryczne)

Przyjrzyj się poniższej alternatywnej definicji domknięcia symetrycznego. Udowodnij, że jest ona równoważna sc. Dlaczego jest ona gorsza niż sc?

Inductive sc' {A : Type} (R : rel A) : rel A :=
| sc'_step :
    forall x y : A, R x y -> sc' R x y
| sc'_symm :
    forall x y : A, sc' R x y -> sc' R y x.


#[export]
Instance Symmetric_sc' :
  forall (A : Type) (R : rel A), Symmetric (sc' R).

Lemma subrelation_sc' :
  forall (A : Type) (R : rel A), subrelation R (sc' R).

Lemma sc'_smallest :
  forall (A : Type) (R S : rel A),
    subrelation R S -> Symmetric S -> subrelation (sc' R) S.

Lemma sc'_idempotent :
  forall (A : Type) (R : rel A),
    sc' (sc' R) <--> sc' R.

Lemma sc'_Rinv :
  forall (A : Type) (R : rel A),
    Rinv (sc' (Rinv R)) <--> sc' R.

Lemma sc_sc' :
  forall {A : Type} (R : rel A),
    sc R <--> sc' R.

Domknięcie zwrotnosymetryczne


Definition rsc {A : Type} (R : rel A) : rel A :=
  rc (sc' R).


#[export]
Instance Reflexive_rsc :
  forall (A : Type) (R : rel A), Reflexive (rsc R).

#[export]
Instance Symmetric_rsc :
  forall (A : Type) (R : rel A), Symmetric (rsc R).

Lemma subrelation_rsc :
  forall (A : Type) (R : rel A), subrelation R (rsc R).

Lemma rsc_smallest :
  forall (A : Type) (R S : rel A),
    subrelation R S -> Reflexive S -> Symmetric S ->
      subrelation (rsc R) S.

Lemma rsc_idempotent :
  forall (A : Type) (R : rel A),
    rsc (rsc R) <--> rsc R.

Lemma rsc_Rinv :
  forall (A : Type) (R : rel A),
    Rinv (rsc (Rinv R)) <--> rsc R.

Redukcje (TODO)

Redukacja zwrotna


Definition rr {A : Type} (R : rel A) : rel A :=
  fun x y : A => R x y /\ x <> y.

#[export]
Instance Antireflexive_rr :
  forall (A : Type) (R : rel A), Antireflexive (rr R).

From Typonomikon Require Import BC4a.

Lemma rr_rc :
  LEM ->
    forall (A : Type) (R : rel A),
      Reflexive R -> rc (rr R) <--> R.

Redukcja przechodnia


Definition TransitiveReduction {A : Type} (R : rel A) (x y : A) : Prop :=
  R x y /\ forall z : A, rr R x z -> rr R z y -> False.

(*
[export] Instance Antitransitive_TransitiveReduction' {A : Type} (R : rel A) : Antitransitive (TransitiveReduction' R). (* begin hide *)

Proof.
  compute. intros x y z [H11 H12] [H21 H22] [H31 H32].
Abort.
(* end hide *)
*)

Redukacja zwrotno-przechodnia


Definition ReflexiveTransitiveReduction {A : Type} (R : rel A) (x y : A) : Prop :=
  R x y /\ forall z : A, R x z -> R z y -> False.

(*
[export] Instance Antitransitive_TransitiveReduction {A : Type} (R : rel A) : Antitransitive (TransitiveReduction R). (* begin hide *)

Proof.
  compute. intros x y z [H11 H12] [H21 H22] [H31 H32].
  firstorder.
Qed.
(* end hide *)
*)

Podsumowanie

Do obczajenia z biblioteki stdpp:

(* Module wut.

From stdpp Require Import prelude.

Print strict.
Print diamond.
Print locally_confluent.
Print confluent.
Print Trichotomy.
Print PartialOrder.
Print Total.
Print TotalOrder.
Print relation_equivalence.
Print Setoid_Theory.
Print TrichotomyT.
Print DefaultRelation.
Print order.
Print antisymmetric.
Print RewriteRelation.
Print StrictOrder.
Print all_loop.
Print strict.
Print nf.
Print red.

End wut. *)