Zulip Chat Archive
Stream: mathlib4
Topic: Randomness
Yaël Dillies (Jan 13 2023 at 15:39):
Are there any good tutorials on how to use randomness, in particular the docs4#Rand monad?
Sky Wilshaw (Jan 13 2023 at 15:41):
The implementation looks a bit like Control.Monad.Random from haskell. They've probably got some good tutorials.
Mario Carneiro (Jan 13 2023 at 15:43):
I think it's just you use Rand A
as a monad, within which you can call rand : Rand T
to get random elements of any type T
Yaël Dillies (Jan 13 2023 at 15:44):
And how does one "run" it?
Sky Wilshaw (Jan 13 2023 at 15:45):
Sky Wilshaw (Jan 13 2023 at 15:45):
Or to use it with a fixed seed presumably you can just use the StateM
machinery
Eric Wieser (Jan 13 2023 at 15:45):
If it's just ported from mathlib, you can also use (some_rand_obj.run (ULift.up (mkStdGen seed))).1
(docs4#mkStdGen, docs4#StateT.run)
Sky Wilshaw (Jan 13 2023 at 15:46):
Checking the impl of docs4#IO.runRandWith, you can use (StateT.run cmd { down := mkStdGen seed }).fst
Yaël Dillies (Jan 13 2023 at 15:47):
Oh wait, so not much changed from the Lean 3 implementation?
Sky Wilshaw (Jan 13 2023 at 15:47):
Ah, Eric beat me to it!
Eric Wieser (Jan 13 2023 at 15:49):
You might be interested in https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Stating.20properties.20of.20the.20results.20of.20monads/near/319573258, @Yaël Dillies, which has some example code
Yaël Dillies (Jan 13 2023 at 15:51):
Time to see whether the bug I found in https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Not.20so.20random is still there :stuck_out_tongue_closed_eyes:
Mario Carneiro (Jan 13 2023 at 15:57):
it is, it's documented as such
Yaël Dillies (Jan 13 2023 at 18:18):
How would one fill this in?
import Mathlib.Control.Random
def Random.pi {α β g : Type} [RandomGen g] (r : α → RandG g β) : RandG g (α → β) := sorry
Do we have a typeclass to say that α
is enumerable?
Yaël Dillies (Jan 13 2023 at 18:21):
Is docs4#Encodable a reasonable choice?
Yaël Dillies (Jan 13 2023 at 18:21):
Ah! Not there yet.
Eric Wieser (Jan 13 2023 at 18:27):
You could fill it in as pure default
Eric Wieser (Jan 13 2023 at 18:32):
What's the algorithm for an docs#encodable type?
Eric Wieser (Jan 13 2023 at 18:32):
I don't see how you would generate an infinite series of bits with a finite number of invocations of the random number generator
Yaël Dillies (Jan 13 2023 at 18:33):
I'm going with
class Enum (α : Type _) where
enum : List α
enum_nodup : enum.Nodup
mem_enum : ∀ a : α, a ∈ enum
which sounds like it should work?
Eric Wieser (Jan 13 2023 at 18:33):
So fintype
?
Yaël Dillies (Jan 13 2023 at 18:33):
No!
Yaël Dillies (Jan 13 2023 at 18:34):
I need an order on the elements, else I cannot pass around the monad.
Eric Wieser (Jan 13 2023 at 18:34):
/me laughs in quot.unquot
Eric Wieser (Jan 13 2023 at 18:34):
I don't think you can prove anything interesting about RandG
anyway
Eric Wieser (Jan 13 2023 at 18:34):
So peeking under the quotient feels like fair game
Yaël Dillies (Jan 13 2023 at 18:35):
I can make it generate tests for my CATAM projects, however.
Eric Wieser (Jan 13 2023 at 18:35):
Right, my point is that the unsafe
on docs4#Quot.unquot isn't a problem for you
Yaël Dillies (Jan 13 2023 at 18:35):
In my case, α
is Fin n
, so it feels a bit dumb not providing List.finRange
directly
Eric Wieser (Jan 13 2023 at 18:36):
To the compiler, I would guess finset.univ.unquot is exactly that
Eric Wieser (Jan 13 2023 at 18:36):
But I guess we don't have docs4#Fintype so that's irrelevant
Yaël Dillies (Jan 13 2023 at 18:36):
Regardless, how do I then use this enumerating list to build my random function?
Eric Wieser (Jan 13 2023 at 18:36):
Oh, we have an ad-hoc port for precisely what you need! (the fin instances)
Eric Wieser (Jan 13 2023 at 18:37):
Yaël Dillies said:
Regardless, how do I then use this enumerating list to build my random function?
mmap
over the list of all α
, and turn the list back into a function
Yaël Dillies (Jan 13 2023 at 18:38):
Ah! List.ofFn
!
Eric Wieser (Jan 13 2023 at 18:38):
No, the reverse; docs4#List.get
Yaël Dillies (Jan 13 2023 at 18:43):
Does that sound right?
def pi [Enum α] (r : α → RandG g β) : RandG g (α → β) :=
(λ l a ↦ _) <$> (Enum.enum : List α).mapM r
Eric Wieser (Jan 13 2023 at 18:58):
That's only going to work for Fin
Eric Wieser (Jan 13 2023 at 18:58):
You're going to need to produce a list of (a, b)
pairs to do a lookup for other types (either by zipping with Enum.enum
, or by using do b <- r a, pure (a, b)
in the map.
Eric Wieser (Jan 13 2023 at 18:59):
Which is also going to be slower
Yaël Dillies (Jan 13 2023 at 19:00):
This is what it looks like now
class Enum (α : Type _) where
enum : List α
enum_nodup : enum.Nodup
index : α → Fin enum.length
enum_index : ∀ a, enum.get (index a) = a
instance {n : ℕ} : Enum (Fin n) :=
{ enum := List.finRange _,
enum_nodup := List.nodup_finRange _,
index := λ n ↦ ⟨n, by rw [List.length_finRange]; exact n.2⟩,
enum_index := λ a ↦ by simp }
namespace Random
variable {α β g : Type} [RandomGen g]
def pi [Enum α] [BEq α] (r : α → RandG g β) : RandG g (α → β) :=
(λ l a ↦ List.get l $ ⟨Enum.index a, sorry⟩) <$> (Enum.enum : List α).mapM r
Yaël Dillies (Jan 13 2023 at 19:01):
The problem is that I cannot fill in the sorry
because I don't know that the list I'm getting out of the mapM
has the right length, which is exactly the same problem as you had, right?
Eric Wieser (Jan 13 2023 at 19:01):
Yes, you need a docs4#Vector.mapM (docs#vector.mmap)
Eric Wieser (Jan 13 2023 at 19:01):
Or to map over the subtype
Yaël Dillies (Jan 13 2023 at 19:03):
Do we not have docs4#List.length_mapM?
Eric Wieser (Jan 13 2023 at 19:05):
How would you state it?
Yaël Dillies (Jan 13 2023 at 19:06):
Oh
Eric Wieser (Jan 13 2023 at 19:07):
You could just grab that vector lemma from mathport
Yaël Dillies (Jan 13 2023 at 19:07):
I guess I need to port Data.Vector.Basic
, then :P
Eric Wieser (Jan 13 2023 at 19:07):
It has some dependencies yet
Eric Wieser (Jan 13 2023 at 19:07):
I think data.fin.tuple.basic and data.list.of_fn
Kyle Miller (Jan 13 2023 at 19:27):
@Yaël Dillies You probably don't want an Enum
that has an actual List
of all elements if you're going to be choosing random elements for programming purposes (for example, a random 32-bit integer is impossible this way, or, equivalently, getting a random Fin 32 -> Fin 2
).
Another option is to have Enum
be a wrapper around Equiv
to a Fin n
for some n
. A while back I had some experiment with this: https://gist.github.com/kmill/9055e1fc32d75b818cbb036650d309ed#file-vec_experiment2-lean-L73 (not for randomness though)
Yaël Dillies (Jan 13 2023 at 19:33):
I'm not sure I understand your objection.
Yaël Dillies (Jan 13 2023 at 19:34):
I just managed to get a matrix of random Bool
s!
[[true, false, false, true, false, false, false, true, true, false], [false, false, false, false, false, true, false, false, false, false], [false, false, false, true, false, false, false, false, false, true], [false, false, false, false, false, false, false, false, false, true], [false, true, true, true, false, true, true, false, false, false], [false, false, false, false, true, false, false, true, true, true], [false, true, true, false, true, false, false, true, false, false], [true, false, false, true, true, false, true, false, false, false], [false, false, false, false, true, false, true, false, true, true], [true, false, false, true, true, false, true, false, false, true]]
Yaël Dillies (Jan 13 2023 at 19:35):
(and I set the probability to 0.33333333333
)
Yaël Dillies (Jan 13 2023 at 19:36):
The other ingredient was
instance {α : Type _} {β : α → Type _} [Enum α] [∀ a, ToString (β a)] : ToString (∀ a, β a) :=
⟨λ f ↦ toString $ Enum.enum.map $ λ a ↦ toString $ f a⟩
Eric Wieser (Jan 13 2023 at 19:37):
That seems like bad ToString
Eric Wieser (Jan 13 2023 at 19:37):
Also, you'll get nice line-wrappign if you implement Repr
instead
Yaël Dillies (Jan 13 2023 at 19:37):
How should I unbad my ToString
?
Eric Wieser (Jan 13 2023 at 19:38):
Well right now it pretends functions are lists
Eric Wieser (Jan 13 2023 at 19:39):
You could print it as λ a ↦ [elems...].get ⟨Enum.index a, _⟩
Yaël Dillies (Jan 13 2023 at 19:39):
That's the role of Repr
, right? ToString
can just "look nice"
Eric Wieser (Jan 13 2023 at 19:40):
I have no idea what docs4#ToString is for
Kyle Miller (Jan 13 2023 at 19:41):
I thought you were using a different design, where you use Enum
and choose elements uniformly at random, since this is something you can do for everything that implements Enum
.
Yaël Dillies (Jan 13 2023 at 19:43):
Ah no, the role of Enum
here is to pick a canonical order in which to run the dice rolls.
Yaël Dillies (Jan 13 2023 at 20:06):
I just defined
def randFin' [NeZero n] : RandG g (Fin n) :=
λ ⟨g⟩ ↦ randNat g 0 n |>.map (λ a ↦ Fin.ofNat' a $ NeZero.pos n) ULift.up
instance [Enum α] [Nonempty α] : NeZero (Enum.enum : List α).length := sorry
def uniform [Enum α] [Nonempty α] : RandG g α := Enum.enum.get <$> randFin'
which I assume is what you meant?
Yaël Dillies (Jan 13 2023 at 20:08):
I still don't understand your objection. If I had
def List.pi (l : List α) (f : ∀ a, List (β a)) : List (∀ a, β a) :=
then I would be able to provide an Enum (Fin 32 → Fin 2)
instance and generate 32-bits integers uniformly at random.
Yaël Dillies (Jan 13 2023 at 20:09):
Or is your argument that it is too slow?
Kyle Miller (Jan 13 2023 at 20:19):
Mathematically it's fine, but a List (Fin 32 → Fin 2)
that enumerates that type would take 96 gigabytes of RAM minimum.
Kyle Miller (Jan 13 2023 at 20:23):
(That's two 8-byte pointers per List.cons
, and 8 bytes per Fin 32 -> Fin 2
, which is an optimistic lower bound for how Lean would represent such a function.)
Yaël Dillies (Jan 13 2023 at 21:32):
How is your suggested Enum
better?
Yaël Dillies (Jan 13 2023 at 22:06):
And more precisely does it make what I'm doing harder?
Yaël Dillies (Jan 13 2023 at 22:09):
Right now, I have
def pi [Enum α] (r : α → RandG g β) : RandG g (α → β) :=
(λ l a ↦ List.get l.toList $ ⟨Enum.index a, by simp⟩) <$> Vector.mapM r ⟨Enum.enum, rfl⟩
Do you see any obvious way of generalizing it to dependent types?
Eric Wieser (Jan 13 2023 at 22:11):
Put sigma types in the list?
Yaël Dillies (Jan 13 2023 at 22:14):
Yes but then I need a specific type of vectors whose keys are unique and complete.
Yaël Dillies (Jan 13 2023 at 22:14):
DVector
, if you wish
Eric Wieser (Jan 13 2023 at 22:15):
docs#finmap? But I guess of fixed-length
Eric Wieser (Jan 13 2023 at 22:16):
Is this an #xy problem, or are you solving this problem because you find it interesting, independent of whether you actually need it?
Yaël Dillies (Jan 13 2023 at 22:16):
The latter
Yaël Dillies (Jan 13 2023 at 22:17):
I am happy to leave it as is, but I thought there might be something interesting here.
Eric Wieser (Jan 13 2023 at 22:17):
Yaël Dillies said:
Yes but then I need a specific type of vectors whose keys are unique and complete.
Why do you need the keys to be unique and complete? What goes wrong if you just have a vector of sigmas?
Yaël Dillies (Jan 13 2023 at 22:20):
Okay, they may not need be unique, but I need to find the entry corresponding to a key, so I need the key to exist.
Eric Wieser (Jan 13 2023 at 22:21):
Can't you just look in the ith index like you do now?
Yaël Dillies (Jan 13 2023 at 22:21):
Hmm...
Eric Wieser (Jan 13 2023 at 22:22):
Maybe you get stuck trying to cast the sigma type
Yaël Dillies (Jan 13 2023 at 22:48):
Indeed, I have lost the information that (l.toList.get $ Enum.index a).1 = a
(λ l a ↦ _ -- `β a`
) <$> Vector.mapM (λ a ↦ (r a).map $ Sigma.mk a) ⟨Enum.enum, rfl⟩
Kyle Miller (Jan 14 2023 at 12:48):
@Yaël Dillies This should be reasonably efficient (though it's still incomplete and would need more development). Generally, Array
is more efficient than List
.
import Mathlib.Logic.Equiv.Basic
import Std
import Mathlib.Control.Random
import Mathlib.Data.Nat.Order.Basic
def Fin.arrayOf (f : Fin n → α) : Array α := Id.run do
let mut a := Array.mkEmpty n
for h : i in [0 : n] do
a := a.push (f ⟨i, h.2⟩)
return a
def Fin.size_arrayOf (f : Fin n → α) : (Fin.arrayOf f).size = n := sorry
def Fin.get_arrayof (f : Fin n → α) (i : Fin (Fin.arrayOf f).size) :
(Fin.arrayOf f).get i = f (by rw [← Fin.size_arrayOf f]; exact i) := sorry -- should cast
class Enum (α : Type _) where
card : ℕ
enum : Fin card ≃ α
arrayOf : Unit → Array α -- a function, which prevents computing an `Array` unless one is actually needed.
arrayOf_eq : arrayOf () = Fin.arrayOf enum
theorem Enum.size_arrayOf [Enum α] : (Enum.arrayOf () : Array α).size = Enum.card α := by
rw [arrayOf_eq, Fin.size_arrayOf]
instance {n : ℕ} : Enum (Fin n) where
card := n
enum := Equiv.refl (Fin n)
arrayOf := fun _ => Fin.arrayOf id
arrayOf_eq := rfl
namespace Random
variable {α β g : Type} [RandomGen g]
def randFin' [NeZero n] : RandG g (Fin n) := do
λ ⟨g⟩ ↦ randNat g 0 n |>.map (λ a ↦ Fin.ofNat' a $ NeZero.pos n) ULift.up
instance [Enum α] [Nonempty α] : NeZero (Enum.card α) := sorry
def uniform [Enum α] [Nonempty α] : RandG g α := Enum.enum <$> randFin'
def pi [Enum α] (β : α → Type _) (r : (x : α) → RandG g (β x)) : RandG g ((x : α) → β x) := do
let a : Array ((x : α) × β x) ← -- graph of function we're constructing
(Enum.arrayOf () : Array α).mapM (fun x => do return ⟨x, ← r x⟩)
-- can't prove these two within the do block:
have hsize : a.size = Enum.card α := sorry
have hprop : ∀ i,
(a.get i).1 = (Enum.arrayOf ()).get ⟨i, by { cases i; rwa [Enum.size_arrayOf, ← hsize] }⟩ := sorry
return λ x ↦
match h : a.get ⟨Enum.enum.symm x, sorry⟩ with
| ⟨x', b⟩ => by
have : x = x' := sorry
subst this
exact b
Kyle Miller (Jan 14 2023 at 12:50):
Having both Enum.enum
and Enum.arrayOf
gives instances the ability to provide efficient implementations of them. Instances can also fill in one using the other if they don't want to do anything special.
Yaël Dillies (Jan 14 2023 at 20:00):
Interesting! As a test case, can you generalize the following to dependent functions? Nevermind, you already have. Lean 4 notation is throwing me off.
Last updated: Dec 20 2023 at 11:08 UTC