example definition theorem program
Let be a Function — think of as apples, as buckets, and as putting each apple in a bucket. Three monotone maps between the power sets are induced automatically, forming two Galois connections :
| map | formula | apples & buckets |
|---|---|---|
| preimage / pullback | all apples in the chosen buckets | |
| direct image (left adjoint) | buckets containing at least one chosen apple | |
| dual image (right adjoint) | buckets all of whose apples are chosen (empty buckets count) |
Sources: 7 Sketches Example 1.117, Exercise 1.118; Kittenlab Lecture 14 (“Pullback”, “Direct image”); DaoFP §11.3–11.4 (dependent sum and product as adjoints to substitution), §7.4.4 of 7 Sketches (quantification); CTfS Example 3.4.3.2, Exercise 3.4.3.4, §5.1.1.10 (“Quantifiers as adjoints”)
Kittenlab (Lecture 14) phrases the same in terms of characteristic functions: (“pullback”, also “preimage”), and (its is 7 Sketches’ ). Both preserve the ordering of subsets, making a contravariant and a covariant Functor .
Proof that is monotone (Kittenlab): if and holds then , hence , hence .
Why it matters. “We did not invent these mappings: they were induced by . It is one of the pleasures of category theory that adjoints so often turn out to have interesting semantic interpretations.” The adjoints are the existential and universal quantifiers of topos logic (7 Sketches §7.4.4) and, at the level of categories, the Dependent Sum Dependent Product adjoints to the base-change (DaoFP Ch. 11); for databases, (Data Migration Functor).
Example (7S Exercise 1.118 solution): projecting down. , ; ; (the empty bucket), .
Docs: FinSets — Kittenlab Lecture 14
Builds on: Category (FinFunction) — run that note’s Julia code first.
# Kittenlab Lecture 14 on subsets of {1..n} as BitVectors
struct FinSet′; n::Int end
struct FinFunction′; dom::FinSet′; codom::FinSet′; values::Vector{Int} end
const FinSubset = BitVector
pullback_subset(f::FinFunction′, U::FinSubset) = FinSubset([U[y] for y in f.values]) # f^*
function direct_image(f::FinFunction′, U::FinSubset) # f_!
V = FinSubset(zeros(Bool, f.codom.n))
for i in 1:f.dom.n
U[i] && (V[f.values[i]] = true)
end
V
end
function dual_image(f::FinFunction′, U::FinSubset) # f_*
FinSubset([all(U[i] for i in 1:f.dom.n if f.values[i] == y) for y in 1:f.codom.n])
end
f = FinFunction′(FinSet′(3), FinSet′(3), [1, 3, 3]) # a₁ ↦ a, c₁,c₂ ↦ c
direct_image(f, FinSubset([true, true, false])) # {a, c}
dual_image(f, FinSubset([false, false, false])) # {b}: the empty bucketCatlab version (run in a fresh Julia session — Catlab exports its own compose, id, FinFunction, …):
# Catlab (a separate session): fibers of a FinFunction
using Catlab
g = FinFunction([1, 3, 3], 3)
preimage(g, 3) # [2, 3]#check @Set.image -- f '' A = f_! A
#check @Set.preimage -- f ⁻¹' B = f^* B
-- the two Galois connections
#check @Set.image_preimage -- GaloisConnection (Set.image f) (Set.preimage f)
#check @Set.preimage_kernImage -- GaloisConnection (Set.preimage f) (Set.kernImage f) (f_* = kernImage)type Subset a = a -> Bool
preimage :: (a -> b) -> Subset b -> Subset a
preimage f chi = chi . f -- f^*
directImage :: Eq b => [a] -> (a -> b) -> Subset a -> Subset b
directImage as f chi b = or [chi a | a <- as, f a == b] -- f_! : ∃
dualImage :: Eq b => [a] -> (a -> b) -> Subset a -> Subset b
dualImage as f chi b = and [chi a | a <- as, f a == b] -- f_* : ∀