perga/examples/algebra.pg

237 lines
9.5 KiB
Text
Raw Normal View History

2024-11-20 07:37:57 -08:00
-- --------------------------------------------------------------------------------------------------------------
-- | BASIC LOGIC |
-- --------------------------------------------------------------------------------------------------------------
@include logic.pg
2024-11-20 07:37:57 -08:00
-- --------------------------------------------------------------------------------------------------------------
-- | BASIC DEFINITIONS |
-- --------------------------------------------------------------------------------------------------------------
section BasicDefinitions
2024-11-20 07:37:57 -08:00
-- note we leave off the type ascriptions for most of these, as the type isn't
-- very interesting
-- I'd always strongly recommend including the type ascriptions for theorems
2024-11-20 07:37:57 -08:00
-- Fix some set A
2024-12-10 20:31:53 -08:00
variable (A : ★);
2024-11-20 07:37:57 -08:00
2024-12-10 20:31:53 -08:00
-- a unary operation is a function `A → A`
def unop := A → A;
2024-11-20 07:37:57 -08:00
2024-12-10 20:31:53 -08:00
-- a binary operation is a function `A → A → A`
def binop := A → A → A;
2024-11-20 07:37:57 -08:00
2024-12-10 23:36:34 -08:00
-- fix some binary operation `*`
2024-12-10 20:31:53 -08:00
variable (* : binop);
infixl 20 *;
2024-11-20 07:37:57 -08:00
-- it is associative if ...
2024-12-10 20:31:53 -08:00
def assoc := forall (a b c : A), eq A (a * (b * c)) ((a * b) * c);
2024-11-20 07:37:57 -08:00
-- fix some element `e`
variable (e : A);
2024-11-20 07:37:57 -08:00
-- it is a left identity with respect to binop `op` if `∀ a, e * a = a`
2024-12-10 20:31:53 -08:00
def id_l := forall (a : A), eq A (e * a) a;
2024-11-20 07:37:57 -08:00
-- likewise for right identity
2024-12-10 20:31:53 -08:00
def id_r := forall (a : A), eq A (a * e) a;
-- an element is an identity element if it is both a left and right identity
2024-12-10 20:31:53 -08:00
def id := id_l ∧ id_r;
-- b is a left inverse for a if `b * a = e`
-- NOTE: we don't require `e` to be an identity in this definition.
-- this definition is purely for convenience's sake
2024-12-10 20:31:53 -08:00
def inv_l (a b : A) := eq A (b * a) e;
-- likewise for right inverse
2024-12-10 20:31:53 -08:00
def inv_r (a b : A) := eq A (a * b) e;
-- and full-on inverse
2024-12-10 20:31:53 -08:00
def inv (a b : A) := inv_l a b ∧ inv_r a b;
end BasicDefinitions
2024-11-20 07:37:57 -08:00
-- --------------------------------------------------------------------------------------------------------------
-- | ALGEBRAIC STRUCTURES |
-- --------------------------------------------------------------------------------------------------------------
-- a set `S` with binary operation `op` is a semigroup if its operation is associative
2024-12-10 20:31:53 -08:00
def semigroup (S : ★) (op : binop S) : ★ := assoc S op;
2024-11-20 07:37:57 -08:00
section Monoid
2024-12-10 23:36:34 -08:00
-- Let `M` be a set with binary operation `*` and element `e`.
variable (M : ★) (* : binop M) (e : M);
2024-12-10 20:31:53 -08:00
infixl 50 *;
2024-12-10 23:36:34 -08:00
-- a set `M` with binary operation `(*)` and element `e` is a monoid
def monoid : ★ := (semigroup M (*)) ∧ (id M (*) e);
2024-12-10 20:31:53 -08:00
-- Suppose `(M, *, e)` is a monoid
hypothesis (Hmonoid : monoid);
-- some "getters" for `monoid` so we don't have to do a bunch of very verbose
-- and-eliminations every time we want to use something
2024-12-10 23:36:34 -08:00
def id_lm : id_l M (*) e :=
and_elim_l (id_l M (*) e) (id_r M (*) e)
(and_elim_r (semigroup M (*)) (id M (*) e) Hmonoid);
2024-12-10 23:36:34 -08:00
def id_rm : id_r M (*) e :=
and_elim_r (id_l M (*) e) (id_r M (*) e)
(and_elim_r (semigroup M (*)) (id M (*) e) Hmonoid);
2024-12-10 23:36:34 -08:00
def assoc_m : assoc M (*) := and_elim_l (semigroup M (*)) (id M (*) e) Hmonoid;
-- now we can prove that, for any monoid, if `a` is a left identity, then it
-- must be "the" identity
2024-12-10 23:36:34 -08:00
def monoid_id_l_implies_identity (a : M) (H : id_l M (*) a) : eq M a e :=
-- WTS a = a * e = e
-- we can use `eq_trans` to glue proofs of `a = a * e` and `a * e = e` together
2024-12-10 20:31:53 -08:00
eq_trans M a (a * e) e
-- first, `a = a * e`, but we'll use `eq_sym` to flip it around
2024-12-10 20:31:53 -08:00
(eq_sym M (a * e) a
-- now the goal is to show `a * e = a`, which follows immediately from `id_r`
(id_rm a))
-- now we need to show that `a * e = e`, but this immediately follows from `H`
(H e);
-- the analogous result for right identities
2024-12-10 23:36:34 -08:00
def monoid_id_r_implies_identity (a : M) (H : id_r M (*) a) : eq M a e :=
-- this time, we'll show `a = e * a = e`
2024-12-10 20:31:53 -08:00
eq_trans M a (e * a) e
-- first, `a = e * a`
2024-12-10 20:31:53 -08:00
(eq_sym M (e * a) a
-- this time, it immediately follows from `id_l`
(id_lm a))
-- and `e * a = e`
(H e);
end Monoid
section Group
2024-12-10 23:36:34 -08:00
variable (G : ★) (* : binop G) (e : G) (i : unop G);
2024-12-10 20:31:53 -08:00
infixl 50 *;
-- groups are just monoids with inverses
2024-12-10 23:36:34 -08:00
def has_inverses : ★ := forall (a : G), inv G (*) e a (i a);
2024-12-10 23:36:34 -08:00
def group : ★ := (monoid G (*) e) ∧ has_inverses;
hypothesis (Hgroup : group);
-- more "getters"
2024-12-10 23:36:34 -08:00
def monoid_g : monoid G (*) e := and_elim_l (monoid G (*) e) has_inverses Hgroup;
def assoc_g : assoc G (*) := assoc_m G (*) e monoid_g;
def id_lg : id_l G (*) e := id_lm G (*) e (and_elim_l (monoid G (*) e) has_inverses Hgroup);
def id_rg : id_r G (*) e := id_rm G (*) e (and_elim_l (monoid G (*) e) has_inverses Hgroup);
def inv_g : forall (a : G), inv G (*) e a (i a) := and_elim_r (monoid G (*) e) has_inverses Hgroup;
def left_inverse (a b : G) := inv_l G (*) e a b;
def right_inverse (a b : G) := inv_r G (*) e a b;
def inv_lg (a : G) : left_inverse a (i a) := and_elim_l (inv_l G (*) e a (i a)) (inv_r G (*) e a (i a)) (inv_g a);
def inv_rg (a : G) : right_inverse a (i a) := and_elim_r (inv_l G (*) e a (i a)) (inv_r G (*) e a (i a)) (inv_g a);
-- An interesting theorem: left inverses are unique, i.e. if b * a = e, then b = a^-1
def left_inv_unique (a b : G) (h : left_inverse a b) : eq G b (i a) :=
-- b = b * e
-- = b * (a * a^-1)
-- = (b * a) * a^-1
-- = e * a^-1
-- = a^-1
2024-12-10 20:31:53 -08:00
eq_trans G b (b * e) (i a)
-- b = b * e
2024-12-10 20:31:53 -08:00
(eq_sym G (b * e) b (id_rg b))
-- b * e = a^-1
2024-12-10 20:31:53 -08:00
(eq_trans G (b * e) (b * (a * i a)) (i a)
--b * e = b * (a * a^-1)
2024-12-10 23:36:34 -08:00
(eq_cong G G e (a * i a) ((*) b)
-- e = a * a^-1
2024-12-10 20:31:53 -08:00
(eq_sym G (a * i a) e (inv_rg a)))
-- b * (a * a^-1) = a^-1
2024-12-10 20:31:53 -08:00
(eq_trans G (b * (a * i a)) (b * a * i a) (i a)
-- b * (a * a^-1) = (b * a) * a^-1
(assoc_g b a (i a))
-- (b * a) * a^-1 = a^-1
2024-12-10 20:31:53 -08:00
(eq_trans G (b * a * i a) (e * i a) (i a)
-- (b * a) * a^-1 = e * a^-1
2024-12-10 20:31:53 -08:00
(eq_cong G G (b * a) e (fun (x : G) => x * i a) h)
-- e * a^-1 = a^-1
(id_lg (i a)))));
2024-12-06 16:30:44 -08:00
-- And so are right inverses
def right_inv_unique (a b : G) (h : right_inverse a b) : eq G b (i a) :=
-- b = e * b
-- = (a^-1 * a) * b
-- = a^-1 * (a * b)
-- = a^-1 * e
-- = a^-1
2024-12-10 20:31:53 -08:00
eq_trans G b (e * b) (i a)
2024-12-06 16:30:44 -08:00
-- b = e * b
2024-12-10 20:31:53 -08:00
(eq_sym G (e * b) b (id_lg b))
2024-12-06 16:30:44 -08:00
-- e * b = a^-1
2024-12-10 20:31:53 -08:00
(eq_trans G (e * b) (i a * a * b) (i a)
2024-12-06 16:30:44 -08:00
-- e * b = (a^-1 * a) * b
2024-12-10 20:31:53 -08:00
(eq_cong G G e (i a * a) (fun (x : G) => x * b)
2024-12-06 16:30:44 -08:00
-- e = (a^-1 * a)
2024-12-10 20:31:53 -08:00
(eq_sym G (i a * a) e (inv_lg a)))
2024-12-06 16:30:44 -08:00
-- (a^-1 * a) * b = a^-1
2024-12-10 20:31:53 -08:00
(eq_trans G (i a * a * b) (i a * (a * b)) (i a)
2024-12-06 16:30:44 -08:00
-- (a^-1 * a) * b = a^-1 * (a * b)
2024-12-10 20:31:53 -08:00
(eq_sym G (i a * (a * b)) (i a * a * b) (assoc_g (i a) a b))
2024-12-06 16:30:44 -08:00
-- a^-1 * (a * b) = a^-1
2024-12-10 20:31:53 -08:00
(eq_trans G (i a * (a * b)) (i a * e) (i a)
2024-12-06 16:30:44 -08:00
-- a^-1 * (a * b) = a^-1 * e
2024-12-10 23:36:34 -08:00
(eq_cong G G (a * b) e ((*) (i a)) h)
2024-12-06 16:30:44 -08:00
-- a^-1 * e = a^-1
(id_rg (i a)))));
2024-12-08 17:40:37 -08:00
-- the classic shoes and socks theorem, namely that (a * b)^-1 = b^-1 * a^-1
2024-12-10 20:31:53 -08:00
def shoes_and_socks (a b : G) : eq G (i (a * b)) (i b * i a) :=
eq_sym G (i b * i a) (i (a * b))
(right_inv_unique (a * b) (i b * i a)
2024-12-08 17:40:37 -08:00
-- WTS: (a * b) * (b^-1 * a^-1) = e
(let
-- helper function to prove that x * a^-1 = y * a^-1 given x = y
2024-12-10 20:31:53 -08:00
(under_ai (x y : G) (h : eq G x y) := eq_cong G G x y (fun (z : G) => z * (i a)) h)
2024-12-08 17:40:37 -08:00
in
-- (a * b) * (b^-1 * a^-1) = ((a * b) * b^-1) * a^-1
-- = (a * (b * b^-1)) * a^-1
-- = (a * e) * a^-1
-- = a * a^-1
-- = e
2024-12-10 20:31:53 -08:00
eq_trans G (a * b * (i b * i a)) (a * b * i b * i a) e
2024-12-08 17:40:37 -08:00
-- (a * b) * (b^-1 * a^-1) = ((a * b) * b^-1) * a^-1
2024-12-10 20:31:53 -08:00
(assoc_g (a * b) (i b) (i a))
2024-12-08 17:40:37 -08:00
-- ((a * b) * b^-1) * a^-1 = e
2024-12-10 20:31:53 -08:00
(eq_trans G (a * b * i b * i a) (a * (b * i b) * i a) e
2024-12-08 17:40:37 -08:00
-- ((a * b) * b^-1) * a^-1 = (a * (b * b^-1)) * a^-1
2024-12-10 20:31:53 -08:00
(under_ai (a * b * i b) (a * (b * i b)) (eq_sym G (a * (b * i b)) (a * b * i b) (assoc_g a b (i b))))
2024-12-08 17:40:37 -08:00
-- (a * (b * b^-1)) * a^-1 = e
2024-12-10 20:31:53 -08:00
(eq_trans G (a * (b * i b) * i a) (a * e * i a) e
2024-12-08 17:40:37 -08:00
-- (a * (b * b^-1)) * a^-1 = (a * e) * a^-1
2024-12-10 20:31:53 -08:00
(eq_cong G G (b * i b) e (fun (x : G) => (a * x * i a)) (inv_rg b))
2024-12-08 17:40:37 -08:00
-- (a * e) * a^-1 = e
2024-12-10 20:31:53 -08:00
(eq_trans G (a * e * i a) (a * i a) e
2024-12-08 17:40:37 -08:00
-- (a * e) * a^-1 = a * a^-1
2024-12-10 20:31:53 -08:00
(under_ai (a * e) a (id_rg a))
2024-12-08 17:40:37 -08:00
-- a * a^-1 = e
(inv_rg a))))
end));
end Group