BC2h: Relacje a funkcje [TODO]
Require Import FunctionalExtensionality Nat Lia.
Require Import List.
Import ListNotations.
From Typonomikon Require Export BC2g.
Rodzaje relacji heterogenicznych
Relacje lewo- i prawostronnie unikalne
Class LeftUnique {A B : Type} (R : hrel A B) : Prop :=
{
left_unique :
forall (a a' : A) (b : B), R a b -> R a' b -> a = a'
}.
Class RightUnique {A B : Type} (R : hrel A B) : Prop :=
{
right_unique :
forall (a : A) (b b' : B), R a b -> R a b' -> b = b'
}.
Dwoma podstawowymi rodzajami relacji są relacje unikalne z lewej i prawej
strony. Relacja lewostronnie unikalna to taka, dla której każde b : B
jest w relacji z co najwyżej jednym a : A. Analogicznie definiujemy
relację prawostronnie unikalną.
#[export]
Instance LeftUnique_eq (A : Type) : LeftUnique (@eq A).
#[export]
Instance RightUnique_eq (A : Type) : RightUnique (@eq A).
Najbardziej elementarną intuicję stojącą za tymi koncepcjami można
przedstawić na przykładzie relacji równości: jeżeli dwa obiekty są
równe jakiemuś trzeciemu obiektowi, to muszą być także równe sobie
nawzajem.
Pojęcie to jest jednak bardziej ogólne i dotyczy także relacji, które
nie są homogeniczne. W szczególności jest ono różne od pojęcia relacji
przechodniej, które pojawi się już niedługo.
#[export]
Instance LeftUnique_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
LeftUnique R -> LeftUnique S -> LeftUnique (Rcomp R S).
#[export]
Instance RightUnique_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
RightUnique R -> RightUnique S -> RightUnique (Rcomp R S).
Składanie zachowuje oba rodzaje relacji unikalnych. Nie ma tu co
za dużo filozofować — żeby się przekonać, narysuj obrazek. TODO.
#[export]
Instance LeftUnique_Rinv :
forall (A B : Type) (R : hrel A B),
RightUnique R -> LeftUnique (Rinv R).
#[export]
Instance RightUnique_Rinv :
forall (A B : Type) (R : hrel A B),
LeftUnique R -> RightUnique (Rinv R).
Już na pierwszy rzut oka widać, że pojęcia te są w pewien sposób
symetryczne. Aby uchwycić tę symetrię, możemy posłużyć się operacją
Rinv. Okazuje się, że zamiana miejscami argumentów relacji lewostronnie
unikalnej daje relację prawostronnie unikalną i vice versa.
#[export]
Instance LeftUnique_Rand :
forall (A B : Type) (R S : hrel A B),
LeftUnique R -> LeftUnique (Rand R S).
#[export]
Instance RightUnique_Rand :
forall (A B : Type) (R S : hrel A B),
RightUnique R -> RightUnique (Rand R S).
Lemma not_LeftUnique_Ror :
exists (A B : Type) (R S : hrel A B),
LeftUnique R /\ LeftUnique S /\ ~ LeftUnique (Ror R S).
Lemma not_RightUnique_Ror :
exists (A B : Type) (R S : hrel A B),
RightUnique R /\ RightUnique S /\ ~ RightUnique (Ror R S).
Koniunkcja relacji unikalnej z inną daje relację unikalną, ale dysjunkcja
nawet dwóch relacji unikalnych nie musi dawać w wyniku relacji unikalnej.
Wynika to z interpretacji operacji na relacjach jako operacji na
kolekcjach par.
Wyobraźmy sobie, że relacja
R : hrel A B to kolekcja par
p : A * B.
Jeżeli para jest elementem kolekcji, to jej pierwszy komponent jest w
relacji
R z jej drugim komponentem. Dysjunkcję relacji
R i
S w
takim układzie stanowi kolekcja, która zawiera zarówno pary z kolekcji
odpowiadającej
R, jak i te z kolekcji odpowiadającej
S. Koniunkcja
odpowiada kolekcji par, które są zarówno w kolekcji odpowiadającej
R,
jak i tej odpowiadającej
S.
Tak więc dysjunkcja
R i
S może do
R "dorzucić" jakieś pary, ale
nie może ich zabrać. Analogicznie, koniunkcja
R i
S może zabrać
pary z
R, ale nie może ich dodać.
Teraz interpretacja naszego wyniku jest prosta. W przypadku relacji
lewostronnie unikalnych jeżeli każde
b : B jest w relacji z co
najwyżej jednym
a : A, to potencjalne zabranie jakichś par z tej
relacji niczego nie zmieni. Z drugiej strony, nawet jeżeli obie relacje
są lewostronnie unikalne, to dodanie do
R par z
S może spowodować
wystąpienie powtórzeń, co niszczy unikalność.
Lemma not_LeftUnique_Rnot :
exists (A B : Type) (R : hrel A B),
LeftUnique R /\ ~ LeftUnique (Rnot R).
Lemma not_RightUnique_Rnot :
exists (A B : Type) (R : hrel A B),
LeftUnique R /\ ~ LeftUnique (Rnot R).
Negacja relacji unikalnej również nie musi być unikalna. Spróbuj podać
interpretację tego wyniku z punktu widzenia operacji na kolekcjach par.
Ćwiczenie
Znajdź przykład relacji, która:
- nie jest unikalna ani lewostronnie, ani prawostronnie
- jest unikalna lewostronnie, ale nie prawostronnie
- jest unikalna prawostronnie, ale nie nie lewostronnie
- jest obustronnie unikalna
Relacje lewo- i prawostronnie totalne
Class LeftTotal {A B : Type} (R : hrel A B) : Prop :=
{
left_total : forall a : A, exists b : B, R a b
}.
Class RightTotal {A B : Type} (R : hrel A B) : Prop :=
{
right_total : forall b : B, exists a : A, R a b
}.
Kolejnymi dwoma rodzajami heterogenicznych relacji binarnych są relacje
lewo- i prawostronnie totalne. Relacja lewostronnie totalna to taka, że
każde
a : A jest w relacji z jakimś elementem
B. Definicja relacji
prawostronnie totalnej jest analogiczna.
Za pojęciem tym nie stoją jakieś wielkie intuicje: relacja lewostronnie
totalna to po prostu taka, w której żaden element
a : A nie jest
"osamotniony".
#[export]
Instance LeftTotal_RTrue :
forall A : Type, LeftTotal (@RTrue A A).
#[export]
Instance RightTotal_RTrue :
forall A : Type, RightTotal (@RTrue A A).
TODO
#[export]
Instance LeftTotal_RFalse_Empty :
LeftTotal (@RFalse Empty_set Empty_set).
#[export]
Instance RightTotal_RFalse_Empty :
RightTotal (@RFalse Empty_set Empty_set).
Lemma not_LeftTotal_RFalse_inhabited :
forall A B : Type,
A -> ~ LeftTotal (@RFalse A B).
Lemma not_RightTotal_RFalse_inhabited :
forall A B : Type,
B -> ~ RightTotal (@RFalse A B).
TODO
#[export]
Instance LeftTotal_eq :
forall A : Type, LeftTotal (@eq A).
#[export]
Instance RightTotal_eq :
forall A : Type, RightTotal (@eq A).
Równość jest relacją totalną, gdyż każdy term x : A jest równy samemu
sobie.
#[export]
Instance LeftTotal_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
LeftTotal R -> LeftTotal S -> LeftTotal (Rcomp R S).
#[export]
Instance RightTotal_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
RightTotal R -> RightTotal S -> RightTotal (Rcomp R S).
#[export]
Instance RightTotal_Rinv :
forall (A B : Type) (R : hrel A B),
LeftTotal R -> RightTotal (Rinv R).
#[export]
Instance LeftTotal_Rinv :
forall (A B : Type) (R : hrel A B),
RightTotal R -> LeftTotal (Rinv R).
Między lewo- i prawostronną totalnością występuje podobna symetria jak
między dwoma formami unikalności: relacja odwrotna do lewostronnie
totalnej jest prawostronnie totalna i vice versa. Totalność jest również
zachowywana przez składanie.
Lemma not_LeftTotal_Rnot :
exists (A B : Type) (R : hrel A B),
RightTotal R /\ ~ RightTotal (Rnot R).
Lemma not_RightTotal_Rnot :
exists (A B : Type) (R : hrel A B),
RightTotal R /\ ~ RightTotal (Rnot R).
Negacja relacji totalnej nie może być totalna. Nie ma się co dziwić —
negacja wyrzuca z relacji wszystkie pary, których w niej nie było, a
więc pozbywa się czynnika odpowiedzialnego za totalność.
Lemma not_LeftTotal_Rand :
exists (A B : Type) (R S : hrel A B),
LeftTotal R /\ LeftTotal S /\ ~ LeftTotal (Rand R S).
Lemma not_RightTotal_Rand :
exists (A B : Type) (R S : hrel A B),
RightTotal R /\ RightTotal S /\ ~ RightTotal (Rand R S).
Lemma LeftTotal_Ror :
forall (A B : Type) (R S : hrel A B),
LeftTotal R -> LeftTotal (Ror R S).
Lemma RightTotal_Ror :
forall (A B : Type) (R S : hrel A B),
RightTotal R -> RightTotal (Ror R S).
Związki totalności z koniunkcją i dysjunkcją relacji są podobne jak
w przypadku unikalności, lecz tym razem to dysjunkcja zachowuje
właściwość, a koniunkcja ją niszczy. Wynika to z tego, że dysjunkcja
nie zabiera żadnych par z relacji, więc nie może uszkodzić totalności.
Z drugiej strony koniunkcja może zabrać jakąś parę, a wtedy relacja
przestaje być totalna.
Ćwiczenie
Znajdź przykład relacji, która:
- nie jest totalna ani lewostronnie, ani prawostronnie
- jest totalna lewostronnie, ale nie prawostronnie
- jest totalna prawostronnie, ale nie nie lewostronnie
- jest obustronnie totalna
Bonusowe punkty za relację, która jest "naturalna", tzn. nie została
wymyślona na chama specjalnie na potrzeby zadania.
Rodzaje relacji heterogenicznych v2
Poznawszy cztery właściwości, jakie relacje mogą posiadać, rozważymy
teraz relacje, które posiadają dwie lub więcej z tych właściwości.
Relacje funkcyjne
Class Functional {A B : Type} (R : hrel A B) : Prop :=
{
F_LT :> LeftTotal R;
F_RU :> RightUnique R;
}.
Lewostronną totalność i prawostronną unikalność możemy połączyć, by
uzyskać pojęcie relacji funkcyjnej. Relacja funkcyjna to relacja,
która ma właściwości takie, jak funkcje — każdy lewostronny argument
a : A jest w relacji z dokładnie jednym b : B po prawej stronie.
#[export]
Instance fun_to_Functional {A B : Type} (f : A -> B)
: Functional (fun (a : A) (b : B) => f a = b).
Require Import IndefiniteDescription.
Definition Functional_to_fun
{A B : Type} (R : hrel A B) (F : Functional R) : A -> B.
Proof.
intro a. destruct F as [[LT] [RU]].
specialize (LT a).
apply constructive_indefinite_description in LT.
destruct LT as [b _]. exact b.
Qed.
Z każdej funkcji można w prosty sposób zrobić relację funkcyjną, ale
bez dodatkowych aksjomatów nie jesteśmy w stanie z relacji funkcyjnej
zrobić funkcji. Przemilczając kwestie aksjomatów możemy powiedzieć
więc, że relacje funkcyjne odpowiadają funkcjom.
#[export]
Instance Functional_eq :
forall A : Type, Functional (@eq A).
Równość jest rzecz jasna relacją funkcyjną. Funkcją odpowiadającą
relacji @eq A jest funkcja identycznościowa @id A.
#[export]
Instance Functional_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
Functional R -> Functional S -> Functional (Rcomp R S).
Złożenie relacji funkcyjnych również jest relacją funkcyjną. Nie powinno
nas to dziwić — wszakże relacje funkcyjne odpowiadają funkcjom, a złożenie
funkcji jest przecież funkcją. Jeżeli lepiej mu się przyjrzeć, to okazuje
się, że składanie funkcji odpowiada składaniu relacji, a stąd już prosta
droga do wniosku, że złożenie relacji funkcyjnych jest relacją funkcyjną.
Lemma not_Functional_Rinv :
exists (A B : Type) (R : hrel A B),
Functional R /\ ~ Functional (Rinv R).
Odwrotność relacji funkcyjnej nie musi być funkcyjna. Dobrą wizualicją
tego faktu może być np. funkcja f(x) = x^2 na liczbach rzeczywistych.
Z pewnością jest to funkcja, a zatem relacja funkcyjna. Widać to na jej
wykresie — każdemu punktowi dziedziny odpowiada dokładnie jeden punkt
przeciwdziedziny. Jednak po wykonaniu operacji Rinv, której odpowiada
tutaj obrócenie układu współrzędnych o 90 stopni, nie otrzymujemy wcale
wykresu funkcji. Wprost przeciwnie — niektórym punktom z osi X na takim
wykresie odpowiadają dwa punkty na osi Y (np. punktowi 4 odpowiadają 2
i -2). Stąd wnioskujemy, że odwrócenie relacji funkcyjnej f nie daje w
wyniku relacji funkcyjnej.
Lemma not_Functional_Rand :
exists (A B : Type) (R S : hrel A B),
Functional R /\ Functional S /\ ~ Functional (Rand R S).
Lemma not_Functional_Ror :
exists (A B : Type) (R S : hrel A B),
Functional R /\ Functional S /\ ~ Functional (Ror R S).
Lemma not_Functional_Rnot :
exists (A B : Type) (R : hrel A B),
Functional R /\ ~ Functional (Rnot R).
Ani koniunkcje, ani dysjunkcje, ani negacje relacji funkcyjnych nie
muszą być wcale relacjami funkcyjnymi. Jest to po części konsekwencją
właściwości relacji lewostronnie totalnych i prawostronnie unikalnych:
pierwsze mają problem z
Rand, a drugie z
Ror, oba zaś z
Rnot.
Ćwiczenie
Możesz zadawać sobie pytanie: po co nam w ogóle pojęcie relacji
funkcyjnej, skoro mamy funkcje? Funkcje muszą być obliczalne (ang.
computable) i to na mocy definicji, zaś relacje — niekonieczne.
Czasem prościej może być najpierw zdefiniować relację, a dopiero
później pokazać, że jest funkcyjna. Czasem zdefiniowanie danego
bytu jako funkcji może być niemożliwe.
Funkcję Collatza klasycznie definiuje się w ten sposób: jeżeli n
jest parzyste, to f(n) = n/2. W przeciwnym przypadku f(n) = 3n + 1.
Zaimplementuj tę funkcję w Coqu. Spróbuj zaimplementować ją zarówno
jako funkcję rekurencyjną, jak i relację. Czy twoja funkcja dokładnie
odpowiada powyższej specyfikacji? Czy jesteś w stanie pokazać, że twoja
relacja jest funkcyjna?
Udowodnij, że f(42) = 1.
Class Injective {A B : Type} (R : hrel A B) : Prop :=
{
I_Fun :> Functional R;
I_LU :> LeftUnique R;
}.
Proof.
split.
apply fun_to_Functional.
rel.
Qed.
*)
Relacje funkcyjne, które są lewostronnie unikalne, odpowiadają funkcjom
injektywnym.
#[export]
Instance Injective_eq :
forall A : Type, Injective (@eq A).
#[export]
Instance Injective_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
Injective R -> Injective S -> Injective (Rcomp R S).
Lemma not_Injective_Rinv :
exists (A B : Type) (R : hrel A B),
Injective R /\ ~ Injective (Rinv R).
Lemma not_Injective_Rand :
exists (A B : Type) (R S : hrel A B),
Injective R /\ Injective S /\ ~ Injective (Rand R S).
Lemma not_Injective_Ror :
exists (A B : Type) (R S : hrel A B),
Injective R /\ Injective S /\ ~ Injective (Ror R S).
Lemma not_Injective_Rnot :
exists (A B : Type) (R : hrel A B),
Injective R /\ ~ Injective (Rnot R).
Właściwości relacji injektywnych są takie, jak funkcji injektywnych,
gdyż te pojęcia ściśle sobie odpowiadają.
Ćwiczenie
Udowodnij, że powyższe zdanie nie kłamie.
Class Surjective {A B : Type} (R : hrel A B) : Prop :=
{
S_Fun :> Functional R;
S_RT :> RightTotal R;
}.
Proof. rel. Qed.
*)
Relacje funkcyjne, które są prawostronnie totalne, odpowiadają funkcjom
surjektywnym.
#[export]
Instance Surjective_eq :
forall A : Type, Surjective (@eq A).
#[export]
Instance Surjective_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
Surjective R -> Surjective S -> Surjective (Rcomp R S).
Lemma not_Surjective_Rinv :
exists (A B : Type) (R : hrel A B),
Surjective R /\ ~ Surjective (Rinv R).
Lemma not_Surjective_Rand :
exists (A B : Type) (R S : hrel A B),
Surjective R /\ Surjective S /\ ~ Surjective (Rand R S).
Lemma not_Surjective_Ror :
exists (A B : Type) (R S : hrel A B),
Surjective R /\ Surjective S /\ ~ Surjective (Ror R S).
Lemma not_Surjective_Rnot :
exists (A B : Type) (R : hrel A B),
Surjective R /\ ~ Surjective (Rnot R).
Właściwości relacji surjektywnych także są podobne do tych, jakie są
udziałem relacji funkcyjnych.
Relacje bijektywne
Class Bijective {A B : Type} (R : hrel A B) : Prop :=
{
B_Fun :> Functional R;
B_LU :> LeftUnique R;
B_RT :> RightTotal R;
}.
Relacje funkcyjne, które są lewostronnie totalne (czyli injektywne) oraz
prawostronnie totalne (czyli surjektywne), odpowiadają bijekcjom.
#[export]
Instance Bijective_eq :
forall A : Type, Bijective (@eq A).
#[export]
Instance Bijective_Rcomp :
forall (A B C : Type) (R : hrel A B) (S : hrel B C),
Bijective R -> Bijective S -> Bijective (Rcomp R S).
#[export]
Instance Bijective_Rinv :
forall (A B : Type) (R : hrel A B),
Bijective R -> Bijective (Rinv R).
Lemma not_Bijective_Rand :
exists (A B : Type) (R S : hrel A B),
Bijective R /\ Bijective S /\ ~ Bijective (Rand R S).
Lemma not_Bijective_Ror :
exists (A B : Type) (R S : hrel A B),
Bijective R /\ Bijective S /\ ~ Bijective (Ror R S).
Lemma not_Bijective_Rnot :
exists (A B : Type) (R : hrel A B),
Bijective R /\ ~ Bijective (Rnot R).
Właściwości relacji bijektywnych różnią się jednym szalenie istotnym
detalem od właściwości relacji funkcyjnych, injektywnych i surjektywnych:
odwrotność relacji bijektywnej jest relacją bijektywną.
Nieco więcej o relacjach funkcyjnych (TODO)
Pojęcie relacji funkcyjnej jest dość zwodnicze, więc pochylimy się nad nim
nieco dłużej.
Relacje funkcyjne a aksjomat wyboru (TODO)
Tutaj zademonstrować różnicę między
Functional a poniższym tworem:
Class FunctionalAC {A B : Type} (R : hrel A B) : Prop :=
fac : exists f : A -> B, forall (a : A) (b : B), R a b <-> f a = b.
Relacje funkcyjne a funkcje (TODO)
Tutaj powtórka historyjki z rozdziału o rozstrzygalności. Dowiedzieliśmy
się tam, że fakt, że zdanie jest określone, nie oznacza jeszcze, że jest
ono rozstrzygalne. Zdanie jest określone, gdy zachodzi dla niego
wyłączony środek, zaś rozstrzygalne, gdy istnieje program, który sprawdza,
która z tych dwóch możliwości zachodzi. Podobna sytuacja ma miejsce dla
przeróżnych porządków: to, że relacja jest np. trychotomiczna, nie znaczy
jeszcze, że potrafimy napisać program, który sprawdza, który z argumentów
jest większy.
Z relacjami funkcyjnymi jest podobnie: to, że relacja jest funkcyjna, nie
znaczy od razu, że potrafimy znaleźć funkcję, której jest ona wykresem.
Relacje wykresowe (TODO)
Class Graphic {A B : Type} (R : A -> B -> Prop) : Type :=
{
grapher : A -> B;
is_graph : forall (a : A) (b : B), R a b <-> grapher a = b;
}.
Zależności między różnymi rodzajami relacji
#[export]
Instance LeftTotal_Reflexive :
forall {A : Type} (R : rel A),
Reflexive R -> LeftTotal R.
#[export]
Instance RightTotal_Reflexive :
forall {A : Type} (R : rel A),
Reflexive R -> RightTotal R.
Lemma Reflexive_from_Symmetric_Transitive_RightTotal :
forall {A : Type} (R : rel A),
Symmetric R -> Transitive R -> RightTotal R -> Reflexive R.