Zulip Chat Archive

Stream: new members

Topic: How to prove that ⊂ list mapping results in ⊂ btwn lists


aron (Oct 14 2025 at 20:34):

I can't figure out how to prove this. That if you have list and newList where newList = list.map f and you know that f removes some specific item uv from the gather-projection of each item, and you know that there is at least one item whose projection contains uv, you know that the collected projection of newList will be a strict subset of that of list.

just can't figure out how to prove this. I end up tying myself into knots every time I try.

import Mathlib

theorem mapped_list_is_ssubset
  {list newList : List α} {gather : α  Finset β}
  [Union (Finset β)]

  (h : newList = list.map f)

  {uv : β}
  (hin : uv  gather item  gather (f item)  gather item)
  (hnin : uv  gather item  gather (f item) = gather item)

  (f_removes_uv : uv  gather (f item)) -- this is probably inferrable from the above two but just listing it for clarity

  (hexists :  a  list, uv  gather a)

  : newList.foldr (fun a s  gather a  s)   list.foldr (fun a s  gather a  s)  := by sorry

aron (Oct 14 2025 at 20:35):

I feel like there is probably also a far more elegant statement of this theorem but any attempts at such that I've tried have gotten me even further away from being able to prove it :frown:

Anthony Wang (Oct 14 2025 at 22:53):

Your theorem as stated right now is false. If list is the empty list, then you don't get a strict subset.

aron (Oct 14 2025 at 22:55):

That's why I've got (hexists : ∃ a ∈ list, uv ∈ gather a) so the list can't be empty

Anthony Wang (Oct 14 2025 at 22:56):

Oh sorry, I missed that line.

aron (Oct 14 2025 at 22:57):

No worries, there's a lot going on here :sweat_smile: it's not a very elegant theorem statement atm

Matt Diamond (Oct 14 2025 at 22:59):

seems like you're using .foldr to do a flat-map kind of operation... maybe it would be simpler to express it that way?

aron (Oct 14 2025 at 23:01):

yeah the foldr has been a pain to work with, but how do I go from a list of items to a single combined set of βs in one go with a flat-map? wouldn't a flat-map still just give you a list?

Matt Diamond (Oct 14 2025 at 23:02):

what if you converted to a finset first?

Matt Diamond (Oct 14 2025 at 23:03):

is there a reason you need to use a list to begin with?

Matt Diamond (Oct 14 2025 at 23:03):

perhaps the list / finset conversion is adding to the complexity

aron (Oct 14 2025 at 23:05):

hmm. well the original data is in a list. but no, there's no reason to map in the list and then gather the βs in a set as opposed to convert to a set of αs first and then flatmap directly to βs if that's what you're saying

Matt Diamond (Oct 14 2025 at 23:07):

yeah, like maybe it would be easier to convert first and then flat map

docs#Finset.sup_map might be useful

Anthony Wang (Oct 14 2025 at 23:10):

@aron How about something like this for the theorem statement, using Finset.biUnion instead of a foldr?

import Mathlib

theorem mapped_list_is_ssubset [DecidableEq β] {A B : Finset α} {gather : α  Finset β}
    [Union (Finset β)]
    (h : B = A.map f)
    {uv : β}
    (hin :  item, uv  gather item  gather (f item)  gather item)
    (hnin :  item, uv  gather item  gather (f item) = gather item)
    (hexists :  a  A, uv  gather a)
    : B.biUnion gather  A.biUnion gather := by

Matt Diamond (Oct 14 2025 at 23:12):

also I would get rid of [Union (Finset β)] (I know that was in the original post) and replace it with [DecidableEq β]

Matt Diamond (Oct 14 2025 at 23:13):

in fact I wonder if that was half the trouble... you're using an arbitrary definition of Union!

aron (Oct 14 2025 at 23:13):

Is biUnion the Finset version of a flatmap? I was looking for Finset.flatMap or Finset.bind but couldn't find anything – but the biUnion docs say it used to be called bind so that might be it?

aron (Oct 14 2025 at 23:14):

Matt Diamond said:

in fact I wonder if that was half the trouble... you're using an arbitrary definition of Union!

oh... I hope that wasn't it :melting_face:

Matt Diamond (Oct 14 2025 at 23:14):

Finset.bind exists since Finset has a Monad instance

aron (Oct 14 2025 at 23:15):

but I get this:

Invalid field `bind`: The environment does not contain `Finset.bind`
  newList
has type
  Finset α

aron (Oct 14 2025 at 23:16):

the biUnion doc comment says:

`Finset.biUnion s t` is the union of `t a` over `a  s`.

(This was formerly `bind` due to the monad structure on types with `DecidableEq`.)

Matt Diamond (Oct 14 2025 at 23:16):

sorry, I misspoke... I meant that you can call bind on a Finset

Matt Diamond (Oct 14 2025 at 23:17):

and you can use >>=

Matt Diamond (Oct 14 2025 at 23:18):

though the implementation is just Finset.sup as docs#Finset.bind_def shows

Matt Diamond (Oct 14 2025 at 23:23):

that's why I was suggesting docs#Finset.sup_map ... the goal being to prove that newList >>= gather is really just list >>= gather ∘ f

Anthony Wang (Oct 14 2025 at 23:24):

Here's a concise proof of the theorem statement with biUnion:

theorem mapped_list_is_ssubset [DecidableEq β] {list newlist : Finset α} {gather : α  Finset β}
    (h : newlist = list.map f)
    {uv : β}
    (hin :  item, uv  gather item  gather (f item)  gather item)
    (hnin :  item, uv  gather item  gather (f item) = gather item)
    (f_removes_uv :  item, uv  gather (f item))
    (hexists :  a  list, uv  gather a)
    : newlist.biUnion gather  list.biUnion gather :=
  Finset.ssubset_iff_of_subset (by grind) |>.mpr (by use uv; grind)

aron (Oct 14 2025 at 23:30):

oh wow what. thanks @Anthony Wang!

I doubt I'd have thought of that myself in a million years :sweat_smile: I'm very unfamiliar with the Finset library and I've found apply? and grind both pretty useless at pushing me forward

aron (Oct 14 2025 at 23:31):

ah but one problem – f seems to have type α ↪ α. It's not a regular function α → α

aron (Oct 14 2025 at 23:41):

looks like the difference is that f also needs to be injective

/-- `α ↪ β` is a bundled injective function. -/
structure Embedding (α : Sort*) (β : Sort*) where
  /-- An embedding as a function. Use coercion instead. -/
  toFun : α  β
  /-- An embedding is an injective function. Use `Function.Embedding.injective` instead. -/
  inj' : Injective toFun

From the definition of Finset.map:

/-- When `f` is an embedding of `α` in `β` and `s` is a finset in `α`, then `s.map f` is the image
finset in `β`. The embedding condition guarantees that there are no duplicates in the image. -/
def map (f : α  β) (s : Finset α) : Finset β :=
  s.1.map f, s.2.map f.2

Anthony Wang (Oct 14 2025 at 23:41):

Oh, you need Finset.image instead of Finset.map. The same proof works.

import Mathlib

theorem mapped_list_is_ssubset [DecidableEq α] [DecidableEq β] {list newlist : Finset α} {gather : α  Finset β}
    (h : newlist = list.image f)
    {uv : β}
    (hin :  item, uv  gather item  gather (f item)  gather item)
    (hnin :  item, uv  gather item  gather (f item) = gather item)
    (f_removes_uv :  item, uv  gather (f item))
    (hexists :  a  list, uv  gather a)
    : newlist.biUnion gather  list.biUnion gather :=
  Finset.ssubset_iff_of_subset (by grind) |>.mpr (by use uv; grind)

aron (Oct 14 2025 at 23:44):

ah ok nice! I was gonna say that I understand why Finset.map wants it to be injective, but for my use case I don't care if f a = f b even if a ≠ b. I'd just have multiple αs resulting in the same β, which is fine for me

aron (Oct 15 2025 at 16:58):

Follow-up question: how do I make this work with prop versions of the gather function? I.e. instead of it having type α → Finset β it has type α → Finset β → Prop representing a relation of input to output instead of an actual function?

For reference I initially had an inductive representing the operation of "gather all βs from a list of αs like:

inductive ListGathering {α β} [DecidableEq β] (gather : α  Finset β  Prop) : (list : List α)  (combined : Finset β)  Prop where
  | nil :
    ListGathering gather [] 

  | cons :
    gather head headItems 
    ListGathering gather tail tailItems 
    combined = headItems  tailItems 
    ListGathering gather (head :: tail) combined

but after the above conversation I think it has to be something more like:

def ListGathering' {α β} [DecidableEq α] [DecidableEq β] (gather : α  Finset β) : (list : List α)  (combined : Finset β)  Prop :=
  fun list combined 
    let finset := Finset.mk (Multiset.ofList list).dedup (List.nodup_dedup list)
    combined = finset.biUnion gather

which in addition to me not knowing how to adapt it to a prop-valued version of gather is also just far less elegant

aron (Oct 16 2025 at 11:06):

Ok I managed to figure it out!

The version of ListGathering that uses the function version of gather : α → Finset β is:

def ListGathering' {α β} [DecidableEq α] [DecidableEq β] (gather : α  Finset β)
  : (list : List α)  (combined : Finset β)  Prop :=
  fun list combined 
    combined = list.toFinset.biUnion gather

For the relation version of gather : α → Finset β → Prop I first need to introduce a helper prop ListMappingRel:

/-- Relation that maps between an input and output list given a relation prop -/
inductive ListMappingRel (p : α  β  Prop) : (list : List α)  (newList : List β)  Prop where
  | nil : ListMappingRel p [] []
  | cons :
    p head newHead 
    ListMappingRel p tail newTail 
    ListMappingRel p (head :: tail) (newHead :: newTail)

We can then define ListGathering'Rel like:

def ListGathering'Rel {α β} [DecidableEq α] [DecidableEq β]
  (gather_rel : α  Finset β  Prop)
  : (list : List α)  (combined : Finset β)  Prop :=
  fun list combined 
     listOfIds,
      ListMappingRel gather_rel list listOfIds 
      combined = ((listOfIds : Multiset (Finset β)) |>.map Finset.val |>.join |>.toFinset)

aron (Oct 16 2025 at 11:13):

Here's the combined #mwe containing 4 theorems for each version of the prop relating the presence or absence of a certain β in the original list with the presence or absence of that β in the combined Finset:

Full #mwe

aron (Oct 16 2025 at 11:19):

My next step is to adapt this mapped_list_is_ssubset theorem for use with my ListGathering'Rel prop :sparkles:

#new members > How to prove that ⊂ list mapping results in ⊂ btwn lists @ 💬


Last updated: Dec 20 2025 at 21:32 UTC