The primitive recursive functions #
The primitive recursive functions are the least collection of functions
ℕ → ℕ
which are closed under projections (using the pair
pairing function), composition, zero, successor, and primitive recursion
(i.e. Nat.rec
where the motive is C n := ℕ
).
We can extend this definition to a large class of basic types by
using canonical encodings of types as natural numbers (Gödel numbering),
which we implement through the type class Encodable
. (More precisely,
we need that the composition of encode with decode yields a
primitive recursive function, so we have the Primcodable
type class
for this.)
In the above, the pairing function is primitive recursive by definition.
This deviates from the textbook definition of primitive recursive functions,
which instead work with n
-ary functions. We formalize the textbook
definition in Nat.Primrec'
. Nat.Primrec'.prim_iff
then proves it is
equivalent to our chosen formulation. For more discussionn of this and
other design choices in this formalization, see [carneiro2019].
Main definitions #
Nat.Primrec f
:f
is primitive recursive, for functionsf : ℕ → ℕ
Primrec f
:f
is primitive recursive, for functions betweenPrimcodable
typesPrimcodable α
: well-behaved encoding ofα
intoℕ
, i.e. one such that roundtripping through the encoding functions adds no computational power
References #
- [Mario Carneiro, Formalizing computability theory via partial recursive functions][carneiro2019]
Calls the given function on a pair of entries n
, encoded via the pairing function.
Equations
- Nat.unpaired f n = f (Nat.unpair n).1 (Nat.unpair n).2
Instances For
The primitive recursive functions ℕ → ℕ
.
- zero : Nat.Primrec fun (x : ℕ) => 0
- succ : Nat.Primrec succ
- left : Nat.Primrec fun (n : ℕ) => (unpair n).1
- right : Nat.Primrec fun (n : ℕ) => (unpair n).2
- pair {f g : ℕ → ℕ} : Nat.Primrec f → Nat.Primrec g → Nat.Primrec fun (n : ℕ) => Nat.pair (f n) (g n)
- comp {f g : ℕ → ℕ} : Nat.Primrec f → Nat.Primrec g → Nat.Primrec fun (n : ℕ) => f (g n)
- prec {f g : ℕ → ℕ} : Nat.Primrec f → Nat.Primrec g → Nat.Primrec (unpaired fun (z n : ℕ) => Nat.rec (f z) (fun (y IH : ℕ) => g (Nat.pair z (Nat.pair y IH))) n)
Instances For
A Primcodable
type is, essentially, an Encodable
type for which
the encode/decode functions are primitive recursive.
However, such a definition is circular.
Instead, we ask that the composition of decode : ℕ → Option α
with
encode : Option α → ℕ
is primitive recursive. Said composition is
the identity function, restricted to the image of encode
.
Thus, in a way, the added requirement ensures that no predicates
can be smuggled in through a cunning choice of the subset of ℕ
into
which the type is encoded.
- prim : Nat.Primrec fun (n : ℕ) => Encodable.encode (Encodable.decode n)
Instances
Equations
- Primcodable.ofDenumerable α = { toEncodable := Denumerable.toEncodable, prim := ⋯ }
Builds a Primcodable
instance from an equivalence to a Primcodable
type.
Equations
- Primcodable.ofEquiv α e = { toEncodable := Encodable.ofEquiv α e, prim := ⋯ }
Instances For
Equations
- Primcodable.empty = { toEncodable := IsEmpty.toEncodable, prim := Nat.Primrec.zero }
Equations
- Primcodable.unit = { toEncodable := PUnit.encodable, prim := Primcodable.unit._proof_3 }
Equations
- Primcodable.option = { toEncodable := Option.encodable, prim := ⋯ }
Equations
- Primcodable.bool = { toEncodable := Bool.encodable, prim := Primcodable.bool._proof_5 }
Primrec f
means f
is primitive recursive (after
encoding its input and output as natural numbers).
Equations
- Primrec f = Nat.Primrec fun (n : ℕ) => Encodable.encode (Option.map f (Encodable.decode n))
Instances For
Equations
- Primcodable.prod = { toEncodable := Encodable.Prod.encodable, prim := ⋯ }
Alias of Primrec.list_getElem?₁
.
Primrec₂ f
means f
is a binary primitive recursive function.
This is technically unnecessary since we can always curry all
the arguments together, but there are enough natural two-arg
functions that it is convenient to express this directly.
Instances For
PrimrecPred p
means p : α → Prop
is a (decidable)
primitive recursive predicate, which is to say that
decide ∘ p : α → Bool
is primitive recursive.
Equations
- PrimrecPred p = Primrec fun (a : α) => decide (p a)
Instances For
PrimrecRel p
means p : α → β → Prop
is a (decidable)
primitive recursive relation, which is to say that
decide ∘ p : α → β → Bool
is primitive recursive.
Equations
- PrimrecRel s = Primrec₂ fun (a : α) (b : β) => decide (s a b)
Instances For
Alias of Primrec.list_idxOf₁
.
A function is PrimrecBounded
if its size is bounded by a primitive recursive function
Equations
- Primrec.PrimrecBounded f = ∃ (g : α → ℕ), Primrec g ∧ ∀ (x : α), Encodable.encode (f x) ≤ g x
Instances For
To show a function f : α → ℕ
is primitive recursive, it is enough to show that the function
is bounded by a primitive recursive function and that its graph is primitive recursive
Equations
- Primcodable.sum = { toEncodable := Sum.encodable, prim := ⋯ }
Equations
- Primcodable.list = { toEncodable := List.encodable, prim := ⋯ }
Alias of Primrec.sumInl
.
Alias of Primrec.sumInr
.
Alias of Primrec.sumCasesOn
.
Alias of Primrec.list_getElem?
.
Alias of Primrec.list_idxOf
.
A subtype of a primitive recursive predicate is Primcodable
.
Equations
- Primcodable.subtype hp = { toEncodable := Subtype.encodable, prim := ⋯ }
Instances For
Equations
- Primcodable.fin = Primcodable.ofEquiv { a : ℕ // id a < n } Fin.equivSubtype
Equations
Equations
Equations
An alternative inductive definition of Primrec
which
does not use the pairing function on ℕ, and so has to
work with n-ary functions on ℕ instead of unary functions.
We prove that this is equivalent to the regular notion
in to_prim
and of_prim
.
- zero : Primrec' fun (x : List.Vector ℕ 0) => 0
- succ : Primrec' fun (v : List.Vector ℕ 1) => v.head.succ
- get {n : ℕ} (i : Fin n) : Primrec' fun (v : List.Vector ℕ n) => v.get i
- comp {m n : ℕ} {f : List.Vector ℕ n → ℕ} (g : Fin n → List.Vector ℕ m → ℕ) : Primrec' f → (∀ (i : Fin n), Primrec' (g i)) → Primrec' fun (a : List.Vector ℕ m) => f (List.Vector.ofFn fun (i : Fin n) => g i a)
- prec {n : ℕ} {f : List.Vector ℕ n → ℕ} {g : List.Vector ℕ (n + 2) → ℕ} : Primrec' f → Primrec' g → Primrec' fun (v : List.Vector ℕ (n + 1)) => Nat.rec (f v.tail) (fun (y IH : ℕ) => g (y ::ᵥ IH ::ᵥ v.tail)) v.head
Instances For
A function from vectors to vectors is primitive recursive when all of its projections are.
Equations
- Nat.Primrec'.Vec f = ∀ (i : Fin m), Nat.Primrec' fun (v : List.Vector ℕ n) => (f v).get i