Zulip Chat Archive
Stream: general
Topic: RFC: nat.card as a simp normal form
Yury G. Kudryashov (Nov 25 2022 at 06:16):
Hi, what do you think about the following change?
- We define
enat.card
and redefinenat.card
withoutcardinal
s, see here. - Optional: we prove
fin.equiv_iff_eq
in another way to avoid dependency onfinset
/fintype
. - We make
nat.card
, notfintype.card
, thesimp
-normal form.
Pros
- No non-Prop argument in
card
. - We can define
card
without theory aboutlist.perm
,finset
etc. - Don't duplicate API (currently we state many lemmas both for
fintype.card
andnat.card
).
Cons
- Loss of computability.
Kevin Buzzard (Nov 25 2022 at 06:36):
Some people would regard loss of computability as another pro!
Yury G. Kudryashov (Nov 25 2022 at 08:02):
I don't know if this can cause troubles for meta programming.
Yury G. Kudryashov (Nov 25 2022 at 08:18):
Here is a proof that works in data.fin.basic
, right after pred_inj
:
lemma nonempty_embedding_iff : nonempty (fin n ↪ fin m) ↔ n ≤ m :=
begin
refine ⟨λ h, _, λ h, ⟨(cast_le h).to_embedding⟩⟩,
induction n with n ihn generalizing m, { exact m.zero_le },
cases h with e,
rcases exists_eq_succ_of_ne_zero (pos_iff_nonempty.2 (nonempty.map e infer_instance)).ne'
with ⟨m, rfl⟩,
refine nat.succ_le_succ (ihn ⟨_⟩),
refine ⟨λ i, (e.set_value 0 0 i.succ).pred (e.set_value_ne i.succ_ne_zero _), λ i j h, _⟩,
simpa only [pred_inj, embedding_like.apply_eq_iff_eq, succ_inj] using h,
end
lemma equiv_iff_eq : nonempty (fin n ≃ fin m) ↔ n = m :=
⟨λ ⟨e⟩, le_antisymm (nonempty_embedding_iff.1 ⟨e⟩) (nonempty_embedding_iff.1 ⟨e.symm⟩),
λ h, h ▸ ⟨equiv.refl _⟩⟩
Yury G. Kudryashov (Nov 25 2022 at 08:23):
It relies on this addition to logic.embedding.basic
:
theorem set_value_ne {α β} (f : α ↪ β) {a a' : α} (h : a' ≠ a) (b : β) [∀ a', decidable (a' = a)]
[∀ a', decidable (f a' = b)] : set_value f a b a' ≠ b :=
((set_value f a b).injective.ne_iff' $ set_value_eq f a b).2 h
Kyle Miller (Nov 25 2022 at 09:28):
Yury G. Kudryashov said:
- We make
nat.card
, notfintype.card
, thesimp
-normal form.
I worry about this one -- sometimes one can use the fintype
instance to get a specific convenient finset
where it's easy to get the cardinality. That said, I think it should be fine if we leave the fintype.card ==> nat.card
lemma non-simp (that way it's opt-in).
I agree with preferring nat.card
over fintype.card
for whenever you don't need an actual fintype
instance.
Kyle Miller (Nov 25 2022 at 09:37):
It also makes sense for enat.card
and nat.card
to be defined only in terms of fin
and equiv
, at least for sake of removing dependencies for the basic notion of finite cardinalities. (Having #ₙ α
notation is a nice idea too.)
Yaël Dillies (Nov 25 2022 at 11:47):
I don't see much point in removing dependencies for the sake of removing dependencies. From what I understand, this will mostly end up duplicating API.
Yury G. Kudryashov (Nov 25 2022 at 17:18):
@Yaël Dillies We need to prove that the projection from cardinal
to enat
is a ring homomorphism. We can either prove it directly (by a choice analysis) or by using this trick.
Yury G. Kudryashov (Nov 25 2022 at 17:18):
So, there is not much code duplication here.
Yury G. Kudryashov (Nov 25 2022 at 17:19):
@Kyle Miller If we have a nice fintype
structure, then we should add a lemma about nat.card _ = _
that rewrites to fintype.card
.
Yury G. Kudryashov (Nov 25 2022 at 17:20):
But nat.card ((s : finset _) : set _)
should be simplified to finset.card s
.
Yury G. Kudryashov (Nov 25 2022 at 17:22):
So, we can completely remove fintype.card
(and use nat.card
instead) and use finset.univ.card
whenever you want a specific fintype
instance.
Kyle Miller (Nov 25 2022 at 17:28):
I wouldn't go as far as removing fintype.card
, since it can be useful for programming (though admittedly types with specific enumerations are more useful programming wise). My comment was mainly about just avoiding having a fintype.card ==> nat.card
simp lemma since that strips away potentially useful information (the specific fintype
instance in the term), and there's no need to normalize one to the other.
Yaël Dillies (Nov 25 2022 at 23:20):
Yury G. Kudryashov said:
So, we can completely remove
fintype.card
(and usenat.card
instead) and usefinset.univ.card
whenever you want a specificfintype
instance.
Unfortunately that would be much longer to type because Lean can't infer the type of finset.univ
. If anything, making fintype.card
an abbreviation
sounds like a much better way of achieving this specific goal.
Yury G. Kudryashov (Nov 26 2022 at 00:10):
Kyle Miller said:
I wouldn't go as far as removing
fintype.card
, since it can be useful for programming (though admittedly types with specific enumerations are more useful programming wise). My comment was mainly about just avoiding having afintype.card ==> nat.card
simp lemma since that strips away potentially useful information (the specificfintype
instance in the term), and there's no need to normalize one to the other.
If we don't normalize, then we need lemmas about both nat.card
and fintype.card
everywhere.
Junyan Xu (Nov 26 2022 at 02:59):
Simplifying fintype.card to nat.card is like simplifying docs#category_theory.groupoid.inv to docs#category_theory.inv (#16624, exactly for the purpose of reusing simp lemmas for category_theory.inv) but surely has a much larger impact. I guess I'm in favor of the change if you could make mathlib compile :)
Kyle Miller (Nov 26 2022 at 09:10):
@Yury G. Kudryashov I'm not sure what assumptions you're making to reach this conclusion.
I'll say my concern in a different way: one should be able to decide whether to simp with fintype.card X ==> nat.card X
or with fintype.card X = finset.univ.card
. This is a nonconfluence junction unless you turn all finset.card
into nat.card
as well. I doubt we want to do that, so it should be left to the user.
We don't need a rich set of simp lemmas for fintype.card
since if someone is using it for whatever reason, to prove things about it the first step should be to decide whether it's better to go via nat.card
or finset.card
.
Kyle Miller (Nov 26 2022 at 09:22):
Somewhat relatedly, I think it would be nice additionally having a set.ncard s := nat.card s
specifically to preserve that the argument is a set. I've run into a number of situations where there are well-meaning simp lemmas that convert fintype.card s
into something involving an equivalent subtype, which isn't good because the next steps would be rewriting types, one of those evils in type theory. Keeping things in the language of set
means congr and friends are more likely to work.
Yury G. Kudryashov (Nov 28 2022 at 03:14):
Rewriting types is not that bad if you don't have a [fintype s]
assumption nearby.
Peter Nelson (Mar 11 2023 at 19:03):
Kyle Miller said:
Somewhat relatedly, I think it would be nice additionally having a
set.ncard s := nat.card s
specifically to preserve that the argument is a set. I've run into a number of situations where there are well-meaning simp lemmas that convertfintype.card s
into something involving an equivalent subtype, which isn't good because the next steps would be rewriting types, one of those evils in type theory. Keeping things in the language ofset
means congr and friends are more likely to work.
Would there be support for a PR that adds this? That is, a new file defining set.ncard
that contains more or less the same lemmas as data.finset.card
, in noncomputable forms with each (s : finset a)
argument being replaced with (s : set a)
as well as [finite s]
where needed.
I'm doing stuff involving cardinality calculations for complicated sets where I want the full set
API as well as cardinality, and the instance mismatch issues associated with to_finset
are getting really annoying. This would be a solution. It's not even very much work.
Yury G. Kudryashov (Mar 13 2023 at 22:30):
Looks like a good idea to me. I postponed my refactor till after the porting to mathlib4 to avoid doing it twice but yours suggestion adds a leaf file instead of making a refactor.
Eric Wieser (Mar 13 2023 at 22:32):
Reiterating my comment from the PR (#18576) it seem a bit weird to add a shorthand for nat.card (coe_sort s)
, but then have the shorthand use finite (coe_sort s)
instead of the correspondings.finite
shorthand
Peter Nelson (Mar 14 2023 at 17:54):
Thanks for the comments!
At @Yury G. Kudryashov 's suggestion, I'm refactoring the PR to use (hs : s.finite)
instead of [finite s]
,
with auto_param
s to make things work smoothly in the case where s
is a set in a finite
type.
This is working ok, but it seems like it gives some annoying behaviour while writing proofs that
use the auto_param
lemmas. Here is what I mean.
import data.finite.card
variables {α : Type*} {s t : set α}
open set
/-- A tactic that finds a `t.finite` term for a set `t` in a `finite` type. -/
meta def to_finite_tac : tactic unit := `[apply set.to_finite]
/-- Cardinality -/
noncomputable def set.ncard (s : set α) := nat.card s
@[simp] lemma ncard_eq_zero (hs : s.finite . to_finite_tac) :
s.ncard = 0 ↔ s = ∅ :=
sorry
example (ht : t.finite) (hst : s ⊆ t) (hs : s.ncard = 0) : s = ∅ :=
begin
-- proving this lemma using the one above. I don't know that s is finite yet, but I'll
-- prove that in a minute; in the meantime I'll state the lemma I need.
have := ncard_eq_zero,
/- Won't tell me what the lemma actually says until I supply the parameter; instead
gives me a red underline and uninformative error message
tactic failed, there are unsolved goals
state:
α : Type u_1,
s t : set α,
ht : t.finite,
hst : s ⊆ t,
hs : s.ncard = 0
⊢ ?m_2.finite
This is fixed by appending an `@`, but then it is quite hard to read the lemma signature
because of all the `auto_param` boilerplate. -/
end
Is this just an inherent thing with auto_param
, or is there a good way to handle this?
Yury G. Kudryashov (Mar 15 2023 at 01:33):
I don't know.
Yury G. Kudryashov (Mar 15 2023 at 01:35):
In this case, you'll have to use ncard_eq_zero (ht.subset hst)
anyway.
Eric Wieser (Mar 15 2023 at 01:39):
have := λ h, ncard_eq_zero h
should work as you intend
Last updated: Dec 20 2023 at 11:08 UTC