Programs in a rose tree machine (RTM) #
This file contains the main definition of the rose tree machine, its programs including semantics and time and space resource consumption.
Main definitions and notations #
Prog- the programProgSem- semantics and resource consumptionInPlace- a fragment of the language that can be easily simulated using multi-tape Turing machines.Prog.ComputesInTimeAndSpace- this defines the complexity notion for the RTM computation model, based onDatavalues.Prog.ComputesBoolFunInTimeAndSpace- the complexity notion transferred to functions on binary strings, this making it compatible to all other computation models.ComputableInOTime- generic time-complexity in the RTM modelComputableInOSpace- generic space-complexity in the RTM model
Prog is the syntax representation of a functional language that has a resource consumption
model which is compatible to that of a Turing machine.
The data structure it operates on is a rose tree (Data). The advantage of this data structure
is that the majority of lean data types have a direct encoding.
- var
(id : ℕ)
: Prog
Variable reference at de Bruijn level
id. - empty : Prog
The empty constructor of
Data. - cons
(h t : Prog)
: Prog
The
consconstructor ofData: prepend the value ofhto the list value oft. - elim
(v emp cs : Prog)
: Prog
Destructor of
Data: evaluatev; if it isempty, runemp; otherwise destructure intoheadandtailand apply the functioncstoheadand then totail. - ifEq
(x y then_ else_ : Prog)
: Prog
Equality comparison: If
xevaluates to the same data asy, runthen_, else runelse_. This can be simulated usingwhile_below, but it is useful to have. - while_
(init body : Prog)
: Prog
While loop: evaluate
initto the starting accumulator;bodymust evaluate to a one-argument function which is applied to the current accumulator on each iteration. Return the accumulator if its head is empty. - fn
(body : Prog)
: Prog
Abstraction / closure in one variable.
- app
(fn arg : Prog)
: Prog
Function application.
Instances For
Equations
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
Whether the program Prog refers to the variable at de Bruijn index i.
Equations
- One or more equations did not get rendered due to their size.
- Complexity.RoseTreeMachine.Prog.hasVar i (Complexity.RoseTreeMachine.Prog.var j) = decide (i = j)
- Complexity.RoseTreeMachine.Prog.hasVar i Complexity.RoseTreeMachine.Prog.empty = false
- Complexity.RoseTreeMachine.Prog.hasVar i (h.cons t) = (Complexity.RoseTreeMachine.Prog.hasVar i h || Complexity.RoseTreeMachine.Prog.hasVar i t)
- Complexity.RoseTreeMachine.Prog.hasVar i (init.while_ body) = (Complexity.RoseTreeMachine.Prog.hasVar i init || Complexity.RoseTreeMachine.Prog.hasVar i body)
- Complexity.RoseTreeMachine.Prog.hasVar i body.fn = Complexity.RoseTreeMachine.Prog.hasVar i body
- Complexity.RoseTreeMachine.Prog.hasVar i (f.app arg) = (Complexity.RoseTreeMachine.Prog.hasVar i f || Complexity.RoseTreeMachine.Prog.hasVar i arg)
Instances For
The size of a closure's environment, restricted to variables that body actually
references.
Equations
- Complexity.RoseTreeMachine.closureSize body env = Complexity.RoseTreeMachine.closureSize.go body 0 env
Instances For
The size of a Value. The size of data is the length of its encoding and the size
of a closure is the sum of the sizes of the referenced variables.
Equations
Instances For
The closure size over an appended environment splits into the size over the prefix plus the
contribution of the suffix (counted starting at depth l1.length).
closureSize.go is monotone in the set of accessed variables.
If every variable accessed by body1 is also accessed by body2, then body1 has a smaller
closure size over any environment.
Semantics of Prog including time and space resource bounds.
ProgSem σ p x t s means that on environment σ, the program p evaluates to the value
x and uses t time and s space.
- var {σ : List Value} {i : ℕ} : ProgSem σ (Prog.var i) (σ[i]?.getD Value.empty) (σ[i]?.getD Value.empty).size (σ[i]?.getD Value.empty).size
- empty {σ : List Value} : ProgSem σ Prog.empty Value.empty 2 2
- cons {σ : List Value} {head : Prog} {hd : Data} {hd_t hd_s : ℕ} {tail : Prog} {tl : Data} {tl_t tl_s : ℕ} (h₁ : ProgSem σ head (Value.data hd) hd_t hd_s) (h₂ : ProgSem σ tail (Value.data tl) tl_t tl_s) : ProgSem σ (head.cons tail) (Value.data (Data.l (hd :: tl.asList))) (hd_t + tl_t) (hd_s + tl_s)
- elim_nil
{σ : List Value}
{val : Prog}
{t_v s_v : ℕ}
{emp : Prog}
{r : Value}
{t_emp s_emp : ℕ}
{cs : Prog}
(h₁ : ProgSem σ val (Value.data (Data.l [])) t_v s_v)
(h₂ : ProgSem σ emp r t_emp s_emp)
: ProgSem σ (val.elim emp cs) r (t_v + t_emp) (s_v + s_emp)
elim, empty branch:vis the empty list, so runempin the current environment. - elim_cons
{σ : List Value}
{val : Prog}
{hd : Data}
{tl : List Data}
{t_v s_v : ℕ}
{cs : Prog}
{cv : Value}
{t_cs s_cs : ℕ}
{cv' : Value}
{t₁ s₁ : ℕ}
{r : Value}
{t₂ s₂ : ℕ}
{emp : Prog}
(h_v : ProgSem σ val (Value.data (Data.l (hd :: tl))) t_v s_v)
(h_cs : ProgSem σ cs cv t_cs s_cs)
(h_app₁ : AppSem cv (Value.data hd) cv' t₁ s₁)
(h_app₂ : AppSem cv' (Value.data (Data.l tl)) r t₂ s₂)
: ProgSem σ (val.elim emp cs) r (t_v + t_cs + t₁ + t₂) (s_v + s_cs + s₁ + s₂)
elim, cons branch:vdestructures tohd :: tl; evaluate the functioncsto a closure and apply it first tohdand then totl(socsis a curried two-argument function). - ifEq_then {σ : List Value} {x : Prog} {vx : Data} {t_x s_x : ℕ} {y : Prog} {t_y s_y : ℕ} {then_ : Prog} {r : Value} {t_then s_then : ℕ} {else_ : Prog} (h_x : ProgSem σ x (Value.data vx) t_x s_x) (h_y : ProgSem σ y (Value.data vx) t_y s_y) (h_then : ProgSem σ then_ r t_then s_then) : ProgSem σ (x.ifEq y then_ else_) r (t_x + t_y + t_then) (s_x + s_y + s_then)
- ifEq_else {σ : List Value} {x : Prog} {vx : Data} {t_x s_x : ℕ} {y : Prog} {vy : Data} {t_y s_y : ℕ} {else_ : Prog} {r : Value} {t_else s_else : ℕ} {then_ : Prog} (h_x : ProgSem σ x (Value.data vx) t_x s_x) (h_y : ProgSem σ y (Value.data vy) t_y s_y) (h_neq : vx ≠ vy) (h_else : ProgSem σ else_ r t_else s_else) : ProgSem σ (x.ifEq y then_ else_) r (t_x + t_y + t_else) (s_x + s_y + s_else)
- while_ {σ : List Value} {init : Prog} {acc : Data} {t_init s_init : ℕ} {body : Prog} {bodyVal : Value} {t_body s_body : ℕ} {r : Data} {t_w s_w : ℕ} (h_init : ProgSem σ init (Value.data acc) t_init s_init) (h_body : ProgSem σ body bodyVal t_body s_body) (h_while : WhileSem bodyVal acc r t_w s_w) : ProgSem σ (init.while_ body) (Value.data r) (t_init + t_body + t_w) (s_init + s_body + s_w)
- fn {σ : List Value} {body : Prog} : ProgSem σ body.fn (Value.closure body σ) (Value.closure body σ).size (Value.closure body σ).size
- app {σ : List Value} {fn : Prog} {fv : Value} {t_f s_f : ℕ} {arg : Prog} {v : Value} {t_a s_a : ℕ} {r : Value} {t_b s_b : ℕ} (h_fn : ProgSem σ fn fv t_f s_f) (h_arg : ProgSem σ arg v t_a s_a) (h_app : AppSem fv v r t_b s_b) : ProgSem σ (fn.app arg) r (t_f + t_a + t_b) (s_f + s_a + s_b)
Instances For
Application of a value to an argument value. AppSem f v r t s means that applying the
closure f to the argument v yields r using t time and s space. Only closures can be
applied; applying a first-order value has no derivation (the program is stuck).
- mk {σ : List Value} {v : Value} {body : Prog} {r : Value} {t s : ℕ} (h_body : ProgSem (σ ++ [v]) body r t s) : AppSem (Value.closure body σ) v r t s
Instances For
Iterates the closure bodyVal of a while_ loop, threading the accumulator.
WhileSem bodyVal acc r t s means that, starting from accumulator acc, repeatedly applying
bodyVal to the current accumulator eventually yields result r using t time and s space.
Before each iteration the halting condition is checked on the current accumulator: iteration
terminates (with the accumulator as result) when acc is empty or its head is empty.
Otherwise bodyVal is applied and its result becomes the new accumulator. Non-terminating
loops simply have no derivation.
- halt {bodyVal : Value} {acc : Data} (h_stop : acc.asList.head?.getD (Data.l []) = Data.l []) : WhileSem bodyVal acc acc acc.size acc.size
- step {bodyVal : Value} {acc v : Data} {t_b s_b : ℕ} {r : Data} {t_r s_r : ℕ} (h_cont : acc.asList.head?.getD (Data.l []) ≠ Data.l []) (h_app : AppSem bodyVal (Value.data acc) (Value.data v) t_b s_b) (h_rest : WhileSem bodyVal v r t_r s_r) : WhileSem bodyVal acc r (t_b + t_r) (max s_b s_r)
Instances For
Producing a value costs at least its size, in both time and space. This holds because every
ProgSem derivation either reads/builds the value directly (charging its size) or returns a
value produced by a sub-derivation whose cost it includes. Provable by mutual induction over
ProgSem/AppSem/WhileSem.
Value-determinism of the relational semantics: a program evaluates to at most one value in a
given environment. The mutually-defined AppSem/WhileSem relations are value-deterministic too.
The potential branch overlaps (elim_nil/elim_cons, ifEq_then/ifEq_else) are ruled out by
value-determinism of the scrutinee / compared values, supplied by the induction hypotheses.
The program p computes the value y from the value x in time t and space s.
Equations
Instances For
The program p computes the function f (on binary strings) in time t and space s.
This is the main definition that defines complexity for this computation model.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The function f is computable in time t in the RTM model, up to constant factors
Equations
Instances For
The function f is computable in space s in the RTM model, up to constant factors
Equations
Instances For
The in-place (first-order) fragment of the functional language.
InPlace p holds when every fn in p occurs in an immediately-consumed position — as the
operator of an app, or as the (curried) branch of an elim/while_. Consequently no
closure ever escapes: every abstraction is created and used on the spot, so all values that
flow through the environment are first-order Data. This is exactly the fragment a
defunctionalising compiler targets, and it is closed under the operational semantics.
A Turing machine can directly implement this fragment without the need for closures: One tape is used for each "node" in the syntax tree.
- var {i : ℕ} : InPlace (Prog.var i)
- empty : InPlace Prog.empty
- cons {h t : Prog} (hh : InPlace h) (ht : InPlace t) : InPlace (h.cons t)
- elim {v emp body : Prog} (hv : InPlace v) (hemp : InPlace emp) (hbody : InPlace body) : InPlace (v.elim emp body.fn.fn)
- ifEq {x y then_ else_ : Prog} (hx : InPlace x) (hy : InPlace y) (hthen : InPlace then_) (helse : InPlace else_) : InPlace (x.ifEq y then_ else_)
- while_ {init body : Prog} (hinit : InPlace init) (hbody : InPlace body) : InPlace (init.while_ body.fn)
- app
{body arg : Prog}
(hbody : InPlace body)
(harg : InPlace arg)
: InPlace (body.fn.app arg)
appwhose operator is a literal one-argument functionfn body: this is alet, binding the value ofargand runningbodyon the spot. The abstraction is consumed immediately, so no closure escapes. Nested applications of this form give multi-argumentlet-chains; arbitrary arities follow by repeated use of this constructor.