Zulip Chat Archive
Stream: maths
Topic: Defining a Type with multiple zeros
Max Bobbin (Jun 09 2023 at 15:22):
I am working on defining a type for physical quantities in Lean. I have already finished defining a type for dimensional analysis, the code can be found here. We have a manuscript almost done on the dimensional analysis stuff. For physical quantities the idea was to define a physical quantity type as the product of a number and the dimension, ie:
def science_unit (α : Type*) := prod ℝ (dimension α)
Right now just using real numbers, but it will eventually be generalized to a vector space. For dimensional analysis, there is no zero because there is no identity element for addition. However, for physical quantities there is an identity element for addition, however there are multiple. That's because if we had 1 Length and added 0, 0 would have to have dimensions of Length too. That's because the addition of two physical quantities is only allowed if they have the same dimension.
protected noncomputable def add {α} : science_unit α → science_unit α → science_unit α
|a b := (a.fst + b.fst, a.snd + b.snd)
I wanted to get feedback on how we would go about defining zero, or even if it was possible to define a zero if a Type has multiple forms of zero.
Yury G. Kudryashov (Jun 09 2023 at 15:56):
You mean a.snd
, not a.snd + b.snd
Yury G. Kudryashov (Jun 09 2023 at 15:57):
Do you want add
to take a proof of a.snd = b.snd
as an argument, or define it for all science_unit
s and ask for a.snd = b.snd
in proofs of properties?
Yury G. Kudryashov (Jun 09 2023 at 15:58):
In the first case you're sure that one can't get 0 m + 1 s
by accident.
Yury G. Kudryashov (Jun 09 2023 at 15:59):
Another approach is to allow such sums and deal with dimension α →₀ ℝ
instead.
Yury G. Kudryashov (Jun 09 2023 at 16:00):
(or, if you have a group
structure on dimension
s, then monoid_algebra ℝ (dimension α)
)
Yury G. Kudryashov (Jun 09 2023 at 16:01):
In this case you allow expressions like 0 m + 1 s
Yury G. Kudryashov (Jun 09 2023 at 16:01):
Unfortunately, the product type doesn't have a vector space (or even an additive group) structure.
Yury G. Kudryashov (Jun 09 2023 at 16:02):
(and probably you want to talk about (10 m + 20 m) / (30 s) ^ 2
)
Yury G. Kudryashov (Jun 09 2023 at 16:02):
Are there projects that formalize computations with dimensions in Coq, Isabelle, etc?
Yury G. Kudryashov (Jun 09 2023 at 16:03):
If yes, what choices did they make?
Yury G. Kudryashov (Jun 09 2023 at 16:03):
BTW, do you want to allow 1 cm + 3 m
or 5 in + 3 cm
?
Yury G. Kudryashov (Jun 09 2023 at 16:04):
Or (1 cm).to_dim in
?
Max Bobbin (Jun 09 2023 at 16:23):
Yury G. Kudryashov said:
You mean
a.snd
, nota.snd + b.snd
We defined addition already in dimensional analysis using classical choice such that it only allows to dimensions to be added if they are like dimensions. So you can have 1 m + 1 s
but it won't simplify.
protected noncomputable def add {α}: dimension α → dimension α → dimension α :=
classical.epsilon $ λ f, ∀ a b, a = b → f a b = a
Max Bobbin (Jun 09 2023 at 16:24):
Yury G. Kudryashov said:
(or, if you have a
group
structure ondimension
s, thenmonoid_algebra ℝ (dimension α)
)
We have already defined the multiplicative comm_group for dimensions, but no groups for addition because dimensions don't have a zero
Max Bobbin (Jun 09 2023 at 16:24):
Yury G. Kudryashov said:
Are there projects that formalize computations with dimensions in Coq, Isabelle, etc?
not from what I have seen, but I could have missed it
Yury G. Kudryashov (Jun 09 2023 at 16:29):
If you want to allow expressions like 1 m + 1 s
, then I recommend using docs#monoid_algebra.
Yury G. Kudryashov (Jun 09 2023 at 16:30):
It explicitly allows such expressions without asking for addition on dimension
s.
Yury G. Kudryashov (Jun 09 2023 at 16:31):
Then you can define division that makes sense only if the denominator is a non-zero single
Max Bobbin (Jun 09 2023 at 16:32):
Yury G. Kudryashov said:
BTW, do you want to allow
1 cm + 3 m
or5 in + 3 cm
?
We want to allow this, but the addition won't simplify unless you convert the units. My idea of defining these units was:
def meter (α) [has_length α] : science_unit α := (1, dimension.length α)
noncomputable def centimeter (α) [has_length α] := 100*(meter α)
def length_meter (α) [has_length α] (value : ℝ) := value*(meter α)
Although this is the current idea, I'm not happy with it because it requires us to agree that the base SI units are the reference points. Could possibly get around this with the class strucutre
Max Bobbin (Jun 09 2023 at 16:33):
Yury G. Kudryashov said:
If you want to allow expressions like
1 m + 1 s
, then I recommend using docs#monoid_algebra.
Let me look at the monoid_algebra. I'm not familiar with it. What is the flaw with the current way if the addition for dimensions is already defined since the addition of the dimensions checks to make sure the units are compatible to add?
Jireh Loreaux (Jun 09 2023 at 16:33):
Max, you definitely want docs#monoid_algebra.
Max Bobbin (Jun 09 2023 at 16:35):
Ok, let me play around with some stuff and see how it works. Thank you!
Max Bobbin (Jun 09 2023 at 16:35):
Do you have any recommendations for resources to learn about monoid algebra and how I would apply it here?
Jireh Loreaux (Jun 09 2023 at 16:42):
You would use monoid_algebra ℝ (dimension α)
. This is a synonym for dimension α →₀ ℝ
, the collection of finitely supported functions on dimension α
. You should think of these as finite formal sums of scalar multiples of dimensions. (e.g., like 2 d + π d'
, where d d' : dimension α
). This structure forms an ℝ
-algebra. The multiplication is defined in the way you want (as a convolution using the comm_group
structure you have on dimensions).
Jireh Loreaux (Jun 09 2023 at 16:53):
So, suppose d₁ d₂ d₃ d₄ : dimension α
, r₁ r₂ r₃ r₃ : ℝ
and r₁ d₁ + r₂ d₂
and r₃ d₃ + r₄ d₄ : monoid_algebra ℝ (dimension α)
(these last two are not valid syntax), then
(r₁ d₁ + r₂ d₂) * (r₃ d₃ + r₄ d₄) = (r₁ * r₃) (d₁ * d₃) + (r₁ * r₄) (d₁ * d₄) + (r₂ * r₃) (d₂ * d₃) + (r₂ * r₄) (d₂ * d₄)
And, supposing for the sake of example that d₁ * d₃ = d₂ * d₄
and d₁ * d₄ = d₂ * d₃
, then the above is equal to: (r₁ * r₃ + r₂ * r₄) (d₁ * d₃) + (r₁ * r₄ + r₂ * r₃) (d₁ * d₄)
Jireh Loreaux (Jun 09 2023 at 16:54):
The classic example of a monoid_algebra
is the group ring of a group G
(which is monoid_algebra ℤ G
)
Eric Wieser (Jun 09 2023 at 17:05):
Max Bobbin said:
Yury G. Kudryashov said:
If you want to allow expressions like
1 m + 1 s
, then I recommend using docs#monoid_algebra.Let me look at the monoid_algebra. I'm not familiar with it.
@Max Bobbin, can you confirm whether you actually do want to allow 1 m + 1 s
?
Jireh Loreaux (Jun 09 2023 at 17:20):
(If not then (d : dimension α)
should be a parameter to science_unit
, and then science_unit
is just a fancy type synonym for ℝ
and it will be hard to combine units)
Kevin Buzzard (Jun 09 2023 at 17:39):
That weird auxiliary sigma type which we use in grading theory has lots of zeros
Max Bobbin (Jun 09 2023 at 18:27):
Eric Wieser said:
Max Bobbin said:
Yury G. Kudryashov said:
If you want to allow expressions like
1 m + 1 s
, then I recommend using docs#monoid_algebra.Let me look at the monoid_algebra. I'm not familiar with it.
Max Bobbin, can you confirm whether you actually do want to allow
1 m + 1 s
?
We don't want 1 m + 1 s
to be a valid form, but I was thinking we would have to allow it to at least be possible to write so we can write complex equations like 1 m / 1 s + 1 m/s
The second equation is a valid equation to reduce to 2 m/s
where as 1 m + 1 s
can't be reduced. Not sure if that answers the question though, I may be misunderstanding what you are asking.
Yury G. Kudryashov (Jun 09 2023 at 19:11):
Another way to do it is to use Lean 4 docs4#HMul and docs4#HAdd
Yury G. Kudryashov (Jun 09 2023 at 19:13):
You can say that ScienceUnit (d : Dimension α) := ℝ
, then define AddCommGroup
instance for each ScienceUnit d
and HMul
instances for ScienceUnit d1 -> ScienceUnit d2 -> ScienceUnit (d1 * d2)
.
Yury G. Kudryashov (Jun 09 2023 at 19:14):
This way you will be able to write a + b
only if a
and b
have definitionally equal dimensions.
Yury G. Kudryashov (Jun 09 2023 at 19:14):
(so, you need computable dimensions)
Jireh Loreaux (Jun 09 2023 at 19:15):
I think requiring defeq dimensions would be miserable. However, using monoid_algebra
with an is_valid_form
predicate saying that an element is equal to some finsupp.single
would make sense.
Jireh Loreaux (Jun 09 2023 at 19:15):
That way you avoid computability requirements.
Yury G. Kudryashov (Jun 09 2023 at 19:19):
As you can see, you can get at least n + 1
opinions from n
experienced users. ;-)
Jireh Loreaux (Jun 09 2023 at 19:25):
1 m / 1 s + 1 m/s
is exactly the kind of thing you could do with monoid_algebra
(or more precisely, 1 m * 1 s⁻¹ + 1 (m * s⁻¹)
) and have it make sense easily. Admittedly, Yury's heterogeneous suggestion should also work, but I would be concerned you would run into unification headaches (maybe that's an unfounded concern).
Max Bobbin (Jun 09 2023 at 19:28):
I see, I'll work on implementing this. The other thing I've been mulling over is scale. I'm curious to see what others think, but my guess is using monoid_algebra
we could define meter := monoid_algebra 1 dimension.length
and then centimeter := meter/100
. I worry that this definition is limiting, but I could be wrong.
Yury G. Kudryashov (Jun 09 2023 at 19:57):
You can define centimeter := 100⁻¹ • meter
Max Bobbin (Jun 10 2023 at 18:47):
Yury G. Kudryashov said:
You can define
centimeter := 100⁻¹ • meter
What I meant was, I think there should be a better way then hoping that everyone agrees that the reference unit for length is 1 meter and everything is defined off the meter. The worry is someone comes along and defines the foot and they define it as 1, then you have 1 meter = 1 foot. Wanted to know if people had thoughts on this if its avoidable
Kevin Buzzard (Jun 10 2023 at 18:50):
You can either (a) use nonnegative reals and label your axes with the unit being used (meter, foot,...), (b) use no labelling and make your axes homogeneous spaces (so there is no "1" but you can still add, and multiply by a natural) or (c) use the nonnegative reals, not label your axes, and then have the ambiguity.
Mario Carneiro (Jun 10 2023 at 18:51):
Just call the type structure Length := (meters : Real)
, now if someone does the same definition for feet there is a clash
Max Bobbin (Jun 10 2023 at 18:52):
What do you mean by labeling your axes? The second option sounds like what I could be looking for, but I'm not familiar with homogeneous spaces
Mario Carneiro (Jun 10 2023 at 18:54):
the idea being that when you define def meter : Length := { meters := 1 }
, in order to get unit confusion you would need to also define def foot : Length := { meters := 1 }
which is obviously suspicious
Mario Carneiro (Jun 10 2023 at 18:55):
This is basically the argument for newtypes in the setting of lean
Yury G. Kudryashov (Jun 10 2023 at 22:08):
@Mario Carneiro This doesn't help with 1 m / 1 s
Kevin Buzzard (Jun 10 2023 at 22:19):
Max Bobbin said:
What do you mean by labeling your axes? The second option sounds like what I could be looking for, but I'm not familiar with homogeneous spaces
Labeling your axes: I mean what Mario is suggesting, with the constructor names saying which units you'er using. The homogeneous space idea: I'm suggesting not using the real numbers but a 1-dimensional vector space with no fixed choice of basis. This would be the "correct" abstract unitless way to do it. So you just have some 1-d space V and then a non-zero element of V represents a measurement, and given two nonzero elements of V there's a unique real number such that , but it doesn't make sense to say "v=1" or "v=37" or anything, until you choose the unit, i.e. pick a basis. The moment you pick a basis for (e.g. 1 metre or 1 foot) then every is of the form for a real number and now you can call "r metres" or "r feet" or whatever, although of course the value of changes depending on which basis you pick.
Eric Wieser (Jun 10 2023 at 22:36):
Max Bobbin said:
I have already finished defining a type for dimensional analysis, the code can be found here.
This code looks a bit weird to me:
class has_time (α : Type u) :=
[dec : decidable_eq α]
(time [] : α)
(h : [time].nodup)
What was your intention with [time].nodup
? [x].nodup
is always true, docs#list.nodup_singleton
Yury G. Kudryashov (Jun 10 2023 at 22:54):
@Kevin Buzzard How do you suggest to implement a 1-dimensional vector space with no fixed choice of basis?
Yury G. Kudryashov (Jun 10 2023 at 22:54):
What is the type definition?
Mario Carneiro (Jun 10 2023 at 23:00):
Yury G. Kudryashov said:
Mario Carneiro This doesn't help with
1 m / 1 s
What I described was just the 1D space for lengths. If you want to have all your units in a pot you need some monoid_algebra
thing
Eric Wieser (Jun 10 2023 at 23:00):
The "all the units in a pot" thing feels like the wrong design to me
Mario Carneiro (Jun 10 2023 at 23:00):
but you can build that on top of Length
as one of the coordinates
Eric Wieser (Jun 10 2023 at 23:01):
If you're working in any real physical problem all your dimensions are known down to constructors anyway, so you have no defeq problems
Mario Carneiro (Jun 10 2023 at 23:01):
you might have an n-dimensional problem?
Mario Carneiro (Jun 10 2023 at 23:02):
granted that's probably not too common in physics, unless you are talking about e.g. the lagrangian in n-dimensional phase space
Eric Wieser (Jun 10 2023 at 23:02):
These are dimensions in the sense of metrology not space
Eric Wieser (Jun 10 2023 at 23:03):
Ah, I guess your lagrangian remark covers that
Eric Wieser (Jun 10 2023 at 23:03):
I think it's rare enough that dealing with a dimension.cast
of some form very rarely is much less bad than having to constantly persuade lean you haven't mixed incompatible dimensions
Eric Wieser (Jun 10 2023 at 23:35):
I'd probably go for something like
class has_time (α : Type u) := (time [] : α)
class has_length (α : Type u) := (length [] : α)
class has_mass (α : Type u) := (mass [] : α)
@[reducible] def dimension (α : Type u) : Type := α → multiplicative ℚ
@[reducible] def dimension.of {α : Type u} (a : α) : dimension α := pi.single a (multiplicative.of_add 1)
def length (α) [has_length α] [decidable_eq α] : dimension α := dimension.of $ has_length.length α
def time (α) [has_time α] [decidable_eq α] : dimension α := dimension.of $ has_time.time α
def mass (α) [has_mass α] [decidable_eq α] : dimension α := dimension.of $ has_mass.mass α
@[nolint unused_arguments]
structure measurement (V) {α : Type*} (dim : α) :=of :: (value : V)
Eric Wieser (Jun 10 2023 at 23:37):
And then use it as
@[derive decidable_eq]
inductive system1
| second | meter
instance : has_time system1 := { time := system1.second }
instance : has_length system1 := { length := system1.meters }
-- `measurement real (time system1)` is a real-valued duration
Kevin Buzzard (Jun 11 2023 at 07:10):
@Yury G. Kudryashov just ask findim V = 1
(or whatever the nat version of dimension is called)
Yury G. Kudryashov (Jun 11 2023 at 15:07):
I meant "how to define types", not "how to define a typeclass".
Yury G. Kudryashov (Jun 11 2023 at 15:08):
BTW, not a formalization but a good C++ library for units: https://www.boost.org/doc/libs/1_65_0/doc/html/boost_units.html
Max Bobbin (Jun 12 2023 at 23:09):
Eric Wieser said:
I'd probably go for something like
class has_time (α : Type u) := (time [] : α) class has_length (α : Type u) := (length [] : α) class has_mass (α : Type u) := (mass [] : α) @[reducible] def dimension (α : Type u) : Type := α → multiplicative ℚ @[reducible] def dimension.of {α : Type u} (a : α) : dimension α := pi.single a (multiplicative.of_add 1) def length (α) [has_length α] [decidable_eq α] : dimension α := dimension.of $ has_length.length α def time (α) [has_time α] [decidable_eq α] : dimension α := dimension.of $ has_time.time α def mass (α) [has_mass α] [decidable_eq α] : dimension α := dimension.of $ has_mass.mass α @[nolint unused_arguments] structure measurement (V) {α : Type*} (dim : α) :=of :: (value : V)
I remember being recommended something like this in the past, but I couldn't find information that explained what multiplicative
was. Could someone explain how multiplicative
works?
Max Bobbin (Jun 12 2023 at 23:13):
I guess my big confusion is how multiplicative and additive structures relate.
Max Bobbin (Jun 12 2023 at 23:17):
I notice that when I use this definition: @[reducible] def dimension (α : Type u) := multiplicative (α → ℚ)
The abelian group comes automatically, however, addition doesn't come (as to be expected). If I do .to_additive
does this convert multiplication operations to addition? My guess was the .to_additive
makes the dimensions act like we were manipulating a vector of exponent values, which are done under the operations of addition. If so, how does this work with the need to define the addition of dimensions to be idempotent? My other concern with this way is I'm not sure if the exponents are being added correctly. While I can say area := length^2
I want the underlying code to have an area of 2 when indexed by the Length base dimension class. This is important when we try to implement the Buckingham pi theorem.
Max Bobbin (Jun 12 2023 at 23:19):
Kevin Buzzard said:
Max Bobbin said:
What do you mean by labeling your axes? The second option sounds like what I could be looking for, but I'm not familiar with homogeneous spaces
Labeling your axes: I mean what Mario is suggesting, with the constructor names saying which units you'er using. The homogeneous space idea: I'm suggesting not using the real numbers but a 1-dimensional vector space with no fixed choice of basis. This would be the "correct" abstract unitless way to do it. So you just have some 1-d space V and then a non-zero element of V represents a measurement, and given two nonzero elements of V there's a unique real number such that , but it doesn't make sense to say "v=1" or "v=37" or anything, until you choose the unit, i.e. pick a basis. The moment you pick a basis for (e.g. 1 metre or 1 foot) then every is of the form for a real number and now you can call "r metres" or "r feet" or whatever, although of course the value of changes depending on which basis you pick.
Unless I'm misunderstanding, the only problem I have with implementing this is I can't make dimensions into a module because they don't form an add_comm_monoid. The exponents of dimensions form one, I believe.
Max Bobbin (Jun 12 2023 at 23:25):
or wait, the vector space isn't over the dimensions, is it?
Yury G. Kudryashov (Jun 12 2023 at 23:27):
multiplicative G
is a type synonym for G
; for each additive structure on G
(has_add
, add_comm_monoid
etc), it defines the corresponding multiplicative structure which is mathematically the same structure with different notation.
Eric Wieser (Jun 12 2023 at 23:39):
The abelian group comes automatically, however, addition doesn't come (as to be expected).
I'd go as far as saying that you don't want the additive structure in the first place. Leave that to monoid_algebra
to generate if you really want it.
Eric Wieser (Jun 12 2023 at 23:46):
If so, how does this work with the need to define the addition of dimensions to be idempotent?
If you replace this with "addition of (mixed) measurements", then monoid_algebra
handles this for you; 1 * of R meters + 2 * of R meters = 3 * of R meters
Eric Wieser (Jun 12 2023 at 23:46):
If you restrict yourself to well-typed measurements then the question does't even make sense
Eric Wieser (Jun 12 2023 at 23:47):
My other concern with this way is I'm not sure if the exponents are being added correctly.
Write a theorem to prove that they are!
Last updated: Dec 20 2023 at 11:08 UTC