Zulip Chat Archive

Stream: mathlib4

Topic: Bug in `polyrith`?


Heather Macbeth (Mar 06 2023 at 01:42):

On the following test polyrith gives the output

polyrith found the following certificate, but it failed to close the goal:
↑s * ↑y * h₁ + ↑r * ↑x * h₂ - 1 * h₃ + (↑b + 1) * h₄ + (↑a + 1) * h₅ + h₆

as well as quite a number of other complaints ("ring failed, ring expressions not equal", "ring failed: not an equality", "failed to synthesize instance HMul ℕ ℕ ℤ"):

import Mathlib.Tactic.Polyrith

example {r a p s b q t u x y z : }
    (h₁ : (r:) = (a + 1) * p)
    (h₂ : (s:) = (b + 1) * q)
    (h₃ : (t:) = ((1 + a + b) + 1) * u)
    (h₄ : (x:) * (r:) * q = u)
    (h₅ : (y:) * p * (s:) = u)
    (h₆ : (z:) * (r:) * (s:) = ((x:) + (y:)) * (r:) * (s:)) :
    (z:) * (r:) * (s:) = (t:) := by
  polyrith

Heather Macbeth (Mar 06 2023 at 01:43):

Any ideas? I think it's something to do with the interaction with casting, because the same test works fine when the variables are integers.

Mario Carneiro (Mar 06 2023 at 01:52):

polyrith actually has to go through an Expr -> Syntax -> Expr roundtrip because it is trying to produce something that can be fed to linear_combination. So things like lossy pretty printing do affect it

Mario Carneiro (Mar 06 2023 at 01:54):

there are some tricks it could pull to make the roundtrip lossless even though users can't write it, but I think that would weaken the claim of a Try this: <stuff that doesn't work> prompt

Heather Macbeth (Mar 06 2023 at 01:57):

It works in mathlib3! Is there a different mechanism there?

Thomas Murrills (Mar 06 2023 at 01:59):

Maybe coercions could be rendered in an "input form", though; if you replace up-arrows with type annotations the following call to linear_combination works:

linear_combination (s : ) * (y : ) * h₁ + (r : ) * (x : ) * h₂ - 1 * h₃ + ((b : ) + 1) * h₄ + ((a : ) + 1) * h₅ + h₆

Mario Carneiro (Mar 06 2023 at 02:02):

Oh, I just noticed that the "failed to synthesize instance" error occurs at position 0:0 which makes it look like the import line didn't work

Mario Carneiro (Mar 06 2023 at 02:03):

Thomas Murrills said:

Maybe coercions could be rendered in an "input form", though; if you replace up-arrows with type annotations the following call to linear_combination works:

linear_combination (s : ) * (y : ) * h₁ + (r : ) * (x : ) * h₂ - 1 * h₃ + ((b : ) + 1) * h₄ + ((a : ) + 1) * h₅ + h₆

Sure, but lossless pretty printing has been a dream for ages and it's a huge project

Thomas Murrills (Mar 06 2023 at 02:05):

I see, so not worth doing piecemeal?

Mario Carneiro (Mar 06 2023 at 02:06):

Heather Macbeth said:

It works in mathlib3! Is there a different mechanism there?

Yes, linear_combination works differently in mathlib4, being primarily a macro now which manipulates syntax directly instead of working on exprs

Mario Carneiro (Mar 06 2023 at 02:16):

It is pretty funny (but expected) that this works:

example {r a p s b q t u x y z : }
    (h₁ : (r:) = (a + 1) * p)
    (h₂ : (s:) = (b + 1) * q)
    (h₃ : (t:) = ((1 + a + b) + 1) * u)
    (h₄ : (x:) * (r:) * q = u)
    (h₅ : (y:) * p * (s:) = u)
    (h₆ : (z:) * (r:) * (s:) = ((x:) + (y:)) * (r:) * (s:)) :
    (z:) * (r:) * (s:) = (t:) := by
  set_option pp.all true in
  polyrith

Mario Carneiro (Mar 06 2023 at 02:17):

Also @Heather Macbeth can you elaborate on what you mean by "it works in mathlib3"? Does it actually produce something you can successfully copy-paste?

Mario Carneiro (Mar 06 2023 at 02:19):

I don't know whether there is an option for coercions to show the expected type like @Thomas Murrills suggests. Using set_option pp.coercions false causes it to print Nat.cast applications, which doesn't really fix the type ambiguities here

Heather Macbeth (Mar 06 2023 at 04:21):

@Mario Carneiro On the following Lean 3 code

import tactic.polyrith

example {r a p s b q t u x y z : }
    (h₁ : (r:) = (a + 1) * p)
    (h₂ : (s:) = (b + 1) * q)
    (h₃ : (t:) = ((1 + a + b) + 1) * u)
    (h₄ : (x:) * (r:) * q = u)
    (h₅ : (y:) * p * (s:) = u)
    (h₆ : (z:) * (r:) * (s:) = ((x:) + (y:)) * (r:) * (s:)) :
    (z:) * (r:) * (s:) = (t:) :=
begin
  polyrith,
end

I get "goals accomplished :tada:" with a blue underline "try this" for

linear_combination ↑s * ↑y * h₁ + ↑r * ↑x * h₂ - h₃ + (↑b + 1) * h₄ + (↑a + 1) * h₅ + h₆

and when you click the "try this" to insert that code, it also works.

Heather Macbeth (Mar 06 2023 at 04:25):

Maybe the key point is that (as you said) linear_combination is more robust in mathlib3: this (without coercions)

linear_combination s * y * h₁ + r * x * h₂ - h₃ + (b + 1) * h₄ + (a + 1) * h₅ + h₆

solves the example in mathlib3 but not in mathlib4.

Mario Carneiro (Mar 06 2023 at 04:32):

To give a simpler example:

example {r s a b : } (h₁ : (r : ) = a + 1) (h₂ : (s : ) = b + 1) :
    r * s = (a + 1 : ) * (b + 1) := by
  polyrith

expands to

example {r s a b : } (h₁ : (r : ) = a + 1) (h₂ : (s : ) = b + 1) :
    r * s = (a + 1 : ) * (b + 1) := by
  linear_combination
    (b + 1) * h₁ + r * h₂

which expands to

open Mathlib.Tactic.LinearCombination in
example {r s a b : } (h₁ : (r : ) = a + 1) (h₂ : (s : ) = b + 1) :
    r * s = (a + 1 : ) * (b + 1) := by
    refine eq_of_add (add_pf (c_mul_pf (b + 1) h₁) (c_mul_pf r h₂)) ?_ <;>
    ring1

which fails due to elaboration issues in the refine line

Heather Macbeth (Mar 06 2023 at 04:36):

Does the mathlib3 implementation effectively do that, too?

Mario Carneiro (Mar 06 2023 at 04:39):

The mathlib3 version assumes everything is over a single type and elaborates everything in advance of constructing the term. In this case we are literally constructing a syntax to pass to refine

Mario Carneiro (Mar 06 2023 at 04:41):

one of the things the new implementation buys us is that it can be much more flexible with regard to putting h's in different places. The mathlib3 version is limited to linear combinations (as the name suggests) with the h's on the right, but the mathlib4 version can do stuff like h1 * h2 + 3 / h3 as well

Heather Macbeth (Mar 06 2023 at 04:44):

Perhaps we could have both. It would be nice to have a very robust version which we could even just call polyrith_with_witness, which fixes a type and doesn't need the flexibility since the syntax is provided by autogenerated output. That could follow the mathlib3 implementation. And also keep the current macro version.

Mario Carneiro (Mar 06 2023 at 04:44):

but at the end of the day it is more or less just a macro over refine, so the issues here can be traced back to elaborator troubles. For example, it is odd that this doesn't work:

theorem c_mul_pf [Mul α] (p : b = c) (a : α) : a * b = a * c := p  rfl

example {r a b : } (h₁ : (r : ) = a + 1) : True := by
    have := c_mul_pf (b + 1) h₁

have := c_mul_pf (α := ℤ) (↑b + 1) h₁ works

Mario Carneiro (Mar 06 2023 at 04:45):

In this case we know the types of the h's, so I can add more type ascriptions if that's what is needed to keep the elaborator from getting confused

Mario Carneiro (Mar 06 2023 at 04:47):

It seems that putting the variable second is sufficient to fix the issue:

theorem c_mul_pf' [Mul α] (p : b = c) (a : α) : a * b = a * c := p  rfl

example {r a b : } (h₁ : (r : ) = a + 1) : True := by
    have := c_mul_pf' h₁ (b + 1)

Mario Carneiro (Mar 06 2023 at 05:10):

!4#2678

Heather Macbeth (Mar 06 2023 at 05:10):

Thanks!!

Heather Macbeth (Mar 06 2023 at 05:11):

You give the test case

example {r s a b : } (h₁ : (r : ) = a + 1) (h₂ : (s : ) = b + 1) :
    r * s = (a + 1 : ) * (b + 1) := by
  linear_combination (b + 1) * h₁ + r * h₂

Does this also work on your branch?

example {r s a b : } (h₁ : (r : ) = a + 1) (h₂ : (s : ) = b + 1) :
    r * s = (a + 1 : ) * (b + 1) := by
  linear_combination (b + 1) * h₁ + r * h₂

If I understand correctly, it would have in mathlib3.

Mario Carneiro (Mar 06 2023 at 05:12):

yes it does. The version with the up arrows is relevant for being the style that polyrith generates

Kevin Buzzard (Mar 06 2023 at 06:20):

Mario Carneiro said:

one of the things the new implementation buys us is that it can be much more flexible with regard to putting h's in different places. The mathlib3 version is limited to linear combinations (as the name suggests) with the h's on the right, but the mathlib4 version can do stuff like h1 * h2 + 3 / h3 as well

What the heck does 3 / h3 mean?

Mario Carneiro (Mar 06 2023 at 06:21):

if h3 : x = y then it is a proof of 3 / x = 3 / y

Mario Carneiro (Mar 06 2023 at 06:22):

You can basically write arbitrary expressions involving equations this way, it's just a variation on congr

Mario Carneiro (Mar 06 2023 at 06:27):

There was another mathlib3 tactic that was added to construct things like this with a congr like name but I can't find it now... it had syntax like apply_fun a + _1 * _2, does this ring any bells?

Eric Wieser (Mar 06 2023 at 06:41):

tactic#congrm?

Eric Wieser (Mar 06 2023 at 06:41):

I think someone is porting that to lean 4

Mario Carneiro (Mar 06 2023 at 07:23):

yeah, that

Mario Carneiro (Mar 06 2023 at 07:24):

basically linear_combination does that but specialized to +,-,*,/, and then passes the results to ring1

Heather Macbeth (Apr 02 2023 at 02:29):

I think the following might be another bug in polyrith (cc @Mario Carneiro):

import Mathlib.Tactic.Polyrith

-- works
example (y a : ) (k : ) (h : a ^ k = 0) : a ^ k * y = 0 := by
  linear_combination y * h

-- works
example (y a : ) (h : a = 0) : a * y = 0 := by polyrith

-- fails
example (y a : ) (k : ) (h : a ^ k = 0) : a ^ k * y = 0 := by
  polyrith
/-
polyrith failed to retrieve a solution from Sage! TypeError:
a is neither an integer nor a rational
-/

Mario Carneiro (Apr 02 2023 at 02:41):

hm, this is annoying to solve. It appears that sage doesn't like it when you give it a "polynomial expression" like a ^ k * y

Heather Macbeth (Apr 02 2023 at 02:47):

But here a ^ k should be sent as a single token, right? Because k is not a numeral.

Heather Macbeth (Apr 02 2023 at 02:47):

I guess the issue is to parse a ^ 17 differently from a ^ k.

Heather Macbeth (Apr 02 2023 at 02:54):

(It works in mathlib3.)

Mario Carneiro (Apr 02 2023 at 02:56):

...or not: !4#3224

Mario Carneiro (Apr 02 2023 at 02:57):

The issue is that polyrith builds on ring, which has a more permissive concept of "polynomial" due to its ring_exp heritage in lean 3

Mario Carneiro (Apr 02 2023 at 02:58):

so it has to explicitly not parse things the same way ring would in order to not produce non-polynomial expressions that will confuse sage

Heather Macbeth (Apr 02 2023 at 03:03):

Thanks!


Last updated: Dec 20 2023 at 11:08 UTC