leanprover / leanprover/lean4

instance synthesis size has exponential growth

Open
#7,587 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug P-medium
Dominant language
Lean
Stars
9.2k
Forks
990
Avg merge
1d 17h
Merged PRs (30d)
175

Description

Prerequisites

Please put an X between the brackets as you perform the following steps:

Description

When proving decidability of a chain of implications, the size of the term appears to grow exponentially. Consider the example:

set_option synthInstance.maxSize 45035996273704960 in

variable (a b c d e f g h i j k : Nat) in
#synth Decidable <|
      a ≠ b → a ≠ c → a ≠ d → a ≠ e → a ≠ f → a ≠ g → a ≠ h → a ≠ i → a ≠ j → a ≠ k → b ≠ c → b ≠ d → 
      b ≠ e → b ≠ f → b ≠ g → b ≠ h → b ≠ i → b ≠ j → b ≠ k → c ≠ d → c ≠ e → c ≠ f → c ≠ g → c ≠ h → 
      c ≠ i → c ≠ j → c ≠ k → d ≠ e → d ≠ f → d ≠ g → d ≠ h → d ≠ i → d ≠ j → d ≠ k → e ≠ f → e ≠ g → 
      e ≠ h → e ≠ i → e ≠ j → e ≠ k → f ≠ g → f ≠ h → f ≠ i → f ≠ j → f ≠ k → g ≠ h → g ≠ i → g ≠ j → 
      g ≠ k → h ≠ i → h ≠ j → h ≠ k → i ≠ j → i ≠ k → j ≠ k

Observe that there are 54 implications, and the maxSize counter is set to exactly 5 * 2 ^ 53. Decreasing it by even one makes the instance fail. Recall also that the default value of this counter is 2 ^ 7.

Context

I attempted to figure out what's going on here: the remainder of the "Context" section is about this.

First consider the same example but with each implication replaced by a conjunction:

set_option synthInstance.maxSize 164 in

variable (a b c d e f g h i j k : Nat) in
#synth Decidable <|
      a ≠ b ∧ a ≠ c ∧ a ≠ d ∧ a ≠ e ∧ a ≠ f ∧ a ≠ g ∧ a ≠ h ∧ a ≠ i ∧ a ≠ j ∧ a ≠ k ∧ b ≠ c ∧ b ≠ d ∧ 
      b ≠ e ∧ b ≠ f ∧ b ≠ g ∧ b ≠ h ∧ b ≠ i ∧ b ≠ j ∧ b ≠ k ∧ c ≠ d ∧ c ≠ e ∧ c ≠ f ∧ c ≠ g ∧ c ≠ h ∧ 
      c ≠ i ∧ c ≠ j ∧ c ≠ k ∧ d ≠ e ∧ d ≠ f ∧ d ≠ g ∧ d ≠ h ∧ d ≠ i ∧ d ≠ j ∧ d ≠ k ∧ e ≠ f ∧ e ≠ g ∧ 
      e ≠ h ∧ e ≠ i ∧ e ≠ j ∧ e ≠ k ∧ f ≠ g ∧ f ≠ h ∧ f ≠ i ∧ f ≠ j ∧ f ≠ k ∧ g ≠ h ∧ g ≠ i ∧ g ≠ j ∧ 
      g ≠ k ∧ h ≠ i ∧ h ≠ j ∧ h ≠ k ∧ i ≠ j ∧ i ≠ k ∧ j ≠ k

Here a value of 164 is the smallest possible (still above the default of 128, but significantly less).
(This example takes close to a minute to compile, but the point can be equally well made with noncomputable def here).

This leads me to believe one of the instances instDecidableForall or forall_prop_decidable is at fault: both can apply. Turning instDecidableForall off (back in the original) means the smallest value of maxSize is now 5 * 2 ^ 54 - 3 so forall_prop_decidable appears the culprit. Indeed, turning this one off leads to a maxSize of 164 again.

The options

set_option trace.Meta.synthInstance.resume true in
set_option trace.Meta.synthInstance.answer true in

show where the failure occurs in the original example with value 5 * 2 ^ 53 - 1 (ie one less than the smallest size): it's in the very last resume step, where this line informs that the instance size is too large. While I'm of course not sure, I find it incredibly hard to believe that this example has an instance size that massive (Wolfram Alpha informs me that if I had 5 * 2 ^ 53 bits, I'd have more content than the digitised version of the Library of Congress).

These two data points suggest to me that there's weird interaction between forall_prop_decidable and the resume function in synthesis.

Steps to Reproduce
  1. Code as above

Expected behavior: Decidable instance can be synthesized with a reasonable size limit

Actual behavior: Decidable instance requires exponentially growing size limit

Versions

live.lean-lang.org

Impact

Add 👍 to issues you consider important. If others are impacted by this issue, please ask them to add 👍 to it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Run the minimal reproducer with the synthesis tracing options, then read the referenced resume logic in src/Lean/Meta/SynthInstance.lean around lines 421 and 603. Compare the behavior with instDecidableForall and forall_prop_decidable disabled; done means the Decidable instance synthesizes without exponential maxSize growth.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.