Zulip Chat Archive

Stream: mathlib4

Topic: !4#5256 RingTheory.Trace


Riccardo Brasca (Jun 19 2023 at 14:28):

In !4#5256 the following is taking forever

set_option maxHeartbeats 1200000 in
theorem Algebra.isIntegral_trace [FiniteDimensional L F] {x : F} (hx : IsIntegral R x) :
    IsIntegral R (Algebra.trace L F x) := by

Note that already in !4#5074 the slowest declarations are related to IsIntegral or to eval. Should we try to investigate this more closely?

Riccardo Brasca (Jun 19 2023 at 14:28):

@Xavier Roblot I am afraid you will face the same problem in !4#5262

Xavier Roblot (Jun 19 2023 at 15:01):

Thanks for the warning. I am not quite there but getting close

Riccardo Brasca (Jun 19 2023 at 15:12):

Here is something interesting: if I use set_option maxHeartbeats 1100000 in (instead of 1200000), after the last convert the goal is

((aeval y) (minpoly R x) = 0) = ((aeval y) (minpoly R x) = 0)

Let me see what happens with set_option pp.all true.

Eric Wieser (Jun 19 2023 at 15:14):

maxHeartbeats changes the behavior of convert!?

Riccardo Brasca (Jun 19 2023 at 15:15):

It seems so... maybe because the LHS and the RHS are defeq, but with a small maxHeartbeats Lean gives up?

Eric Wieser (Jun 19 2023 at 15:16):

Is it not possible to distinguish "not defeq" from "could not determine if defeq within the time limit"?

Eric Wieser (Jun 19 2023 at 15:17):

This strikes me as a bug

Riccardo Brasca (Jun 19 2023 at 15:17):

Well, of course the output is huge... 24464 lines

Riccardo Brasca (Jun 19 2023 at 15:18):

The goals starts at line 8754

Riccardo Brasca (Jun 19 2023 at 15:21):

If I replace convert by exact I get a timeout (with both limits)

Riccardo Brasca (Jun 19 2023 at 15:27):

Using set_option pp.explicit true the output is much shorter

 @Eq Prop
    (@Eq
      ((fun x  @AlgebraicClosure F inst✝⁵)
        ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
              (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
          x))
      ((@aeval R
            ((fun x  @AlgebraicClosure F inst✝⁵)
              ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                    (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                x))
            (@CommRing.toCommSemiring R inst✝¹⁴)
            (@Ring.toSemiring
              ((fun x  @AlgebraicClosure F inst✝⁵)
                ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                      (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                  x))
              (@CommRing.toRing
                ((fun x  @AlgebraicClosure F inst✝⁵)
                  ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                        (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                    x))
                (@EuclideanDomain.toCommRing
                  ((fun x  @AlgebraicClosure F inst✝⁵)
                    ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                          (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                      x))
                  (@Field.toEuclideanDomain
                    ((fun x  @AlgebraicClosure F inst✝⁵)
                      ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                            (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                        x))
                    (@AlgebraicClosure.instFieldAlgebraicClosure F inst✝⁵)))))
            this y)
        (@minpoly R F inst✝¹⁴ (@DivisionRing.toRing F (@Field.toDivisionRing F inst✝⁵)) inst✝² x))
      0)
    (@Eq
      ((fun x_1 
          (fun x  @AlgebraicClosure F inst✝⁵)
            ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                  (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
              x))
        (@minpoly R F inst✝¹⁴ (@CommRing.toRing F (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)))
          inst✝² x))
      ((@aeval R
            ((fun x  @AlgebraicClosure F inst✝⁵)
              ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                    (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                x))
            (@CommRing.toCommSemiring R inst✝¹⁴)
            (@CommSemiring.toSemiring
              ((fun x  @AlgebraicClosure F inst✝⁵)
                ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                      (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                  x))
              (@Semifield.toCommSemiring
                ((fun x  @AlgebraicClosure F inst✝⁵)
                  ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                        (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                    x))
                (@Field.toSemifield
                  ((fun x  @AlgebraicClosure F inst✝⁵)
                    ((@trace L F (@EuclideanDomain.toCommRing L (@Field.toEuclideanDomain L inst✝⁸))
                          (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)) inst✝³)
                      x))
                  (@AlgebraicClosure.instFieldAlgebraicClosure F inst✝⁵))))
            this y)
        (@minpoly R F inst✝¹⁴ (@CommRing.toRing F (@EuclideanDomain.toCommRing F (@Field.toEuclideanDomain F inst✝⁵)))
          inst✝² x))
      0)

Riccardo Brasca (Jun 19 2023 at 16:24):

From 1200000 to 240000!

attribute [-instance] Field.toEuclideanDomain
set_option maxHeartbeats 240000 in
theorem Algebra.isIntegral_trace [FiniteDimensional L F] {x : F} (hx : IsIntegral R x) :
    IsIntegral R (Algebra.trace L F x) := by
  have hx' : IsIntegral L x := isIntegral_of_isScalarTower hx
  have splits :(minpoly L x).Splits (algebraMap L (AlgebraicClosure F)) := by
    apply IsAlgClosed.splits_codomain
  rw [ isIntegral_algebraMap_iff
    (algebraMap L (AlgebraicClosure F)).injective, trace_eq_sum_roots splits]
  refine' (IsIntegral.multiset_sum _).nsmul _
  intro y hy
  rw [mem_roots_map (minpoly.ne_zero hx')] at hy
  use minpoly R x, minpoly.monic hx
  rw [ aeval_def]
  refine' minpoly.aeval_of_isScalarTower (K := L) R x y _
  rw [aeval_def]
  convert hy

Riccardo Brasca (Jun 19 2023 at 16:25):

Can docs4#Field.toEuclideanDomain be a big problem? This new proof is much better, it is almost the same as in mathlib3

Riccardo Brasca (Jun 19 2023 at 16:25):

I am trying to set

attribute [-instance] Field.toEuclideanDomain

also in other slow files to see what happens

Riccardo Brasca (Jun 19 2023 at 16:32):

Step.isIntegral in FieldTheory.IsAlgClosed.AlgebraicClosure goes from 700000 to 450000 :scream:

Ruben Van de Velde (Jun 19 2023 at 16:38):

I'm finding it hard to read the zeroes, is that better?

Riccardo Brasca (Jun 19 2023 at 16:38):

From 700 to 450

Riccardo Brasca (Jun 19 2023 at 16:39):

Seeing what happens if it is a def everywhere

Riccardo Brasca (Jun 19 2023 at 16:40):

(I mean, it's not even mathematically clear that a field is a euclidean domain...)

Sebastien Gouezel (Jun 19 2023 at 16:40):

Do you achieve the same improvement by just lowering its priority?

Riccardo Brasca (Jun 19 2023 at 16:41):

It's already 100

Riccardo Brasca (Jun 19 2023 at 16:42):

There is even a comment

-- Porting note: this seems very slow.
-- see Note [lower instance priority]

Sebastien Gouezel (Jun 19 2023 at 16:46):

You could put it at 10 to see if it makes a difference.

Riccardo Brasca (Jun 19 2023 at 16:47):

I will play with this after dinner. For the time being !4#5264 and #19205 to see if we use the instance somewhere.

Sebastien Gouezel (Jun 19 2023 at 17:50):

Using the instance

instance (priority := 100) Field.toEuclideanDomain {K : Type _} [Field K] : EuclideanDomain K :=
{ toCommRing := Field.toCommRing
  quotient := (· / ·), remainder := fun a b => a - a * b / b, quotient_zero := div_zero,
  quotient_mul_add_remainder_eq := fun a b => by
    -- Porting note: was `by_cases h : b = 0 <;> simp [h, mul_div_cancel']`
    by_cases h : b = 0 <;> dsimp only
    · dsimp only
      rw [h, zero_mul, mul_zero, zero_div, zero_add, sub_zero]
    · dsimp only
      rw [mul_div_cancel' _ h]
      simp only [ne_eq, h, not_false_iff, mul_div_cancel, sub_self, add_zero]
  r := fun a b => a = 0  b  0,
  r_wellFounded :=
    WellFounded.intro fun a =>
      (Acc.intro _) fun b hb, _ => (Acc.intro _) fun c _, hnb => False.elim <| hnb hb,
  remainder_lt := fun a b hnb => by simp [hnb],
  mul_left_not_lt := fun a b hnb hab, hna => Or.casesOn (mul_eq_zero.1 hab) hna hnb }

is probably much better.

Riccardo Brasca (Jun 19 2023 at 17:55):

Let me try also this

Sebastien Gouezel (Jun 19 2023 at 18:20):

This better instance also solves the porting note:

instance (priority := 100) Field.toEuclideanDomain {K : Type _} [Field K] : EuclideanDomain K :=
{ toCommRing := Field.toCommRing
  quotient := (· / ·), remainder := fun a b => a - a * b / b, quotient_zero := div_zero,
  quotient_mul_add_remainder_eq := fun a b => by
    by_cases h : b = 0 <;> simp [h, mul_div_cancel']
  r := fun a b => a = 0  b  0,
  r_wellFounded :=
    WellFounded.intro fun a =>
      (Acc.intro _) fun b hb, _ => (Acc.intro _) fun c _, hnb => False.elim <| hnb hb,
  remainder_lt := fun a b hnb => by simp [hnb],
  mul_left_not_lt := fun a b hnb hab, hna => Or.casesOn (mul_eq_zero.1 hab) hna hnb }

works just fine. With set_option pp.explicit true, the old definition takes 2100 lines in #print Field.toEuclideanDomain, the new one 90.

Riccardo Brasca (Jun 19 2023 at 18:25):

So, in !4#5264 I've made the instance a def and fixed the two errors. Then I had a look at the files that we ported in the last month and that were slow, reducing the heartbeats to the minimum that works. It's not miraculous, but it's definitely better.

Riccardo Brasca (Jun 19 2023 at 18:26):

Now I will compare what happens with the new instance.

Riccardo Brasca (Jun 19 2023 at 19:07):

OK, forget about !4#5264. Indeed the modification proposed in !4#5266 is as good as making the instance a def.

Tagging @Damiano Testa @Eric Wieser @Kevin Buzzard @Ruben Van de Velde @Chris Hughes @Xavier Roblot since they were involved is all these problems with SplittingField and friends.

Riccardo Brasca (Jun 19 2023 at 19:08):

Note that not all problems are solved, the situation is only slightly better, but maybe we only need to fix some instance.

Damiano Testa (Jun 19 2023 at 19:22):

Is it a good heuristic to say that we should stay away from with in instances, unless we are really sure about what we are doing?

Kevin Buzzard (Jun 19 2023 at 19:31):

Removing with does not completely solve the problem. I changed another instance here and it had a similar effect: things got fractionally better but the problems are still there. It's not the with per se, it's the term the with produces.

Matthew Ballard (Jun 19 2023 at 19:50):

I have more elementary question: what should I expect Lean to be doing when it wants a CommRing and I hand it

 Field K with
    add := (· + ·), mul := (· * ·), one := 1, zero := 0, neg := Neg.neg,

? It seems crazy over-specified and, during porting, there were other instances (ha) where stripping out things helped behavior.

Riccardo Brasca (Jun 19 2023 at 20:18):

Kevin Buzzard said:

Removing with does not completely solve the problem. I changed another instance here and it had a similar effect: things got fractionally better but the problems are still there. It's not the with per se, it's the term the with produces.

You also did something very similar in !4#4507 and it also helped. We have to find the other places where this is needed...

Eric Wieser (Jun 19 2023 at 20:18):

I would imagine we have a very large number of such places

Eric Wieser (Jun 19 2023 at 20:20):

For instance, all of the docs4#Function.Surjective.ring type instances will have this problem

Yaël Dillies (Jun 19 2023 at 20:21):

Is there any chance that we can make the with notation smarter rather than working around it?

Eric Wieser (Jun 19 2023 at 20:22):

I don't think that necessarily helps, sometimes we might not even be handing with an appropriate argument

Eric Wieser (Jun 19 2023 at 20:22):

Unless you had with! for "i know none of my base classes are really base classes, but I meant it anyway"

Xavier Roblot (Jun 19 2023 at 20:29):

I have just merged !4#5266 into the port of RingTheory.Norm and the improvement is phenomenal!

Xavier Roblot (Jun 19 2023 at 20:30):

Everything now works so nicely :smile:

Riccardo Brasca (Jun 19 2023 at 20:52):

docs4#lift_of_splits is still slow (why in the root namespace?!). It is something related to this message. Maybe docs4#Subalgebra.algebra' is suboptimal, but I am not sure.

Xavier Roblot (Jun 19 2023 at 20:56):

Well, !4#5262 is ready (but it depends on !4#5266)

Kevin Buzzard (Jun 19 2023 at 20:59):

            [] [13.358618s] refine' (AdjoinRoot.liftHom (minpoly (Algebra.adjoin F (s : Set K)) a) y hy).comp _ 
            [] [30.068997s] exact (AlgEquiv.adjoinSingletonEquivAdjoinRootMinpoly (Algebra.adjoin F (s : Set K)) a).toAlgHom 

was why lift_of_splits was slow in master. It will be interesting to see if there are still issues such as

                  [] [9.118708s]  CommSemiring.toSemiring =?= CommSemiring.toSemiring

after !4#5266 .

Kevin Buzzard (Jun 19 2023 at 21:07):

The issue is that the left hand side is

@AdjoinRoot.instCommRing { x // x  Algebra.adjoin F s } (Subalgebra.toCommRing (Algebra.adjoin F s))
  (minpoly { x // x  Algebra.adjoin F s } a) : CommRing (AdjoinRoot (minpoly { x // x  Algebra.adjoin F s } a))

and the right hand side is

@AdjoinRoot.instCommRing { x // x  Algebra.adjoin F s } EuclideanDomain.toCommRing
  (minpoly { x // x  Algebra.adjoin F s } a) : CommRing (AdjoinRoot (minpoly { x // x  Algebra.adjoin F s } a))

where the Euclidean domain instance is coming from letI := fieldOfFiniteDimensional F (Algebra.adjoin F (↑s : Set K)) (on master)

Riccardo Brasca (Jun 19 2023 at 21:24):

Now I see

[Meta.synthInstance] [7.769144s]  Algebra { x // x  Algebra.adjoin F s } (AdjoinRoot (minpoly { x // x  Algebra.adjoin F s } a))

and if I click on the arrow the output is huge

Riccardo Brasca (Jun 19 2023 at 21:31):

It does this

[] [0.720522s]  CommSemiring.toSemiring =?= Ring.toSemiring

several times. The LHS is

@CommSemiring.toSemiring { x // x  Algebra.adjoin F s } (Subalgebra.toCommSemiring (Algebra.adjoin F s)) : Semiring { x // x  Algebra.adjoin F s }

and the RHS is

@Ring.toSemiring { x // x  Algebra.adjoin F s } (SubringClass.toRing (Algebra.adjoin F s)) : Semiring { x // x  Algebra.adjoin F s }

Kevin Buzzard (Jun 19 2023 at 21:38):

It's much quicker than before though.

Sebastien Gouezel (Jun 20 2023 at 06:19):

Again, the current form of fieldOfFiniteDimensional is suboptimal. It should probably be changed to

lemma glouk (F : Type _) {K : Type _} [Field F] [Ring K] [IsDomain K]
    [Algebra F K] [FiniteDimensional F K] {x : K} (H : x  0) :  y, x * y = 1 := by
  have : Function.Surjective (LinearMap.mulLeft F x) :=
    LinearMap.injective_iff_surjective.1 fun y z => ((mul_right_inj' H).1 : x * y = x * z  y = z)
  exact this 1

/-- A domain that is module-finite as an algebra over a field is a division ring. -/
noncomputable def divisionRingOfFiniteDimensional (F K : Type _) [Field F] [h : Ring K] [IsDomain K]
    [Algebra F K] [FiniteDimensional F K] : DivisionRing K :=
  { IsDomain K with
    toRing := h
    inv := fun x => if H : x = 0 then 0 else Classical.choose <| glouk F H
    mul_inv_cancel := fun x hx =>
      show x * dite _ _ _ = _ by
        rw [dif_neg hx]
        exact (Classical.choose_spec (glouk F hx) :)
    inv_zero := dif_pos rfl }
#align division_ring_of_finite_dimensional divisionRingOfFiniteDimensional

/-- An integral domain that is module-finite as an algebra over a field is a field. -/
noncomputable def fieldOfFiniteDimensional (F K : Type _) [Field F] [h : CommRing K] [IsDomain K]
    [Algebra F K] [FiniteDimensional F K] : Field K :=
  { divisionRingOfFiniteDimensional F K with
    toCommRing := h }
#align field_of_finite_dimensional fieldOfFiniteDimensional

What is going on is that the definition of a Field has a CommRing embedded inside it. The old definition was taking a CommRing instance but did not use it directly to build the CommRing field, instead it broke it into pieces and then reassembled the bits by hand.

Also, having an auxiliary lemma before the first definition that hides away the LinearMap.mulLeft business and the algebra structure makes sure that the definition of linear maps does not show up in the definition of the inverse and makes the corresponding term much shorter.

Kevin Buzzard (Jun 20 2023 at 08:05):

I tried a version of this two weeks ago but it didn't make any difference to the problem I was trying to solve, namely slowdowns (I failed to get !bench working so I just timed it locally and there was no difference). Perhaps my error was that I didn't also change the DivisionRing instance? I'll try this version now.

Kevin Buzzard (Jun 20 2023 at 09:05):

If I make the change above and then recompile what needs to be recompiled on master, I get

real    10m35.143s
user    85m38.248s
sys 1m56.750s

If I then revert the change and recompile again, I get

real    10m32.122s
user    85m27.783s
sys 1m58.641s

So, just as my experiment two weeks ago, it might make the terms nicer but it doesn't make mathlib compile any faster.

Kevin Buzzard (Jun 20 2023 at 09:10):

On the other hand it might make debugging slowdowns easier so it's probably worth making the change anyway.

Sebastien Gouezel (Jun 20 2023 at 09:16):

Sure, these are not instances, they are defs, so they are only relevant when they are explicitely used, which is not a lot (once for each of them).

Kevin Buzzard (Jun 20 2023 at 09:20):

Aah I see!

Kevin Buzzard (Jun 20 2023 at 09:22):

!4#5288


Last updated: Dec 20 2023 at 11:08 UTC