com.sri.ai.grinder.sgdpllt.library.CommutativeAssociative.java Source code

Java tutorial

Introduction

Here is the source code for com.sri.ai.grinder.sgdpllt.library.CommutativeAssociative.java

Source

/*
 * Copyright (c) 2013, SRI International
 * All rights reserved.
 * Licensed under the The BSD 3-Clause License;
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 * 
 * http://opensource.org/licenses/BSD-3-Clause
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the aic-expresso nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.sri.ai.grinder.sgdpllt.library;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.google.common.annotations.Beta;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.sri.ai.expresso.api.Expression;
import com.sri.ai.expresso.helper.Expressions;
import com.sri.ai.grinder.sgdpllt.api.Context;
import com.sri.ai.grinder.sgdpllt.simplifier.api.TopSimplifier;
import com.sri.ai.util.Util;
import com.sri.ai.util.base.Equals;

/**
 * Implements the basics for commutative, associative operators with a neutral
 * element such as addition, multiplication, conjunction, etc. It relies on
 * actual implementations providing the functor of the operation, its neutral
 * element, a predicate identifying arguments that can be operated on (for
 * example, addition operates on constants, but not variables), and a method
 * applying the operation on these operable arguments. It works by either
 * returning the result of the operation on all arguments, if all arguments are
 * operable (for addition, e.g., 1 + 2 + 3 returns 6), or returning an
 * application of the operation on the result on operable arguments together
 * with the non-operable arguments (for addition, 1 + X + 3 becomes 4 + X).
 * 
 * @author braz
 */
@Beta
public abstract class CommutativeAssociative implements TopSimplifier {

    public abstract Object getFunctor();

    protected abstract Expression getNeutralElement();

    protected abstract Expression getAbsorbingElement();

    protected abstract boolean isIdempotent();

    protected abstract Predicate<Expression> getIsOperableArgumentSyntaxTreePredicate();

    protected abstract Expression operationOnOperableArguments(LinkedList<Expression> operableArguments);

    public Expression getNeutralElementExpression() {
        return getNeutralElement();
    }

    public Predicate<Expression> getIsOperableArgumentExpressionPredicate() {
        Predicate<Expression> result = new Predicate<Expression>() {
            @Override
            public boolean apply(Expression expression) {
                boolean result = getIsOperableArgumentSyntaxTreePredicate().apply(expression);
                return result;
            }
        };
        return result;
    }

    @Override
    public Expression apply(Expression expression, Context context) {

        if (!expression.hasFunctor(getFunctor())) {
            return expression;
        }

        List<Expression> arguments = expression.getArguments();

        if (arguments.isEmpty()) {
            return getNeutralElementExpression();
        }

        if (arguments.size() == 1) {
            return expression.get(0);
        }

        LinkedList<Expression> operableArguments = new LinkedList<Expression>();
        LinkedList<Expression> nonOperableArguments = new LinkedList<Expression>();
        int indexOfFirstOperable = Util.collect(arguments, operableArguments,
                getIsOperableArgumentExpressionPredicate(), nonOperableArguments);

        if (operableArguments.size() == 0) {
            return expression; // everything is an non-operable argument, nothing that can be done.
        }

        if (operableArguments.contains(getAbsorbingElement())) {
            return getAbsorbingElement();
        }

        // get result on operable arguments
        Expression resultOnOperableArgumentsExpression = operationOnOperableArguments(operableArguments);

        // this next if then else is both an optimization for the case in which there is a single operable,
        // and a way to make sure
        // we return the same expression instance when it doesn't change (like x + 2), lest we generate
        // a distinct but equal instance that would keep being re-evaluated by the same manipulator.
        if (operableArguments.size() == 1) {
            if (operableArguments.getFirst().equals(getNeutralElementExpression())) {
                // we don't need to include the neutral element, and are left with non-operable arguments only, done.
                Expression result = makeExpressionWithSameFunctorAsThis(nonOperableArguments);
                return result;
            } else {
                // the expression is formed by non-operable arguments and a single non-neutral operable argument, nothing else to do.
                return expression;
            }
        }

        // now we deal with the case of more than one operable argument

        // if there are no non-operable arguments, that's it.
        if (nonOperableArguments.size() == 0) {
            return resultOnOperableArgumentsExpression;
        }

        // if there are non-operable arguments, put them together with operables's result, unless this is the neutral element.
        LinkedList<Expression> argumentsOfResultingExpression = nonOperableArguments; // changing semantics, and therefore, for clarity, the name. It's ok to modify it since it's been created locally.
        if (!resultOnOperableArgumentsExpression.equals(getNeutralElement())) {
            argumentsOfResultingExpression.add(indexOfFirstOperable, resultOnOperableArgumentsExpression);
        }

        // return operation on result on operable and non-operable arguments.
        Expression result = makeExpressionWithSameFunctorAsThis(argumentsOfResultingExpression);
        return result;
    }

    /**
     * An instance method version of {@link CommutativeAssociative#make(Object, List, Object, boolean)}
     * that uses the current object for obtaining the functor and neutral element.
     */
    public Expression makeWithSameFunctorAsThis(List<Expression> arguments) {
        return make(Expressions.wrap(getFunctor()), arguments, getNeutralElement(), isIdempotent());
    }

    /**
     * An instance method version of {@link CommutativeAssociative#make(Object, List, Object, boolean)}
     * that uses the current object for obtaining the functor and neutral element.
     */
    public Expression makeExpressionWithSameFunctorAsThis(List<Expression> arguments) {
        return make(Expressions.wrap(getFunctor()), arguments, getNeutralElement(), isIdempotent());
    }

    /**
     * Similar to {@link Expressions#apply(Object, Object...)},
     * but makes a simplified function application of an associative commutative operator,
     * that is, its application but for the cases in which there are no arguments, or a single argument.
     * When there are no arguments, a given neutral element value is returned.
     * When a single argument is provided, it is returned itself.
     * @param isIdempotent TODO
     */
    public static Expression make(Object functor, List<Expression> arguments, Expression neutralElement,
            boolean isIdempotent) {
        Predicate<Expression> notEqualToNeutralElement = Predicates.not(new Equals<Expression>(neutralElement));
        arguments = Util.collectToList(arguments, notEqualToNeutralElement);
        if (isIdempotent) {
            Set<Expression> argumentsSet = new LinkedHashSet<Expression>(arguments);
            arguments = new ArrayList<Expression>(argumentsSet);
        }
        if (arguments.isEmpty()) {
            return Expressions.wrap(neutralElement);
        }
        if (arguments.size() == 1) {
            return arguments.get(0);
        }
        Expression result = Expressions.makeExpressionOnSyntaxTreeWithLabelAndSubTrees(Expressions.wrap(functor),
                arguments);
        result = Associative.associateWhenSureOperatorIsAssociative(result);
        return result;
    }

    public static Expression make(Object functor, Collection<? extends Expression> arguments,
            Expression absorbingElement, Expression neutralElement, boolean isIdempotent) {
        return make(functor, arguments.iterator(), absorbingElement, neutralElement, isIdempotent);
    }

    /**
     * Makes a commutative associative application from arguments in an iterator's range.
     * This is potentially efficient in that it will stop as soon as the result is determined
     * when it finds an absorbing element.
     * If the arguments are being computed on the fly (for example, the iterator is a UnaryFunctionIterator),
     * this can save a lot of time.
     */
    public static Expression make(Object functor, Iterator<? extends Expression> argumentsIterator,
            Expression absorbingElement, Expression neutralElement, boolean isIdempotent) {
        absorbingElement = Expressions.wrap(absorbingElement);
        List<Expression> arguments = new LinkedList<Expression>();
        while (argumentsIterator.hasNext()) {
            Expression argument = argumentsIterator.next();
            if (argument.equals(absorbingElement)) {
                return absorbingElement;
            }
            if (!argument.equals(neutralElement)) {
                arguments.add(argument);
            }
        }
        Expression result = make(functor, arguments, neutralElement, isIdempotent);
        return result;
    }

    /**
     * If expression is an application of functor, returns its arguments.
     * If not, assumes that it is equivalent to <code>functor(expression)</code>
     * (a property of commutative associative functions)
     * and returns its arguments, that is, a singleton collection containing expression.
     */
    public static List<Expression> getArgumentsOfNormalizedApplicationOf(Object functor, Expression expression) {
        if (expression.hasFunctor(functor)) {
            return expression.getArguments();
        }
        return Util.list(expression);
    }
}