Documentation

LambdaS.PiTheorem

The Pi theorem #

Kennedy's Theorem 4: a first-order unit-polymorphic type is isomorphic to one with n − r dimensionless arguments, where r is the rank of the exponent matrix. He listed its proof as work in progress on his WMM 2008 slides and no completion appears in the eighteen years since.

The route taken #

Kennedy proves it by reducing the exponent matrix through a sequence of syntactic type isomorphisms: column operations C1C3, row operations R1R3, then r instances of an elimination isomorphism D. That route needs the isomorphism witnesses to be terms, hence a term-level rational power xᵠ, hence positivity, and it is what has been blocking progress here.

This file takes the semantic route instead. The fundamental theorem (fundamental_free) already gives every parametric convert-free term an unrestricted scaling law. Working directly from that law (in logarithmic coordinates, where the scaling action is a translation), the theorem becomes a statement in linear algebra and no term witnesses are needed.

Concretely, in log coordinates ξ = log x the action of a scaling ψ is ξ ↦ ξ + Aᵀψ, and the scaling law reads F(ξ + Aᵀψ) = F(ξ) + ⟨b, ψ⟩. Given a solution X of A X = b (Kennedy's solvability hypothesis), subtracting the linear functional ⟨X, ·⟩ produces a function invariant under the entire action. That invariant function is exactly the "function of the dimensionless groups", and

f(x) = (∏ xᵢ^{Xᵢ}) · g(Π₁, …, Π_{n−r})

is Buckingham's conclusion.

Why ℚ pays off a fourth time #

Kennedy reduces the matrix to Smith Normal Form, which is what ℤ forces. Here the orbit subspace is range Aᵀ over a field, its dimension is rank A, and invariant_descends obtains n − r coordinates from a rational basis of the kernel, using linear extension with values in ℝ to separate the orbits. The coordinate count is rank-nullity (Mathlib's). The rational-exponent decision keeps paying in places it was not made for.

From the paper's long form: Dimensional Analysis #

The paper's tag long-form carries this section in full; it is reproduced here, converted to Markdown, so the documentation develops what the paper now summarizes. Section references name the module that carries the section; theorem references name the declaration.

The abstraction theorems of “The Price of Conversion” (Fundamental.lean) have two further consequences, which the programmer never asks for but a numerical library wants: a non-definability theorem and the Pi theorem of dimensional analysis.

First, non-definability. For any nontrivial u (some base exponent nonzero), no term of Λs built from variables, rational literals, and the field operations computes square root at type Q u² → Q u (sqrt_not_definable_tm), so the powers of “The Calculus” (Typing.lean) must be primitive. (The full calculus does inhabit the type, by naming a unit, λ x. 1_u; the Newton paragraph below prices that seed.) Note that the scaling law alone cannot deliver this. Applied to a hypothetical f : Q u² → Q u, the theorem “Abstraction, convert-free” (fundamental_free, Fundamental.lean) gives f(k² x) = k · f(x), an equation square root satisfies; parametricity says that any such f is a constant multiple of square root, not that none exists. What excludes the function is a span argument on the syntax. From an argument at u² and dimensionless literals, closure under the field operations reaches exactly the integer powers of u²: multiplication and division add and subtract exponent vectors, so the reachable units form the ℤ-span of the argument's unit, and u = (u²)^(1/2) requires the ℚ-span. For such u, the type Q u² → Q u is therefore uninhabited by arithmetic terms, a conclusion stronger than uncomputability (sqrt_not_definable, over a self-contained arithmetic grammar; arith_of_hasTy lifts the statement to the calculus's own typing derivations). The primitive is also a permitted one: powers are parametric, so the theorem “Abstraction, convert-free” (fundamental_free, Fundamental.lean) covers them (sqrt_scales, instantiated at λ x. √x). Atkey et al. [2013] prove stronger results of this kind (cube root remains undefinable even given square root); we include ours because the exponent-vector representation makes it a two-line span argument, and because it justifies a constructor of Λs.

Recursion would not rescue definability. Newton's iteration x_(n+1) = (x_n + a/x_n)/2 is unit-correct at every step; what it lacks is the seed x₀ : Q u, and manufacturing one from a : Q u² is the non-definability theorem itself (no_newton_seed; at the term grammar, no_newton_seed_tm). The full calculus offers the seed 1_u, at a price the theory names: every finite run of the constant-seeded program depends on the unit system, boundedly under a relative tolerance (an absolute one is not even writable parametrically), while a seed passed as an argument rescales with everything else and restores parametricity wholesale. The artifact's documentation develops the full analysis, seeds, tolerances, and the piecewise-rational argument that no recursive program computes square root exactly (module LambdaS.NonDefinability). Its content is that the square root cannot be computed: what the power primitive buys is exactness, a denotation that commutes with rescaling pointwise rather than up to tolerance.

Second, the Pi theorem, which we motivate with its classical example before stating it. What determines the period of a pendulum? List everything the period could plausibly depend on: the mass m of the bob, the length ℓ of the arm, the gravitational acceleration g, and the release amplitude θ, so that T = f(m, ℓ, g, θ) for some unknown f. Dimensional analysis extracts the form of f from the units alone, before any mechanics. Record the exponents of each argument's unit over the base units of mass, length, and time as the columns of a matrix (g is an acceleration, at L/T²; the amplitude is an angle, dimensionless, hence a zero column):

mgθ
M1000
L0110
T00-20

The matrix has rank 3, so the dimensionless power products m^c₁ℓ^c₂g^c₃θ^c₄ of the arguments, whose exponent vectors form the kernel of A, make up a space of dimension 4 - 3 = 1; solving Ac = 0 forces c₁ = c₂ = c₃ = 0 and leaves c₄ free, so the amplitude is the only dimensionless combination of the arguments. The output T has exponent vector b = (0, 0, 1), and AX = b has the solution X = (0, 1/2, -1/2, 0): the power product ℓ^(1/2) g^(-1/2) = √(ℓ/g). The Pi theorem says that these two computations determine f up to one unknown function of one variable:

T  =  √(ℓ/g) · G(θ).

Equivalently, the ratio T√(g/ℓ) is the problem's single dimensionless invariant. Note what the mass row has already decided: mass occurs in exactly one argument and not in the output, so every solution of AX = b has zero in the mass coordinate, and so does every kernel vector; f cannot depend on the bob's mass. The period's independence from mass, a fact usually credited to the mechanics, follows from the type of f. Mechanics contributes only the function G, with G(θ) → 2π in the small-amplitude limit.

The general statement is the scaling law of the theorem “Abstraction, convert-free” (fundamental_free, Fundamental.lean) handed to linear algebra, and the artifact carries the handoff as theorems. For a first-order program (n scalar arguments, one scalar result), instantiating the scaling law at every rescaling of the unit symbols in scope, base units and unit variables alike, says that multiplying each argument by its unit's power product of factors multiplies the result by its own (den_mulScaleLaw). The program's dependence on the unit system is then governed by the exponent matrix A (one row per unit symbol, one column per argument) together with the output's exponent vector b; we call the pair (A, b) the program's signature. Taking logarithms turns the multiplicative action into translation (scaleLaw_of_mulScaleLaw); the logarithm of the output needs positivity, but the positivity belongs to that presentation, not to the theorem, and the factorization below is stated without it. The invariance theory becomes linear algebra: the invariants of the translation action are exactly the dimensionless power products of the arguments, a space of dimension n - rank A, and every function obeying the scaling law factors through them.

Theorem (Pi; pi_theorem). Let (A, b) be a signature whose output's exponents are solvable from the arguments (AX = b for some X). A function obeying its scaling law factors as an explicit power product times a function invariant under every rescaling; the invariant monomials form a space of dimension n - rank A, and the factorization is an equivalence.

The artifact states the clauses separately: invariant_iff_dimensionless characterizes the invariant monomials, pi_count counts them, mulScaleLaw_factorization gives the factorization in multiplicative coordinates with an invariant factor of arbitrary sign (the zero function is the factorization at H = 0), and piEquiv with piEquivSigned package the equivalences, in additive and in signed multiplicative form. Solvability of AX = b is half of a dichotomy (mulScaleLaw_dichotomy): when no X solves it, some combination of rescalings fixes every argument while moving the output, so the only function obeying the scaling law is identically zero (mulScaleLaw_eq_zero_of_unsolvable; at the term level, its one-variable case is scaleLaw_forces_zero). An unsolvable signature is dimensional inconsistency.

For the pendulum, the power product is √(ℓ/g), the invariant space is spanned by the amplitude, and the factorization is the displayed equation above. The artifact runs this instance: pendulum is the 3 × 4 matrix above, with output exponents (0, 0, 1) and the exhibited solution (0, 1/2, -1/2, 0) (pendulum_period_solution), and the mass conclusion follows (pendulum_mass_absent): a base unit occurring in exactly one argument and absent from the output forces that argument's exponent to zero in every solution (solution_eq_zero_of_appears_once) and in every invariant (eq_zero_of_appears_once). Note also what the theorem offers a programmer: it bounds what any unit-polymorphic first-order function can depend on, an interface-narrowing result that costs nothing. Whatever a function of the pendulum's signature computes, it can depend on its four arguments only through √(ℓ/g) and θ.

The counting is Buckingham's [1914], in the modern rank formulation, obtained here as a corollary of parametricity: the reading Kennedy proposed [Kennedy 1997] and, for one variable, Atkey et al. [2013] mechanized. Ours is the general n-variable statement with the rank condition, mechanized and connected to the calculus; we claim the mechanization, not the mathematics. The standing hypotheses are the theorem “Abstraction, convert-free” (fundamental_free, Fundamental.lean)'s: the program must be parametric (no unit constants) and convert-free; solvability's failure is the dichotomy's other half, settled above. Neither syntactic hypothesis is redundant. A program that converts obeys the scaling law only for coherent rescalings, and the Pi argument quantifies over all rescalings; the drift analysis of “Accumulated Ratios, and a Decidable Diagnostic” (Twist.lean) suggests the sharper hypothesis, drift-free rather than convert-free, and we leave that weakening to future work (the paper's conclusion). Parametricity cannot be dropped even for convert-free terms: λ x. (x/1_u)·1_v is convert-free, denotes the identity at Q u → Q v, and violates the unrestricted law.

def LambdaS.Pi.act {m n : } (A : ExpMatrix m n) (ψ : Fin m) (i : Fin n) :

The displacement a scaling induces on the log-arguments. In log coordinates the scaling action is translation by Aᵀψ.

Equations
Instances For
    def LambdaS.Pi.ScaleLaw {m n : } (A : ExpMatrix m n) (b : Fin m) (F : (Fin n)) :

    The scaling law of a first-order signature, in log coordinates.

    F is log ∘ f ∘ exp; rescaling the units translates the arguments and shifts the value by the output's own scale. This is exactly what fundamental_free delivers for a parametric convert-free term, transported to logs.

    Equations
    Instances For
      theorem LambdaS.Pi.sum_act_eq {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) (ψ : Fin m) :
      i : Fin n, X i * act A ψ i = v : Fin m, (b v) * ψ v

      The exchange identity at the heart of the reduction: a solution of A X = b turns the argument displacement into exactly the output shift.

      This is where Kennedy's solvability hypothesis does its work.

      theorem LambdaS.Pi.pi_theorem {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) {F : (Fin n)} (hF : ScaleLaw A b F) :
      ∃ (G : (Fin n)), (∀ (ψ : Fin m) (ξ : Fin n), (G fun (i : Fin n) => ξ i + act A ψ i) = G ξ) ∀ (ξ : Fin n), F ξ = i : Fin n, X i * ξ i + G ξ

      The Pi theorem.

      Any function satisfying the scaling law factors as an explicit power-product times a function that is invariant under every rescaling, hence a function of the dimensionless groups alone, of which there are n − r.

      In the original coordinates this reads f(x) = (∏ xᵢ^{Xᵢ}) · g(Π₁, …, Π_{n−r}), which is Buckingham's conclusion and Kennedy's isomorphism.

      theorem LambdaS.Pi.invariant_iff_dimensionless {m n : } (A : ExpMatrix m n) (c : Fin n) :
      (∀ (ψ : Fin m), i : Fin n, c i * act A ψ i = 0) ∀ (v : Fin m), i : Fin n, (A v i) * c i = 0

      The invariants are exactly the dimensionless groups.

      A monomial ∏ xᵢ^{cᵢ} (a linear functional ⟨c, ·⟩ in log coordinates) is invariant under every rescaling precisely when c lies in the kernel of the exponent matrix, which is Dimensionless A.

      This characterizes linear invariants only. invariant_descends separately proves that every invariant function descends to coordinates in a rational basis of the kernel; finrank_dimensionless_add_rank gives their count.

      The count. The dimensionless groups form a space of dimension n − r, and invariant_descends shows that every invariant depends on at most that many arguments. Particular functions may ignore some or all of them.

      Restated here from finrank_dimensionless_add_rank to keep the theorem's two halves (the factorization and the count) in one place.

      The isomorphism #

      Kennedy states the theorem as a type isomorphism. Here it is, as an actual bijection: the functions satisfying a signature's scaling law correspond exactly to the scale-invariant functions, and the correspondence is adding and subtracting the power-product ∏ xᵢ^{Xᵢ}.

      def LambdaS.Pi.Invariant {m n : } (A : ExpMatrix m n) (G : (Fin n)) :

      A function invariant under every rescaling: a function of the dimensionless groups alone, as invariant_descends proves below.

      Equations
      Instances For

        Descent to finitely many dimensionless coordinates #

        noncomputable def LambdaS.Pi.dimensionlessBasis {m n : } (A : ExpMatrix m n) :

        A rational basis of the dimensionless power products, indexed by the Buckingham count. Choosing it is noncomputable; each exponent is rational.

        Equations
        Instances For
          noncomputable def LambdaS.Pi.piCoordinates {m n : } (A : ExpMatrix m n) (ξ : Fin n) (j : Fin (n - Matrix.rank A)) :

          The logarithms of the independent dimensionless power products.

          Equations
          Instances For
            theorem LambdaS.Pi.exists_act_of_annihilates {m n : } (A : ExpMatrix m n) (δ : Fin n) ( : ∀ (c : (Dimensionless A)), i : Fin n, (c i) * δ i = 0) :
            ∃ (ψ : Fin m), act A ψ = δ

            If a real vector annihilates every rational dependency among the columns, it is an orbit displacement. Rational linear extension with real values avoids any appeal to an unproved scalar-extension identity for kernels.

            theorem LambdaS.Pi.exists_act_of_piCoordinates_eq {m n : } (A : ExpMatrix m n) {ξ η : Fin n} (h : piCoordinates A ξ = piCoordinates A η) :
            ∃ (ψ : Fin m), (fun (i : Fin n) => η i + act A ψ i) = ξ

            The independent dimensionless coordinates separate scaling orbits.

            theorem LambdaS.Pi.invariant_descends {m n : } (A : ExpMatrix m n) {H : (Fin n)} (hH : Invariant A H) :
            ∃ (G : (Fin (n - Matrix.rank A))), ∀ (ξ : Fin n), H ξ = G (piCoordinates A ξ)

            Arity descent. Every arbitrary (not necessarily linear or continuous) invariant is a function of n - rank A rational dimensionless power products, in logarithmic coordinates. This supplies the descent missing from the invariant-factor theorem alone.

            theorem LambdaS.Pi.scaleLaw_add_linear {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) {G : (Fin n)} (hG : Invariant A G) :
            ScaleLaw A b fun (ξ : Fin n) => i : Fin n, X i * ξ i + G ξ

            Adding the power-product back turns an invariant function into one obeying the scaling law. The inverse direction of pi_theorem.

            noncomputable def LambdaS.Pi.piEquiv {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) :
            { F : (Fin n) // ScaleLaw A b F } { G : (Fin n) // Invariant A G }

            Kennedy's isomorphism, as a bijection.

            Functions obeying the scaling law of the signature (A, b) are in bijection with scale-invariant functions. The separate invariant_descends theorem expresses each invariant in n − r rational dimensionless coordinates; this equivalence itself retains the invariant-function subtype as its codomain.

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

              The multiplicative bridge #

              ScaleLaw lives in log coordinates. The law the calculus itself delivers (scaleLaw in LambdaS.Fundamental) is multiplicative: rescaling each argument by the scale factor of its unit multiplies the result by the scale factor of the result unit. This section states that multiplicative law as a predicate on functions and factors any function satisfying it, on the positive orthant, as the power product ∏ xᵢ^{Xᵢ} times an invariant function of the log magnitudes.

              No positivity of the function is assumed anywhere in the factorization: only the arguments pass through exp and log, and the invariant factor carries whatever sign the function has (the zero function is carried by the zero invariant). Positivity enters only in scaleLaw_of_mulScaleLaw below, which additionally takes the logarithm of the output to present the law additively; that is a presentation choice, not a hypothesis of the theorem.

              def LambdaS.Pi.MulScaleLaw {m n : } (A : ExpMatrix m n) (b : Fin m) (f : (Fin n)) :

              The multiplicative scale law. Scaling each base unit v by a positive factor κ v scales argument i by ∏ v, κ v ^ A v i and the result by ∏ v, κ v ^ b v. This is the form of the scaling law a Λs term actually satisfies (den_mulScaleLaw below); ScaleLaw is its image in log coordinates. Exponents are rational, so the powers are Real.rpow under a cast.

              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                noncomputable def LambdaS.Pi.logConj {n : } (f : (Fin n)) (ξ : Fin n) :

                The log conjugate log ∘ f ∘ exp of a function on magnitudes. This is the F that ScaleLaw and pi_theorem speak about, produced from the multiplicative function the calculus provides.

                Equations
                Instances For
                  theorem LambdaS.Pi.prod_exp_rpow {m : } (ψ : Fin m) (c : Fin m) :
                  v : Fin m, Real.exp (ψ v) ^ (c v) = Real.exp (∑ v : Fin m, (c v) * ψ v)

                  A product of real powers of exponentials is the exponential of a dot product. The computation that carries the multiplicative law into log coordinates and back.

                  def LambdaS.Pi.ExpScaleLaw {m n : } (A : ExpMatrix m n) (b : Fin m) (g : (Fin n)) :

                  The scale law in exponential coordinates. The multiplicative law transported to log coordinates on the arguments only: the output is scaled, not logged, so the predicate makes sense for functions of any sign. This is the signed carrier of the Pi reduction; ScaleLaw is its image under a further logarithm on the values, available only for positive functions.

                  Equations
                  Instances For
                    theorem LambdaS.Pi.sum_act_shift {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) (ψ : Fin m) (ξ : Fin n) :
                    i : Fin n, X i * (ξ i + act A ψ i) = i : Fin n, X i * ξ i + v : Fin m, (b v) * ψ v

                    The displacement of the linear functional ⟨X, ·⟩ along the scaling action is exactly the output shift: sum_act_eq packaged with the splitting of the sum, since every use below needs the two together.

                    theorem LambdaS.Pi.expScaleLaw_of_mulScaleLaw {m n : } (A : ExpMatrix m n) (b : Fin m) {f : (Fin n)} (hmul : MulScaleLaw A b f) :
                    ExpScaleLaw A b fun (ξ : Fin n) => f fun (i : Fin n) => Real.exp (ξ i)

                    A function satisfying the multiplicative law, precomposed with exp, satisfies the exponential-coordinates law. No hypothesis on the function beyond the law itself: the exponentials are all on the argument side.

                    noncomputable def LambdaS.Pi.piEquivSigned {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) :
                    { g : (Fin n) // ExpScaleLaw A b g } { H : (Fin n) // Invariant A H }

                    Kennedy's isomorphism at the multiplicative level. Functions obeying the exponential-coordinates scale law of the signature (A, b) are in bijection with the scale-invariant functions, of any sign: the correspondence multiplies and divides by the exponential of the linear functional ⟨X, ·⟩, which is the power product ∏ xᵢ^{Xᵢ} in the original coordinates. The additive piEquiv is the same bijection read after a further logarithm on the values, which is where positivity would enter and why it is not needed here.

                    Equations
                    • One or more equations did not get rendered due to their size.
                    Instances For
                      theorem LambdaS.Pi.mulScaleLaw_factorization {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) {f : (Fin n)} (hmul : MulScaleLaw A b f) :
                      ∃ (H : (Fin n)), Invariant A H ∀ (x : Fin n), (∀ (i : Fin n), 0 < x i)f x = (∏ i : Fin n, x i ^ X i) * H fun (i : Fin n) => Real.log (x i)

                      Buckingham's factorization for multiplicative data, signed. A function satisfying the multiplicative scale law factors, on the positive orthant, as the power product ∏ i, x i ^ X i times a function of the log magnitudes that is invariant under every rescaling, hence a function of the dimensionless groups alone (invariant_descends). The explicit reduced-arity statement is mulScaleLaw_factorization_reduced.

                      No positivity of f is assumed and none survives in the conclusion: H is real-valued of whatever sign f takes, and the zero function is carried by H = 0. The factorization needs only the solvability witness X; when the system is unsolvable there is nothing to factor, because f is forced to vanish (mulScaleLaw_eq_zero_of_unsolvable).

                      theorem LambdaS.Pi.mulScaleLaw_factorization_reduced {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) {f : (Fin n)} (hmul : MulScaleLaw A b f) :
                      ∃ (G : (Fin (n - Matrix.rank A))), ∀ (x : Fin n), (∀ (i : Fin n), 0 < x i)f x = (∏ i : Fin n, x i ^ X i) * G (piCoordinates A fun (i : Fin n) => Real.log (x i))

                      Buckingham factorization with the reduced arity. On positive inputs, a function of arbitrary output sign satisfying the multiplicative scaling law is a power product times a function of n - rank A rational dimensionless combinations. piCoordinates A (log x) lists their logarithms.

                      The solvability dichotomy #

                      Kennedy's hypothesis that A X = b be solvable is not bookkeeping either, but its failure mode is total collapse rather than a missing factorization: an unsolvable signature admits only the zero function. Unsolvability over means b is separated from the column space of A by a linear functional; instantiating the multiplicative law along the one-parameter family of scalings in that functional's direction leaves every argument fixed while multiplying the output by a nonconstant factor, which forces the value to zero. Everywhere, not just on the positive orthant: the law itself quantifies over all argument vectors.

                      theorem LambdaS.Pi.exists_act_annihilator_of_unsolvable {m n : } (A : ExpMatrix m n) (b : Fin m) (hb : ¬∃ (X : Fin n), ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) :
                      ∃ (ψ : Fin m), (∀ (i : Fin n), act A ψ i = 0) v : Fin m, (b v) * ψ v 0

                      Unsolvability is witnessed by an annihilator. If A X = b has no real solution, some direction ψ annihilates every column of A (so the induced scaling moves no argument) while pairing nontrivially with b (so it moves the output). The linear geometry behind the dichotomy: b outside the column space of A is separated from it by a dual functional, and a functional on Fin m → ℝ is again a vector.

                      theorem LambdaS.Pi.mulScaleLaw_eq_zero_of_unsolvable {m n : } (A : ExpMatrix m n) (b : Fin m) (hb : ¬∃ (X : Fin n), ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) {f : (Fin n)} (hmul : MulScaleLaw A b f) (x : Fin n) :
                      f x = 0

                      Unsolvable signatures force zero. When A X = b has no real solution, the only function satisfying the multiplicative scale law is the zero function, on the whole space. The scaling built from the annihilator fixes every argument pointwise while scaling the output by a factor other than 1, and the only value that survives is 0.

                      scaleLaw_forces_zero in LambdaS.Definability is the one-variable special case of this collapse, stated for terms: there the result unit mentions a base unit no argument mentions, so the row of that base unit is zero in the matrix while its entry in b is not, and the annihilator is the coordinate direction itself.

                      theorem LambdaS.Pi.mulScaleLaw_dichotomy {m n : } (A : ExpMatrix m n) (b : Fin m) {f : (Fin n)} (hmul : MulScaleLaw A b f) :
                      (∃ (X : Fin n), (∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) ∃ (H : (Fin n)), Invariant A H ∀ (x : Fin n), (∀ (i : Fin n), 0 < x i)f x = (∏ i : Fin n, x i ^ X i) * H fun (i : Fin n) => Real.log (x i)) ∀ (x : Fin n), f x = 0

                      The dichotomy. A signature either admits a solution of A X = b, and then every function satisfying its multiplicative law factors through the dimensionless groups with the solution as exponent vector, or it admits none, and then the zero function is the only one satisfying the law. There is no third case: Kennedy's solvability hypothesis is not a gap in the theorem but the boundary between factorization and collapse.

                      The additive presentation #

                      theorem LambdaS.Pi.scaleLaw_of_mulScaleLaw {m n : } (A : ExpMatrix m n) (b : Fin m) {f : (Fin n)} (hmul : MulScaleLaw A b f) (hpos : ∀ (x : Fin n), (∀ (i : Fin n), 0 < x i)0 < f x) :

                      The additive presentation of the bridge. A function satisfying the multiplicative scale law and positive on positive inputs has a log conjugate satisfying the additive ScaleLaw with the same exponent matrix and result vector, which is the form pi_theorem and piEquiv consume.

                      Positivity is a presentation choice, not a hypothesis of the factorization: mulScaleLaw_factorization needs no sign information, because it exponentiates only the arguments. Only this statement, which also takes the logarithm of the output, must know the output is in the logarithm's domain. The zero function shows the hypothesis cannot be dropped from this presentation: it satisfies every multiplicative law, and its log conjugate is the constant log 0 = 0, which satisfies ScaleLaw A b only when b = 0.

                      theorem LambdaS.Pi.mulScaleLaw_factorization_pos {m n : } (A : ExpMatrix m n) (b : Fin m) (X : Fin n) (hX : ∀ (v : Fin m), i : Fin n, (A v i) * X i = (b v)) {f : (Fin n)} (hmul : MulScaleLaw A b f) (hpos : ∀ (x : Fin n), (∀ (i : Fin n), 0 < x i)0 < f x) :
                      ∃ (G : (Fin n)), Invariant A G ∀ (x : Fin n), (∀ (i : Fin n), 0 < x i)f x = (∏ i : Fin n, x i ^ X i) * Real.exp (G fun (i : Fin n) => Real.log (x i))

                      The positive corollary of mulScaleLaw_factorization: when f is positive on positive inputs, the invariant factor is positive everywhere and can be written as an exponential, recovering the classical form f(x) = (∏ xᵢ^{Xᵢ}) · exp(G(log x)). Derived from the signed factorization rather than proved separately: positivity only repackages H as Real.exp ∘ G.

                      From terms to the multiplicative law #

                      The last gap: scaleLaw in LambdaS.Fundamental speaks about environments and Scalings, while MulScaleLaw speaks about vectors of magnitudes and a matrix of exponents. This section closes it. For a first-order term over n scalar arguments, the denotation as a function of the argument magnitudes satisfies MulScaleLaw with the matrix whose column i collects the exponents of argument i's unit and the vector of the result unit's exponents. The row index runs over every rescalable symbol in scope, base units and unit variables alike, enumerated by an equivalence Fin m ≃ B ⊕ Fin k: a Scaling B k carries an independent factor for each, and scaleLaw quantifies over all of them, so the exponent matrix must have a row for each or the bridge would silently restrict the scaling group.

                      def LambdaS.Pi.envOf {B D : Type} {j k : } (us : List (UExp B k)) :
                      (Fin us.length)Env (scalarCtx us)

                      The environment of a first-order scalar signature, assembled from a vector of argument magnitudes. Inverse to reading the magnitudes off the environment.

                      Equations
                      Instances For
                        theorem LambdaS.Pi.scaleEnv_envOf {B D : Type} [Fintype B] {j k : } (ψ : Scaling B k) (us : List (UExp B k)) (x : Fin us.length) :
                        scaleEnv ψ us (envOf us x) = envOf us fun (i : Fin us.length) => ψ.scale (us.get i) * x i

                        Rescaling the environment of a scalar signature is rescaling each magnitude by the scale factor of its unit. Connects scaleEnv, which walks the context, to the componentwise scaling MulScaleLaw quantifies over.

                        noncomputable def LambdaS.Pi.scalingOfFactors {m : } {B : Type} {k : } (e : Fin m B Fin k) (κ : Fin m) :

                        The scaling whose factors are a given vector of positive reals, read through an enumeration of the base units and the unit variables in scope. The witness that instantiates scaleLaw at the scalings MulScaleLaw quantifies over: one independent factor for every symbol a scaling can move.

                        Equations
                        Instances For
                          theorem LambdaS.Pi.scale_scalingOfFactors {m : } {B : Type} [Fintype B] {k : } (e : Fin m B Fin k) (κ : Fin m) ( : ∀ (v : Fin m), 0 < κ v) (u : UExp B k) :
                          (scalingOfFactors e κ).scale u = v : Fin m, κ v ^ (Sum.elim u.base u.vars (e v))

                          The scale factor of scalingOfFactors e κ on a unit is the power product of the factors against the unit's full exponent vector, base and variable exponents both. Positivity of the factors is required: scale exponentiates the logs of κ, and log collapses nonpositive inputs.

                          theorem LambdaS.Pi.den_mulScaleLaw {B D : Type} [Fintype B] [UnitSys B D] {j k m : } {Δ : DCtx D j k} {e : Tm B D j k} {us : List (UExp B k)} {u₀ : UExp B k} (d : HasTy Δ (scalarCtx us) e (Ty.Q u₀)) (hp : e.Parametric) (hf : e.ConvertFree) (V : Scaling B k) (eqv : Fin m B Fin k) :
                          MulScaleLaw (Matrix.of fun (v : Fin m) (i : Fin us.length) => Sum.elim (us.get i).base (us.get i).vars (eqv v)) (fun (v : Fin m) => Sum.elim u₀.base u₀.vars (eqv v)) fun (x : Fin us.length) => den V d (envOf us x)

                          The term-level multiplicative scale law. The denotation of a first-order parametric convert-free term, as a function of its argument magnitudes, satisfies MulScaleLaw for the matrix of its arguments' exponents and the vector of its result unit's exponents, with one row for every rescalable symbol in scope: base units and unit variables alike, enumerated by Fin m ≃ B ⊕ Fin k.

                          Together with mulScaleLaw_factorization this closes the chain from a well-typed Λs term to Buckingham's factorization on positive inputs, with no sign restriction on the output. The term supplies the multiplicative law, and the factorization is signed. mulScaleLaw_dichotomy adds the other half: a signature whose system is unsolvable forces the denotation to vanish.

                          The law quantifies over the full scaling group of the signature: a rescaling may move base units and unit variables independently, which is exactly the freedom scaleLaw itself provides. For a closed program (k = 0) the Fin k summand is empty and the matrix is the base-unit matrix, so the concrete examples are unchanged.

                          theorem LambdaS.Pi.den_mulScaleLaw_driftFree {B D : Type} [Fintype B] [UnitSys B D] {j k : } [DecidableEq B] {m : } {Δ : DCtx D j k} {e : Tm B D j k} {us : List (UExp B k)} {u₀ : UExp B k} {d : HasTy Δ (scalarCtx us) e (Ty.Q u₀)} (h1 : unitDrift d = some 1) (V : Scaling B k) (eqv : Fin m B Fin k) :
                          MulScaleLaw (Matrix.of fun (v : Fin m) (i : Fin us.length) => Sum.elim (us.get i).base (us.get i).vars (eqv v)) (fun (v : Fin m) => Sum.elim u₀.base u₀.vars (eqv v)) fun (x : Fin us.length) => den V d (envOf us x)

                          The term-level multiplicative scale law, drift-free case. The same conclusion as den_mulScaleLaw with the convert-free hypothesis replaced by a drift diagnosis of 1: a program may convert as it likes, provided its conversions cancel (scaleLaw_of_driftFree). Parametricity is not assumed separately, because a unit constant is a ucon node and the drift analysis declines it. The two hypotheses are incomparable, so both theorems stay: (λx. λy. x + y) a b is convert-free but its drift is undetermined, since the two branches' ratios are atoms under distinct binders.

                          The pendulum, again #

                          The mass conclusion, stated here so the full pipeline can see it, in both halves. The mass exponent is zero in every solution (Pi.pendulum_period_independent_of_mass), so the power product ∏ xᵢ^{Xᵢ} does not mention the mass; and it is zero in every dimensionless group (Pi.pendulum_mass_drops_out), so G cannot mention it either. The period is independent of the mass, and both factors of the factorization say so.

                          theorem LambdaS.Pi.pendulum_mass_absent :
                          (∀ {X : Fin 4}, Matrix.mulVec pendulum X = ![0, 0, 1]X 0 = 0) ∀ {x : Fin 4}, x Dimensionless pendulumx 0 = 0