Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A plan for optimizing PLE #500

Closed
facundominguez opened this issue Oct 28, 2021 · 4 comments · Fixed by #569
Closed

A plan for optimizing PLE #500

facundominguez opened this issue Oct 28, 2021 · 4 comments · Fixed by #569

Comments

@facundominguez
Copy link
Collaborator

facundominguez commented Oct 28, 2021

I spent some time last week looking at dumps of formulas from PLE, and it is looking like we could go far by reducing the amount of equations that are produced and then their size.

There are some categories of equations in the output, discussed below. I have the impression that addressing these, and some redundant computations, the PLE version based on fastEval could be sped up to less than half the time it takes today. I have some prototype implementation to build this intuition, but it still fails on some tests.

I still would need to understand PLE with autorewrites to know how this story works there, but I wanted to share these thoughts in case they inspire others working on LF.

1. Equations that reduce datatype selectors

Equations like

(Language.Stitch.LH.Data.List.Cons##lqdc##$select##Language.Stitch.LH.Data.List.Cons##1
  (Language.Stitch.LH.Data.List.Cons
    (Language.Stitch.LH.Data.List.Cons##lqdc##$select##Language.Stitch.LH.Data.List.Cons##1 xs##a3Vf)
    (Language.Stitch.LH.Data.List.append
        (Language.Stitch.LH.Data.List.Cons##lqdc##$select##Language.Stitch.LH.Data.List.Cons##2 xs##a3Vf)
        ys##a3Vg))) =
         (Language.Stitch.LH.Data.List.Cons##lqdc##$select##Language.Stitch.LH.Data.List.Cons##1 xs##a3Vf))

or

is$Language.Stitch.LH.Data.List.Nil Language.Stitch.LH.Data.List.Nil = true;

(is$Language.Stitch.LH.Data.List.Nil (Language.Stitch.LH.Data.List.Cons
    (Language.Stitch.LH.Data.List.Cons##lqdc##$select##Language.Stitch.LH.Data.List.Cons##1 xs##a3Vf)
    (Language.Stitch.LH.Data.List.append
        (Language.Stitch.LH.Data.List.Cons##lqdc##$select##Language.Stitch.LH.Data.List.Cons##2 xs##a3Vf)
        ys##a3Vg)) = false;

The SMT solver knows about datatypes, so one could hope that spelling this equalities is redundant.
#499 gets rid of the equalities involving selectors. I haven't explored how to eliminate the constructor tests.

2. Reduction of if-then-else

The following equality states that the top level if-then-else can be reduced since its condition is known to be False.

      ((if (is$Language.Stitch.LH.Check.Lam e0##a37V) then
          (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (lqdc##$select##Language.Stitch.LH.Check.Lam##2 e0##a37V) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##Langu
age.Stitch.LH.Check.Lam##1 e0##a37V))
        else
          (if (is$Language.Stitch.LH.Check.App e0##a37V) then
             && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (Language.Stitch.LH.Check.e1 e0##a37V));
                 (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.App##1 e0##a37V))]
           else
             (if (is$Language.Stitch.LH.Check.Let e0##a37V) then
                && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Let##2 e0##a37V));
                    (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType (lqdc##$select##Language.Stitch.LH.Check.Let##2 e0##a37V)) Language.
Stitch.LH.Data.List.Nil) (lqdc##$select##Language.Stitch.LH.Check.Let##1 e0##a37V))]
              else
                (if (is$Language.Stitch.LH.Check.Arith e0##a37V) then
                   && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Arith##3 e0##a37V));
                       (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Arith##1 e0##a37V))]
                 else
                   (if (is$Language.Stitch.LH.Check.Cond e0##a37V) then
                      && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Cond##3 e0##a37V));
                          && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (Language.Stitch.LH.Check.a e0##a37V));
                              (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Cond##1 e0##a37V))]]
                    else
                      (if (is$Language.Stitch.LH.Check.Fix e0##a37V) then
                         (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Fix##1 e0##a37V))
                       else
                         (if (is$Language.Stitch.LH.Check.IntE e0##a37V) then
                            true
                          else
                            true))))))) =
         (if (is$Language.Stitch.LH.Check.App e0##a37V) then
            && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (Language.Stitch.LH.Check.e1 e0##a37V));
                (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.App##1 e0##a37V))]
          else
            (if (is$Language.Stitch.LH.Check.Let e0##a37V) then
               && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Let##2 e0##a37V));
                   (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType (lqdc##$select##Language.Stitch.LH.Check.Let##2 e0##a37V)) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##Language.Stitch.LH.Check.Let##1 e0##a37V))]
             else
               (if (is$Language.Stitch.LH.Check.Arith e0##a37V) then
                  && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Arith##3 e0##a37V));
                      (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Arith##1 e0##a37V))]
                else
                  (if (is$Language.Stitch.LH.Check.Cond e0##a37V) then
                     && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Cond##3 e0##a37V));
                         && [(Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (Language.Stitch.LH.Check.a e0##a37V));
                             (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Cond##1 e0##a37V))]]
                   else
                     (if (is$Language.Stitch.LH.Check.Fix e0##a37V) then
                        (Language.Stitch.LH.Check.checkBindings Language.Stitch.LH.Data.List.Nil (lqdc##$select##Language.Stitch.LH.Check.Fix##1 e0##a37V))
                      else
                        (if (is$Language.Stitch.LH.Check.IntE e0##a37V) then
                           true
                         else
                           true))))));

But if the SMT solver knows that the condition of the if-then-else is False, it already knows that this equation holds! There is no point either in asserting it explicitly.

3. Unfoldings of recursive functions

The following equation unfolds a call to a reflected function called checkBindings. Every invocation of the function duplicates pretty much the whole body.

(Language.Stitch.LH.Check.checkBindings
    (Language.Stitch.LH.Data.List.Cons
        (Language.Stitch.LH.Check.exprType e2##a37X)
        Language.Stitch.LH.Data.List.Nil) VV##F##147
    ) =
         (if (is$Language.Stitch.LH.Check.Var VV##F##147) then
            ((Language.Stitch.LH.Data.List.elemAt (lqdc##$select##Language.Stitch.LH.Check.Var##1 VV##F##147) (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language
.Stitch.LH.Data.List.Nil)) =
               (lqdc##$select##Language.Stitch.LH.Check.Var##2 VV##F##147))
          else
            (if (is$Language.Stitch.LH.Check.Lam VV##F##147) then
               (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (lqdc##$select##Language.Stitch.LH.Check.Lam##2 VV##F##147) (Language.Stitch.LH.Data.List.Cons (Language.Sti
tch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil)) (lqdc##$select##Language.Stitch.LH.Check.Lam##1 VV##F##147))
             else
               (if (is$Language.Stitch.LH.Check.App VV##F##147) then
                  && [(Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (Language.Stitch.LH.Ch
eck.e1 VV##F##147));
                      (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##Langua
ge.Stitch.LH.Check.App##1 VV##F##147))]
                else
                  (if (is$Language.Stitch.LH.Check.Let VV##F##147) then
                     && [(Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##Lan
guage.Stitch.LH.Check.Let##2 VV##F##147));
                         (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType (lqdc##$select##Language.Stitch.LH.Check.Let##2 VV##F##147)) (L
anguage.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil)) (lqdc##$select##Language.Stitch.LH.Check.Let##1 VV##F##147))]
                   else
                     (if (is$Language.Stitch.LH.Check.Arith VV##F##147) then
                        && [(Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##
Language.Stitch.LH.Check.Arith##3 VV##F##147));
                            (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##
Language.Stitch.LH.Check.Arith##1 VV##F##147))]
                      else
                        (if (is$Language.Stitch.LH.Check.Cond VV##F##147) then
                           && [(Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$selec
t##Language.Stitch.LH.Check.Cond##3 VV##F##147));
                               && [(Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (Language
.Stitch.LH.Check.a VV##F##147));
                                   (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$s
elect##Language.Stitch.LH.Check.Cond##1 VV##F##147))]]
                         else
                           (if (is$Language.Stitch.LH.Check.Fix VV##F##147) then
                              (Language.Stitch.LH.Check.checkBindings (Language.Stitch.LH.Data.List.Cons (Language.Stitch.LH.Check.exprType e2##a37X) Language.Stitch.LH.Data.List.Nil) (lqdc##$select##Language.Stitch.LH.Check.Fix##1 VV##F##147))
                            else
                              (if (is$Language.Stitch.LH.Check.IntE VV##F##147) then
                                 true
                               else
                                 true))))))))

I'm thinking that it should be possible to serialize the body of the function without recursion once for all of PLE, and rewrite the above equality as

(Language.Stitch.LH.Check.checkBindings
    (Language.Stitch.LH.Data.List.Cons
        (Language.Stitch.LH.Check.exprType e2##a37X)
        Language.Stitch.LH.Data.List.Nil) VV##F##147
    ) =
(checkBindingsNonRecursive
    (Language.Stitch.LH.Data.List.Cons
        (Language.Stitch.LH.Check.exprType e2##a37X)
        Language.Stitch.LH.Data.List.Nil) VV##F##147
    )
    Language.Stitch.LH.Check.checkBindings
)

which leaves to the SMT solver the trouble of unfolding checkBindingsNonRecursive which should have been previously defined as

define-fun checkBindingsNonRecursive (xs List) (f Int) Bool (... apply f xs' ... apply f xs'' ...)

4. Nested unfoldings

Equalities of the form

... (g x) ... = ... body_of_g ...

are unnecessarily verbose since they duplicate the context in which g is invoked. Unless x is bound by a lambda in the context (which appears to be a rare situation), the same information can be conveyed with g x = body_of_g

@ranjitjhala
Copy link
Member

Hi @facundominguez -- these are excellent observations, thanks! I suspect some of them will get addressed with the interpreter-ple mode that @michaelborkowski has been working on, so I think we should get that merged in ASAP and see what remains then?

@robinbb
Copy link

robinbb commented Nov 13, 2021

@ranjitjhala Is there a PR for that, or has it been merged already?

@facundominguez
Copy link
Collaborator Author

facundominguez commented Nov 13, 2021

There is pr #502. In principle, it is rather complementary to these optimizations but it affects the same code.

There is another pr #508 that could have more of an overlap, but I haven't dived into it yet.

@facundominguez
Copy link
Collaborator Author

facundominguez commented Apr 1, 2022

Hello! I've been quietly working on this during the last weeks. I accumulated some achievements but I have been finding tests again and again which challenged my understanding of the interactions of PLE with LH.

At the moment, I have only one test failing: my own Rest.hs, and by extension stitch-lh. All other tests in LH are passing.

Here's a list of the more interesting things that I have implemented

  • Remove the surrounding context of unfoldings. This is point (4) in the description of this ticket. Unexpected: Had to figure out how to deal with lambda expressions so I preserve the bindings.
  • Unfold equations only if any of the guards can really be reduced. This reduces equations drastically, since no nested if-then-else appear, only the results of reducing them.
  • Separated selectors and constructor tests from the other measures, as the smt solver already knows about them. They are used for rewriting candidate expressions, but otherwise produce no unfolding equations themselves.
  • Have PLE and REST work with elaborated terms, where explicit casts appear to instantiate type variables. This one was a challenge to pull off but I think it is reasonably contained.
  • Unfold measures as if they were reflected functions when the measure appears applied to an expression which isn't a constructor application. Unexpected: Had to deal with polymorphic measure (e.g. prop) which can offer multiple equations with different incompatible sorts.

I did other little experiments that I leave to discuss when I start the prs. Here's the work-in-progress. As usual, I'm keeping the commits small, and will try to get them merged in smaller chunks.

The few benchmarks I ran don't show huge speed ups, but I think the code in PLE is simpler and the quality of the produced dumps is improved. The produced equalities are fewer and smaller to the point that I believe they approach what a human would have written for the task. Anyway, I'm going to revisit the benchmarks when I get all of the tests passing.

I'm going to be taking some vacation time soon, so likely merging and discussing all this is going to extend well into May.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants