Documentation

LambdaS.Examples

Λs, running #

Every line below runs the checker at build time via #guard. If any result were different, this file would not compile.

A note on what that does and does not establish. #guard executes the compiled infer, so these are tests, not kernel-checked proofs: kernel reduction gets stuck on the Rat instance chain that Mathlib's algebraic hierarchy is built from, which is a known cost of building on it. The correctness guarantee does not come from here: it comes from LambdaS.Typing, where check returns the derivation (so soundness holds by construction) and check_eq is proved in the kernel, saying nothing well-typed is rejected. These examples show the verified checker running.

Five base units; note that three of them measure Length. That is deliberate: it is the configuration a "one named unit per dimension" restriction forbids, and the one conversion factors live in.

Instances For
    @[instance_reducible]
    Equations
    @[instance_reducible]
    Equations
    • One or more equations did not get rendered due to their size.

    The dimensions those units measure.

    Instances For
      @[instance_reducible]
      Equations
      @[instance_reducible]

      The declared dimension of each base unit. Meter, foot, and yard collapse to the same dimension; nothing else does.

      Note that dim lands in DExp Dim 0, a dimension expression, not a single base dimension. That is what lets a derived unit be declared directly at a compound dimension.

      Equations
      • One or more equations did not get rendered due to their size.
      @[reducible, inline]

      The empty dimension context: a closed term has no unit variables in scope.

      Equations
      Instances For
        @[reducible, inline]

        Terms and types of the running example, at the closed scope.

        Equations
        Instances For
          @[reducible, inline]

          The type a closed term is assigned.

          Equations
          Instances For
            @[reducible, inline]

            The type a term is assigned in a non-empty value context.

            Equations
            Instances For
              @[reducible, inline]
              abbrev LambdaS.Examples.bu {k : } (b : Base) :

              A base unit as a unit expression, at any scope.

              Equations
              Instances For

                Scalars #

                1.3 m: a literal times a unit constant.

                Equations
                Instances For

                  The error that motivates the whole exercise #

                  Adding a length to a duration: the Mars Climate Orbiter failure, in one line.

                  Equations
                  Instances For

                    Roots, where ℚ exponents pay for themselves #

                    Kennedy's Λu and F# both type sqrt : float<'u^2> -> float<'u>, which to apply at a volume requires solving 2·vec(u) = 3 over ℤ. There is no solution, so F# rejects the square root of a volume. Over ℚ the root is total, and this lands at m^(3/2).

                    Spaces #

                    A state space whose components carry different units: position in meters, momentum in kg·m/s. This is the non-uniform case F# cannot express at all, since it parameterizes a type by a single unit.

                    Linear maps #

                    Unit polymorphism #

                    The reason Kennedy's calculus exists, now over spaces, with the dimension bound that lets a polymorphic function still convert.

                    ∀u. τ is not primitive. It is ∀δ. ∀u:δ. τ: a unit variable bounded by a dimension variable. That is what makes it genuinely unbounded, and the guards below check both halves: that δ matches any concrete dimension on instantiation, and that nothing concrete matches δ inside the binder.

                    @[reducible, inline]

                    The bound unit variable, inside one unit binder.

                    Equations
                    Instances For
                      @[reducible, inline]

                      The bound dimension variable, inside one dimension binder.

                      Equations
                      Instances For
                        @[reducible, inline]
                        abbrev LambdaS.Examples.sqrAt (d : DExp Dim 0) (μ : UExp Base 0) :
                        Equations
                        Instances For

                          Addition at any unit, but the same unit on both sides.

                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For

                            Polymorphism does not weaken the check. Λδ. Λu:δ. λ(x : Q u). x + 1 is rejected, because u is rigid inside the binder and cannot be 1.

                            Equations
                            Instances For

                              Two distinct unit variables cannot be added either: nested binders really introduce fresh variables rather than shadowing.

                              Equations
                              • One or more equations did not get rendered due to their size.
                              Instances For

                                Conversion under a binder #

                                This is what the dimension bound buys, and what an unbounded quantifier cannot express: a polymorphic function that converts its argument.

                                Λu:Length. λ(x : Q u). convert x u meter: take a length in any unit, return it in meters.

                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For

                                  The unbounded quantifier cannot convert. The same body under ∀δ. ∀u:δ. is rejected: dimOf reports the dimension variable δ, which is not the dimension of the meter, so SameDim fails. This is the rejection an unbounded quantifier ought to give, and it is why the bound is a dimension variable rather than the trivial dimension: under the trivial dimension u would be claimed dimensionless and the conversion would wrongly be accepted.

                                  Equations
                                  • One or more equations did not get rendered due to their size.
                                  Instances For

                                    Physics: where the exponents come from #

                                    @[reducible, inline]

                                    G = m³/(kg·s²)

                                    Equations
                                    • One or more equations did not get rendered due to their size.
                                    Instances For
                                      Equations
                                      • One or more equations did not get rendered due to their size.
                                      Instances For
                                        Equations
                                        • One or more equations did not get rendered due to their size.
                                        Instances For
                                          Equations
                                          • One or more equations did not get rendered due to their size.
                                          Instances For

                                            The base-measure problem, as a type error #

                                            A probability density over a space whose measure carries μ itself carries μ⁻¹; that is what makes ∫ p dμ dimensionless. So log p is not well-typed, which is the base-measure problem: a density means nothing except relative to the measure it was taken against, and representing a distribution by its density silently discards that measure.

                                            Radul and Alexeev (arXiv:2010.09647) identify exactly this failure in probabilistic programming systems and fix it by library convention. Here the checker rejects it.

                                            @[reducible, inline]

                                            A probability density over a length-parameterized space: m⁻¹.

                                            Equations
                                            Instances For
                                              @[reducible, inline]

                                              A wavefunction is a half-density, at m^(-1/2). Squaring it gives a weight-1 density, which is the normalization condition.

                                              Equations
                                              Instances For

                                                Conversion, and the ambiguity that never arises #

                                                Comp 311's unit-conversion assignment has a latent bug: with units nameable in terms of other units, convert walks a declared structure, and two routes from u to v need not agree. Λs cannot exhibit it. Conversion is a ratio of one valuation (LambdaS.Conversion), so convChain_eq makes path independence a theorem rather than a proof obligation on declarations.

                                                What the checker enforces is the side condition (that the two units measure the same thing), and that is SameDim.

                                                Declarations, and the conflict that cannot be declared away #

                                                unit yard = 3 foot declares a generator and an equation. Three such declarations give two routes from yard to meter, which is the Comp 311 bug. Here the redundant declaration is either arithmetically right or the system has no solution: there is never a choice of route to get wrong.

                                                Base units of equal declared dimension are interchangeable, via dimOf_ofBase rather than decide, since kernel reduction sticks on 's instance chain. The dimension equation is rfl for any two Length units.

                                                unit yard = 0.9144 meter: the redundant declaration, stated correctly.

                                                Equations
                                                Instances For

                                                  unit yard = 0.9 meter: the same declaration, stated wrongly. This is the one that would have made convert route-dependent.

                                                  Equations
                                                  Instances For

                                                    The consistent set forces the redundant factor. Any valuation satisfying the first two determines the third, so the second route cannot disagree with the first; it is not free to.

                                                    The conflicting set has no valuation at all.

                                                    This is the Comp 311 bug, decided rather than papered over. The assignment's convert had to walk a declared structure and could walk the wrong way; here the configuration that would force a choice is exactly the configuration with no solution, so it is rejected at declaration time.

                                                    Dimension abbreviations, elaborated #

                                                    Unit declarations constrain; dimension declarations abbreviate, and the only check they need is scoping (DimAbbrev.elabDimDefs, in LambdaS.Declare). The chain below elaborates, with Accel landing at the vector length·time⁻². The cyclic pair Velocity = Length/Time; Length = Velocity/Time is rejected at its second line for rebinding the generator Length, and a forward reference is rejected because an undefined name does not denote.

                                                    Velocity = Length/Time; Length = Velocity/Time: rebinds a generator.

                                                    Equations
                                                    Instances For

                                                      Primary-unit declarations: unit meter : Length; unit second : Time; unit hertz : Frequency with Frequency an abbreviation. Elaborates; the duplicate and the unknown dimension are rejected.

                                                      Equations
                                                      Instances For

                                                        The valuation the declarations determine, and the evaluator using it #

                                                        yard_conflict says the bad set has no valuation; this is the other half, exhibited rather than asserted: the consistent set has one, written down. Its factors then reach the evaluator through evalC_convert_declared: the number the evaluator multiplies by is the number the declaration names, with no route through an informal reading of "3". The theorems below run eval at carrier , with conv ψyd as the conversion oracle; LambdaS.Algorithms runs the same evaluator at Float with the declared magnitudes transcribed.

                                                        noncomputable def LambdaS.Examples.ψyd :

                                                        The valuation meter = 1, foot = 0.3048, yard = 0.9144 (in log space).

                                                        Equations
                                                        • One or more equations did not get rendered due to their size.
                                                        Instances For

                                                          The consistent set is satisfiable: the counterpart to yard_conflict, with the witness constructed rather than assumed.

                                                          Cycles are constraints, not definitions #

                                                          Declarations carry no order and no acyclicity condition, and need none: each is an equation, the set is a simultaneous system, and a cycle is just a dependency the criterion decides. Nothing can reference an undeclared generator either, since the base units are the parameter B. The benign cycle below is satisfiable, by the same valuation as the straight set; the vicious one forces 3 = 1 and no valuation exists.

                                                          unit foot = 1/3 yard, the converse of dYardFoot: a benign cycle.

                                                          Equations
                                                          Instances For

                                                            A benign cycle is satisfiable: yard = 3 foot and foot = 1/3 yard say the same thing, and the criterion sees a passing dependency, not a loop.

                                                            A vicious cycle is rejected: yard = 3 foot and foot = yard force 3 = 1, so no valuation exists.

                                                            theorem LambdaS.Examples.one_yard_is_three_feet :
                                                            ∃ (n : ), evalC (conv ψyd) n [] ((Tm.ucon yd).convert yd ft) = some (Val.scalar { mag := 3, unit := ft })

                                                            One yard is three feet, on the evaluator. Evaluating (1 yd) in ft at carrier , with the conversion oracle the declarations determine, multiplies by exactly the declared 3 and lands at ft: evalC_convert_declared, instantiated. The binary runs the same eval at Float (LambdaS.Algorithms).

                                                            theorem LambdaS.Examples.one_yard_in_meters :
                                                            ∃ (n : ), evalC (conv ψyd) n [] ((Tm.ucon yd).convert yd m) = some (Val.scalar { mag := (9144 / 10000), unit := m })

                                                            And directly to meters, by the redundant declaration: the same number the chain through feet produces, which is yard_forced made numeric.

                                                            theorem LambdaS.Examples.one_yard_in_meters_via_feet :
                                                            ∃ (n : ), evalC (conv ψyd) n [] (((Tm.ucon yd).convert yd ft).convert ft m) = some (Val.scalar { mag := (9144 / 10000), unit := m })

                                                            The route through feet lands on the same number. ((1 yd) in ft) in m multiplies by the declared 3 and then by the declared 0.3048, and 3 × 0.3048 = 0.9144 is exactly the magnitude the direct route produces: evalC_convert_declared applied twice, the inner conversion's value read off through Decl.conv_eq_factor.

                                                            theorem LambdaS.Examples.yard_routes_agree :
                                                            ∃ (n₁ : ) (n₂ : ), evalC (conv ψyd) n₁ [] ((Tm.ucon yd).convert yd m) = evalC (conv ψyd) n₂ [] (((Tm.ucon yd).convert yd ft).convert ft m)

                                                            The two routes agree: whatever fuel each needs, the evaluator returns the same value for one yard in meters whether it converts directly or through feet. This is path independence (conv_trans) at the evaluator, for the declared factors.

                                                            Regression: substitution under nested binders #

                                                            Attempting the type-soundness proof turned up a genuine defect in Ty.subst.

                                                            Inside ∀u. τ, de Bruijn index 0 is the bound variable and 1 is the outer one, so substituting for the outer variable must leave the binder alone. The original definition recursed as τ.subst σ.weaken, which substitutes index 0 (the bound variable) and shifts the outer one down into its place. Ty.weaken had the matching defect, inserting the fresh variable at index 0 rather than past the binder.

                                                            Nothing here reached it. Every single-binder use is unaffected, and the one test with nested unit binders (twoVars) is rejected by the checker before substitution runs. The two definitions were also wrong in a way that canceled, so subst_weaken (the only theorem about them) held regardless.

                                                            It took writing the soundness proof to surface it: the composition lemma for substitution refused to hold, and the reason was that liftU had no counterpart in the quantifier case. The fix was to define subst and weaken as instances of a simultaneous substitution that lifts properly. These guards pin the corrected behavior.

                                                            That is the argument for mechanizing a calculus rather than describing one. The defect is invisible to inspection, invisible to the examples, and invisible to the one property anybody would have thought to state about it.

                                                            @[reducible, inline]

                                                            ∀u. Q v₁: the body mentions the outer variable, not the bound one.

                                                            Equations
                                                            Instances For
                                                              @[reducible, inline]

                                                              ∀u. Q v₀: the body mentions the bound variable.

                                                              Equations
                                                              Instances For

                                                                Algebraic units, in types and in measurements #

                                                                Two expressibility checks worth keeping visible, because a reader of the grammar can miss both. Unit expressions are the free abelian group: a type may be indexed by an algebraic combination of bound unit variables, and a measurement is a literal times a unit constant at any compound unit.

                                                                Λu:Length. Λv:Time. λx : Q (u·v). x: a polymorphic identity at a compound unit built from two distinct bound variables. Inside the two binders, de Bruijn variable 1 is u and 0 is v.

                                                                Equations
                                                                • One or more equations did not get rendered due to their size.
                                                                Instances For

                                                                  5 m/s: a literal times the unit constant at a compound unit. The type is Q (1 · m/s), and the exponent-vector representation makes that the same index as Q (m/s); the #guard compares the vectors.

                                                                  Equations
                                                                  Instances For

                                                                    The drift diagnostic, exercised #

                                                                    Three programs, three answers. Converting meters to feet and back cancels: unitDrift answers 1, and the result is unit-system independent. Converting one way does not: the drift is m/ft, and the diagnostic names it. And a sum whose branches share a variable carries the branches' shared drift.

                                                                    Addition demands agreeing branch ratios, and x in ft + x in ft has them: both branches carry the drift m/ft.

                                                                    Equations
                                                                    Instances For
                                                                      Equations
                                                                      • One or more equations did not get rendered due to their size.
                                                                      Instances For

                                                                        x in ft + y in ft for distinct measurements x y : Q m is accepted too, with the same drift m/ft. This is the frees-at-one assignment at work: x and y are inputs, inputs rescale ideally, so each contributes the ratio 1 rather than an opaque atom, and the branch ratios are the same exponent vector. An earlier design gave every free variable an atom and declined this program, answering a stronger question than the certified theorems ask: the theorems instantiate the inputs' ratios at 1 (oneTwEnv), and at that instantiation the two branches agree.

                                                                        Equations
                                                                        Instances For
                                                                          Equations
                                                                          • One or more equations did not get rendered due to their size.
                                                                          Instances For

                                                                            What add still declines at first order: a genuine drift disagreement. x in ft + y with x : Q m and y : Q ft converts one branch and not the other, so the branch drifts are m/ft and 1, distinct exponent vectors, and the sum has no uniform drift; indeed the program is not scale-invariant. By Tw.normEq_iff_eval_eq such disagreements are the only declines at add between atom-free ratios, which is what a first-order program produces once its internal applications are normalized away (hoSum below).

                                                                            Equations
                                                                            Instances For
                                                                              Equations
                                                                              • One or more equations did not get rendered due to their size.
                                                                              Instances For

                                                                                A residue of internal abstraction. An abstraction applied inside a first-order program leaves a redex behind: (λf. f x) (λy. y in ft) has the ratio m/ft under every scaling, and unitDrift names it, because deciding triviality normalizes the ratio fully (Tw.nfOne). But twistOf reduces one β-step at construction, and the redex the outer application creates, (λy. ...) 1, survives in the ratio as an app node, which Tw.flat treats as an opaque atom. The ratio as built is not atom-free; its normal form is, and the branch comparison runs on normal forms (Tw.normEq).

                                                                                Equations
                                                                                • One or more equations did not get rendered due to their size.
                                                                                Instances For
                                                                                  Equations
                                                                                  • One or more equations did not get rendered due to their size.
                                                                                  Instances For

                                                                                    Summing hoApp with x in ft, two branches worth m/ft under every scaling, is accepted at that drift: the comparison normalizes both branches first, so the residue is reduced away and two unit expressions are compared. Before the comparison ran on normal forms this sum was declined, an atom against a unit.

                                                                                    Equations
                                                                                    Instances For

                                                                                      Division of a unit by itself is the trivial unit: exponent vectors subtract to zero.

                                                                                      log ((x in ft)/(x in ft)): the argument's conversions cancel, so it is drift-free, and a drift-free value at Q 1 is declaration-independent under every rescaling. Its logarithm is declaration-independent too, and the analysis now accepts it: log runs the add-style check against the literal ratio 1 rather than declining unconditionally.

                                                                                      Equations
                                                                                      Instances For
                                                                                        Equations
                                                                                        • One or more equations did not get rendered due to their size.
                                                                                        Instances For

                                                                                          What log still declines: an argument at Q 1 whose ratio genuinely drifts. (x in ft)/y for x : Q m, y : Q ft is dimensionless but carries the drift m/ft, and no ratio describes how its logarithm moves.

                                                                                          Equations
                                                                                          Instances For
                                                                                            Equations
                                                                                            • One or more equations did not get rendered due to their size.
                                                                                            Instances For

                                                                                              β at the ratio level. The left branch wraps x in ft in an identity application, so its ratio arrives as a β-redex; twistOf reduces it on the spot (Tw.appE), the flat form sees the bare exponent vector m/ft rather than an opaque atom, and the sum is accepted with the shared drift. Before the reduction this program was declined for a spelling difference.

                                                                                              Equations
                                                                                              • One or more equations did not get rendered due to their size.
                                                                                              Instances For
                                                                                                Equations
                                                                                                • One or more equations did not get rendered due to their size.
                                                                                                Instances For

                                                                                                  The λ-wrapped kernel: λx:Q m. λy:Q m. (x in ft) + (y in ft). Closed, so unitDrift has no context to pin; unitDriftLam strips the two leading binders, treats x and y as inputs, and reports the kernel's drift, the same verdict addTwoVars receives as an open term: by unitDriftLam_eq_unitDrift the two spellings' verdicts are one computation, and unitDriftLam_spec makes the verdict exact for the applied kernel.

                                                                                                  Equations
                                                                                                  • One or more equations did not get rendered due to their size.
                                                                                                  Instances For
                                                                                                    Equations
                                                                                                    • One or more equations did not get rendered due to their size.
                                                                                                    Instances For

                                                                                                      Branch ratios compare up to the unit algebra, not up to spelling. The two branches below convert the same product of meters to feet, placed differently: the first converts the product once at m·m, the second converts each factor at m. Their ratio terms differ syntactically (the syntactic check Tw.beq rejects exactly this pair), but Tw.normEq flattens both to the unit vector m²/ft² with atom exponent 1 on each of x and y, so the analysis answers, and the answer is the drift the branches share.

                                                                                                      Equations
                                                                                                      • One or more equations did not get rendered due to their size.
                                                                                                      Instances For
                                                                                                        Equations
                                                                                                        • One or more equations did not get rendered due to their size.
                                                                                                        Instances For

                                                                                                          The generic caster #

                                                                                                          Under ∀δ. ∀u:δ, conversion out of u is not blocked wholesale: it can target any expression of dimension δ, and in particular another variable bounded by the same δ. The generic caster

                                                                                                          Λδ. Λu:δ. Λv:δ. λx:Q u. convert x u v

                                                                                                          typechecks, at the polymorphic cast type, and instantiating both unit quantifiers at same-dimension ground units yields an ordinary cast. What remains rejected is conversion from u to a concrete unit, since no concrete unit has dimension δ. The caster is a term the two abstraction theorems separate: coherent rescalings give u and v one shared factor and leave it invariant; independent factors move it.

                                                                                                          Λδ. Λu:δ. Λv:δ. λx:Q u. convert x u v.

                                                                                                          Equations
                                                                                                          • One or more equations did not get rendered due to their size.
                                                                                                          Instances For

                                                                                                            Drift through the unit quantifiers #

                                                                                                            The caster's ratio is a family: at bind shape it is indexed by the instantiating unit, and its body's drift is the open exponent vector u/v. The normalizer reads the family at chosen units, so reading it at two fresh unit variables exhibits the open vector itself; and an instantiation is a recorded uapp that twistOf performs on the spot (Tw.uappE).

                                                                                                            Checker plus diagnostic, for the guards below: the drift of a quantity-typed term over Γ, or none where either the checker or the analysis declines.

                                                                                                            Equations
                                                                                                            • One or more equations did not get rendered due to their size.
                                                                                                            Instances For

                                                                                                              The polymorphic round trip: Λδ. Λu:δ. Λv:δ. λx:Q u. (x in v) in u.

                                                                                                              Equations
                                                                                                              • One or more equations did not get rendered due to their size.
                                                                                                              Instances For

                                                                                                                The coherent fundamental theorem, at a rescaling that moves a dimension #

                                                                                                                fundamental quantifies over dimension rescalings Φ. This section instantiates it at a Φ that is not the identity: it multiplies every Length by 2 and fixes Mass and Time. The term is a conversion from meters to feet, so the instantiation exercises the convert case of the theorem, the case that requires coherence.

                                                                                                                A dimension rescaling that doubles Length and fixes Mass and Time. In log coordinates the Length entry is log 2, which is nonzero, so this rescaling moves a dimension.

                                                                                                                Equations
                                                                                                                • One or more equations did not get rendered due to their size.
                                                                                                                Instances For

                                                                                                                  ΦDouble is not the trivial rescaling: it moves the Length dimension.

                                                                                                                  The unit rescaling ΦDouble pulls back to: each base unit scales by the factor of its dimension, so meter, foot, and yard all scale by 2 while kilogram and second are fixed.

                                                                                                                  Equations
                                                                                                                  • One or more equations did not get rendered due to their size.
                                                                                                                  Instances For

                                                                                                                    ψDouble factors through dimension via ΦDouble, by construction: its base field is definitionally the sum Scaling.Factors requires.

                                                                                                                    λ(x : Q m). convert x m ft, closed and parametric: the conversion is the only non-variable subterm, and Tm.Parametric admits convert.

                                                                                                                    Equations
                                                                                                                    Instances For

                                                                                                                      fundamental, instantiated at a rescaling that moves a dimension. The conversion program is related to itself under ΦDouble, which rescales Length by 2. Unfolding RelCo at the arrow type: for inputs related by ψDouble.scale m, the outputs are related by ψDouble.scale ft. The convert case of fundamental carries the content here, since the conversion factor conv V m ft must be invariant under the rescaling, and coherence of ψDouble (every Length unit scales by the same factor 2) is exactly what makes it so.

                                                                                                                      Vector and matrix literals #

                                                                                                                      Until the introduction forms existed, vectors and matrices entered only as free variables: the calculus could consume its data but not construct it. These guards exercise the forms that close the gap: a State vector built one component at a time, matrices built one row at a time, the rejections the rules impose, and the zero-row width story that mnil's annotation exists to tell.

                                                                                                                      @[reducible, inline]

                                                                                                                      The momentum unit of State, kg·m/s.

                                                                                                                      Equations
                                                                                                                      Instances For

                                                                                                                        The State vector as a literal: position 1.3 m consed onto momentum 21 kg·m/s.

                                                                                                                        Equations
                                                                                                                        Instances For

                                                                                                                          A map from State to W = [sec] as a literal: one row whose entry i carries sec / δ_State(i), which is the rank-one condition the mcons rule checks.

                                                                                                                          Equations
                                                                                                                          • One or more equations did not get rendered due to their size.
                                                                                                                          Instances For

                                                                                                                            A two-row map the other way, one row per component of State.

                                                                                                                            Equations
                                                                                                                            • One or more equations did not get rendered due to their size.
                                                                                                                            Instances For

                                                                                                                              Row extraction #

                                                                                                                              mrow is the elimination form for Lin, dual to mcons as idx is to vcons. Introduction and elimination meet on the nose: row 0 of mcons w r M has exactly the type r was checked at. Without it a matrix could be built and applied but never read, so no closed term could name a matrix element, a column, or a transpose.

                                                                                                                              A transpose, at fixed arity. The transpose of a map V → W is the map W* → V* on the dual spaces, whose units are the reciprocals. Here fromTime : Lin W State, so its transpose lives at Lin [1/m, 1/pmom] [1/sec], and the one row of the transpose is the one column of fromTime, assembled entry by entry from row extractions. The mcons rule checks that the entries m/sec and pmom/sec are exactly (1/sec) / (1/m) and (1/sec) / (1/pmom), which is where the dual spaces come from.

                                                                                                                              Equations
                                                                                                                              • One or more equations did not get rendered due to their size.
                                                                                                                              Instances For

                                                                                                                                Drift for vectors and matrices #

                                                                                                                                The drift of a vector is a vector of drifts, and the drift of a matrix is a matrix of drifts. A literal whose component converts one way is reported with a drift vector naming the component's ratio, not declined; idx projects the component's drift back out; and a matrix application carries the add-style agreement condition per output row, across the summed index, so a row whose summands carry genuinely different drifts is declined.

                                                                                                                                x : Q m, t : Q s ⊢ ⟨x in ft, t⟩: a vector literal whose first component converts one way and whose second does not.

                                                                                                                                Equations
                                                                                                                                Instances For
                                                                                                                                  Equations
                                                                                                                                  • One or more equations did not get rendered due to their size.
                                                                                                                                  Instances For

                                                                                                                                    Indexing the drifting component recovers exactly the scalar diagnosis the one-way conversion would get on its own.

                                                                                                                                    Equations
                                                                                                                                    Instances For
                                                                                                                                      @[reducible, inline]

                                                                                                                                      A context for the matrix-application examples: a length, and a dimensionless entry at exactly the unit ft/ft the rank-one row space demands.

                                                                                                                                      Equations
                                                                                                                                      Instances For

                                                                                                                                        A 1×2 matrix applied to a drifting vector, agreement holding: both argument components carry the same variable through the same conversion, and both row entries are the same variable, so the two products across the sum agree and the output row's drift is their common value.

                                                                                                                                        Equations
                                                                                                                                        • One or more equations did not get rendered due to their size.
                                                                                                                                        Instances For
                                                                                                                                          Equations
                                                                                                                                          • One or more equations did not get rendered due to their size.
                                                                                                                                          Instances For
                                                                                                                                            @[reducible, inline]

                                                                                                                                            The same shape with distinct measurements in the argument components: the mapp analogue of addTwoVars. Under the frees-at-one assignment both components contribute the ratio 1 through their own variable, so the products across the sum agree at the exponent vector m/ft and the application is accepted; an earlier design gave x and y distinct atoms and declined it.

                                                                                                                                            Equations
                                                                                                                                            Instances For
                                                                                                                                              Equations
                                                                                                                                              • One or more equations did not get rendered due to their size.
                                                                                                                                              Instances For
                                                                                                                                                Equations
                                                                                                                                                • One or more equations did not get rendered due to their size.
                                                                                                                                                Instances For
                                                                                                                                                  @[reducible, inline]

                                                                                                                                                  What mapp still declines at first order: a genuine drift disagreement across the summed index, the mapp analogue of addMixed. One argument component converts (x in ft, drift m/ft) and the other is already in feet (drift 1), so the row's two products carry distinct exponent vectors and no uniform output drift exists.

                                                                                                                                                  Equations
                                                                                                                                                  Instances For
                                                                                                                                                    Equations
                                                                                                                                                    • One or more equations did not get rendered due to their size.
                                                                                                                                                    Instances For
                                                                                                                                                      Equations
                                                                                                                                                      • One or more equations did not get rendered due to their size.
                                                                                                                                                      Instances For

                                                                                                                                                        Case study: a mixed-unit ballistics kernel #

                                                                                                                                                        The drift diagnostic has so far run on one-line programs. This section runs it on a small straight-line kernel with the shape real conversion bugs have: field data arrives in imperial units, the physics is metric, and the report goes back out in imperial units.

                                                                                                                                                        Inputs: a distance in feet (variable 0) and a time in seconds (variable 1). The shared pipeline converts the distance to meters, forms the velocity in m/s, and computes the kinetic energy per unit mass v²/2 in m²/s². Four reporting variants, four verdicts, all decided at build time:

                                                                                                                                                        Every routine typechecks by a #guard on the inferred type, and every drift claim is a #guard on unitDrift, so a wrong claim fails the build.

                                                                                                                                                        @[reducible, inline]

                                                                                                                                                        The kernel's input context: a distance in feet and a time in seconds.

                                                                                                                                                        Equations
                                                                                                                                                        Instances For

                                                                                                                                                          Meter and foot share a dimension, so velocity squared does too: the side condition the energy conversions below discharge, proved by the homomorphism laws for dimOf rather than by decide.

                                                                                                                                                          The shared pipeline #

                                                                                                                                                          Real code shares subterms, so the variants do too: one converted distance, one metric velocity, one imperial velocity, each with its derivation.

                                                                                                                                                          Metric velocity: the converted distance over the raw time input.

                                                                                                                                                          Equations
                                                                                                                                                          Instances For

                                                                                                                                                            Imperial velocity: the raw inputs, no conversion.

                                                                                                                                                            Equations
                                                                                                                                                            Instances For
                                                                                                                                                              @[reducible, inline]

                                                                                                                                                              The one-way drift both drifted variants share: one foot-to-meter conversion, squared through .

                                                                                                                                                              Equations
                                                                                                                                                              Instances For

                                                                                                                                                                Variant 1: convert in, compute, convert out #

                                                                                                                                                                (1/2) · ((velM · velM) in ft²/s²): metric energy, reported imperial. The ftm conversion inside velM enters the ratio twice through the square; the output conversion contributes m²/ft² and cancels it exactly.

                                                                                                                                                                Equations
                                                                                                                                                                • One or more equations did not get rendered due to their size.
                                                                                                                                                                Instances For
                                                                                                                                                                  Equations
                                                                                                                                                                  • One or more equations did not get rendered due to their size.
                                                                                                                                                                  Instances For

                                                                                                                                                                    Variant 2: convert in, report metric #

                                                                                                                                                                    Variant 3: the same quantity, two ways, in one sum #

                                                                                                                                                                    (1/2) · (velM·velM + ((velF·velF) in m²/s²)): the left branch converts the input then squares, the right squares the raw imperial velocity then converts once at ft²/s². Averaging the two would divide by a further literal; adding them keeps the branch comparison the interesting step.

                                                                                                                                                                    Equations
                                                                                                                                                                    • One or more equations did not get rendered due to their size.
                                                                                                                                                                    Instances For
                                                                                                                                                                      Equations
                                                                                                                                                                      • One or more equations did not get rendered due to their size.
                                                                                                                                                                      Instances For

                                                                                                                                                                        Variant 4: the rooted variant, followed through the root #

                                                                                                                                                                        √(2 · kernelOneWay): the speed recovered from the energy. Well-typed at m/s (rational powers are total over ℚ exponents), and inside the analysis: Tw.qpow lifts the energy's drift to the power.

                                                                                                                                                                        Equations
                                                                                                                                                                        Instances For

                                                                                                                                                                          A Jacobi rotation, and what the units force #

                                                                                                                                                                          The reason ifle was added. Section 5 classifies dimensioned maps, and svd_entry_const says a singular value decomposition needs a uniform space because sorting singular values requires a shared unit. That is a claim about types, and until now nothing in the development ran an actual kernel against it. This does.

                                                                                                                                                                          The setting is the symmetric eigenvalue problem, the fourth Hart class: A : V ⊸ dual V on a uniform V, so every entry carries 1 / (V j * V i) and, uniformly, m⁻². A Jacobi sweep annihilates the off-diagonal entry by a rotation whose angle is computed from the entries themselves.

                                                                                                                                                                          A uniform two-component space.

                                                                                                                                                                          Equations
                                                                                                                                                                          Instances For

                                                                                                                                                                            The context the kernel runs in: one symmetric matrix.

                                                                                                                                                                            Equations
                                                                                                                                                                            Instances For

                                                                                                                                                                              The tangent parameter of the rotation angle.

                                                                                                                                                                              Every entry carries m⁻², so the difference is legal and the ratio is dimensionless. The rotation is forced to be a pure number, and it is the uniformity of the space that forces it: on a non-uniform space the numerator below does not typecheck.

                                                                                                                                                                              Equations
                                                                                                                                                                              Instances For

                                                                                                                                                                                t = sign(τ) / (|τ| + √(1 + τ²)), the standard stable form, written with the two branches the sign requires. This is the term that could not be written before ifle.

                                                                                                                                                                                Equations
                                                                                                                                                                                • One or more equations did not get rendered due to their size.
                                                                                                                                                                                Instances For

                                                                                                                                                                                  The rotation itself, an endomorphism of the uniform space. Its entries carry V j / V i = 1, so a rotation is a matrix of plain numbers, which is entry_id_diag and entry_perm_prod seen from the term side.

                                                                                                                                                                                  Equations
                                                                                                                                                                                  • One or more equations did not get rendered due to their size.
                                                                                                                                                                                  Instances For

                                                                                                                                                                                    Its transpose, assembled column by column with mrow. The rotation is orthogonal, so this is also its inverse, and it lands at the dual spaces.

                                                                                                                                                                                    Equations
                                                                                                                                                                                    • One or more equations did not get rendered due to their size.
                                                                                                                                                                                    Instances For

                                                                                                                                                                                      One Jacobi sweep, the congruence Rᵀ A R. It needs both extensions at once: mrow to build the transpose and ifle to choose the angle. The type is preserved exactly, so sweeps compose.

                                                                                                                                                                                      Equations
                                                                                                                                                                                      Instances For

                                                                                                                                                                                        What the units refuse #

                                                                                                                                                                                        The rotation algebra above is not merely typeable on a uniform space; it is typeable only there. On a non-uniform space the two diagonal entries carry different units, so the numerator of tau is an addition at unequal units and the term is rejected before any question of correctness arises. Hart's uniformity condition is visible here as a typing constraint of this particular Jacobi kernel. svd_entry_const supplies the unit identity; the checker guard under ΓN below demonstrates the rejection. No typed general SVD algorithm is mechanized.

                                                                                                                                                                                        What the units say about the stopping test #

                                                                                                                                                                                        A convergence test compares the off-diagonal to a tolerance, and the tolerance must carry the entries' unit. Both forms below typecheck; parametricity distinguishes how their tolerances are supplied.

                                                                                                                                                                                        A relative tolerance scales a quantity already in hand by a dimensionless factor. It names no unit, so it stays inside the parametric fragment and Theorem 6.1 applies to the kernel.

                                                                                                                                                                                        The fixed absolute tolerance below is written using a named unit constant ucon, which Tm.Parametric excludes. The kernel still typechecks and runs, but the abstraction theorem no longer applies. An absolute tolerance passed as an input could remain parametric and would rescale with the inputs; absolute tolerances are not excluded in general.

                                                                                                                                                                                        Equations
                                                                                                                                                                                        • One or more equations did not get rendered due to their size.
                                                                                                                                                                                        Instances For

                                                                                                                                                                                          The diagnostic at the sweep #

                                                                                                                                                                                          unitDriftGen takes an arbitrary context and a .lin result, so it applies to the kernel exactly as written, matrix argument and matrix result. This is the guard that says so.

                                                                                                                                                                                          What the experiment found #

                                                                                                                                                                                          Three things the kernel confirms, one of which took a generalization to reach.

                                                                                                                                                                                          Confirmed: uniformity is not a hypothesis but a precondition for writing the code at all, and the parametric fragment separates a relative tolerance from an absolute one. Both were predicted before the kernel was written.

                                                                                                                                                                                          Not confirmed at first: the drift analysis did not reach this program. unitDrift is typed HasTy Δ (scalarCtx us) e (.Q u) → Option (UExp B k), so it wanted scalar arguments and a scalar result, and sweep has a matrix argument and a matrix result. No rearrangement of the kernel helped, because the restriction was on the shape of the judgment rather than on the term.

                                                                                                                                                                                          That was a coverage gap between two parts of the development rather than an unsoundness, and the diagnosis said where to look: twistOf, the analysis underneath, is indexed by Ctx.shapes and already handled vector and matrix shapes, which is why Tw.agree needed cases at those shapes at all. Only the wrapper was scalar-only. The gap mattered because it fell exactly between two claims the development makes: LambdaS.Map classifies dimensioned linear operators, the diagnostic decides invariance, and the kernels motivating the first sat outside the second.

                                                                                                                                                                                          The wrapper did generalize, on both axes. unitDriftLin reports one ratio per entry of a matrix result, and unitDriftGen drops the scalar-context restriction as well, so it applies to sweep exactly as written. The guard above runs it and finds every entry trivial, which is the answer the congruence of rotations should have: the kernel converts nowhere, so nothing drifts. Neither axis needed a new theorem, and Twist.scaling turned out to have been fully general already.

                                                                                                                                                                                          The kernel restated over scalar arguments #

                                                                                                                                                                                          Written when the gap above was open, as the workaround: pass the entries rather than the matrix, so that the context is a scalarCtx and only the result shape stands between the kernel and the diagnostic. unitDriftGen has since removed the need for it. It stays because it exercises a genuinely different path, a scalar context with a matrix result, and because the same rotation algebra written two ways is a check that the shapes are doing no work the units are not.

                                                                                                                                                                                          The entry unit of a symmetric matrix on the uniform space.

                                                                                                                                                                                          Equations
                                                                                                                                                                                          Instances For

                                                                                                                                                                                            The matrix assembled from them, symmetric by construction.

                                                                                                                                                                                            Equations
                                                                                                                                                                                            • One or more equations did not get rendered due to their size.
                                                                                                                                                                                            Instances For
                                                                                                                                                                                              Equations
                                                                                                                                                                                              • One or more equations did not get rendered due to their size.
                                                                                                                                                                                              Instances For
                                                                                                                                                                                                Equations
                                                                                                                                                                                                • One or more equations did not get rendered due to their size.
                                                                                                                                                                                                Instances For
                                                                                                                                                                                                  Equations
                                                                                                                                                                                                  • One or more equations did not get rendered due to their size.
                                                                                                                                                                                                  Instances For

                                                                                                                                                                                                    The sweep over a scalar context. Everything unitDrift asks of the context is satisfied here; only the result shape remains.

                                                                                                                                                                                                    Equations
                                                                                                                                                                                                    Instances For