Zulip Chat Archive
Stream: mathlib4
Topic: Order dual tactic
Violeta Hernández (Dec 08 2024 at 09:34):
I would imagine this has been discussed before but: is there any progress on a tactic alike to_additive
that converts a theorem to its order-dual version?
Violeta Hernández (Dec 08 2024 at 09:37):
Something like:
import Mathlib.Order.SuccPred.Limit
open Order
@[to_order_dual] -- IsPredLimit a ↔ a ≠ ⊤ ∧ IsPredPrelimit a
theorem isSuccLimit_iff' [PartialOrder α] [OrderBot α] {a : α} :
IsSuccLimit a ↔ a ≠ ⊥ ∧ IsSuccPrelimit a :=
isSuccLimit_iff
Violeta Hernández (Dec 08 2024 at 09:41):
@Yaël Dillies
Yaël Dillies (Dec 08 2024 at 10:00):
No more than last time, at least as much as I'm aware of. @Calle Sönne was interested in the category theoretical version but has since been plagued with painful wrists like me
Eric Wieser (Dec 08 2024 at 12:19):
One complication here is that you would expect it to sometimes reorder arguments
Yaël Dillies (Dec 08 2024 at 15:04):
And sometimes you want it to generate lemmas for some
Violeta Hernández (Dec 08 2024 at 22:12):
I would imagine that even this basic version could already get rid of a lot of cruft throughout the library
Violeta Hernández (Dec 08 2024 at 22:13):
Other examples of order duals would include Sup
and Inf
, or IsMin
and IsMax
, or IsLUB
and IsGLB
, ...
Bryan Gin-ge Chen (Feb 11 2025 at 13:30):
I've started playing around with using the to_additive
infrastructure to make an order_dual
tactic in #21719. (#21696 is where I'm putting non-order_dual
-specific changes to to_additive
.) I'm a relative newcomer to metaprogramming, so I'd be happy to collaborate with anyone who's interested!
I'm currently stuck on a decidable instance which isn't getting translated:
application type mismatch
ite (b ≤ a)
argument has type
Decidable ((fun x1 x2 ↦ x1 ≤ x2) a b)
but function has type
[h : Decidable (b ≤ a)] → α → α → α
Damiano Testa (Feb 11 2025 at 15:00):
I look forward to this very much! In my experiments, the main issue was generating the "correct" ordering of the variable names in the statements, plus some twisted difficulty with associativity for statements involving more than 2 elements.
Bryan Gin-ge Chen (Feb 11 2025 at 15:06):
Ah, sounds like your experiments may have proceeded farther than mine... if you've got any notes or old code still lying around I'd love to take a look.
Damiano Testa (Feb 11 2025 at 15:16):
I think that #12528 was one of the latest experiments that I had. It is not at all polished!
Bryan Gin-ge Chen (Feb 12 2025 at 01:58):
@Damiano Testa I had a quick look at #12528 and it looks like you implemented some useful functions! I'm still working on figuring out the to_additive
machinery, but hopefully what you wrote can be plugged in somewhere...
Bryan Gin-ge Chen (Feb 12 2025 at 02:11):
As a general progress report, the very simple approach of just replacing the multiplicative->additive dictionaries with an order-dual dictionary and using to_additive
's reorder
to reverse the arguments of every ≤
in sight in #21719 is now stuck at LinearOrder.lean
and WithBot.lean
with different issues. I'll keep digging into what's going on so I can try to ask more focused questions later, but any experts with spare time are welcome to take a look and suggest / push fixes.
Damiano Testa (Feb 12 2025 at 08:49):
If I remember correctly, I did not hook into the to_additive
machinery, but made a simple syntactic translation, so I think that your approach probably complements nicely the one that I had started.
Damiano Testa (Feb 12 2025 at 08:50):
I am definitely interested in this automation and I have had it in the back of my mind for a while: I'll help, but also do remind me, in case I get distracted by other projects! :smile:
Bryan Gin-ge Chen (Feb 13 2025 at 03:40):
I made some progress on understanding what is broken in LinearOrder.lean
. It turns out the naïve approach of just swapping the arguments to ≤
is incompatible with the definition of max
that we're using. Compare:
min := fun a b => if a ≤ b then a else b
max := fun a b => if a ≤ b then b else a
If we just swap the arguments of ≤
, then min
would get translated to:
max := fun a b => if b ≤ a then b else a
(which is actually what mathlib3 used up until mathlib3#17470).
Now if we change the definition of max
back to this, then order_dual
seems to work quite well translating theorems about min and max... until we try to define a linear order on the natural numbers, where all hell breaks loose because max is defined in core with the first definition -- which is of course the reason that we had to land mathlib3#17470 in the first place.
It is possible to manually apply the attribute to any lemmas whose proofs unfold max
or min
to ≤
, so I'll try doing that next. I'm hoping we eventually get to a point where the max / min API gets sealed off and order_dual
can take over.
Mario Carneiro (Feb 14 2025 at 21:35):
or maybe core just has the wrong definition of max
Mario Carneiro (Feb 14 2025 at 21:36):
and possibly things have changed enough that we can actually have the backport go the other way around
Bryan Gin-ge Chen (Feb 14 2025 at 22:09):
I haven't made it that much further yet but so far the min
max
messiness seems to be contained to a handful of lemmas.
Mario Carneiro (Feb 14 2025 at 22:10):
I mean sure we can keep working around this but I think the only reason we backported at the time was because core is annoying to PR to and it still kinda is but less so
Mario Carneiro (Feb 14 2025 at 22:11):
I'm almost certain whoever wrote that definition of max did not think about this issue at all and just wrote the first ordering that came to mind
Bryan Gin-ge Chen (Feb 14 2025 at 22:15):
My first reaction was certainly the same: wow things would be much cleaner if we just switched the definition back. But as I tag more and more lemmas with order_dual
I'm less and less sure that I'm not just imposing my own arbitrary choices when I tell the tactic to re-order variables.
Bryan Gin-ge Chen (Feb 15 2025 at 04:59):
Today's progress update (all work can be found in #21719): As always, any feedback / questions here or on the PR is welcome!
I've now managed to tag most lemmas where the order_dual
applies through the start of Order.Basic
. Up to now I've been doing it all "by feel", so I think now is a good time to try and put the process into words, if just for my own benefit. So far most of the declarations order_dual
has been applied to are just simple statements about <
and ≤
, with a few that mention min
and max
; I did do some tests with WithBot
and WithTop
as well, but maybe I'll talk about those in a later post.
First, everything starts at the top of Order.Defs.PartialOrder, where I decided that order_dual
should transform a ≤ b
to b ≤ a
(and similarly a < b
should be transformed to b < a
, etc.):
attribute [order_dual existing (reorder := 3 4) LE.le] LE.le
attribute [order_dual existing (reorder := 3 4) LT.lt] LT.lt
attribute [order_dual existing (reorder := 3 4) GE.ge] GE.ge
attribute [order_dual existing (reorder := 3 4) GT.gt] GT.gt
Here you can see the use of to_additive
's reorder
option to swap the arguments of these functions.
Now that these have been registered, order_dual
can transform theorems or defs that mention any of these functions to their dual versions, where "dual" just means that the LHS and RHS of every ≤
, <
, etc. in sight have been swapped. Note that a theorem like le_rfl : a ≤ a
is manifestly self-dual so there's no reason to tag it with order_dual
as the default behavior is to do nothing.
So now suppose we have a declaration where order_dual
actually transforms it into something different. Consider the resulting dual declaration:
Case 1: Most of the time, the transformed version is just some slight variation of the original theorem that no one has needed up until now. For the time being, I'm manually naming these theorems as the original name + OD
as a suffix:
@[order_dual le_of_lt_or_eqOD] -- {a b : α} (h : b < a ∨ a = b) : b ≤ a
lemma le_of_lt_or_eq (h : a < b ∨ a = b) : a ≤ b := h.elim le_of_lt le_of_eq
@[order_dual le_of_eq_or_ltOD] -- {a b : α} (h : a = b ∨ b < a) : b ≤ a
lemma le_of_eq_or_lt (h : a = b ∨ a < b) : a ≤ b := h.elim le_of_eq le_of_lt
It's a bit unfortunate that this leads to a bunch of new trivial theorems, but we need to add these variants so that order_dual
will know how to transform the source theorems properly when they show up in proofs later on.
Case 2: Sometimes we get lucky and the transformed version of a theorem is secretly the same theorem except with some arguments permuted. In that case we just register the theorem as the dual version of itself with reorder
:
@[order_dual existing (reorder := 3 4) lt_iff_le_not_le]
-- note that swapping the LHS and RHS of < and ≤ below yields:
-- b < a ↔ b ≤ a ∧ ¬a ≤ b
-- which is also what we get if we swap a and b
lemma lt_iff_le_not_le : a < b ↔ a ≤ b ∧ ¬b ≤ a := Preorder.lt_iff_le_not_le _ _
Case 3: Sometimes we get even more lucky and the transformed theorem happens to be one that is proved later on in the file. This is the happy case where order_dual
will let us delete lines of code (though I'm just commenting things out for now):
@[order_dual (attr := trans) gt_of_gt_of_ge]
lemma lt_of_lt_of_le (hab : a < b) (hbc : b ≤ c) : a < c :=
lt_of_le_not_le (le_trans (le_of_lt hab) hbc) fun hca ↦ not_le_of_lt hab (le_trans hbc hca)
@[order_dual (attr := trans) gt_of_ge_of_gt]
lemma lt_of_le_of_lt (hab : a ≤ b) (hbc : b < c) : a < c :=
lt_of_le_not_le (le_trans hab (le_of_lt hbc)) fun hca ↦ not_le_of_lt hbc (le_trans hca hab)
-- @[trans] lemma gt_of_gt_of_ge (h₁ : a > b) (h₂ : b ≥ c) : a > c := lt_of_le_of_lt h₂ h₁
-- @[trans] lemma gt_of_ge_of_gt (h₁ : a ≥ b) (h₂ : b > c) : a > c := lt_of_lt_of_le h₂ h₁
One thing that I've been treating inconsistently is whether I should identify variations that use ge
and gt
as the same as the ones with le
and lt
. Also, case is still relatively rare so far, but I expect that the proportion will be higher for pairs of theorems about min
max
top
bot
, etc. rather than these theorems about le
and lt
.
I think we eventually want special order_dual self
syntax for order_dual
to verify case 2, since it's easy to screw up the reordering. Also, it would be nice to have some automation to search for case 3 theorems as well, since searching for defeq theorems later in the file with a different name than expected is a bit annoying. Previously I was also trying to use reorder
to try to match up the output of order_dual
with later declarations, but I kept making mistakes, so I ended up undoing all of those and using reorder
only when we get back the original declaration. Maybe there's some way to use automation here as well?
I haven't given much thought at all to how order_dual
should be changing the names of theorems like these at all - this might help with identifying case 3 declarations. Right now, order_dual
only knows to change min
to max
, bot
to top
, sup
to inf
, so theorems about lt
and le
just end up with the same name.
Violeta Hernández (Feb 15 2025 at 05:35):
(I don't mean to sidetrack you but my sincere thanks for this! It'll really come in handy in a lot of places)
Violeta Hernández (Feb 15 2025 at 05:39):
I think it's fine and even expected that lt
and le
should be kept the same. The only exception is when this would lead to the entire name being the same for two different theorems, which ime is where a lot of these ge
/gt
theorems come from.
Violeta Hernández (Feb 15 2025 at 05:41):
Are you planning to hit a certain tagging goal before PRing the tactic? I think it's important to have a working proof of concept before we get this into Mathlib, but tagging everything that could be tagged is complete overkill at this point.
Bryan Gin-ge Chen (Feb 15 2025 at 06:21):
Yeah, I’m not sure how far I should tag before cleaning up the tactic changes. I kind of want to see if I can get to lattices, but maybe it’s already useful enough?
Bryan Gin-ge Chen (Feb 15 2025 at 06:23):
(If anyone feels like pushing some commits tagging more lemmas and defs to my branch while I’m sleeping I certainly don’t mind!)
Mario Carneiro (Feb 15 2025 at 14:26):
Bryan Gin-ge Chen said:
Case 3: Sometimes we get even more lucky and the transformed theorem happens to be one that is proved later on in the file. This is the happy case where
order_dual
will let us delete lines of code (though I'm just commenting things out for now):@[order_dual (attr := trans) gt_of_gt_of_ge] lemma lt_of_lt_of_le (hab : a < b) (hbc : b ≤ c) : a < c := lt_of_le_not_le (le_trans (le_of_lt hab) hbc) fun hca ↦ not_le_of_lt hab (le_trans hbc hca) @[order_dual (attr := trans) gt_of_ge_of_gt] lemma lt_of_le_of_lt (hab : a ≤ b) (hbc : b < c) : a < c := lt_of_le_not_le (le_trans hab (le_of_lt hbc)) fun hca ↦ not_le_of_lt hbc (le_trans hca hab) -- @[trans] lemma gt_of_gt_of_ge (h₁ : a > b) (h₂ : b ≥ c) : a > c := lt_of_le_of_lt h₂ h₁ -- @[trans] lemma gt_of_ge_of_gt (h₁ : a ≥ b) (h₂ : b > c) : a > c := lt_of_lt_of_le h₂ h₁
By the way, I don't exactly consider this example a win, the order_dual
tactic produces a proof from scratch by "copy paste" while the original proof was much shorter because it's defeq to an existing lemma
Mario Carneiro (Feb 15 2025 at 14:29):
given the additional issue about gt
here I would be enclined to just tag the latter lemma rather than trying to translate it
Bryan Gin-ge Chen (Feb 15 2025 at 14:36):
Yeah, that makes sense! This is along the lines of something I mentioned at the end of my message, but one thing about tagging lemmas after the fact with existing
is that order_dual
no longer goes through the proof and checks that things translate properly, so it's easy to accidentally add a tag that's nonsense. What I've been doing is temporarily generating a new lemma and then checking with an example
that it is indeed defeq to the other declaration - it'd be nice if there was some kind of automated checking here (though I haven't thought through exactly what I want, I guess).
Mario Carneiro (Feb 15 2025 at 15:59):
Bryan Gin-ge Chen said:
but one thing about tagging lemmas after the fact with
existing
is thatorder_dual
no longer goes through the proof and checks that things translate properly, so it's easy to accidentally add a tag that's nonsense
that sounds fixable? order_dual
/ to_additive
should be able to validate these at attribute application time
Kim Morrison (Feb 16 2025 at 01:48):
Yes, we're open to change the definitions up in Lean, but I'd like to be presented with a "yes, it all works, including Mathlib, and people have signed off on the fact the change really makes tactic implementations easier" PR if possible. I don't have time to do this myself right now.
Bryan Gin-ge Chen (Mar 13 2025 at 20:34):
I finally found some time this week to come back to this and I've added the order_dual self
syntax that I mentioned earlier, so tagging self-dual lemmas is now a lot less verbose.
I also attempted to add some validation for order_dual self
and order_dual existing
to check that the type of the declaration generated by order_dual
(after variable reordering) is indeed defeq to the type of the original declaration itself (for self
) or the type of the provided declaration (for existing
), respectively. However, I'm getting mismatches where there just seems to be a difference in a universe variable:
order_dual failed validation!
tgt type:
∀ {α : Type u_2} [self : Preorder α] (a b c : α), a ≤ b → b ≤ c → a ≤ c,
transformed type:
∀ {α : Type u_1} [inst : Preorder α] (a b c : α), a ≤ b → b ≤ c → a ≤ c
The main validation code consists of these 8 lines or so, and I was already well outside my metaprogramming comfort zone when I wrote them, so there's probably some obvious mistake(s).
Before PRing, I also need to refactor things to separate out the parts of the code which are specific to to_additive
, so I would appreciate it anyone has any suggestions on how to do this cleanly; more generally, any comments about the current state of the meta code are very welcome!
Damiano Testa (Mar 13 2025 at 20:46):
I do not know how AttrM
works, but couldn't you try to combine the two MetaM.run
s into a single one?
Damiano Testa (Mar 13 2025 at 20:47):
I think that the universe level information may be stored in the MetaM
state somewhere and maybe the two runs do not keep track of the same assignments?
Bryan Gin-ge Chen (Mar 13 2025 at 21:03):
Thanks Damiano! However, I tried putting isDefEq
into the same function that generates the order dual type in the next commit and unfortunately I'm still getting the same errors.
Damiano Testa (Mar 13 2025 at 21:04):
Ok, so maybe "manually" creating fresh (universe) metavariables before unification might help?
Aaron Liu (Mar 13 2025 at 21:04):
How does to_additive
do it?
Damiano Testa (Mar 13 2025 at 21:05):
I'm not sure that to_additive
has this check.
Damiano Testa (Mar 13 2025 at 21:05):
In fact, today I was goofing around and you can actually place multiple copies of the to_additive
attribute on the same declaration and you get new "identical" declarations.
Damiano Testa (Mar 13 2025 at 21:06):
(You need to give them manually different names, though, since the environment catches the name clash otherwise.)
Aaron Liu (Mar 13 2025 at 21:08):
In that case I would instantiate one with universe variables and the other one with fresh universe metavariables, and do a defeq check on those. Then do it the other way around too to be safe.
Bryan Gin-ge Chen (Mar 13 2025 at 21:39):
OK, thanks! I guess the issue is the one that Kyle mentioned in Name
s not Expr
s. Is there an easy way to refresh universe metavariables in my use case?
For reference here's the current checking code (far from an MWE, see #21719):
def checkDeclType (b : BundledExtensions)
(tgt : Name) (srcDecl tgtDecl : ConstantInfo) (reorder : List (List Nat) := []) :
MetaM (Expr × Bool) := do
let mut decl := srcDecl.updateName tgt
if 0 ∈ reorder.flatten then
decl := decl.updateLevelParams decl.levelParams.swapFirstTwo
let exp ← applyReplacementFun b <| ← reorderForall reorder
<| ← expand b <| ← unfoldAuxLemmas decl.type
return ⟨exp, ← isDefEq exp tgtDecl.type⟩
and I guess I need to do more setup with exp
and tgtDecl.type
before passing them to isDefEq
.
edit: this was resolved in
Aaron Liu (Mar 13 2025 at 21:55):
You can use docs#Lean.ConstantInfo.instantiateTypeLevelParams
Damiano Testa (Mar 13 2025 at 21:57):
If I understand correctly, Bryan wants to do this for the "new" expr, the one that does not have a corresponding declaration.
Aaron Liu (Mar 13 2025 at 22:06):
You can docs#Lean.ConstantInfo.updateType an existing declaration.
Jovan Gerbscheid (Mar 14 2025 at 01:06):
You mentioned having trouble with the order on WithBot
and WithTop
. Note that there is some ongoing work on refactoring these definitions at #19668, by @Yaël Dillies.
Bryan Gin-ge Chen (Mar 14 2025 at 03:29):
For the time being I've just turned the validation error into a logInfo
so I can continue. Interestingly, I'm now seeing panics in later files coming from the reorderForall
function where sometimes the validation function is sending an expression with too few arguments. So there's probably something else wrong in my approach here...
Jovan Gerbscheid (Mar 14 2025 at 12:45):
In the reorderForall
function, you accidentally replaced mkForallFVars
by mkLambdaFVars
, which gives the mismatch.
Bryan Gin-ge Chen (Mar 14 2025 at 13:19):
Ah that was silly of me! Fixing that does get rid of most of the mismatches, but it looks like there are still two remaining coming from:
/-
reorderForall tried to reorder a list that was too small:
forall {α : Type.{u_2}} [self : LinearOrder.{u_2} α],
DecidableRel.{succ u_2, succ u_2} α α (fun (x1._@.Mathlib.Order.Defs.LinearOrder._hyg.636 : α)
(x2._@.Mathlib.Order.Defs.LinearOrder._hyg.636 : α) =>
LE.le.{u_2} α (Preorder.toLE.{u_2} α (PartialOrder.toPreorder.{u_2} α (LinearOrder.toPartialOrder.{u_2} α self))) x1._@.Mathlib.Order.Defs.LinearOrder._hyg.636 x2._@.Mathlib.Order.Defs.LinearOrder._hyg.636)
#[_uniq.1658, _uniq.1659]
[[2, 3]]
-/
attribute [order_dual self (reorder := 3 4)] LinearOrder.decidableLE
/-
reorderForall tried to reorder a list that was too small:
forall {α : Type.{u_2}} [self : LinearOrder.{u_2} α],
DecidableRel.{succ u_2, succ u_2} α α (fun (x1._@.Mathlib.Order.Defs.LinearOrder._hyg.661 : α)
(x2._@.Mathlib.Order.Defs.LinearOrder._hyg.661 : α) =>
LT.lt.{u_2} α (Preorder.toLT.{u_2} α (PartialOrder.toPreorder.{u_2} α (LinearOrder.toPartialOrder.{u_2} α self))) x1._@.Mathlib.Order.Defs.LinearOrder._hyg.661 x2._@.Mathlib.Order.Defs.LinearOrder._hyg.661)
#[_uniq.1691, _uniq.1692]
[[2, 3]]
-/
attribute [order_dual self (reorder := 3 4)] LinearOrder.decidableLT
in Order.Defs.LinearOrder
.
If I had to guess, probably the functions from to_additive
don't handle DecidableRel
well?
Jovan Gerbscheid (Mar 14 2025 at 13:40):
A priori DecidableRel
is not a forall. Only after unfolding it, does it become a forall (Luckily it is reducible, so in the reducible
transparency it is a forall). So the fix is probably to replace some ForallTelescope by ForallTelescopeReducing. (And making sure you are in the reducible
tranparency)
Jovan Gerbscheid (Mar 14 2025 at 13:52):
Although I'm not sure if reducing abbreviations is wanted in general. To avoid unnecessarily unfolding reducibles, you can use ForallBoundedTelescopeReducing (or something like that). This one only unfolds up to how many foralls you want to have.
Jovan Gerbscheid (Mar 14 2025 at 16:13):
I don't think I fully understand what the role/purpose is of reordering the arguments in LinearOrder.decidableLE
. Can you explain why they should be swapped?
Bryan Gin-ge Chen (Mar 14 2025 at 16:33):
I can't say I fully understand it either, but without the reordering it seems like the instance doesn't apply when we need it to. Looks like the first failure is (edit: let me look for the actual first failure...)lt_trichotomy
:
Jovan Gerbscheid (Mar 14 2025 at 16:38):
I looked at docs#Decidable.lt_or_eq_of_le, and I'm confused why this theorem exists, because it is weaker than docs#lt_or_eq_of_le. I thought mathlib was a classical mathematics library.
Damiano Testa (Mar 14 2025 at 16:39):
I think that it is a classical mathematical library outside the Decidable
namespace.
Ruben Van de Velde (Mar 14 2025 at 16:41):
This theorem dates back to the core lean 3 repository. I'm not sure anyone would mind if it disappeared
Bryan Gin-ge Chen (Mar 14 2025 at 16:49):
OK, if I remove Decidable
from the proof of lt_trichotomy
, then order_dual
(without reordering in LinearOrder.decidableLE
) is happy with it, however things blow up just a little bit further below:
/-
application type mismatch
dite (a ≤ b)
argument has type
Decidable ((fun x1 x2 ↦ x1 ≤ x2) b a)
but function has type
∀ [h : Decidable (a ≤ b)],
(a ≤ b → b < a ∨ a ≤ b) → (¬a ≤ b → b < a ∨ a ≤ b) → b < a ∨ a ≤ b
-/
@[order_dual lt_or_leOD]
lemma lt_or_le (a b : α) : a < b ∨ b ≤ a :=
if hba : b ≤ a then Or.inr hba else Or.inl <| lt_of_not_ge hba
Aaron Liu (Mar 14 2025 at 16:55):
Can you show this with pp.explicit
? The problem is in the invisible instance.
Bryan Gin-ge Chen (Mar 14 2025 at 16:56):
Sure, here you go!
application type mismatch
@dite
(Or (@LT.lt α (@Preorder.toLT α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) b a)
(@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b))
(@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b)
(@LinearOrder.decidableLE α inst✝ b a)
argument has type
Decidable
((fun x1 x2 ↦ @LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) x1 x2)
b a)
but function has type
∀
[h :
Decidable (@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b)],
(@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b →
Or (@LT.lt α (@Preorder.toLT α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) b a)
(@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b)) →
(Not (@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b) →
Or (@LT.lt α (@Preorder.toLT α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) b a)
(@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b)) →
Or (@LT.lt α (@Preorder.toLT α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) b a)
(@LE.le α (@Preorder.toLE α (@PartialOrder.toPreorder α (@LinearOrder.toPartialOrder α inst✝))) a b)
Yaël Dillies (Mar 14 2025 at 16:58):
It's the classic "(if a ≤ b then a else b) = (if b ≤ a then b else a)
is not defeq", no?
Aaron Liu (Mar 14 2025 at 17:03):
I think the easiest way to fix this would just be to reorder the arguments to LinearOrder.decidableLE
.
Bryan Gin-ge Chen (Mar 14 2025 at 17:04):
Right, the proof was working when attribute [order_dual self (reorder := 3 4)] LinearOrder.decidableLE
was not commented out, so (as in the short version of the error) I think it's just that the known decidability of ≤
doesn't apply to (fun x2 x1 ↦ x1 ≤ x2)
.
Aaron Liu (Mar 14 2025 at 17:17):
It looks like you need to change these lines to a docs#Lean.Meta.forallBoundedTelescope.
Bryan Gin-ge Chen (Mar 14 2025 at 17:26):
Thanks! Looks like that worked. Now it's just the issue with the universe names confusing isDefEq
.
Jovan Gerbscheid (Mar 14 2025 at 18:58):
In your implementation, you didn't pass a bound to forallBoundedTelescope
, which kind of defeats the purpose of using forallBoundedTelescope
over forallTelescopeReducing
. You can instead pass it the bound reorder.flatten.max?
(and if the Option
value is none, then don't telescope at all, since there is nothing to reorder)
Bryan Gin-ge Chen (Mar 14 2025 at 19:21):
Makes sense! I guess I'll do the same thing with lambdaBoundedTelescope
in reorderLambda
.
Jovan Gerbscheid (Mar 14 2025 at 19:29):
Oh and I said earlier to use the reducible
transparency, but I take that back, because now that we're using a bounded telescope, it will only reduce if explicitly told to do so. (Assuming you didn't modify the transparency yourself, it's set to default
by default, so you don't need to change it)
Bryan Gin-ge Chen (Mar 14 2025 at 19:35):
Right, I didn't change the transparency at all.
Here's what I have now (I had to add a +1
to the bound but it works):
/-- Reorder pi-binders. See doc of `reorderAttr` for the interpretation of the argument -/
def reorderForall (reorder : List (List Nat) := []) (src : Expr) : MetaM Expr := do
if let some maxReorder := reorder.flatten.max? then
forallBoundedTelescope src (some (maxReorder + 1)) fun xs e => do
if xs.size > maxReorder then
mkForallFVars (xs.permute! reorder) e
else
dbg_trace "reorderForall tried to reorder a list that was too small:\n\
{src}\n{xs}\n{reorder}"
return src
else
return src
/-- Reorder lambda-binders. See doc of `reorderAttr` for the interpretation of the argument -/
def reorderLambda (reorder : List (List Nat) := []) (src : Expr) : MetaM Expr := do
if let some maxReorder := reorder.flatten.max? then
lambdaBoundedTelescope src (maxReorder + 1) fun xs e => do
if xs.size > maxReorder then
mkLambdaFVars (xs.permute! reorder) e
else
dbg_trace "reorderLambda tried to reorder a list that was too small:\n\
{src}\n{xs}\n{reorder}"
return src
else
return src
Jovan Gerbscheid (Mar 14 2025 at 19:40):
I would write "if xs.size = maxReorder + 1" (although this shouldn't change the effect of the code)
Bryan Gin-ge Chen (Mar 14 2025 at 20:00):
Is there a reason to prefer that over xs.size > maxReorder
?
Jovan Gerbscheid (Mar 14 2025 at 20:02):
I think it makes it more readable, and it would throw an error if we accidentally passed the wrong bound to the bounded telescope
Jovan Gerbscheid (Apr 21 2025 at 13:52):
@Bryan Gin-ge Chen, I've had a look at the decidableLE
issue, and one solution I've found is to define decidableGE
, which is the dual of decidableLE
, as follows:
abbrev DecidableGT (α : Type*) [LT α] := DecidableRel (fun a b : α ↦ LT.lt b a)
abbrev DecidableGE (α : Type*) [LE α] := DecidableRel (fun a b : α ↦ LE.le b a)
attribute [order_dual existing DecidableGT] DecidableLT
attribute [order_dual existing DecidableGE] DecidableLE
However, I think a cleaner solution would be to refactor DecidableLE
to not be an abbrev
, but instead a structure. Then we can tag its projection function with order_dual
Bryan Gin-ge Chen (Apr 21 2025 at 13:59):
Thanks for taking a look! Right, I thought briefly about having a dual class, but it does indeed seem messy. DecidableLE
and friends are defined in core, right? I wonder if they had some specific reasons for making it an abbrev rather than a structure.
I also wonder if there's a way to use metaprogramming to have order_dual
reorder the arguments to instances appropriately when the type contains ≤
or something else tagged with the "reorder" attribute.
Jovan Gerbscheid (Apr 21 2025 at 14:06):
Bryan Gin-ge Chen said:
I also wonder if there's a way to use metaprogramming to have
order_dual
reorder the arguments to instances appropriately when the type contains≤
or something else tagged with the "reorder" attribute.
I presume you're talking about for example lt_or_eq_of_le
, where order_dual
gives the error
application type mismatch
@dite (b < a ∨ a = b) (a ≤ b) (inst✝ b a)
argument
inst✝ b a
has type
Decidable (b ≤ a) : Type
but is expected to have type
Decidable (a ≤ b) : Type
Although it is true that swapping a
and b
here solves the problem, I think this is hard to acomplish automatically. The same problem would be solved by either making decidableLE
a structure, or giving it a dual decidableGE
.
Jovan Gerbscheid (Apr 21 2025 at 14:11):
I think core only defined DecidableLE
quite recently. Before that it was just DecidableRel LE.le
(which would work better with order_dual
). Anyways I think there is no real downside to defining DecidableLE
as a structure.
Bryan Gin-ge Chen (Apr 21 2025 at 14:14):
Right, I see how making DecidableLE
a structure or giving it a dual will solve the issue, but I'd like to better understand why it'd be hard to deal with instances automatically.
It's certainly out of reach of my current metaprogramming abilities, but doesn't something like the following algorithm work? Whenever the proof that we're dualizing contains an instance term, first dualize its type and note all reordered arguments, then use that to swap the arguments to the instance?
Bryan Gin-ge Chen (Apr 21 2025 at 14:15):
In any case, let's ping @Kim Morrison to see whether making DecidableLE
a structure in core would be acceptable.
Jovan Gerbscheid (Apr 21 2025 at 14:21):
In the part where you say "first dualize its type", what exactly would decidableLE α
be dualized to? I think in your model it wouldn't be dualized. So then "note all reordered arguments" would have to look at something else than the usual reorder
Bryan Gin-ge Chen (Apr 21 2025 at 14:25):
Ah, OK I see now. I was imagining that we could see through the abbrev, but obviously that doesn't happen for free...
Aaron Liu (Apr 21 2025 at 14:36):
Why doesn't reorder
just work?
Jovan Gerbscheid (Apr 21 2025 at 14:45):
What @Bryan Gin-ge Chen wanted to do is reorder arguments a
and b
in inst b a
. But inst
is a local variable, so it cannot be globally tagged with reorder
.
Aaron Liu (Apr 21 2025 at 14:52):
Just reorder on DecidableLE
then, it's a @[reducible, inline] def
defined in Prelude
Aaron Liu (Apr 21 2025 at 14:52):
or does that not work for some reason
Aaron Liu (Apr 21 2025 at 14:55):
Do it the same way you reorder LE.le
Jovan Gerbscheid (Apr 21 2025 at 14:57):
Which arguments of DecidableLE
do you want to swap? It only takes 2 arguments: a type and an instance.
Aaron Liu (Apr 21 2025 at 14:57):
The third and fourth argument :)
Aaron Liu (Apr 21 2025 at 14:57):
wait, that doesn't work
Aaron Liu (Apr 21 2025 at 14:58):
hmmmmmm
Aaron Liu (Apr 21 2025 at 14:59):
I guess the best way to do this is to reorder inst
Aaron Liu (Apr 21 2025 at 14:59):
Since it's a local variable, it must have been introduced into the context at some point
Aaron Liu (Apr 21 2025 at 15:00):
Then you can just mark it as reorder when it's introduced
Aaron Liu (Apr 21 2025 at 15:00):
and that's based on the type
Aaron Liu (Apr 21 2025 at 15:02):
I guess this is the price you pay for having dependent types
Aaron Liu (Apr 21 2025 at 15:03):
Maybe this could be a new attribute? Something like type_reorder
Jovan Gerbscheid (Apr 21 2025 at 15:28):
I think this may cause some problems if you have both decidableLE
and decidableRel LE.le
. Because one of them gets dualized by swapping the arguments to the local instance, and the other by dualizing the type of the local instance. So we end up with two reducibly defEq expressions that get dualized to two non-defEq expressions. This could give rise to very obscure failures
Aaron Liu (Apr 21 2025 at 15:35):
oh, I can see how that could cause problems
Bryan Gin-ge Chen (Apr 21 2025 at 18:25):
Is there a library design constraint lurking here? Something more general than "don't use abbrevs to define classes if you want to be able to use order_dual
".
Jovan Gerbscheid (Apr 21 2025 at 18:29):
What sort of constraint do you mean? In my experience, using abbrev
s to define classes can lead to trouble, but it is convenient because it saves you from writing many instances.
I had a go at implementing DecidableLE
as a structure, but to be able to use that, I think we'd need to rewrite all type class instances such as e.g. instance (a b : Int16) : Decidable (a ≤ b)
into the form instance : DecidableLE Int16
, which is kind of annoying to do for all of Lean and Mathlib.
On the other hand, using the abbrev
DecidableGE
isn't actually that bad, because type class search can automatically infer DecidableLE
and DecidableGE
from eachother.
Bryan Gin-ge Chen (Apr 21 2025 at 20:05):
OK, I was a bit hesitant to add another class, but if DecidableGE
doesn't end up being too hard to work with then we can go with that. One minor downside is that theorems that use DecidableLE
can't be self-dual anymore, but I don't think there are that many theorems which use DecidableLE
anyways.
Aaron Liu (Apr 21 2025 at 20:07):
Can typeclass infer DecidableGE
from DecidableLE
?
Jovan Gerbscheid (Apr 21 2025 at 20:10):
Yes, because they unfold to ∀ a b, Decidable a ≤ b
, and ∀ a b, Decidable b ≤ a
respectively. And Lean introduces the ∀ variables as metavariables.
Jovan Gerbscheid (Apr 21 2025 at 22:39):
I made some progress using the approach with DecidableGE
and pushed my changes. Some comments:
- When I write
@[order_dual self (attr := simp)]
, does it apply thesimp
tag once or twice to the same lemma? I tried instead writing@[simp, order_dual self]
, but then the linter complains that it wants 'both' lemmas tagged withsimp
- I generally prefer to not change statements of lemmas in the process of using
order_dual
. For example I noticed you setge_trans
to be the dual ofle_trans
, but this means that it is now defined in terms of≤
instead of≥
. Instead, by reordering the arguments in the right way,le_trans
andge_trans
can both be self-dual. -
I know there was the idea to change the definition of
max
ormin
in core to make it a bit more convenient fororder_dual
. But I think there is actually a good reason for the specific definitions in core, which is written in a comment atmaxOfLe
andminOfLe
:
-- Marked inline so that `min x y + max x y` can be optimized to a single branch.
So I think the point is thatmin
andmax
have the same condition so that using both of them together generates efficient code. -
To make tagging easier, would it be sensible to have special support for
alias
es? If A is an alias of B, then the dual of A should just be the dual of B. - To make tagging easier, can there be a syntax (or lack of syntax) to indicate that the dual name should be the name followed by
OD
, if the name would otherwise be the same?
Bryan Gin-ge Chen (Apr 21 2025 at 23:12):
When I write
@[order_dual self (attr := simp)]
, does it apply thesimp
tag once or twice to the same lemma?
Good question. applyAttributes
and copyMetadata
don't check if src
and tgt
are the same; is applying attributes not idempotent?
The other bullet points / suggestions sound good to me other than the last one. The OD
suffix was basically me being lazy and putting off thinking about names, so I'm hesitant to make it a permanent default; I'm fine if we make it a temporary syntax / convention though (i.e. add a TODO in the comments).
Aaron Liu (Apr 21 2025 at 23:16):
I would suggest making better names, but if you're just testing and the names are temporary then I guess it's fine.
Jovan Gerbscheid (Apr 22 2025 at 06:40):
I think whether attribute application is idempotent depends on the implementation of that attribute, (i.e. does it use a HashSet or an Array)
Jovan Gerbscheid (Apr 22 2025 at 06:44):
For the names, the main case in which I didn't know how to do the naming is if the theorem contains both ≤ and =, like le_of_eq
. Because the order dual swaps the arguments in ≤ but not in =, so it's almost the same theorem again. Do we pick a keyword to append to the name? Like le_of_eq_swapped
?
Jovan Gerbscheid (Apr 22 2025 at 06:58):
Some more points:
- Attribute syntax: I noticed that the syntax of
order_dual
takes the arguments self, existing, (reorder := ..) and (attr := ..), in any order (and even multiple times). I think it would be nicer for consistency to pick one order and enforce that. I've been going fororder_dual (self/existing)? attr? reorder? name?
. - Syntax hovering: it would be nice if the attribute syntax had better hover information. This should include an example of the attribute syntax and the required order of arguments (from the above point), and also the hover information for the
attr :=
attributes. And more generally a basic explanation oforder_dual
. simp
Iff lemmas: for iff lemmas tagged withsimp
, lean uses some weird auxiliary lemma instead of just applying prop_ext every time. I ran into this issue yesterday and I could solve it by squeezing thesimp
, but that isn't a nice solution.
Yaël Dillies (Apr 22 2025 at 07:39):
Jovan Gerbscheid said:
For the names, the main case in which I didn't know how to do the naming is if the theorem contains both ≤ and =, like
le_of_eq
. Because the order dual swaps the arguments in ≤ but not in =, so it's almost the same theorem again. Do we pick a keyword to append to the name? Likele_of_eq_swapped
?
Naming convention is ge_of_eq
Jovan Gerbscheid (Apr 22 2025 at 07:41):
Do you mean that out of these three
le_of_eq : a = b → a ≤ b
le_of_eq' : a = b → b ≤ a
ge_of_eq : a = b → a ≥ b
the middle one should be called ge_of_eq
?
Jovan Gerbscheid (Apr 22 2025 at 08:33):
le_trans' : b ≤ c → a ≤ b → a ≤ c
ge_trans : a ≥ b → b ≥ c → a ≥ c
These two lemmas both exist, but your convention seems to imply le_trans'
should be called ge_trans
?
Edward van de Meent (Apr 22 2025 at 08:34):
actually, in my opinion that one should be le_comp
Edward van de Meent (Apr 22 2025 at 08:35):
since it's precisely the difference between comp
and trans
, applied to le
Jovan Gerbscheid (Apr 22 2025 at 08:36):
So would you suggest removing the current ge_trans
in favour of the current le_trans'
?
Edward van de Meent (Apr 22 2025 at 08:37):
i don't see why yet
Jovan Gerbscheid (Apr 22 2025 at 08:43):
I find it a bit confusing with the ≤
vs ≥
notation and le
vs ge
naming. For the notation, appart from a few exceptions, we always use ≤
. And I thought that this should be reflected in the name: always use le
for ≤
. But @Yaël Dillies is suggesting to use ge
in the name for ≤
if the lemma is generated by the order dual tactic. Don't you think that this would be very confusing?
Edward van de Meent (Apr 22 2025 at 08:48):
yes. i guess the issue is that in our typical naming scheme, we hardly track where which free variables end up, which is typically precisely the difference between le
and ge
Yaël Dillies (Apr 22 2025 at 08:50):
Jovan Gerbscheid said:
@Yaël Dillies is suggesting to use
ge
in the name for≤
if the lemma is generated by the order dual tactic. Don't you think that this would be very confusing?
No, this is not the naming convention. The naming convention is approximately
- Turn
foo_le_bar
intobar_le_foo
- If that fails (because eg
foo
=bar
or there is another ambiguity), instead replacele
withge
Jovan Gerbscheid (Apr 22 2025 at 08:51):
So far I'm mostly looking at lemmas where the two sides of le
are free variables, so point 1 doesn't apply.
Yaël Dillies (Apr 22 2025 at 08:52):
Then le
→ ge
it is. But I must warn you that point 1 really is the one that applies most often in practice
Yaël Dillies (Apr 22 2025 at 08:52):
(I know it makes the naming algorithm much harder, there's a reason why I failed to write to_dual
for several years!)
Edward van de Meent (Apr 22 2025 at 08:53):
i guess it might be best if we can reserve an option to explicitly name the dual lemma?
Jovan Gerbscheid (Apr 22 2025 at 08:54):
Oh right, should we call it to_dual
instead of order_dual
? It is really about any kind of dual.
Jovan Gerbscheid (Apr 22 2025 at 08:54):
Yes, we can choose to override the the automatically generated name for the dual lemma
Yaël Dillies (Apr 22 2025 at 08:55):
Yes, eg @Calle Sönne is hoping to steal all your work on this tactic and apply it to category theory :smile:
Jovan Gerbscheid (Apr 22 2025 at 11:58):
Ok, I'll make a PR to clean up some of these lemmas to use the gt
and ge
naming, but using <
and ≤
. But first I have #24277 to remove some @[trans]
annotations.
Bryan Gin-ge Chen (Apr 22 2025 at 13:05):
If we want to_dual
to dualize both order-theoretically and category-theoretically at the same time, then it might already be possible to start tagging theorems now with the current state of the attribute.
Jovan Gerbscheid (Apr 22 2025 at 13:37):
Some of the changes to the to_additive
infrastructure should probably be PR-ed before to_dual
itself. Your check that to_additive existing
does the correct thing is very useful, and will probably find some mistakes in mathlib. (In fact I found one by accident here #24283).
Additionally I think reorderForall
and reorderLambda
should throw an error if the permutation numbers are out of bounds.
Jovan Gerbscheid (Apr 25 2025 at 21:59):
@Yaël Dillies, I'm trying to replace lemmas about >
with lemmas about <
, but still calling them gt
when appropriate. Do you think the following lemmas should use ge
/gt
in the name?
lt_or_le (a b : α) : a < b ∨ b ≤ a
lt_or_lt_of_ne (h : a ≠ b) : a < b ∨ b < a
the naming conventions seem to suggest the names I wrote, but the names lt_or_ge
and lt_or_gt_of_ne
also seem natural.
Yaël Dillies (Apr 25 2025 at 22:02):
Both pairs of names are fine (and possibly we should have both)
Yaël Dillies (Apr 25 2025 at 22:02):
Jovan Gerbscheid (Apr 26 2025 at 01:56):
Do you also think Ne.lt_of_le' : b ≠ a → a ≤ b → a < b
should be renamed to Ne.gt_of_ge
?
Yaël Dillies (Apr 26 2025 at 06:50):
This one is a bit tougher in the sense that I think of it as having the !=
reversed, not the <=
and <
Jovan Gerbscheid (Apr 26 2025 at 12:12):
I have tagged a bunch of category theory results now with order_dual
. I ran into a few issues:
- Applying attributes in the case of
order_dual self
. I write@[simp, order_dual self (reorder := ..)]
, but the linter complains that it wants@[order_dual self (attr := simp) (reorder := ..)]
. So if we want the first form, then we should change the linter to complain about the second option instead of the first. - Higher order functions. A few functions require swapping arguments inside an argument in order to be
order_dual
to themself, such asPrefunctor.mk {V : Type u₁} [Quiver V] {W : Type u₂} [Quiver W] (obj : V → W) (map : {X Y : V} → (X ⟶ Y) → (obj X ⟶ obj Y)) : V ⥤q W
(need to swapX
andY
). I could imagine extending thereorder
syntax to support this, but that seems overly complicated, and otherwise we can just have such constants not be self-dual. IsThin
suffers the same problem asdecidableLE
, so we get the dualIsThin'
which is equivalent toIsThin
.- When tagging a structure projection function with
order_dual (reorder := ..)
, the reordering doesn't apply if the projection is in the form of anExpr.proj
. So I think we need to refold all of theExpr.proj
expressions into their projection functions before transforming the expression. This comes up because thereassoc
attribute creates such proof terms.
Jovan Gerbscheid (Apr 26 2025 at 12:18):
I noticed there is a special meaning when the number 0
appears in the reorder := ..
syntax, which swaps the order of the first two universe variables. I think this is very confusing, and it isn't used or documented anywhere. Should we remove it?
Jovan Gerbscheid (Apr 26 2025 at 12:19):
Have you considered using ==
instead of isDefEq
to check that the generated and actual order dual expressions are the same? I would expect that they should satisfy ==
.
Bryan Gin-ge Chen (Apr 26 2025 at 12:52):
Jovan Gerbscheid said:
I noticed there is a special meaning when the number
0
appears in thereorder := ..
syntax, which swaps the order of the first two universe variables. I think this is very confusing, and it isn't used or documented anywhere. Should we remove it?
I didn't realize this was unused. Maybe @Floris van Doorn has an opinion on this? (It seems the feature was added along with the reorder syntax in !3#7888)
Bryan Gin-ge Chen (Apr 26 2025 at 12:53):
Jovan Gerbscheid said:
Have you considered using
==
instead ofisDefEq
to check that the generated and actual order dual expressions are the same? I would expect that they should satisfy==
.
I didn't consider it at all -- do you think it'd be beneficial? I can test it out when I have a chance, but if you're currently hacking on order_dual
, maybe you're better poised to do so at the moment. I'd be interested to know the results.
Jovan Gerbscheid (Apr 26 2025 at 12:55):
I also renamed it to to_dual
just now :grinning:
Jovan Gerbscheid (Apr 26 2025 at 13:11):
Ah actually I got confused about the off-by-one property of reorder
. So what happens is that if reorder := ..
contains a 1, then it reorders the first two universe variables, which is required for HPow
-> HSMul
.
Jovan Gerbscheid (Apr 26 2025 at 13:12):
But I do think this could lead to surprising behaviour, as it is a hack.
Jovan Gerbscheid (Apr 26 2025 at 15:43):
I tried using ==
, which works fine. The change made it so that LinearOrder.toDecidableLE
can't be self dual, but that is fine, as we can define LinearOrder.toDecidableGE
. I also discovered the following weird universe thing in the process:
run_meta do -- (α : Type u) → [inst : LT α] → Type (max 0 u)
let info ← Lean.getConstInfo ``DecidableLT
Lean.logInfo m! "{info.type}"
Anyways, we do need a way to turn off the verification with syntax like self!
or existing!
(which I saw you suggested in a comment). The place where verification breaks is the constructor CategoryTheory.Iso.mk
, where we need to swap the fields hom_inv_id : hom ≫ inv = 𝟙 X := by aesop_cat
and inv_hom_id : inv ≫ hom = 𝟙 Y := by aesop_cat
, but the two by aesop_cat
autoParam
s aren't ==
to eachother (which is a bit unfortunate)
Jovan Gerbscheid (Apr 27 2025 at 11:47):
I made a PR (#24401) that introduces to_dual
's verification step into to_additive
, and I had to fix around 20 files where warnings were shown.
Jovan Gerbscheid (Apr 27 2025 at 11:50):
Also, my PR refactoring order lemmas is now at the new #24396, because in the previous PR it was getting unmanageable to fix all of the broken proofs. So instead I've added the old names as aliases, with a TODO to deprecate them.
Floris van Doorn (Apr 27 2025 at 23:32):
Bryan Gin-ge Chen said:
Jovan Gerbscheid said:
I noticed there is a special meaning when the number
0
appears in thereorder := ..
syntax, which swaps the order of the first two universe variables. I think this is very confusing, and it isn't used or documented anywhere. Should we remove it?I didn't realize this was unused. Maybe Floris van Doorn has an opinion on this? (It seems the feature was added along with the reorder syntax in !3#7888)
Oh, I thought I had documented that.
It is definitely used in to_additive
. The universes have to be reordered correctly as well, and as a hack, I only implemented the following case: if the first two arguments are reordered, to_additive
assumes that both of them are types, and then also reorders the first two universe levels. This is a very common scenario, and was necessary for many lemmas to_additive
generates with the reorder
option. Not reordering them would cause failures when declarations using the faulty one are additivized.
Of course, this is a hack. Feel free to implement a more robust solution.
Floris van Doorn (Apr 27 2025 at 23:35):
Oh, I now read further and see that Jovan also realized this.
Jovan Gerbscheid (Apr 28 2025 at 09:34):
(Note that the universes are swapped when doing any permutation involving the first argument, not just a swap with the second argument)
Jovan Gerbscheid (Apr 28 2025 at 16:31):
Should we use export LinearOrder (le_total min_def max_def)
instead of having two identical lemmas in the root and LinearOrder
namespaces? The duplication is a bit annoying for tagging.
Yaël Dillies (Apr 28 2025 at 18:21):
Jovan Gerbscheid said:
Should we use
export LinearOrder (le_total min_def max_def)
instead of having two identical lemmas in the root andLinearOrder
namespaces? The duplication is a bit annoying for tagging.
It's useful to not use export
because then the lemmas show up under the expected name in the docs (hopefully doc-gen can be fixed to avoid needing this hack)
Last updated: May 02 2025 at 03:31 UTC