Zulip Chat Archive

Stream: general

Topic: continuity tactic and caching failure


Sebastien Gouezel (Nov 18 2020 at 15:29):

While playing with irreducible continuity, I tried to have a look at what makes the continuity tactic too slow to be really usable. A good example is trans in path_connected.lean, in which two lines are written by hand since continuity is too slow. Put back a continuity there. With set_option trace.class_instances true, one can see that Lean tries several times to find the same instance, with the message caching failure for ...

Sebastien Gouezel (Nov 18 2020 at 15:36):

@Gabriel Ebner , is that something to be expected?

Gabriel Ebner (Nov 18 2020 at 15:52):

This is definitely the opposite of expected.

Sebastien Gouezel (Nov 18 2020 at 15:58):

That's what I was expecting :-)

Gabriel Ebner (Nov 18 2020 at 16:01):

Weirdly enough, if you insert have : has_mul X, apply_instance instead of continuity, then it fails quickly (and the cache works as expected).

Sebastien Gouezel (Nov 18 2020 at 16:21):

More minimal example:

import topology.instances.real

set_option trace.class_instances true

def f (X : Type*) (n : ) : Prop := sorry

lemma foo1 (X : Type*) (n : ) [has_mul X] : f X n := sorry

lemma foo2 (X : Type*) (n : ) [has_mul X] : f X n := sorry

lemma bar (X : Type*) (n : ) : f X n :=
begin
  apply_rules [foo1, foo2],
end

The instance trace shows that the instance cache created when trying to apply foo1 is not used when trying to apply foo2.

Sebastien Gouezel (Nov 18 2020 at 16:23):

This means that continuity will try to infer instances zillions of times (each time it applies a new lemma, it tries again to solve the instances). It's not surprising it's slow then!

Gabriel Ebner (Nov 18 2020 at 16:37):

Then continuity is even worse, because the cache doesn't work at all there. (There are some cached failure for with apply_rules but not with continuity)

Sebastien Gouezel (Nov 18 2020 at 17:39):

Yes, it's true, continuity is even worse. If I get it correctly, continuity is essentially apply_rules called from tidy, so both are probably doing something wrong with respect to caching (or caching is doing something wrong with respects to tactics). I don't remember doing anything fancy when I wrote apply_rules, but my tactic writing skills are not really developed :)

Sebastien Gouezel (Nov 18 2020 at 19:59):

Even more basic example:

import topology.instances.real

set_option trace.class_instances true

def f (X : Type*) (n : ) : Prop := sorry

lemma foo1 (X : Type*) (n : ) [has_mul X] : f X n := sorry

lemma foo2 (X : Type*) (n : ) [field X] : f X n := sorry

lemma bar (X : Type*) (n : ) : f X n :=
begin
  apply foo1 <|> apply foo2,
end

apply foo1 fails, but the instance cache is not preserved for the apply foo2 search. This is normal since <|> does not preserve anything when the first branch fails, if I understand correctly. Instead, one would need a more clever combinator, doing first the instance search for foo1 (and adding it to the instance cache), and then applying foo1 if possible, or going to the foo2 branch if necessary. This is above my pay grade, though :-(

Sebastien Gouezel (Nov 19 2020 at 14:00):

In fact, I think I know how to fix the apply_rules issue, precisely by replacing the combinator <|> which ditches the state after the application of apply foo1 (and therefore the newly built instance cache) before calling apply foo2. With a new combinator that propagates the state, the instance cache seems to be kept. The code is

section
open interaction_monad.result

parameter {state : Type}
local notation `m` := interaction_monad state

meta def interaction_monad_orelse_propagating_state {α : Type u} (t₁ t₂ : m α) : m α :=
λ s, interaction_monad.result.cases_on (t₁ s)
  success
  (λ e₁ ref₁ s', interaction_monad.result.cases_on (t₂ s')
     success
     exception)
end

meta def apply_list_expr (opt : apply_cfg) : list (tactic expr)  tactic unit
| []     := fail "no matching rule"
| (h::t) :=
@interaction_monad_orelse_propagating_state tactic_state _
  (do e  h, interactive.concat_tags (apply e opt))
  (apply_list_expr t)

instead of the original

meta def apply_list_expr (opt : apply_cfg) : list (tactic expr)  tactic unit
| []     := fail "no matching rule"
| (h::t) := (do e  h, interactive.concat_tags (apply e opt)) <|> apply_list_expr t

Does it makes sense, or I am overlooking an obvious issue? (the tests for apply_rules pass as well with the new version)

Sebastien Gouezel (Nov 19 2020 at 14:48):

@Gabriel Ebner , in fact the problem in the continuity application doesn't seem to be related to the continuitytactic, it already shows up with a single apply. Maybe related to the way caching works along different instance search paths? In topology/path_connected.lean, if you replace the line 298

    { exact ((path.continuous γ).comp continuous_proj_I).comp (continuous_const.mul continuous_id')},

with

    { apply continuous.mul, },

of course this fails, but the instance search message is too long, truncated at 262144 characters, and there are a lot of caching failure for linear_ordered_field X in the instance trace.

Sebastien Gouezel (Nov 19 2020 at 14:51):

(I guess the linear_ordered_field instances are in fact not the same wrt how the multiplication would be defined, so they count as different)

Gabriel Ebner (Nov 19 2020 at 14:52):

This is interesting.

Gabriel Ebner (Nov 19 2020 at 14:53):

Sebastien Gouezel said:

(I guess the linear_ordered_field instances are in fact not the same wrt how the multiplication would be defined, so they count as different)

The caching mechanism assumes that all instances are definitionally equal. It won't try to find an instance twice.

Sebastien Gouezel (Nov 19 2020 at 14:57):

Then it's really weird

Sebastien Gouezel (Nov 20 2020 at 14:46):

Issue registered as lean#504. I could not minimize further than

import topology.algebra.infinite_sum

set_option trace.class_instances true

lemma blah (α : Type*) [topological_space α] (f :   α) : continuous f :=
begin
  apply continuous.mul,
end

(the trace of the failing apply is really scary)

Sebastien Gouezel (Nov 20 2020 at 17:21):

lean#505. @Gabriel Ebner is amazing, but we already knew that :smiley:


Last updated: Dec 20 2023 at 11:08 UTC