Zulip Chat Archive

Stream: new members

Topic: need for 'explicit' type conversion using rw


Jasper Ganten (Jun 24 2025 at 11:45):

Hello, I am tinkering around with PMFs and proving my definition of binomial is the PMF.binomial, I came across this problem:

import Mathlib

noncomputable def binom_do_rec (p : ENNReal) (h : p  1) : (n : )  PMF (Fin (n+1))
  | 0 => PMF.pure 0
  | n+1 => do
    let Head  PMF.bernoulli p h
    let X  binom_do_rec p h n
    if Head then
      return X+1
    else
      return X

theorem my_binom_is_binom (p : ENNReal) (h : p  1) : binom_do_rec p h = PMF.binomial p h := by
  ext n S
  refine DFunLike.congr ?_ rfl
  induction n with
  | zero =>
    dsimp [binom_do_rec]
    ext x
    simp
    rw [Fin.fin_one_eq_zero x, if_pos rfl]
    rw [PMF.binomial_apply_self p h] -- does not find instance of (PMF.binomial p h ?n) ↑?n
    rw [ pow_zero]
  | succ m hm => sorry

The proof state is 1 = (PMF.binomial p h 0) 0. I got that the casting of the 0 is not trivial and AI pointed me to the workaround:

    have : (0 : Fin 1) = (0 : ) := by simp
    rw [this]

before the rewrite in question works but I still wonder why the casting is not done by lean? Are there stronger tactics that manage this (simp only did not work in my case). How can I figure out if the problem is lean not casting/converting or maybe just not the correct theorem in the general case?
Many thanks!

Jasper Ganten (Jun 24 2025 at 11:47):

note: the beginning also feels kind of sketchy - happy for any suggestion :)

Kenny Lau (Jun 24 2025 at 11:55):

why are you using do in definitions...

Kenny Lau (Jun 24 2025 at 11:56):

Also I would recommend that you define it directly rather than recursively.

Kenny Lau (Jun 24 2025 at 11:56):

I see, you also referenced the already-defined PMF.binomial.

Kenny Lau (Jun 24 2025 at 11:57):

you can refer to how the source code defines it directly, as I predicted

Kenny Lau (Jun 24 2025 at 12:05):

@Jasper Ganten here's the correct code, note the first part as well:

import Mathlib

noncomputable def binom_do_rec (p : ENNReal) (h : p  1) : (n : )  PMF (Fin (n+1))
  | 0 => PMF.pure 0
  | n+1 => do
    let Head  PMF.bernoulli p h
    let X  binom_do_rec p h n
    if Head then
      return X.succ
    else
      return X.castSucc

theorem my_binom_is_binom (p : ENNReal) (h : p  1) : binom_do_rec p h = PMF.binomial p h := by
  ext n S
  refine DFunLike.congr ?_ rfl
  induction n with
  | zero =>
    ext x
    simp [binom_do_rec, Fin.fin_one_eq_zero x]
  | succ m hm => sorry

Kenny Lau (Jun 24 2025 at 12:05):

you need succ and castSucc instead of +1

Kenny Lau (Jun 24 2025 at 12:06):

and simp is quite powerful if you just supply it with the relevant lemmas

Jasper Ganten (Jun 24 2025 at 13:16):

Kenny Lau said:

why are you using do in definitions...

more or less as an exercise... if done more beautifully I think (especially for the discrete PMFs) the code becomes more like the intuitive definition. thats the motivation

Jasper Ganten (Jun 24 2025 at 13:21):

I think my error was to only try simp only, which then doesnt do the necessary simplifications!

Jasper Ganten (Jun 24 2025 at 13:23):

Kenny Lau said:

you need succ and castSucc instead of +1

I get why this is much 'cleaner' but shouldnt the return automatically try to convert?
If the defintion is not correct then this should throw an error, or not?
Thanks for your answers :)

Aaron Liu (Jun 24 2025 at 13:27):

Jasper Ganten said:

Kenny Lau said:

you need succ and castSucc instead of +1

I get why this is much 'cleaner' but shouldnt the return automatically try to convert?
If the defintion is not correct then this should throw an error, or not?
Thanks for your answers :)

It converts to a Nat, and then to a Fin, which introduces a mod n in the value

Kenny Lau (Jun 24 2025 at 13:37):

Jasper Ganten said:

Kenny Lau said:

you need succ and castSucc instead of +1

I get why this is much 'cleaner' but shouldnt the return automatically try to convert?
If the defintion is not correct then this should throw an error, or not?
Thanks for your answers :)

It does throw and error if you click on the code itself (the top right corner of every code block has a little button that allows you to run it online)

Kenny Lau (Jun 24 2025 at 13:37):

image.png

Aaron Liu (Jun 24 2025 at 13:38):

Did they get rid of the Fin -> Nat -> Fin shenanigans?

Kenny Lau (Jun 24 2025 at 13:40):

it could be possible

Kyle Miller (Jun 24 2025 at 14:59):

Kenny Lau said:

why are you using do in definitions...

It's the Giry monad!

I thought I remembered that there was a do in the definition of product measures at some point, but I can't find it. Maybe it it got generalized because of the universe level constraints with do.

Jasper Ganten (Jun 24 2025 at 21:50):

how should I think about the limitations of do notation?
I was given the task to explore discrete PMFs and write them down using do but I am not even sure if bernoulli can be written from the dirac PMFs in do notation...

Kyle Miller (Jun 24 2025 at 22:23):

The universe limitation is that Bind.bind requires both the source and target to be in the same universe. If all of your types are in Type for example, then do is ok. Otherwise, you can use PMF.bind manually.

Kyle Miller (Jun 24 2025 at 22:26):

I think you're right that Bernoulli is basic (in the sense that it can't be defined with do alone and uniform PMFs), without some sort of operation to re-weight things.

Kyle Miller (Jun 24 2025 at 23:29):

A kind of fun definition of the binomial distribution is

noncomputable def binom₀ (p : ENNReal) (h : p  1) (n : ) : PMF  := do
  let choices  sequence <| List.replicate n (PMF.bernoulli p h)
  return choices.count true

It's very clearly "take a sequence of n weighted coin flips and count the number of heads".

Proving the support of this is what I thought it was took some doing though... The proof I came up with is really not good. It's clear though that there's some missing theory for dealing with do/monads and PMFs.

theorem binom_support (p : ENNReal) (h0 : 0 < p) (h1 : p < 1) (n : ) :
    (binom₀ p h1.le n).support = Set.Iic n

code

Kenny Lau (Jun 25 2025 at 08:04):

oh, the repeat function is called List.replicate!!!!! I was searching for that for so long, because I expected List.repeat (sorry off topic)

Jasper Ganten (Jun 25 2025 at 09:09):

@Kyle Miller That's amazing, thanks!
Why is the List converted to a sequence in your definition?
Is there no .count for lists (if yes, why)?
update: got it more or less - for flattening / evaluating the List

Jasper Ganten (Jun 25 2025 at 09:14):

@Kyle Miller The other question would be the motivation for using Set.Iic n as the support. Is this in general more elegant since going for some more explicit statement would result in proving the less-equal statement and show it from there?

Kyle Miller (Jun 25 2025 at 14:31):

Jasper Ganten said:

update: got it more or less - for flattening / evaluating the List

Yeah, the sequence function takes a list of PMFs and yields a PMF of lists.

The function is generic too, it works for any Traversable type.

Kyle Miller (Jun 25 2025 at 14:32):

Don't read much into the choice of Set.Iic -- it was just convenient when writing the theorem statement!

Jasper Ganten (Jul 01 2025 at 10:05):

Why is the step in between return a -> Pure.pure a -> PMF.pure a necessary? Is there a case when you don't want to apply the PMF.pure definition? Since simp handles the Pure.pure conversion this is not such a big thing but I wonder whats the reasoning behind this extra step in the interpretation of the do notation...

Robin Arnez (Jul 01 2025 at 10:59):

It's the same thing as 3 + 4 not being elaborated to Nat.add 3 4. The idea is that the "notation" pure should rather be used by lemmas, unless that's unwanted in which case there should a simp lemma converting it into the underlying operation (e.g. PMF.pure).

Jasper Ganten (Jul 01 2025 at 11:28):

I get that in a general case. But using do notation to my understanding already requires the Monad I am in to be specified. So if I say sth like (3+4 : Nat) should be elaborated.
A simpler case of my confusion is this:

noncomputable def pureDo (a : α) : (PMF α) := do
  return a

noncomputable def pureDo' (a : α) : (PMF α) := PMF.pure a

So why should the do notation in this case return a not be elaborated to PMF.pure?
To be honest I did not really understand what you wanted to say with 'unless thats unwanted'.
The main point is that the proofs for requires the additional step from Pure.pureto PMF.pure (a problem that is none if you use simp, but I am asking to undestand the elaboration).

noncomputable def pureDo {α} (a : α) : (PMF α) := do
  return a

noncomputable def pureDo' {α} (a : α) : (PMF α) := PMF.pure a

theorem pureDo_eval {α} (a : α) : (pureDo a) a = 1 := by
  rw [pureDo]
  simp only [Pure.pure] -- extra needed only with the do notation
  rw [PMF.pure_apply]
  exact if_pos rfl

theorem pureDo_eval' {α} (a : α) : (pureDo' a) a = 1 := by
  rw [pureDo']
  rw [PMF.pure_apply]
  exact if_pos rfl

Aaron Liu (Jul 01 2025 at 11:50):

It's the same reason we don't elaborate (3 + 4 : Real) to Real.add 3 4 and (3 + 4 : Rat) to Rat.add 3 4 and (3 + 4 : Int) to Int.add 3 4 and (3 + 4 : Nat) to Nat.add 3 4. It's so you can use the same lemmas (like docs#add_comm) for all these cases. In the case of monads we have stuff like docs#bind_pure.

Kyle Miller (Jul 01 2025 at 12:01):

You're running into the "multiple languages for the same thing" problem @Jasper Ganten. The PMF library seems to be written primarily in terms of PMF functions directly, and there's not much in the way of theory about monads. Yes, Pure.pure for PMF is defined to be PMF.pure, but the first is in the "monad language" and the second is in the "PMF language". It's best not to think of these two ways of expressing things as being exactly the same. You should think "these are (slightly) different languages, and it will take translation".

It would be worth writing some translation lemmas, like I did with PMF.monad_pure_eq_pure, and then possibly make a simp set, so that way you can write simp [pmf_of_do] and turn all the "do language" into "PMF language".

Jasper Ganten (Jul 01 2025 at 12:54):

Thanks to the both of you, this makes lots of sense. My confusion is probably the symptom of trying to learn lean and category theory/monads at the same time :smile: ... The suggestion to make a simp set is great, since there is not much I will do on the monad level (although it's intriguing to expand on this in the future maybe) :)


Last updated: Dec 20 2025 at 21:32 UTC