Documentation

Analysis.Misc.UnitsSystem

A framework to formalize units (such as length, time, mass, velocity, etc.) in Lean.

class UnitsSystem :
Type (u_1 + 1)
Instances
    @[reducible, inline]

    The two key types here are Formal and Scalar d. Scalar d is the space of scalar quantities whose units are given by d:Dimensions. Collectively, they generate a graded commutative ring Formal, which can be conveniently described using the existing Mathlib structure AddMonoidAlgebra. Algebraic manipulations of scalar quantities will be most conveniently handled by casting these quantities into the commutative ring Formal, where one can use standard Mathlib tactics such as ring.

    In principle one could also develop vector-valued quantities with dimension, but for now we restrict attention to scalar quantities only.

    Equations
    Instances For

      The data val of a scalar quantity can be interpreted as the numerical value of that quantity with respect to some standard set of units (e.g., SI units).

      Instances For
        theorem UnitsSystem.Scalar.ext {inst✝ : UnitsSystem} {d : Dimensions} {x y : Scalar d} (val : x.val = y.val) :
        x = y
        theorem UnitsSystem.Scalar.ext_iff {inst✝ : UnitsSystem} {d : Dimensions} {x y : Scalar d} :
        x = y x.val = y.val
        theorem UnitsSystem.Scalar.val_inj [UnitsSystem] {d : Dimensions} (q₁ q₂ : Scalar d) :
        q₁.val = q₂.val q₁ = q₂

        We will encounter a technical issue with Lean's type system, namely that the type Scalar d and Scalar d' are not identical if d' and d are merely propositionally equal (as opposed to definitionally equal); for instance, Scalar (d₁+d₂) and Scalar (d₂+d₁) are distinct types. Technically, this renders multiplication on scalar types noncommutative. To get around this, we create a casting operator, where the propositional equality is attempted to be resolved by the Lean tactic module whenever possible. Unfortunately, the casting operator from Scalar d to Scalar d' cannot be captured by standard Lean coercion classes such as Coe or CoeOut as each of the types here contain parameters not present in the other.

        Equations
        Instances For
          theorem UnitsSystem.Scalar.cast_eq [UnitsSystem] {d d' : Dimensions} (q : Scalar d) (q' : Scalar d') (h : d = d' := by module) :
          q.val = q'.val q = q'.cast h

          This is a variant of Scalar.val_inj that handles casts.

          theorem UnitsSystem.Scalar.cast_eq_symm [UnitsSystem] {d d' : Dimensions} (q : Scalar d) (q' : Scalar d') (h : d = d' := by module) :
          q = q'.cast h q' = q.cast
          @[simp]
          theorem UnitsSystem.Scalar.cast_val [UnitsSystem] {d d' : Dimensions} (q : Scalar d) (h : d' = d := by module) :
          (q.cast h).val = q.val

          The existing Mathlib method AddMonoidAlgebra.single is perfect for embedding each type of scalar into the formal graded ring Formal.

          Equations
          Instances For
            @[simp]
            theorem UnitsSystem.Scalar.toFormal_inj [UnitsSystem] {d : Dimensions} (q₁ q₂ : Scalar d) :
            q₁ = q₂ q₁ = q₂

            Many identities involving several types of Scalars can be dealt with by applying simp [←toFormal_inj] to move everything to Formal. A large number of further simp lemmas in this file are then designed to simplify such Formal expressions, often by pushing casting operators inward back to the Scalar types. As such, there will be significant overlap between the simp and norm_cast tags.

            @[simp]
            theorem UnitsSystem.Scalar.toFormal_cast [UnitsSystem] {d d' : Dimensions} (q : Scalar d) (h : d' = d := by module) :
            (q.cast h) = q

            Conveniently, casts from one scalar to another will automatically disappear when moving to Formal.

            We will use the NeZero class to tag some scalars as non-zero; this becomes relevant when using such scalars as units. One could also introduce API to tag some scalars as positive, but we currently are not implementing this.

            In the next few lines of code we give Scalar d the structure of a real vector space, which is of course compatible with the real vector space structure on Formal.

            Equations
            @[simp]
            theorem UnitsSystem.Scalar.val_add [UnitsSystem] {d : Dimensions} (q₁ q₂ : Scalar d) :
            (q₁ + q₂).val = q₁.val + q₂.val
            @[simp]
            theorem UnitsSystem.Scalar.toFormal_add [UnitsSystem] {d : Dimensions} (q₁ q₂ : Scalar d) :
            ↑(q₁ + q₂) = q₁ + q₂

            Note how the simp lemma is in the direction of pushing casts inward.

            @[simp]
            theorem UnitsSystem.Scalar.toFormal_neg [UnitsSystem] {d : Dimensions} (q : Scalar d) :
            ↑(-q) = -q
            Equations
            @[simp]
            theorem UnitsSystem.Scalar.val_sub [UnitsSystem] {d : Dimensions} (q₁ q₂ : Scalar d) :
            (q₁ - q₂).val = q₁.val - q₂.val
            @[simp]
            theorem UnitsSystem.Scalar.toFormal_sub [UnitsSystem] {d : Dimensions} (q₁ q₂ : Scalar d) :
            ↑(q₁ - q₂) = q₁ - q₂
            instance UnitsSystem.Scalar.instSMul [UnitsSystem] {α : Type u_2} {d : Dimensions} [SMul α ] :
            SMul α (Scalar d)
            Equations
            @[simp]
            theorem UnitsSystem.Scalar.val_smul [UnitsSystem] {α : Type u_1} {d : Dimensions} [SMul α ] (a : α) (q : Scalar d) :
            (a q).val = a q.val

            The dimensionless scalars Scalar 0 can be identified with real numbers.

            Equations
            • r = { val := r }
            Instances For
              @[simp]
              theorem UnitsSystem.Scalar.coe_val [UnitsSystem] (r : ) :
              (↑r).val = r
              @[simp]
              theorem UnitsSystem.Scalar.coe_inj [UnitsSystem] {r s : } :
              r = s r = s
              @[simp]
              theorem UnitsSystem.Scalar.coe_add [UnitsSystem] (r s : ) :
              ↑(r + s) = r + s
              @[simp]
              theorem UnitsSystem.Scalar.coe_neg [UnitsSystem] (r : ) :
              ↑(-r) = -r
              @[simp]
              theorem UnitsSystem.Scalar.coe_sub [UnitsSystem] (r s : ) :
              ↑(r - s) = r - s

              It is convenient to view the real numbers as a subring of the Formal ring, thus identifying scalar multiplication with ordinary multiplication.

              Equations
              @[simp]
              @[simp]
              @[simp]
              theorem UnitsSystem.Formal.coe_nat [UnitsSystem] (n : ) :
              n = n
              @[simp]
              theorem UnitsSystem.Formal.coe_int [UnitsSystem] (n : ) :
              n = n
              @[simp]
              theorem UnitsSystem.Scalar.toFormal_smul [UnitsSystem] {d : Dimensions} (c : ) (q : Scalar d) :
              ↑(c q) = c * q
              @[simp]
              theorem UnitsSystem.Formal.smul_eq_mul [UnitsSystem] (c : ) (x : Formal) :
              c x = c * x
              @[simp]
              theorem UnitsSystem.Formal.smul_eq_mul' [UnitsSystem] (c : ) (x : Formal) :
              c x = c * x
              @[simp]
              theorem UnitsSystem.Formal.smul_eq_mul'' [UnitsSystem] (c : ) (x : Formal) :
              c x = c * x
              @[simp]
              theorem UnitsSystem.Scalar.coe_mul [UnitsSystem] (r s : ) :
              ↑(r * s) = r s

              We are finally able to view Scalar as a vector space over as promised.

              Equations
              @[simp]
              theorem UnitsSystem.Scalar.val_smul' [UnitsSystem] {d : Dimensions} (c : ) (q : Scalar d) :
              (c q).val = c * q.val
              @[simp]
              theorem UnitsSystem.Scalar.val_smul'' [UnitsSystem] {d : Dimensions} (c : ) (q : Scalar d) :
              (c q).val = c * q.val
              @[simp]
              theorem UnitsSystem.Scalar.toFormal_smul' [UnitsSystem] {d : Dimensions} (c : ) (q : Scalar d) :
              ↑(c q) = c * q
              @[simp]
              theorem UnitsSystem.Scalar.toFormal_smul'' [UnitsSystem] {d : Dimensions} (c : ) (q : Scalar d) :
              ↑(c q) = c * q
              instance UnitsSystem.Scalar.instHMul [UnitsSystem] {d₁ d₂ : Dimensions} :
              HMul (Scalar d₁) (Scalar d₂) (Scalar (d₁ + d₂))

              One can multiply a Scalar d₁ and Scalar d₂ quantities to obtain a Scalar (d₁+d₂) quantity, in a manner compatible with multiplication in Formal.

              Equations
              @[simp]
              theorem UnitsSystem.Scalar.val_hMul [UnitsSystem] {d₁ d₂ : Dimensions} (q₁ : Scalar d₁) (q₂ : Scalar d₂) :
              (q₁ * q₂).val = q₁.val * q₂.val
              @[simp]
              theorem UnitsSystem.Scalar.toFormal_hMul [UnitsSystem] {d₁ d₂ : Dimensions} (q₁ : Scalar d₁) (q₂ : Scalar d₂) :
              ↑(q₁ * q₂) = q₁ * q₂
              noncomputable def UnitsSystem.Scalar.pow [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
              Scalar (n d)

              Similarly, one can raise a Scalar d quantity to a natural number power n to obtain a Scalar (n • d) quantity. One could also implement exponentiation to an integer, but I have elected not to do this, implementing an inversion relation instead.

              Equations
              Instances For

                One cannot use the Mathlib classes Pow or HPow here because the output type Scalar (n • d) depends on the input n. As the symbol ^ is reserved for such classes, we use the symbol ** isntead.

                Equations
                Instances For
                  @[simp]
                  theorem UnitsSystem.Scalar.val_pow [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                  (q**n).val = q.val ^ n
                  @[simp]
                  theorem UnitsSystem.Scalar.toFormal_pow [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                  ↑(q**n) = q ^ n
                  noncomputable def UnitsSystem.Scalar.inv [UnitsSystem] {d : Dimensions} (q : Scalar d) :

                  We cannot use Mathlib's Inv class here or the associated ⁻¹ notation because Inv requires the output to be of the same type as the input.

                  Equations
                  Instances For
                    @[simp]
                    theorem UnitsSystem.Scalar.mul_inv_self [UnitsSystem] {d : Dimensions} (q : Scalar d) [h : NeZero q] :
                    q * q.inv = 1
                    @[simp]
                    theorem UnitsSystem.Scalar.inv_mul_self [UnitsSystem] {d : Dimensions} (q : Scalar d) [h : NeZero q] :
                    q.inv * q = 1
                    @[simp]
                    theorem UnitsSystem.Scalar.inv_coe [UnitsSystem] (r : ) :
                    (↑r).inv = r⁻¹
                    @[simp]
                    theorem UnitsSystem.Scalar.mul_inv [UnitsSystem] {d₁ d₂ : Dimensions} (q₁ : Scalar d₁) (q₂ : Scalar d₂) :
                    (q₁ * q₂).inv = (q₁.inv * q₂.inv).cast
                    @[simp]
                    theorem UnitsSystem.Scalar.pow_inv [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                    (q**n).inv = (q.inv**n).cast
                    noncomputable instance UnitsSystem.Scalar.instHDiv [UnitsSystem] {d₁ d₂ : Dimensions} :
                    HDiv (Scalar d₁) (Scalar d₂) (Scalar (d₁ - d₂))

                    Multiplication and inversion combine to give division in the usual fashion.

                    Equations
                    @[simp]
                    theorem UnitsSystem.Scalar.val_hDiv [UnitsSystem] {d₁ d₂ : Dimensions} (q₁ : Scalar d₁) (q₂ : Scalar d₂) :
                    (q₁ / q₂).val = q₁.val / q₂.val
                    @[simp]
                    theorem UnitsSystem.Scalar.toFormal_hDiv [UnitsSystem] {d₁ d₂ : Dimensions} (q₁ : Scalar d₁) (q₂ : Scalar d₂) :
                    ↑(q₁ / q₂) = q₁ * q₂.inv
                    noncomputable instance UnitsSystem.Scalar.instHDiv' [UnitsSystem] {d : Dimensions} :
                    Equations
                    noncomputable instance UnitsSystem.Scalar.instHDiv'' [UnitsSystem] {d : Dimensions} :
                    Equations
                    Equations
                    @[simp]
                    theorem UnitsSystem.Scalar.val_hDiv' [UnitsSystem] {d : Dimensions} (q : Scalar d) (r : ) :
                    (q / r).val = q.val / r
                    @[simp]
                    theorem UnitsSystem.Scalar.val_hDiv'' [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                    (q / n).val = q.val / n
                    @[simp]
                    theorem UnitsSystem.Scalar.val_hDiv''' [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                    (q / n).val = q.val / n
                    @[simp]
                    theorem UnitsSystem.Scalar.toFormal_hDiv' [UnitsSystem] {d : Dimensions} (q : Scalar d) (r : ) :
                    ↑(q / r) = q * r⁻¹
                    @[simp]
                    theorem UnitsSystem.Scalar.toFormal_hDiv'' [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                    ↑(q / n) = q * (↑n)⁻¹
                    @[simp]
                    theorem UnitsSystem.Scalar.toFormal_hDiv''' [UnitsSystem] {d : Dimensions} (q : Scalar d) (n : ) :
                    ↑(q / n) = q * (↑n)⁻¹
                    Equations
                    • One or more equations did not get rendered due to their size.

                    The standard unit of Scalar d is the quantity whose data val is equal to 1.

                    Equations
                    Instances For
                      @[simp]
                      @[simp]
                      theorem UnitsSystem.StandardUnit.mul' [UnitsSystem] (d₁ d₂ : Dimensions) :
                      (StandardUnit d₁) * (StandardUnit d₂) = (StandardUnit (d₁ + d₂))
                      @[simp]
                      @[simp]
                      noncomputable def UnitsSystem.Scalar.in [UnitsSystem] {d : Dimensions} (unit q : Scalar d) :

                      unit.in q is q:Scalar d measured in terms of unit:Scalar d.

                      Equations
                      Instances For
                        @[simp]
                        theorem UnitsSystem.Scalar.val_in [UnitsSystem] (d : Dimensions) (unit q : Scalar d) :
                        unit.in q = q.val / unit.val
                        theorem UnitsSystem.Scalar.in_def [UnitsSystem] {d : Dimensions} (unit q : Scalar d) [h : NeZero unit] :
                        q = unit.in q unit
                        @[simp]
                        theorem UnitsSystem.Scalar.in_smul [UnitsSystem] {d : Dimensions} (c : ) (unit q : Scalar d) :
                        unit.in (c q) = c * unit.in q
                        theorem UnitsSystem.Scalar.in_inj [UnitsSystem] {d : Dimensions} (unit q₁ q₂ : Scalar d) [h : NeZero unit] :
                        unit.in q₁ = unit.in q₂ q₁ = q₂