Zulip Chat Archive

Stream: maths

Topic: Well-founded recursion for pgames


Apurva Nakade (May 19 2021 at 00:11):

I've been stuck on a big proof for surreal numbers for quite some time now.

I'm trying to prove a complicated theorem f(x, y) for {x y : pgames} by recursion.
I end up having to use statements like f(x - xL i, yL j), f(xL k, yR l - y), etc in the proof where xL _ are the left moves of x and xR _ are the right moves of x and similarly for y. Note that the "depth" always increases and there are no terms of the form f(x - xL i, y) so mathematically the recursion is fine.

Lean is unable to prove well-founded recursion for this. I tried doing a trace and it seems to be that the terms of the form x - xL i are throwing it off.

I'm not sure if I can come up with a mwe for this. I can post my big messy proof here.

Any suggestions on how to prove that this kind of recursion is well-founded?

Scott Morrison (May 19 2021 at 00:14):

You might have to post. Sometimes if you're really suffering with a well-founded recursion, it can be helpful to work out how to instead do a inductive proof on nat itself. (e.g. introduce a complexity measure, and hypotheses that explicitly control your arguments in terms of it). If nothing else sometimes it's helpful as a paper-proof step, to make sure you're really doing a sensible induction.

Mario Carneiro (May 19 2021 at 00:16):

Usually surreal numbers require a much higher order induction than nat though

Scott Morrison (May 19 2021 at 00:16):

From the looks of the terms you mention, no lexicographic ordering on the arguments is going to help you, so you're certainly going to have to introduce by hand a different complexity measure, whether you do it with using_well_founded, or by induction on nat

Scott Morrison (May 19 2021 at 00:16):

Ah, good point.

Mario Carneiro (May 19 2021 at 00:17):

probably you can do it using a surreal number or ordinal as the measure though

Scott Morrison (May 19 2021 at 00:18):

I suspect though @Apurva Nakade might be talking about Theorem 3.8 of http://www.cs.cmu.edu/afs/cs/academic/class/15859-s05/www/lecture-notes/comb-games-notes.pdf, which claims to work by induction on a tuple of natural numbers.

Apurva Nakade (May 19 2021 at 00:42):

I'll clean up the code a bit and post it soon.

Yes, I'm trying to prove a weaker version of Theorem 3.8 from those notes. The theorem as it is in the notes is too hard to code. They prove inequalities involving sums of products, which is insane!

In those notes they define a "depth" which I think in Conway's terms might be "day" on which the numbers are born. I think I might need to prove some well_foundedness in terms of "depth". Tbh I've been using the tactic blindly and don't understand well_foundedness much. Is there any reference for this? Or some existing theorems about well_foundedness in mathlib?

Apurva Nakade (May 19 2021 at 00:50):

Here's the code

import set_theory.surreal

namespace pgame
open pgame

-- some lemmas first
theorem numeric_sub {x y : pgame} (ox : numeric x) (oy : numeric y) : numeric (x - y) :=
numeric_add ox (numeric_neg oy)

theorem zero_le₁ {x : pgame} : 0  x 
  ( j : x.right_moves, 0 < x.move_right j) :=
by { rw le_def_lt, exact λ hl, hr j, hr j, λ hr, by rintro  ⟩, hr⟩⟩ }

theorem le_zero₁ {x : pgame} : x  0 
  ( i : x.left_moves, x.move_left i < 0) :=
by { rw le_def_lt, exact λ hl, hr i, hl i, λ hl, hl, by rintro  ⟩⟩⟩ }

theorem zero_lt₁ {x : pgame} : 0 < x 
  ( i : x.left_moves, 0  x.move_left i) :=
by {rw lt_def_le, dsimp, simp[forall_pempty] }

theorem lt_zero₁ {x : pgame} : x < 0 
  ( j : x.right_moves, x.move_right j  0) :=
by {rw lt_def_le, dsimp, simp[forall_pempty] }

-- missing trans rules for pgames
@[trans] theorem lt_of_lt_of_equiv' {x y z : pgame} (h₁ : x < y) (h₂ : y  z) : x < z := lt_of_lt_of_le h₁ h₂.1
@[trans] theorem le_of_le_of_equiv' {x y z : pgame} (h₁ : x  y) (h₂ : y  z) : x  z := le_trans h₁ h₂.1
@[trans] theorem lt_of_equiv_of_lt' {x y z : pgame} (h₁ : x  y) (h₂ : y < z) : x < z := lt_of_le_of_lt h₁.1 h₂
@[trans] theorem le_of_equiv_of_le' {x y z : pgame} (h₁ : x  y) (h₂ : y  z) : x  z := le_trans h₁.1 h₂

--problem will well-founded recursion :(

set_option trace.solve_by_elim true

theorem foobar : Π {x y : pgame},
(x.numeric  y.numeric  0 < x  0 < y  0 < x * y) 
(x.numeric  y.numeric  0  x  0  y  0  x * y)
| (mk xl xr xL xR) (mk yl yr yL yR) :=
begin
  let x := mk xl xr xL xR,
  let y := mk yl yr yL yR,
  split,
  { intros ox oy pos_x pos_y,
    have nonneg_x : 0  x, from le_of_lt numeric_zero ox pos_x,
    have nonneg_y : 0  y, from le_of_lt numeric_zero oy pos_y,
    obtain i, hi := zero_lt₁.1 pos_x,
    obtain j, hj := zero_lt₁.1 pos_y,
    rw zero_lt₁,
    use sum.inl (i, j),
    calc 0  yL j * (x - xL i)
           : foobar.2 (oy.2.1 j) (numeric_sub ox (ox.2.1 i))
            hj ((@le_iff_sub_nonneg (xL i) x).1 (le_of_lt (ox.2.1 i) ox (ox.move_left_lt i)))
       ...  (x - xL i) * yL j
           : mul_comm_equiv _ _
       ...  0 + (x - xL i) * yL j
           : (zero_add_equiv _).symm
       ...  xL i * y + (x - xL i) * yL j
           : add_le_add_right $ foobar.2 (ox.2.1 i) oy hi nonneg_y
       ...  xL i * y + x * yL j - xL i * yL j
           : by { apply @quotient.exact pgame, simp, abel } },
  { intros ox oy nonneg_x nonneg_y,
    have hx' := zero_le₁.1 nonneg_x, dsimp at hx',
    have hy' := zero_le₁.1 nonneg_y, dsimp at hy',
    rw zero_le₁,
    rintros (⟨i, j | i, j⟩),
    { by_cases h : 0  xL i,
      { calc 0  xL i * y : foobar.2 (ox.2.1 i) oy h nonneg_y
      ...  xL i * y + 0 : (add_zero_equiv _).symm
      ... < xL i * y + (x - xL i) * yR j
          : add_lt_add_left $ foobar.1 (numeric_sub ox (ox.2.1 i)) (oy.2.2 j)
                                (lt_iff_sub_pos.1 (ox.move_left_lt i)) (hy' j)
      ...  xL i * y + x * yR j - xL i * yR j
          : by { apply @quotient.exact pgame, simp, abel } },
      { rw not_le at h,
        calc 0   x * yR j
                : foobar.2 ox (oy.2.2 j) nonneg_x (le_of_lt numeric_zero (oy.2.2 j) (hy' j))
          ...  x * yR j + 0 : (add_zero_equiv _).symm
          ... < x * yR j + (0 - xL i) * (yR j - y)
                : add_lt_add_left $ foobar.1 (numeric_sub numeric_zero (ox.2.1 i))
                                       (numeric_sub (oy.2.2 j) oy)
                                       (lt_iff_sub_pos.1 h)
                                       (lt_iff_sub_pos.1 (oy.lt_move_right j))
          ...  xL i * y + x * yR j - xL i * yR j
              : by { apply @quotient.exact pgame, simp, abel } } },
  {
    by_cases h : 0  yL j,
    {calc 0 < xR i * (y - yL j)
            : foobar.1 (ox.2.2 i) (numeric_sub oy (oy.2.1 j)) (hx' i) (lt_iff_sub_pos.1 (oy.move_left_lt j))
     ...  0 + xR i * (y - yL j) : (zero_add_equiv _).symm
     ...  x * yL j + xR i * (y - yL j)
         : add_le_add_right $ foobar.2 ox (oy.2.1 j) nonneg_x h
     ...  xR i * y + x * yL j - xR i * yL j : by { apply @quotient.exact pgame, simp, abel } },
    {rw not_le at h,
    calc 0
         < (xR i - x) * (0 - yL j)
         : foobar.1 (numeric_sub (ox.2.2 i) ox) (numeric_sub numeric_zero (oy.2.1 j))
           (lt_iff_sub_pos.1 (ox.lt_move_right i))
           (lt_iff_sub_pos.1 h)
      ...  0 + (xR i - x) * (0 - yL j)
          : (zero_add_equiv _).symm
      ...  xR i * y + (xR i - x) * (0 - yL j)
          : add_le_add_right $ foobar.2 (ox.2.2 i) oy
                                  (le_of_lt numeric_zero (ox.2.2 i) (hx' i)) nonneg_y
      ...  xR i * y + x * yL j - xR i * yL j : by { apply @quotient.exact pgame, simp, abel } } } },
end
using_well_founded { dec_tac := pgame_wf_tac }

end pgame

Apurva Nakade (May 19 2021 at 00:58):

The notes prove the following instead by expanding everything out

theorem foo {x y w z : pgame} : x.numeric  y.numeric  w.numeric  z.numeric 
(x < w)  (y < z) 
(x * z + w * y < x * y + w * z) := sorry

but we can transfer all the variables to one side and reduce the theorem to mul_pos. Because le and lt are defined in terms of each other, we end up having to prove the two theorems simultaneously.

Apurva Nakade (May 19 2021 at 00:59):

(deleted)

Apurva Nakade (May 19 2021 at 01:27):

Mario Carneiro said:

probably you can do it using a surreal number or ordinal as the measure though

How would one go about doing this? I think any connection between pgame and ordinal would be great. Does there exist a map pgame \to ordinals?

Mario Carneiro (May 19 2021 at 01:31):

import set_theory.game
import set_theory.ordinal_arithmetic

namespace pgame

noncomputable def {u} bday : pgame.{u}  ordinal.{u}
| (mk xl xr xL xR) := ordinal.sup.{u u} $
  sum.elim (λ i, (xL i).bday.succ) (λ i, (xR i).bday.succ)

end pgame

Apurva Nakade (May 19 2021 at 01:42):

Great, thanks! I should be able to prove that bday behaves well under arithmetic operations of pgames.

Given such a functor, how does one prove something is well_founded? Mathematically, I want to say that any descending chain of ordinals is finite but I dunno how this is encoded into well_founded in lean.

Apurva Nakade (May 19 2021 at 01:50):

(deleted)

Mario Carneiro (May 19 2021 at 01:59):

there is an instance already saying that ordinals are well ordered by <

Mario Carneiro (May 19 2021 at 02:00):

So you would use something like inv_image (<) bday and typeclass inference knows that this relation has a well founded instance

Apurva Nakade (May 19 2021 at 02:08):

Awesome, thanks again! I'll try this out.

Kevin Buzzard (May 19 2021 at 06:38):

Re foo -- can you deduce it from (z-y)*(w-x)>0? I have this vague memory that there are problems with distributivity though...

Apurva Nakade (May 19 2021 at 12:36):

Yeah, this is exactly what i'm trying to prove. Distributivity is fine as we've already shown all the ring properties for pgames up to equivalence.

theorem mul_pos {x y w z : pgame} : x.numeric  y.numeric  w.numeric  z.numeric 
(0 < w - x)  (0 < z - y) 
0 < (w  - x)(z  -  y) := sorry

You can't prove the above theorem by itself as < is defined in terms of \le so you end up having to prove both simultaneously.

Kevin Buzzard (May 19 2021 at 12:38):

I am asking whether it's easier to prove a>0 and b>0 and a,b numeric implies ab>0 (two variables not four), but I don't know anything about this stuff.

Apurva Nakade (May 19 2021 at 12:39):

Yups, this is the theorem I'm currently trying:

theorem foobar : Π {x y : pgame},
(x.numeric  y.numeric  0 < x  0 < y  0 < x * y) 
(x.numeric  y.numeric  0  x  0  y  0  x * y)

Apurva Nakade (May 19 2021 at 12:40):

Really hoping to make the well-founded recursion problem go away for this.

Kevin Buzzard (May 19 2021 at 12:41):

so what happens when you just try doing this by induction on birthday?

Kevin Buzzard (May 19 2021 at 12:41):

That's how Conway would do it, right?

Apurva Nakade (May 19 2021 at 12:44):

I haven't tried it yet. But hopefully it works after that.

Yes! That's my thought too, just need to set it up.
Need to prove a few lemmas about birthday first. I think this might take some time...

Kevin Buzzard (May 19 2021 at 12:47):

Conway seems to give an argument in his proof of Theorem 8(iii) on p19 of ONAG

Kevin Buzzard (May 19 2021 at 12:49):

and, as you originally suggested, he proved x>0 and y>0 implies xy>0 from this more general statement you were originally considering.

Apurva Nakade (May 19 2021 at 12:54):

I looking at his proofs, he is proving the theorem with 4 variables directly :scared:
There are lots of WALOGs and "... can be deduced from ..." in there.

Apurva Nakade (May 21 2021 at 14:20):

One very basic question: ordinal has a preorder instance. From this, how do I extract ordinal.le_trans? I tried the following but it did not quite work

import set_theory.game
import set_theory.ordinal_arithmetic

noncomputable instance temp : preorder ordinal := by apply_instance

#check temp.le_trans           -- unknown identifier
#check ordinal.le_trans       -- unknown identifier

Eric Wieser (May 21 2021 at 14:23):

#check @le_trans ordinal _  -- ∀ {a b c : ordinal}, a ≤ b → b ≤ c → a ≤ c

Eric Wieser (May 21 2021 at 14:26):

Although #check temp.le_trans works for me?

Apurva Nakade (May 21 2021 at 14:27):

Oh, I've open pgame at the start, I should add that to the mwe

Apurva Nakade (May 21 2021 at 14:28):

import set_theory.game
import set_theory.ordinal_arithmetic

open pgame
namespace pgame

noncomputable instance temp : preorder ordinal := by apply_instance

#check temp.le_trans           -- unknown identifier
#check ordinal.le_trans       -- unknown identifier

end pgame

Eric Wieser (May 21 2021 at 14:28):

#check pgame.temp.le_trans

Eric Wieser (May 21 2021 at 14:29):

Named instances are only accessible via their fully-qualified name; instance is roughly short for @[instance] protected def

Apurva Nakade (May 21 2021 at 14:29):

Oh, wow!

Apurva Nakade (May 21 2021 at 14:29):

Thanks, this worked!

Apurva Nakade (May 21 2021 at 14:29):

Ah, I see

Eric Wieser (May 21 2021 at 14:30):

Although it's unlikely you actually need to specify which le_trans you want here anyway

Apurva Nakade (May 21 2021 at 14:34):

There's also the le_trans coming from pgame

Eric Wieser (May 21 2021 at 14:34):

_root_.le_trans will avoid that ambiguity

Apurva Nakade (May 21 2021 at 14:38):

Great, this worked perfectly, thanks!

Apurva Nakade (May 21 2021 at 14:49):

Is there any way to use _root_.le_trans in calc mode?

Eric Wieser (May 21 2021 at 14:53):

local attribute [-trans] pgame.le_trans?

Eric Wieser (May 21 2021 at 14:53):

(deleted)

Apurva Nakade (May 21 2021 at 14:55):

I'm getting 300:1: cannot remove attribute [trans]
I tried putting it inside a section and namespace still the same error.

Eric Wieser (May 21 2021 at 14:55):

Then instead, remove the @[trans] from pgame.le_trans and add local attribute [trans] pgame.le_trans only where you need it

Apurva Nakade (May 21 2021 at 14:56):

This will be too complicated. pgame.le_trans is used in almost every single proof :(

Eric Wieser (May 21 2021 at 14:57):

Sure, but you can add the attribute locally at the top of the section

Apurva Nakade (May 21 2021 at 14:58):

Ah I see, cool, I'll try it out. Will need to shuffle the current proofs around a bit.

Apurva Nakade (May 22 2021 at 12:51):

Apurva Nakade said:

I've been stuck on a big proof for surreal numbers for quite some time now.

I'm trying to prove a complicated theorem f(x, y) for {x y : pgames} by recursion.
I end up having to use statements like f(x - xL i, yL j), f(xL k, yR l - y), etc in the proof where xL _ are the left moves of x and xR _ are the right moves of x and similarly for y. Note that the "depth" always increases and there are no terms of the form f(x - xL i, y) so mathematically the recursion is fine.

Lean is unable to prove well-founded recursion for this. I tried doing a trace and it seems to be that the terms of the form x - xL i are throwing it off.

I'm not sure if I can come up with a mwe for this. I can post my big messy proof here.

Any suggestions on how to prove that this kind of recursion is well-founded?

I think this recursion is not well-founded. It could happen that (x - xL i).bday > x.bday (for example, x = n and xL i = -1.

I dunno how to prove numeric_mul anymore :(
The math proofs I've seen require you to prove things like

theorem foo {x y w z : pgame} : x.numeric  y.numeric  w.numeric  z.numeric 
(x < w)  (y < z) 
(x * z + w * y < x * y + w * z) := sorry

But the LHS and RHS of these inequalities will have 16 terms so we'll end up having to prove 32 inequalities!
Most texts simply wave their hands and say that we'll do one case and the rest are similar, I'm not sure if this is actually true.

Dunno how to proceed from here.

Apurva Nakade (May 22 2021 at 12:54):

Moreover, you cannot even prove (x < w) → (y < z) →(x * z + w * y < x * y + w * z) by itself. Instead you simultaneously have to mul_congr!

Eric Wieser (Apr 27 2022 at 10:55):

Mario Carneiro said:

import set_theory.game
import set_theory.ordinal_arithmetic

namespace pgame

noncomputable def {u} bday : pgame.{u}  ordinal.{u}
| (mk xl xr xL xR) := ordinal.sup.{u u} $
  sum.elim (λ i, (xL i).bday.succ) (λ i, (xR i).bday.succ)

end pgame

This is now in mathlib as docs#pgame.birthday

Eric Wieser (Apr 27 2022 at 10:57):

Although the definition is now instead:

noncomputable def birthday : pgame.{u}  ordinal.{u}
| xl, xr, xL, xR :=
    max (ordinal.lsub.{u u} $ λ i, birthday (xL i)) (ordinal.lsub.{u u} $ λ i, birthday (xR i))

I assume the difference isn't important? Or do you think your formulation is more convenient @Mario Carneiro than @Violeta Hernández's

Mario Carneiro (Apr 27 2022 at 10:58):

no, I imagine that will work out basically the same

Violeta Hernández (Apr 27 2022 at 12:35):

The supremum of a set of successors is definitionally what lsub is

Violeta Hernández (May 03 2022 at 01:03):

Oh I hadn't noticed that this thread was for the exact thing I've been struggling with for the past week

Violeta Hernández (May 03 2022 at 01:04):

But yeah, as for the surreal multiplication proof

Violeta Hernández (May 03 2022 at 01:04):

It can be proven by a very specific induction on birthdays

Violeta Hernández (May 03 2022 at 01:08):

https://github.com/leanprover-community/mathlib/blob/6d7ead9ed67cf7ce04fe63829f7388b9b521e05b/src/set_theory/surreal/basic.lean#L318

Violeta Hernández (May 03 2022 at 01:09):

This is basically the Schleicher proof, except that I figured out you could use birthdays instead of "depth", and also that P2 and P4 could be merged into a single statement

Violeta Hernández (May 03 2022 at 01:10):

The induction, despite being quite complex, relies on a single nontrivial statement on birthdays, which is birthday x < birthday y → birthday (z + x) < birthday (z + y)

Violeta Hernández (May 03 2022 at 01:12):

This can be proven easily from birthday x ≤ birthday y → birthday (z + x) ≤ birthday (z + y), which can be done by unfolding the definition of birthday (x + y) into the maximum of four lsubs and proving each less-equal by the induction hypothesis

Violeta Hernández (May 03 2022 at 01:13):

The only reason I haven't done this yet is that I'm waiting for #13611 to pass, which makes working with addition much less painful

Violeta Hernández (May 03 2022 at 01:15):

If anyone wants to help fill the proof up, it would be greatly appreciated

Violeta Hernández (May 03 2022 at 01:16):

The calculations themselves are basically the same as those in the Schleicher PDF

Violeta Hernández (May 03 2022 at 08:09):

Two updates!

Violeta Hernández (May 03 2022 at 08:09):

First, most of the proof has now been written down

Violeta Hernández (May 03 2022 at 08:09):

Second, I found another simplification of the argument in the PDF

Violeta Hernández (May 03 2022 at 08:10):

Turns out that you don't actually need P1 to conclude P4

Violeta Hernández (May 03 2022 at 08:10):

Schleicher invokes P1 to conclude (xy)^L < xy and xy < (xy)^R, but these inequalities hold for all games, not just numeric games

Violeta Hernández (May 03 2022 at 08:11):

What might not hold is (xy)^L < (xy)^R, which fortunately isn't used

Violeta Hernández (May 03 2022 at 08:24):

So you can in fact prove the following statement: for any x₁, x₂, y, the following hold simultaneously:

  • if x₁ = x₂, then x₁ * y is numeric.
  • If x₁ ≈ x₂, then x₁ * y ≈ x₂ * y.
  • If x₁ < x₂, then,
    • For any left option yL, x₂ * yL + x₁ * y < x₁ * yL + x₂ * y.
    • For any left option yR, x₂ * y + x₁ * yR < x₁ * y + x₂ * yR.

Violeta Hernández (May 03 2022 at 08:24):

And you can prove this by bare Conway induction on x₁ + x₂ + y + y

Violeta Hernández (May 03 2022 at 08:25):

(this wasn't possible before due to that single application of P1)

Violeta Hernández (May 03 2022 at 09:13):

Checking the induction hypothesis pretty much amounts to verifying a bunch of goals similar to "yL + yR + x1 + x2 is subsequent to x1 + x2 + y + y up to relabelling"

Violeta Hernández (May 03 2022 at 09:15):

If the arguments we're in the correct order, this could be done via solve_by_elim

Violeta Hernández (May 03 2022 at 09:15):

And, if addition were commutative, we could use abel for this

Violeta Hernández (May 03 2022 at 09:15):

Unfortunately we run into the issue of relabellings

Violeta Hernández (May 03 2022 at 09:16):

So I'm thinking, perhaps we should have a type of pregames quotiented by relabelling?

Yaël Dillies (May 03 2022 at 09:16):

How is that different from game?

Violeta Hernández (May 03 2022 at 09:16):

game is a much stronger quotient

Violeta Hernández (May 03 2022 at 09:16):

It quotients by antisymmetry

Violeta Hernández (May 03 2022 at 09:17):

So for instance, the empty pregame { | } and the pregame { 1 | -1 } are equal as games but they're not relabellings of one another

Violeta Hernández (May 03 2022 at 09:21):

Conversely, the empty pregame with move types pempty and the empty pregame with move types fin 0 are relabellings but can't be proven equal

Violeta Hernández (May 03 2022 at 09:21):

And as mentioned, another annoying consequence is that x + y is not always equal to y + x, even though they're relabellings

Yaël Dillies (May 03 2022 at 09:24):

Is x + 0 a relabelling of x?

Violeta Hernández (May 03 2022 at 09:24):

Indeed

Yaël Dillies (May 03 2022 at 09:25):

But a * (b + c) is not a relabelling of a * b + a * c?

Violeta Hernández (May 03 2022 at 09:25):

It also is

Violeta Hernández (May 03 2022 at 09:26):

Pre-games up to relabelling are an abelian monoid with commutative and distributive multiplication

Yaël Dillies (May 03 2022 at 09:26):

What's the strongest you can prove about your relabeled_pgames?

Violeta Hernández (May 03 2022 at 09:26):

They don't have additive nor multiplicative inverses though

Violeta Hernández (May 03 2022 at 09:27):

They don't have a partial order (neither do games), but they have all covariant instances for addition (as do pre-games)

Violeta Hernández (May 03 2022 at 09:28):

Oh and addition and multiplication are also associative

Yaël Dillies (May 03 2022 at 09:28):

That sounds good enough to want another type.

Violeta Hernández (May 03 2022 at 09:28):

Glad you agree

Violeta Hernández (May 03 2022 at 09:28):

What I'm wondering is nomenclature

Yaël Dillies (May 03 2022 at 09:29):

I would go for something short, like rgame.

Violeta Hernández (May 03 2022 at 09:29):

I like that

Yaël Dillies (May 03 2022 at 09:33):

Btw, if you're interested in another refactor, you can redefine game as antisymmetrization (≤) pgame.

Violeta Hernández (May 03 2022 at 09:37):

That might be nice

Violeta Hernández (May 03 2022 at 09:37):

I'll look into that

Violeta Hernández (May 03 2022 at 20:27):

Oh wait

Violeta Hernández (May 03 2022 at 20:27):

Turns out rgame isn't as strong as I thought

Violeta Hernández (May 03 2022 at 20:28):

You can define multiplication, and it satisfies x * 0 = 0, x * 1 = 1, x * y = y * x, and (-x) * y = x * (-y) = -(x * y)

Violeta Hernández (May 03 2022 at 20:28):

But unfortunately it isn't distributive nor associative

Violeta Hernández (May 03 2022 at 20:31):

That said, these properties should still make it much easier to deal with multiplication in games

Violeta Hernández (May 03 2022 at 20:31):

Since you can convert inequalities about game to inequalities about rgame and then use these laws via rw

Violeta Hernández (May 03 2022 at 20:31):

What I don't know is what structure these form

Violeta Hernández (May 03 2022 at 20:32):

I think it's just a unital magma? lol

Eric Rodriguez (May 03 2022 at 20:38):

mul_zero_one_class and has_distrib_neg ;b

Eric Rodriguez (May 03 2022 at 20:38):

old-school names are for old-school people

Eric Rodriguez (May 03 2022 at 20:38):

oh wait, is that distrib_neg? or sth else

Violeta Hernández (May 03 2022 at 20:38):

Oh cool

Violeta Hernández (May 03 2022 at 20:38):

It's good to know that there's names for these in mathlib

Kevin Buzzard (May 03 2022 at 21:27):

I sometimes feel like we just made them to make fun of the people who study hierarchies of algebraic structures

Junyan Xu (May 04 2022 at 03:30):

I wonder if people have thought about defining pgame as the type of types (of positions) equipped with two relations L and R (moves) such that the union of both relations is well-founded, together with the starting position. Seems closer to the definition in ONAG than the current inductive type definition, and would be easier to extend to loopy games by dropping the well-founded condition. Moreover, you should be able to get rid of all the relabelling stuff.(maybe not)
image.png

Michael Stoll (May 04 2022 at 11:29):

I tried something like this (it was my first attempt to work in Lean, so don't be too critical...).

import tactic

namespace games

-- We really would like to do the following:

-- inductive Game : Type
-- | make : set Game → set Game → Game

-- but Lean complains of missing "positivity"...

-- so we have to do it in a more pedestrian way.

-- Define the type of (Conway) games.
constant Game : Type

-- Each game has a set of left and a set of right options,
-- which are themselves games.
constants L_opts R_opts : Game  set Game

-- We can construct a game from its left and right options.
noncomputable constant make : set Game  set Game  Game

notation `[` G1 `|` G2 `]` := make G1 G2

-- The following state that these two operations are inverses.
@[simp] axiom make_LR (G : Game) : make (L_opts G) (R_opts G) = G
@[simp] axiom make_L (GL : set Game) (GR : set Game) : L_opts (make GL GR) = GL
@[simp] axiom make_R (GL : set Game) (GR : set Game) : R_opts (make GL GR) = GR

-- A simple consequence is that it suffices to check equality
-- of the left and right options to conclude that two games are the same.
theorem eq_LR (G1 G2 : Game) : G1 = G2  L_opts G1 = L_opts G2  R_opts G1 = R_opts G2 :=
begin
  split,
    tauto,
  intro hyp,
  rw [ make_LR G1,  make_LR G2], cases hyp with h1 h2,
  rw [h1, h2],
end

-- For applications in proofs, this implication is useful.
theorem eq_of_eq_LR (G1 G2 : Game) : L_opts G1 = L_opts G2  R_opts G1 = R_opts G2  G1 = G2 :=
begin
  rw eq_LR, tauto,
end

-- We define the notion of "option" of a game.
def is_option (G' : Game) (G : Game) : Prop :=
  G'  L_opts G  G'  R_opts G

-- We note that the elements of `L_opts G` and of `R_opts G` are options of `G`.
lemma L_is_option (G : Game) (GL : Game) : GL  L_opts G  is_option GL G :=
begin
  intro h, rw is_option, left, assumption,
end

lemma R_is_option (G : Game) (GR : Game) : GR  R_opts G  is_option GR G :=
begin
  intro h, rw is_option, right, assumption,
end

--------------------------------------------------------------------
-- The induction principle
--------------------------------------------------------------------

-- This is the "descending game condition":
-- the relation "is option of" is well-founded.
axiom DGC : well_founded is_option

-- This gives us Conway induction for statements involving a single game.
theorem Conway_ind_opt (P : Game  Prop) (hyp :  G : Game, ( G', is_option G' G   P G')  P G) :  G : Game, P G :=
begin
  apply DGC.fix hyp,
end

-- A variant that inducts explicitly on the left and right options.
theorem Conway_ind_LR (P : Game  Prop) (hyp :  G : Game, (( GL  L_opts G, P GL)  ( GR  R_opts G, P GR))  P G) :  G : Game, P G :=
begin
  apply Conway_ind_opt,
  intros G hyp1,
  apply hyp G,
  split,
    intros GL GLG,
    apply hyp1 GL,
    rw is_option, left, assumption,
  intros GR GRG,
  apply hyp1 GR,
  rw is_option, right, assumption,
end


-- Now repeat for pairs of games, when we replace one or both of them by an option.
def is_option_2 (GG1 : Game × Game) (GG : Game × Game) : Prop :=
  (GG1.fst = GG.fst  is_option GG1.snd GG.snd)
    (is_option GG1.fst GG.fst  GG1.snd = GG.snd)
    (is_option GG1.fst GG.fst  is_option GG1.snd GG.snd)

def L_opts1_is_option_2 (GL : Game) (GG : Game × Game) : GL  L_opts GG.fst  is_option_2 (GL, GG.snd) GG :=
begin
  intro hyp, unfold is_option_2, right, left, simp, apply L_is_option, assumption,
end

def R_opts1_is_option_2 (GR : Game) (GG : Game × Game) : GR  R_opts GG.fst  is_option_2 (GR, GG.snd) GG :=
begin
  intro hyp, unfold is_option_2, right, left, simp, apply R_is_option, assumption,
end

def L_opts2_is_option_2 (GL : Game) (GG : Game × Game) : GL  L_opts GG.snd  is_option_2 (GG.fst, GL) GG :=
begin
  intro hyp, unfold is_option_2, left, simp, apply L_is_option, assumption,
end

def R_opts2_is_option_2 (GR : Game) (GG : Game × Game) : GR  R_opts GG.snd  is_option_2 (GG.fst, GR) GG :=
begin
  intro hyp, unfold is_option_2, left, simp, apply R_is_option, assumption,
end

-- Note that this is a subrelation of the lexicographic product.
lemma is_option_2_sub_lex : subrelation is_option_2 (prod.lex is_option is_option) :=
begin
  unfold subrelation,
  intros GG1 GG2 hyp, refine (prod.lex_def is_option is_option).mpr _,
  unfold is_option_2 at hyp, tauto,
end

-- Then we can use existing machinery to deduce that this is well-founded.
lemma DGC_2 : well_founded is_option_2 :=
begin
  apply subrelation.wf is_option_2_sub_lex _,
  apply prod.lex_wf DGC DGC,
end

-- ... and derive the induction principle.
theorem Conway_ind_opt_2 (P : Game × Game  Prop) (hyp :  G : Game × Game, ( GG, is_option_2 GG G   P GG)  P G) :  G : Game × Game, P G :=
begin
  apply DGC_2.fix hyp,
end

 (etc.)

It was not clear to me, however, how to show in Lean that a structure exists that satisfies the axioms.

Junyan Xu (May 04 2022 at 12:36):

I'll take a closer look at your code when I have more time. Here's an attempt I started last night, where I constructed a game from the smaller games after left/right moves, but am yet to prove well-foundedness. Negation of a game is obtained by switching L and R, and sum of two games have pos defined to be the product of the two pos; I've yet to think about multiplication (of surreals). (corrected to add the starting position in structure pgame.)

import tactic

universes u v

structure pgame :=
(pos : Type u)
(L R : pos  pos  Prop)
(wf : well_founded $ λ x y, L x y  R x y)
(start : pos)

namespace pgame
open sum

/- special case of sigma.lex -/
inductive sigma_disjoint {α : Type u} {T : α  Type v}
  (r : Π a, T a  T a  Prop) : (Σ a, T a)  (Σ a, T a)  Prop
| mk :  a x y, r a x y  sigma_disjoint a,x a,y

/-- construct a game from left/right options -/
def of_L_R {α β : Type u} (L : α  pgame.{u}) (R : β  pgame.{u}) : pgame.{u} :=
{ pos := ((Σ a, (L a).pos)  Σ b, (R b).pos)  unit,
  start := inr (),
  L := λ x y, match x, y with
    | inl (inl a, p⟩), inr _ := p = (L a).start
    | inl (inl p₁), inl (inl p₂) := sigma_disjoint (λ a, (L a).L) p₁ p₂
    | inl (inr p₁), inl (inr p₂) := sigma_disjoint (λ b, (R b).L) p₁ p₂
    | _, _ := false
    end,
  R := λ x y, match x, y with
    | inl (inr b, p⟩), inr _ := p = (R b).start
    | inl (inl p₁), inl (inl p₂) := sigma_disjoint (λ a, (L a).R) p₁ p₂
    | inl (inr p₁), inl (inr p₂) := sigma_disjoint (λ b, (R b).R) p₁ p₂
    | _, _ := false
    end,
  wf := λ p, begin
    sorry,
  end ⟩}

def neg (g : pgame.{u}) : pgame.{u} :=
{ pos := g.pos,
  L := g.R,
  R := g.L,
  wf := by { convert g.wf, ext, exact or.comm },
  start := g.start }

inductive game_sum_rel {α β : Type u} (r₁ : α  α  Prop) (r₂ : β  β  Prop) :
  α × β  α × β  Prop
| fst :  a a' b, r₁ a a'  game_sum_rel (a,b) (a',b)
| snd :  a b b', r₂ b b'  game_sum_rel (a,b) (a,b')

def add (g₁ : pgame.{u}) (g₂ : pgame.{u}) : pgame.{u} :=
{ pos := g₁.pos × g₂.pos,
  L := game_sum_rel g₁.L g₂.L,
  R := game_sum_rel g₁.R g₂.R,
  wf := sorry,
  start := (g₁.start, g₂.start) }

end pgame

Michael Stoll (May 04 2022 at 15:05):

Here is another snippet from my attempt:

-- Define the four simplest games.
noncomputable def G0 : Game := [ {} | {} ]
noncomputable def G1 : Game := [ {G0} | {} ]
noncomputable def Gn1 : Game := [ {} | {G0} ]
noncomputable def Gstar : Game := [ {G0} | {G0} ]

-- Note that `Game` is inhabited.
noncomputable instance Game_inhabited : inhabited Game :=  G0 

-- Define negation of games.
-- We use well-founded recursion for that.
-- Ideally, we want to have
--   `make {f GR (R_is_option G GR h) GR | h : GR ∈ R_opts G} {f GL ... | GL ∈ L_opts G}` ,
-- but `{f x | x ...}` is not legal set syntax.

-- This describes the recursive construction:
-- we assume that `f G'` gives `-G'` for the options `G'` of `G` and construct `-G` out of these values.
noncomputable def neg_help (G : Game) (f : Π G1, is_option G1 G  Game) : Game :=
  make {GNL : Game |  (GR : Game) (h : GR  R_opts G), GNL = f GR (R_is_option G GR h)}
            {GNR : Game |  (GL : Game) (h : GL  L_opts G), GNR = f GL (L_is_option G GL h)}
-- Now apply the fixed point operator for the recursion.
noncomputable def neg (G :Game) : Game :=
  DGC.fix neg_help G

--noncomputable instance Game_has_neg : has_neg Game := ⟨ neg ⟩

notation `-` G := neg G

-- The following basically establish rewrite rules.
@[simp] lemma L_opts_of_neg (G : Game) : L_opts (-G) = {GRN : Game |  GR  (R_opts G), GRN = -GR} :=
begin
  unfold neg, rw DGC.fix_eq, unfold neg_help, simp,
end

@[simp] lemma R_opts_of_neg (G : Game) : R_opts (-G) = {GLN : Game |  GL  (L_opts G), GLN = -GL} :=
begin
  unfold neg, rw DGC.fix_eq, unfold neg_help, simp,
end

-- Show that `-(-G) = G`.
@[simp] theorem neg_neg (G : Game) : -(-G) = G :=
begin
  revert G,
  -- We use Conway induction for this.
  have ind := Conway_ind_opt (λ G, -(-G) = G), simp at ind,
  apply ind,
  intros G hyp,
  -- It is sufficient to show that the sets of left/right options agree.
  rw eq_LR, split,
    simp, refine set.ext _, intro G1, simp, split,
      intro hyp1, cases hyp1 with GLN hyp1, cases hyp1 with hyp1 hyp2, cases hyp1 with GL hyp1,
      cases hyp1 with hyp11 hyp12,
      have h := hyp GL (L_is_option G GL hyp11),
      rw [hyp2, hyp12, h],
      assumption,
    intro h1, have h := hyp G1 (L_is_option G G1 h1),
    use -G1,
    split,
      use G1, split, assumption, refl,
    rw h,

    simp, refine set.ext _, intro G1, simp, split,
      intro hyp1, cases hyp1 with GRN hyp1, cases hyp1 with hyp1 hyp2, cases hyp1 with GR hyp1,
      cases hyp1 with hyp11 hyp12,
      have h := hyp GR (R_is_option G GR hyp11),
      rw [hyp2, hyp12, h],
      assumption,
    intro h1, have h := hyp G1 (R_is_option G G1 h1),
    use -G1,
    split,
      use G1, split, assumption, refl,
    rw h,
end

-- We get the fact that negation is injective.
lemma neg_inj (G1 G2 : Game) : -G1 = -G2  G1 = G2 :=
begin
  intro hyp,
  rw  (neg_neg G1), rw  (neg_neg G2), rw hyp,
end

-- Also, `G₁ = -G₂` implies `G₂ = -G₁`.
lemma neg_swap (G₁ G₂ : Game) : G₁ = -G₂  G₂ = -G₁ :=
begin
  intro h, rw  neg_neg G₂, apply eq.symm, rw h,
end

-- Some simple statements on the simplest games and negation.
lemma G0_neg : -G0 = G0 :=
begin
  rw (eq_LR (-G0) G0), unfold G0, simp,
end

lemma G1_neg : -G1 = Gn1 :=
begin
  rw (eq_LR (-G1) Gn1), unfold Gn1, unfold G1, simp, apply G0_neg,
end

lemma Gstar_neg : -Gstar = Gstar :=
begin
  rw (eq_LR (-Gstar) Gstar), unfold Gstar, simp, apply G0_neg,
end

-- Now we do addition.
-- To use Conway induction on pairs of games,
-- we need to work with the product type.
noncomputable def add_help (GG : Game × Game) (f : Π (GG1 : Game × Game), is_option_2 GG1 GG  Game) : Game :=
  make {GAL : Game | ( (GL : Game) (h : GL  L_opts GG.fst), GAL = f (GL, GG.snd) (L_opts1_is_option_2 GL GG h))
                           ( (GL : Game) (h : GL  L_opts GG.snd), GAL = f (GG.fst, GL) (L_opts2_is_option_2 GL GG h))}
       {GAR : Game | ( (GR : Game) (h : GR  R_opts GG.fst), GAR = f (GR, GG.snd) (R_opts1_is_option_2 GR GG h))
                           ( (GR : Game) (h : GR  R_opts GG.snd), GAR = f (GG.fst, GR) (R_opts2_is_option_2 GR GG h))}

noncomputable def add_pair (GG : Game × Game) : Game :=
  DGC_2.fix add_help GG

-- Translate  `Game × Game → Game`  into  `Game → Game → Game` .
noncomputable def add (G1 G2 : Game) : Game :=  add_pair (G1, G2)

--noncomputable Game_has_add : has_add Game := ⟨ add ⟩

notation G1 ` + ` G2 := add G1 G2

-- Lemmas that can be used for simplification.
@[simp] lemma L_opts_of_add (G1 G2 : Game) : L_opts (G1 + G2) =
  {GAL : Game | ( (GL : Game) (h : GL  L_opts G1), GAL = GL + G2)
                 ( (GL : Game) (h : GL  L_opts G2), GAL = G1 + GL)} :=
begin
  unfold add, unfold add_pair, rw DGC_2.fix_eq, unfold add_help, simp,
end

@[simp] lemma R_opts_of_add (G1 G2 : Game) : R_opts (G1 + G2) =
  {GAR : Game | ( (GR : Game) (h : GR  R_opts G1), GAR = GR + G2)
                 ( (GR : Game) (h : GR  R_opts G2), GAR = G1 + GR)} :=
begin
  unfold add, unfold add_pair, rw DGC_2.fix_eq, unfold add_help, simp,
end

example : G0 + G0 = G0 :=
begin
  rw eq_LR, unfold G0, simp,
end

(then I go on to prove facts on addition).
The proofs can probably be considerably simplified...

Violeta Hernández (May 04 2022 at 17:30):

I don't believe defining games in terms of sets of games works in type theory

Violeta Hernández (May 04 2022 at 17:31):

Otherwise you could define a game whose left set is the set of all games

Violeta Hernández (May 04 2022 at 17:31):

And that's an absurd

Violeta Hernández (May 04 2022 at 17:32):

Mathlib already has pre-games, and it defines them via type indexed families of pre-games

Violeta Hernández (May 04 2022 at 17:33):

Crucially the indexing types must belong to universe u, while the type of pre-games is in universe u+1, which avoids the contradiction

Violeta Hernández (May 04 2022 at 17:34):

This has the annoyance that pre-games with equal left and right sets of games can still be different if the indexing types or the indexing itself are different, but I'm currently defining tbe quotient of pre-games by extensionality to overcome this

Mario Carneiro (May 04 2022 at 17:37):

In surreals you have an equivalence of recursive equivalence like "forall a, exists b, A_left a <= B_left b" to deal with this, and pSet has something similar - src#pSet.equiv

Violeta Hernández (May 04 2022 at 17:37):

Pre-games already have a notion of a relabelling

Mario Carneiro (May 04 2022 at 17:38):

I assume relabelling is a bit too strict of an equivalence for games though

Violeta Hernández (May 04 2022 at 17:38):

And existence of a relabelling almost corresponds to ZFC equality of pre-games

Violeta Hernández (May 04 2022 at 17:38):

Not quite because it assumes that you have multisets instead of sets

Violeta Hernández (May 04 2022 at 17:38):

Of course, it's too strict for games

Violeta Hernández (May 04 2022 at 17:39):

That's a quotient by antisymmetry

Violeta Hernández (May 04 2022 at 17:39):

Not just structural equality

Mario Carneiro (May 04 2022 at 17:39):

for pSet it's also not quite relabeling, because it needs to be relabeling where after the relabeling the elements are equivalent

Mario Carneiro (May 04 2022 at 17:39):

or maybe that's part of relabeling

Mario Carneiro (May 04 2022 at 17:40):

I suppose pre-games don't have any quotient on them?

Mario Carneiro (May 04 2022 at 17:40):

that's what makes them pre-games

Michael Stoll (May 04 2022 at 17:44):

Violeta Hernández said:

I don't believe defining games in terms of sets of games works in type theory

That's the problem I had; this is why I resorted to axioms and constants to describe the set-up in a way that was as close as possible to Conway's definition.
Note that I wrote this code before I was aware that combinatorial games are present in mathlib. (And before I had any experience with Lean to speak of.)

Mario Carneiro (May 04 2022 at 17:44):

Regarding @Junyan Xu 's point about loopy games, I believe the correct approach is to use a coinductive type instead of an inductive type but otherwise with the same definition. Because we would want to consider the A -> A and B -> B and A -> B -> A all be equivalent loopy games (there is only one infinite line of play)

Violeta Hernández (May 04 2022 at 17:46):

Junyan Xu said:

Moreover, you should be able to get rid of all the relabelling stuff.(maybe not)
image.png

I don't think that would get rid of the relabelling stuff. If you have a type of positions, then it stands that you can have two games that are exactly the same except they use a different type.

Mario Carneiro (May 04 2022 at 17:47):

@Michael Stoll using sets of games leads to the same paradoxes in set theory too. Well-foundedness is an essential component, and I believe Conway uses an informal description of transfinite induction in ONAG

Mario Carneiro (May 04 2022 at 17:47):

using inductive types is by far the easiest way to do this and get the right answer in CIC

Violeta Hernández (May 04 2022 at 17:47):

Doesn't set theory circumvent this by having games be a proper class?

Michael Stoll (May 04 2022 at 17:48):

In a way, Conway defines games as sets with two kinds of elements.

Michael Stoll (May 04 2022 at 17:49):

And well-foundedness is sort of built-in ("... all games arise in this way"); alternatively, it is equivalent to the induction principle for games.

Michael Stoll (May 04 2022 at 17:51):

If one wants to build the theory within set theory, then the games are not a set, but a proper class (which is not a problem).

Junyan Xu (May 04 2022 at 18:32):

An element of set α in Lean is actually a predicate, which may be an actual set or a proper class, if α is in a higher universe; for example when α is pgame.{u} : Type (u+1), s : set α is small (an "actual set" in Type u) if subtype s is equipotent to some type in Type u, and a proper class otherwise.

Conway defines games/numbers like how you define the von Neumann universe or constructible universe in set theory, implicitly using transfinite induction; at each step you can only use games/numbers you already constructed. In Lean, this smallness is guaranteed by using indexing types in Type u for left and right options.

The point of my new definition is: if we have to use indexing types, why not use a single indexing type for all positions? It also makes some definitions simpler, e.g. negation, and impartial games would simply be those with L=R.

I don't think we need force isomorphic games be equal using coinductive types or something like Aczel's anti-foundation axiom; there are many different types of games as you can see in Winning Ways and it's easy to find coincidences where the games are CGT equivalent or even the game graphs are the same, but they don't have to be the same pgame; it would be enough to prove by induction that you can substitute one with the other without changing the CGT value (equivalence class) of the whole game.

Mario Carneiro (May 04 2022 at 18:37):

Describing what it means for a game to be well founded is approximately as complicated as defining pgame in the first place though

Mario Carneiro (May 04 2022 at 18:37):

you basically have to say that a game has a pgame that surjects onto it in a certain sense

Mario Carneiro (May 04 2022 at 18:39):

I think that your general concept of game-graphs are useful but distinct from either the inductive or coinductive concept of pgame

Mario Carneiro (May 04 2022 at 18:41):

You need the coinductive analogue of game to provide the values of loopy games and game-graphs

Junyan Xu (May 04 2022 at 18:43):

Mario Carneiro said:

Describing what it means for a game to be well founded is approximately as complicated as defining pgame in the first place though

Is this complicated, or problematic, or you mean something else?

universes u v

structure pgame :=
(pos : Type u)
(L R : pos  pos  Prop) /- L x y means x is a option at position y for left -/
(wf : well_founded $ λ x y, L x y  R x y)
(start : pos)

Mario Carneiro (May 04 2022 at 18:47):

src#pgame

Mario Carneiro (May 04 2022 at 18:47):

I think it's more complicated

Mario Carneiro (May 04 2022 at 18:48):

the fact that you can't pattern match on it and have to carry start around seems like it will make proofs harder

Mario Carneiro (May 04 2022 at 18:48):

for graphs you usually don't designate a start vertex in the structure itself

Mario Carneiro (May 04 2022 at 18:49):

Also, you might not want to assume the whole type is well founded, only the elements under start

Mario Carneiro (May 04 2022 at 18:49):

that is acc (λ x y, L x y ∨ R x y) start

Junyan Xu (May 04 2022 at 18:50):

You can remove unreachable elements and get an equivalent game.

Mario Carneiro (May 04 2022 at 18:50):

sure, but those kinds of arguments are messy in lean

Junyan Xu (May 04 2022 at 18:50):

I'll definitely try to replicate current development in mathlib to see how easy my definition is to work with.

Junyan Xu (May 04 2022 at 18:51):

You may try the two sorrys in my code posted above, I won't get to them until 6pm (~3 hours later).

Junyan Xu (May 04 2022 at 18:57):

I think docs#well_founded.well_founded_iff_has_min and docs#set.well_founded_on_iff_no_descending_seq will be handy. (The second uses dependent choice but mathlib is fine with it.)

Violeta Hernández (May 04 2022 at 19:46):

I find that Lean generally makes it so that it's harder to use that characterization of well foundedness

Violeta Hernández (May 04 2022 at 19:46):

Usually, if everything is setup right, you can do bare induction and get what you want more easily

Violeta Hernández (May 04 2022 at 19:47):

This is just my opinion though

Junyan Xu (May 05 2022 at 06:04):

I've defined ≤ and showed it's reflexive using the new definition and I'll call it a day; more to come!
https://gist.github.com/alreadydone/cdf39674c4bec9a8f298e43024771621

Violeta Hernández (May 05 2022 at 13:58):

Are you planning for this to replace the current definition in mathlib? Or is this just a side thing?

Violeta Hernández (May 05 2022 at 13:59):

What you're doing seems nice but I feel like refactoring pgame with this would be a ton of work with little (if any) benefit

Junyan Xu (May 05 2022 at 15:34):

I believe my definition is the most natural when dealing with concrete games (that are described by which moves are possible at each position) and loopy games, and realizing addition as juxtaposition of games is really nice. Currently it's in exploration phase, and if the mathlib definition turns out to be superior for some purposes we may work on providing some glues between the two.

Violeta Hernández (May 05 2022 at 18:09):

How would multiplication work? Don't you need well foundedness to define that?

Violeta Hernández (May 05 2022 at 18:09):

Which wouldn't quite work on loopy games

Junyan Xu (May 05 2022 at 18:58):

Yeah, I don't think multiplication is well-behaved even for well-founded games (and there are at least two definitions of ≤ as the loopy games slides show, and there is the misère play convention (first player without move wins) where there isn't a nice notion of ≤). Multiplication won't respect equivalence unless restricted to surreals. I expect of_L_R in my earlier post will be required to define multiplication. I haven't thought about surreals much, but I have done some computations in On₂, the (algebraically closed, etc.) field of characteristic two formed by all ordinals by considering them as impartial games, where multiplication is defined like in the surreal case. I think Winning Ways also defined multiplication for more general games that's well-behaved under certain conditions, but I have to look them up.

Scott Morrison (May 06 2022 at 01:25):

I think it would be fine to have an alternative definition in parallel, if there is appropriate glue.

Reid Barton (May 06 2022 at 01:40):

docs#pgame.state already looks pretty similar

Scott Morrison (May 06 2022 at 02:01):

I'd forgotten about that one, even though the domineering example I built uses it.

Scott Morrison (May 06 2022 at 02:03):

For lots of concrete games the turn bound is easy and convenient, and in practice how you might be proving well-foundedness anyway.

Scott Morrison (May 06 2022 at 02:07):

I think it would be great to have more actual concrete games! Hex, dots-and-boxes, and go would all be lovely (some amount of fitting square pegs in round holes to treat these all as Conway style games, but they could be described other ways, too).

Kevin Buzzard (May 06 2022 at 06:35):

My paper on dots and boxes describes how at a professional level it has little to do with combinatorial game theory, but strings and coins (:= dots and boxes with the normal play rule rather than "most boxes is the winner") would be lovely :-)

Junyan Xu (May 07 2022 at 22:16):

Update: I've shown game is a ordered_add_comm_group under the new definition within 374 lines: https://gist.github.com/alreadydone/cdf39674c4bec9a8f298e43024771621

Junyan Xu (May 11 2022 at 01:58):

Updates: I have been able to show the well-foundedness of the game constructed from left/right options (formerly called of_L_R and now simply called of); see the end of https://gist.github.com/alreadydone/93e25ccda07b0c9c1ef2b40efedf3339. However, at the same time I came up with an explicit construction of a type of positions for the product of two games (has anyone written about it before?) and plan to use it instead of of:

instance : has_mul pgame :=
{ mul := λ g₁ g₂,
  { pos := tree3 (bool × g₁.pos × g₂.pos),
    /- could also potentially be `multiset (bool × g₁.pos × g₂.pos)`; each element
       of the multiset is seen as an expression of the form +x*y or -x*y, where x is a position in g₁ and y a position in g₂. -/
    L := closure (mul_rel tt g₁.L g₂.L  mul_rel ff g₁.L g₂.R 
                  mul_rel ff g₁.R g₂.L  mul_rel tt g₁.R g₂.R),
    R := closure (mul_rel ff g₁.L g₂.L  mul_rel tt g₁.L g₂.R 
                  mul_rel tt g₁.R g₂.L  mul_rel ff g₁.R g₂.R),
    wf := sorry,
    start := (tt, g₁.start, g₂.start) } }

See the beginning of the gist above for the full code.

I like the symmetric form of the definition of L and R fields. It works for surreal multiplication, but presumably not for multiplying a positive game with an arbitrary game, which I'll worry about later. (I'm currently experimenting with jux : (fin 3 → tree3) → tree3 instead of jux : tree3 → tree3 → tree3 → tree3.)

To fill in the sorry above, I basically need to prove the termination of a version of the hydra game; basically, the options of the product of two games are each a sum of three products, that is to say if you cut a head of the hydra, it grows back three heads at some lower levels ("less than" the original product w.r.t. the < relation that is the transitive closure of (sgn, xL/xR, y), (sgn, x, yL/yR) < (sgn', x, y)). I realized I need some form of Kőnig's lemma and Googled with the keywords "Kőnig hydra", and this MO problem came up. After browsing the answers, I found that @Peter LeFanu Lumsdaine 's approach seems simpler than setting up the stage to apply Kőnig's lemma, so that's what I'm gonna formalize now. The statement is as below (with multiset instead of list); it doesn't seem to show up when defining multiplication via Conway induction as done in mathlib.

import data.multiset.basic

variables {α : Type*} [decidable_eq α] (r : α  α  Prop)

/-- Cut one head `a` of a hydra `s`, and it grows back an arbitrary finite number of
  heads at "lower levels" than the head cut. -/
inductive cut_rel : multiset α  multiset α  Prop
| cut {s t : multiset α} (a  s) : ( a'  t, r a' a)  cut_rel (s.erase a + t) s

theorem well_founded.cut_rel (hr : well_founded r) : well_founded (cut_rel r) := sorry

Since the line count is growing large, I'll probably split this project into multiple files and create a folder (like set_theory/game_new) in mathlib to contain it and create a branch on github. If people find it easier to work with this new definition, we may continue to add materials to that folder and gradually deprecate the old one.

Violeta Hernández (May 11 2022 at 03:33):

I'm still really iffy on this. The way I see it, your approach has the following pros and cons:

Pros:

  • Allows a natural extension to loopy games.

Cons:

  • The induction arguments become considerably harder.
  • Much worse definitional equalities (since you force a game and all subsequent moves to be part of the same graph).
  • The notion of "relabellings" becomes much more complicated.
  • The refactor would take a large amount of work, and to me it's not even clear that everything would work out nicely.
  • This deviates from Conway's approach even more radically than our current approach (and supposedly, that's what we're trying to formalize).

Call it force of habit, but I'd rather stick to what we have unless there's other substantial reasons to switch, or unless there's a substantial theory on loopy games that we can formalize using your definition.

Violeta Hernández (May 11 2022 at 03:38):

To give a basic example of something that would break in a way that's hard to fix: ordinal.to_pgame. Currently, a lot of the results about it induct on the ordinal, which is possible because left moves of o.to_pgame are exactly a.to_pgame for a < o. With your definition, this wouldn't be the case, so you'd need to consider relabellings in pretty much all of the proofs.

Violeta Hernández (May 11 2022 at 03:38):

Pretty much this same reasoning extends to nim.

Junyan Xu (May 11 2022 at 04:44):

set_theory.game.ordinal is a short file. Would it convince you it's a viable approach if I reproduce the results therein? Your "relabelling" is basically my iso, which I showed preserves Rlose (i.e. 0 ≤ g) in map_iso_Rlose_iff, and use it in the following lemmas to show commutativity, associativity, etc. I later defined a structure iso which is quite simple:

structure iso (g g' : pgame) :=
(pos : g.pos  g'.pos)
(L : inv_image g'.L pos = g.L)
(R : inv_image g'.R pos = g.R)

I'm puzzled by your comment that "induction arguments become considerably harder" and "much worse definitional equalities". Can you elaborate and maybe give an example in my code, or have I not got to the point where they'll be problematic? My experience has been that induction/recursion becomes unnecessary in many places with my approach. It's a one-off thing to prove well-foundedness in various constructions, and I wouldn't call them hard except for multiplication. Contrary to your comment, I think one advantage of my approach is that addition, negation, multiplication, and impartial games now have totally non-inductive definitions (is_numeric can also be defined by quantifying over all positions using the inductively defined Lwin); when defining Rlose/Lwin, I used induction on positions instead of games.

I agree it's a deviation from Conway's approach but I'd argue using indexing types for left/right options is also a deviation, and necessitates relabelling which makes things inelegant (though using a ordinal-indexed union of games born by birthday x would be more inelegant). As I said before, the initial motivation of my definition is : "if we have to use indexing types, why not use a single one?" In general, I think we should choose what works best (like the filter approach to topology, or is_localization in mathlib), which can only be found out through experimentation. It would be great if more people like you are interested to join the effort. I think I currently have all the essential results in game.pgame and game.basic except for mul and inv, and those are my ongoing work. My plan is to gradually work through all files under game and surreal, which seem to contain everything about CGT in mathlib and don't seem that massive, though I think I probably don't have the energy to complete a refactor without other people's interest and help. So I think it's a sensible approach to add another folder, so people can choose whichever approach that suits their purpose when they develop CGT and/or contribute to mathlib. I'd like to keep the same pgame, game etc. namespace (which shouldn't be a problem if you don't import files from both development), but I expect some linters may complain.

Junyan Xu (May 11 2022 at 05:25):

For ordinals or nimbers, we can actually realize them simply as different starting positions in the "same" game, with all ordinals in the universe as positions, L = (<), and R = (<) for nimbers and empty for ordinals; just the start needs to be changed to the ordinal. This is at the expense of elevating the game from pgame.{u} to pgame.{u+1} though. The larger ordinals are unreachable in the game, but this is the flexibility offered by my approach and not a bug (e.g. the extraneous positions also occur in my definition of multiplication).

Scott Morrison (May 11 2022 at 06:21):

I would be interested in seeing a concrete game translated to this formalism, too, e.g. domineering.

Mario Carneiro (May 11 2022 at 07:05):

I think that this approach makes more sense without the wffield

Mario Carneiro (May 11 2022 at 07:06):

The fact that you have to universe-bump is a pretty fundamental difference

Scott Morrison (May 11 2022 at 08:03):

You only have to universe bump if you want to fit all ordinals in, don't you? There'll be some u-small ordinals trick otherwise?

Junyan Xu (May 11 2022 at 08:07):

The current approach uses o.out.α (invokes choice) which seems to be the only option to lower the universe.

Mario Carneiro (May 11 2022 at 09:00):

note that ordinal is rampantly choice-assuming. It would need a very significant rewrite in order to make it not need choice and many theorems would not hold

Mario Carneiro (May 11 2022 at 09:01):

It's usually possible to avoid using o.out but there isn't much gain to be had in doing so because choice already appears ten times in the dependencies

Mario Carneiro (May 11 2022 at 09:10):

Note that the definition of to_pgame is actually computable; the reason for the noncomputable! is because one of the auxiliary definitions lean creates is unexpectedly noncomputable and it causes a weird error message

Mario Carneiro (May 11 2022 at 09:20):

Here's a definition that avoids noncomputable!

Mario Carneiro (May 11 2022 at 09:20):

/-- Converts a well order into the corresponding pre-game. -/
def well_order_to_pgame {α : Type u} [has_well_founded α] : α  pgame.{u}
| a := ⟨{x // has_well_founded.r x a}, pempty,
  λ y, have _, from y.2, well_order_to_pgame y.1, pempty.elim
using_well_founded { dec_tac := tactic.assumption }

/-- Converts an ordinal into the corresponding pre-game. -/
def to_pgame (o : ordinal.{u}) : pgame.{u} :=
o.out.α, pempty, well_order_to_pgame, pempty.elim

David Wärn (May 11 2022 at 14:41):

Note that you can define loopy games in the same style as docs#pgame by writing a coinductive definition instead of an inductive one. Here's a sketch:

import data.pfunctor.univariate.M
import set_theory.game.pgame

universe u

def loopy_game : Type (u+1) := pfunctor.M Type u × Type u, λ p, ulift.{u+1} (p.fst  p.snd)⟩

def pgame.loopy_game : pgame.{u}  loopy_game.{u} :=
pfunctor.M.corec $ λ G, ⟨(G.left_moves, G.right_moves),
                          λ p, sum.elim G.move_left G.move_right p.down

namespace loopy_game

variable (G : loopy_game.{u})

def left_moves : Type u := G.head.fst
def right_moves : Type u := G.head.snd
def move_left (x : left_moves G) : loopy_game := G.children (ulift.up (sum.inl x))
def move_right (y : right_moves G) : loopy_game := G.children (ulift.up (sum.inr y))

inductive is_wf : loopy_game.{u}  Prop
| sup {G : loopy_game} :
  ( x : G.left_moves, is_wf (G.move_left x)) 
  ( y : G.right_moves, is_wf (G.move_right y)) 
  is_wf G

def is_wf.pgame (h : is_wf G) : pgame := h.rec $ λ _ _ _ ls rs, pgame.mk _ _ ls rs

end loopy_game

Probably you could go further and show that pgame is equivalent to the subtype of well-founded loopy games in this language

The code above essentially defines loopy_game to be a terminal coalgebra for a certain polynomial functor. Junyan's definition instead looks at the type of all (well-founded) pointed coalgebras. This makes a lot of sense if we want to construct explicit elements of loopy_game -- normally we define elements of a coinductive type by corecursion, i.e. by constructing explicit coalgebras. (Actually this is analogous to how we deal with bisimulation in mathlib: instead of defining an indexed coinductive prop of bisimulations, we take a bisimulation to be some coalgebra for the same functor.)

I think it's not clear which of these two approaches to loopy games is the best, but for well-founded games I don't understand what the problem with docs#pgame is (other than it being difficult to construct concrete games, but this problem seems to be solved by docs#pgame.state)

Junyan Xu (May 11 2022 at 16:59):

Thanks but I'm yet to read more about pfunctor and coinduction. From my limited understanding about bisimulation, it seems to be a better and more general notion of strong equivalence between games than relabelling. Here strong equivalence means it guarantees substitutability (in sums and as options) in any (loopy or wf) game under both the normal and misere play convention. (If you want to see more theory about loopy and misere games, there are whole chapters in Aaron Siegel's Combinatorial Game Theory.

As evidence of better defeq with my approach, see lines 135-137: neg_zero and neg_neg are propositional equalities in mathlib; with my approach the former is defeq in Lean 3, and the latter (I think) is defeq in Lean 4 due to structure eta being defeq. neg_add is only true up to relabelling in mathlib; with my approach it's also defeq (which is "abused" in add_le_add_iff_Rlose, for example).

Violeta Hernández (May 12 2022 at 03:15):

A bunch of equalities in pgame might become def-eq in Lean 4, but I'm unsure to what extent that will help.

Violeta Hernández (May 12 2022 at 03:29):

It's very interesting that you're able to avoid induction in many circumstances. It's also interesting that you get better definitional equalities in these basic definitions. That speaks to some mathematical elegance in your approach. That said, there are a lot of induction proofs scattered throughout, some quite hard and involved, and I'm still not convinced that your approach can avoid all of them. And there's still the problem of definitional equalities in inductively defined families of games like ordinal.to_pgame and nim.

I very strongly disagree with your approach of bumping the universe of ordinal.to_pgame. This sort of universe bumping can lead to very hard to solve problems later down the line. I'm interested in seeing how the ordinal.lean file would work with your approach without said universe bumping.

I could help with your new definition, but I've yet to see benefits large enough to justify all the work that would be needed for this refactoring. If you only want loopy games, then the coinductive approach seems to be by far the smoothest alternative given the current state of the library.

Violeta Hernández (May 12 2022 at 03:31):

I really don't want to come off as mean, because I can imagine that you've been working for hours on this, and I don't want to give the impression that said work is worthless or uninteresting. I'd just like to more carefully evaluate what's better for mathlib in the short and long run.

Violeta Hernández (May 12 2022 at 03:34):

Also, I'm quite biased given my recent heavy involvement on the mathlib pgame code base. So I'd like to hear opinions from others too.

Violeta Hernández (May 14 2022 at 20:25):

After this < refactor, there's another pretty big refactor I want to do

Violeta Hernández (May 14 2022 at 20:26):

We currently have a lot of congr lemmas for the different relations, and even then, proving inequalities is a total hassle

Violeta Hernández (May 14 2022 at 20:26):

This could be made way easier if we defined game earlier

Violeta Hernández (May 14 2022 at 20:27):

Then, to prove an inequality on pgame, we could simply change it into the def-eq inequality on game, and use a few rws

Junyan Xu (May 15 2022 at 00:24):

My latest thoughts: although I can provide an explicit model for the positions in the multiplication of two games, it doesn't seem to simplify any proofs, unlike in the addition case, so I probably won't open a PR for this approach in the near future. I will continue to explore the approach for concrete games, loopy games, and ordinals, when I have the leisure. I have yet to understand the coinductive approach to loopy games; for example, how would I construct a loopy_game from my pos, L, R and vice versa?

I might soon PR some work related to well-foundedness that has arisen from this endeavour, including the termination of the hydra game, which provides one way to show the well-foundedness of the induction for properties of surreal multiplication in the notes of Schleicher--Stoll; namely, if you consider the arguments of P1(x,y), P2(x1,x2,y) and P4(x,y1,y2) as multisets {x,y}, {x1,x2,y}, and {x,y1,y2}, then each of P1, P2, P4 is deduced from P1, P2, P4 with smaller argument multisets under the cut_expand relation. The proof of termination is actually pretty elegant using acc; interestingly it uses the add_rel for game addition.

def cut_expand {α : Type*} [decidable_eq α]
  (r : α  α  Prop) (s' s : multiset α) : Prop :=
 (t : multiset α) (a  s), ( a'  t, r a' a)  s' = s.erase a + t

Violeta Hernández (May 15 2022 at 01:39):

If your cut_expand approach works, that would be super elegant

Violeta Hernández (May 15 2022 at 01:49):

Well, it might be a bit inconvenient to set up, since P1, P2, P4 are meant to have a fixed amount of arguments

Violeta Hernández (May 15 2022 at 01:49):

So you might need to induct on a generic multiset s and make the inductive hypotheses "if {x, y} is less than s then P1, ..."

Violeta Hernández (May 15 2022 at 04:02):

On the topic of relabellings and extensionality

Violeta Hernández (May 15 2022 at 04:02):

I wonder if relabellings are even useful to begin with

Violeta Hernández (May 15 2022 at 04:03):

Just from reading the definition of docs#pSet.equiv, it seems like the same amount of work to prove two games are relabellings and to prove they're extensionally equal

Violeta Hernández (May 15 2022 at 04:03):

If this is the case, then surely we don't need relabellings at all?

Violeta Hernández (May 15 2022 at 04:07):

On an unrelated note, I hadn't ever noticed just how similar pgame and pSet are

Violeta Hernández (May 15 2022 at 04:08):

pSet can very well be called the unary version of pgame

Violeta Hernández (May 15 2022 at 04:08):

Which I guess is to be expected, ZFC games are constructed almost the same way as ZFC sets, just twice

Violeta Hernández (May 15 2022 at 04:28):

I'm going to try to get rid of relabelling in favor of identical, which actually describes extensionality

Violeta Hernández (May 15 2022 at 04:28):

If proofs become no more complicated than they were before, then to me that suggests that there's no need for relabelling at all

Violeta Hernández (May 15 2022 at 04:33):

This is my idea:

/--
`identical x y` says that `x` and `y` are extensionally the same game. Specifically, for every move
in `x` there's an inductively `identical` move for `y`, and viceversa. Compare to `pSet.equiv`.

In ZFC, identical games would indeed be the same games.
-/
inductive identical : pgame.{u}  pgame.{u}  Type (u+1)
| mk : Π {x y : pgame} (Lxy : x.left_moves  y.left_moves) (Lyx : y.left_moves  x.left_moves)
         (Rxy : x.right_moves  y.right_moves) (Ryx : y.right_moves  x.right_moves),
         ( i, identical (x.move_left i) (y.move_left (Lxy i))) 
         ( i, identical (y.move_left i) (x.move_left (Lyx i))) 
         ( j, identical (x.move_right j) (y.move_right (Rxy j))) 
         ( j, identical (y.move_right j) (x.move_right (Ryx j))) 
       identical x y

Mario Carneiro (May 15 2022 at 04:37):

isn't that the game equivalence relation?

Mario Carneiro (May 15 2022 at 04:38):

game is to pgame as Set is to pSet

Mario Carneiro (May 15 2022 at 04:38):

(the similarity is not a coincidence, pgame was mostly a copy-paste-paste job)

Junyan Xu (May 15 2022 at 04:38):

isn't that the game equivalence relation?

Certainly not. g + -g, which can be arbitrarily complicated, is equivalent to 0 in game

Junyan Xu (May 15 2022 at 04:40):

pSet can very well be called the unary version of pgame
Which I guess is to be expected, ZFC games are constructed almost the same way as ZFC sets, just twice

Yes, that's why I drew a parallel between Conway's construction of games and the von Neumann universe. The axiom of regularity/foundation says the von Neumann universe contains all the sets.

Junyan Xu (May 15 2022 at 04:41):

pSet.equiv is a Prop though, are you sure you want to keep the data in identical?

Mario Carneiro (May 15 2022 at 04:41):

it's also defined by recursion not induction

Mario Carneiro (May 15 2022 at 04:42):

I think this is the difference between docs#function.bijective and docs#equiv; I wouldn't be surprised if both notions are useful

Junyan Xu (May 15 2022 at 04:49):

Then, to prove an inequality on pgame, we could simply change it into the def-eq inequality on game, and use a few rws

As mentioned in #13925, I wonder whether this would be a nice application of the norm_cast (specifically rw_mod_cast) tactic. If that's the case, I think @Mario Carneiro can probably provide some guidance on how to write an "extension" or something for that tactic to apply to pgames.

Mario Carneiro (May 15 2022 at 04:52):

I don't think you need to write an extension or anything; you just mark the relevant theorems with the @[norm_cast] attribute

Violeta Hernández (May 15 2022 at 05:16):

What exactly is restricted supposed to mean?

Violeta Hernández (May 15 2022 at 05:16):

My new definition of identical really resembles it

Violeta Hernández (May 15 2022 at 05:16):

But I don't quite understand the docstring

Violeta Hernández (May 15 2022 at 05:20):

Also, is in use in mathlib? I want to use that for identical

Junyan Xu (May 15 2022 at 05:26):

It means what the docstring says: x has no more left moves and no less right moves than y. If you have fewer moves you are more likely to lose, so it implies x ≤ y: x is worse or equal for left than y.
It's different from your definition because the maps are single sided: (L : x.left_moves → y.left_moves), not the other way around, (R : y.right_moves → x.right_moves), not the other way around.

Junyan Xu (May 15 2022 at 05:37):

From my reading of the paper, to utilize rw_mod_cast for pgame, it seems we need to do the following:

  • Introduce a has_coe instance from pgame to game (seems necessary).
  • write the move lemmas and mark them with @[norm_cast]: the quotient map (the cast) pgame → game respects add/neg/sub/zero
  • write and mark the elim lemmas: < ≤ ⧏ are preserved by the cast, plus ↑a = ↑b ↔ a ≈ b
  • squash lemmas should only be necessary when we have the casts pgame → pgame-mod-identical → game (maybe also for {x // x.numeric} → surreal → game and {x // x.numeric} → pgame → game?)

Violeta Hernández (May 15 2022 at 05:44):

I proved that x ≡ y implies restricted x y (and of course, restricted y x)

Violeta Hernández (May 15 2022 at 05:44):

I wonder if the converse is true?

Violeta Hernández (May 15 2022 at 05:44):

That is, restricted x y and restricted y x imply x ≡ y

Violeta Hernández (May 15 2022 at 06:08):

I have just produced one of the goofiest proofs ever

/-- If `w` is identical to `x` and `y` is identical to `z`, then `w + y` is identical to `x + z`. -/
theorem identical.add_congr :  {w x y z : pgame.{u}}, w  x  y  z  w + y  x + z
| wl, wr, wL, wR xl, xr, xL, xR yl, yr, yL, yR zl, zr, zL, zR
  Lwx, Lxw, Rwx, Rxw, HLwx, HLxw, HRwx, HRxw Lyz, Lzy, Ryz, Rzy, HLyz, HLzy, HRyz, HRzy :=
begin
  let Hwx : wl, wr, wL, wR  xl, xr, xL, xR := Lwx, Lxw, Rwx, Rxw, HLwx, HLxw, HRwx, HRxw⟩,
  let Hyz : yl, yr, yL, yR  zl, zr, zL, zR := Lyz, Lzy, Ryz, Rzy, HLyz, HLzy, HRyz, HRzy⟩,
  fsplit;
  rintro (i | i),
  { exact sum.inl (Lwx i) },
  { exact sum.inr (Lyz i) },
  { exact sum.inl (Lxw i) },
  { exact sum.inr (Lzy i) },
  { exact sum.inl (Rwx i) },
  { exact sum.inr (Ryz i) },
  { exact sum.inl (Rxw i) },
  { exact sum.inr (Rzy i) },
  all_goals {dsimp},
  { exact (HLwx i).add_congr Hyz },
  { exact Hwx.add_congr (HLyz i) },
  { exact (HLxw i).add_congr Hyz },
  { exact Hwx.add_congr (HLzy i) },
  { exact (HRwx i).add_congr Hyz },
  { exact Hwx.add_congr (HRyz i) },
  { exact (HRxw i).add_congr Hyz },
  { exact Hwx.add_congr (HRzy i) }
end
using_well_founded { dec_tac := pgame_wf_tac }

Violeta Hernández (May 15 2022 at 06:10):

This is the only proof so far that has been somewhat longer than the previous proof about relabelling, and even then, I'd argue this proof is clearer than the original one

Violeta Hernández (May 15 2022 at 06:11):

(btw, I reordered some arguments in identical which made some proofs quite easier)

Violeta Hernández (May 15 2022 at 06:24):

Yeah, I think I was right! Proving that things are identical is pretty much as easy / hard as proving they're relabellings

Violeta Hernández (May 15 2022 at 06:25):

And in fact, I was even able to golf a bunch of proofs

Junyan Xu (May 15 2022 at 06:26):

Violeta Hernández said:

That is, restricted x y and restricted y x imply x ≡ y

It seems x = {1,0| }, y = {1| } is a counterexample.

Violeta Hernández (May 15 2022 at 07:06):

Hmm... it seems like there is one theorem in mathlib that is no longer true with identical but was with relabelling

Violeta Hernández (May 15 2022 at 07:06):

docs#pgame.short_of_relabelling

Violeta Hernández (May 15 2022 at 07:06):

This isn't currently used anywhere, though

Violeta Hernández (May 15 2022 at 07:09):

What should be done here?

Violeta Hernández (May 15 2022 at 07:09):

I do think that there's little case in keeping relabelling around if we're going to have identical... but maybe there's some case

Violeta Hernández (May 15 2022 at 07:15):

Violeta Hernández said:

This isn't currently used anywhere, though

Oh, it's actually used somewhere

Violeta Hernández (May 15 2022 at 07:16):

Hmm

Violeta Hernández (May 15 2022 at 07:17):

Being a relabelling is a strictly stronger notion than being identical, so maybe what we should have is a theorem showing one implies the other

Violeta Hernández (May 15 2022 at 07:21):

Would be nice to have another symbol for the relabelling relation

Violeta Hernández (May 15 2022 at 07:23):

Maybe ≡r?

Scott Morrison (May 15 2022 at 07:32):

Is it used to show domineering is a short game? I forget.

Violeta Hernández (May 15 2022 at 07:37):

It's used in some theorems about short games, yeah

Violeta Hernández (May 15 2022 at 07:37):

I guess that for those, it's important to provide an actual bijection between moves

Mario Carneiro (May 15 2022 at 08:00):

One way to recover that theorem is to define something like "essentially short" which means that from each position there are a finite number of equivalence classes up to identical

Mario Carneiro (May 15 2022 at 08:03):

note that a game is essentially short iff it is identical to a short game, and if you take that as the definition then the theorem becomes almost tautologically true

Violeta Hernández (May 15 2022 at 08:06):

I think the question we should be asking is: should something like {0, 0, 0, ... | }with infinitely many zeros be short?

Mario Carneiro (May 15 2022 at 08:07):

it is essentially short by my definition

Violeta Hernández (May 15 2022 at 08:07):

This would probably break all of the computability on short games, though

Mario Carneiro (May 15 2022 at 08:07):

how so

Mario Carneiro (May 15 2022 at 08:08):

especially if essentially short came with an actual finset of equivalence class representatives

Mario Carneiro (May 15 2022 at 08:08):

actually make that a list or multiset, there is no point in distinctness here

Violeta Hernández (May 15 2022 at 08:09):

Hmm, that might work

Violeta Hernández (May 15 2022 at 08:10):

So, to be clear, we do want to replace relabelling by identical, right?

Mario Carneiro (May 15 2022 at 08:12):

I don't see why not have both, but making identical be the load bearing one seems good to me

Violeta Hernández (May 15 2022 at 08:13):

...yeah, actually, it might be a better idea to just have both

Violeta Hernández (May 15 2022 at 08:14):

I do think we would need to mix them in the API, though

Violeta Hernández (May 15 2022 at 08:14):

When possible, have identical in the hypotheses, and relabelling in the conclusions

Mario Carneiro (May 15 2022 at 08:14):

well one implies the other so that seems fine

Violeta Hernández (May 15 2022 at 08:19):

I now have another problem

Violeta Hernández (May 15 2022 at 08:19):

I attempted to make relabelling into a Prop

Kevin Buzzard (May 15 2022 at 08:19):

When making API for nnreal it's certainly handy to have a \ne 0 in the hypotheses and 0 < a in the conclusions because of dot notation and docs#has_lt.lt.ne' , one could play the same trick here

Violeta Hernández (May 15 2022 at 08:20):

Which to me makes a lot of sense: relabelling should be an equivalence relation, and it's not like we ever use the data

Violeta Hernández (May 15 2022 at 08:21):

...except, we actually do use the data

Violeta Hernández (May 15 2022 at 08:21):

docs#pgame.short_of_relabelling

Mario Carneiro (May 15 2022 at 08:21):

you said something about computability? I think we do use the data

Violeta Hernández (May 15 2022 at 08:22):

There's no way to avoid this, is there? Unless we were to make short games noncomputable

Mario Carneiro (May 15 2022 at 08:22):

certainly if identical is data and you have identical_of_relabelling you will need the data

Violeta Hernández (May 15 2022 at 08:22):

I didn't mean for identical to be data either

Mario Carneiro (May 15 2022 at 08:22):

like I said earlier this is like equiv vs bijective

Mario Carneiro (May 15 2022 at 08:22):

guess which one sees more use in mathlib?

Violeta Hernández (May 15 2022 at 08:23):

I don't think this is quite the same situation

Violeta Hernández (May 15 2022 at 08:23):

Under normal circumstances, we really don't have any use for the data in a relabelling

Violeta Hernández (May 15 2022 at 08:23):

We can't "apply" a relabelling like we can an equivalence

Mario Carneiro (May 15 2022 at 08:24):

why not?

Mario Carneiro (May 15 2022 at 08:24):

you can take a strategy for one game and make a strategy for the other, or other things like that

Violeta Hernández (May 15 2022 at 08:24):

That's just the relabelling itself

Violeta Hernández (May 15 2022 at 08:24):

You're not applying it to anything

Violeta Hernández (May 15 2022 at 08:25):

You might suppose you can apply a relabelling between a and b to a subsequent game of a, but even if this is possible we have no API whatsoever for this

Mario Carneiro (May 15 2022 at 08:25):

if you have a play sequence on one game you can apply it to get a play sequence on the other

Mario Carneiro (May 15 2022 at 08:26):

I think just about any data "about" a game can be remapped across the relabelling

Mario Carneiro (May 15 2022 at 08:26):

so it really does look like equiv to me

Violeta Hernández (May 15 2022 at 08:26):

Hmm... you're right

Violeta Hernández (May 15 2022 at 08:26):

We currently have no API for this at all, but it's certainly something that could at some point be worked on

Mario Carneiro (May 15 2022 at 08:26):

that all mostly applies to identical as well

Violeta Hernández (May 15 2022 at 08:27):

Alright, I see why this should be data

Violeta Hernández (May 15 2022 at 08:36):

Before trying to do any of the identical stuff, I've opened #14155 to add the ≡rnotation for relabellings and clean up some proofs

Violeta Hernández (May 15 2022 at 08:57):

I should probably outline what I'm getting at with all this

Violeta Hernández (May 15 2022 at 09:00):

The idea is to first define this identical relation. Then, we define the quotient of games by this relation and prove that negation, addition, and multiplication (?) can be lifted to this quotient. This should save us from the pain of congr lemmas when proving identities or relabellings.

Violeta Hernández (May 15 2022 at 09:01):

Note that game can't do this, as the relabelling relation doesn't lift to it

Violeta Hernández (May 15 2022 at 09:07):

Hmmm... wait. If I really want to do this, then I actually need to define two quotients, one for relabelings and one for identical games

Violeta Hernández (May 15 2022 at 09:08):

This feels excessive and redundant, but it also seems like the easiest way to work with these two relations

Junyan Xu (May 15 2022 at 19:53):

Violeta Hernández said:

So you might need to induct on a generic multiset s and make the inductive hypotheses "if {x, y} is less than s then P1, ..."

I'm currently using your mul_args:

def cut_expand {α : Type*} [decidable_eq α] (r : α  α  Prop) (s' s : multiset α) : Prop :=
 (t : multiset α) (a  s), ( a'  t, r a' a)  s' = s.erase a + t

inductive mul_args : Type (u+1)
| P1 (x y : pgame.{u}) : mul_args
| P24 (x₁ x₂ y : pgame.{u}) : mul_args

def to_multiset : mul_args  multiset pgame
| (mul_args.P1 x y) := {x, y}
| (mul_args.P24 x₁ x₂ y) := {x₁, x₂, y}

def hyp : mul_args  Prop
| (mul_args.P1 x y) := numeric x  numeric y  numeric (x * y)
| (mul_args.P24 x₁ x₂ y) := numeric x₁  numeric x₂  numeric y  P24 x₁ x₂ y

def ces := by classical; exact inv_image (cut_expand subsequent) to_multiset

theorem P1_of_P24 (x y : pgame) (ih :  a', ces a' (mul_args.P1 x y)  hyp a')
  (hx : x.numeric) (hy : y.numeric) : (x * y).numeric := sorry

seems to be working fine!

Violeta Hernández (May 15 2022 at 20:40):

That's pretty amazing

Violeta Hernández (May 15 2022 at 20:40):

Do you have a proof sketch for the hydra game ending?

Violeta Hernández (May 15 2022 at 20:42):

I think that warrants its own separate PR

Violeta Hernández (May 15 2022 at 20:50):

And I'd be glad to help if possible

Junyan Xu (May 15 2022 at 20:57):

This is a full proof in under 100 lines.

Violeta Hernández (May 15 2022 at 21:02):

Yeah, I believe this is an interesting enough result to maybe even have its own file

Violeta Hernández (May 15 2022 at 21:03):

Obviously the PR wouldn't be stated in terms of games, though

Violeta Hernández (May 15 2022 at 21:04):

Note: the proof of le_trans could be stated in terms of this, though I don't think it's large enough that this would be able to golf it

Junyan Xu (May 15 2022 at 21:06):

Since it's just 100 lines I think it could go to the end of https://leanprover-community.github.io/mathlib_docs/logic/relation.html instead of having its own file.

I'd like to PR it, but maybe I'll wait until I fill in more details in the Theorem 3.8 proof to convince that this well-foundedness result is the only thing left to make the proof work.

Violeta Hernández (May 15 2022 at 21:07):

Wait, you're working on that independently?

Junyan Xu (May 15 2022 at 21:08):

I have looked at your code

Junyan Xu (May 15 2022 at 21:09):

but instead of doing it in one stretch I try to extract lemmas that apply to multiple cases

Violeta Hernández (May 15 2022 at 21:11):

That's what I've attempted too, but all of these lemmas seem to depend on the same induction hypotheses

Junyan Xu (May 15 2022 at 21:13):

your surreal_mul branch hasn't been updated for almost 2 weeks, is there any new progress? I see that you are busy with other PRs.

I think I really just started to write code about Theorem 3.8 today, after the < correction was merged. I've shown one of the four cases of P1 follows from the induction hypothesis, and is now hoping the same lemmas that solved this case would also solve the other cases.

Violeta Hernández (May 15 2022 at 21:14):

I just merged with master :P

Violeta Hernández (May 15 2022 at 21:14):

Yeah, I've been busy with other PRs

Violeta Hernández (May 15 2022 at 21:14):

I wanted a certain lemma on birthdays, which necessitated a certain lemma on ordinals, and that led me down a rabbit hole of fixing some glaring holes in that API

Violeta Hernández (May 15 2022 at 21:14):

I still haven't even added that darned lemma

Violeta Hernández (May 15 2022 at 21:15):

But it wouldn't be necessary with your new approach

Violeta Hernández (May 15 2022 at 21:15):

I'll see if I can continue work on the proof within the next few days

Violeta Hernández (May 15 2022 at 21:16):

And you know, the sheer work you've put into your new framework for games is starting to convince me that it might be worth it, but I have no idea how I would pitch this to everyone else

Violeta Hernández (May 15 2022 at 21:17):

Still a bit worried about e.g. the ordinal and nim API though

Junyan Xu (May 15 2022 at 21:22):

Violeta Hernández said:

I wanted a certain lemma on birthdays, which necessitated a certain lemma on ordinals, and that led me down a rabbit hole of fixing some glaring holes in that API

I find it strange to see the birthday (x + y) stuff in your code. Why not express it in terms of birthday x and birthday y? Seems that (x + y) doesn't appear in the options of the product of two games?

Violeta Hernández (May 15 2022 at 21:42):

As far as I know (and I don't know that many things) there isn't any nice form for the birthday of the sum of two games

Violeta Hernández (May 15 2022 at 21:42):

(deleted)

Violeta Hernández (May 15 2022 at 21:42):

However, this value does have the property that it decreases when you replace either game by a subsequent one

Violeta Hernández (May 15 2022 at 21:44):

Further, if the birthday of y is less than the birthday of z, the birthday of x + y is less than the birthday of x + z

Kevin Buzzard (May 15 2022 at 22:06):

I think that's now called something like covariant_class (+) (<) or something

Violeta Hernández (May 15 2022 at 22:31):

We don't have any named relation for "the birthday of x is less than the birthday of y" though

Violeta Hernández (May 15 2022 at 22:31):

I don't think we need it either

Violeta Hernández (May 15 2022 at 23:18):

Violeta Hernández said:

Further, if the birthday of y is less than the birthday of z, the birthday of x + y is less than the birthday of x + z

Note, I'm only 99% sure of this

Violeta Hernández (May 15 2022 at 23:18):

I wanted to prove it in Lean, but for that I needed a lemma lsub_sum, which would be a consequence of sup_sum, which would be a consequence of csup_sum, which I had no idea where to put because conditionally_complete_lattice.lean and complete_lattice.lean are very different files despite most of the same theorems being true... you get my point

Violeta Hernández (May 15 2022 at 23:36):

Predictably, this lf refactor broke what I had of the surreal multiplication proof

Violeta Hernández (May 15 2022 at 23:37):

I'll try to fix it later today

Mario Carneiro (May 15 2022 at 23:44):

Violeta Hernández said:

Violeta Hernández said:

Further, if the birthday of y is less than the birthday of z, the birthday of x + y is less than the birthday of x + z

Note, I'm only 99% sure of this

That sounds false, at least with strict less than

Mario Carneiro (May 15 2022 at 23:46):

I think if the birthday of x is large then it will dominate and the birthday of x + y will be the same as x or maybe one more

Mario Carneiro (May 15 2022 at 23:48):

(at least, I am taking it as analogous to the claim that rank y < rank z -> rank (x ⊔ y) < rank (x ⊔ z) in zfc)

Mario Carneiro (May 15 2022 at 23:50):

Oh no wait I remember now, this is the natural sum operation on ordinals

Mario Carneiro (May 15 2022 at 23:51):

which we don't have any API for

Mario Carneiro (May 15 2022 at 23:52):

That is, I claim that birthday (x + y) = birthday x # birthday y

Violeta Hernández (May 16 2022 at 00:02):

Let me sketch a proof of that claim:

We first prove that birthday y ≤ birthday z → birthday (x + y) ≤ birthday (x + z) by Conway induction. Separate each birthday as the max of four lsubs. By the inductive hypotheses, these are less-or-equal respectively.

Then, if birthday y < birthday z, there exists some optionz' of z with birthday y ≤ birthday z', so that birthday (x + y) ≤ birthday (x + z') < birthday (x + z).

Violeta Hernández (May 16 2022 at 00:03):

Mario Carneiro said:

Oh no wait I remember now, this is the natural sum operation on ordinals

Nice! Yet another thing I can do with ordinals!

Mario Carneiro (May 16 2022 at 00:03):

And yes, I believe that y < z -> x # y < x # z

Mario Carneiro (May 16 2022 at 00:06):

the theory of natural sum/product is pretty darn similar to the theory of birthdays of surreals though, it might not be worth the rephrasing

Mario Carneiro (May 16 2022 at 00:07):

there is one novelty you get with the ordinal formulation though, which is the concrete definition of natural sum given by lining up the cantor normal forms and adding things pointwise

Violeta Hernández (May 16 2022 at 00:08):

Whoa, that works?

Violeta Hernández (May 16 2022 at 00:08):

That's really cool

Violeta Hernández (May 16 2022 at 00:09):

We don't need to "rephrase" ordinal birthdays in terms of the natural sum and product. But we could define the natural sum and product on ordinals, prove all the necessary theorems, then simply transfer them to surreal birthdays

Mario Carneiro (May 16 2022 at 00:11):

yup, it's mentioned at the end of that wikipedia section

Mario Carneiro (May 16 2022 at 00:17):

The easiest way to prove it probably goes via the observation that the natural sum is the greatest order type of a total order extending the disjoint union of types with order types x and y respectively. It is easy to see that the pointwise sum of cantor orderings is a way to order that disjoint union: you take all the A stuff and then all the B stuff in each coordinate, which proves that the cantor formula is <= x # y. For the reverse I think you can use ordinal induction, although it looks messy

Violeta Hernández (May 16 2022 at 00:31):

I think we'll need quite a lot more API for Cantor normal forms to get this to work

Violeta Hernández (May 16 2022 at 05:56):

It's not immediate to fix my progress on the multiplication proof

Violeta Hernández (May 16 2022 at 05:56):

I had taken a few shortcuts that just don't work anymore

Violeta Hernández (May 16 2022 at 05:57):

Which leads to the question of whether I was actually simplifying the proof by using lf instead of lt, or if my approach was flawed and I just hadn't realized it

Violeta Hernández (May 16 2022 at 05:58):

I'm going to have to go over the proof again with what I now know and evaluate this, and I am currently too tired to do so

Violeta Hernández (May 16 2022 at 05:58):

So maybe tomorrow

Violeta Hernández (May 16 2022 at 06:20):

On an unrelated note

Violeta Hernández (May 16 2022 at 06:20):

We have a consensus on \<| and \lf being useful shortcuts, right?

Violeta Hernández (May 16 2022 at 06:49):

I lied, I'm looking at the proof right now :P

Violeta Hernández (May 16 2022 at 06:49):

image.png This right here is an iff, if z₁ and z₂ are numeric

Violeta Hernández (May 16 2022 at 06:49):

This is an immediate consequence of le_def_lt, the numeric counterpart to le_def_lf (which I have already PR'd)

Violeta Hernández (May 16 2022 at 06:50):

I wonder, can we get away with the if direction without assuming that these are numeric?

Violeta Hernández (May 16 2022 at 06:50):

It isn't really a problem, this dependency still satisfies the hydra relation, but it's a dependency I hadn't accounted for before

Junyan Xu (May 16 2022 at 06:55):

pgame.le_def_lf doesn't assume numeric, and we are proving lt, which implies lf unconditionally, so I don't see any additional dependency.

Violeta Hernández (May 16 2022 at 06:57):

Oh, you're right

Violeta Hernández (May 16 2022 at 07:00):

So, it seems like we can prove P2 by using a weaker form of P4 using lf instead of lt

Violeta Hernández (May 16 2022 at 07:01):

Here's the question: can we perhaps do the rest of the proof from this weaker P4'?

Violeta Hernández (May 16 2022 at 07:01):

If so then we've found a way to simplify the argument

Violeta Hernández (May 16 2022 at 07:03):

I'm a bit skeptical of this working though, since add_lf_add isn't actually true

Violeta Hernández (May 16 2022 at 07:04):

Hmm... in fact, this would have doomed my proof if I hadn't realized we had lt and lf swapped

Violeta Hernández (May 16 2022 at 07:39):

Alright, I got the proof working again... or rather, what I had written of it

Violeta Hernández (May 16 2022 at 07:39):

There's little missing now, other than the proof of well-foundedness

Violeta Hernández (May 16 2022 at 07:39):

I think I can finish the skeleton tonight

Violeta Hernández (May 16 2022 at 08:42):

Yeah actually no

Violeta Hernández (May 16 2022 at 08:42):

I am way past my bedtime and I only finished one of eight (?) remaining cases

Violeta Hernández (May 16 2022 at 15:01):

I got this as the output of abel

Violeta Hernández (May 16 2022 at 15:01):

image.png

Violeta Hernández (May 16 2022 at 15:01):

Note that ⟦x₁R jx₁ * mk yl yr yL yR⟧ appears on both sides of the inequality

Violeta Hernández (May 16 2022 at 15:02):

Is there some easy way to make abel realize this and cancel them out? Or some other tactic that does the job better?

Violeta Hernández (May 16 2022 at 15:02):

Note: the original expression was even more complicated, and abel did successfully cancel out one of two pairs of common terms

Yaël Dillies (May 16 2022 at 15:04):

"cancelling out" means "applying a monotonicity lemma", so I think it's out of scope for abel.

Violeta Hernández (May 16 2022 at 15:11):

Is there any other tactic that can deal with this?

Violeta Hernández (May 16 2022 at 15:12):

For full context: here's my hypothesis and my goal image.png

Violeta Hernández (May 16 2022 at 15:12):

x₁ is def-eq to mk x₁l x₁r x₁L xᵣ btw, same goes for x₂

Violeta Hernández (May 16 2022 at 15:13):

Actually, there might be some syntactic equality troubles that prevent a single tactic from closing the goal, but simplifying H₃ enough that I can just use one or two rw add_comm and exact would be amazing

Reid Barton (May 16 2022 at 15:21):

Use some lemma to rewrite H3 to the form 0 < _ and then try abel?

Yaël Dillies (May 16 2022 at 15:23):

tactic#monotonicity

Violeta Hernández (May 16 2022 at 15:46):

You mean mono?

Violeta Hernández (May 16 2022 at 15:46):

mono at H₃ doesn't seem to work

Violeta Hernández (May 16 2022 at 15:48):

Reid Barton said:

Use some lemma to rewrite H3 to the form 0 < _ and then try abel?

This might work, maybe apply_rules could do this?

Violeta Hernández (May 16 2022 at 16:56):

I have proven four out of eight goals in the very last stretch of the skeleton of the proof!

Violeta Hernández (May 16 2022 at 16:56):

The skeleton will be ready really soon

Violeta Hernández (May 16 2022 at 16:56):

(today in messages that are funnier without context)

Violeta Hernández (May 16 2022 at 18:05):

Six out of eight!

Violeta Hernández (May 16 2022 at 18:11):

Eight out of eight!!!!!!

Violeta Hernández (May 16 2022 at 18:11):

Alright, the skeleton of the proof is done

Violeta Hernández (May 16 2022 at 18:12):

This is almost 300 lines of code for a single proof (which I presume is some sort of mathlib record?)

Violeta Hernández (May 16 2022 at 18:12):

There's definitely room for golfing though

Violeta Hernández (May 16 2022 at 18:16):

@Junyan Xu

Violeta Hernández (May 16 2022 at 18:17):

I tried to make the induction step easier to prove by deduplicating applications of the induction hypothesis

Violeta Hernández (May 16 2022 at 18:18):

Even then, there are a whopping 36 applications we need to prove well-founded

Violeta Hernández (May 16 2022 at 18:36):

I'm going through each of them and verifying they satisfy the cut_expand relation

Violeta Hernández (May 16 2022 at 18:36):

Not formally, yet

Violeta Hernández (May 16 2022 at 18:39):

Actually, the cut_expand relation is a wee bit too weak

Violeta Hernández (May 16 2022 at 18:39):

It seems like we need its transitive closure

Violeta Hernández (May 16 2022 at 18:39):

Fortunately I proved not too long ago that the transitive closure of a well-founded relation is well-founded

Violeta Hernández (May 16 2022 at 18:42):

Now, here's the kicker

Violeta Hernández (May 16 2022 at 18:43):

What's a succint way to prove two multisets satisfy the cut_expand relation?

Violeta Hernández (May 16 2022 at 18:43):

image.png If we provide t, are the other proofs rfl?

Junyan Xu (May 16 2022 at 18:44):

It's great that you finished the proof! I'll hopefully PR my part tonight. I'll probably change s' = s.erase a + t to s' + {a} = s + t in cut_expand, as it avoids decidable_eq and it's probably easier to show concrete multisets satisfy the relation with + instead of erase.

And I think I'll also define an inductive relation on mul_args that is a subrelation of inv_image (trans_gen $ cut_expand is_option) (so well-founded and we can have the has_well_founded instance on mul_args) and covers all cases we need, and then we can define a wf tactic that use solve_by_elim with the constructors of the relation to fulfill the wf obligations.

Junyan Xu (May 16 2022 at 18:45):

Violeta Hernández said:

What's a succint way to prove two multisets satisfy the cut_expand relation?

The docs for fin_casesappears like that it could solve these, but actually not.

Junyan Xu (May 16 2022 at 18:45):

The most systematic way I can think of now is to use add_singleton to expand it to sum of singletons, then use commutativity, associativity etc. (Notice that {a,b,c} is something like ({a}.cons b).cons c under the hood.)

Violeta Hernández (May 16 2022 at 18:48):

Junyan Xu said:

And I think I'll also define an inductive relation on mul_args that is a subrelation of inv_image (trans_gen $ cut_expand is_option) (so well-founded and we can have the has_well_founded instance on mul_args) and covers all cases we need, and then we can define a wf tactic that use solve_by_elim with the constructors of the relation to fulfill the wf obligations.

Already on it:

instance : has_lt mul_args :=
inv_image (relation.trans_gen $ cut_expand subsequent) to_multiset

instance : has_well_founded mul_args :=
{ r := (<),
  wf := inv_image.wf _ (cut_expand.wf wf_subsequent).trans_gen }

Violeta Hernández (May 16 2022 at 18:50):

I'm writing down the sorries for the well-foundedness proofs

Violeta Hernández (May 16 2022 at 19:00):

There! I've sorried all 36 of them

Violeta Hernández (May 16 2022 at 19:00):

And they should all be true

Violeta Hernández (May 16 2022 at 19:04):

How does your new definition for cut_expand look like?

Violeta Hernández (May 16 2022 at 19:04):

I want to try some things out with it

Violeta Hernández (May 16 2022 at 19:08):

I presume it's this?

def cut_expand (r : α  α  Prop) (s s' : multiset α) : Prop :=
 (t : multiset α) (a  s), ( a'  t, r a' a)  s' + {a} = s + t

example : cut_expand (<) ({0, 1, 3} : multiset ) ({0, 1, 2, 2, 2, 2} : multiset ) :=
begin
  refine ⟨{2, 2, 2, 2}, 3, _, λ a h, _, _⟩,
  { dec_trivial },
  { fin_cases h;
    dec_trivial },
  { dec_trivial }
end

Junyan Xu (May 16 2022 at 19:11):

Interesting, changing erase to + makes it decidable and fin_cases applicable?

Violeta Hernández (May 16 2022 at 19:12):

At least for naturals

Junyan Xu (May 16 2022 at 19:12):

Oh yes, pgame doesn't have decidable_eq

Violeta Hernández (May 16 2022 at 19:13):

Is there any way to prove goals like x₁ ∈ {x₁, x₂, x₃} easily without decidable_eq?

Violeta Hernández (May 16 2022 at 19:15):

Or god forbid, something like this image.png

Violeta Hernández (May 16 2022 at 19:59):

I've started filling out the sorries

Violeta Hernández (May 16 2022 at 20:00):

The one that I'm going to leave open is the one for cut_expand.wf

Violeta Hernández (May 16 2022 at 20:00):

That's all yours

Junyan Xu (May 16 2022 at 20:00):

Violeta Hernández said:

Is there any way to prove goals like x₁ ∈ {x₁, x₂, x₃} easily without decidable_eq?

Maybe simp only [multiset.mem_cons] then use rfl with some or.inl, or.inr.

Violeta Hernández said:

Or god forbid, something like this image.png

maybe simp only [<- multiset.singleton_add], abel

Violeta Hernández (May 16 2022 at 20:04):

I filled in some simple lemmas for P1 x' y' < P1 x y, we're already down to 26 sorries

Junyan Xu (May 16 2022 at 20:05):

Actually I want to remove the a ∈ s condition, which is unnecessary for irreflexive relation r.

Violeta Hernández (May 16 2022 at 20:42):

Every well-founded relation is irreflexive

Violeta Hernández (May 16 2022 at 20:42):

So yeah, you should remove that

Violeta Hernández (May 17 2022 at 01:45):

I'm having a bit of trouble

Violeta Hernández (May 17 2022 at 01:45):

Not with the proofs themselves

Violeta Hernández (May 17 2022 at 01:45):

But rather, giving names to like 10 auxiliary lemmas for that relation is not really working out haha

Junyan Xu (May 17 2022 at 02:26):

Junyan Xu said:

maybe simp only [<- multiset.singleton_add], abel

Just need an additional rw multiset.has_insert to make it work:

import data.multiset.basic

example : ({1,2} : multiset ) + {2,3,5} = {2,3,5,1,2} :=
begin
  rw multiset.has_insert,
  simp only [ multiset.singleton_add], abel,
end

Junyan Xu (May 17 2022 at 02:27):

However it's still cumbersome. Maybe wrap it into a single local tactic? I think Lean can't come up with t (=s' + {a} - s) automatically because multiset.has_sub uses decidable_eq.

Violeta Hernández (May 17 2022 at 02:49):

On second thought, I'm not sure this will help

Violeta Hernández (May 17 2022 at 02:50):

I've been able to produce relatively short proofs for the theorems I've needed, without relying on things like these

Junyan Xu (May 17 2022 at 05:16):

I think if you adopt my approach and define (<) on mul_args as an inductive relation, then you can replace the ~10 auxiliary lemmas with ~10 constructors; you can prove it's a subrelation of the inv_image in a single lemma, and use subrelation.wf to show it's well-founded. And here's a way to name the constructors systematically; for example, the constructor that produces mul_args.P1 y x₂L < mul_args.P24 x₁ x₂ y may be named eyo₂, meaning that the first argument is equal to y and the second is an option of x₂. Similarly, the constructor that produces mul_args.P24 yL yR x < mul_args.P1 x y may be named oyoyex.

Junyan Xu (May 17 2022 at 08:22):

#14190

Violeta Hernández (May 17 2022 at 17:03):

I decided against defining < on mul_args directly, instead opting to define a relation on multisets themselves. That way, I'm able to directly apply lemmas like {x, y} = {y, x}without having to use change or unfold

Violeta Hernández (May 17 2022 at 17:21):

But yeah, your approach still works

Violeta Hernández (May 17 2022 at 17:23):

There's some auxiliary lemmas on cut_expand I'll still need, but I'll PR those after your PR is merged

Violeta Hernández (May 18 2022 at 15:54):

I just realized

Violeta Hernández (May 18 2022 at 15:54):

I think you can prove equality of multisets like {x₁, x₂, x₃} = {x₂, x₂, x₁, x₃} by ext, tauto

Mario Carneiro (May 18 2022 at 16:01):

ext, simp should also work

Scott Morrison (May 18 2022 at 21:09):

I've always held that if ext, simp fails to prove a "these things are the same because they are made out of the same ingredients" goal, then you are obligated to go write the missing @[ext] and @[simp] lemmas before proceeding!

Yaël Dillies (May 18 2022 at 21:12):

In that case I'm not sure this holds, because there can be arbitrarily many or_comm required to prove the goal.

Violeta Hernández (May 19 2022 at 21:25):

We got \lf, \<|, and \fuzzy added to vscode-lean!

Yaël Dillies (May 19 2022 at 21:33):

Did you add them to the other extensions as well?

Violeta Hernández (May 19 2022 at 21:35):

Uhhhhh no

Violeta Hernández (May 19 2022 at 21:35):

I probably should do that

Violeta Hernández (May 19 2022 at 21:35):

What other extensions are there?

Julian Berman (May 19 2022 at 22:42):

(I pull in changes every so often to neovim, so I pulled yours in already. There's emacs too but I think Sebastian does the same every so often. Probably some day we'll have a shared location that doesn't involve copy pasting, but for now I think you're good)

Yaël Dillies (May 19 2022 at 23:03):

There's also @Arthur Paulino's Chrome extension

Eric Wieser (May 21 2022 at 08:21):

I only just learnt to use \lf and \rf to type and :(

Yaël Dillies (May 21 2022 at 08:22):

Aaah, no that was one of my favorite shortcuts :sad:

Eric Wieser (May 21 2022 at 08:23):

What does "lf" stand for in the pgame world?

Yaël Dillies (May 21 2022 at 08:23):

"less than or fuzzy to"

Eric Wieser (May 21 2022 at 08:56):

ltf might be a reasonable compromise then, although I don't remember what the rules are for resolving ambiguity of abbreviations

Eric Wieser (May 21 2022 at 08:57):

Maybe adding \floor for ⌊$CURSOR⌋ is a better solution anyway

Ruben Van de Velde (May 21 2022 at 09:16):

Or both?

Eric Wieser (May 21 2022 at 09:23):

It would be great if we could move the management of these aliases to within mathlib itself

Eric Wieser (May 21 2022 at 09:24):

But that's probably a conversation for a new thread

Violeta Hernández (May 21 2022 at 23:31):

I decided to go ahead and PR natural addition: #14291

Violeta Hernández (May 21 2022 at 23:32):

This is relevant to games in two ways (that I know of!)

Violeta Hernández (May 21 2022 at 23:32):

a.to_pgame + b.to_pgame ≈ (of_nat_ordinal (to_nat_ordinal a + to_nat_ordinal b)).to_pgame

Violeta Hernández (May 21 2022 at 23:32):

(i.e. natural addition is the addition on ordinal games)

Violeta Hernández (May 21 2022 at 23:33):

And birthday (a + b) = of_nat_ordinal (to_nat_ordinal (birthday a) + to_nat_ordinal (birthday b))

Violeta Hernández (May 21 2022 at 23:34):

This of and to notation totally sucks, but natural addition and multiplication have a ton of algebraic properties, so it was worth making into a type alias so that we could provide all the appropriate instances

Eric Rodriguez (May 21 2022 at 23:51):

maybe add some notation for of/to? localised maybe?

Violeta Hernández (May 22 2022 at 00:05):

Any suggestions?

Violeta Hernández (May 22 2022 at 00:06):

I'm guessing such a notation would look something like ᵒᵈ and friends

Scott Morrison (May 22 2022 at 03:47):

Or just put them in a namespace so by opening the namespace you can just write to and from? I think this might be too specialized to warrant notation.

Violeta Hernández (May 22 2022 at 05:39):

That's clever

Violeta Hernández (May 22 2022 at 06:07):

to_nat_ordinal and of_nat_ordinal are both already in a nat_ordinal namespace. Should I just rename them to to and of?

Junyan Xu (May 22 2022 at 06:48):

Wikipedia uses the notations ⊕ and ⊗ which I think are standard. The ⊕ notation is also common for Nim sum / xor, and nim sum/product also have tons of algebraic properties (forming an algebraically closed field On₂, see Lieven le Bruyn's blog posts), but we may use ⊕₂ ⊗₂ to signify both their binary nature and their similarity to natural sum/product.

By the way, well partial orders is spelt docs#set.is_pwo in mathlib, and it would be nice to connect them to natural sums/products as explained in the first Wikipedia page.

Violeta Hernández (May 22 2022 at 06:49):

is definitely a no-go, since it's used for type sums

Violeta Hernández (May 22 2022 at 06:50):

Also, the decision to make a new type in order to use + and * was quite intentional. These natural operations have a lot of algebraic structure to them, and we'd be missing out on tons of instances if we just made them operations on ordinal itself

Violeta Hernández (May 22 2022 at 06:50):

They're commutative, associative, preserve order, are cancellative, and distributive

Violeta Hernández (May 22 2022 at 06:51):

And they have the usual 0 and 1 from ordinals

Junyan Xu (May 22 2022 at 06:54):

Ok yes, a lot of nice properties seem impossible to state without the had_add has_mul instances, and those would conflict with the original ones, so a type synonym seems necessary.

Violeta Hernández (May 22 2022 at 06:56):

In an ideal world we'd have group (+) instead of add_group and such, but this isn't that world :sob:

Junyan Xu (May 22 2022 at 07:05):

Maybe what we want is a ⊗ b = of_nat_ordinal (to_nat_ordinal a * to_nat_ordinal b) and the alike.

Here's how people solve the notation conflict with type sum in biproducts ("direct sum") in the categorical setting; in concrete categories its underlying type is usually exactly the type sum. I wonder if it makes sense and easy to make the ⊕ notation localized.

By the way, anyone knows why lf and equiv are displayed in the docs instead of their notations?

Eric Wieser (May 22 2022 at 07:31):

It's because the notation is local

Violeta Hernández (May 22 2022 at 07:34):

Notations still give you syntactic equality, right? If so, that's definitely the right approach

Violeta Hernández (May 22 2022 at 07:35):

Still, I wouldn't use , since that's also used for nimber addition

Junyan Xu (May 22 2022 at 07:36):

Wikipedia also suggests the # ⨳ notations.

Violeta Hernández (May 22 2022 at 07:36):

Yeah, I'd rather go with that

Violeta Hernández (May 22 2022 at 07:36):

# could potentially clash with the notation for cardinality, but I find it unlikely we'll ever involve the two at the same time

Violeta Hernández (May 22 2022 at 07:50):

Hmm... actually, I'm not so sure about the local notation anymore

Violeta Hernández (May 22 2022 at 07:50):

Because, the theorems about nat_ordinal are most naturally stated in terms of nat_ordinal, of course

Violeta Hernández (May 22 2022 at 07:50):

But the # notation requires us to state our theorems as theorems about ordinal instead

Mario Carneiro (May 22 2022 at 07:50):

Violeta Hernández said:

# could potentially clash with the notation for cardinality, but I find it unlikely we'll ever involve the two at the same time

umm... cardinal and ordinal are very often used together

Violeta Hernández (May 22 2022 at 07:50):

Hmm.... true

Mario Carneiro (May 22 2022 at 07:51):

I think it doesn't need a symbol beyond what it gets via nat_ordinal

Mario Carneiro (May 22 2022 at 07:52):

a.nadd b / a.nmul b?

Junyan Xu (May 22 2022 at 07:53):

If a b are nat_ordinal then these are just a + b a * b.

Mario Carneiro (May 22 2022 at 07:53):

I mean for the ordinal ops

Junyan Xu (May 22 2022 at 07:54):

Oh yes

Mario Carneiro (May 22 2022 at 07:54):

nat_ordinal would wrap those ops and give them notation

Junyan Xu (May 22 2022 at 07:55):

Yes nadd nmul look short enough. Though I just looked into Unicode just for fun:

The official name for ⨳ is "smash product", and in the same block there are also ⩩ and ⩨ , but ⌗ is in a different block (0x2317, Miscellaneous Technical). This vertical version looks better than the slanted # #, and also avoids conflict.
There is also
Vai syllable pu: ꖛ
Unicode hexadecimal: 0xa59b
In block: Vai (a language in Liberia and Sierra Leone)

Violeta Hernández (May 22 2022 at 07:55):

I'm worried that by wrapping around + and *, we either

  • lose a lot of flexibility due to not having typeclasses
  • have to prove a lot of lemmas twice

Mario Carneiro (May 22 2022 at 07:56):

? nat_ordinal would have typeclasses

Mario Carneiro (May 22 2022 at 07:57):

you could use nat_ordinal to prove theorems about nadd and nmul by wrapping, but I don't think there will be a huge number of such lemmas

Violeta Hernández (May 22 2022 at 07:57):

Yeah, but we wouldn't have, say nadd_assoc or nadd_comm, or nadd_lt_nadd_left, ... unless we went ahead and wrote every single one of them out

Mario Carneiro (May 22 2022 at 07:57):

you will definitely have nadd_assoc and nadd_comm

Mario Carneiro (May 22 2022 at 07:57):

because you have to prove those

Violeta Hernández (May 22 2022 at 07:58):

My point is that we have those for + on nat_ordinal rather than for nadd

Violeta Hernández (May 22 2022 at 07:58):

I guess it's not that much boilerplate we'd need to add, but still

Mario Carneiro (May 22 2022 at 07:58):

you will have them on nat_ordinal by wrapping nadd_assoc

Mario Carneiro (May 22 2022 at 07:58):

calling the operation + doesn't magically prove it is commutative and associative

Mario Carneiro (May 22 2022 at 07:59):

there are some "theorems for free" but that is not one of them

Mario Carneiro (May 22 2022 at 08:00):

like nadd_left_assoc, okay you might want to defer to group lemmas (although the proof is so trivial it might be more work to prove by reflecting to nat_ordinal than directly)

Violeta Hernández (May 22 2022 at 08:01):

No, I rather mean

Violeta Hernández (May 22 2022 at 08:01):

If we were to wrap around + by using nadd, we'd need to prove a bunch of boilerplate like

theorem nadd_comm :  a b, nadd a b = nadd b a := @add_comm nat_ordinal _
theorem nadd_assoc :  a b c, (nadd a b).nadd c = nadd a (nadd b c) := @add_assoc nat_ordinal _

and so on

Violeta Hernández (May 22 2022 at 08:02):

But I guess it's not that much boilerplate anyways

Mario Carneiro (May 22 2022 at 08:02):

I'm saying that those lemmas would have substantive proofs

Violeta Hernández (May 22 2022 at 08:02):

Sure, they'd have the proofs from nat_ordinal

Mario Carneiro (May 22 2022 at 08:02):

and they would be referenced in the instance : add_comm_group nat_ordinal

Violeta Hernández (May 22 2022 at 08:02):

Oh yeah, I'm aware of that

Junyan Xu (May 22 2022 at 08:03):

It seems another occasion where we'd like to norm_cast into nat_ordinal when we want to prove something about nat sum/prod, in order for instances on nat_ordinal to fire. Maybe just a little bit of boilerplate would make that work? I haven't tried it with pgame->game.

Mario Carneiro (May 22 2022 at 08:04):

Personally I think nat_ordinal and nadd will not get much use beyond the few pgame lemmas you are working on right now

Mario Carneiro (May 22 2022 at 08:04):

they are pretty much only ever brought up in connection with surreals / combinatorial games

Mario Carneiro (May 22 2022 at 08:05):

which is why I think having a couple definitions and no notation is fine

Violeta Hernández (May 22 2022 at 08:25):

I went ahead and defined nadd on ordinal. As expected, there were a lot of boilerplate lemmas to transfer over, though they're almost all def-eq so that's no issue. I think this is a good price to pay for much simpler notation.

https://github.com/leanprover-community/mathlib/pull/14291/commits/f5c5e1e710c92089b88f7f329626f45959785b97

Violeta Hernández (May 22 2022 at 08:27):

And yeah, I guess that what I'm ultimately doing is pretty niche. But that doesn't mean we can't make it look pretty!

Mario Carneiro (May 22 2022 at 08:28):

It looks like nat_ordinal has a ton of boilerplate going in the opposite direction

Mario Carneiro (May 22 2022 at 08:28):

my suggesion is to steal the definition of nat_ordinal.add and paste it in ordinal.nadd

Mario Carneiro (May 22 2022 at 08:28):

and then prove ordinal.nadd_assoc directly

Mario Carneiro (May 22 2022 at 08:29):

and then nat_ordinal will only be boilerplate lemmas

Violeta Hernández (May 22 2022 at 08:30):

I don't think this will entirely work out, since a few of the boilerplate lemmas (the nadd_lt_nadd ones) are generated from the covariant and contravariant instances

Violeta Hernández (May 22 2022 at 08:31):

So at the very least, those have to be proven in nat_ordinal first

Mario Carneiro (May 22 2022 at 08:31):

Aren't those instances true on ordinal too?

Violeta Hernández (May 22 2022 at 08:32):

Not all of them, and even if they were, they wouldn't be on the same operation!

Mario Carneiro (May 22 2022 at 08:32):

of course, s/+/nadd/

Mario Carneiro (May 22 2022 at 08:32):

and if they aren't instances, just call them nadd_lt_nadd

Violeta Hernández (May 22 2022 at 08:33):

The instances automatically generate the iff and cancel lemmas though

Violeta Hernández (May 22 2022 at 08:33):

If I proved these theorems on ordinal first, I'd need to manually prove those lemmas

Mario Carneiro (May 22 2022 at 08:34):

which instance are you talking about specifically?

Mario Carneiro (May 22 2022 at 08:34):

all the ones I see apply to ordinal too

Violeta Hernández (May 22 2022 at 08:34):

I'm talking about add_covariant_class_lt and the seven other similar ones

Mario Carneiro (May 22 2022 at 08:35):

instance add_covariant_class_lt :
  covariant_class ordinal.{u} ordinal.{u} nadd (<) :=

et al

Violeta Hernández (May 22 2022 at 08:36):

Unfortunately, those instances don't have support for any operations other than +,*, or their swapped counterparts (and relations other than < and )

Violeta Hernández (May 22 2022 at 08:36):

So doing this wouldn't autogenerate the lemmas

Violeta Hernández (May 22 2022 at 08:37):

In fact, it wouldn't even give us access to the add_lt_add_left and such lemmas

Violeta Hernández (May 22 2022 at 08:37):

Since they require a has_add instance specifically

Mario Carneiro (May 22 2022 at 08:38):

do you want rel_iff_cov?

Mario Carneiro (May 22 2022 at 08:39):

poking around in the relevant files I see several generic lemmas, they have generic names of course

Mario Carneiro (May 22 2022 at 08:41):

I think if the instances are expressible they should definitely be stated on ordinal, possibly in addition to nat_ordinal

Violeta Hernández (May 22 2022 at 08:42):

My understanding is that there's little to no support for these custom covariant and contravariant classes

Violeta Hernández (May 22 2022 at 08:42):

It's the reason I didn't implement them for lf on pgame

Violeta Hernández (May 22 2022 at 08:42):

Maybe someone else can confirm

Mario Carneiro (May 22 2022 at 08:44):

I'm wondering what kind of nontrivial thing you are even getting out of these classes. We have it stated in a bunch of different ways that x < y -> f x < f y implies f is injective and strictly increasing and iff-preserves lt and le

Mario Carneiro (May 22 2022 at 08:45):

but not having the lemmas doesn't seem like a good reason to not have the instances

Mario Carneiro (May 22 2022 at 08:48):

theorem nadd_lt_nadd_left :  {b c} (h : b < c) a, a  b < a  c :=
@add_lt_add_left nat_ordinal _ _ _
theorem lt_of_nadd_lt_nadd_left :  {a b c}, a  b < a  c  b < c :=
@lt_of_add_lt_add_left nat_ordinal _ _ _
theorem nadd_lt_nadd_iff_left :  a {b c}, a  b < a  c  b < c :=
@add_lt_add_iff_left nat_ordinal _ _ _ _

We don't need all three of these, the last one is plenty

Violeta Hernández (May 22 2022 at 08:49):

Well, we get all three almost for free on nat_ordinal

Mario Carneiro (May 22 2022 at 08:50):

I know, but we get all three for free just from nadd_lt_nadd_iff_left

Mario Carneiro (May 22 2022 at 08:50):

in fact, you could probably prove the first two even shorter by using the last one

Mario Carneiro (May 22 2022 at 08:51):

but I would just drop them, we try to discourage the one way version of iff lemmas unless they are really heavily used

Mario Carneiro (May 22 2022 at 08:52):

it's more compositional that way

Violeta Hernández (May 22 2022 at 08:53):

The way I see it, if we're going to write down boilerplate, we might as well be thorough

Violeta Hernández (May 22 2022 at 08:54):

I wouldn't mourn the loss of the one-sided theorems though

Mario Carneiro (May 22 2022 at 08:55):

well it's a lot less boilerplate if you do it the other way around (I contest)

Mario Carneiro (May 22 2022 at 08:55):

like nat_ordinal.blsub doesn't need to exist at all

Mario Carneiro (May 22 2022 at 08:57):

also ditto on what eric said, instead of a + b ≤ of_nat_ordinal (to_nat_ordinal a + to_nat_ordinal b) you should use only one of the two functions at a time, to_nat_ordinal (a + b) ≤ to_nat_ordinal a + to_nat_ordinal b

Eric Wieser (May 22 2022 at 08:58):

The advantage of having it is that for n : nat_ordinal, n.blsub doesn't resolve to something with a messy type

Mario Carneiro (May 22 2022 at 08:58):

I don't think you should ever need to write n.blsub

Mario Carneiro (May 22 2022 at 09:00):

if all the substantive proof is over ordinal and nadd then the only thing that nat_ordinal needs to do is lift the ring ops

Violeta Hernández (May 22 2022 at 09:01):

nat_ordinal.blsub was actually Eric's suggestion, haha

Violeta Hernández (May 22 2022 at 09:01):

It really only makes things easier to look at

Violeta Hernández (May 22 2022 at 09:02):

I could remove it and save a few rewrites

Mario Carneiro (May 22 2022 at 09:02):

I'm saying that blsub of a nat_ordinal is not a thing that should be happening

Mario Carneiro (May 22 2022 at 09:02):

regardless of how it is written

Eric Wieser (May 22 2022 at 09:03):

From a mathematical perspective, you mean?

Mario Carneiro (May 22 2022 at 09:03):

yes

Mario Carneiro (May 22 2022 at 09:03):

it's used inside the definition of nat_ordinal.add but if that is defined directly on ordinal then I think you won't ever need it elsewhere

Violeta Hernández (May 22 2022 at 09:04):

Surely from a mathematical perspective, nat_ordinal wouldn't need to exist?

Violeta Hernández (May 22 2022 at 09:04):

It only exists because Lean doesn't allow us to define more than one addition for a type

Violeta Hernández (May 22 2022 at 09:04):

Mathematicians have no issue with that

Mario Carneiro (May 22 2022 at 09:05):

sure, so do your best to not use it at all and see how far you can get

Mario Carneiro (May 22 2022 at 09:05):

(that's not sarcasm)

Mario Carneiro (May 22 2022 at 09:06):

I can believe that there are some theorems that currently are only stated for plus-like things so you need to transfer stuff to nat_ordinal, but just use it to get the theorem and then go back to ordinal to finish the proof

Mario Carneiro (May 22 2022 at 09:08):

(and don't forget that you can switch between nat_ordinal and ordinal namespaces multiple times)

Eric Rodriguez (May 22 2022 at 09:50):

I just realised, another option is to make notation that does all the casting stuff for you, and then you should be able to do many rws and stuff as if the notation was a real +/*

Violeta Hernández (May 22 2022 at 17:47):

I think I get your point: we have nat_ordinal for the instances, and nothing more, right?

Violeta Hernández (May 22 2022 at 17:59):

I like that design better

Violeta Hernández (May 22 2022 at 17:59):

It should avoid most if not all of the annoying casting we currently have

Violeta Hernández (May 22 2022 at 17:59):

And if we ever need some theorem that's available through an instance on nat_ordinal, we can simply def-eq cast it

Violeta Hernández (May 22 2022 at 18:55):

What precedences should I set for and ?

Violeta Hernández (May 22 2022 at 18:55):

I'm still not using the latter, but still

Mario Carneiro (May 22 2022 at 19:37):

For this application, it makes sense to make them the same as + and * respectively

Violeta Hernández (May 22 2022 at 19:40):

And what would those precedences be?

Violeta Hernández (May 22 2022 at 19:40):

I don't know where to find them

Violeta Hernández (May 22 2022 at 19:44):

By the way, I did the refactor Mario suggested, and the lemmas look much nicer!

Mario Carneiro (May 22 2022 at 20:01):

#print +

Mario Carneiro (May 22 2022 at 20:01):

I think it is 65

Violeta Hernández (May 23 2022 at 03:02):

Currently having a go at defining natural multiplication and I'm already at a roadblock with distributivity

Violeta Hernández (May 23 2022 at 03:02):

image.png Other than a stupidly long rw chain, how can I prove this?

Violeta Hernández (May 23 2022 at 03:02):

(note that the terms a' ⨳ b' and a ⨳ b' in this cancel out, giving the result)

Violeta Hernández (May 23 2022 at 03:04):

I could cast this into a (huge) expression in nat_ordinal, but afaik there's no tactic that can finish the goal even then

Mario Carneiro (May 23 2022 at 03:30):

A calc block is probably the clearest way

Mario Carneiro (May 23 2022 at 03:31):

you should be able to normalize it using simp [comm, left_comm, assoc]

Violeta Hernández (May 23 2022 at 03:32):

Is there any way I can normalize it, while specifying the order of the terms?

Violeta Hernández (May 23 2022 at 03:32):

That would be extremely useful if possible

Mario Carneiro (May 23 2022 at 03:39):

one hack is to choose fortuitous variable names

Mario Carneiro (May 23 2022 at 03:40):

otherwise, you are probably better off using rw [left_comm] to bring something to the front

Violeta Hernández (May 23 2022 at 03:44):

docs#left_comm

Violeta Hernández (May 23 2022 at 03:44):

This?

Mario Carneiro (May 23 2022 at 03:48):

nadd_left_comm probably

Violeta Hernández (May 23 2022 at 04:05):

Oh, I see!

Violeta Hernández (May 23 2022 at 04:05):

Once you simp [assoc] you can use nadd_left_comm to move terms as you will

Violeta Hernández (May 23 2022 at 04:05):

Clever!

Violeta Hernández (May 23 2022 at 04:07):

I did it! I managed to prove this!

Violeta Hernández (May 23 2022 at 04:07):

And it only took like 10 rws!

Damiano Testa (May 23 2022 at 04:46):

On a just vaguely related note: if you had actual has_add.add, the proposed tactic move_add gives you the option of specifying which terms in a sum should be first/last at will. You can also of course pass the full permutation, if you so wish!

I'm simply bringing this up since it is the second time that I see a place where move_add would have been wanted "in the wild". Also, adding support for operations other than + would be fairly easy.

Mario Carneiro (May 23 2022 at 04:47):

you would still need to know that the new operation is comm/assoc

Damiano Testa (May 23 2022 at 04:48):

Yes, you would need the analogues of add_assoc, add_comm and add_left_comm.

Damiano Testa (May 23 2022 at 04:49):

These are the three "external" inputs to the tactic, everything else is moving exprs around.

Violeta Hernández (May 23 2022 at 04:53):

Damiano Testa said:

I'm simply bringing this up since it is the second time that I see a place where move_add would have been wanted "in the wild". Also, adding support for operations other than + would be fairly easy.

Make that a third! I would have benefited a lot from that in the surreal multiplication proof

Violeta Hernández (May 23 2022 at 04:53):

I have some huge rw blocks over there too

Damiano Testa (May 23 2022 at 04:56):

Ok, I'll leave the current PR as is, since it has already gone through some revisions. But I will add a second PR allowing "custom" operations.

I was thinking of just + and * support, but I might try to see if I can get a "generic" tactic that takes also an operation as input. It might be tricky to specify the comm/assoc lemmas to use in this case, though.

Violeta Hernández (May 23 2022 at 05:12):

We wouldn't need this if there were some easy way to convert a lemma on ordinal and natural operations to the corresponding one on nat_ordinal

Violeta Hernández (May 23 2022 at 05:13):

I've heard people say norm_cast could help but I don't really know how that works

Damiano Testa (May 23 2022 at 05:19):

I do not know neither the maths, nor the mathlib side of this story, so I'll leave this to someone else!

My naïve view is that norm_cast would take care of dealing with seen and unseen coercions and uniformize everything. Thus, if there were a coe between nat_ordinal and pgames (or games, I really don't know), then norm_cast could convert expressions where one appear into expressions where the other appears.

Damiano Testa (May 23 2022 at 05:20):

I think that you might have to tag the relevant lemmas with a norm_cast attribute, but this is something that I have never done and might be wrong about.

Violeta Hernández (May 23 2022 at 06:49):

I managed to prove the basic properties of natural multiplication and wow were they painful

Violeta Hernández (May 23 2022 at 06:50):

At least now I'm almost sure I have the correct definition

Violeta Hernández (May 23 2022 at 06:50):

I had to extrapolate a bit since every source I looked at just gave the Cantor form definition instead of the recursive one

Violeta Hernández (May 23 2022 at 07:20):

@Junyan Xu hydra PR got merged! I got a bit distracted as of late with the whole natural operations schtick, but I'll PR some auxiliary lemmas on cut_expand shortly and hopefully finish the proof with them

Junyan Xu (May 23 2022 at 09:10):

A few days ago I experimented with the idea of exploiting symmetry to reduce the number of cases in the surreal multiplication proof; e.g. there are 2x2x3=12 cases to show P1 (that x*y is numeric), but I can almost reduce it to just 3 cases using P24_neg, P24_neg', and docs#pgame.quot_mul_neg, docs#pgame.quot_mul_comm etc. (The 3 cases are used to show left_lt_right, and lines 566-581 use left_lt_right to show all 2x2=4 cases. Line 582 onwards are old proofs.) It seems the < and > cases could also follow from one another. However, using symmetry introduces additional obligations to prove identities and handle induction hypotheses (?), so I'm not sure the proofs will end up shorter. I only thought carefully about proving P1, but I expect to exploit symmetry when proving P2 and P4, you'd still apply the symmetries to P2 and P4 instead of P1.

The code is obviously very disorganized so I wouldn't be surprised if it ends up being unhelpful ... However
I am at a conference and don't expect to be able to work on this in the next few days, and since you are getting back to this, I decided to push it in the hope that it could be somewhat inspirational.

Violeta Hernández (May 23 2022 at 12:36):

(deleted)

Violeta Hernández (May 23 2022 at 12:39):

By the way, as for a tactic to solve numeric obligations

Violeta Hernández (May 23 2022 at 12:40):

You can often use solve_by_elim [numeric.move_left, numeric.move_right]

Violeta Hernández (May 23 2022 at 21:44):

I'm wondering

Violeta Hernández (May 23 2022 at 21:45):

Given that the order relations on games are much, much nicer to work with than those on games, should we have a simp lemma converting between them? Or is that dangerous?

Violeta Hernández (May 23 2022 at 21:45):

And if making it a simp lemma isn't the best idea, could it be made norm_cast? I don't know how that works really

Violeta Hernández (May 23 2022 at 21:51):

At the very least we should have the lemmas for rewriting

Eric Rodriguez (May 23 2022 at 21:58):

Violeta Hernández said:

Given that the order relations on games are much, much nicer to work with than those on games, should we have a simp lemma converting between them? Or is that dangerous?

one of these should be pgames and I guess it's the first one

Violeta Hernández (May 23 2022 at 22:15):

Oh sorry

Violeta Hernández (May 23 2022 at 22:15):

It's the second one

Eric Rodriguez (May 23 2022 at 22:16):

lost the 5050 ;b

Violeta Hernández (May 27 2022 at 00:30):

@Junyan Xu I have some time to look into this again

Violeta Hernández (May 27 2022 at 00:30):

What's the general idea in your simplification? I'm having a bit of a hard time following it

Violeta Hernández (May 27 2022 at 01:27):

I agree with some of your ideas, like separating P1 and such into definitions and proving comm lemmas and the like. This should make the proof shorter and easier to follow.

Violeta Hernández (May 27 2022 at 01:29):

I do disagree with various of your ideas here. mul_args should have only two constructors. That way, you get a stronger induction hypothesis for less work (instead of trying to prove that P1 implies both P2 and P4 for the same set of values).

Violeta Hernández (May 27 2022 at 01:29):

Further, defining an ad-hoc relation on mul_args and proving it well founded wouldn't really lead to less work, since we'd still have to prove exactly the same things we're currently proving to prove that it's a subrelation of cut_expand.

Violeta Hernández (May 27 2022 at 01:31):

And totally disagree on defining move_left on {x // numeric x}. There's absolutely no reason we should be working directly with these subtypes. Just separate x and numeric x.

Violeta Hernández (May 27 2022 at 01:37):

Btw I PR'd the lemmas to cast inequalities from pgame to game, and the lemma that relabellings preserve being numeric

Violeta Hernández (May 27 2022 at 01:52):

Oh and also, I don't think that tactic to prove games numeric would be all that useful, since almost always you just apply numeric.move_left and numeric.move_right once or twice

Junyan Xu (May 28 2022 at 15:51):

Violeta Hernández said:

And totally disagree on defining move_left on {x // numeric x}. There's absolutely no reason we should be working directly with these subtypes. Just separate x and numeric x.

Those are just some random ideas that are not necessarily good. However it seems nice to change numeric from a predicate to set, which allows us to write x : numeric through coercion instead of x : {x // numeric x}: https://github.com/leanprover-community/mathlib/compare/surreal_set?expand=1#diff-cfb19983c79209e00ac093e670e2db956ca48e00238011a5854479250cbaec67L58

I am back from the conference and can probably flesh out and clean up more of my plan in branch#surreal_mul_symm this weekend; I'll keep you posted.

Violeta Hernández (May 29 2022 at 01:52):

Would that still allow for dot notation? If so that might be a good idea

Violeta Hernández (May 29 2022 at 02:15):

From your code it seems like the answer is yes

Violeta Hernández (May 29 2022 at 02:15):

Yeah, this looks like a good idea

Junyan Xu (Jun 01 2022 at 06:09):

I've finished the proof of P1 with my symmetry approach at https://github.com/leanprover-community/mathlib/compare/surreal_mul_symm'?expand=1#diff-cfb19983c79209e00ac093e670e2db956ca48e00238011a5854479250cbaec67R318
I'll leave P2 and P4 to tomorrow.

Violeta Hernández (Jun 01 2022 at 18:47):

Oh nice! I haven't been nearly as free as I thought I'd be, but I'll check your code thoroughly as soon as I can

Violeta Hernández (Jun 10 2022 at 00:19):

Should the type equality lemmas like x.left_moves = y be simp?

Violeta Hernández (Jun 10 2022 at 00:19):

I've been told previously that they shouldn't be, but it seems like all over the code this decision has already been taken as a "yes"

Violeta Hernández (Jun 10 2022 at 00:20):

Consider for instance docs#pgame.zero_left_moves or docs#pgame.one_left_moves or docs#pgame.pow_half_left_moves

Violeta Hernández (Jun 10 2022 at 00:20):

Moreover, zero_left_moves is actually used a few times in some dsimps

Violeta Hernández (Jun 10 2022 at 00:24):

The rationale I got for not making them simp is that type equality is finicky and that this could break things. But surely simp will just not work in those instances?

Violeta Hernández (Jun 10 2022 at 00:24):

Moreover, there's a few simp lemmas on types like pempty and punit that aren't available on general empty / unique types

Violeta Hernández (Jun 10 2022 at 00:25):

Like ∀ x : pempty, p x and (∀ x : punit, p x) ↔ p punit.star

Violeta Hernández (Jun 10 2022 at 01:05):

Well, I guess something that should be done anyways is generalizing forall_pempty and exists_pempty to empty types

Junyan Xu (Jun 10 2022 at 02:44):

I made an attempt to redefine pSet and pgame such that they have the correct notion of equality (namely "identity") out of the box, where type equality is replaced by set equality, but Lean does not accept it:

import data.set.basic
universe u

structure indexed_set (α : Type u) : Type u := (I : Sort u) (f : I  α)

instance (α : Type u) : setoid (indexed_set α) :=
{ r := λ s₁ s₂, set.range s₁.f = set.range s₂.f,
  iseqv := λ _, rfl, λ _ _, eq.symm, λ _ _ _, eq.trans }

inductive pSet : Type u
| mk (s : quotient $ indexed_set.setoid pSet) : pSet

inductive pgame : Type u
| mk (L R : quotient $ indexed_set.setoid pgame) : pgame
/-inductive type being declared can only be nested
inside the parameters of other inductive types -/

I then immediately found this thread confirming that quotient is forbidden, so we're out of luck.

Junyan Xu (Jun 10 2022 at 03:06):

It seems to me that allowing quotient won't lead to paradoxes that arise when allowing set, but am not sure whether there are other technical problems.

Personally, I am fine with making type equalities simp lemmas in the situation of pgames.

Junyan Xu (Jun 10 2022 at 03:24):

But, the philosophy is that we care about the underlying set and not the indexing type, so maybe this and the right/existential versions are more benign lemmas:

lemma left_moves_induction {xl xr xL xR} (P : pgame  Prop) :
  ( x : (mk xl xr xL xR).left_moves, P $ move_left _ x)  ( x : xl, P $ xL x) := iff.rfl

Violeta Hernández (Jun 10 2022 at 03:27):

Sure, but those won't fire whenever the game in question isn't reducible

Violeta Hernández (Jun 10 2022 at 03:29):

Also, there's a few rare situations in which we do care about the types, though granted, they arise only because of our specific design. That's when we're proving equalities (not equivalences or relabellings) between pre-games.

Violeta Hernández (Jun 10 2022 at 03:29):

Or when we're showing that the move sets for some game are empty or unique

Violeta Hernández (Jun 10 2022 at 03:31):

Most of the time you can just use pempty.is_empty or punit.unique for these proofs, but if the definition is irreducible, then you need to do some type rewriting

Violeta Hernández (Jun 10 2022 at 03:31):

Something like that happens in the ordinal.lean file

Junyan Xu (Jun 10 2022 at 04:09):

Whether the type is reducible, you should be able to do apply (left_moves_induction _).2.

Violeta Hernández (Jun 10 2022 at 04:13):

That wouldn't work either

Violeta Hernández (Jun 10 2022 at 04:13):

I'm talking about games that aren't definitionally equal to anything of the form mk xl xr xL xR

Junyan Xu (Jun 10 2022 at 04:15):

I'm talking about games that aren't definitionally equal to anything of the form mk xl xr xL xR

For example?

Junyan Xu (Jun 10 2022 at 04:17):

The simp lemmas you want are for explicit pgames, right? The examples you gave were zero, one, and pow_half n. And mk is the only constructor...

Violeta Hernández (Jun 10 2022 at 04:25):

ordinal.to_pgame

Violeta Hernández (Jun 10 2022 at 04:25):

Since it's defined via the equation compiler, you need to use rw to_pgame to prove the equality with mk _ _ _ _

Violeta Hernández (Jun 10 2022 at 04:25):

Which is definitely odd, but that's how it goes

Violeta Hernández (Jun 10 2022 at 04:26):

Another example might be a pre-game retrieved from an exists

Violeta Hernández (Jun 10 2022 at 04:26):

It's not going to be def-eq to any specific mk _ _ _ _

Violeta Hernández (Jun 10 2022 at 04:26):

Although of course you can case on it

Junyan Xu (Jun 10 2022 at 04:35):

Another example might be a pre-game retrieved from an exists

I don't think you can simp its moves though because you don't know what they are ...

Junyan Xu (Jun 10 2022 at 04:36):

For ordinal.to_pgame, yes if you #print it you see it's well_founded.fix under the hood:

def ordinal.to_pgame._main._pack : Π (_x : ordinal), (λ (_x : ordinal), pgame) _x :=
λ (_x : ordinal),
  has_well_founded.wf.fix
    (λ (_x : ordinal),
       id_rhs ((Π (_y : ordinal), has_well_founded.r _y _x  pgame)  pgame)
         (λ (_F : Π (_y : ordinal), has_well_founded.r _y _x  pgame),
            mk (quotient.out _x).α pempty
              (λ (x : (quotient.out _x).α), let hwf : typein has_lt.lt x < _x := _ in _F (typein has_lt.lt x) hwf)
              pempty.elim))
    _x

Junyan Xu (Jun 10 2022 at 04:55):

But in this case to_pgame_left_moves etc. aren't defeq, so they won't fire with dsimp and rewriting by them will cause problems, right? Seems a situation where induction principles would be more useful.

Junyan Xu (Jun 10 2022 at 06:08):

Would this be more convenient?

lemma to_pgame_move_left (o : ordinal) :
  set.range o.to_pgame.move_left = set.range (λ x : o.out.α, (typein (<) x).to_pgame) :=
by { rw [to_pgame], refl }

together with lemmas:

import data.set.basic

variables {α : Type*} {I₁ I₂ : Sort*} (f₁ : I₁  α) (f₂ : I₂  α)

lemma range_eq_iff_forall_exists :
  set.range f₁ = set.range f₂  ( i₁,  i₂, f₂ i₂ = f₁ i₁)  ( i₂,  i₁, f₁ i₁ = f₂ i₂) :=
λ h, λ i₁, h.subst (set.mem_range_self i₁), λ i₂, h.substr (set.mem_range_self i₂)⟩,
 λ h, set.ext $ λ x, λ i₁, hx⟩, hx  h.1 i₁, λ i₂, hx⟩, hx  h.2 i₂⟩⟩

lemma range_eq_iff_forall_iff :
  set.range f₁ = set.range f₂   (P : α  Prop), ( i, P $ f₁ i)  ( i, P $ f₂ i) :=
begin
  split,
  { intros h P, iterate 2 { rw  set.forall_range_iff }, rw h },
  { intros h, ext x, split; revert x; rw set.forall_range_iff; intro i,
    exacts [(h $ set.range f₂).2 (λ i, i, rfl⟩) i, (h $ set.range f₁).1 (λ i, i, rfl⟩) i] },
end

lemma range_eq_iff_exists_iff :
  set.range f₁ = set.range f₂   (P : α  Prop), ( i, P $ f₁ i)  ( i, P $ f₂ i) :=
begin
  simp_rw [ not_forall_not, not_iff_not], rw range_eq_iff_forall_iff f₁ f₂, split,
  { exact λ h P, h (λ x, ¬P x) },
  { intros h P, convert h (λ x, ¬P x); simp_rw not_not },
end

Violeta Hernández (Jun 10 2022 at 06:24):

More convenient than what? I think the current API works pretty well already

Violeta Hernández (Jun 10 2022 at 06:25):

to_left_moves_to_pgame together with its small API are really all you need

Junyan Xu (Jun 10 2022 at 06:31):

My point is that in general when the pgame is defined recursively and not defeq to some mk, you may prove an equality between set.range and automatically unlock the forall/exists lemmas.

More generally, if you want to change the indexing type, you may show one of the four conditions and automatically get the other three.

It doesn't seem to me that the APIs around ordinal.to_pgame are completely satisfactory; I think we should strive to eliminate heq lemmas if possible.

Violeta Hernández (Jun 10 2022 at 06:33):

That approach certainly works, though I still prefer the current one. Building the equivalence between set.Iio o and o.to_pgame.left_moves just makes it really convenient to build a given move, or to reason about an arbitrary one.

Violeta Hernández (Jun 10 2022 at 06:34):

There is a heq lemma, but it's used only once and never again. If we wanted to, we could make it private and it wouldn't break anything. The API doesn't really depend on it.

Violeta Hernández (Jun 10 2022 at 06:41):

Moreover, the current aprroach allows you to explicitly build a move that leads to a certain position, instead of just declaring it exists

Violeta Hernández (Jun 10 2022 at 06:41):

This pattern of building equivalences between left/right moves of games and simpler types is one I've replicated throughout the whole game API, and it's worked very well

Junyan Xu (Jun 10 2022 at 07:10):

I think my approach still allows you to build move if desired; it only forgets the equivalence (or weaker correspondences) that goes between the two indexing types, but still remembers the two indexing functions. For example, with this

example (o : ordinal) :
  set.range o.to_pgame.move_left = set.range (λ x : set.Iio o, to_pgame x) :=
begin
  symmetry, rw to_pgame, convert function.surjective.range_comp (enum_iso_out o).surjective _,
  ext, congr, exact subtype.ext_iff.1 ((enum_iso_out o).symm_apply_apply x).symm,
end

you can choose to build the move using o.to_pgame.move_left or using to_pgame.

Violeta Hernández (Jun 10 2022 at 07:17):

What I mean is, say you want a move i such that (to_pgame 2).move_left i = to_pgame 1. With your approach, you need to show to_pgame 1 ∈ set.range (λ x : set.Iio 2, to_pgame x) (which is pretty easy, granted), then rewrite into to_pgame 1 ∈ set.range o.to_pgame.move_left, and that still only tells you that there exists some move with the property you want. You need to use cases to actually retrieve the move.

If you're building data, you'll instead need to call classical.some.

With my approach, you can explicitly build said move as to_left_moves_to_pgame ⟨1, one_lt_two⟩, and simp will do the rest.

Violeta Hernández (Jun 10 2022 at 07:21):

docs#ordinal.to_pgame_lf is a very good example of this in action.

Junyan Xu (Jun 10 2022 at 07:42):

With my approach the proof for to_pgame_lf would be:

lemma range_move_left_eq_range_to_pgame (o : ordinal) :
  set.range o.to_pgame.move_left = set.range (λ x : set.Iio o, to_pgame x) :=
sorry /- see earlier post -/

lemma range_eq_iff_forall_iff {α : Type*} {I₁ I₂ : Sort*} (f₁ : I₁  α) (f₂ : I₂  α) :
  set.range f₁ = set.range f₂   (P : α  Prop), ( i, P $ f₁ i)  ( i, P $ f₂ i) :=
sorry /- see earlier post -/

theorem to_pgame_lf {a b : ordinal} (h : a < b) : a.to_pgame  b.to_pgame :=
((range_eq_iff_forall_iff _ _).1 (range_move_left_eq_range_to_pgame b) (function.swap lf _)).1 move_left_lf a, h

Violeta Hernández (Jun 10 2022 at 07:45):

Yeah, I feel like my approach is simpler both conceptually and in terms of the final code

Violeta Hernández (Jun 10 2022 at 07:45):

Seems odd having to use a clever induction principle for something like this

Violeta Hernández (Jun 10 2022 at 07:46):

Especially when the pencil and paper proof of this theorem is just "since a < b, there's a left move from b.to_pgame to a.to_pgame, Q.E.D"

Junyan Xu (Jun 10 2022 at 14:42):

The following might everything more convenient and will make simp lemmas like docs#pgame.zero_left_moves obsolete, but may need a large refactor: namely, for each concrete pgame, register two instances has_left_moves and has_right_moves; for example, for ordinal.to_pgamewe register the instance has_left_moves (o.to_pgame) which holds the data of an indexing type set.Iio o (which lies in a higher universe but is more convenient), the indexing function λ x, to_pgame x, and a proof that the range of the indexing function is equal to the range of x.move_left. For the theorems that mentions left_moves and move_left, make a version that takes an additional typeclass argument has_left_moves x, and use the indexing type as left_moves and the indexing function as move_left instead; the new version would be proved from the old using my range_eq_iff_forall_iff and range_eq_iff_exists_iff. This way, I think we can forget about left_moves and always directly work with the desired indexing function.

Violeta Hernández (Jun 10 2022 at 16:34):

I'm going to oppose this on grounds that it's just too clever

Violeta Hernández (Jun 10 2022 at 16:34):

I've worked with large and rapidly changing codebases before, and being too clever is what ultimately led to their downfall

Violeta Hernández (Jun 10 2022 at 16:36):

And really, I don't want to forget about the concrete types. They're a quirk of our design choices, sure, but they're quirks that make sense and are convenient within type theory, which is what we're working with.

Junyan Xu (Jun 10 2022 at 17:21):

Well, I'm just trying my best to address your original question about simp lemmas, but it seems you are not open enough to new ideas :( I think in mathlib we are not afraid of doing large refactors, not to mention that the current combinatorial game library is only an isolated small corner of mathlib.

As a rule of thumb, I think in the case of non-defeq indexing types, the indexing type and the indexing function should be bundled and rewritten together, and equality between set.range is the condition that allows you to rewrite between two such bundled functions.

My approach doesn't forget about "concrete types", instead it designate a preferred indexing type via typeclass resolution. In the case of ordinal.pgame, Iio is arguably the indexing type we want, but it lives in a higher universe, so we must complete the construction using o.out.α first and change the indexing type later. Once we have our desired indexing type and function and know it's equivalent to the original via set.range, I do think we can forget the original indexing type and the transition functions between the indexing types. After all combinatorial game theory talks about sets of left and right options, so if there's anything that depends on the particular indexing type, that doesn't belong to CGT and it would be nice if our API hides it.

Violeta Hernández (Jun 10 2022 at 17:32):

Don't get me wrong, I'm open to new ideas, but hiding implementation details via an ad-hoc typeclass just doesn't seem like a good one. Once again, the current approach achieves the same thing, and I argue that it achieves it more clearly and easily.

Violeta Hernández (Jun 10 2022 at 17:34):

And even if we went with your approach, I'm not sure how that answers the question about simp lemmas. We might need them less often, sure, but there's still the possibility.

Violeta Hernández (Jun 10 2022 at 17:38):

In fact, even if we went with your approach, why would using set.range be preferable to the equivalence approach? And if it isn't, and we went with the equivalence approach, surely the only thing we'd accomplish is hiding the equivalence behind a typeclass, which doesn't seem like a good idea?

Junyan Xu (Jun 10 2022 at 17:55):

set.range is convenient as there is a large amount of APIs around it, for example docs#function.surjective.range_comp allows you to reindex by the domain of a surjective function to the original indexing type; an equivalence is in particular surjective.

Notice that since I aim to forget the original indexing type, there is no way I can keep the equivalence (which I referred to more generally as the "transition function"), so it has to be forgotten as well. Of course, if you reindex using g ∘ f instead of g, then the transition function f is effectively still there. In the ordinal.to_pgame case however, we have another preferred indexing function h = to_pgame with domain Iio o, and after proving that h = g ∘ f, we indeed forget about f and g, which is good IMO.

Violeta Hernández (Jun 10 2022 at 19:11):

There is a lot of API around equivalences too

Violeta Hernández (Jun 10 2022 at 19:13):

Another grounds on which I disagree with this refactor is that in Lean, types and functions are simply much more convenient than sets, no matter how you put it. Rephrasing traditionally set-theoretic concepts in terms of types is something mathlib doesn't shy away from, and I don't think we should either unless there's a clear benefit.

Junyan Xu (Jun 10 2022 at 19:22):

Let me repeat: I never propose to throw away the indexing type anywhere above. Even in the quotient indexed_set.setoid approach which gives you a type of small sets (as opposed to proper classes), you still construct a small set from an indexing type/function. What I said is results in CGT should be independent of the indexing type/function, so you are free to change to any equivalent indexing type/function, and this post contains the essential APIs to work with the equivalence. set.range is just the simplest way (among the four equivalent ways) of stating the most general condition (more general than equivalences) for two indexing types/functions to be considered equivalent. Typeclass mechanism can be used to automatically come up with the preferred indexing type/function.

Violeta Hernández (Jun 10 2022 at 19:30):

I see, so your approach is strictly more general

Violeta Hernández (Jun 10 2022 at 19:32):

I don't think there's any current circumstance where we need to deduplicate moves, and I can't think of a possible scenario for this

Junyan Xu (Jun 11 2022 at 02:19):

The intention of my current proposal is not to deduplicate move or to define the identity relation on pgames (that would require a condition more general than equality of ranges, as some options may be identical but not equal), but merely to freely switch the original move indexing type/function to our preferred one.

I think in your original examples zero, one, and pow_half, where the desired indexing type/function is defeq to the original, my proposal isn't of much use and dsimp would be good enough; the ordinal.to_pgame example that you raised later is a situation my proposal is aimed at: we want to change to a indexing type that is not only not defeq to the original, but actually live in a higher universe.

In the meantime, I've come up with another situation where my proposal would be useful, namely (-x).left_moves, which is equal to x.right_moves but not defeq, which necessitates docs#pgame.to_left_moves_neg and 8 lemmas following it. If we make x.right_moves the default indexing type for (-x).left_moves, we should be able to reduce the number of lemmas and simplify the development. I think the same holds true for addition (docs#pgame.to_left_moves_add) and multiplication as well, and other cases of non-defeq-ness due to the use of well_founded.fix (feel free to add examples!).

Violeta Hernández (Jun 11 2022 at 05:09):

Ah, I can see the appeal now

Violeta Hernández (Jun 11 2022 at 05:09):

I think there's an alternate solution that's a bit easier, though

Violeta Hernández (Jun 11 2022 at 05:09):

We can do induction lemmas

Violeta Hernández (Jun 11 2022 at 05:10):

"if a proposition holds true for any left move of x, it holds for any right move of -x"

Violeta Hernández (Jun 11 2022 at 05:10):

I think this would achieve the same goal more easily

Violeta Hernández (Jun 14 2022 at 18:08):

@Scott Morrison @Junyan Xu I think impartial shouldn't be a typeclass

Violeta Hernández (Jun 14 2022 at 18:09):

#3974, which is where it was made into a typeclass, gives two justifications:

  • it means you don't have to use lemmas like impartial_add all the time
  • grundy_value doesn't have this annoying extra argument

Violeta Hernández (Jun 14 2022 at 18:10):

The second reasoning is now obsolete, since I removed the impartial argument from grundy_value a while back
docs#pgame.grundy_value

Violeta Hernández (Jun 14 2022 at 18:10):

Although the theorems need it, the definition itself doesn't

Violeta Hernández (Jun 14 2022 at 18:11):

The first argument seems like not a very compelling reason on its own

Violeta Hernández (Jun 14 2022 at 18:11):

The major con of this decision is that it goes against the general design style that typeclasses are meant for types, with rare exceptions

Violeta Hernández (Jun 14 2022 at 18:13):

And of course, there's the issues of using unfreezingI

Violeta Hernández (Jun 14 2022 at 18:13):

I wonder what Scott's reasoning for being against this was

Violeta Hernández (Jun 14 2022 at 19:03):

(deleted)

Junyan Xu (Jun 16 2022 at 02:37):

One advantage of making it an typeclass is that we can introduce instance like

instance impartial_mk (α : Type u) (f : α  pgame.{u}) [ a, impartial (f a)] :
  impartial (pgame.mk α α f f) := sorry

and for new impartial pgames you define, you can use @[derive impartial] to automatically generate the instance. I don't know how good Lean is at inferring instances like ∀ a, impartial (f a), though.
This is more suitable for the "actual" impartial games, requiring the left options to be the same as the right options. It's not so suitable for the current mathlib definition, because the G ≈ -G condition can be nontrivial to verify and is not a typeclass.

Similarly, we might introduce

instance dicotic_mk_nonempty (xl xr) [nonempty xl] [nonempty xr]
  (xL : xl  pgame) (xR : xr  pgame) [ i, dicotic (xL i)] [ i, dicotic (xR i)] :
  dicotic (mk xl xr xL xR) := sorry

instance dicotic_mk_empty (xl xr) [is_empty xl] [is_empty xr] (xL : xl  pgame) (xR : xr  pgame) :
  dicotic (mk xl xr xL xR) := sorry
/- relabelling of the zero game, not of much use; `zero_dicotic` plus the above should suffice. -/
/- basically the same as `dicotic_of_is_empty_moves`. -/

If Lean has trouble inferring the forall instances, we might introduce

instance dicotic_mk_const (xl xr xL xR) [nonempty xl] [nonempty xr] [dicotic xL] [dicotic xR] :
  dicotic (mk xl xr (λ _, xL) (λ _, xR)) := sorry

which covers the case where xl and xr are punit.

Violeta Hernández (Jun 16 2022 at 03:06):

I'm still not sure if this justifies the typeclass approach. Sure, it's more convenient in some ways, but we aren't making every definition into a typeclass and I'm interested in knowing why

Junyan Xu (Jun 16 2022 at 04:52):

The major con of this decision is that it goes against the general design style that typeclasses are meant for types, with rare exceptions

Indeed not many examples of typeclasses come to mind that are not for types; typeclasses in file#order/rel_classes, file#algebra/covariant_and_contravariant, core/init/algebra/classes are mostly not classes on types but maybe they're meant for some canonical operations/relations on types.

In this particular case, since pgames are constructed in a hierarchical way, I think it makes sense to introduce classes on elements of pgame: deriving a property of a pgame from similar properties of its constituents is akin to deriving structure on / property of a more complicated type constructed from a simpler type, from the structure on / property of the simpler type, like docs#witt_vector.comm_ring or docs#mv_polynomial.unique_factorization_monoid. When we can automate derivation of a structure/property simply by pattern matching, and if it will be used a lot, then I think it's worth being made a typeclass and declare the patterns as instances.

Violeta Hernández (Jun 16 2022 at 06:27):

Well in that case, it's probably worth making numeric into a typeclass too

Violeta Hernández (Jun 16 2022 at 06:27):

I wonder what the maintainers think

Junyan Xu (Jun 16 2022 at 06:35):

For numeric, the left_lt_right condition isn't usually trivial to check, and lt isn't a typeclass. Of course, if one of the left/right move set is empty, then the condition is vacuously true, and this case could be made an instance.

Violeta Hernández (Jun 16 2022 at 16:30):

But why a typeclass? We already have a theorem that does the same thing

Violeta Hernández (Jun 16 2022 at 16:31):

docs#pgame.numeric_of_is_empty_left_moves

Violeta Hernández (Jun 16 2022 at 16:33):

Also, I've been told that an alternate and simpler approach is to do the thing you suggested a while back, a tactic to solve numeric goals

Violeta Hernández (Jun 16 2022 at 16:33):

This does have a precedent in mathlib, with tactics like monotonicity and continuity

Violeta Hernández (Jun 16 2022 at 16:34):

That said, I still haven't gotten any specific reason against the typeclass pattern, other than various people finding it annoying and little use throughout mathlib

Violeta Hernández (Jun 16 2022 at 17:12):

@Bhavik Mehta just told me that the main reason is to avoid overuse of typeclass inference

Violeta Hernández (Jun 16 2022 at 17:13):

So now the question is, how do we make these numericity and impartiality and whatnot tactics?

Bhavik Mehta (Jun 16 2022 at 18:45):

Violeta Hernández said:

Bhavik Mehta just told me that the main reason is to avoid overuse of typeclass inference

At least, that's what I think the reason is - there could well be a better reason!

Junyan Xu (Jun 16 2022 at 20:54):

Instead of replacing typeclass, the coherence tactic instead introduced two typeclasses, so I doesn't seem to be a universal pattern to replace typeclasses by tactics. And after all, apply_instance is also a tactic and it solves goals if you set up the instances right.

A key difference between numeric/impartial/dicotic and continuity/measurability/monotonicity is that the latter are used widely across mathlib, so overusing typeclasses may cause performance issues, especially when there are many instances of the typeclasses. For the relatively secluded theory of combinatorial games, it seems need not worry much about performance issues. Here (as in the case of coherence) I consider typeclass inference just as a domain-specific way to automate by pattern matching. (I think the typeclasses for the coherence are more secluded and only used in the tactic though.)

Violeta Hernández (Jun 16 2022 at 21:01):

I'm on board with you, but I still want to hear a maintainer's opinion

Junyan Xu (Jun 16 2022 at 21:03):

Maybe let us hear from @Scott Morrison since he authored both the first coherence tactic #13125 and part of the game library.

Anne Baanen (Jun 17 2022 at 09:39):

In my experience, typeclasses work well if nearly all instances can be inferred using only a set of theorems of the form P1 (f x) → P2 (g y) → ... → Q x y ... z, where f x and g y are stated literally and don't do any computation, and there's never theorems P → P, or both P → Q and Q → P at the same time. As soon as you have to deal with equalities (your instance is on fx' and fx' = f x but not literally the same expression), or there is the possibility of looping, you're going to get a bad time.

Anne Baanen (Jun 17 2022 at 09:42):

In this case, if there is a nontrivial equality on pgame (i.e. two pgames can be definitionally or propositionally equal without literally being written the same), I suspect typeclasses can't keep up.

Anne Baanen (Jun 17 2022 at 09:44):

I actually suspect the inference speed is not a huge issue in this case, in my experience the real slowdown comes from checking definitional equality of instances in types that have instances that have types etc.

Anne Baanen (Jun 17 2022 at 09:47):

One approach you could consider is to define a tactic meta def impartial_tac := apply_instance and use this as a docs#auto_param: instead of [impartial p] you write (h : impartial p . impartial_tac) and you basically get the same semantics at the moment, and when instances don't scale you replace the code of impartial_tac with something smarter.

Anne Baanen (Jun 17 2022 at 09:49):

Drawback is that instance implicits like [impartial p] are much more common than auto_params so there's the possiblity that some tactics (or some parts of the elaborator) can't handle auto_param well.

Anne Baanen (Jun 17 2022 at 09:50):

Which is why you should first try using apply_instance as definition for the auto_param, then you can be sure that nothing breaks before you upgrade the tactic.

Kevin Buzzard (Jun 17 2022 at 11:15):

@Anne Baanen I really love the insights you give to this community :heart: Thank you.

Violeta Hernández (Jun 18 2022 at 05:16):

Do we have any consensus on keeping/refactoring impartial?

Violeta Hernández (Jun 18 2022 at 05:18):

It seems like the general consensus isn't a good design pattern even though it's slightly useful sometimes

Violeta Hernández (Jun 18 2022 at 05:18):

So, not really a strong argument either way

Violeta Hernández (Jun 18 2022 at 05:26):

There might be a sort of "death by a thousand cuts" argument against having it as a class

Violeta Hernández (Jun 18 2022 at 05:36):

Actually, let me list out pros and cons

Pros:

  • theorems like impartial_add, impartial_neg, impartial_sub, impartial_zero can be omitted
  • we might be able to derive the class in some common circumstances

Cons:

  • we have resetI issues when we want to manipulate an impartial pre-game, like e.g. doing induction on it or unfolding the definition
  • pre-game equality is really finicky and rarely definitional (due to the inductive definitions), so instances aren't super effective
  • typeclasses seem to be somewhat of an antipattern in this situation (there's limited precedent)
  • grundy_value no longer depends on the typeclass parameter, this was one of the original reasons for the typeclass
  • a tactic could do both of the pros

Violeta Hernández (Jun 18 2022 at 05:41):

I'd also argue that the pros are barely anything to write home about. It's super simple to just call these theorems explicitly.

Violeta Hernández (Jun 18 2022 at 05:41):

So, even if there's not a strong argument either way, there seem to be much more weak arguments in favor of ditching the class

Violeta Hernández (Jun 18 2022 at 05:43):

Oh, and one last con: if we keep impartial as a class, then it stands to reason we should do the same for other definitions that are closed under basic operations, like numeric games, or small games, or dicotic games, so the cons scale up

Eric Wieser (Jun 18 2022 at 09:44):

I think we're missing some basic machinery mathlib-wide for propositions closed under operations

Eric Wieser (Jun 18 2022 at 09:45):

docs#submonoid_class and docs#is_submonoid come close, but neither seems to be used much for this type of thing

Violeta Hernández (Jun 25 2022 at 05:05):

As you may have noticed, I've been a bit busy tying up what I consider to be loose ends with the game API

Violeta Hernández (Jun 25 2022 at 05:07):

Nothing incredibly important, but still, a lot of things that are breaking changes

Violeta Hernández (Jun 25 2022 at 05:08):

Once I'm done with all that, I'll be free to clean up the multiplication proof and hopefully PR it

Violeta Hernández (Jun 25 2022 at 06:26):

Unrelated but what's up with docs#pgame.restricted?

Violeta Hernández (Jun 25 2022 at 06:27):

Might have asked before but I'm thinking about this again and I'm confused

Violeta Hernández (Jun 25 2022 at 06:27):

The description would be accurate if L and R were injective, but they aren't

Violeta Hernández (Jun 25 2022 at 06:28):

And I can't think of any natural examples of a game being a restriction of another, except for the case of relabellings

Violeta Hernández (Jun 25 2022 at 06:28):

So this really just seems like an auxiliary definition of sorts

Violeta Hernández (Jun 25 2022 at 06:32):

Maybe the idea was to define it in terms of injective functions and prove x <= y from that, but then someone realized you didn't need the injective hypothesis

Violeta Hernández (Jun 25 2022 at 06:50):

I don't think the definition is worthless, it's certainly an... interesting way to show an inequality. But maybe it should be renamed, and at the very least the docstring should be changed

Violeta Hernández (Jun 25 2022 at 17:00):

Another question, what's up with docs#pgame.equiv.has_coe?

Violeta Hernández (Jun 25 2022 at 17:00):

I don't think it's currently used anywhere

Violeta Hernández (Jun 25 2022 at 17:01):

Is there any precedent for using casts for proofs?

Violeta Hernández (Jun 26 2022 at 01:31):

I just had this idea that, in retrospect, is something @Junyan Xu had suggested

Violeta Hernández (Jun 26 2022 at 01:32):

We should make a comparison function for games, that outputs one of four outcomes

Violeta Hernández (Jun 26 2022 at 01:33):

Less, equivalent, greater, fuzzy

Violeta Hernández (Jun 26 2022 at 01:33):

There's a lot we could do with such a function

Violeta Hernández (Jun 26 2022 at 01:36):

Of particular interest to me are the fact this function can be lifted to games and surreals, and that its value is the same when you add a game to both sides

Violeta Hernández (Jun 26 2022 at 01:37):

By proving this, we could get a lot of lemmas currently either taking a lot of API space or missing, almost for free

Violeta Hernández (Jun 26 2022 at 02:01):

For instance, to prove cmp w x = cmp y z it suffices to prove w \le x \iff y \le z and x \le w \iff z \le y, which in many cases can be reduced to just the \le case by symmetry

Violeta Hernández (Jun 26 2022 at 02:02):

But that gives you r w x \iff r y z for \le and < and \equiv and \fuzzy for free which is super cool

Violeta Hernández (Jun 26 2022 at 02:07):

I'll prove some basic theorems about this comparison function

Violeta Hernández (Jun 26 2022 at 02:07):

I can't do a lot since I currently have like three sweeping changes on the same files PR'd

Violeta Hernández (Jun 26 2022 at 02:13):

Speaking of which, if so done could review my "comparison API review" and my refactor on relabellings that would be awesome

Violeta Hernández (Jun 26 2022 at 06:34):

Oh wow

Violeta Hernández (Jun 26 2022 at 06:35):

This new API on comparison is so overpowered I was able to comfortably prove pretty much every single variant of the covariant and contravariant theorems on pre-games without ever even appealing to the game structure

Violeta Hernández (Jun 26 2022 at 06:46):

It's extremely boilerplatey, but that's what peak performance looks like ;)

Yaël Dillies (Jun 26 2022 at 20:09):

Violeta Hernández said:

For instance, to prove cmp w x = cmp y z it suffices to prove w ≤ x ↔ y ≤ z and x ≤ w ↔ z ≤ y, which in many cases can be reduced to just the `≤ case by symmetry

Do you know about docs#lt_iff_lt_of_le_iff_le'?

Junyan Xu (Jun 26 2022 at 21:47):

Should we introduce fuzzy and lf for any preorder? And equiv (≈), which is already there if we use docs#antisymm_rel.setoid.

Junyan Xu (Jun 26 2022 at 21:50):

I think this cmp function can be defined on all preorders without trouble.

Yaël Dillies (Jun 26 2022 at 21:51):

docs#cmp is already a thing, but it has linear orders in mind.

Violeta Hernández (Jun 26 2022 at 22:10):

Yaël Dillies said:

Violeta Hernández said:

For instance, to prove cmp w x = cmp y z it suffices to prove w ≤ x ↔ y ≤ z and x ≤ w ↔ z ≤ y, which in many cases can be reduced to just the `≤ case by symmetry

Do you know about docs#lt_iff_lt_of_le_iff_le'?

Yep, these aren't quite the same lemmas though

Yaël Dillies (Jun 26 2022 at 22:10):

Sure, you also need docs#iff.not. But my point is that those two are enough.

Violeta Hernández (Jun 26 2022 at 22:11):

Junyan Xu said:

Should we introduce fuzzy and lf for any preorder? And equiv (≈), which is already there if we use docs#antisymm_rel.setoid.

We discussed this back when I introduced lf. Our conclusion was basically that yes, we could define this on all partial orders, but it wouldn't be very useful, since we couldn't think of any other example where the relation ¬ x ≤ yhad any particular importance.

Violeta Hernández (Jun 26 2022 at 22:13):

Further, if we just defined it in the obvious way, ¬ x ≤ y, we'd mess up the def-eqs on pre-games. We'd need to take the approach of lt, where it's defined as a field of the preorder class, and that just seems awkward.

Violeta Hernández (Jun 26 2022 at 22:13):

Not to mention, the name lf wouldn't make sense at all outside of games

Violeta Hernández (Jun 26 2022 at 22:14):

Yaël Dillies said:

Sure, you also need docs#iff.not. But my point is that those two are enough.

I'm talking about a partial order here though, not a linear order

Violeta Hernández (Jun 26 2022 at 22:14):

Now as for a general cmp function on preorders, we could totally do that

Violeta Hernández (Jun 26 2022 at 22:15):

We should have the fields lt, gt, equiv, and incomp

Violeta Hernández (Jun 26 2022 at 22:15):

We can even provide some API for converting between these values and those given by cmp on linear orders

Yaël Dillies (Jun 26 2022 at 22:16):

Who talked about linear orders? docs#lt_iff_lt_of_le_iff_le is for linear_orders, but docs#lt_iff_lt_of_le_iff_le' is for any preorder.

Violeta Hernández (Jun 26 2022 at 22:17):

Oh I see! I read the wrong theorem

Violeta Hernández (Jun 26 2022 at 22:18):

Yeah, what I basically did was prove this but for all the other relations on games

Violeta Hernández (Jun 26 2022 at 22:24):

Yeah I think that's the play here

Violeta Hernández (Jun 26 2022 at 22:24):

I'll add the comparison function on preorders, then use it to prove results about pre-games

Violeta Hernández (Jun 27 2022 at 04:23):

I think I just deciphered what docs#pgame.restricted really "means"

Violeta Hernández (Jun 27 2022 at 04:23):

And it's... disappointing

Violeta Hernández (Jun 27 2022 at 04:24):

Replacr the functions L and R and the hypotheses by existence lemmas

Violeta Hernández (Jun 27 2022 at 04:26):

And you literally just get a weaker version of the converse of docs#pgame.le_def

Violeta Hernández (Jun 27 2022 at 04:27):

So really, any inequality x \le y you can prove by building a restriction, can be proven by just using le_def

Violeta Hernández (Jun 27 2022 at 04:27):

I think that settles it, this needs to go

Violeta Hernández (Jul 11 2022 at 15:29):

I'm back!

Violeta Hernández (Jul 11 2022 at 15:31):

Junyan Xu said:

I've finished the proof of P1 with my symmetry approach at https://github.com/leanprover-community/mathlib/compare/surreal_mul_symm'?expand=1#diff-cfb19983c79209e00ac093e670e2db956ca48e00238011a5854479250cbaec67R318
I'll leave P2 and P4 to tomorrow.

I'll try and merge this with what we have.

Violeta Hernández (Jul 11 2022 at 15:32):

I hadn't noticed your comments on docs#pgame.quot_mul_comm and docs#pgame.quot_neg_mul, that seems like a nice thing to do on the side

Violeta Hernández (Jul 22 2022 at 16:09):

I want to try PR-ing the mul_option stuff, but #15252 is currerntly blocking that

Violeta Hernández (Jul 22 2022 at 16:09):

I think that's the last thing we would need before PRing the surreal multiplication proof

Junyan Xu (Jul 22 2022 at 16:10):

#15252 has a merge conflict now

Violeta Hernández (Jul 22 2022 at 16:12):

Ah true, it will conflict with my recent swapping of arguments on relabellings

Violeta Hernández (Jul 22 2022 at 16:12):

On that note, I've been thinking about relabellings again

Violeta Hernández (Jul 22 2022 at 16:13):

I think I might have brought this up before, but now I'm more convinced: we eventually need to ditch relabellings in favor of extensional equivalence

Violeta Hernández (Jul 22 2022 at 16:13):

Relabellings are just a consequence of our implementation of games, which doesn't coincide with the ZFC one

Violeta Hernández (Jul 22 2022 at 16:16):

The definition of extensional equivalence, e.g. being "identical" games, would be pretty much the same one as for docs#pSet.equiv: x ≡ y when every left move of x is extensionally equivalent to some left move of y, and viceversa, and likewise for right moves

Violeta Hernández (Jul 22 2022 at 16:17):

We could then define the quotient of pre-games by extensional equivalence, which would correspond to the actual ZFC notion of pre-games

Violeta Hernández (Jul 22 2022 at 16:18):

It's definitely a confusing situation, since we would have pgame which is an implementation detail, then some other type (igame?) for ZFC pre-games (which they don't call pre-games, they just call them games), and then actually game corresponding to ZFC pre-games quotiented by antisymmetry

Violeta Hernández (Jul 22 2022 at 16:19):

And surreals of course

Violeta Hernández (Jul 22 2022 at 16:19):

The nice thing about igame is that you can define left and right moves of igame as igames themselves, which is something you can't do with neither games nor surreals

Violeta Hernández (Jul 22 2022 at 16:19):

Which means we can define the birthday of an igame too

Violeta Hernández (Jul 22 2022 at 16:20):

And we can also correctly define short igames as an igame with a finite left and right move set, and prove that an igame is short iff it has finite birthday, a result that isn't true on pgame

Violeta Hernández (Jul 22 2022 at 16:21):

The philosophy here would be the same one as with docs#pSet vs docs#Set: we treat the former as an implementation detail, only proving the necessary results, and move everything to the latter

Junyan Xu (Jul 22 2022 at 16:37):

Violeta Hernández said:

I think I might have brought this up before, but now I'm more convinced: we eventually need to ditch relabellings in favor of extensional equivalence

docs#pSet.equiv is purely in terms of quantifiers, while docs#pgame.relabelling has the equivalences/functions that go between the left/right moves of two equivalent games. I remember you previously said it's useful to have those functions; if that's true we may consider keeping relabelling instead of ditching it, but maybe the lesson from docs#pSet tells us we don't really need those functions.

Violeta Hernández (Jul 22 2022 at 16:48):

I previously said that we use those functions. The single place where they're used is in the definition of short, which as I've mentioned previously doesn't match mathematical usage.

Violeta Hernández (Jul 22 2022 at 16:48):

We could still define identical games in terms of functions, i.e. explicit maps from left moves of x to identical left moves of y, and all the others, instead of just having the quantifiers

Violeta Hernández (Jul 22 2022 at 16:49):

This might still enable the VM to play short games, though I don't know if that's a priority

Violeta Hernández (Jul 22 2022 at 16:50):

In any case, it's clear to me that relabellings are a quirk of Lean's type theory, rather that something mathematicians would care much about.

Violeta Hernández (Jul 22 2022 at 17:11):

@Scott Morrison was the one who made the code for short so I'd like to hear his opinion.

Violeta Hernández (Jul 24 2022 at 18:06):

I'm wondering

Violeta Hernández (Jul 24 2022 at 18:06):

If #15289 is merged, lf will no longer have a weird inductive definition

Violeta Hernández (Jul 24 2022 at 18:07):

It will literally be defined as (≥)ᶜ

Violeta Hernández (Jul 24 2022 at 18:08):

So, in order to be able to use this smoothly on both pgame and game, maybe we should make notation for (≥)ᶜ?

Violeta Hernández (Jul 24 2022 at 18:09):

I also suggested here that we could define more general notation for "equal up to antisymmetry" and for incomparability

Violeta Hernández (Jul 24 2022 at 18:10):

So we wouldn't need specialized pgame.equiv and pgame.fuzzy predicates, nor their game counterparts, nor their igame counterparts if we ever do that

Violeta Hernández (Jul 24 2022 at 18:37):

Or you know what?

Violeta Hernández (Jul 24 2022 at 18:37):

Maybe we don't need lf anymore

Violeta Hernández (Jul 24 2022 at 18:39):

Which is probably somewhat of a hot take given the effort we put into making lf into what it is

Violeta Hernández (Jul 24 2022 at 18:41):

There was previously the argument that lf was useful because it provided a nice def-eq for le, but that no longer applies after the aforementioned refactor

Violeta Hernández (Jul 24 2022 at 18:43):

In fact, changing the def-eq barely broke anything

Junyan Xu (Jul 24 2022 at 18:43):

What's that nice def-eq? I think one reason for keeping lf is it makes statements like src#pgame.lf_of_le_of_lf look natural.

Junyan Xu (Jul 24 2022 at 18:43):

and it's everywhere in the literature

Violeta Hernández (Jul 24 2022 at 18:46):

The nice def-eqs in question are the ones given by docs#pgame.le_iff_forall_lf and docs#pgame.lf_iff_exists_le

Violeta Hernández (Jul 24 2022 at 18:46):

I guess they're not exactly def-eqs but they're really close

Violeta Hernández (Jul 24 2022 at 18:49):

It's true that lf makes some statements look more natural. But I don't think the statements translated into preorders are particularly unclear either. We could just prove not_le_of_le_of_not_le and now we don't need to prove it separately for pgame and game

Violeta Hernández (Jul 24 2022 at 18:49):

Again, there's also the option of just making lf a notation on an arbitrary preorder, and make aliases for the theorems we want to port

Violeta Hernández (Jul 24 2022 at 18:53):

Ultimately what I want is to prove as few results on preorders as possible. All that should be done elsewhere, and we should strive not to duplicate that in the game theory files.

Violeta Hernández (Jul 25 2022 at 00:57):

I wonder if we could have a whole file set_theory/game/lf.lean where we introduce the notation and all relevant aliases

Violeta Hernández (Jul 25 2022 at 00:58):

That way, if we want to use lf notation on pre-games, games, surreals, or any future quotient or subtype we come up with, we can just open that namespace/locale and get it

Violeta Hernández (Jul 25 2022 at 01:00):

I'm still not sure if we need the aliases though, maybe we can keep the notation but still use not_le or not_ge in theorem names?

FR (Aug 30 2022 at 14:31):

Maybe a bit outdated, can we still get any convenient computable constructing functions if we use extensional equivalence? I recently found it hard to define something like smallset Set → Set (smallset is quotient of indexed set which is used for pSet and pgame) without giving up computability.

Yuyang Zhao (Feb 26 2023 at 23:38):

Violeta Hernández said:

It's definitely a confusing situation, since we would have pgame which is an implementation detail, then some other type (igame?) for ZFC pre-games (which they don't call pre-games, they just call them games), and then actually game corresponding to ZFC pre-games quotiented by antisymmetry

I feel like we probably shouldn't call a docs#game a game. We can't really play a docs#game.

Kevin Buzzard (Feb 27 2023 at 08:37):

I think it's important to stick to the terminology in the mathematical literature.

Yuyang Zhao (Feb 27 2023 at 11:31):

So igames should just be called games. Then what should we call current docs#game (games quotiented by antisymmetry)?

Kevin Buzzard (Feb 27 2023 at 14:00):

Are you suggesting that docs#game is not what Conway calls a game? I am a bit confused.

Kevin Buzzard (Feb 27 2023 at 14:07):

On ONAG p76 (sorry, reading from my physical copy) Conway stresses the definition of two games being equal, uses the symbol = for it, and says "when we speak of the game 0, we mean to refer also to the games 1-1, *+* and so on".

Yuyang Zhao (Feb 27 2023 at 16:44):

Conway talks about the identity of games in the book, which implies that "games" actually contain more structure - we can't talk about the identity of games after quotient. But I admit it's also reasonable to call docs#game games, and equality is likely more important than identity.

Trebor Huang (Feb 27 2023 at 23:54):

Conway is just doing the usual mathematician thing and calling all three types "games".

Violeta Hernández (Feb 28 2023 at 05:26):

Kevin Buzzard said:

On ONAG p76 (sorry, reading from my physical copy) Conway stresses the definition of two games being equal, uses the symbol = for it, and says "when we speak of the game 0, we mean to refer also to the games 1-1, *+* and so on".

Conway does a very weird thing where he distinguishes between equality of games, and identity, which he takes as stronger. Of course, mathlib would not do things this way.

Yuyang Zhao (Feb 28 2023 at 18:45):

Violeta Hernández said:

I think I might have brought this up before, but now I'm more convinced: we eventually need to ditch relabellings in favor of extensional equivalence
Relabellings are just a consequence of our implementation of games, which doesn't coincide with the ZFC one

I'm trying to define real identical and remove relabelling. See #18515.

Yuyang Zhao (Mar 01 2023 at 03:46):

I removed all the relabelling except those about short. I am not sure how to change the definition of short appropriately so that it respects identity.

Yuyang Zhao (Mar 01 2023 at 03:47):

Maybe I should define igame first, then define short for igame.

Violeta Hernández (Mar 01 2023 at 11:19):

Yuyang Zhao said:

I removed all the relabelling except those about short. I am not sure how to change the definition of short appropriately so that it respects identity.

Short games should be those with a finite birthday

Yuyang Zhao (Mar 01 2023 at 14:18):

Yes, this definition can be used for pgame, but most of the properties of short games seem to need to be proved for igame.

Junyan Xu (Apr 10 2023 at 07:05):

Combinatorial game enthusiasts: CGSuite 2.0 was released with support for standard rulesets, loopy games, misère impartial games, and surreal numbers. May serve as inspiration to some extent for our implementation.

Yuyang Zhao (Jul 16 2023 at 12:21):

Because mathlib has been ported, #5901 is the PR for mathlib4.

Timo Carlin-Burns (Sep 15 2023 at 02:02):

The current definition of Relabelling is too restrictive compared to Identical, but was kept in #7162 for potential computational use cases (it is in Type _ not Prop). It's possible to define a Type _ which is compatible with Identical like this

inductive Relabelling : PGame.{u}  PGame.{u}  Type (u + 1)
  |
  mk {x y : PGame}
    (toFunL : x.LeftMoves  y.LeftMoves)
    (invFunL : y.LeftMoves  x.LeftMoves)
    (toFunR : x.RightMoves  y.RightMoves)
    (invFunR : y.RightMoves  x.RightMoves) :
      ( i, Relabelling (x.moveLeft i) (y.moveLeft (toFunL i))) 
      ( j, Relabelling (x.moveLeft (invFunL j)) (y.moveLeft j)) 
      ( i, Relabelling (x.moveRight i) (y.moveRight (toFunR i))) 
      ( j, Relabelling (x.moveRight (invFunR j)) (y.moveRight j))  Relabelling x y

I have proven in a branch that with this definition, Identical x y \iff Nonempty (Relabelling x y).

I don't know what the computational use cases in mind for Relabelling are, but would this be a better definition for them? And would there be any benefit to redefining Identical x y as Nonempty (Relabelling x y) with this definition?

Timo Carlin-Burns (Sep 15 2023 at 04:02):

Ah this has been thought of already:

Violeta Hernández said:

We could still define identical games in terms of functions, i.e. explicit maps from left moves of x to identical left moves of y, and all the others, instead of just having the quantifiers

Yuyang Zhao (Sep 15 2023 at 05:31):

It is now mainly needed for file Mathlib.SetTheory.Game.State.

Yuyang Zhao (Sep 15 2023 at 05:40):

Timo Carlin-Burns said:

I don't know what the computational use cases in mind for Relabelling are, but would this be a better definition for them? And would there be any benefit to redefining Identical x y as Nonempty (Relabelling x y) with this definition?

They handle multiplicity differently. If docs#PSet.Equiv did what Relabelling does, then docs#ZFSet would be an analog of docs#Multiset instead of docs#Finset.

Yuyang Zhao (Sep 15 2023 at 06:08):

Ah sorry, I realized you were talking about your new definition.

Yuyang Zhao (Sep 15 2023 at 06:23):

That might be an option, but it would no longer preserve the current PGame.Short.

Yuyang Zhao (Sep 15 2023 at 06:29):

Of course, PGame.Short itself is likely to be removed...

Timo Carlin-Burns (Sep 15 2023 at 13:43):

I don't see any references to relabelling in the definition of Short, so we're just talking about whether x.Short -> Relabelling x y -> y.Short, right? If you want such a version of Short which is computational, you could always define it as the dependent product def RShort x := (y : PGame) \times y.Short \times Relabelling x y

Yuyang Zhao (Sep 15 2023 at 14:46):

Yes I'm talking about docs#SetTheory.PGame.shortOfRelabelling. I'm not sure what we should do with PGame.Short, I might remove the current definition.


Last updated: Dec 20 2023 at 11:08 UTC