Skip to content

Commit

Permalink
Extend direct functional dependencies and rewrite equations.
Browse files Browse the repository at this point in the history
- EnumerationAtom provides reverse lookup of enumerated terms.
- DirectFunctionalDependency considers EnumerationLiterals, and
  ComparisonLiterals are rewritten if they provide more dependencies.
- EquationRefactoring provides actual rewriting of equations.
  • Loading branch information
AntoniusW committed Jan 12, 2020
1 parent a7d23cd commit 6f81390
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ public boolean isGround() {
return left.isGround() && right.isGround();
}

public Term getLeft() {
return left;
}

public Term getRight() {
return right;
}

public ArithmeticOperator getArithmeticOperator() {
return arithmeticOperator;
}

@Override
public List<VariableTerm> getOccurringVariables() {
LinkedHashSet<VariableTerm> occurringVariables = new LinkedHashSet<>(left.getOccurringVariables());
Expand Down Expand Up @@ -192,6 +204,20 @@ public Integer eval(Integer left, Integer right) {

}
}

public ArithmeticOperator inverseOperator() {
switch (this) {
case PLUS:
return MINUS;
case MINUS:
return PLUS;
case TIMES:
return DIV;
case DIV:
return TIMES;
}
return null;
}
}

public static class MinusTerm extends ArithmeticTerm {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
public class EnumerationAtom extends BasicAtom {
public static final Predicate ENUMERATION_PREDICATE = Predicate.getInstance("_Enumeration", 3);
private static final HashMap<Term, HashMap<Term, Integer>> ENUMERATIONS = new HashMap<>();
private static final HashMap<Term, HashMap<Integer, Term>> REVERSE_ENUMERATIONS = new HashMap<>();

public EnumerationAtom(List<Term> terms) {
super(ENUMERATION_PREDICATE, terms);
Expand All @@ -36,6 +37,7 @@ public EnumerationAtom(List<Term> terms) {

public static void resetEnumerations() {
ENUMERATIONS.clear();
REVERSE_ENUMERATIONS.clear();
}

private Integer getEnumerationIndex(Term identifier, Term enumerationTerm) {
Expand All @@ -45,13 +47,21 @@ private Integer getEnumerationIndex(Term identifier, Term enumerationTerm) {
if (assignedInteger == null) {
int enumerationIndex = enumeratedTerms.size() + 1;
enumeratedTerms.put(enumerationTerm, enumerationIndex);
REVERSE_ENUMERATIONS.putIfAbsent(identifier, new HashMap<>());
HashMap<Integer, Term> indexToTerms = REVERSE_ENUMERATIONS.get(identifier);
indexToTerms.put(enumerationIndex, enumerationTerm);
return enumerationIndex;
} else {
return assignedInteger;
}

}

public Term getTermWithIndex(Term identifier, Integer index) {
HashMap<Integer, Term> indexToTerms = REVERSE_ENUMERATIONS.get(identifier);
return indexToTerms.get(index);
}

public void addEnumerationToSubstitution(Substitution substitution) {
Term idTerm = getTerms().get(0).substitute(substitution);
Term enumerationTerm = getTerms().get(1).substitute(substitution);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import at.ac.tuwien.kr.alpha.common.atoms.ComparisonLiteral;
import at.ac.tuwien.kr.alpha.common.atoms.Literal;
import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm;
import at.ac.tuwien.kr.alpha.common.terms.Term;
import at.ac.tuwien.kr.alpha.common.terms.VariableTerm;
import at.ac.tuwien.kr.alpha.grounder.NonGroundRule;
import at.ac.tuwien.kr.alpha.grounder.Substitution;
import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationAtom;
import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationLiteral;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -18,20 +20,57 @@
import java.util.List;
import java.util.Set;

import static at.ac.tuwien.kr.alpha.Util.oops;

/**
* Provides support for direct functional dependencies to be used in completions. Also includes an algorithm to identify
* and create DirectFunctionalDependencies for a {@link NonGroundRule}.
*
* Copyright (c) 2020, the Alpha Team.
*/
public class DirectFunctionalDependency {
private static final Logger LOGGER = LoggerFactory.getLogger(DirectFunctionalDependency.class);

private final List<Literal> evaluationOrder = new ArrayList<>();


/**
* Evaluates the functional dependency, i.e., it enlarges the given substitution with those values derivable by
* this {@link DirectFunctionalDependency}.
* @param substitution the {@link Substitution} to enlarge.
* @return the enlarged {@link Substitution}.
*/
public Substitution evaluate(Substitution substitution) {
LOGGER.debug("Evaluating FD.");
Substitution extendedSubstitution = substitution;
for (Literal literal : evaluationOrder) {
extendedSubstitution = ((ComparisonLiteral) literal).getSubstitutions(extendedSubstitution).get(0);
if (literal instanceof ComparisonLiteral) {
extendedSubstitution = ((ComparisonLiteral) literal).getSubstitutions(extendedSubstitution).get(0);
} else if (literal instanceof EnumerationLiteral) {
EnumerationAtom enumerationAtom = (EnumerationAtom) literal.getAtom();
Term identifier = enumerationAtom.getTerms().get(0).substitute(extendedSubstitution);
Term term = enumerationAtom.getTerms().get(1).substitute(extendedSubstitution);
Term index = enumerationAtom.getTerms().get(3).substitute(extendedSubstitution);

// Distinguish between the two possible FDs for EnumerationLiterals.
if (identifier.isGround() && term.isGround()) {
// FD is (A,X) -> I.
enumerationAtom.addEnumerationToSubstitution(extendedSubstitution);
} else if (identifier.isGround() && index.isGround() && !term.isGround()) {
// FD is (A,I) -> X
Term groundedTerm = enumerationAtom.getTermWithIndex(identifier, (Integer) ((ConstantTerm) index).getObject());
// Unify the obtained ground term with the non-ground one to extend the substitution.
Substitution unifyTestSubstitution = new Substitution(extendedSubstitution);
if (unifyTestSubstitution.unifyTerms(term, groundedTerm)) {
extendedSubstitution = unifyTestSubstitution;
} else {
throw oops("Substitution from EnumerationLiteral does not unify with given functional dependency: " + literal);
}
} else {
throw oops("Recorded functional dependency for EnumerationLiteral has unexpected properties: " + literal);
}
} else {
throw oops("Unknown DirectFunctionalDependency encountered, literal is: " + literal);
}
}
LOGGER.debug("Extended substitution {} into {}.", substitution, extendedSubstitution);
return extendedSubstitution;
Expand All @@ -53,23 +92,38 @@ public static DirectFunctionalDependency computeDirectFunctionalDependencies(Non
iterator.remove();
continue;
}
// We need to construct a dummy substitution and ground the arithmetic term to get correct result from isLefOrRightAssigning.
// TODO: this may break if dummy substitution value of 1 leads to undefined arithmetics.
// TODO: for p(X1) :- r(X), X1 = X+1 we need some way to get a functional dependency of X=X1-1 !
Substitution dummyGroundingSubst = new Substitution();
for (VariableTerm knownVariable : knownVariables) {
dummyGroundingSubst.put(knownVariable, ConstantTerm.getInstance(1));
}
ComparisonLiteral dummyGrounded = comparisonLiteral.substitute(dummyGroundingSubst);
if (dummyGrounded.isLeftOrRightAssigning()) {
knownVariables.addAll(dummyGrounded.getBindingVariables());
// Try to transform the equation such that it assigns one variable.
try {
ComparisonLiteral transformComparison = EquationRefactoring.transformToUnassignedEqualsRest(knownVariables, comparisonLiteral);
VariableTerm assignedVariable = (VariableTerm)transformComparison.getAtom().getTerms().get(0);
knownVariables.add(assignedVariable);
iterator.remove();
didChange = true;
directFunctionalDependency.evaluationOrder.add(comparisonLiteral);
directFunctionalDependency.evaluationOrder.add(transformComparison);
} catch (EquationRefactoring.CannotRewriteException ignored) {
// Cannot transform the equation, skip it for now.
continue;
}
} else if (bodyLiteral instanceof EnumerationLiteral) {
// TODO: we know functional dependency, use it.
iterator.remove();
EnumerationAtom enumerationAtom = (EnumerationAtom) bodyLiteral.getAtom();
List<VariableTerm> variablesInA = enumerationAtom.getTerms().get(0).getOccurringVariables();
List<VariableTerm> variablesInX = enumerationAtom.getTerms().get(1).getOccurringVariables();
VariableTerm enumerationIndexVariable = (VariableTerm) enumerationAtom.getTerms().get(2);

// Enumeration(A,X,I) has two FDs (A,I) -> X and (A,X) -> I
if (knownVariables.containsAll(variablesInA) && knownVariables.contains(enumerationIndexVariable)) {
// (A,I) is known, add variables in X.
knownVariables.addAll(variablesInX);
iterator.remove();
didChange = true;
directFunctionalDependency.evaluationOrder.add(bodyLiteral);
} else if (knownVariables.containsAll(variablesInA) && knownVariables.containsAll(variablesInX)) {
// (A,X) is known, add I then.
knownVariables.add(enumerationIndexVariable);
iterator.remove();
didChange = true;
directFunctionalDependency.evaluationOrder.add(bodyLiteral);
}
} else {
// For all other literals, no functional dependency is known, stop considering them.
iterator.remove();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package at.ac.tuwien.kr.alpha.grounder.structure;

import at.ac.tuwien.kr.alpha.common.ComparisonOperator;
import at.ac.tuwien.kr.alpha.common.atoms.ComparisonAtom;
import at.ac.tuwien.kr.alpha.common.atoms.ComparisonLiteral;
import at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm;
import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm;
import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm;
import at.ac.tuwien.kr.alpha.common.terms.IntervalTerm;
import at.ac.tuwien.kr.alpha.common.terms.Term;
import at.ac.tuwien.kr.alpha.common.terms.VariableTerm;

import java.util.HashSet;
import java.util.Set;

import static at.ac.tuwien.kr.alpha.Util.oops;

/**
* Helpe methods to refactor an equation such that a selected variable occurs alone on the left-hand side of the
* refactored equation. For example, (S + X) = T becomes X = (T - S) where S and T are arbitrary arithmetic formulas.
*
* Copyright (c) 2020, the Alpha Team.
*/
public class EquationRefactoring {

/**
* Rewrites an equation such that the given variable is alone on the left-hand side.
* Assumption is that the variable occurs exactly once in the original equation!
* @param leftHandSide the left-hand side of the original equation.
* @param rightHandSide the right-hand side of the original equation.
* @param variable the variable to put alone on the left-hand side.
* @return an equation with the variable alone on the left-hand side.
*/
private static ComparisonAtom rewriteEquationForVariable(Term leftHandSide, Term rightHandSide, VariableTerm variable) {
// Stop rewriting if left or right is just the variable.
if (leftHandSide.equals(variable)) {
// Check that the variable does not occur on the other side (i.e. it occurred twice in the original equation.
if (containsVariable(rightHandSide, variable)) {
throw new CannotRewriteException();
}
return new ComparisonAtom(leftHandSide, rightHandSide, ComparisonOperator.EQ);
}
if (rightHandSide.equals(variable)) {
// Check that the variable does not occur on the other side (i.e. it occurred twice in the original equation.
if (containsVariable(leftHandSide, variable)) {
throw new CannotRewriteException();
}
return new ComparisonAtom(rightHandSide, leftHandSide, ComparisonOperator.EQ);
}
// Check whether the variable is in left- or right-hand side.
Term sideWithVariable;
Term sideWithoutVariable;
if (containsVariable(leftHandSide, variable)) {
sideWithVariable = leftHandSide;
sideWithoutVariable = rightHandSide;
} else {
sideWithVariable = rightHandSide;
sideWithoutVariable = leftHandSide;
}
// Since the side with variable contains more than the variable alone, it must be an arithmetic term.
if (!(sideWithVariable instanceof ArithmeticTerm)) {
throw new CannotRewriteException();
}
// Move one level of operation from the side with the variable to the side without it.
// Example: (S + X) = T becomes X = (T - S).
Term subTermLeft = ((ArithmeticTerm)sideWithVariable).getLeft();
Term subTermRight = ((ArithmeticTerm)sideWithVariable).getRight();
Term subTermWithVariable;
Term subTermWithoutVariable;
if (containsVariable(subTermLeft, variable)) {
subTermWithVariable = subTermLeft;
subTermWithoutVariable = subTermRight;
} else {
subTermWithVariable = subTermRight;
subTermWithoutVariable = subTermLeft;
}
// Get the inverse operator to put on the right side.
ArithmeticTerm.ArithmeticOperator inverseOperator = ((ArithmeticTerm) sideWithVariable).getArithmeticOperator().inverseOperator();
if (inverseOperator == null) {
throw new CannotRewriteException();
}
Term newRightSide = ArithmeticTerm.getInstance(sideWithoutVariable, inverseOperator, subTermWithoutVariable);
// We removed just one level, if there are multiple, continue recursively.
return rewriteEquationForVariable(subTermWithVariable, newRightSide, variable);
}

private static boolean containsVariable(Term arithmeticTerm, VariableTerm variable) {
if (arithmeticTerm instanceof ConstantTerm) {
return false;
}
if (arithmeticTerm instanceof FunctionTerm) {
return false;
}
if (arithmeticTerm instanceof VariableTerm) {
return arithmeticTerm.equals(variable);
}
if (arithmeticTerm instanceof ArithmeticTerm) {
return containsVariable(((ArithmeticTerm) arithmeticTerm).getLeft(), variable)
|| containsVariable(((ArithmeticTerm) arithmeticTerm).getRight(), variable);
}
if (arithmeticTerm instanceof IntervalTerm) {
throw oops("Term rewriting for completion cannot handle IntervalTerm" + arithmeticTerm);
}
throw oops("Term rewriting for completion cannot handle Term" + arithmeticTerm);
}

static ComparisonLiteral transformToUnassignedEqualsRest(Set<VariableTerm> assignedVariables, ComparisonLiteral originalEquation) {
HashSet<VariableTerm> vars = new HashSet<>(originalEquation.getOccurringVariables());
vars.removeAll(assignedVariables);
if (vars.size() != 1) {
throw new CannotRewriteException();
}
VariableTerm unassignedVariable = vars.iterator().next();
ComparisonAtom comparisonAtom = originalEquation.getAtom();
ComparisonAtom rewrittenComparisonAtom = rewriteEquationForVariable(comparisonAtom.getTerms().get(0), comparisonAtom.getTerms().get(1), unassignedVariable);
// Note: if the variable occurs twice, rewriting throws a CannotRewriteException.
return new ComparisonLiteral(rewrittenComparisonAtom, true);
}

static class CannotRewriteException extends RuntimeException {
}
}

0 comments on commit 6f81390

Please sign in to comment.