Zulip Chat Archive

Stream: mathlib4

Topic: erw and IsDedekindDomain.HeightOneSpectrum.adicCompletion


Kevin Buzzard (May 18 2024 at 20:29):

Finite adele rings don't really have a lot of love right now in mathlib and I need to use them for FLT so I'm trying to get on top of what's going on in the relevant files. When trying to write some code extending the API for these objects, I got myself into a situation where rw failed and erw worked, but if I wrote change <copy the goal here> beforehand then rw suddenly worked again. I've managed to debug this...to a certain extent.

In Mathlib/RingTheory/DedekindDomain/AdicValuation.lean there's the definition docs#IsDedekindDomain.HeightOneSpectrum.adicCompletion :

def adicCompletion :=
  @UniformSpace.Completion K v.adicValued.toUniformSpace

On lines 431 to 432 of that file, we have

      -- This used to be `rw`, but we need `erw` after leanprover/lean4#2644
      erw [mem_adicCompletionIntegers, h, Valued.valuedCompletion_apply]

We can take this apart as

      rw [mem_adicCompletionIntegers, h]
      -- ⊢ Valued.v (↑K ((algebraMap R K) r)) ≤ 1
      erw [Valued.valuedCompletion_apply]

and erw fails if you change it to rw. (NB That ↑K in the goal is pretty weird, but this is a coercion issue unrelated to what I want to talk about here; this is a coercion docs#UniformSpace.Completion.coe' which takes an explicit input and the input is being displayed explicitly. )

OK so here's a fix to make that erw into a rw: just change the goal to itself :-/

      rw [mem_adicCompletionIntegers, h]
      -- ⊢ Valued.v (↑K ((algebraMap R K) r)) ≤ 1
      change Valued.v (((algebraMap R K) r) : UniformSpace.Completion K)  1
      -- ⊢ Valued.v (↑K ((algebraMap R K) r)) ≤ 1
      rw [Valued.valuedCompletion_apply] -- now works fine

With pp.all on of course there _is_ a difference (quite a large one), and the main difference is that, for reasons unknown to me, the change line is unfolding IsDedekindDomain.HeightOneSpectrum.adicCompletion. Indeed, this works too:

      rw [mem_adicCompletionIntegers, h]
      unfold IsDedekindDomain.HeightOneSpectrum.adicCompletion -- no visible effect on goal
      rw [Valued.valuedCompletion_apply] -- now works fine

I don't know why change is choosing to do this, but it occurred to me that this might be evidence that if we want to avoid erw then perhaps IsDedekindDomain.HeightOneSpectrum.adicCompletion should be an abbrev rather than a def. So I tried making this change, and the consequences are:

1) The erw around line 431 can be changed back to a rw;
2) the porting note around line 438 ("this proof used to be X") -- the original proof X now works again;
3) The erw on line 445 can be changed to a rw (except that this erw is now going to be deleted completely because of point (2));
4) The porting notes "added dsimp" on lines 461 and 466 or so can now be deleted;
and, unfortunately
5) the instance at the end of the file breaks :-(

The issue at the end of the file is that

#synth SMul ((adicCompletionIntegers K v)) (adicCompletion K v)

used to work, but now fails. Here

def adicCompletionIntegers : ValuationSubring (v.adicCompletion K) :=
  Valued.v.valuationSubring

So here are some proposals:

1) Leave IsDedekindDomain.HeightOneSpectrum.adicCompletion as a def, add

lemma adicCompletion_def : v.adicCompletion K = @UniformSpace.Completion K v.adicValued.toUniformSpace := rfl

and then add in a bunch of simp_rw [adicCompletion_def], e.g.

     erw [mem_adicCompletionIntegers, Valued.valuedCompletion_apply]

becomes

      simp_rw [mem_adicCompletionIntegers, adicCompletion_def, Valued.valuedCompletion_apply]

(you need simp_rw not rw because the adicCompletion is buried in the term, and the motive isn't type correct if you just rw).

2) Make it an abbrev, get nice clean code, delete some porting notes, and then deal with the fact that even though

#synth CommRing (adicCompletion K v)

works,

#synth SMul (adicCompletion K v) (adicCompletion K v)

fails (?!). This can be fixed with

instance : Algebra (adicCompletion K v) (adicCompletion K v) := Algebra.id _

but this is perhaps indicative of something not being OK.

Any suggestions?

Kevin Buzzard (May 18 2024 at 20:43):

Thinking about this a bit more, I'm minded to go for the abbrev approach and try and debug the typeclass instance failure. The trace for this

variable (A : Type*) [CommRing A] in
#synth Algebra A A

is

[Meta.synthInstance] ✅ Algebra A A ▼
  [] new goal Algebra A A ▼
    [instances] #[Algebra.id, inst✝²]
  [] ❌ apply inst✝² to Algebra A A ▶
  [] ✅ apply Algebra.id to Algebra A A ▶
  [] result Algebra.id A

However the trace for this

#synth Algebra (adicCompletion K v) (adicCompletion K v) -- fails

is this:

[Meta.synthInstance] 💥 Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
      (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▼
  [] new goal Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▼
    [instances] #[Algebra.id, UniformSpace.Completion.algebra, UniformSpace.Completion.algebra', IsDedekindDomain.HeightOneSpectrum.AdicCompletion.algebra', IsDedekindDomain.HeightOneSpectrum.instAlgebraAdicCompletion, inst✝¹]
  [] ❌ apply inst✝¹ to Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▶
  [] ❌ apply IsDedekindDomain.HeightOneSpectrum.instAlgebraAdicCompletion to Algebra
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▶
  [] 💥 apply IsDedekindDomain.HeightOneSpectrum.AdicCompletion.algebra' to Algebra
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▶

My reading of that trace is: "there are 6 ways that Lean can think of as feasible for finding this instance; the one which works (Algebra.id) is the most fundamental and is hence tried last because of design decisions which I am still not 100% sure that I buy, and of the five other ways, the first two fail and then the third one explodes (what the heck does 💥 mean and how is it different from ?) and for some reason we can never recover from the explosion and try the other two instances including the one which would have worked."

Ruben Van de Velde (May 18 2024 at 20:48):

Is the explosion "ran out of heartbeats"?

Kevin Buzzard (May 18 2024 at 20:58):

The error is failed to synthesize Algebra (adicCompletion K v) (adicCompletion K v) (as opposed to "please bump up heartbeats").

Here's a mwe:

import Mathlib.RingTheory.DedekindDomain.Ideal
import Mathlib.RingTheory.Valuation.ExtendToLocalization
import Mathlib.Topology.Algebra.ValuedField
import Mathlib.Algebra.Order.Group.TypeTags

noncomputable section

open scoped Classical DiscreteValuation

open IsDedekindDomain

variable {R : Type*} [CommRing R] [IsDedekindDomain R] {K : Type*} [Field K]
  [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R)

namespace IsDedekindDomain.HeightOneSpectrum

def intValuationDef (r : R) : ℤₘ₀ :=
  if r = 0 then 0
  else
    (Multiplicative.ofAdd
      (-(Associates.mk v.asIdeal).count (Associates.mk (Ideal.span {r} : Ideal R)).factors : ))

theorem int_valuation_ne_zero' (x : nonZeroDivisors R) : v.intValuationDef x  0 :=
  sorry

/-- The `v`-adic valuation on `R`. -/
def intValuation : Valuation R ℤₘ₀ where
  toFun := v.intValuationDef
  map_zero' := sorry
  map_one' := sorry
  map_mul' := sorry
  map_add_le_max' := sorry

def valuation (v : HeightOneSpectrum R) : Valuation K ℤₘ₀ :=
  v.intValuation.extendToLocalization
    (fun r hr => Set.mem_compl <| v.int_valuation_ne_zero' r, hr⟩) K

def adicValued : Valued K ℤₘ₀ :=
  Valued.mk' v.valuation

variable (K)

abbrev adicCompletion :=
  @UniformSpace.Completion K v.adicValued.toUniformSpace

#synth Algebra (v.adicCompletion K) (v.adicCompletion K) -- works

instance AdicCompletion.algebra' : Algebra R (v.adicCompletion K) := sorry

#synth Algebra (v.adicCompletion K) (v.adicCompletion K) -- fails

Kevin Buzzard (May 18 2024 at 21:03):

If you don't like the sorried data in AdicCompletion.algebra', then another approach is to open the file Mathlib/RingTheory/DedekindDomain/AdicValuation.lean, change def adicCompletion := on line 336 to abbrev adicCompletion := and then observe that #synth Algebra (adicCompletion K v) (adicCompletion K v) doesn't work at the end of the file just before the error, but does work before line 395 where the exploding algebra' instance is defined)

Kevin Buzzard (May 18 2024 at 21:12):

It also fails on nightly-testing with what looks like the same failure, which is this:

[Meta.synthInstance] 💥 Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
      (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▼
  [] new goal Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▼
    [instances] #[Algebra.id, UniformSpace.Completion.algebra, UniformSpace.Completion.algebra', IsDedekindDomain.HeightOneSpectrum.AdicCompletion.algebra', inst✝¹]
  [] ❌ apply inst✝¹ to Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▶
  [] 💥 apply IsDedekindDomain.HeightOneSpectrum.AdicCompletion.algebra' to Algebra
        (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) ▼
    [tryResolve] 💥 Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)
          (IsDedekindDomain.HeightOneSpectrum.adicCompletion K
            v) ≟ Algebra ?m.391137 (IsDedekindDomain.HeightOneSpectrum.adicCompletion ?m.391140 ?m.391144) ▼
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▼
        [] new goal Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
        [] ❌ apply inst✝¹ to Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
        [] ❌ apply Algebra.id to Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▼
        [] result <not-available> (cached)
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶
      [] ❌ Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K ▶

Michael Stoll (May 18 2024 at 21:13):

The question is, why does it try Algebra (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v) K so many times?

Kevin Buzzard (May 18 2024 at 21:13):

Just to be clear, I didn't truncate; it tries it that many times.

Michael Stoll (May 18 2024 at 21:13):

(after it decided that such an instance is not available the first time round)

Kevin Buzzard (May 18 2024 at 21:15):

My understanding of this trace is: "we need to find an Algebra instance, there are 5 ways to do it; the 5th one we'll try will work but we're not to know this yet; the first one fails, the second one explodes and we never recover, finishing by asking the same question 15 times"

Kevin Buzzard (May 18 2024 at 21:17):

This was supposed to be a question about the subtleties of rw v erw but I'm becoming convinced that the solution is just to change the def to an abbrev and then work around this issue somehow.

Michael Stoll (May 18 2024 at 21:18):

With set_option trace.Meta.synthPending true, I see [synthPending] synthPending ?m.8600 each time this is tried, but I have no idea what this means (there is no ?m8600 elsewhere in the output).

Michael Stoll (May 18 2024 at 21:19):

My feeling is that this is a bug in the instance synthesis algorithm.

Michael Stoll (May 18 2024 at 21:20):

It would be good to minimize this more. (I need to get some sleep, though.)

Jovan Gerbscheid (May 18 2024 at 23:11):

If you set_option trace.Meta.isDefEq true, you can see that the unification invoked by this instance does a lot of stuff, and throws an error in the end. In the type class resolution algorithm, there is one big tryCatch function on the outside, but there is no tryCatch around the individual isDefEq calls. It is for this reason that the error propagates all the way to the outside, and the other instances are not attempted.

Kevin Buzzard (May 18 2024 at 23:31):

I have it down to this but it's not going to get any smaller and it's still huge, it uses loads of algebra and loads of topology:

import Mathlib.RingTheory.DedekindDomain.Ideal
import Mathlib.RingTheory.Valuation.ExtendToLocalization
import Mathlib.Topology.Algebra.ValuedField
import Mathlib.Algebra.Order.Group.TypeTags

open scoped Classical DiscreteValuation

open IsDedekindDomain

variable {R : Type*} [CommRing R] [IsDedekindDomain R] {K : Type*} [Field K]
  [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R)

namespace IsDedekindDomain.HeightOneSpectrum

def intValuation {R : Type*} [CommRing R] [IsDedekindDomain R]
  (v : HeightOneSpectrum R) : Valuation R ℤₘ₀ where
  toFun r := if r = 0 then 0 else 0 -- just 0 doesn't work
  map_zero' := sorry
  map_one' := sorry
  map_mul' := sorry
  map_add_le_max' := sorry

-- this uses loads of algebra
noncomputable def valuation {R : Type*} [CommRing R] [IsDedekindDomain R] {K : Type*}
    [Field K] [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R) : Valuation K ℤₘ₀ :=
  v.intValuation.extendToLocalization
    (sorry : nonZeroDivisors R  v.intValuation.supp.primeCompl) K

variable (K)

-- this uses loads of topology
abbrev adicCompletion :=
  @UniformSpace.Completion K (Valued.mk' v.valuation).toUniformSpace

#synth Algebra (adicCompletion K v) (adicCompletion K v) -- works

instance AdicCompletion.algebra' {R : Type u_1} [CommRing R] [IsDedekindDomain R] [DecidableEq R]
    (K : Type u_2) [Field K] [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R) :
  Algebra R (v.adicCompletion K) := sorry

-- set_option trace.Meta.synthInstance true
-- set_option trace.Meta.isDefEq true
#synth Algebra (adicCompletion K v) (adicCompletion K v) -- fails

@Jovan Gerbscheid what is this "error in the end"? Are you talking about

                                                      [onFailure] 💥 Decidable.rec (fun h  (fun x  0) h) (fun h  (fun x  0) h)
                                                            (Classical.propDecidable
                                                              (((IsLocalization.toLocalizationMap (nonZeroDivisors R)
                                                                        K).sec
                                                                    z).1 =
                                                                0)) =?= Decidable.rec (fun h  (fun x  0) h)
                                                            (fun h  (fun x  0) h)
                                                            (Classical.propDecidable
                                                              (((IsLocalization.toLocalizationMap
                                                                        (nonZeroDivisors
                                                                          (IsDedekindDomain.HeightOneSpectrum.adicCompletion
                                                                            K v))
                                                                        K).sec
                                                                    z).1 =
                                                                0))

?

Jovan Gerbscheid (May 18 2024 at 23:37):

Hmmm, when setting set_option trace.Meta.isDefEq.hint true, the trace goes a bit further even.

Kevin Buzzard (May 18 2024 at 23:43):

It's possible to remove Classical and add decidable instances instead:

import Mathlib.RingTheory.DedekindDomain.Ideal
import Mathlib.RingTheory.Valuation.ExtendToLocalization
import Mathlib.Topology.Algebra.ValuedField
import Mathlib.Algebra.Order.Group.TypeTags

open scoped DiscreteValuation

open IsDedekindDomain

variable {R : Type*} [CommRing R] [IsDedekindDomain R] [DecidableEq R] {K : Type*} [Field K]
  [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R)

namespace IsDedekindDomain.HeightOneSpectrum

def intValuation {R : Type*} [CommRing R] [IsDedekindDomain R] [DecidableEq R]
  (v : HeightOneSpectrum R) : Valuation R ℤₘ₀ where
  toFun r := if r = 0 then 0 else 0 -- just 0 doesn't work
  map_zero' := sorry
  map_one' := sorry
  map_mul' := sorry
  map_add_le_max' := sorry

-- this uses loads of algebra
noncomputable def valuation {R : Type*} [CommRing R] [IsDedekindDomain R] [DecidableEq R] {K : Type*}
    [Field K] [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R) : Valuation K ℤₘ₀ :=
  v.intValuation.extendToLocalization
    (sorry : nonZeroDivisors R  v.intValuation.supp.primeCompl) K

variable (K)

-- this uses loads of topology
abbrev adicCompletion :=
  @UniformSpace.Completion K (Valued.mk' v.valuation).toUniformSpace

#synth Algebra (adicCompletion K v) (adicCompletion K v) -- works

instance AdicCompletion.algebra' {R : Type u_1} [CommRing R] [IsDedekindDomain R] [DecidableEq R]
    (K : Type u_2) [Field K] [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R) :
  Algebra R (v.adicCompletion K) := sorry

-- set_option trace.Meta.synthInstance true
--set_option trace.Meta.isDefEq true
--set_option trace.Meta.isDefEq.hint true
#synth Algebra (adicCompletion K v) (adicCompletion K v) -- fails

Jovan Gerbscheid (May 18 2024 at 23:48):

In the trace, I see at the end a [onFailure] trace node with the explode emoji, followed by two [hint] traces. Here is the code that must have thrown an error:

@[specialize] private def unstuckMVar (e : Expr) (successK : Expr  MetaM Bool) (failK : MetaM Bool): MetaM Bool := do
  match ( getStuckMVar? e) with
  | some mvarId =>
    trace[Meta.isDefEq.stuckMVar] "found stuck MVar {mkMVar mvarId} : {← inferType (mkMVar mvarId)}"
    if ( Meta.synthPending mvarId) then
      let e  instantiateMVars e
      successK e
    else
      failK
  | none   => failK

private def isDefEqOnFailure (t s : Expr) : MetaM Bool := do
  withTraceNodeBefore `Meta.isDefEq.onFailure (return m!"{t} =?= {s}") do
    unstuckMVar t (fun t => Meta.isExprDefEqAux t s) <|
    unstuckMVar s (fun s => Meta.isExprDefEqAux t s) <|
    tryUnificationHints t s <||> tryUnificationHints s t

From this I conclude that the second call to tryUnificationHints throws an error.

We have

def tryUnificationHints (t s : Expr) : MetaM Bool := do
  trace[Meta.isDefEq.hint] "{t} =?= {s}"
  unless ( read).config.unificationHints do
    return false
  if t.isMVar then
    return false
  let hints := unificationHintExtension.getState ( getEnv)
  let candidates  hints.discrTree.getMatch t UnificationHints.config
  for candidate in candidates do
    if ( tryCandidate candidate) then
      return true
  return false
where
  tryCandidate candidate : MetaM Bool :=
    withTraceNode `Meta.isDefEq.hint
      (return m!"{exceptBoolEmoji ·} hint {candidate} at {t} =?= {s}") do
    ...

from the trace I conclude that tryCandidate is not called. The only function I see that could give an error is hints.discrTree.getMatch.

Jovan Gerbscheid (May 18 2024 at 23:51):

The tryCatch block that I was talking about earlier only catches the very specific error throwIsDefEqStuck. It just so happens to be that DiscrTree.getMatch is able to throw this error :)

Kevin Buzzard (May 18 2024 at 23:55):

Indeed I see this in the trace:

[stuckMVar] found stuck MVar ?m.7891 : DecidableEq (IsDedekindDomain.HeightOneSpectrum.adicCompletion K v)

Jovan Gerbscheid (May 18 2024 at 23:57):

I don't know whether that is related or not

Jovan Gerbscheid (May 19 2024 at 00:04):

The expression being looked up the the DiscrTree during tryUnificationHints is this:

Decidable.rec
(fun h 
  (fun x 
      (Multiplicative.ofAdd
          (-↑((Associates.mk
                    ?m.8602.asIdeal).count
                (Associates.mk
                    (Ideal.span
                      {((IsLocalization.toLocalizationMap
                                (nonZeroDivisors
                                  (IsDedekindDomain.HeightOneSpectrum.adicCompletion
                                    K v))
                                K).sec
                            z).1})).factors))))
    h)
(fun h  (fun x  0) h)
(Classical.propDecidable
  (((IsLocalization.toLocalizationMap
            (nonZeroDivisors
              (IsDedekindDomain.HeightOneSpectrum.adicCompletion
                K v))
            K).sec
        z).1 =
    0))

And in the code for DiscrTree lookup, we have

  | .const c _     =>
    if ( getConfig).isDefEqStuckEx && e.hasExprMVar then
      if ( isRec c) then
        Meta.throwIsDefEqStuck

Jovan Gerbscheid (May 19 2024 at 00:05):

So that is what triggers the error

Jovan Gerbscheid (May 19 2024 at 00:08):

Note that recursors are unfolded if possible before lookup, so the fact that the recursor is still there means that it can't be unfolded due to metavariables.

Jovan Gerbscheid (May 19 2024 at 00:11):

Now I don't know what the correct fix for this bug is: do we put more catchInternalId isDefEqStuckExceptionId inside of type class synthesis, or do we put this in tryUnificationHints?

Kevin Buzzard (May 19 2024 at 00:13):

I think that this needs to move to #lean4 now -- would you like to open a thread there asking for advice? You can explain what's going on far more coherently than me. I think I'll just work around this by adding an instance and press on.

Jovan Gerbscheid (May 19 2024 at 00:42):

Update: I tried turning off unification hints, which gets rid of the bug. But then we get a deterministic time-out.

import Mathlib.RingTheory.DedekindDomain.Ideal
import Mathlib.RingTheory.Valuation.ExtendToLocalization
import Mathlib.Topology.Algebra.ValuedField
import Mathlib.Algebra.Order.Group.TypeTags

open scoped Classical DiscreteValuation

open IsDedekindDomain

variable {R : Type*} [CommRing R] [IsDedekindDomain R] {K : Type*} [Field K]
  [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R)

namespace IsDedekindDomain.HeightOneSpectrum

def intValuation {R : Type*} [CommRing R] [IsDedekindDomain R]
  (v : HeightOneSpectrum R) : Valuation R ℤₘ₀ where
  toFun r := if r = 0 then 0 else 0 -- just 0 doesn't work
  map_zero' := sorry
  map_one' := sorry
  map_mul' := sorry
  map_add_le_max' := sorry

-- this uses loads of algebra
noncomputable def valuation {R : Type*} [CommRing R] [IsDedekindDomain R] {K : Type*}
    [Field K] [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R) : Valuation K ℤₘ₀ :=
  v.intValuation.extendToLocalization
    (sorry : nonZeroDivisors R  v.intValuation.supp.primeCompl) K

variable (K)

-- this uses loads of topology
abbrev adicCompletion :=
  @UniformSpace.Completion K (Valued.mk' v.valuation).toUniformSpace

#synth Algebra (adicCompletion K v) (adicCompletion K v) -- works

instance AdicCompletion.algebra' {R : Type u_1} [CommRing R] [IsDedekindDomain R] [DecidableEq R]
    (K : Type u_2) [Field K] [Algebra R K] [IsFractionRing R K] (v : HeightOneSpectrum R) :
  Algebra R (v.adicCompletion K) := sorry

-- set_option trace.Meta.synthInstance true
-- set_option trace.Meta.isDefEq true
open Lean Meta Elab Command

syntax (name := ha) "#mysynth" term : command
@[command_elab ha] def elabSynth : CommandElab := fun stx => do
  let term := stx[1]
  withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_synth_cmd do
    withConfig ({· with unificationHints := false}) do
    let inst  Term.elabTerm term none
    Term.synthesizeSyntheticMVarsNoPostponing
    let inst  instantiateMVars inst
    let val   synthInstance inst
    logInfo val
    pure ()

#mysynth Algebra (adicCompletion K v) (adicCompletion K v) -- fails

Kevin Buzzard (May 19 2024 at 00:42):

My instinct is that you'll get more relevant eyes on this stuff if you open a new thread in #lean4 .

Michael Stoll (May 19 2024 at 08:53):

Kevin Buzzard said:

I think I'll just work around this by adding an instance and press on.

Alternatively, I assume that

attribute [local instance 1100] Algebra.id

would also make it use the instance that is successful.

Michael Stoll (May 19 2024 at 08:56):

BTW, Algebra.id has default priority 1000, but it is the very first Algebra instance defined, so it is tried last among the default priority instances.

Kevin Buzzard (May 19 2024 at 09:12):

And probably a great one to have higher priority because it's likely to fail quickly when it doesn't apply

Michael Stoll (May 19 2024 at 09:15):

I'm just now trying to see what the effect is: #13032

Michael Stoll (May 19 2024 at 10:43):

build instructions: -113.839 * 10⁹ (-0.1%)
I had to set the priority back to default in two places, where proofs broke.

Jovan Gerbscheid (May 19 2024 at 12:40):

Note that it is not true that instances with the same priority are always first-in-last-out. This is only true for instances that get stored under the same index in the DiscrTree. Which index is retrieved first is a DiscrTree implementation detail. But the implementation is such that star-patterns end up being tried last (which I think is a good thing), and Algebra.id has a star pattern for all its arguments, so that is why it gets tried last.

Michael Stoll (May 19 2024 at 15:41):

Kevin has fixed the breakages without the need to restore default instance priority, and the effect is a net positive (if small compared to the total build). I'll add the "awaiting review" label to #13032; let's see if there are further comments...

Henrik Böving (Jun 30 2024 at 16:46):

Michael Stoll said:

build instructions: -113.839 * 10⁹ (-0.1%)
I had to set the priority back to default in two places, where proofs broke.

Came here from Jovan's latest PR. Just to make sure: You are aware of the fact that build instructions are not at all deterministic for Lean and changes at this magnitude can also simply amount to noise yes? Lean's implementation contains a couple of things that do not have deterministic behavior in terms of instruction count at all so elaborating a Lean file twice does not have to yield the same instruction count.

Kevin Buzzard (Jun 30 2024 at 16:51):

Which things don't have deterministic behaviour, out of interest? I've seen count_heartbeats give different answers on the same declaration before and never understood why.

Michael Stoll (Jun 30 2024 at 17:00):

And can you be more precise as to the (expected) relative size of the noise? That would be helpful in interpreting the !bench results.

Henrik Böving (Jun 30 2024 at 19:28):

Kevin Buzzard said:

Which things don't have deterministic behaviour, out of interest? I've seen count_heartbeats give different answers on the same declaration before and never understood why.

I'm not aware of all things that produce non deterministic behavior but I know one for sure. Some of the core routines that operate on Expr do build caches based on pointers modulo cache size. For example forEachExprWhere has this implementation for its cache.

abbrev cacheSize : USize := 8192 - 1

structure State where
  /--
  Implements caching trick similar to the one used at `FindExpr` and `ReplaceExpr`.
  -/
  visited : Array Expr   -- Remark: our "unsafe" implementation relies on the fact that `()` is not a valid Expr
  /--
  Set of visited subterms that satisfy the predicate `p`.
  We have to use this set to make sure `f` is applied at most once of each subterm that satisfies `p`.
  -/
  checked : HashSet Expr

unsafe def initCache : State := {
  visited := mkArray cacheSize.toNat (cast lcProof ())
  checked := {}
}

abbrev ForEachM {ω : Type} (m : Type  Type) [STWorld ω m] [MonadLiftT (ST ω) m] [Monad m] := StateRefT' ω State m

unsafe def visited (e : Expr) : ForEachM m Bool := do
  let s  get
  let h := ptrAddrUnsafe e
  let i := h % cacheSize
  let k := s.visited.uget i lcProof
  if ptrAddrUnsafe k == h then
    return true
  else
    modify fun s => { s with visited := s.visited.uset i e lcProof }
    return false

Now what can happen is that because all modern operating systems use ASLR and similar mechanisms to randomize addresses it might be that an Expr that was previously cached just happens to get evicted from the cache earlier on the next run and you end up doing different computation. A similar mechanism is used in Expr.replace, potentially causing a result to be cached in one run but by coincidence not in another run.

There are probably more things but this is at least a witness for the existence of non determinism.

Michael Stoll said:

And can you be more precise as to the (expected) relative size of the noise? That would be helpful in interpreting the !bench results.

I think the best way to figure that out for mathlib would be to simply rerun a !bench twice (or more) on the same commit and see how much your values are varying? All numbers I'm aware of are specific to the Lean compiler repository though there it is usually in the .x percent or lower. Other "almost deterministic" metrics like branch-misses do tend to vary low single digit percentages for example.

Kim Morrison (Jul 01 2024 at 02:10):

Definitely ignore anything under 0.5% variation in instruction count, and be skeptical of anything under 1%. Above 1% you're allowed to bring out the :tada: right away. :-)

Kim Morrison (Jul 01 2024 at 02:11):

file#CategoryTheory/Limits/FilteredColimitCommutesFiniteLimit has previously been identified as an example of a particularly nondeterministic file.

Kim Morrison (Jul 01 2024 at 02:11):

It has been on my TODO list for too long to try to identify exactly what part of this file is so variable.

Kim Morrison (Jul 01 2024 at 02:12):

If anyone would like to help out there that would be extremely helpful.

Kim Morrison (Jul 01 2024 at 02:12):

We would really like to reduce this nondeterminism, because of course it makes optimisation hard. But we need better examples still for this.

Kim Morrison (Jul 01 2024 at 02:13):

Note that Mathlib now has count_heartbeats! in tac which will run tac 10 times and report the variation.

Kim Morrison (Jul 01 2024 at 02:13):

I hope this is useful for understanding examples like this file (which it was written for, but then I haven't got back to).


Last updated: May 02 2025 at 03:31 UTC