Skip to content

Commit

Permalink
fix stratified evaluation performance
Browse files Browse the repository at this point in the history
  • Loading branch information
madmike200590 committed Sep 3, 2020
1 parent 10ac240 commit 045d5ca
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private boolean computeStartingLiterals() {
}
}

public Collection<Literal> getStartingLiterals() {
public List<Literal> getStartingLiterals() {
return Collections.unmodifiableList(startingLiterals);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -17,7 +16,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import at.ac.tuwien.kr.alpha.common.Predicate;
import at.ac.tuwien.kr.alpha.common.atoms.Atom;
Expand Down Expand Up @@ -64,10 +62,6 @@ public class StratifiedEvaluation extends ProgramTransformation<AnalyzedProgram,

private LiteralInstantiator literalInstantiator;

// TODO remove - debugging help only!
// private TreeMap<InternalRule, Integer> ruleEvaluationCounts = new TreeMap<>((r1, r2) -> r1.toString().compareTo(r2.toString()));
// private TreeMap<InternalRule, Integer> ruleSubstitutionCounts = new TreeMap<>((r1, r2) -> r1.toString().compareTo(r2.toString()));

@Override
public InternalProgram apply(AnalyzedProgram inputProgram) {
// Calculate a stratification and initialize working memory.
Expand Down Expand Up @@ -99,9 +93,6 @@ public InternalProgram apply(AnalyzedProgram inputProgram) {
this.evaluateComponent(currComponent);
}

// TODO remove, debugging help
// this.dbgPrintCounts();

// Build the program resulting from evaluating the stratified part.
List<Atom> outputFacts = this.buildOutputFacts(inputProgram.getFacts(), this.additionalFacts);
List<InternalRule> outputRules = new ArrayList<>();
Expand All @@ -111,16 +102,6 @@ public InternalProgram apply(AnalyzedProgram inputProgram) {
return retVal;
}

// TODO remove, debugging help
// private void dbgPrintCounts() {
// System.out.println("********** GROUNDING INFO ***********");
// for (InternalRule rule : this.ruleEvaluationCounts.keySet()) {
// System.out.println("Rule: " + rule + ", evaluations = " + this.ruleEvaluationCounts.get(rule) + ", non-unique-substitutions = "
// + this.ruleSubstitutionCounts.get(rule));
// }
// System.out.println("******* END OF GROUNDING INFO *******");
// }

// extra method is better visible in CPU traces when profiling
private List<Atom> buildOutputFacts(List<Atom> initialFacts, Set<Atom> newFacts) {
Set<Atom> atomSet = new LinkedHashSet<>(initialFacts);
Expand All @@ -138,13 +119,15 @@ private void evaluateComponent(SCComponent comp) {
this.prepareComponentEvaluation(SetUtils.union(evaluationInfo.nonRecursiveRules, evaluationInfo.recursiveRules));
// Rules outside of dependency cycles only need to be evaluated once.
if (!evaluationInfo.nonRecursiveRules.isEmpty()) {
this.addFactsToProgram(this.evaluateRules(evaluationInfo.nonRecursiveRules));
this.addFactsToProgram(this.evaluateRules(evaluationInfo.nonRecursiveRules, true));
}
boolean isInitialRun = true;
if (!evaluationInfo.recursiveRules.isEmpty()) {
do {
// Now do the rules that cyclically depend on each other,
// evaluate these until nothing new can be derived any more.
this.addFactsToProgram(this.evaluateRules(evaluationInfo.recursiveRules));
this.addFactsToProgram(this.evaluateRules(evaluationInfo.recursiveRules, isInitialRun));
isInitialRun = false;
// If evaluation of rules doesn't modify the working memory we have a fixed point.
} while (!this.workingMemory.modified().isEmpty());
}
Expand All @@ -153,12 +136,12 @@ private void evaluateComponent(SCComponent comp) {
.forEach((rule) -> this.solvedRuleIds.add(rule.getRuleId()));
}

private Map<Predicate, List<Instance>> evaluateRules(Set<InternalRule> rules) {
private Map<Predicate, List<Instance>> evaluateRules(Set<InternalRule> rules, boolean isInitialRun) {
Map<Predicate, List<Instance>> addedInstances = new HashMap<>();
this.workingMemory.reset();
LOGGER.debug("Starting component evaluation run...");
for (InternalRule r : rules) {
this.evaluateRule(r);
this.evaluateRule(r, !isInitialRun);
}
this.modifiedInLastEvaluationRun = new HashMap<>();
// Since we're stratified we never have to backtrack, therefore just collect the added instances.
Expand Down Expand Up @@ -202,64 +185,79 @@ private void prepareComponentEvaluation(Set<InternalRule> rulesToEvaluate) {
}
}

private void evaluateRule(InternalRule rule) {
private void evaluateRule(InternalRule rule, boolean checkAllStartingLiterals) {
LOGGER.debug("Evaluating rule {}", rule);
List<Substitution> satisfyingSubstitutions = this.calculateSatisfyingSubstitutionsForRule(rule);
// TODO remove - debugging only
// this.ruleEvaluationCounts.putIfAbsent(rule, 0);
// this.ruleEvaluationCounts.put(rule, this.ruleEvaluationCounts.get(rule) + 1);
// this.ruleSubstitutionCounts.putIfAbsent(rule, 0);
// this.ruleSubstitutionCounts.put(rule, this.ruleSubstitutionCounts.get(rule) + satisfyingSubstitutions.size());
List<Substitution> satisfyingSubstitutions = this.calculateSatisfyingSubstitutionsForRule(rule, checkAllStartingLiterals);
for (Substitution subst : satisfyingSubstitutions) {
this.fireRule(rule, subst);
}
}

private List<Substitution> calculateSatisfyingSubstitutionsForRule(InternalRule rule) {
private List<Substitution> calculateSatisfyingSubstitutionsForRule(InternalRule rule, boolean checkAllStartingLiterals) {
LOGGER.debug("Grounding rule {}", rule);
RuleGroundingOrders groundingOrders = rule.getGroundingOrders();
List<Substitution> groundSubstitutions = new ArrayList<>(); // the actual full ground substitutions for the rule
LOGGER.debug("Is fixed rule? {}", rule.getGroundingOrders().fixedInstantiation());
if (groundingOrders.fixedInstantiation()) {
groundSubstitutions.addAll(this.calcSubstitutionsWithGroundingOrder(rule, groundingOrders.getFixedGroundingOrder()));
if (groundingOrders.fixedInstantiation()) { // FIXME fixed instantiation handling is ugly AF
RuleGroundingOrder fixedGroundingOrder = groundingOrders.getFixedGroundingOrder();
groundSubstitutions.addAll(this.calcSubstitutionsWithGroundingOrder(rule, fixedGroundingOrder, 0,
Collections.singletonList(new Substitution())));
} else {
Collection<Literal> startingLiterals = groundingOrders.getStartingLiterals();
for (Literal lit : startingLiterals) {
groundSubstitutions.addAll(this.calcSubstitutionsWithGroundingOrder(rule, groundingOrders.orderStartingFrom(lit)));
List<Literal> startingLiterals = groundingOrders.getStartingLiterals();
List<Substitution> substitutionsForStartingLiteral;
if (!checkAllStartingLiterals) {
// If we don't have to check all literals, i.e. we're in the first evaluation run, just use the first one
Literal lit = startingLiterals.get(0);
substitutionsForStartingLiteral = this.calcSubstitutionsWithGroundingOrder(rule, groundingOrders.orderStartingFrom(lit), 0,
this.substituteFromRecentlyAddedInstances(lit));
groundSubstitutions.addAll(substitutionsForStartingLiteral);
} else {
for (Literal lit : startingLiterals) {
substitutionsForStartingLiteral = this.calcSubstitutionsWithGroundingOrder(rule, groundingOrders.orderStartingFrom(lit), 0,
this.substituteFromRecentlyAddedInstances(lit));
groundSubstitutions.addAll(substitutionsForStartingLiteral);
}
}
}
return groundSubstitutions;
}

private List<Substitution> calcSubstitutionsWithGroundingOrder(InternalRule rule, RuleGroundingOrder groundingOrder) {
Literal startingLiteral = groundingOrder.getStartingLiteral();
List<Substitution> startingLiteralSubstitutions;
// In case of fixed grounding orders, the startingLiteral supplied by the grouding oder is null,
// so in that case start with an empty substitution, actual starting literal is the one at the first position of the
// grounding order.
if (startingLiteral != null) {
LiteralInstantiationResult startingLiteralInstantiationResult = this.literalInstantiator
.instantiateLiteral(startingLiteral, new Substitution());
if (startingLiteralInstantiationResult.getType() != LiteralInstantiationResult.Type.CONTINUE) {
return Collections.emptyList();
/**
* Use this to find initial substitutions for a starting literal when grounding a rule.
* In order to avoid finding the same ground instantiations of rules again, only look at
* <code>this.modifiedInLastEvaluationRun</code> to obtain instances.
*
* @param lit the literal to substitute
* @return valid ground substitutions for the literal based on the recently added instances (i.e. instances derived in
* the last evaluation run)
*/
private List<Substitution> substituteFromRecentlyAddedInstances(Literal lit) {
List<Substitution> retVal = new ArrayList<>();
Set<Instance> instances = this.modifiedInLastEvaluationRun.get(lit.getPredicate());
if (instances == null) {
return Collections.emptyList();
}
Substitution initialSubstitutionForCurrentInstance;
for (Instance instance : instances) {
initialSubstitutionForCurrentInstance = Substitution.unify(lit, instance, new Substitution());
if (initialSubstitutionForCurrentInstance != null) {
retVal.add(initialSubstitutionForCurrentInstance);
}
// Note that it is not necessary to check the assignment status for substitutions here,
// WorkingMemoryBasedInstantiationStrategy will never return substitutions based on unassigned Atoms.
startingLiteralSubstitutions = startingLiteralInstantiationResult.getSubstitutions().stream()
.map((pair) -> pair.left)
.collect(Collectors.toList());
} else {
startingLiteralSubstitutions = new ArrayList<>();
startingLiteralSubstitutions.add(new Substitution());
}
// Iterate through the rest of the grounding order. Whenever instantiation of a Literal with a given substitution causes
return retVal;
}

private List<Substitution> calcSubstitutionsWithGroundingOrder(InternalRule rule, RuleGroundingOrder groundingOrder, int startFromOrderPosition,
List<Substitution> startingSubstitutions) {
// Iterate through the grounding order starting at index startFromOrderPosition.
// Whenever instantiation of a Literal with a given substitution causes
// a result with a type other than CONTINUE, discard that substitution.
List<Substitution> currentSubstitutions = startingLiteralSubstitutions;
List<Substitution> currentSubstitutions = startingSubstitutions;
List<Substitution> updatedSubstitutions = new ArrayList<>();
Literal currentLiteral;
LiteralInstantiationResult currentLiteralResult;
int orderPosition = 0;
while ((currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition)) != null) {
int curentOrderPosition = startFromOrderPosition;
while ((currentLiteral = groundingOrder.getLiteralAtOrderPosition(curentOrderPosition)) != null) {
for (Substitution subst : currentSubstitutions) {
currentLiteralResult = this.literalInstantiator.instantiateLiteral(currentLiteral, subst);
if (currentLiteralResult.getType() == LiteralInstantiationResult.Type.CONTINUE) {
Expand All @@ -274,7 +272,7 @@ private List<Substitution> calcSubstitutionsWithGroundingOrder(InternalRule rule
}
currentSubstitutions = updatedSubstitutions;
updatedSubstitutions = new ArrayList<>();
orderPosition++;
curentOrderPosition++;
}
return currentSubstitutions;
}
Expand Down Expand Up @@ -306,7 +304,7 @@ private ComponentEvaluationInfo getRulesToEvaluate(SCComponent comp) {
for (Literal lit : rule.getPositiveBody()) {
if (headPredicates.contains(lit.getPredicate())) {
// rule body contains a predicate that is defined in the same component,
// rule is therefore part of a dependency cycle and must be evaluated repeatedly
// rule is therefore part of a dependency chain within this component and must be evaluated repeatedly
recursiveRules.add(rule);
} else {
nonRecursiveRules.add(rule);
Expand Down

0 comments on commit 045d5ca

Please sign in to comment.