Zulip Chat Archive

Stream: mathlib4

Topic: Why does `nlinarith` succeed?


Martin Dvořák (Aug 13 2023 at 19:39):

For reasons I don't understand, nlinarith succeeds here:

example {x : } : 2*x^2 - 6*x + 9  0 := by
  nlinarith

However, it does not work here:

example {x : } : x^2 - 6*x + 9  0 := by
  nlinarith

If I hint a proof, everything is all right again:

example {x : } : x^2 - 6*x + 9  0 := by
  convert_to (x - 3) ^ 2  0
  · ring
  nlinarith

In my attempt to narrow down where nlinarith clicks, I multiplied the original inequality by ten. This works:

example {x : } : 20*x^2 - 60*x + 90  0 := by
  nlinarith

This does not:

example {x : } : 19*x^2 - 60*x + 90  0 := by
  nlinarith

I made a few more attempts but I failed to learn to predict whether nlinarith succeeds.

I am not complaining about cases where nlinarith cannot close the goal automatically.
I am puzzled by what nlinarith does to succeed in cases 2*x^2 - 6*x + 9 ≥ 0 and similar.

Mario Carneiro (Aug 13 2023 at 20:06):

nlinarith is not complete for nonlinear arithmetic (I don't think it is decidable). The algorithm is described in the doc string:

The preprocessing is as follows:

  • For every subterm a ^ 2 or a * a in a hypothesis or the goal,
    the assumption 0 ≤ a ^ 2 or 0 ≤ a * a is added to the context.
  • For every pair of hypotheses a1 R1 b1, a2 R2 b2 in the context, R1, R2 ∈ {<, ≤, =},
    the assumption 0 R' (b1 - a1) * (b2 - a2) is added to the context (non-recursively),
    where R ∈ {<, ≤, =} is the appropriate comparison derived from R1, R2.

Mario Carneiro (Aug 13 2023 at 20:10):

But every subterm means every subterm, not just x^2 ≥ 0 but also (x^2)^2 ≥ 0 and (2*x^2 - 6*x + 9)^2 ≥ 0, as well as x^2 * (2*x^2 - 6*x + 9) ≤ 0 because we are assuming by contradiction that 2*x^2 - 6*x + 9 < 0. It turns out that linarith was able to find a very clever contradiction from these facts:

The expression
      81 * -1 +
      18 * (2 * x ^ 2 - 6 * x + 9) +
      4 * -(-x ^ 2 * -x ^ 2) +
      4 * -(-x ^ 2 * (2 * x ^ 2 - 6 * x + 9)) +
      -((2 * x ^ 2 - 6 * x + 9) * (2 * x ^ 2 - 6 * x + 9))
should be both 0 and negative

Martin Dvořák (Aug 13 2023 at 20:57):

How do you generate the explanation?

Mario Carneiro (Aug 13 2023 at 21:14):

set_option trace.linarith true

Kevin Buzzard (Aug 13 2023 at 22:30):

Note that this option is explicitly mentioned in the docstring.


Last updated: Dec 20 2023 at 11:08 UTC