/** * Performs one pass of generating more equivalences by rewriting sub-expressions in terms of known equivalences. */ private void generateMoreEquivalences() { Collection<Set<Expression>> equivalentClasses = equalities.getEquivalentClasses(); // Map every expression to the set of equivalent expressions ImmutableMap.Builder<Expression, Set<Expression>> mapBuilder = ImmutableMap.builder(); for (Set<Expression> expressions : equivalentClasses) { expressions.forEach(expression -> mapBuilder.put(expression, expressions)); } // For every non-derived expression, extract the sub-expressions and see if they can be rewritten as other expressions. If so, // use this new information to update the known equalities. Map<Expression, Set<Expression>> map = mapBuilder.build(); for (Expression expression : map.keySet()) { if (!derivedExpressions.contains(expression)) { for (Expression subExpression : filter(SubExpressionExtractor.extract(expression), not(equalTo(expression)))) { Set<Expression> equivalentSubExpressions = map.get(subExpression); if (equivalentSubExpressions != null) { for (Expression equivalentSubExpression : filter(equivalentSubExpressions, not(equalTo(subExpression)))) { Expression rewritten = ExpressionTreeRewriter.rewriteWith(new ExpressionNodeInliner(ImmutableMap.of(subExpression, equivalentSubExpression)), expression); equalities.findAndUnion(expression, rewritten); derivedExpressions.add(rewritten); } } } } } }
private Expression rewriteExpression(Expression expression, Predicate<Symbol> symbolScope, boolean allowFullReplacement) { Iterable<Expression> subExpressions = SubExpressionExtractor.extract(expression); if (!allowFullReplacement) { subExpressions = filter(subExpressions, not(equalTo(expression))); } ImmutableMap.Builder<Expression, Expression> expressionRemap = ImmutableMap.builder(); for (Expression subExpression : subExpressions) { Expression canonical = getScopedCanonical(subExpression, symbolScope); if (canonical != null) { expressionRemap.put(subExpression, canonical); } } // Perform a naive single-pass traversal to try to rewrite non-compliant portions of the tree. Prefers to replace // larger subtrees over smaller subtrees // TODO: this rewrite can probably be made more sophisticated Expression rewritten = ExpressionTreeRewriter.rewriteWith(new ExpressionNodeInliner(expressionRemap.build()), expression); if (!symbolToExpressionPredicate(symbolScope).apply(rewritten)) { // If the rewritten is still not compliant with the symbol scope, just give up return null; } return rewritten; }
private Expression normalize(Expression expression) { Expression identityNormalizedExpression = expressionCache.get(expression); if (identityNormalizedExpression == null) { // Make sure all sub-expressions are normalized first for (Expression subExpression : Iterables.filter(SubExpressionExtractor.extract(expression), Predicates.not(Predicates.equalTo(expression)))) { normalize(subExpression); } // Since we have not seen this expression before, rewrite it entirely in terms of the normalized sub-expressions identityNormalizedExpression = ExpressionTreeRewriter.rewriteWith(new ExpressionNodeInliner(expressionCache), expression); expressionCache.put(identityNormalizedExpression, identityNormalizedExpression); } return identityNormalizedExpression; } }
public static List<Expression> extractAll(Iterable<Expression> expressions) { ImmutableList.Builder<Expression> builder = ImmutableList.builder(); for (Expression expression : expressions) { extract(builder, expression); } return builder.build(); }
public static List<Expression> extractAll(Expression expression) { ImmutableList.Builder<Expression> builder = ImmutableList.builder(); extract(builder, expression); return builder.build(); }
public static Set<Expression> extract(Expression expression) { final ImmutableSet.Builder<Expression> builder = ImmutableSet.builder(); extract(builder, expression); return builder.build(); }
@Override public int compare(Expression expression1, Expression expression2) { // Current cost heuristic: // 1) Prefer fewer input symbols // 2) Prefer smaller expression trees // 3) Ordering.arbitrary() - creates a stable consistent ordering (extremely useful for unit testing) // TODO: be more precise in determining the cost of an expression return ComparisonChain.start() .compare(DependencyExtractor.extractAll(expression1).size(), DependencyExtractor.extractAll(expression2).size()) .compare(SubExpressionExtractor.extract(expression1).size(), SubExpressionExtractor.extract(expression2).size()) .compare(expression1, expression2, Ordering.arbitrary()) .result(); } });
/** * Performs one pass of generating more equivalences by rewriting sub-expressions in terms of known equivalences. */ private void generateMoreEquivalences() { Collection<Set<Expression>> equivalentClasses = equalities.getEquivalentClasses(); // Map every expression to the set of equivalent expressions Map<Expression, Set<Expression>> map = new HashMap<>(); for (Set<Expression> expressions : equivalentClasses) { expressions.stream().forEach(expression -> map.put(expression, expressions)); } // For every non-derived expression, extract the sub-expressions and see if they can be rewritten as other expressions. If so, // use this new information to update the known equalities. for (Expression expression : map.keySet()) { if (!derivedExpressions.contains(expression)) { for (Expression subExpression : filter(SubExpressionExtractor.extract(expression), not(equalTo(expression)))) { Set<Expression> equivalentSubExpressions = map.get(subExpression); if (equivalentSubExpressions != null) { for (Expression equivalentSubExpression : filter(equivalentSubExpressions, not(equalTo(subExpression)))) { Expression rewritten = ExpressionTreeRewriter.rewriteWith(new ExpressionNodeInliner(ImmutableMap.of(subExpression, equivalentSubExpression)), expression); equalities.findAndUnion(expression, rewritten); derivedExpressions.add(rewritten); } } } } } }
private Expression rewriteExpression(Expression expression, Predicate<Symbol> symbolScope, boolean allowFullReplacement) { Iterable<Expression> subExpressions = SubExpressionExtractor.extract(expression); if (!allowFullReplacement) { subExpressions = filter(subExpressions, not(equalTo(expression))); } ImmutableMap.Builder<Expression, Expression> expressionRemap = ImmutableMap.builder(); for (Expression subExpression : subExpressions) { Expression canonical = getScopedCanonical(subExpression, symbolScope); if (canonical != null) { expressionRemap.put(subExpression, canonical); } } // Perform a naive single-pass traversal to try to rewrite non-compliant portions of the tree. Prefers to replace // larger subtrees over smaller subtrees // TODO: this rewrite can probably be made more sophisticated Expression rewritten = ExpressionTreeRewriter.rewriteWith(new ExpressionNodeInliner(expressionRemap.build()), expression); if (!symbolToExpressionPredicate(symbolScope).apply(rewritten)) { // If the rewritten is still not compliant with the symbol scope, just give up return null; } return rewritten; }
private Expression normalize(Expression expression) { Expression identityNormalizedExpression = expressionCache.get(expression); if (identityNormalizedExpression == null) { // Make sure all sub-expressions are normalized first for (Expression subExpression : Iterables.filter(SubExpressionExtractor.extract(expression), Predicates.not(Predicates.equalTo(expression)))) { normalize(subExpression); } // Since we have not seen this expression before, rewrite it entirely in terms of the normalized sub-expressions identityNormalizedExpression = ExpressionTreeRewriter.rewriteWith(new ExpressionNodeInliner(expressionCache), expression); expressionCache.put(identityNormalizedExpression, identityNormalizedExpression); } return identityNormalizedExpression; } }