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:
- zdania to obiekty typu Prop. Twierdzą one coś na temat świata:
"niebo jest niebieskie", P -> Q etc. W uproszczeniu możemy myśleć o
nich, że są prawdziwe lub fałszywe, co nie znaczy wcale, że można to
automatycznie rozstrzygnąć. Udowodnienie zdania P to skonstruowanie
obiektu p : P. W Coqu zdania służą nam do podawania specyfikacji
programów. W celach klasyfikacyjnych możemy uznać, że są to funkcje
biorące zero argumentów i zwracające Prop.
- predykaty to funkcje typu A -> Prop dla jakiegoś A : Type. Można
za ich pomocą przedstawiać stwierdzenia na temat właściwości obiektów:
"liczba 5 jest parzysta", odd 5. Dla niektórych argumentów zwracane
przez nie zdania mogą być prawdziwe, a dla innych już nie. Dla celów
klasyfikacji uznajemy je za funkcje biorące jeden argument i zwracające
Prop.
- relacje to funkcje biorące dwa lub więcej argumentów, niekoniecznie
o takich samych typach, i zwracające Prop. Służą one do opisywania
zależności między obiektami, np. "Grażyna jest matką Karyny",
Permutation (l ++ l') (l' ++ '). Niektóre kombinacje obiektów mogą
być ze sobą w relacji, tzn. zdanie zwracane dla nich przez relację
może być prawdziwe, a dla innych nie.
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:
- "podstawowa" własność nie ma przedrostka, np. "zwrotna", "reflexive"
- zanegowana własność ma przedrostek "nie" (lub podobny w nazwach
angielskich), np. "niezwrotna", "irreflexive"
- przeciwieństwo tej właściwości ma przedrostek "anty-" (po angielsku
"anti-"), np. "antyzwrotna", "antireflexive"
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).
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).
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).
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.
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;
}.
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).
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;
}.
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.
#[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.
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.
#[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.
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.
Proof.
compute. intros x y z [H11 H12] [H21 H22] [H31 H32].
Abort.
*)
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.
Proof.
compute. intros x y z [H11 H12] [H21 H22] [H31 H32].
firstorder.
Qed.
*)
Podsumowanie
Do obczajenia z biblioteki stdpp: