Adequacy: the evaluator computes the denotation, at the declared factors #
Two halves of the conversion story have been proved separately and never joined.
LambdaS.Declare says when a set of unit declarations determines a valuation,
and rejects the sets that do not. LambdaS.Conversion says a valuation makes
conversion path-independent. LambdaS.Fundamental gives the denotational
semantics, in which convert reads that valuation for its factor. (Unit
application also consults it, but only to pick the index at which a family is
sampled.)
LambdaS.Dynamics gives the evaluator, which takes the conversion factor from
an abstract oracle cf, constrained by nothing at all.
So the declarations determine a number, and the evaluator multiplies by a
number, and nothing but eval_adeq says they are the same number.
Every worked example elsewhere passes fun _ _ => 1.0; this file is where the
conversion oracle is finally pinned.
eval_adeq is the join. Take the oracle to be conv V (the conversion the
valuation determines, hence the one the declarations determine), and evaluation
of a well-typed term agrees with its denotation, unit and magnitude both. The
convert case is where the content is: the evaluator's factor is
conv V (substU η u) (substU η v) and the denotation's is
conv (V.pull η) u v, and Scaling.scale_pull says those are equal.
Why the relation carries the environments #
Adeq is indexed by the runtime unit and dimension environments rather than
stated at grounded types. That is what keeps it free of transports: at ∀u:δ. τ
the relation recurses at Fin.cons μ η and at τ itself, so the denotation
stays in Ty.den τ and never has to be cast along Ty.den_subst. The scaling
follows the environments by Scaling.pull, and pull_cons is the lemma that
says instantiating a unit variable and extending the scaling agree.
Function, unit-polymorphic and dimension-polymorphic values are related
behaviorally, as in LambdaS.Normalization: a closure is adequate when
applying it to adequate arguments yields adequate results. Nothing is said about
the closure's captured scope, which is what lets the relation live at a single
type while the evaluator works at many.
Adequacy at a type: a runtime value carries exactly the magnitude the denotation predicts, at exactly the unit the type predicts.
Equations
- One or more equations did not get rendered due to their size.
- LambdaS.Adeq V x✝² x✝¹ (LambdaS.Ty.Q u) x✝ x_10 = (x✝ = LambdaS.Val.scalar { mag := x_10, unit := LambdaS.substU x✝² u })
- LambdaS.Adeq V x✝² x✝¹ (LambdaS.Ty.vec Vs) x✝ f = (x✝ = LambdaS.Val.vector (List.ofFn f) (List.map (LambdaS.substU x✝²) Vs))
Instances For
Adequacy of an environment, pointwise. An inductive rather than a recursive
definition so that its indices unify up to definitional equality: the binder
cases present the context as Γ.weaken, not as a literal list.
- nil {B D : Type} [Fintype B] [DecidableEq B] [Fintype D] [DecidableEq D] [UnitSys B D] {V : Scaling B 0} {j k : ℕ} {η : UEnv B k} {δ : DEnv D j} {ρ : List (Val ℝ B D)} {ρd : Env []} : EnvAdeq V η δ [] ρ ρd
- cons {B D : Type} [Fintype B] [DecidableEq B] [Fintype D] [DecidableEq D] [UnitSys B D] {V : Scaling B 0} {j k : ℕ} {η : UEnv B k} {δ : DEnv D j} {τ : Ty B D j k} {Γ : Ctx B D j k} {w : Val ℝ B D} {ρ : List (Val ℝ B D)} {ρd : Env (τ :: Γ)} : Adeq V η δ τ w ρd.1 → EnvAdeq V η δ Γ ρ ρd.2 → EnvAdeq V η δ (τ :: Γ) (w :: ρ) ρd
Instances For
Transporting adequacy along a substitution #
Grounding the type corresponds to composing the environments, which is what
lets the binder cases of eval_adeq appeal to the induction hypothesis. It is
the same shape as rel_ground, and for the same reason: Adeq is a type-indexed
family, so a change of type has to be matched by a transport of the denotation.
Adequacy transports along grounding.
Instantiating a unit variable.
Weakening under a dimension binder.
Instantiating a dimension variable.
The list arithmetic the space forms need #
The dot product of two tabulated vectors, in the form Num.matVec unfolds
to: the class default for dot is inlined, so this is the shape that appears.
Looking up an adequate environment gives an adequate value.
Adequacy. With the conversion oracle taken to be the one the valuation determines, evaluation of a well-typed term agrees with its denotation: the unit it carries and the magnitude it holds.
This is the theorem that joins the declaration story to the evaluator. The
convert case is its content: the evaluator multiplies by
conv V (substU η u) (substU η v) and the denotation by conv (V.pull η) u v,
and Scaling.scale_pull says those agree. Everything else is bookkeeping.
What the declarations buy #
LambdaS.Declare decides whether a set of unit declarations determines a
valuation, and rejects the sets that do not. eval_adeq says what a valuation
is worth to the evaluator. Composing them is the point of this file: the number
the evaluator multiplies by is the number the declarations name.
And when the declarations conflict, not_satisfiable_of_chain says no valuation
exists, so there is no oracle to run with, rather than a choice of oracles to
pick wrongly between.
Adequacy for closed terms of scalar type.
The evaluator computes the denotation. Every well-typed closed term of scalar type evaluates (at some finite fuel, by normalization) to exactly the magnitude its denotation predicts, carrying exactly the unit its type predicts, provided the conversion oracle is the one the valuation determines.
This is the join. Before it, LambdaS.Dynamics multiplied by a number nobody had
constrained and LambdaS.Fundamental reasoned about a number nobody computed.
The evaluator multiplies by the declared factor. Converting along a declaration multiplies the magnitude by the number the declaration names: not something equal to it up to a chain of intermediate steps, but that number.
Decl.conv_eq_factor said the valuation assigns that factor; this says the
evaluator uses it.