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

com.sri.ai.grinder.sgdpllt.tester.TheoryTestingSupport Maven / Gradle / Ivy

/*
 * 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-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.tester;

import static com.sri.ai.expresso.helper.Expressions.parse;
import static com.sri.ai.util.Util.mapIntoArrayList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.google.common.annotations.Beta;
import com.sri.ai.expresso.api.Expression;
import com.sri.ai.expresso.api.Type;
import com.sri.ai.expresso.type.FunctionType;
import com.sri.ai.grinder.helper.GrinderUtil;
import com.sri.ai.grinder.sgdpllt.api.Context;
import com.sri.ai.grinder.sgdpllt.api.Theory;
import com.sri.ai.grinder.sgdpllt.core.TrueContext;
import com.sri.ai.grinder.sgdpllt.theory.compound.CompoundTheory;
import com.sri.ai.grinder.sgdpllt.theory.compound.CompoundTheoryTestingSupport;
import com.sri.ai.grinder.sgdpllt.theory.differencearithmetic.DifferenceArithmeticTheory;
import com.sri.ai.grinder.sgdpllt.theory.differencearithmetic.DifferenceArithmeticTheoryTestingSupport;
import com.sri.ai.grinder.sgdpllt.theory.equality.EqualityTheory;
import com.sri.ai.grinder.sgdpllt.theory.equality.EqualityTheoryTestingSupport;
import com.sri.ai.grinder.sgdpllt.theory.function.BruteForceFunctionTheory;
import com.sri.ai.grinder.sgdpllt.theory.function.BruteForceFunctionTheoryTestingSupport;
import com.sri.ai.grinder.sgdpllt.theory.linearrealarithmetic.LinearRealArithmeticTheory;
import com.sri.ai.grinder.sgdpllt.theory.linearrealarithmetic.LinearRealArithmeticTheoryTestingSupport;
import com.sri.ai.grinder.sgdpllt.theory.propositional.PropositionalTheory;
import com.sri.ai.grinder.sgdpllt.theory.propositional.PropositionalTheoryTestingSupport;
import com.sri.ai.grinder.sgdpllt.theory.tuple.TupleTheory;
import com.sri.ai.grinder.sgdpllt.theory.tuple.TupleTheoryTestingSupport;
import com.sri.ai.util.Util;

/**
 * Interface for wrappers around {@link Theory} objects that know how to generate problems for testing.
 * @author braz
 *
 */
@Beta
public interface TheoryTestingSupport {	
	/**
	 * 
	 * @return the theory testing support is being provided for.
	 */
	Theory getTheory();
	
	/**
	 * 
	 * @return get the Random being used for testing support.
	 */
	Random getRandom();

	/**
	 * Set the random to be used for testing support.
	 * 
	 * @param random   the random to use.
	 */
	void setRandom(Random random);

	/** Sets variables to be used in randomly generated literals. */
	void setVariableNamesAndTypesForTesting(Map variableNamesForTesting);

	/** Gets variables to be used in randomly generated literals. */
	Map getVariableNamesAndTypesForTesting();

	/**
	 * Returns the variable names as returned by
	 * {@link #getVariableNamesAndTypesForTesting()} as an array list (this is
	 * cached and updated as needed).
	 * 
	 * @return
	 */
	List getVariableNamesForTesting();

	default ArrayList getVariablesForTesting() {
		List variableNames = getVariableNamesForTesting();
		ArrayList result = mapIntoArrayList(variableNames, s -> parse(s));
		return result;
	}

	/**
	 * Returns a set of types appropriate for testing this theory; note that
	 * this set may include types not related to any of the testing variables.
	 */
	Collection getTypesForTesting();
	
	/**
	 * Picks one of the testing variables returned by {@link #getTestingVariables()}
	 * with uniform probability.
	 * 
	 * @return
	 */
	default String pickTestingVariableAtRandom() {
		String result = pickTestingVariableAtRandom((variable) -> true);
		return result;
	}
		
	default String pickTestingVariableAtRandom(Predicate variableNameFilter) {
		List variableNames = getVariableNamesAndTypesForTesting().keySet().stream().filter(variableNameFilter).collect(Collectors.toList());
		if (variableNames.isEmpty()) {
			throw new Error("There are no testing variables to select from. Theory testing support is :"+this);
		}
		String result = Util.pickUniformly(variableNames, getRandom());
				
		return result;
	}
	
	default String pickTestingVariableAtRandom(Type expectedType, Predicate variableNameFilter) {
		String result = pickTestingVariableAtRandom(getVariableNamesAndTypesForTesting(), expectedType, variableNameFilter);
		return result;
	}
	
	default String pickTestingVariableAtRandom(Map nameToTypes, Type expectedType, Predicate variableNameFilter) {
		List compatibleVariableNames = getVariableNamesWhoseTypesAreSubtypesOf(nameToTypes, expectedType);
		
		List variableNames = compatibleVariableNames.stream().filter(variableNameFilter).collect(Collectors.toList());
		if (variableNames.isEmpty()) {
			throw new Error("There are no testing variables of Type="+expectedType+" to select from. Theory testing support is :"+this);
		}
		String result = Util.pickUniformly(variableNames, getRandom());
				
		return result;
	}
	
	/**
	 * Get variable names whose types are subtypes of a given type.
	 * 
	 * @param type
	 *            the type to be checked.
	 * @return a list of variable names whose types are subtypes of the given
	 *         type.
	 */
	default List getVariableNamesWhoseTypesAreSubtypesOf(Type type) {
		List result = getVariableNamesWhoseTypesAreSubtypesOf(getVariableNamesAndTypesForTesting(), type);
		return result;
	}
		
	default List getVariableNamesWhoseTypesAreSubtypesOf(Map nameToTypes, Type type) {		
		List result =
				nameToTypes.entrySet().stream()
					.filter(entry -> {
						boolean include;
						// When the type is a non-function type but the entry's type is a FunctionType
						// we want to use the entry type's codomain as we can use a function application
						// of this variable in this instance.
						if (!(type instanceof FunctionType) && entry.getValue() instanceof FunctionType) {
							include = GrinderUtil.isTypeSubtypeOf(((FunctionType)entry.getValue()).getCodomain(), type);
						}
						else {
							include = GrinderUtil.isTypeSubtypeOf(entry.getValue(), type);
						}						
						return include;
					})
					.map(entry -> entry.getKey())
					.collect(Collectors.toList());		
		return result;
	}
	
	/**
	 * Given a variable (a symbolic name or function application) return its
	 * corresponding identifying name.
	 * 
	 * @param variable
	 *            the variable whose identifying name is to be retrieved.
	 * @return the identifying name for the variable.
	 */
	default String getVariableName(String variable) {
		String result;
		Expression variableExpresison = parse(variable);
		Expression functor = variableExpresison.getFunctor();
		if (functor == null) {
			result = variable;
		}
		else {
			result = functor.toString();
		}
		return result;
	}
	
	/**
	 * Get the type associated with the given testing variable.
	 * 
	 * @param variable
	 *            the testing variable whose type is to be returned.
	 * @return the type of the given testing variable.
	 */
	default Type getTestingVariableType(String variable) {
		String variableName = getVariableName(variable);		
		Type result = getVariableNamesAndTypesForTesting().get(variableName);
		// We need to check if the variable is a function application
		// because if it is we need to use the codomain of its function type
		// as the type of the testing variable.
		Expression variableExpresison = parse(variable);
		Expression functor = variableExpresison.getFunctor();
		if (functor != null) {
			result = ((FunctionType)result).getCodomain();
		}
		return result;
	}
	
	/**
	 * Returns a random atom in this theory on a given variable.
	 * This is useful for making random constraints for correctness and performance testing.
	 * @param variable the name of the variable to make the random atom on.
	 * @param context a context
	 */
	Expression makeRandomAtomOn(String variable, Context context);

	/**
	 * @param variable
	 * @param context
	 * @return
	 */
	default Expression makeRandomLiteralOn(String variable, Context context) {
		Expression atom = makeRandomAtomOn(variable, context);
		Expression literal = getRandom().nextBoolean()? atom : getTheory().getLiteralNegation(atom, context);
		return literal;
	}

	/**
	 * Same as {@link #makeRandomLiteralOn(String, Context),
	 * but applied randomly to one of the testing variables.
	 */
	default Expression makeRandomLiteral(Context context) {
		String variableToBeUsed = pickTestingVariableAtRandom();
		Expression result = makeRandomLiteralOn(variableToBeUsed, context);
		return result;
	}

	Context extendWithTestingInformation(Context context);

	default Context makeContextWithTestingInformation() {
		return extendWithTestingInformation(new TrueContext(getTheory()));
	}
	
	static TheoryTestingSupport make(Random random, TheoryTestingSupport... theoryTestingSupports) {
		TheoryTestingSupport result = new CompoundTheoryTestingSupport(random, theoryTestingSupports);
		return result;
	}
	
	/**
	 * Creates an appropriate {@link TheoryTestingSupport} object given a {@link Theory} and a {@link Random}.
	 * Recognized theories are:
	 * 
    *
  • {@link CompoundTheory} *
  • {@link DifferenceArithmeticTheory} *
  • {@link EqualityTheory} *
  • {@link LinearRealArithmeticTheory} *
  • {@link PropositionalTheory} *
  • {@link BruteForceFunctionTheory} *
  • {@link TupleTheory} *
*

* An {@link UnsupportedOperationException} is thrown if an unrecognized theory is provided. * @param random * @param theory * @return */ static TheoryTestingSupport make(Random random, Theory theory) { TheoryTestingSupport result; if (theory instanceof CompoundTheory) { result = new CompoundTheoryTestingSupport((CompoundTheory) theory, random); } else if (theory instanceof DifferenceArithmeticTheory) { result = new DifferenceArithmeticTheoryTestingSupport((DifferenceArithmeticTheory) theory, random); } else if (theory instanceof EqualityTheory) { result = new EqualityTheoryTestingSupport((EqualityTheory) theory, random); } else if (theory instanceof LinearRealArithmeticTheory) { result = new LinearRealArithmeticTheoryTestingSupport((LinearRealArithmeticTheory) theory, random); } else if (theory instanceof PropositionalTheory) { result = new PropositionalTheoryTestingSupport((PropositionalTheory) theory, random); } else if (theory instanceof BruteForceFunctionTheory) { result = new BruteForceFunctionTheoryTestingSupport((BruteForceFunctionTheory) theory, random); } else if (theory instanceof TupleTheory) { result = new TupleTheoryTestingSupport((TupleTheory) theory, random); } else { throw new UnsupportedOperationException(""+theory.getClass().getSimpleName()+" currently does not have testing support in place."); } return result; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy