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:


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:
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.


Relacje injektywne


Class Injective {A B : Type} (R : hrel A B) : Prop :=
{
  I_Fun :> Functional R;
  I_LU :> LeftUnique R;
}.

(* TODO: injective
[export] Instance inj_to_Injective : forall (A B : Type) (f : A -> B), injective f -> Injective (fun (a : A) (b : B) => f a = b). (* begin hide *)

Proof.
  split.
    apply fun_to_Functional.
    rel.
Qed.
(* end hide *)
*)

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.

(* TODO: injective
Lemma injective_Injective :
  forall (A B : Type) (f : A -> B),
    injective f <-> Injective (fun (a : A) (b : B) => f a = b).
(* begin hide *)
Proof.
  split.
    compute; intros. repeat split; intros.
      exists (f a). reflexivity.
      rewrite <- H0, <- H1. reflexivity.
      apply H. rewrite H0, H1. reflexivity.
    destruct 1 as [[] []] []. red. intros.
      apply left_unique0 with (f x').
        assumption.
        reflexivity.
Qed.
(* end hide *)
*)


Relacje surjektywne


Class Surjective {A B : Type} (R : hrel A B) : Prop :=
{
  S_Fun :> Functional R;
  S_RT :> RightTotal R;
}.

(* TODO: surjective
[export] Instance sur_to_Surjective : forall (A B : Type) (f : A -> B), surjective f -> Surjective (fun (a : A) (b : B) => f a = b). (* begin hide *)

Proof. rel. Qed.
(* end hide *)
*)

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.