Zulip Chat Archive
Stream: general
Topic: finset coercion strangeness
Peter Nelson (Aug 31 2020 at 22:59):
I am trying to define a coercion of finsets of a subtype to finsets of a type, by just taking the image of the subtype-type coercion. The instance below seems to define this coercion correctly, but when I try to give some simple lemmas, I get strange behaviour:
instance coe_subtype_subset {α : Type*}{Y: set α} :
has_coe (finset ↥Y) (finset α) := ⟨λ X, X.image subtype.val⟩
lemma coe_subtype_union {α : Type*}{Y: set α}(X₁ X₂ : finset ↥Y) :
((X₁ ∪ X₂) : finset α) = ((X₁ : finset α) ∪ (X₂ : finset α)) :=
begin
exact rfl,
end
The fact that exact rfl
works as a proof for the lemma seems odd to me, but what is odder is that when I invoke the lemma at some X₁ X₂
later, I get the trivial hypothesis this: ↑X₁ ∪ ↑X₂ = ↑X₁ ∪ ↑X₂
rather than the intended this: ↑(X₁ ∪ X₂) = ↑X₁ ∪ ↑X₂
. I don't see another way to state that my coercion commutes with union - what am I doing wrong?
Ruben Van de Velde (Sep 01 2020 at 08:20):
I think this is because Lean does "outside-in elaboration" (?): it sees ((X₁ ∪ X₂) : finset α)
, realizes that you want X₁ ∪ X₂
to be a finset α
, and derives that you want ∪
to be a function ? → ? → finset α
, and the only ∪
that matches is finset α → finset α → finset α´, so it coerces
X₁ and
X₂ to
finset α` before taking the union.
I haven't tried, but maybe (((X₁ ∪ X₂) : finset ↥Y) : finset α)
on the LHS will yield better results
Heather Macbeth (Sep 01 2020 at 12:48):
I also haven't tried it, but assuming the problem is indeed the outside-in elaboration, here's a previous thread where I hit the same problem and was offered some solutions:
https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.60has_inv.60.20in.20rings
Kevin Buzzard (Sep 01 2020 at 14:39):
Ruben Van de Velde said:
I think this is because Lean does "outside-in elaboration" (?): it sees
((X₁ ∪ X₂) : finset α)
, realizes that you wantX₁ ∪ X₂
to be afinset α
, and derives that you want∪
to be a function? → ? → finset α
, and the only∪
that matches isfinset α → finset α → finset α´, so it coerces
X₁and
X₂to
finset α` before taking the union.I haven't tried, but maybe
(((X₁ ∪ X₂) : finset ↥Y) : finset α)
on the LHS will yield better results
and then you shouldn't need any type annotations on the RHS.
Peter Nelson (Sep 01 2020 at 18:36):
Thank you, that did the trick - however, I'm getting some more strange behaviour. I am running into a problem when applying the lemma within a structure. The below is a minimal problematic example isomorphic to my own:
Essentially, when I invoke the union lemma above, I can get into a situation where a seemingly tautological goal state involving the equality of two coerced finsets can't be proved by rw/refl/simp/trivial. I have tried extensive unfolding to try to find how the two sides of the equality are actually different, to no avail. The only suggestion I can find that they differ is with --set_option pp.all true
, but the difference is so deeply buried in abstraction that I can't make any sense of it.
In this example, apply image_union
does solve my problem, but I am running into similar issues with more complicated examples that cause linarith
to break, so I would really like to understand the issue.
import data.finset
noncomputable theory
open_locale classical
open finset
universe u
-- Coercion stuff
instance coe_subtype_finset {α : Type*}{Y: set α} :
has_coe (finset Y) (finset α) := ⟨λ X, X.image subtype.val⟩
lemma union_lemma {α : Type*}{Y: set α}(S T : finset Y):
(((S ∪ T): finset Y): finset α) = S ∪ T := by apply image_union
-- Structure
structure subadditive_fn_on (γ : Type u) :=
(f : finset γ → ℕ )
(hf : ∀ (S T : finset γ), f (S ∪ T) ≤ f S + f T)
-- restrict the subadditive function to a subset Y of the domain
def restr {γ : Type u}(F : subadditive_fn_on γ)(Y: set γ) : subadditive_fn_on (↥Y):=
{
f := λ S, F.f (S: finset γ),
hf := λ S T,
begin
convert F.hf S T,
-- we now have S T: finset ↥Y
-- goal state is ⊢ ↑(S ∪ T) = ↑S ∪ ↑T
-- apply image_union, WORKS
rw ← union_lemma,
-- goal state is now ⊢ ↑(S ∪ T) = ↑(S ∪ T)
refl, -- Doesn't work...
end
}
Reid Barton (Sep 01 2020 at 18:44):
This is a decidable_eq
instance mismatch issue
Reid Barton (Sep 01 2020 at 18:44):
convert rfl
can take care of it
Peter Nelson (Sep 01 2020 at 18:51):
Thank you!
What would be the best way to, say, handle an invocation of linarith involving multiple expressions that are 'equal' in this way? Do I have to explicitly insert hypotheses that assert the equality of all relevant pairs (how do I even do this when they are syntatically identical?), or is there a way to equip linarith with the ability to see such equalities automatically?
Floris van Doorn (Sep 01 2020 at 19:04):
convert rfl
does basically the same as congr'
, right?
Mario Carneiro (Sep 01 2020 at 19:05):
pretty much
Peter Nelson (Sep 01 2020 at 19:06):
Is there a way to stop the mismatch from happening in the first place?
Floris van Doorn (Sep 01 2020 at 19:10):
In this case: union_lemma
states something about the union of two finsets, which is only defined for finsets over a decidable type. If you add [decidable_eq \a]
(or maybe {h : decidable_eq \a}
) as an argument to union_lemma
, I expect that the issue will resolve.
Floris van Doorn (Sep 01 2020 at 19:14):
In this case, adding [hY : decidable_eq Y]
to union_lemma
works, but I think that might result in problems in other examples.
Peter Nelson (Sep 02 2020 at 12:24):
Is there a good general solution to this? By this I mean one where [decidable_eq Y]
is not being thrown indiscriminately into various lemma statements, and yet where apparently equal expressions are actually equal in a way that doesn't need to be explicitly nailed down and hit with congr
each time. All I can think of is to bundle decidable_eq
with finset
in its own type, and essentially rewrite the finset
API with that. All this will be occuring with multiple types, so a variable
declaration isn't a solution.
Peter Nelson (Sep 03 2020 at 01:46):
A more specific question - is there a way to make sure that all instances of decidable_eq
come from open_locale classical
rather than from elsewhere?
Mario Carneiro (Sep 03 2020 at 02:04):
Unfortunately this won't help, because there are theorems that contain partial dec_eq proofs in them that are not the classical one
Mario Carneiro (Sep 03 2020 at 02:05):
for example "finset A has dec_eq if A does"
Mario Carneiro (Sep 03 2020 at 02:05):
which will cause diamond problems along the lines finset.dec_eq (classical.dec A) != classical.dec (finset A)
Mario Carneiro (Sep 03 2020 at 02:08):
@Pim Spelier Take note, I think this is an example that helps show why we made polynomials classical
Mario Carneiro (Sep 03 2020 at 02:10):
(referencing https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/Noncomputable.20polynomials/near/208745260 )
Peter Nelson (Sep 03 2020 at 02:31):
For what I'm doing, (I think) I don't care about decidable_eq in any sense explicitly - it just apparently crops up implicitly when I want to describe the union of finsets. Does this mean there is no solution to my problems that doesn't involve keeping track of instances very carefully?
Bryan Gin-ge Chen (Jan 06 2021 at 02:54):
I think the issue that prompted @Yakov Pechersky to open #5622 is another example of pain arising from decidable_eq
+ classical
. Are there some general recommendations here? Some linter, or something to add to a library note?
Yakov Pechersky (Jan 06 2021 at 02:56):
(deleted)
Yury G. Kudryashov (Jan 06 2021 at 05:03):
In my experience, the best we can do in API lemmas is to assume decidability of all propositions used both in LHS and RHS. In particular, do not assume [decidable_eq α]
if we actually need [decidable_pred (s : finset α)]
Yury G. Kudryashov (Jan 06 2021 at 05:04):
This way lemmas will work both in classical and non-classical settings.
Yakov Pechersky (Jan 06 2021 at 05:13):
The issue with equiv.perm.sign
in the PR was that the definition of sign
is explicit in requiring [decidable_eq α]
but some lemmas provided it just by open_locale classical
instead of having it as a constraint in the arguments.
Bryan Gin-ge Chen (Jan 06 2021 at 05:14):
@Yury G. Kudryashov Can you give an example where [decidable_pred (s : finset α)]
is needed? Is there a typo or is there a coercion from finsets to predicates that I'm missing?
Yury G. Kudryashov (Jan 06 2021 at 05:17):
This is a typo. I meant [∀ x, decidable (x ∈ s)]
Yury G. Kudryashov (Jan 06 2021 at 05:18):
I don't remember if we have definitions that take [∀ x, decidable (x ∈ s)]
as an argument. If not, then my example is irrelevant.
Yury G. Kudryashov (Jan 06 2021 at 05:19):
Yes, we have: docs#finset.piecewise
Yury G. Kudryashov (Jan 06 2021 at 05:20):
E.g., docs#finset.piecewise_coe takes two [decidable]
arguments instead of deducing one of them from the other.
Yury G. Kudryashov (Jan 06 2021 at 05:21):
And docs#finset.piecewise_insert takes all [decidable]
arguments it needs to use.
Yury G. Kudryashov (Jan 06 2021 at 05:22):
Possibly, we should require that the type of a lemma (not a proof term) involves no non-trivial [decidable]
instances.
Yury G. Kudryashov (Jan 06 2021 at 05:24):
In particular, if we need both [decidable_eq α]
and [decidable_eq (option α)]
to state the lemma, then both should be present as arguments.
Bryan Gin-ge Chen (Jan 06 2021 at 05:32):
Concretely, that means some lemmas in src/group_theory/perm/option.lean
in #5593 should take [decidable_eq (option α)]
as well?
Yakov Pechersky (Jan 06 2021 at 05:34):
I think just on α
is fine, because:
import group_theory.perm.sign
import data.equiv.option
variables {α : Type*} [decidable_eq α]
#check (show decidable_eq (option α), by apply_instance)
/-
show decidable_eq (option α), from λ (a b : option α), option.decidable_eq a b : decidable_eq (option α)
-/
Eric Wieser (Jan 06 2021 at 09:29):
Can we have a linter that verifies lemma statements do not use classical.decideable?
Mario Carneiro (Jan 06 2021 at 09:54):
I don't think we should necessarily do something about all such examples
Last updated: Dec 20 2023 at 11:08 UTC