Zulip Chat Archive

Stream: Is there code for X?

Topic: Max instance for order


Bolton Bailey (Apr 21 2024 at 05:57):

Do we have this?

import Mathlib

instance [Preorder a] : Max (a) := by sorry

Timo Carlin-Burns (Apr 21 2024 at 06:03):

What would the semantics be? What if I write max x y and x and y are incomparable? (e.g. x y : Set Nat, x := {3}, and y := {5})

Bolton Bailey (Apr 21 2024 at 06:05):

Well we have this function in the library

variable [Preorder α] [@DecidableRel α (· < ·)] {l : List α} {a m : α}

/-- `maximum l` returns a `WithBot α`, the largest element of `l` for nonempty lists, and `⊥` for
`[]`  -/
def maximum (l : List α) : WithBot α :=
  argmax id l

so I guess the same semantics as that, which I imagine is if a < b then b else a

Bolton Bailey (Apr 21 2024 at 06:06):

Seems like if we can define a function called maximum, we should be able to define Max

Bolton Bailey (Apr 21 2024 at 06:11):

Hmm, if I am reading you right, are you saying that this maximum function returns a singleton on [{3},{5}]? That's pretty nonintuitive.

Bolton Bailey (Apr 21 2024 at 06:19):

Yep, here it is

import Mathlib

open Classical

example : List.maximum (α := Multiset ) [{3}, {5}] = some {3} := by
  unfold List.maximum List.argmax List.argAux
  simp

Bolton Bailey (Apr 21 2024 at 06:20):

Frankly I am not a fan of Maximum returning a type not the same as the type of the list, I would prefer it require Bot and Max typeclass arguments and return the same type.

Timo Carlin-Burns (Apr 21 2024 at 06:26):

From a programming point of view, I think it's reasonable to be expected to handle the empty case separately. e.g. a signed 32-bit integer technically has a bottom element in -2,147,483,648, but if I'm taking the minimum of a list of them I probably want to error if it's empty, not produce that bottom element. If we want to draw off of other languages, Rust's Iterator::max returns Option which is the moral equivalent of WithBot here.

Timo Carlin-Burns (Apr 21 2024 at 06:27):

And it does seem like these functions should mostly be considered from a programming point of view. I think you would always prefer a function with infimum/supremum semantics for mathematics.

Bolton Bailey (Apr 21 2024 at 06:31):

Well I take your point, but it is mathlib, and I think we are generally oriented toward the more mathy way of doing things. If this were to be moved to Std or Core I would certainly agree it should return an option.

Bolton Bailey (Apr 21 2024 at 06:33):

Related is that I found this through Polynomial.degree_list_sum_le which looks like it would be cleaner if there was degree on both sides, which seems like it would be easier if there was a maximum function that preserved the type.

Bolton Bailey (Apr 21 2024 at 06:35):

I am not sure if there is a type common in programming where the above clear strangeness pops up. A bitmask type perhaps?

Yaël Dillies (Apr 21 2024 at 06:44):

Bolton Bailey said:

Hmm, if I am reading you right, are you saying that this maximum function returns a singleton on [{3},{5}]? That's pretty nonintuitive.

The use case comes from @Mantas Baksys. The point is that docs#List.maximum returns a maximum element for nonempty lists. So unless the list is a chain it doesn't return something that's greater than anything in the list.

Yaël Dillies (Apr 21 2024 at 06:45):

Bolton Bailey said:

Related is that I found this through Polynomial.degree_list_sum_le which looks like it would be cleaner if there was degree on both sides, which seems like it would be easier if there was a maximum function that preserved the type.

Aren't you looking for docs#Tropical by any chance?

Timo Carlin-Burns (Apr 21 2024 at 06:50):

Bolton Bailey said:

I am not sure if there is a type common in programming where the above clear strangeness pops up. A bitmask type perhaps?

So the question here is whether there is a type in programming for which comparison is common that is not a linear order? Floating point types are a classic example because NaN is incomparable to every element, including itself. So e.g.

import Mathlib

def Float.nan : Float := 0.0 / 0.0

instance : Preorder Float where
  le_refl := sorry
  le_trans := sorry
  lt_iff_le_not_le := sorry

#eval List.maximum [0.0, Float.nan] -- ↑0.000000
#eval List.maximum [Float.nan, 0.0] -- ↑NaN

Timo Carlin-Burns (Apr 21 2024 at 06:57):

Bolton Bailey said:

Related is that I found this through Polynomial.degree_list_sum_le which looks like it would be cleaner if there was degree on both sides, which seems like it would be easier if there was a maximum function that preserved the type.

I think this should be restated in terms of a List.supremum like so

def List.supremum {α : Type*} [SemilatticeSup α] [OrderBot α] : List α  α :=
  List.foldr (·  ·) 

theorem Polynomial.degree_list_sum_le' {S : Type u_1} [Semiring S] (l : List (Polynomial S)) :
  Polynomial.degree (List.sum l)  List.supremum (List.map Polynomial.degree l) := sorry

Timo Carlin-Burns (Apr 21 2024 at 07:09):

Hmm I see. If List.maximum was stated in terms of Max and Bot, then all we would need is an instance from SemilatticeSup to Max and it would just be a generalization of my List.supremum

Bolton Bailey (Apr 21 2024 at 16:09):

Yaël Dillies said:

Bolton Bailey said:

Related is that I found this through Polynomial.degree_list_sum_le which looks like it would be cleaner if there was degree on both sides, which seems like it would be easier if there was a maximum function that preserved the type.

Aren't you looking for docs#Tropical by any chance?

Yes I am finally trying to add the structure from this thread and adding an equivalence to lists.

Bolton Bailey (Apr 21 2024 at 16:23):

Timo Carlin-Burns said:

Bolton Bailey said:

Related is that I found this through Polynomial.degree_list_sum_le which looks like it would be cleaner if there was degree on both sides, which seems like it would be easier if there was a maximum function that preserved the type.

I think this should be restated in terms of a List.supremum like so

def List.supremum {α : Type*} [SemilatticeSup α] [OrderBot α] : List α  α :=
  List.foldr (·  ·) 

theorem Polynomial.degree_list_sum_le' {S : Type u_1} [Semiring S] (l : List (Polynomial S)) :
  Polynomial.degree (List.sum l)  List.supremum (List.map Polynomial.degree l) := sorry

Supremum seems like a good name. Is there any precedent for this in Finset? - it would be best to keep the names consistent if possible.

Yaël Dillies (Apr 21 2024 at 16:38):

docs#Finset.sup


Last updated: May 02 2025 at 03:31 UTC