Zulip Chat Archive

Stream: mathlib4

Topic: Relation properties duplication


Snir Broshi (Oct 14 2025 at 00:36):

Mathlib and Std combined have 3 versions of many properties of relations:

Std class Mathlib class Mathlib prop
reflexive relation docs#Std.Refl docs#IsRefl docs#Reflexive
irreflexive relation docs#Std.Irrefl docs#IsIrrefl docs#Irreflexive
symmetric relation docs#Std.Symm docs#IsSymm docs#Symmetric
antisymmetric relation docs#Std.Antisymm docs#IsAntisymm docs#AntiSymmetric
asymmetric relation docs#Std.Asymm docs#IsAsymm
total relation docs#Std.Total docs#IsTotal docs#Total
transitive relation docs#Trans (more generic) docs#IsTrans docs#Transitive
equivalence relation docs#Equivalence (structure) docs#IsEquiv

There are also classes for orders which were upstreamed to core because of the new grind, although the situation here is different because of bundling:

Std class using LE Mathlib class using LE Mathlib unbundled class
preorder docs#Std.IsPreorder docs#Preorder docs#IsPreorder
partial order docs#Std.IsPartialOrder docs#PartialOrder docs#IsPartialOrder
linear order docs#Std.IsLinearOrder docs#LinearOrder docs#IsLinearOrder

I also found docs#emptyRelation and docs#EmptyRelation.

Some relations have instances/theorems for all classes/props, some only for some:

docs#Eq: Equivalence ✓, IsEquiv
docs#Ne: Std.Irrefl x (docs#Ne.irrefl is close), IsIrrefl x, Irreflexive x, Std.Symm x (docs#Ne.symm is close), IsSymm x, Symmetric x
docs#Iff: Equivalence x, IsEquiv
docs#Relation.ReflGen: Std.Refl x, IsRefl ✓, Reflexive
docs#Relation.TransGen: Trans ✓, IsTrans ✓, Transitive
docs#Relation.ReflTransGen: Std.Refl x, IsRefl ✓, Reflexive ✓, Trans ✓, IsTrans ✓, Transitive
docs#Relation.EqvGen: Equivalence ✓, IsEquiv x

I think we should either fill the missing ones and add instances relating everything (already exist for transitive and the order ones) and theorems for the classes to imply the props (already exist for reflexive and transitive), or deprecate Mathlib's classes and props where possible, or at least document this table somewhere.

Thoughts? I assume this has been brought up before but I couldn't find it.

Kim Morrison (Oct 14 2025 at 04:53):

The order classes require more discussion (particularly LinearOrder, which is a mess.)

But personally I would be happy to see docs#IsAntisymm deprecated in favour of Std.Antisymm (and similarly for everything in the first table).

Markus Himmel (Oct 14 2025 at 05:28):

Snir Broshi said:

There are also classes for orders which were upstreamed to core because of the new grind,

Just noting that grind was not the primary motivation for adding these classes. Rather, they were added to support various future standard library developments like consolidating the lemmas for List.min? and adding verified sorting algorithms, binary search, and more. The fact that the classes are useful for grind was more of a happy accident.

Wrenna Robson (Oct 23 2025 at 14:33):

I'm extremely enthusiastic about tidying up this situation.

Ruben Van de Velde (Oct 23 2025 at 15:48):

I'm currently working on Total

Ruben Van de Velde (Oct 24 2025 at 14:50):

Which is now #30855

Ruben Van de Velde (Nov 05 2025 at 16:14):

If anyone has thoughts on deduplicating relation properties like IsTotal/Std.Total, please have a look at #30855.

Violeta Hernández (Dec 22 2025 at 14:55):

I've been wanting this unification for a very long time, thank you!

Snir Broshi (Dec 22 2025 at 19:17):

EmptyRelation #33204

Snir Broshi (Dec 26 2025 at 21:58):

IsSymm #33325

Snir Broshi (Jan 03 2026 at 04:07):

IsAsymm #33503

Snir Broshi (Jan 06 2026 at 22:36):

IsAntisymm #33685

Snir Broshi (Jan 07 2026 at 18:05):

IsIrrefl #33717

Snir Broshi (Jan 08 2026 at 15:25):

IsRefl #33755

Ruben Van de Velde (Jan 08 2026 at 15:31):

Please feel free to take over / redo #30855 if you have time.

Snir Broshi (Jan 08 2026 at 15:38):

Ruben Van de Velde said:

Please feel free to take over / redo #30855 if you have time.

Do you prefer I open another PR or push to yours w/ an invitation?

Ruben Van de Velde (Jan 08 2026 at 15:43):

Either way - I invited you

Snir Broshi (Jan 09 2026 at 13:59):

Ruben Van de Velde said:

Either way - I invited you

Wrong repo

Snir Broshi (Jan 09 2026 at 18:43):

IsTotal #33797

Snir Broshi (Jan 21 2026 at 21:43):

We're close to being done with the (non-order) classes, few last points:

Snir Broshi (Jan 21 2026 at 21:43):

1) docs#Equivalence is a structure and doesn't extend Std.Refl/Std.Symm/IsTrans, so I think we should leave docs#IsEquiv as-is, add an iff theorem between them, and perhaps RFC in core for a new Std.Equiv (though docs#IsPreorder vs docs#Std.IsPreorder might be a blocker)

Snir Broshi (Jan 21 2026 at 21:43):

2) Mathlib's docs#IsTrans vs core's docs#Trans: I suggest changing IsTrans to abbrev IsTrans α r := Trans r r r, but this creates universe problems:

import Mathlib.Tactic.TypeStar
/--
error: Type mismatch
  Trans r r r
has type
  Sort (max 1 u_1)
of sort `Type (max 1 u_1)` but is expected to have type
  Prop
of sort `Type`
-/
#guard_msgs in
abbrev IsTrans (α : Sort*) (r : α  α  Prop) : Prop := Trans r r r

Snir Broshi (Jan 21 2026 at 21:43):

3) lean4#10945 introduced docs#Std.Trichotomous which is ¬r a b → ¬r b a → a = b,
which seems to replace docs#IsTrichotomous but that is r a b ∨ a = b ∨ r b a.
They're of course logically equivalent, but deprecating one for the other seems nontrivial.
Which do you prefer? Should I go ahead and deprecate, or do we prefer mathlib's version and want to suggest it to core?

Snir Broshi (Jan 21 2026 at 21:43):

4) Can I proceed to deprecating the prop versions (e.g. docs#Total / docs#AntiSymmetric)? Their docstrings say e.g. "Std.Total as a definition, suitable for use in proofs", but the classes can also be used in proofs, I don't see a reason to keep them.

Snir Broshi (Jan 21 2026 at 21:46):

cc @Wrenna Robson for trichotomous

Wrenna Robson (Jan 22 2026 at 00:35):

We did deliberately choose the version we have there, essentially because it is then the natural counterpart of antisymm.

Wrenna Robson (Jan 22 2026 at 00:36):

As I recall Mathlib's version feels like a good idea but in practice it doesn't work amazingly.

Wrenna Robson (Jan 22 2026 at 00:37):

I think you probably just want to prove that as a theorem if you don't already have it.

Snir Broshi (Jan 22 2026 at 00:38):

So we should deprecate Mathlib's version, yes?

Wrenna Robson (Jan 22 2026 at 00:38):

Yes, I think so.

Wrenna Robson (Jan 22 2026 at 00:38):

I am sorry that that is nontrivial. I'm happy to help with review chores and things.

Snir Broshi (Jan 22 2026 at 00:41):

No worries, thanks!

Snir Broshi (Jan 24 2026 at 02:19):

IsTrichotomous #34349

Snir Broshi (Jan 24 2026 at 02:45):

Total #34351

Snir Broshi (Feb 10 2026 at 01:45):

AntiSymmetric #35025

Snir Broshi (Feb 12 2026 at 23:39):

Irreflexive #35191

Yaël Dillies (Feb 16 2026 at 17:17):

Are we sure we want to be using typeclasses as fields to structures? Now that #35191 is merged, the following code doesn't work:

import Mathlib.Combinatorics.SimpleGraph.Dart

/-- The graph obtained by contracting a dart in an existing graph. -/
def contractDart {V : Type*} (G : SimpleGraph V) (e : G.Dart) :
    SimpleGraph {x : V // x  e.snd} where
  Adj x y :=
      G.Adj x.val y.val
     x.val = e.fst  y.val  e.fst  G.Adj e.snd y.val
     y.val = e.fst  x.val  e.fst  G.Adj x.val e.snd
  symm := sorry
  loopless
    | x, .inl x_adj_x => G.loopless x.val x_adj_x
    | x, .inr <| .inl x_problem => by tauto
    | x, .inr <| .inr x_problem => by tauto

and one needs to instead do

/-- The graph obtained by contracting a dart in an existing graph. -/
def contractDart {V : Type*} (G : SimpleGraph V) (e : G.Dart) :
    SimpleGraph {x : V // x ≠ e.snd} where
  Adj x y :=
      G.Adj x.val y.val
    ∨ x.val = e.fst ∧ y.val ≠ e.fst ∧ G.Adj e.snd y.val
    ∨ y.val = e.fst ∧ x.val ≠ e.fst ∧ G.Adj x.val e.snd
  symm := sorry
  loopless := by
    constructor
    exact fun
    | x, .inl x_adj_x => G.loopless.1 x.val x_adj_x
    | x, .inr <| .inl x_problem => by tauto
    | x, .inr <| .inr x_problem => by tauto

Snir Broshi (Feb 16 2026 at 17:49):

Relevant

Snir Broshi (Feb 16 2026 at 17:52):

FWIW I think the by constructor; exact fun construction isn't so bad if in return we get better interop with core's relation properties and possibly typeclass inference for properties where possible

Anne Baanen (Feb 17 2026 at 09:49):

(I wouldn't be too hopeful for typeclass inference to do too much here, it seems like aesop_graph autoparams is the way to go.)

Anne Baanen (Feb 17 2026 at 09:54):

But yes, I think this extra abstraction barrier is annoying but is less bad than unbundling. Especially since choosing a consistent spelling makes it easier to copy fields between different structures.

Note that where notation also works as:

import Mathlib.Combinatorics.SimpleGraph.Dart

/-- The graph obtained by contracting a dart in an existing graph. -/
def contractDart {V : Type*} (G : SimpleGraph V) (e : G.Dart) :
    SimpleGraph {x : V // x  e.snd} where
  Adj x y :=
      G.Adj x.val y.val
     x.val = e.fst  y.val  e.fst  G.Adj e.snd y.val
     y.val = e.fst  x.val  e.fst  G.Adj x.val e.snd
  symm := sorry
  loopless.irrefl -- <- changed here
    | x, .inl x_adj_x => G.loopless.irrefl x.val x_adj_x
    | x, .inr <| .inl x_problem => by tauto
    | x, .inr <| .inr x_problem => by tauto

Yaël Dillies (Feb 19 2026 at 19:07):

Ah yes, I knew I was missing something. Annoying that you must go one deeper, but it's workable with

Snir Broshi (Feb 21 2026 at 04:57):

Transitive #35592

Jovan Gerbscheid (Feb 21 2026 at 08:18):

About transivity, we currently have IsTrans (in mathlib) and Trans (from core), and they are separate type classes with a loop like this:

/-- `IsTrans X r` means the binary relation `r` on `X` is transitive. -/
class IsTrans (α : Sort*) (r : α  α  Prop) : Prop where
  trans :  a b c, r a b  r b c  r a c

instance {α : Sort*} {r : α  α  Prop} [IsTrans α r] : Trans r r r :=
  IsTrans.trans _ _ _⟩

instance (priority := 100) {α : Sort*} {r : α  α  Prop} [Trans r r r] : IsTrans α r :=
  fun _ _ _ => Trans.trans

Wouldn't it make more sense to avoid the loop and simply make it an abbrev?

abbrev IsTrans (α : Sort*) (r : α  α  Prop) : Prop := Trans r r r

I suppose this would be a breaking change because the argument implicitness in the trans field is different...

Violeta Hernández (Feb 21 2026 at 10:24):

I've always thought of Trans as an implementation detail of the trans tactic, I don't think you're really supposed to use it in mathematical code.

Wrenna Robson (Feb 21 2026 at 10:25):

Unfortunately I think we do

Jovan Gerbscheid (Feb 21 2026 at 10:27):

The trans tactic is in batteries so Trans wasn't made for the trans. Trans is part of the implementation of calc though.

Jovan Gerbscheid (Feb 21 2026 at 10:59):

Another case of duplicated relation properties is IsWellFounded and WellFounded, which are the exact same thing, except one is a class and the other isn't. Why don't we replace

/-- A well-founded relation. Not to be confused with `IsWellOrder`. -/
@[mk_iff] class IsWellFounded (α : Type u) (r : α  α  Prop) : Prop where
  /-- The relation is `WellFounded`, as a proposition. -/
  wf : WellFounded r

with

attribute [class] WellFounded

We can still have both APIs for using it as a class or passing it around explicitly.

Snir Broshi (Feb 21 2026 at 15:10):

Jovan Gerbscheid said:

Wouldn't it make more sense to avoid the loop and simply make it an abbrev?

abbrev IsTrans (α : Sort*) (r : α  α  Prop) : Prop := Trans r r r

I agree, but see #mathlib4 > Relation properties duplication @ 💬 above, this doesn't work.
I don't know why Trans has max 1 in the universes though. It allows the relations to produce Sort* rather than Prop.

Snir Broshi (Feb 21 2026 at 15:15):

I was planning on RFCing a new Std.Trans to core to half fix this, so that they would solve the duplication

Aaron Liu (Feb 21 2026 at 15:30):

Snir Broshi said:

I don't know why Trans has max 1 in the universes though. It allows the relations to produce Sort* rather than Prop.

I just wanted to comment that, the max 1 is technically unnecessary and you can get rid of it but it's a total pain and breaks a lot of automation


Last updated: Feb 28 2026 at 14:05 UTC