vat: optimization

2005-09-23: Implemented function-tree optimization. In the literature, this is called "editing." It's used, among other reasons, in the hope that compacting useful sections of the tree will make them more robust against disruption by crossover.

In this particular domain, I'm hoping that function optimization will be a significant win in evaluation speed. Each tree only needs to be optimized once, but needs to be evaluated for (tens of) thousands of pixels.

The optimizations currently implemented:

 f(const)=const
 abs(abs(x)) = abs(x)
 x-x=0
 x/x=1
 x mod x=0
 mix(x,x,y) = x
 iflt(const,const,a,b) eliminates one branch
 iflt(x,x,a,b) = b
 iflt(a,b,x,x) = x

Some of these were quite trivial. A lot more can be done with optimization, especially of deeper constructs such as (ADD 1 (ADD 2 X)) = (ADD 3 X)

As part of the optimization work, I had to implement tree comparisons to determine if two sub-trees were equal. It currently works for exact trees. I intend to extend it to also check for equivalency, i.e. the current comparator doesn't exploit the commutative property: (ADD X Y) = (ADD Y X)

I also wrote a fast statistics gatherer which will later be made optional at compile-time. This is currently being used to track how effective different optimizations are.

Here is a sample run of optimizing a randomly generated tree:

setting seed to 15

(MIX (MIX (MIX (SIN (Y)) (MIX (Y) (Y) (Y)) (SQRT (Y))) (SUB (IFLT (CONST
-0.287493) (Y) (Y) (Y)) (MOD (CONST -0.356111) (CONST -0.276621))) (SIN
(SUB (CONST 0.986738) (X)))) (IFLT (SUB (SIN (Y)) (ADD (Y) (X))) (IFLT
(DIV (Y) (X)) (MOD (X) (Y)) (SUB (X) (CONST -0.699458)) (IFLT (CONST
-0.907861) (CONST -0.671048) (Y) (Y))) (ADD (SQRT (CONST -0.641829))
(SIN (Y))) (SUB (SIN (Y)) (MOD (X) (Y)))) (ADD (MIX (SQRT (X)) (IFLT (X)
(Y) (CONST -0.170858) (CONST -0.306319)) (COS (Y))) (SUB (SUB (X) (Y))
(DIV (X) (X)))))

optimized:

(MIX (MIX (MIX (SIN (Y)) (Y) (SQRT (Y))) (SUB (Y) (CONST -0.079489))
(SIN (SUB (CONST 0.986738) (X)))) (IFLT (SUB (SIN (Y)) (ADD (Y) (X)))
(IFLT (DIV (Y) (X)) (MOD (X) (Y)) (SUB (X) (CONST -0.699458)) (Y)) (ADD
(CONST -0.801142) (SIN (Y))) (SUB (SIN (Y)) (MOD (X) (Y)))) (ADD (MIX
(SQRT (X)) (IFLT (X) (Y) (CONST -0.170858) (CONST -0.306319)) (COS (Y)))
(SUB (SUB (X) (Y)) (CONST 1.000000))))

         9 node evaluations
         1 optimize: iflt(const,const,a,b) eliminates one branch
         1 protected square roots
        31 optimize failures due to zero args
         1 protected square roots of a negative
        28 optimize failures due to non-const args
         1 optimize: x/x=1
         1 optimize: iflt(a,b,x,x) = x
        65 optimize attempts
         1 optimize: mix(x,x,y) = x
         2 optimize: f(const)=const

hashtable: 141 updates, 11 items, 16 slots, 3 rehashes

Before and after optimization:

before optimization
after optimization

GraphViz is cool.