Zulip Chat Archive

Stream: mathlib4

Topic: flexible vs rigid tactics


Yaël Dillies (Mar 19 2024 at 17:58):

I think all the tactic combinators like done, skip, try, any_goals, all_goals should be allowed

Damiano Testa (Mar 19 2024 at 18:00):

Ok, I think that the way in which the "following tactic" is selected means that the tactic following the combinator will be flagged as well (unless it is whitelisted). This is the intended behaviour, right? I.e., the combinator should not be a stop-gag, just a see-through tactic that passes on the decision to the next one.

Do you agree?

Yaël Dillies (Mar 19 2024 at 18:03):

I don't really like the current logic of your linter. I think a more pertinent logic would be to mark locations (hypotheses/goals) as "non-squeezedly simped" and forbidding "rigid" tactics from using those assumptions

Yaël Dillies (Mar 19 2024 at 18:04):

Eg this should be allowed:

simp at h0
rw [h1] at h2

but neither of these should

simp at h0
rw [h1] at h0
simp at h0
rw [h0] at h1

Damiano Testa (Mar 19 2024 at 18:04):

Yaël, I do not really understand what you are proposing... what is a "non-squeezedly simped" location?

Damiano Testa (Mar 19 2024 at 18:05):

Maybe I should specify: currently, the "next tactic" is "the next tactic that acts on a goal that a simp produced".

Damiano Testa (Mar 19 2024 at 18:05):

It is not the next tactic that you type.

Damiano Testa (Mar 19 2024 at 18:05):

(or rather, it need not be, although often it will be.)

Yaël Dillies (Mar 19 2024 at 18:06):

That sounds really brittle. That means that

simp
rw [h]

is forbidden but

simp
ring
rw [h]

is not

Yaël Dillies (Mar 19 2024 at 18:06):

Do you understand my proposition better now?

Damiano Testa (Mar 19 2024 at 18:08):

Yes, I think that I understand. You would like something like a cache of "after simp locations" that are only allowed actions by "flexible" tactics, including simp, ring,..., but, for instance, not rw.

Damiano Testa (Mar 19 2024 at 18:08):

I wonder whether this should come after a first implementation of the linter, though...

Damiano Testa (Mar 19 2024 at 18:09):

This seems more like a "non-terminal flexible tactic follower" linter to me.

Yaël Dillies (Mar 19 2024 at 18:09):

I think it's worth exploring! If we merge your linter now, we're at risk of teachings seas of beginners the wrong style

Damiano Testa (Mar 19 2024 at 18:09):

This is certainly a very good suggestion, but I think that focusing on simp might already be productive.

Yaël Dillies (Mar 19 2024 at 18:10):

Damiano Testa said:

This seems more like a "non-terminal flexible tactic follower" linter to me.

Yes, that's exactly what it is, because simp is really no special here

Damiano Testa (Mar 19 2024 at 18:10):

Yaël Dillies said:

I think it's worth exploring! If we merge your linter now, we're at risk of teachings seas of beginners the wrong style

Ok, this is possible, but it is also possible that they can then learn a better style later on.

Yaël Dillies (Mar 19 2024 at 18:10):

Mathport experience would like to disagree...

Yaël Dillies (Mar 19 2024 at 18:11):

I claim that the correct implementation is so different to your current one that it's not worth trying to incrementally get to the correct one from yours

Damiano Testa (Mar 19 2024 at 18:11):

Ok, so maybe the tactics that I am placing in the whitelist should be all allowed and should never be followed by anything else.

This is probably not that hard to implement, although I have not really given this much thought.

Yaël Dillies (Mar 19 2024 at 18:12):

Also, you already ripped out most of the benefits from your linter by PRing fixes to the errors you found locally

Yaël Dillies (Mar 19 2024 at 18:12):

More errors will come, but only with a better logic

Damiano Testa (Mar 19 2024 at 18:12):

Yaël Dillies said:

I claim that the correct implementation is so different to your current one that it's not worth trying to incrementally get to the correct one from yours

The current suggestion is very far from v1 of the linter, not, I think, from v2 that I am testing right now.

Yaël Dillies (Mar 19 2024 at 18:12):

I think you should not allow specific tactics but instead disallow specific rigid tactics

Damiano Testa (Mar 19 2024 at 18:13):

Yaël Dillies said:

Also, you already ripped out most of the benefits from your linter by PRing fixes to the errors you found locally

Very few of those PRs have been merged and have conflicts now. I am planning to close them, if the newer version of the linter is definitely an improvement.

Damiano Testa (Mar 19 2024 at 18:14):

Yaël Dillies said:

I think you should not allow specific tactics but instead disallow them

As I am thinking of this right now, there are only two categories, right? The category of tactics that can only act on goals on which a flexible tactic has not yet acted, and the flexible tactics, right? You are saying that a tactic if automatically flexible, unless it is blacklisted?

Yaël Dillies (Mar 19 2024 at 18:15):

Nono, sorry I clarified

Damiano Testa (Mar 19 2024 at 18:15):

(the two perspective are easy to implement, it seems to be that we are simply deciding what the default category of each tactic is)

Yaël Dillies (Mar 19 2024 at 18:16):

The default should be rigid. This is what I'm claiming

Damiano Testa (Mar 19 2024 at 18:16):

Ok, I agree. By default, everything is like rw (which I consider the default rigid tactic).

Damiano Testa (Mar 19 2024 at 18:17):

And if a goal/location has been produced/touched by a flexible tactic, then only flexible tactics are allowed to touch it.

Yaël Dillies (Mar 19 2024 at 18:17):

... or use it!

Damiano Testa (Mar 19 2024 at 18:18):

(the "location part" I am not too sure about -- the current linter is implemented thinking about goals, but the linting framework does have access to the context.)

Yaël Dillies (Mar 19 2024 at 18:18):

Actually, it's not merely "touched by a flexible tactic" that's bad but "potentially touched"

Damiano Testa (Mar 19 2024 at 18:19):

"potentially touched" might include everything, though? Do you mean that simp_all makes everything flexible, even if it actually just modifies one hypothesis?

Yaël Dillies (Mar 19 2024 at 18:19):

So the correct rule is:

Only flexible tactics are allowed to target or use locations which have been previously been targeted by flexible tactics (regardless of whether said flexible tactics actually changed those locations)

Yaël Dillies (Mar 19 2024 at 18:21):

Yes, simp at * means that no rigid tactic can target anything in that context anymore. If you then do have := something_not_from_context, that can be touched by a rigid tactic however,

Damiano Testa (Mar 19 2024 at 18:21):

I understand your point, although I wonder whether this requires knowledge of what each tactic could do... E.g., when you see a simp, you should figure out whether it is at and what the at locations are. I think that this would be very hard to maintain as a linter.

Yaël Dillies (Mar 19 2024 at 18:21):

Yes, but luckily there aren't many flexible tactics

Yaël Dillies (Mar 19 2024 at 18:22):

Regardless, information on targeted locations should be part of the tactic framework long term

Damiano Testa (Mar 19 2024 at 18:22):

To avoid scope-creep, what do you think of focusing on goals, rather than locations, to begin with?

Yaël Dillies (Mar 19 2024 at 18:23):

I don't see how that could give you anything sensible

Yaël Dillies (Mar 19 2024 at 18:23):

There are too many ways the goal-based heuristic could go wrong

Damiano Testa (Mar 19 2024 at 18:25):

Yaël Dillies said:

There are too many ways the goal-based heuristic could go wrong

While this is true, I also think that for most proofs, the goal heuristic is actually good.

Yaël Dillies (Mar 19 2024 at 18:26):

Yes, but I really really do not want false linter positives

Damiano Testa (Mar 19 2024 at 18:26):

(Also, I am aware of my abilities and, while I can see myself coding the linter with locations and all, I can also see the result as being unusably slow...)

Damiano Testa (Mar 19 2024 at 18:27):

Yaël Dillies said:

Yes, but I really really do not want false linter positives

You can view it as "Every silencing of the linter is a todo"...

Damiano Testa (Mar 19 2024 at 18:28):

I am going to move all this conversation to a separate topic, is that alright?

Damiano Testa (Mar 19 2024 at 18:29):

(assuming that I can...)

Notification Bot (Mar 19 2024 at 18:31):

49 messages were moved here from #mathlib4 > simp followers by Damiano Testa.

Damiano Testa (Mar 19 2024 at 18:33):

Ok, to seriously implement this rigid vs flexible linter, I need to see what kind of information hides in the MetavarContext that the linter offers.

Damiano Testa (Mar 19 2024 at 18:44):

Just to be clear, what you are suggesting is that, after a flexible tactic that potentially changes all locations, that metavariable can (almost) only be closed by flexible tactics: e.g. assumption, tauto, linarith,..., have+something else,... and have or something similar is the only way in which rigid tactics can play again there, and then only on the new hypotheses that have been introduced.

Damiano Testa (Mar 19 2024 at 18:46):

I agree that this would make proofs much less brittle. It may also be somewhat of an added burden to write proofs in this "flexible-vs-rigid" style.

Yaël Dillies (Mar 19 2024 at 19:56):

I don't think it will be much burden since this is already how we write most proofs

Damiano Testa (Mar 30 2024 at 13:02):

Some iteration of the "non-terminal simp" linter produces the output below. Currently, the linter only looks at simp and simp_all, but this is easy to expand. What the linter is supposed to do is check if some tactics that are likely to break with small changes to the simp output follow simp.

The linter makes an effort to flag simp at h; rw [h] as well as simp at h; rw at h.

The lnter should not flag simp at h1; rw [h2] at h3, assuming that h1 is different from h2, h3.

Linted

Damiano Testa (Mar 30 2024 at 13:02):

Let me know if something from the output above looks like something that the linter should handle differently (i.e. should allow it).

[I gave one line of context before each flag, which sometimes may not be enough to reconstruct what the issue was.]

Yaël Dillies (Mar 30 2024 at 13:25):

I'm not sure about the induction as with simp thing. Is simp not an identifier there?

Yaël Dillies (Mar 30 2024 at 13:26):

I assume that in the RingTheory.Polynomial Cyclotomic.Eval example the above tactic is a simp operating on the goal that's focused?

Yaël Dillies (Mar 30 2024 at 13:27):

simp [q_zero]
by_cases p_zero : p = 0

should be legal since by_cases doesn't operate on the goal.

Yaël Dillies (Mar 30 2024 at 13:28):

Similarly for the Probability.Kernel.Disintegration.Density one

Ruben Van de Velde (Mar 30 2024 at 13:35):

No, induction as with simp runs simp on each of the new subgoals

Yaël Dillies (Mar 30 2024 at 13:50):

That's quite confusing given the induction' syntax :thinking:

Damiano Testa (Mar 30 2024 at 15:42):

Yaël Dillies said:

simp [q_zero]
by_cases p_zero : p = 0

should be legal since by_cases doesn't operate on the goal.

Yes, there are still some rough edges around flagging simp when a new goal is created by the tactic.

However, what I pasted there is the first failure in each file with basically no extra filter, so this is promising!

Damiano Testa (Apr 01 2024 at 16:08):

The "flexible" linter is essentially ready: #11821

The PR above only defines the linter and adds a test file: the linter is not running on any file other than the test.

There is a separate PR where I added the linter to most of mathlib: you can find it here: #11822.

Here is a gist with the linter warnings from a separate run: there are approximately 230 files that the linter flags. I scanned quickly a few of them and the linter seems to be doing its job!

Damiano Testa (Apr 01 2024 at 16:10):

One situation that the linter does not handle correctly is the following:

example (h : 0 = 0) : True := by
  simp at h  -- h : True
  assumption

Clearly, assumption is using h, but since h is not part of the syntax of the tactic, the linter is unaware of that.

The linter does flag

example (h : 0 = 0) : True := by
  simp at h  -- h : True
  exact h

Damiano Testa (Apr 01 2024 at 16:11):

Btw, if anyone feels like going through the gist above and knock down a few non-terminal simps, I would be more than thrilled!

Damiano Testa (Apr 01 2024 at 16:12):

(Also, while the whole setup would allow tactics other than simp to be flexible, I am only linting simp at the moment: already as it, it will be quite a lot of work to make mathlib compliant.)

Damiano Testa (Apr 02 2024 at 09:02):

While going through some of the lints, this is the most controversial that I found so far: is the pattern

  ... <;> simp <;> split_ifs <;> simp

acceptable? What if the final simp is exact or rw?

Damiano Testa (Apr 02 2024 at 09:04):

I personally am not a huge fan of the <;>-combinator, because I usually value more clarity than saving space and I find that it more often obscures, rather than explain.

In some cases, my solution has been to unlint, in others to squeeze, in others still to split the <;> combinators, depending on context.

Yaël Dillies (Apr 02 2024 at 09:05):

Damiano Testa said:

While going through some of the lints, this is the most controversial that I found so far: is the pattern

  ... <;> simp <;> split_ifs <;> simp

acceptable? What if the final simp is exact or rw?

Surely that's fine iff the final tactic is flexible?

Damiano Testa (Apr 02 2024 at 09:06):

Well, if could in theory be resolved by a better simp and then split_ifs would fail.

Damiano Testa (Apr 02 2024 at 09:07):

I am not sure that I consider split_if flexible. Are you saying that split_ifs should simply be allowed as a simp follower?

Yaël Dillies (Apr 02 2024 at 09:08):

I think split_ifs is neither flexible nor rigid. It's just a combinator

Yaël Dillies (Apr 02 2024 at 09:08):

Damiano Testa said:

Well, if could in theory be resolved by a better simp and then split_ifs would fail.

In that case, you just remove the combinator, which should be a pretty obvious step to take

Mario Carneiro (Apr 02 2024 at 09:09):

Damiano Testa said:

Well, if could in theory be resolved by a better simp and then split_ifs would fail.

Most flexible tactics have this behavior

Mario Carneiro (Apr 02 2024 at 09:09):

simp can always produce something that the next tactic can't handle, flexible or otherwise

Mario Carneiro (Apr 02 2024 at 09:10):

but it's not dependent on the nitty gritty details of the goal, so it's flexible

Damiano Testa (Apr 02 2024 at 09:10):

Right, I agree that this is the behaviour of most flexible tactics, and the linter is trying to mitigate this by only allowing flexible tactics to follow flexible tactics. There are a lot of rigid tactics following flexible ones already as is, so I am trying to find some common ground...

Damiano Testa (Apr 02 2024 at 09:11):

Anyway, I'll add split_ifs to the ignored list, which means that the linter would

  • not complain on simp <;> split_ifs <;> simp but
  • would complain on simp <;> split_ifs <;> rw

Damiano Testa (Apr 05 2024 at 15:12):

Another question: should this proof be allowed, or should simp be squeezed?

  by simp [multiplicity, rootMultiplicity, Part.Dom]; congr; funext; congr

Note that there is an underlying decision that congr is allowed as a simp follower, so really the question is whether the funext jammed in there is acceptable or whether it is reason for preferring a simp only instead.

Ruben Van de Velde (Apr 05 2024 at 15:42):

The last congr solves the goal? Too bad simp can't handle it on its own

Damiano Testa (Apr 05 2024 at 15:43):

Yes, this is the full declaration:

-- Mathlib/Algebra/Polynomial/Div.lean
theorem rootMultiplicity_eq_multiplicity [DecidableEq R] [@DecidableRel R[X] (·  ·)]
    (p : R[X]) (a : R) :
    rootMultiplicity a p =
      if h0 : p = 0 then 0 else (multiplicity (X - C a) p).get (multiplicity_X_sub_C_finite a h0) :=
  by simp [multiplicity, rootMultiplicity, Part.Dom]; congr; funext; congr
#align polynomial.root_multiplicity_eq_multiplicity Polynomial.rootMultiplicity_eq_multiplicity

Damiano Testa (Apr 05 2024 at 15:44):

and this is the goal before the last congr

Nat.find  = Nat.find 

:man_facepalming:

Damiano Testa (Apr 05 2024 at 15:58):

Is it expected that adding

set_option pp.deepTerms true/false in -- I tried both

before the declaration, I still see the ...?

Yaël Dillies (Apr 05 2024 at 16:26):

Damiano Testa said:

and this is the goal before the last congr

Nat.find  = Nat.find 

:man_facepalming:

I feel like the predicate argument to docs#Nat.find should be explicit

Kevin Buzzard (Apr 05 2024 at 17:02):

Damiano Testa said:

Is it expected that adding

set_option pp.deepTerms true/false in -- I tried both

before the declaration, I still see the ...?

I've been using set_option pp.proofs.threshold 37 to expand ....

Damiano Testa (Apr 05 2024 at 18:31):

Thanks Kevin! This is the goal:

 Nat.find (multiplicity_X_sub_C_finite a (Eq.mpr_not (Eq.refl (p = 0)) x)) =
  Nat.find
    (id (Eq.refl (PartENat.find fun n  ¬(X - C a) ^ (n + 1)  p)) 
      multiplicity_X_sub_C_finite a (Eq.mpr_not (Eq.refl (p = 0)) x))

Kevin Buzzard (Apr 05 2024 at 21:22):

(I tried 0 and it didn't work so I tried 37 and it worked great)

Yaël Dillies (May 23 2024 at 06:51):

I've just noticed that something we left out of the discussion is that tactics have different flexibility regarding their inputs and outputs eg in simp [ha] at hb we have

  • ha is a rigid input
  • hb is a flexible input
  • hb is a flexible output

Yaël Dillies (May 23 2024 at 06:52):

And, perhaps more subtly, ha is a rigid output in have ha : Foo := by simpa using hb but a flexible output in have ha := by simpa using hb

Yaël Dillies (May 23 2024 at 06:54):

I'm not quite sure how to classify ha in intermediate situations like have ha : Foo _ := by simpa using hb. I guess it should be considered rigid even if it allows have ha : _ := by simpa using hb to leave ha rigid

Damiano Testa (May 23 2024 at 07:38):

On the front of the flexible vs rigid, I was pretty happy with the linter output. However, I personally find that there is no uniformly good way of pleasing the linter: replacing simp [tacs] with

  • the output of simp? [tacs] hides what tacs is and is often too verbose;
  • simp? [tacs] says ... is again very verbose and also means that you still have to update the list of tactics when the simp-set changes;
  • suffices ... can again be very verbose, when the goals are long.

Overall, while a non-terminal simp (or rigid tactic following a flexible one more generally) is not great for stability, I am not sure that the alternatives that are currently available are really an net improvement.

Damiano Testa (May 23 2024 at 07:49):

I have been thinking about a possible alternative. Here is an initial idea.

If a simp no longer works on a branch, but was present in master, then squeezing it in master would tell you what to use. How about we write a tactic squeeze_me_earlier that takes a tactic as input (initially just simp) and

  • checks out master,
  • downloads the cache,
  • attempts to find the correct simp;
  • runs simp? recording the output;
  • checks out the branch in development;
  • says Try this: [recorded output of `simp?`].

This could also be something that runs in CI, possibly applying to simps that break downstream of your changes, maybe in an untouched file, to begin with.

Michael Rothgang (Jul 24 2024 at 12:58):

#15101 fixes about a hundred warnings from the "flexible tactic" linter, in a way that is hopefully non-controversial. #15102 was also found by the linter, and is a small performance improvement in itself.

Michael Rothgang (Jul 25 2024 at 14:42):

#15130 has a few more

Michael Rothgang (Aug 02 2024 at 10:47):

#15318 has a few more

Michael Rothgang (Aug 04 2024 at 18:21):

Update: I have finished going through all warnings on mathlib. #13812 either disables the linter with a comment, or fixes its warning. The fixes have been split into #15318.
For the manual suppressions, I can see a few trends

  • a category theory pattern: simp followed by coherence (I have always allowed the linter)
  • a number of errors are about simp followed by erw, with a comment pointing to lean4#2644: I've left those as is, as fixing the underlying issue will also fix this linter warning
  • <;> simp (say, for two goals created by by_cases or induction), followed by some further proof. The simp sets are usually different, and sometimes long enough be unreadable when spelled out
  • there are surely more, for which I'd need to look at the suppressions myself

Michael Rothgang (Aug 15 2024 at 19:10):

I just merged master and fixed CI again: compared to master, enabling the flexible linter requires

  • two minor fixes (three lines, will PR soon) and
  • 187 individual linter suppressions (commented)
    To be honest, this number does not seem absurdly high --- just from the churn perspective, one could even enable the linter by default (with a clear note that this may be disabled easily).

Michael Rothgang (Aug 15 2024 at 19:15):

Out of these 187 exceptions,

  • 14 are because of lean4#2644/related to simp and erw
  • 5 are about simp followed by coherence
  • 4 exceptions are about simp followed by exact, which cannot be combined into simpa
  • most remaining exceptions are cases where either
    • the set of simp lemmas is long enough that squeezing it is ugly
    • simp is applied to several goals, often with different (and/or quite large) simp sets

David Ang (Oct 04 2024 at 10:33):

I'm not sure how flexible the flexible linter is, but do tactic patterns like field_simp; linear_combination go through with no complaints? I believe field_simp is just a juiced up version of simp and linear_combination is just a juiced up version of ring. I'm asking this because I use this pattern quite a lot in my files.

David Ang (Oct 04 2024 at 10:35):

I know that simp; ring is fine, but if field_simp is tagged as a simp but linear_combination is not tagged as a ring then it would complain, since linear_combination is not in the list of flexible tactics.

Damiano Testa (Oct 04 2024 at 11:46):

Neither field_simp, nor linear_combination are "flexible: this means that, by default, they are rigid. Hence, simp; field_simp counts as a non-terminal simp and the linter would flag it.

Damiano Testa (Oct 04 2024 at 11:47):

It is easy to add more tactics to the "flexible" list and maybe there is a case for field_simp to be flexible. I am not sure that linear_combination should count as flexible, though, since it is quite fixed to exact shapes, I think.

Yaël Dillies (Oct 04 2024 at 11:48):

I think of field_simp and linear_combination as flexible, FWIW

Damiano Testa (Oct 04 2024 at 11:52):

With linear_combination, also if it uses a hypothesis that has been the target of simp?

Yaël Dillies (Oct 04 2024 at 11:54):

Surely yes?

Damiano Testa (Oct 04 2024 at 11:55):

I view it as somewhat brittle, since that hypothesis could change with an increase/decrease in the simp-set and it is not clear that linear_combination would be able to cope.

Damiano Testa (Oct 04 2024 at 11:55):

But it is almost no work to make linear_combination and field_simp flexible, and I am happy to add them!

David Ang (Oct 04 2024 at 12:21):

I'd argue that they are flexible, unless you change their default normalisation (and I'm very guilty of this)

Damiano Testa (Oct 04 2024 at 12:28):

Ok, here is an example which is maybe not too contrived of why I am not completely convinced that linear_combination is flexible:

example (x y : ) (h1 : x*y + 2*x = 1) (h2 : 2 * x = 2 * y) : x*y = -2*y + 1 := by
  --simp at h2  -- uncomment to break `linear_combination`
  linear_combination 1*h1 - h2

Ruben Van de Velde (Oct 04 2024 at 12:28):

Yeah, I don't think it is

Damiano Testa (Oct 04 2024 at 12:28):

Of course, any tactic could be broken by doing something different before it, so really everything is rigid.

Ruben Van de Velde (Oct 04 2024 at 12:29):

field_simp is a difficult one, though

Damiano Testa (Oct 04 2024 at 12:29):

But my mental model is that simp is "truly" flexible, while linear_combination is "somewhat flexible", but not so much.

David Ang (Oct 04 2024 at 12:30):

If this is the case, then arguably field_simp; linear_combination should be flagged, but it's not?

Damiano Testa (Oct 04 2024 at 12:32):

Something probably has to be done about field_simp, but I view field_simp as a "normalization" tactic: it should (in theory) produce a reliably equal result from a range of inputs, like norm_num: this, to me, makes it look like a tactic that can follow a flexible one and can be followed by whatever.

Damiano Testa (Oct 04 2024 at 12:32):

(Right now, though, I think that field_simp is purely rigid and cannot follow a flexible one.)

Damiano Testa (Oct 04 2024 at 12:34):

So, I think that field_simp; linear_combination is ok, the linter correctly does not flag it, but for the wrong reason! :smile:

David Ang (Oct 04 2024 at 12:35):

But doesn't field_simp do everything that simp does, except for three lemmas that surely doesn't make it a lot more flexible than simp? I'm now also confused about norm_num because isn't it just norm_num1; simp?

Damiano Testa (Oct 04 2024 at 12:35):

To give a little bit of extra context, the "flexible" linter started as a "non-terminal simp linter". It evolved in splitting tactics into "flexible" and "rigid": a "rigid" tactic cannot follow a "flexible" one, since the output of a flexible one is unreliable and could break the rest of the proof.

Damiano Testa (Oct 04 2024 at 12:36):

In development, some tactics actually take a variety of inputs and convert them to normal form, like norm_num: if you stray a little from the initial input, chances are that norm_num will return the same result.

Damiano Testa (Oct 04 2024 at 12:36):

Such tactics can more reliably be followed by rigid ones, since it would be a big jump to make them normalize to something different.

Damiano Testa (Oct 04 2024 at 12:37):

These tactics are "stoppers": they can follow a flexible one, since they can adapt to multiple outputs, but have "fewer" outputs, so can be followed by rigid ones.

Damiano Testa (Oct 04 2024 at 12:37):

In this context, there is always some discretion of "how big of a change you expect the typical change to be".

Damiano Testa (Oct 04 2024 at 12:38):

Of course, a big enough change will break whatever.

David Ang (Oct 04 2024 at 12:38):

Here is my concern: take your example above and replace simp with norm_num or field_simp, and they both break

Damiano Testa (Oct 04 2024 at 12:38):

But the kind of simp inside norm_num and field_simp is not "all simp lemmas", but "a curated list of lemmas, some that are simp, some that make sense in this context.

Damiano Testa (Oct 04 2024 at 12:40):

David Ang said:

Here is my concern: take your example above and replace simp with norm_num or field_simp, and they both break

Right, so I view this as evidence that linear_combination is "rigid". As for norm_num and field_simp, they of course can normalize to something different, but I view this as more rare.

Damiano Testa (Oct 04 2024 at 12:41):

If your point of view is "a change in the previous tactic may break the next", then nothing would be allowed.

Yaël Dillies (Oct 04 2024 at 12:41):

David Ang said:

If this is the case, then arguably field_simp; linear_combination should be flagged, but it's not?

Given that field_simp is meant as a preprocessor to tactics like ring and linear_combination, it would be very damageable to forbid field_simp; linear_combination!

Damiano Testa (Oct 04 2024 at 12:43):

Right, the example above with field_simp instead of simp breaks linear_combination, but it is unlikely that a working pair field_simp; linear_combination would break after a change in field_simp.

Damiano Testa (Oct 04 2024 at 12:44):

Whereas a working pair simp; linear_combination is less likely to continue working, as simp evolves.

David Ang (Oct 04 2024 at 12:45):

Right, but your "change" in field_simp (that is unlikely to break) refers exclusively to changes in field_simps

Damiano Testa (Oct 04 2024 at 12:46):

Yes, I expect field_simp to converge to some normal form that is "highly unlikely" to change: an expression of the form 2 * x / 2 should be normalized to x no matter how many times field_simp is reimplemented.

Damiano Testa (Oct 04 2024 at 12:47):

Whereas whether that same expression is simplified or not by a simp lemma depends more unreliably on the simp set.

Damiano Testa (Oct 04 2024 at 12:49):

That's why I consider field_simp; something_else "robust": the something_else is using a "field normal form". Something unusual has to have happened for the notion of "field normal form" to have changed.

David Ang (Oct 04 2024 at 12:49):

Maybe my concern is better phrased like this: currently we have tactics (e.g. field_simp or norm_num) that are basically (do something); simp, and you argue that we can put them before doing something rigid. On the other hand, I could may well do (do something); simp; (do something rigid), which should have identical behaviour but is flagged.

Damiano Testa (Oct 04 2024 at 12:49):

They are not a "free" simp, they are a "controlled simp".

Damiano Testa (Oct 04 2024 at 12:50):

You should view them more as simp onlys.

David Ang (Oct 04 2024 at 12:52):

How controlled are they? I don't know exactly how they're defined but the docs tell me e.g.: field_simp [hx, hy] is a short form for simp (disch := field_simp_discharge) [-one_div, -one_divp, -mul_eq_zero, hx, hy, field_simps], which is quite far from being a simp only?

Damiano Testa (Oct 04 2024 at 12:52):

From the doc-string of field_simp:

The goal of `field_simp` is to reduce an expression in a field to an expression of the form `n / d`
where neither `n` nor `d` contains any division symbol, just using the simplifier (with a carefully
crafted simpset named `field_simps`)

Damiano Testa (Oct 04 2024 at 12:55):

There are about 40 field_simps lemmas.

David Ang (Oct 04 2024 at 12:55):

Right, then shouldn't the implementation be closer to simp only [field_simps] rather than simp [field_simps]?

Mario Carneiro (Oct 04 2024 at 12:55):

I think David's point is that it does not use only

Mario Carneiro (Oct 04 2024 at 12:55):

likewise for norm_num

Sébastien Gouëzel (Oct 04 2024 at 12:55):

The field_simps set of lemmas is indeed added to the simpset when applying field_simp, but almost nothing is removed if I recall correctly, so adding new simp lemmas will definitely change the outcome of field_simp. In terms of rigidity, simp and field_simp should definitely be on the same line.

Ruben Van de Velde (Oct 04 2024 at 12:56):

Or should we change that? I'm not sure I want field_simp to go changing random things in my goal

Ruben Van de Velde (Oct 04 2024 at 12:56):

Or we could have both

Damiano Testa (Oct 04 2024 at 12:57):

Ok, so I can then make field_simp flexible and then linear_combination would not be allowed to follow it.

Mario Carneiro (Oct 04 2024 at 12:57):

field_simp only?

Mario Carneiro (Oct 04 2024 at 12:57):

you can already do this for norm_num

Damiano Testa (Oct 04 2024 at 12:58):

So only would mean "just field_simps (plus whatever you explicitly add)"?

Damiano Testa (Oct 04 2024 at 12:58):

That would probably be clearly a stopper tactic! :smile:

David Ang (Oct 04 2024 at 13:00):

Mario Carneiro said:

you can already do this for norm_num

You mean norm_num1? Or can we do norm_num only?

David Ang (Oct 04 2024 at 13:03):

I do think having field_simp only would be very nice, but I don't know how well this works in practice because if I remember correctly the 40 lemmas in field_simps doesn't actually do very much without calling all of simp

Mario Carneiro (Oct 04 2024 at 14:21):

David Ang said:

Mario Carneiro said:

you can already do this for norm_num

You mean norm_num1? Or can we do norm_num only?

Both. norm_num1 doesn't use the simplifier at all, and norm_num only uses the simplifier with the usual meaning of simp only


Last updated: May 02 2025 at 03:31 UTC