Documentation

Lean.Meta.Sym.Simp.SimpM

Structural Simplifier for Symbolic Simulation #

It is a specialized simplifier designed for symbolic simulation workloads. It addresses performance bottlenecks identified in the standard simp tactic when applied to large terms typical in symbolic execution.

Design Goals #

  1. Efficient caching via pointer-based keys on maximally shared terms
  2. Fast pattern matching using the Pattern infrastructure instead of isDefEq
  3. Minimal proof term overhead by using shareCommon and efficient congruence lemma application

Key Performance Problems Addressed #

1. Cache Inefficiency (Hash Collisions) #

The standard simplifier uses structural equality for cache keys. With large terms, hash collisions cause O(n) comparisons that fail, leading to O(n²) behavior.

Solution: Require maximally shared input terms (shareCommon) and use pointer-based cache keys. Structurally equal terms have equal pointers, making cache lookup O(1).

2. isDefEq in Rewrite Matching #

Profiling shows that isDefEq dominates simplification time in many workloads. For each candidate rewrite rule, definitional equality checking with metavariable unification is performed, and sometimes substantial time is spent checking whether the scopes are compatible.

Solution: Use the Pattern infrastructure for syntactic matching:

3. inferType in Proof Construction #

In the standard simplifier, building Eq.trans and congrArg proofs uses inferType on proof terms, which reconstructs large expressions and destroys sharing. It often causes O(n²) allocation.

Solution:

4. funext Proof Explosion #

Nested funext applications create O(n²) proof terms due to implicit arguments {f} {g} of size O(n) repeated n times.

Solution: Generate funext_k for k binders at once, stating the implicit arguments only once.

5. Binder Re-entry Cache Invalidation #

When a simplification theorem restructures a lambda, re-entering creates a fresh free variable, invalidating all cached results for subterms.

Solution: Reuse free variables across binder re-entry when safe:

6. Contextual ite Handling #

The standard ite_congr theorem adds hypotheses when entering branches, invalidating the cache and causing O(2^n) behavior on conditional trees.

Solution: Non-contextual ite handling for symbolic simulation:

Architecture #

Input Requirements #

Integration with SymM #

SimpM is designed to work within the SymM symbolic simulation framework:

Configuration options for the structural simplifier.

  • maxSteps : Nat

    Maximum number of steps that can be performed by the simplifier.

  • maxDischargeDepth : Nat

    Maximum depth of reentrant simplifier calls through dischargers. Prevents infinite loops when conditional rewrite rules trigger recursive discharge attempts.

Instances For

    The result of simplifying an expression e.

    The done flag controls whether simplification should continue after this result:

    • done = false (default): Continue with subsequent simplification steps
    • done = true: Stop processing, return this result as final

    Use cases for done = true #

    In pre simprocs #

    Skip simplification of certain subterms entirely:

    def skipLambdas : Simproc := fun e =>
      if e.isLambda then return .rfl (done := true)
      else return .rfl
    

    In post simprocs #

    Perform single-pass normalization without recursive simplification:

    def singlePassNormalize : Simproc := fun e =>
      if let some (e', h) ← tryNormalize e then
        return .step e' h (done := true)
      else return .rfl
    

    With done = true, the result e' won't be recursively simplified.

    Behavior #

    The done flag affects:

    1. andThen composition: If the first simproc returns done = true, the second simproc is skipped.
    2. Recursive simplification: After pre or post returns .step e' h, simp normally recurses on e'. With done = true, recursion is skipped.

    The flag is orthogonal to caching: both .rfl and .step results are cached regardless of the done flag, and cached results are always treated as final.

    Context-dependent results #

    The contextDependent flag tracks whether the result depends on the local context (e.g., hypotheses introduced when entering binders). Context-dependent results are stored in a transient cache that is cleared when entering binders, while context-independent results persist across binder entry and across simp invocations.

    Propagation rule: when combining sub-results (congruence, transitivity, etc.), contextDependent is the disjunction of all sub-results' flags. This includes .rfl results: if simp returned .rfl (contextDependent := true), it means simp might produce a different result in another local context (e.g., a conditional rewrite could succeed), so all downstream results must be marked context-dependent.

    • rfl (done contextDependent : Bool := false) : Result

      No change. If done = true, skip remaining simplification steps for this term.

    • step (e' proof : Expr) (done contextDependent : Bool := false) : Result

      Simplified to e' with proof proof : e = e'. If done = true, skip recursive simplification of e'.

    Instances For
      def Lean.Meta.Sym.Simp.mkRflResultCD (contextDependent : Bool) :

      Like mkRflResult with done := false.

      Equations
      Instances For
        @[reducible, inline]

        Returns true if this result depends on the local context (e.g., hypotheses).

        Equations
        Instances For

          Read-only context for the simplifier.

          • config : Config

            Simplifier configuration options.

          • initialLCtxSize : Nat

            Size of the local context when simplification started. Used to determine which free variables were introduced during simplification.

          • dischargeDepth : Nat
          Instances For
            @[reducible, inline]

            Cache mapping expressions (by pointer equality) to their simplified results.

            Equations
            Instances For

              Mutable state for the simplifier.

              • numSteps : Nat

                Number of steps performed so far.

              • persistentCache : Cache

                Cache for context-independent results. Survives across binder entry and across simp invocations within a sym => block.

              • transientCache : Cache

                Cache for context-dependent results. Cleared when entering binders (where new hypotheses may change the result).

              • Cache for generated funext theorems

              Instances For
                @[reducible, inline]

                Monad for the structural simplifier, layered on top of SymM.

                Equations
                Instances For
                  Instances For
                    @[implemented_by Lean.Meta.Sym.Simp.Methods.toMethodsRefImpl]
                    @[reducible, inline]
                    Equations
                    Instances For
                      @[implemented_by Lean.Meta.Sym.Simp.MethodsRef.toMethodsImpl]
                      Equations
                      Instances For
                        def Lean.Meta.Sym.Simp.SimpM.run {α : Type} (x : SimpM α) (methods : Methods := { }) (config : Config := { }) (s : State := { }) :

                        Runs a SimpM computation with the given methods, configuration, and state. The transientCache is always reset (context-dependent results don't survive across invocations). The persistentCache and funext cache are preserved from s.

                        Equations
                        • One or more equations did not get rendered due to their size.
                        Instances For
                          def Lean.Meta.Sym.Simp.SimpM.run' {α : Type} (x : SimpM α) (methods : Methods := { }) (config : Config := { }) :
                          SymM α

                          Runs a SimpM computation with the given methods and configuration.

                          Equations
                          • One or more equations did not get rendered due to their size.
                          Instances For
                            @[extern lean_sym_simp]
                            @[reducible, inline]
                            Equations
                            Instances For
                              @[reducible, inline]
                              Equations
                              Instances For
                                @[reducible, inline]

                                Saves and restores both caches and funext. Used by dischargers.

                                Equations
                                • One or more equations did not get rendered due to their size.
                                Instances For
                                  @[reducible, inline]

                                  Saves and restores the transient cache and funext, leaving the persistent cache untouched. Used when entering binders.

                                  Equations
                                  • One or more equations did not get rendered due to their size.
                                  Instances For
                                    @[reducible, inline]
                                    abbrev Lean.Meta.Sym.simp (e : Expr) (methods : Simp.Methods := { }) (config : Simp.Config := { }) :
                                    Equations
                                    Instances For