All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sri.ai.grinder.helper.IsolateUtil Maven / Gradle / Ivy

Go to download

SRI International's AIC Symbolic Manipulation and Evaluation Library (for Java 1.8+)

The newest version!
/*
 * Copyright (c) 2016, 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-praise 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.helper;

import java.util.ArrayList;
import java.util.List;

import com.google.common.annotations.Beta;
import com.sri.ai.expresso.api.Expression;
import com.sri.ai.expresso.helper.Expressions;
import com.sri.ai.grinder.polynomial.api.Monomial;
import com.sri.ai.grinder.polynomial.api.Polynomial;
import com.sri.ai.grinder.polynomial.core.DefaultMonomial;
import com.sri.ai.grinder.polynomial.core.DefaultPolynomial;
import com.sri.ai.grinder.sgdpllt.library.FunctorConstants;
import com.sri.ai.grinder.sgdpllt.library.controlflow.IfThenElse;
import com.sri.ai.grinder.sgdpllt.library.number.Division;
import com.sri.ai.grinder.sgdpllt.library.number.Exponentiation;
import com.sri.ai.grinder.sgdpllt.library.number.Plus;
import com.sri.ai.grinder.sgdpllt.library.number.Times;
import com.sri.ai.util.base.Pair;
import com.sri.ai.util.math.Rational;


/**
 * Utility methods for isolating a generalized linear variable.
 * 
 * A basic procedure to isolate a variable. 
 * 
 * 
 * Formally, given the application of a relational operator op1 (=, !=, <, >, <=, >=)
 * 
 * t1 + ... + t_m op1 r_1 + ... + r_n
 * 
 * and given a `linear generalized variable` V, derive a new equivalent application:
 * 
 * V op2 Term/Alpha
 * 
 * Where V does not appear on Term and Alpha, which must be normalized polynomials. 
 * Alpha may be 0 if V does not appear originally or disappears. V may disappear 
 * from the expression, for example, x = x becomes 0 = 0, also known as `true`. 
 * This still fits `Alpha*V op2 Term`, with Alpha being 0, but now `Alpha` cannot 
 * move to the right-hand side as a denominator. In these cases, we simply return 
 * `0 op2 Term`. 
 * 
 * op2 is the same as op1, if op1 is an equality (i.e. `=` or `!=`), otherwise it can be 
 * a `flipped` op1 if you multiply or divide the inequality by a negative 
 * (or swap the left and right hand sides).
 *
 * In the cases where it cannot be determined if `Alpha` is 0 or not, for example:
 * 
 * y * x = 10 
 * 
 * the resulting expression needs to test for `Alpha` being 0. With respect to the 
 * previous example we would get:
 * 
 * if y != 0 then x = 10/y else 0 = 10
 * 
 * or more generally:
 * 
 * if Alpha != 0 then V = Term/Alpha else 0 = Term
 * 
 * Similarly, if the sign of `Alpha` cannot be determined and `op1` is an inequality 
 * (i.e. `<`, `<=`, `>=`, or `>`) then the general result will be of the form:
 * 
 * if Alpha != 0 
 * then 
 *     if Alpha > 0
 *         V op1 Term/Alpha
 *     else
 *         V  Term/Alpha 
 * else 
 *     0 op1 Term
 * 
 * 
* * @author oreilly */ @Beta public class IsolateUtil { public static Expression isolate(Expression inequality, Expression linearVariableToIsolate) { if (inequality.getFunctor() == null || inequality.numberOfArguments() != 2) { throw new IllegalArgumentException("Not an inequality: "+inequality); } Expression leftExpression = inequality.get(0); Expression operator = inequality.getFunctor(); Expression rightExpression = inequality.get(1); Expression result = isolate(leftExpression, operator, rightExpression, linearVariableToIsolate); return result; } public static Expression isolate(Expression leftExpression, Expression operator, Expression rightExpression, Expression linearVariableToIsolate) { Polynomial leftPolynomial = DefaultPolynomial.make(leftExpression); Polynomial rightPolynomial = DefaultPolynomial.make(rightExpression); Expression result = isolate(leftPolynomial, operator, rightPolynomial, linearVariableToIsolate); return result; } public static Expression isolate(Polynomial leftPolynomial, Expression operator, Polynomial rightPolynomial, Expression linearVariableToIsolate) { assertSupportedOperator(operator); Pair, List> splitIntoSimilarAndDissimilarTerms; splitIntoSimilarAndDissimilarTerms = splitIntoSimilarAndDissimilarTerms(leftPolynomial, linearVariableToIsolate); List leftSimilarTerms = splitIntoSimilarAndDissimilarTerms.first; List leftDissimilarTerms = splitIntoSimilarAndDissimilarTerms.second; splitIntoSimilarAndDissimilarTerms = splitIntoSimilarAndDissimilarTerms(rightPolynomial, linearVariableToIsolate); List rightSimilarTerms = splitIntoSimilarAndDissimilarTerms.first; List rightDissimilarTerms = splitIntoSimilarAndDissimilarTerms.second; // // First move the left dissimilar terms over to the right side for (Monomial leftDissimilarTerm : leftDissimilarTerms) { rightDissimilarTerms.add(DefaultMonomial.make(Times.make(Expressions.MINUS_ONE, leftDissimilarTerm))); } // // Second move the right similar terms over to the left side for (Monomial rightSimilarTerm : rightSimilarTerms) { leftSimilarTerms.add(DefaultMonomial.make(Times.make(Expressions.MINUS_ONE, rightSimilarTerm))); } Polynomial leftSimilarPolynomial = DefaultPolynomial.make(Plus.make(new ArrayList<>(leftSimilarTerms))); Polynomial rightDissimilarPolynomial = DefaultPolynomial.make(Plus.make(new ArrayList(rightDissimilarTerms))); Monomial isolated = null; // Compute Alpha List alphaTerms = new ArrayList<>(); for (Monomial similarTerm : leftSimilarPolynomial.getMonomials()) { List dissimilarFactors = new ArrayList<>(); dissimilarFactors.add(Expressions.makeSymbol(similarTerm.getNumericFactor())); for (Expression factor : similarTerm.getOrderedNonNumericFactors()) { if (factor.equals(linearVariableToIsolate)) { if (isolated == null) { isolated = DefaultMonomial.make(Exponentiation.make(factor, Expressions.makeSymbol(similarTerm.getPowerOfFactor(factor)))); } if (!Rational.ONE.equals(similarTerm.getPowerOfFactor(factor))) { throw new IllegalArgumentException("Only linear (i.e. x^1) can be isolated by this API but found : "+factor); } } else { dissimilarFactors.add(Exponentiation.make(factor, Expressions.makeSymbol(similarTerm.getPowerOfFactor(factor)))); } } alphaTerms.add(DefaultMonomial.make(Times.make(dissimilarFactors))); } Polynomial alpha = DefaultPolynomial.make(Plus.make(alphaTerms), rightDissimilarPolynomial.getVariables()); Expression rightDissimilarTerm; if (alpha.equals(Expressions.ZERO)) { isolated = DefaultMonomial.ZERO; rightDissimilarTerm = rightDissimilarPolynomial; } else { Pair quotientAndRemainder = rightDissimilarPolynomial.divide(alpha); if (quotientAndRemainder.second.equals(Expressions.ZERO)) { rightDissimilarTerm = quotientAndRemainder.first; } else { rightDissimilarTerm = Division.simplify(Division.make(rightDissimilarPolynomial, alpha)); } } Expression result; // If alpha is numeric we can divide (or know it was 0 - handled accordingly already). if (Expressions.isNumber(alpha)) { result = Expressions.apply(flipIfRequired(operator, alpha), isolated, rightDissimilarTerm); } else { Expression thenBranch; if (isEquality(operator)) { thenBranch = Expressions.apply(operator, isolated, rightDissimilarTerm); } else { thenBranch = IfThenElse.make(Expressions.apply(FunctorConstants.GREATER_THAN, alpha, Expressions.ZERO), Expressions.apply(operator, isolated, rightDissimilarTerm), Expressions.apply(flipInequalityOperator(operator), isolated, rightDissimilarTerm)); } result = IfThenElse.make(Expressions.apply(FunctorConstants.DISEQUALITY, alpha, Expressions.ZERO), thenBranch, Expressions.apply(FunctorConstants.EQUALITY, Expressions.ZERO, rightDissimilarPolynomial)); } return result; } /** * Separates the monomials of a given polynomial into those containing a given generalized variable (similar terms), * and those not containing the given generalized variable (dissimilar terms). * @param polynomial * @param generalizedVariable * @return */ public static Pair, List> splitIntoSimilarAndDissimilarTerms(Polynomial polynomial, Expression generalizedVariable) { Pair, List> result = new Pair<>(new ArrayList<>(), new ArrayList<>()); for (Monomial term : polynomial.getMonomials()) { if (term.getFactors().contains(generalizedVariable)) { result.first.add(term); } else { result.second.add(term); } } return result; } public static void assertSupportedOperator(Expression operator) { if (!(operator.equals(FunctorConstants.EQUALITY) || operator.equals(FunctorConstants.DISEQUALITY) || operator.equals(FunctorConstants.LESS_THAN) || operator.equals(FunctorConstants.GREATER_THAN) || operator.equals(FunctorConstants.LESS_THAN_OR_EQUAL_TO) || operator.equals(FunctorConstants.GREATER_THAN_OR_EQUAL_TO) )) { throw new IllegalArgumentException("Operator "+operator+" is not supported"); } } public static Expression flipIfRequired(Expression operator, Polynomial alpha) { // Determine if the operator needs to be flipped Expression result = operator; if (!isEquality(operator)) { Rational alphaCoefficientProduct = Rational.ONE; for (Monomial alphaTerm : alpha.getMonomials()) { alphaCoefficientProduct = alphaCoefficientProduct.multiply(alphaTerm.getNumericFactor()); } if (alphaCoefficientProduct.isNegative()) { result = flipInequalityOperator(operator); } } return result; } public static boolean isEquality(Expression operator) { boolean result = operator.equals(FunctorConstants.EQUALITY) || operator.equals(FunctorConstants.DISEQUALITY); return result; } public static Expression flipInequalityOperator(Expression operator) { Expression result; if (operator.equals(FunctorConstants.LESS_THAN)) { result = Expressions.makeSymbol(FunctorConstants.GREATER_THAN); } else if (operator.equals(FunctorConstants.LESS_THAN_OR_EQUAL_TO)) { result = Expressions.makeSymbol(FunctorConstants.GREATER_THAN_OR_EQUAL_TO); } else if (operator.equals(FunctorConstants.GREATER_THAN)) { result = Expressions.makeSymbol(FunctorConstants.LESS_THAN); } else if (operator.equals(FunctorConstants.GREATER_THAN_OR_EQUAL_TO)) { result = Expressions.makeSymbol(FunctorConstants.LESS_THAN_OR_EQUAL_TO); } else { throw new IllegalArgumentException("operator "+operator+" is not an inequality"); } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy