From cb17d3c4fa7b2eabd83b4e54c23400d3062260c0 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 7 Jan 2019 14:38:56 +0100 Subject: [PATCH 01/66] First attempt at lax grounder heuristics (i.e. heuristics that allow to ground more rules than just those whose positive body is satisfied) --- .../kr/alpha/grounder/NaiveGrounder.java | 73 ++++++++++++------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 379393ed0..3f3aa4ba9 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -385,6 +385,10 @@ public int register(NoGood noGood) { } private List bindNextAtomInRule(NonGroundRule rule, Literal[] groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { + boolean laxGrounderHeuristic = true; // TODO + // note: groundingOrder can contain positive and negative literals + // note: groundingOrder does not contain starting literal + if (orderPosition == groundingOrder.length) { return singletonList(partialSubstitution); } @@ -422,9 +426,14 @@ private List bindNextAtomInRule(NonGroundRule rule, Literal[] grou return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, partialSubstitution, currentAssignment); } - if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() && !workingMemory.get(currentAtom.getPredicate(), true).containsInstance(new Instance(substitute.getTerms()))) { - // Generate no variable substitution. - return emptyList(); + if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() + && !workingMemory.get(currentAtom.getPredicate(), true).containsInstance(new Instance(substitute.getTerms()))) { + if (laxGrounderHeuristic) { + LOGGER.debug("Not aborting binding of rule " + rule + " because lax grounder heuristic is active"); + } else { + // Generate no variable substitution. + return emptyList(); + } } // Check if atom is also assigned true. @@ -435,19 +444,8 @@ private List bindNextAtomInRule(NonGroundRule rule, Literal[] grou } // Atom is not a fact already. - final int atomId = atomStore.putIfAbsent(substitute); - - if (currentAssignment != null) { - final ThriceTruth truth = currentAssignment.getTruth(atomId); - - if (atomId > maxAtomIdBeforeGroundingNewNoGoods || truth == null || !truth.toBoolean()) { - // Atom currently does not hold, skip further grounding. - // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. - if (!disableInstanceRemoval) { - removeAfterObtainingNewNoGoods.add(substitute); - return emptyList(); - } - } + if (storeAtomAndTerminateIfAtomDoesNotHold(currentAssignment, laxGrounderHeuristic, substitute)) { + return emptyList(); } } @@ -468,6 +466,11 @@ private List bindNextAtomInRule(NonGroundRule rule, Literal[] grou } else { instances = storage.getInstancesFromPartiallyGroundAtom(substitute); } + + //TODO: this is still a hack + if (instances.isEmpty() && laxGrounderHeuristic && substitute.isGround()) { + instances = singletonList(new Instance(substitute.getTerms())); + } ArrayList generatedSubstitutions = new ArrayList<>(); for (Instance instance : instances) { @@ -485,18 +488,8 @@ private List bindNextAtomInRule(NonGroundRule rule, Literal[] grou } if (factsFromProgram.get(substitutedAtom.getPredicate()) == null || !factsFromProgram.get(substitutedAtom.getPredicate()).contains(new Instance(substitutedAtom.getTerms()))) { - int atomId = atomStore.putIfAbsent(substitutedAtom); - - if (currentAssignment != null) { - ThriceTruth truth = currentAssignment.getTruth(atomId); - if (atomId > maxAtomIdBeforeGroundingNewNoGoods || truth == null || !truth.toBoolean()) { - // Atom currently does not hold, skip further grounding. - // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. - if (!disableInstanceRemoval) { - removeAfterObtainingNewNoGoods.add(substitutedAtom); - continue; - } - } + if (storeAtomAndTerminateIfAtomDoesNotHold(currentAssignment, laxGrounderHeuristic, substitutedAtom)) { + continue; } } List boundSubstitutions = bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, unified, currentAssignment); @@ -506,6 +499,30 @@ private List bindNextAtomInRule(NonGroundRule rule, Literal[] grou return generatedSubstitutions; } + private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignment, boolean laxGrounderHeuristic, final Atom substitute) { + final int atomId = atomStore.putIfAbsent(substitute); + + if (currentAssignment != null) { + try { + final ThriceTruth truth = currentAssignment.getTruth(atomId); + + if (atomId > maxAtomIdBeforeGroundingNewNoGoods || truth == null || !truth.toBoolean()) { + // Atom currently does not hold, skip further grounding. + // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. + if (!disableInstanceRemoval && !laxGrounderHeuristic) { + // we terminate binding if positive body literal is already assigned false, even in lax grounder heuristic + removeAfterObtainingNewNoGoods.add(substitute); + // TODO: terminate here if atom (i.e. positive body literal) is already assigned false + return true; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + // TODO: is this a bug? if atom is new, assignment does not know it yet + } + } + return false; + } + @Override public Pair, Map> getChoiceAtoms() { return choiceRecorder.getAndReset(); From 9be1542882cabfd8357061558392e2cde1cc6a6d Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 7 Jan 2019 18:02:12 +0100 Subject: [PATCH 02/66] Rename RuleGroundingOrder -> RuleGroundingOrders --- .../{RuleGroundingOrder.java => RuleGroundingOrders.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/at/ac/tuwien/kr/alpha/grounder/{RuleGroundingOrder.java => RuleGroundingOrders.java} (100%) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java similarity index 100% rename from src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java rename to src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java From 932e587250c1fb725283cef26ffbdc1af519bde2 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Jan 2019 08:52:17 +0100 Subject: [PATCH 03/66] Determine position where all vars are bound in RuleGroundingOrder --- .../kr/alpha/grounder/NaiveGrounder.java | 17 ++-- .../kr/alpha/grounder/NonGroundRule.java | 6 +- .../kr/alpha/grounder/RuleGroundingOrder.java | 72 ++++++++++++++ .../alpha/grounder/RuleGroundingOrders.java | 32 +++--- .../grounder/RuleGroundingOrderTest.java | 97 +++++++++++++++++-- 5 files changed, 193 insertions(+), 31 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 3f3aa4ba9..838788054 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -296,7 +296,7 @@ private HashMap bootstrap() { for (NonGroundRule nonGroundRule : fixedRules) { // Generate NoGoods for all rules that have a fixed grounding. - Literal[] groundingOrder = nonGroundRule.groundingOrder.getFixedGroundingOrder(); + RuleGroundingOrder groundingOrder = nonGroundRule.groundingOrder.getFixedGroundingOrder(); List substitutions = bindNextAtomInRule(nonGroundRule, groundingOrder, 0, new Substitution(), null); for (Substitution substitution : substitutions) { registry.register(noGoodGenerator.generateNoGoodsFromGroundSubstitution(nonGroundRule, substitution), groundNogoods); @@ -384,16 +384,19 @@ public int register(NoGood noGood) { return registry.register(noGood); } - private List bindNextAtomInRule(NonGroundRule rule, Literal[] groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { + private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = true; // TODO - // note: groundingOrder can contain positive and negative literals - // note: groundingOrder does not contain starting literal - if (orderPosition == groundingOrder.length) { + Literal[] literals = groundingOrder.getOtherLiterals(); // can contain positive and negative literals + if (orderPosition == literals.length) { return singletonList(partialSubstitution); } + + if (orderPosition >= groundingOrder.getPositionFromWhichAllVarsAreBound()) { + // TODO: now all vars are bound and we have to decide whether to continue binding or to terminate + } - Literal currentLiteral = groundingOrder[orderPosition]; + Literal currentLiteral = literals[orderPosition]; Atom currentAtom = currentLiteral.getAtom(); if (currentLiteral instanceof FixedInterpretationLiteral) { // Generate all substitutions for the builtin/external/interval atom. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java index 557b5bb43..a1cb2981a 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -54,7 +54,7 @@ public class NonGroundRule { private final List bodyAtomsNegative; private final Atom headAtom; - final RuleGroundingOrder groundingOrder; + final RuleGroundingOrders groundingOrder; private NonGroundRule(Rule rule, int ruleId, List bodyAtomsPositive, List bodyAtomsNegative, Atom headAtom) { this.ruleId = ruleId; @@ -71,7 +71,7 @@ private NonGroundRule(Rule rule, int ruleId, List bodyAtomsPositive, List< this.headAtom = headAtom; checkSafety(); - this.groundingOrder = new RuleGroundingOrder(this); + this.groundingOrder = new RuleGroundingOrders(this); groundingOrder.computeGroundingOrders(); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java new file mode 100644 index 000000000..e127d741b --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2019 Siemens AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.grounder; + +import at.ac.tuwien.kr.alpha.common.atoms.Literal; + +/** + * A grounding order computed by {@link RuleGroundingOrders} for a specific {@link NonGroundRule} and a specific starting literal. + */ +public class RuleGroundingOrder { + + private Literal startingLiteral; + private Literal[] otherLiterals; + private int positionLastVarBound; + + /** + * @param startingLiteral + * @param otherLiterals + * @param positionLastVarBound + */ + RuleGroundingOrder(Literal startingLiteral, Literal[] otherLiterals, int positionLastVarBound) { + super(); + this.startingLiteral = startingLiteral; + this.otherLiterals = otherLiterals; + this.positionLastVarBound = positionLastVarBound; + } + + /** + * @return the startingLiteral + */ + public Literal getStartingLiteral() { + return startingLiteral; + } + + /** + * @return the otherLiterals + */ + public Literal[] getOtherLiterals() { + return otherLiterals; + } + + /** + * @return the position in {@link #getOtherLiterals()} from which on all variables are bound + */ + public int getPositionFromWhichAllVarsAreBound() { + return positionLastVarBound + 1; + } + +} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java index 477888fd0..4c5904f1a 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -50,22 +50,21 @@ * * Note that rules with self-joins (rules with p(X,Y), p(A,B) in their body) make it necessary that every positive * literal (whose interpretation is not fixed) is a starting literal, at least for the current grounding procedure. - * Copyright (c) 2017, the Alpha Team. */ -public class RuleGroundingOrder { +public class RuleGroundingOrders { private final NonGroundRule nonGroundRule; - private HashMap groundingOrder; + private HashMap groundingOrders; private HashMap literalSelectivity; private List startingLiterals; private final boolean fixedGroundingInstantiation; - private Literal[] fixedGroundingOrder; + private RuleGroundingOrder fixedGroundingOrder; - RuleGroundingOrder(NonGroundRule nonGroundRule) { + RuleGroundingOrders(NonGroundRule nonGroundRule) { this.nonGroundRule = nonGroundRule; this.literalSelectivity = new HashMap<>(); resetLiteralSelectivity(); - this.groundingOrder = new HashMap<>(); + this.groundingOrders = new HashMap<>(); this.fixedGroundingInstantiation = computeStartingLiterals(); } @@ -127,12 +126,12 @@ public void updateLiteralSelectivity(Literal literal, int numGivenTuples, int nu // TODO: add old selectivity (with a decay factor) and new selectivity. } - Literal[] orderStartingFrom(Literal startingLiteral) { - return groundingOrder.get(startingLiteral); + RuleGroundingOrder orderStartingFrom(Literal startingLiteral) { + return groundingOrders.get(startingLiteral); } - Literal[] getFixedGroundingOrder() { + RuleGroundingOrder getFixedGroundingOrder() { return fixedGroundingOrder; } @@ -169,19 +168,26 @@ private void computeGroundingOrder(Literal startingLiteral) { } else { literalsOrder = new ArrayList<>(bodyLiterals.size() - 1); } + + int position = 0; + int positionLastVarBound = -1; while (!remainingLiterals.isEmpty()) { Literal nextGroundingLiteral = selectNextGroundingLiteral(remainingLiterals, boundVariables); if (nextGroundingLiteral == null) { throw new RuntimeException("Could not find a grounding order for rule " + nonGroundRule + " with starting literal: " + startingLiteral + ". Rule is not safe."); } remainingLiterals.remove(nextGroundingLiteral); - boundVariables.addAll(nextGroundingLiteral.getBindingVariables()); + boolean boundNewVars = boundVariables.addAll(nextGroundingLiteral.getBindingVariables()); + if (boundNewVars) { + positionLastVarBound = position; + } literalsOrder.add(nextGroundingLiteral); + position++; } if (fixedGroundingInstantiation) { - fixedGroundingOrder = literalsOrder.toArray(new Literal[0]); + fixedGroundingOrder = new RuleGroundingOrder(null, literalsOrder.toArray(new Literal[0]), positionLastVarBound); } - groundingOrder.put(startingLiteral, literalsOrder.toArray(new Literal[0])); + groundingOrders.put(startingLiteral, new RuleGroundingOrder(startingLiteral, literalsOrder.toArray(new Literal[0]), positionLastVarBound)); } private Literal selectNextGroundingLiteral(LinkedHashSet remainingLiterals, Set boundVariables) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java index 219a55045..20f9794fb 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java @@ -1,7 +1,39 @@ +/** + * Copyright (c) 2017-2019, the Alpha Team. + * All rights reserved. + * + * Additional changes made by Siemens. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.grounder; +import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +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.parser.ProgramParser; import at.ac.tuwien.kr.alpha.grounder.transformation.VariableEqualityRemoval; import org.junit.Test; @@ -12,7 +44,7 @@ import static org.junit.Assert.assertTrue; /** - * Copyright (c) 2017, the Alpha Team. + * Copyright (c) 2017-2019, the Alpha Team. */ public class RuleGroundingOrderTest { @@ -27,19 +59,19 @@ public void groundingOrder() throws IOException { new VariableEqualityRemoval().transform(program); Rule rule0 = program.getRules().get(0); NonGroundRule nonGroundRule0 = NonGroundRule.constructNonGroundRule(rule0); - RuleGroundingOrder rgo0 = new RuleGroundingOrder(nonGroundRule0); + RuleGroundingOrders rgo0 = new RuleGroundingOrders(nonGroundRule0); rgo0.computeGroundingOrders(); assertEquals(4, rgo0.getStartingLiterals().size()); Rule rule1 = program.getRules().get(1); NonGroundRule nonGroundRule1 = NonGroundRule.constructNonGroundRule(rule1); - RuleGroundingOrder rgo1 = new RuleGroundingOrder(nonGroundRule1); + RuleGroundingOrders rgo1 = new RuleGroundingOrders(nonGroundRule1); rgo1.computeGroundingOrders(); assertEquals(4, rgo1.getStartingLiterals().size()); Rule rule2 = program.getRules().get(2); NonGroundRule nonGroundRule2 = NonGroundRule.constructNonGroundRule(rule2); - RuleGroundingOrder rgo2 = new RuleGroundingOrder(nonGroundRule2); + RuleGroundingOrders rgo2 = new RuleGroundingOrders(nonGroundRule2); rgo2.computeGroundingOrders(); assertTrue(rgo2.fixedInstantiation()); } @@ -48,10 +80,59 @@ public void groundingOrder() throws IOException { public void groundingOrderUnsafe() throws IOException { Program program = parser.parse("h(X,C) :- X = Y, Y = C .. 3, C = X."); new VariableEqualityRemoval().transform(program); - Rule rule0 = program.getRules().get(0); - NonGroundRule nonGroundRule0 = NonGroundRule.constructNonGroundRule(rule0); - RuleGroundingOrder rgo0 = new RuleGroundingOrder(nonGroundRule0); - rgo0.computeGroundingOrders(); + computeGroundingOrdersForRule(program, 0); + } + + @Test + public void testPositionFromWhichAllVarsAreBound_ground() { + Program program = parser.parse("a :- b, not c."); + RuleGroundingOrders rgo0 = computeGroundingOrdersForRule(program, 0); + assertEquals(0, rgo0.getFixedGroundingOrder().getPositionFromWhichAllVarsAreBound()); + } + + @Test + public void testPositionFromWhichAllVarsAreBound_simpleNonGround() { + Program program = parser.parse("a(X) :- b(X), not c(X)."); + RuleGroundingOrders rgo0 = computeGroundingOrdersForRule(program, 0); + assertEquals(1, rgo0.getStartingLiterals().size()); + for (Literal startingLiteral : rgo0.getStartingLiterals()) { + assertEquals(0, rgo0.orderStartingFrom(startingLiteral).getPositionFromWhichAllVarsAreBound()); + } + } + + @Test + public void testPositionFromWhichAllVarsAreBound_longerSimpleNonGround() { + Program program = parser.parse("a(X) :- b(X), c(X), d(X), not e(X)."); + RuleGroundingOrders rgo0 = computeGroundingOrdersForRule(program, 0); + assertEquals(3, rgo0.getStartingLiterals().size()); + for (Literal startingLiteral : rgo0.getStartingLiterals()) { + assertEquals(0, rgo0.orderStartingFrom(startingLiteral).getPositionFromWhichAllVarsAreBound()); + } + } + + @Test + public void testPositionFromWhichAllVarsAreBound_joinedNonGround() { + Program program = parser.parse("a(X) :- b(X), c(X,Y), d(X,Z), not e(X)."); + RuleGroundingOrders rgo0 = computeGroundingOrdersForRule(program, 0); + assertTrue(2 <= rgo0.orderStartingFrom(lit("b", "X")).getPositionFromWhichAllVarsAreBound()); + assertTrue(1 <= rgo0.orderStartingFrom(lit("c", "X", "Y")).getPositionFromWhichAllVarsAreBound()); + assertTrue(1 <= rgo0.orderStartingFrom(lit("d", "X", "Z")).getPositionFromWhichAllVarsAreBound()); + } + + private RuleGroundingOrders computeGroundingOrdersForRule(Program program, int ruleIndex) { + Rule rule = program.getRules().get(ruleIndex); + NonGroundRule nonGroundRule = NonGroundRule.constructNonGroundRule(rule); + RuleGroundingOrders rgo = new RuleGroundingOrders(nonGroundRule); + rgo.computeGroundingOrders(); + return rgo; + } + + private Literal lit(String predicateName, String... variableNames) { + Term[] terms = new Term[variableNames.length]; + for (int i = 0; i < variableNames.length; i++) { + terms[i] = VariableTerm.getInstance(variableNames[i]); + } + return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms).toLiteral(); } } \ No newline at end of file From 46efc995209000b0e587d7c3088e30f5e9ba9fbf Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Jan 2019 09:20:59 +0100 Subject: [PATCH 04/66] Record predicates defined only be facts in NaiveGrounder --- .../kr/alpha/grounder/NaiveGrounder.java | 10 +++++++- .../kr/alpha/grounder/NaiveGrounderTest.java | 25 +++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 838788054..5cd1868dc 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -53,7 +53,7 @@ /** * A semi-naive grounder. - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. */ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGrounder { private static final Logger LOGGER = LoggerFactory.getLogger(NaiveGrounder.class); @@ -70,6 +70,8 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private final Map> rulesUsingPredicateWorkingMemory = new HashMap<>(); private final Map> knownGroundingSubstitutions = new HashMap<>(); private final Map knownNonGroundRules = new HashMap<>(); + + private final Set predicatesDefinedOnlyByFacts = new HashSet<>(); private ArrayList fixedRules = new ArrayList<>(); private LinkedHashSet removeAfterObtainingNewNoGoods = new LinkedHashSet<>(); @@ -112,6 +114,7 @@ private void initializeFactsAndRules(Program program) { // initialize all facts for (Atom fact : program.getFacts()) { final Predicate predicate = fact.getPredicate(); + predicatesDefinedOnlyByFacts.add(predicate); // Record predicate workingMemory.initialize(predicate); @@ -140,6 +143,7 @@ private void initializeFactsAndRules(Program program) { if (nonGroundRule.getHeadAtom() != null) { Predicate headPredicate = nonGroundRule.getHeadAtom().getPredicate(); programAnalysis.recordDefiningRule(headPredicate, nonGroundRule); + predicatesDefinedOnlyByFacts.remove(headPredicate); } // Create working memories for all predicates occurring in the rule @@ -597,6 +601,10 @@ public boolean isFact(Atom atom) { public Set justifyAtom(int atomToJustify, Assignment currentAssignment) { return analyzeUnjustified.analyze(atomToJustify, currentAssignment); } + + public Set getPredicatesDefinedOnlyByFacts() { + return Collections.unmodifiableSet(predicatesDefinedOnlyByFacts); + } private static class FirstBindingAtom { NonGroundRule rule; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 950be0dd5..7c886d4d4 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 Siemens AG + * Copyright (c) 2018-2019 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,13 +31,9 @@ import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.junit.Test; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; +import java.util.*; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * Tests {@link NaiveGrounder} @@ -105,6 +101,21 @@ public void groundConstraintAlreadyGround() { assertTrue(noGoods.containsValue(NoGood.fromConstraint(Arrays.asList(litB), Collections.emptyList()))); } + @Test + public void testPredicatesDefinedOnlyByFacts() { + Program program = PARSER.parse("a(1). b(2). c(3). " + + "d(X) :- c(X). " + + "c(X) :- b(X). "); + + AtomStore atomStore = new AtomStoreImpl(); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); + Set predicatesDefinedOnlyByFacts = grounder.getPredicatesDefinedOnlyByFacts(); + Set expected = new HashSet<>(); + expected.add(Predicate.getInstance("a", 1)); + expected.add(Predicate.getInstance("b", 1)); + assertEquals(expected, predicatesDefinedOnlyByFacts); + } + private void assertExistsNoGoodContaining(Collection noGoods, int literal) { for (NoGood noGood : noGoods) { for (int literalInNoGood : noGood) { From 31491621836049afdd3f480f7c95ea70ff83f9d3 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Jan 2019 12:04:22 +0100 Subject: [PATCH 05/66] Introduce GrounderHeuristicsConfiguration --- .../kr/alpha/grounder/NaiveGrounder.java | 4 + .../GrounderHeuristicsConfiguration.java | 83 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 5cd1868dc..591776dff 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -37,6 +37,7 @@ import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationAtom; import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import at.ac.tuwien.kr.alpha.grounder.bridges.Bridge; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.grounder.structure.AnalyzeUnjustified; import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis; import at.ac.tuwien.kr.alpha.grounder.transformation.*; @@ -79,6 +80,8 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean disableInstanceRemoval; private boolean useCountingGridNormalization; + private GrounderHeuristicsConfiguration heuristicsConfiguration = new GrounderHeuristicsConfiguration(); // TODO: make configurable from CLI + /** * If this configuration parameter is {@code true} (which it is by default), * the grounder stops grounding a rule if it contains a positive body atom which is not @@ -398,6 +401,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO if (orderPosition >= groundingOrder.getPositionFromWhichAllVarsAreBound()) { // TODO: now all vars are bound and we have to decide whether to continue binding or to terminate + // TODO: use parameters in heuristicsConfiguration to make this decision (maybe split literals into positive and negative to make it easier?) } Literal currentLiteral = literals[orderPosition]; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java new file mode 100644 index 000000000..fd7443d99 --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2019 Siemens AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.grounder.heuristics; + +import at.ac.tuwien.kr.alpha.grounder.Grounder; + +/** + * Contains configuration parameters for heuristics used by {@link Grounder}s. + * + * Both parameters {@link #toleranceConstraints} and {@link #toleranceRules} are interpreted as follows: + * A rule (or constraint) is grounded if the following conditions are satisfied: + *
    + *
  • All variables in the rule are bound (either by positive body literals that are already + * satisfied, or because the whole rule is ground).
  • + *
  • No atom occurring positively in the body is assigned F, and no atom occurring negatively + * in the body is assigned T or MBT (because this would make the rule irrelevant in the current + * part of the search space).
  • + *
  • At most {@code N} atoms occurring positively in the body are still unassigned.
  • + *
+ * + * The parameter {@link #toleranceConstraints} specifies {@code N} for constraints, while {@link #toleranceRules} + * specifies {@code N} for all other rules. Infinity is represented by the value {@code -1}. + * The default value for both parameters is {@code 0}, which means that only those rules and constraints are + * grounded whose positive body is already satisfied. + * + */ +public class GrounderHeuristicsConfiguration { + + private int toleranceConstraints = 0; + private int toleranceRules = 0; + + public GrounderHeuristicsConfiguration() { + super(); + } + + /** + * @param toleranceConstraints + * @param toleranceRules + */ + public GrounderHeuristicsConfiguration(int toleranceConstraints, int toleranceRules) { + super(); + this.toleranceConstraints = toleranceConstraints; + this.toleranceRules = toleranceRules; + } + + /** + * @return the tolerance for constraints + */ + public int getToleranceConstraints() { + return toleranceConstraints; + } + + /** + * @return the tolerance for rules that are not constraints + */ + public int getToleranceRules() { + return toleranceRules; + } + +} From fd18df0e5d6e9406c4d2aa2a7473f3dd2608cb3e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Jan 2019 23:53:04 +0100 Subject: [PATCH 06/66] Changes after review by Antonius Weinzierl * removed NaiveGrounder.predicatesDefinedOnlyByFacts again, because it does not provide additional benefit over ProgramAnalysis * comments on design decisions in NaiveGrounder.bindNextAtomInRule * bugfix in NaiveGrounder.storeAtomAndTerminateIfAtomDoesNotHold --- .../kr/alpha/grounder/NaiveGrounder.java | 21 ++++++----------- .../kr/alpha/grounder/NaiveGrounderTest.java | 23 +++++-------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 591776dff..807d9990b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -71,8 +71,6 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private final Map> rulesUsingPredicateWorkingMemory = new HashMap<>(); private final Map> knownGroundingSubstitutions = new HashMap<>(); private final Map knownNonGroundRules = new HashMap<>(); - - private final Set predicatesDefinedOnlyByFacts = new HashSet<>(); private ArrayList fixedRules = new ArrayList<>(); private LinkedHashSet removeAfterObtainingNewNoGoods = new LinkedHashSet<>(); @@ -117,7 +115,6 @@ private void initializeFactsAndRules(Program program) { // initialize all facts for (Atom fact : program.getFacts()) { final Predicate predicate = fact.getPredicate(); - predicatesDefinedOnlyByFacts.add(predicate); // Record predicate workingMemory.initialize(predicate); @@ -146,7 +143,6 @@ private void initializeFactsAndRules(Program program) { if (nonGroundRule.getHeadAtom() != null) { Predicate headPredicate = nonGroundRule.getHeadAtom().getPredicate(); programAnalysis.recordDefiningRule(headPredicate, nonGroundRule); - predicatesDefinedOnlyByFacts.remove(headPredicate); } // Create working memories for all predicates occurring in the rule @@ -459,7 +455,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO return emptyList(); } } - + // substituted atom contains variables if (currentLiteral.isNegated()) { throw oops("Current atom should be positive at this point but is not"); @@ -478,8 +474,10 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO instances = storage.getInstancesFromPartiallyGroundAtom(substitute); } - //TODO: this is still a hack if (instances.isEmpty() && laxGrounderHeuristic && substitute.isGround()) { + // note: this is necessary in the case that the current atom has just been grounded and is not known by the working memory yet + // we do not add the atom to the working memory so as not to trigger additional grounding + // (but maybe the working memory will be redesigned in the future) instances = singletonList(new Instance(substitute.getTerms())); } @@ -514,10 +512,11 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignm final int atomId = atomStore.putIfAbsent(substitute); if (currentAssignment != null) { - try { + if (atomId <= maxAtomIdBeforeGroundingNewNoGoods) { + // atom has not just been grounded final ThriceTruth truth = currentAssignment.getTruth(atomId); - if (atomId > maxAtomIdBeforeGroundingNewNoGoods || truth == null || !truth.toBoolean()) { + if (truth == null || !truth.toBoolean()) { // Atom currently does not hold, skip further grounding. // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. if (!disableInstanceRemoval && !laxGrounderHeuristic) { @@ -527,8 +526,6 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignm return true; } } - } catch (ArrayIndexOutOfBoundsException e) { - // TODO: is this a bug? if atom is new, assignment does not know it yet } } return false; @@ -605,10 +602,6 @@ public boolean isFact(Atom atom) { public Set justifyAtom(int atomToJustify, Assignment currentAssignment) { return analyzeUnjustified.analyze(atomToJustify, currentAssignment); } - - public Set getPredicatesDefinedOnlyByFacts() { - return Collections.unmodifiableSet(predicatesDefinedOnlyByFacts); - } private static class FirstBindingAtom { NonGroundRule rule; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 7c886d4d4..f42d2ff48 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -31,9 +31,13 @@ import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.junit.Test; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Tests {@link NaiveGrounder} @@ -101,21 +105,6 @@ public void groundConstraintAlreadyGround() { assertTrue(noGoods.containsValue(NoGood.fromConstraint(Arrays.asList(litB), Collections.emptyList()))); } - @Test - public void testPredicatesDefinedOnlyByFacts() { - Program program = PARSER.parse("a(1). b(2). c(3). " - + "d(X) :- c(X). " - + "c(X) :- b(X). "); - - AtomStore atomStore = new AtomStoreImpl(); - NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); - Set predicatesDefinedOnlyByFacts = grounder.getPredicatesDefinedOnlyByFacts(); - Set expected = new HashSet<>(); - expected.add(Predicate.getInstance("a", 1)); - expected.add(Predicate.getInstance("b", 1)); - assertEquals(expected, predicatesDefinedOnlyByFacts); - } - private void assertExistsNoGoodContaining(Collection noGoods, int literal) { for (NoGood noGood : noGoods) { for (int literalInNoGood : noGood) { From f36485cf5f4675c479de74ddab3734a3431c25d2 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Jan 2019 23:53:26 +0100 Subject: [PATCH 07/66] RuleGroundingOrder#toString --- .../kr/alpha/grounder/RuleGroundingOrder.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java index e127d741b..e92f9adf9 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java @@ -68,5 +68,26 @@ public Literal[] getOtherLiterals() { public int getPositionFromWhichAllVarsAreBound() { return positionLastVarBound + 1; } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(startingLiteral); + sb.append(" : "); + for (int i = 0; i < otherLiterals.length; i++) { + if (i == positionLastVarBound + 1) { + sb.append("| "); + } + sb.append(otherLiterals[i]); + if (i < otherLiterals.length - 1) { + sb.append(", "); + } + } + + return sb.toString(); + } } From 74546b64076a9eb87bbed97d1a88f592c9e3a90e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Jan 2019 23:55:51 +0100 Subject: [PATCH 08/66] Satisfy CheckStyle --- .../java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 4 ++-- .../heuristics/GrounderHeuristicsConfiguration.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 807d9990b..ceaf7920f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -395,10 +395,10 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO return singletonList(partialSubstitution); } - if (orderPosition >= groundingOrder.getPositionFromWhichAllVarsAreBound()) { +// if (orderPosition >= groundingOrder.getPositionFromWhichAllVarsAreBound()) { // TODO: now all vars are bound and we have to decide whether to continue binding or to terminate // TODO: use parameters in heuristicsConfiguration to make this decision (maybe split literals into positive and negative to make it easier?) - } +// } Literal currentLiteral = literals[orderPosition]; Atom currentAtom = currentLiteral.getAtom(); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java index fd7443d99..1e7dadd2d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java @@ -49,11 +49,13 @@ */ public class GrounderHeuristicsConfiguration { - private int toleranceConstraints = 0; - private int toleranceRules = 0; + private int toleranceConstraints; + private int toleranceRules; public GrounderHeuristicsConfiguration() { super(); + this.toleranceConstraints = 0; + this.toleranceRules = 0; } /** From 8e0dc367ce583c29c2bfd83462b17aed1f4751d7 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 11 Jan 2019 09:13:45 +0100 Subject: [PATCH 09/66] Fix bug in NaiveGrounder#storeAtomAndTerminateIfAtomDoesNotHold The issue here is that it is possible that atoms have been grounded by bootstrap() and are not yet known to the assignment. The computation of maxAtomIdBeforeGroundingNewNoGoods happened therefore too late (after bootstrap()), but we can do without maxAtomIdBeforeGroundingNewNoGoods anyway. --- .../kr/alpha/grounder/NaiveGrounder.java | 24 +++++++------------ .../kr/alpha/solver/TrailAssignment.java | 4 ++-- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index ceaf7920f..8d9ad9d9b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -41,7 +41,6 @@ import at.ac.tuwien.kr.alpha.grounder.structure.AnalyzeUnjustified; import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis; import at.ac.tuwien.kr.alpha.grounder.transformation.*; -import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +73,6 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private ArrayList fixedRules = new ArrayList<>(); private LinkedHashSet removeAfterObtainingNewNoGoods = new LinkedHashSet<>(); - private int maxAtomIdBeforeGroundingNewNoGoods = -1; private boolean disableInstanceRemoval; private boolean useCountingGridNormalization; @@ -316,7 +314,6 @@ public Map getNoGoods(Assignment currentAssignment) { // In first call, prepare facts and ground rules. final Map newNoGoods = fixedRules != null ? bootstrap() : new LinkedHashMap<>(); - maxAtomIdBeforeGroundingNewNoGoods = atomStore.getMaxAtomId(); // Compute new ground rule (evaluate joins with newly changed atoms) for (IndexedInstanceStorage modifiedWorkingMemory : workingMemory.modified()) { // Skip predicates solely used in the solver which do not occur in rules. @@ -512,19 +509,14 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignm final int atomId = atomStore.putIfAbsent(substitute); if (currentAssignment != null) { - if (atomId <= maxAtomIdBeforeGroundingNewNoGoods) { - // atom has not just been grounded - final ThriceTruth truth = currentAssignment.getTruth(atomId); - - if (truth == null || !truth.toBoolean()) { - // Atom currently does not hold, skip further grounding. - // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. - if (!disableInstanceRemoval && !laxGrounderHeuristic) { - // we terminate binding if positive body literal is already assigned false, even in lax grounder heuristic - removeAfterObtainingNewNoGoods.add(substitute); - // TODO: terminate here if atom (i.e. positive body literal) is already assigned false - return true; - } + if (!currentAssignment.isAssigned(atomId) || !currentAssignment.getTruth(atomId).toBoolean()) { + // Atom currently does not hold, skip further grounding. + // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. + if (!disableInstanceRemoval && !laxGrounderHeuristic) { + // we terminate binding if positive body literal is already assigned false, even in lax grounder heuristic + removeAfterObtainingNewNoGoods.add(substitute); + // TODO: terminate here if atom (i.e. positive body literal) is already assigned false + return true; } } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java index 9a48fdab8..fe9d4b2ce 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -130,7 +130,7 @@ public void setCallback(ChoiceManager choiceManager) { @Override public boolean isAssigned(int atom) { - return values[atom] != 0; + return values.length > atom && values[atom] != 0; } @Override From 20572c11a89ab50aa495feae8a9e8f8d29004fb4 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 11 Jan 2019 13:13:55 +0100 Subject: [PATCH 10/66] Throw exception when lax grounder heuristics encounters a dead end i.e. a situation in which it has to terminate binding, but where it could (potentially) continue if the grounding order were different --- .../kr/alpha/grounder/NaiveGrounder.java | 33 ++++++++++- .../kr/alpha/grounder/NonGroundRule.java | 2 +- .../alpha/grounder/RuleGroundingOrders.java | 2 +- .../java/at/ac/tuwien/kr/alpha/TestUtil.java | 55 +++++++++++++++++++ .../kr/alpha/grounder/NaiveGrounderTest.java | 44 ++++++++++++++- .../grounder/RuleGroundingOrderTest.java | 19 ++----- 6 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 8d9ad9d9b..90bbcd40f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -384,7 +384,7 @@ public int register(NoGood noGood) { return registry.register(noGood); } - private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { + List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = true; // TODO Literal[] literals = groundingOrder.getOtherLiterals(); // can contain positive and negative literals @@ -477,6 +477,15 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // (but maybe the working memory will be redesigned in the future) instances = singletonList(new Instance(substitute.getTerms())); } + + if (laxGrounderHeuristic && instances.isEmpty()) { + // we have reached a point where we have to terminate binding, + // but it might be possible that a different grounding order would allow us to continue binding + // under the presence of a lax grounder heuristic + if (laxGrounderHeuristicCouldPotentiallyContinue(substitute, partialSubstitution, groundingOrder, orderPosition)) { + throw new UnsupportedOperationException("Lax grounder heuristic does not support escaping unfortunate grounding orders yet. Grounding order: " + groundingOrder); + } + } ArrayList generatedSubstitutions = new ArrayList<>(); for (Instance instance : instances) { @@ -523,6 +532,28 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignm return false; } + private boolean laxGrounderHeuristicCouldPotentiallyContinue(Atom substitute, Substitution partialSubstitution, RuleGroundingOrder groundingOrder, + int orderPosition) { + final Set remainingVariables = new HashSet<>(); + for (VariableTerm var : substitute.getBindingVariables()) { + if (!partialSubstitution.isVariableSet(var)) { + remainingVariables.add(var); + } + } + if (remainingVariables.isEmpty()) { + return false; + } + Literal[] remainingLiterals = Arrays.copyOfRange(groundingOrder.getOtherLiterals(), orderPosition + 1, groundingOrder.getOtherLiterals().length); + for (Literal literal : remainingLiterals) { + for (VariableTerm var : literal.getBindingVariables()) { + if (remainingVariables.contains(var)) { + return true; + } + } + } + return false; + } + @Override public Pair, Map> getChoiceAtoms() { return choiceRecorder.getAndReset(); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java index a1cb2981a..f9cdbdf05 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NonGroundRule.java @@ -45,7 +45,7 @@ * Copyright (c) 2017-2018, the Alpha Team. */ public class NonGroundRule { - private static final IntIdGenerator ID_GENERATOR = new IntIdGenerator(); + static final IntIdGenerator ID_GENERATOR = new IntIdGenerator(); private final int ruleId; private final Rule rule; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java index 4c5904f1a..fc872c2f3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java @@ -53,7 +53,7 @@ */ public class RuleGroundingOrders { private final NonGroundRule nonGroundRule; - private HashMap groundingOrders; + HashMap groundingOrders; private HashMap literalSelectivity; private List startingLiterals; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java b/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java new file mode 100644 index 000000000..5b1c42c39 --- /dev/null +++ b/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2019 Siemens AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha; + +import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; +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 org.apache.commons.lang3.StringUtils; + +/** + * Provides utility methods for test cases + * + */ +public class TestUtil { + + public static Literal literal(String predicateName, String... termStrings) { + Term[] terms = new Term[termStrings.length]; + for (int i = 0; i < termStrings.length; i++) { + String termString = termStrings[i]; + if (StringUtils.isAllUpperCase(termString.substring(0, 1))) { + terms[i] = VariableTerm.getInstance(termString); + } else { + terms[i] = ConstantTerm.getInstance(termString); + } + } + return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms).toLiteral(); + } + +} diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index f42d2ff48..c247de407 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -27,8 +27,10 @@ import at.ac.tuwien.kr.alpha.common.*; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; +import org.junit.Before; import org.junit.Test; import java.util.Arrays; @@ -36,8 +38,8 @@ import java.util.Collections; import java.util.Map; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static at.ac.tuwien.kr.alpha.TestUtil.literal; +import static org.junit.Assert.*; /** * Tests {@link NaiveGrounder} @@ -45,6 +47,11 @@ public class NaiveGrounderTest { private static final ProgramParser PARSER = new ProgramParser(); + @Before + public void resetRuleIdGenerator() { + NonGroundRule.ID_GENERATOR.resetGenerator(); + } + /** * Asserts that a ground rule whose positive body is not satisfied by the empty assignment * is grounded immediately. @@ -105,6 +112,39 @@ public void groundConstraintAlreadyGround() { assertTrue(noGoods.containsValue(NoGood.fromConstraint(Arrays.asList(litB), Collections.emptyList()))); } + @Test(expected=UnsupportedOperationException.class) + public void avoidDeadEndsWithLaxGrounderHeuristic() { + RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), new Literal[] {literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")}, -1); + RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), new Literal[] {literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")}, -1); + testDeadEnd(groundingOrderP1, groundingOrderQ1, false); + } + + @Test + public void noDeadEndWithLaxGrounderHeuristic() { + RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), new Literal[] {literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")}, -1); + RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), new Literal[] {literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")}, -1); + testDeadEnd(groundingOrderP1, groundingOrderQ1, true); + } + + private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder groundingOrderQ1, boolean expectNoGoods) { + Program program = PARSER.parse("p1(1). q1(1). " + + "x :- p1(X), p2(X), q1(Y), q2(Y). " + + "p2(X) :- something(X). " // this is to trick the grounder into believing that p2 and q2 might become true + + "q2(X) :- something(X). "); + + AtomStore atomStore = new AtomStoreImpl(); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); + + NonGroundRule nonGroundRule = grounder.getNonGroundRule(0); + nonGroundRule.groundingOrder.groundingOrders.put(literal("p1", "X"), groundingOrderP1); + nonGroundRule.groundingOrder.groundingOrders.put(literal("q1", "Y"), groundingOrderQ1); + + TrailAssignment currentAssignment = new TrailAssignment(atomStore); + Map noGoods = grounder.getNoGoods(currentAssignment); + System.out.println(noGoods); + assertEquals(expectNoGoods, !noGoods.isEmpty()); + } + private void assertExistsNoGoodContaining(Collection noGoods, int literal) { for (NoGood noGood : noGoods) { for (int literalInNoGood : noGood) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java index 20f9794fb..4d35dcde0 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java @@ -27,19 +27,16 @@ */ package at.ac.tuwien.kr.alpha.grounder; -import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.common.Rule; -import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; -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.parser.ProgramParser; import at.ac.tuwien.kr.alpha.grounder.transformation.VariableEqualityRemoval; import org.junit.Test; import java.io.IOException; +import static at.ac.tuwien.kr.alpha.TestUtil.literal; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -114,9 +111,9 @@ public void testPositionFromWhichAllVarsAreBound_longerSimpleNonGround() { public void testPositionFromWhichAllVarsAreBound_joinedNonGround() { Program program = parser.parse("a(X) :- b(X), c(X,Y), d(X,Z), not e(X)."); RuleGroundingOrders rgo0 = computeGroundingOrdersForRule(program, 0); - assertTrue(2 <= rgo0.orderStartingFrom(lit("b", "X")).getPositionFromWhichAllVarsAreBound()); - assertTrue(1 <= rgo0.orderStartingFrom(lit("c", "X", "Y")).getPositionFromWhichAllVarsAreBound()); - assertTrue(1 <= rgo0.orderStartingFrom(lit("d", "X", "Z")).getPositionFromWhichAllVarsAreBound()); + assertTrue(2 <= rgo0.orderStartingFrom(literal("b", "X")).getPositionFromWhichAllVarsAreBound()); + assertTrue(1 <= rgo0.orderStartingFrom(literal("c", "X", "Y")).getPositionFromWhichAllVarsAreBound()); + assertTrue(1 <= rgo0.orderStartingFrom(literal("d", "X", "Z")).getPositionFromWhichAllVarsAreBound()); } private RuleGroundingOrders computeGroundingOrdersForRule(Program program, int ruleIndex) { @@ -127,12 +124,4 @@ private RuleGroundingOrders computeGroundingOrdersForRule(Program program, int r return rgo; } - private Literal lit(String predicateName, String... variableNames) { - Term[] terms = new Term[variableNames.length]; - for (int i = 0; i < variableNames.length; i++) { - terms[i] = VariableTerm.getInstance(variableNames[i]); - } - return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms).toLiteral(); - } - } \ No newline at end of file From 492e50b4536c76f662c1056b835b2638acfd320e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 11 Jan 2019 15:01:19 +0100 Subject: [PATCH 11/66] Do not ground rules that are not applicable --- .../kr/alpha/grounder/NaiveGrounder.java | 22 ++++- .../java/at/ac/tuwien/kr/alpha/TestUtil.java | 30 +++++- .../kr/alpha/grounder/NaiveGrounderTest.java | 91 +++++++++++++++---- 3 files changed, 118 insertions(+), 25 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 90bbcd40f..5855ead98 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -41,6 +41,7 @@ import at.ac.tuwien.kr.alpha.grounder.structure.AnalyzeUnjustified; import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis; import at.ac.tuwien.kr.alpha.grounder.transformation.*; +import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -426,6 +427,10 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro if (substitute.isGround()) { // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { + if (isAlreadyAssignedTrue(substitute, currentAssignment)) { + // TODO: not grounding rules that are not applicable can lead to the problem that they are not even added when they are relevant later, at least under presence of aggregates + return emptyList(); + } // Atom occurs negated in the rule, continue grounding return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, partialSubstitution, currentAssignment); } @@ -514,17 +519,28 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro return generatedSubstitutions; } + private boolean isAlreadyAssignedTrue(Atom atom, Assignment currentAssignment) { + if (currentAssignment != null && atom.isGround() && atomStore.contains(atom)) { + int atomId = atomStore.get(atom); + return currentAssignment.isAssigned(atomId) && currentAssignment.getTruth(atomId).toBoolean(); + } + return false; + } + private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignment, boolean laxGrounderHeuristic, final Atom substitute) { final int atomId = atomStore.putIfAbsent(substitute); if (currentAssignment != null) { - if (!currentAssignment.isAssigned(atomId) || !currentAssignment.getTruth(atomId).toBoolean()) { + ThriceTruth truth = currentAssignment.isAssigned(atomId) ? currentAssignment.getTruth(atomId) : null; + if (truth == null || !truth.toBoolean()) { // Atom currently does not hold, skip further grounding. // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. if (!disableInstanceRemoval && !laxGrounderHeuristic) { - // we terminate binding if positive body literal is already assigned false, even in lax grounder heuristic removeAfterObtainingNewNoGoods.add(substitute); - // TODO: terminate here if atom (i.e. positive body literal) is already assigned false + return true; + } + if (truth != null && !truth.toBoolean()) { + // terminate if positive body atom is assigned false return true; } } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java b/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java index 5b1c42c39..a7c249a46 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java @@ -25,7 +25,10 @@ */ package at.ac.tuwien.kr.alpha; +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; @@ -33,13 +36,20 @@ import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import org.apache.commons.lang3.StringUtils; +import java.util.Collection; +import java.util.stream.Collectors; + /** * Provides utility methods for test cases * */ public class TestUtil { - + public static Literal literal(String predicateName, String... termStrings) { + return atom(predicateName, termStrings).toLiteral(); + } + + public static Atom atom(String predicateName, String... termStrings) { Term[] terms = new Term[termStrings.length]; for (int i = 0; i < termStrings.length; i++) { String termString = termStrings[i]; @@ -49,7 +59,23 @@ public static Literal literal(String predicateName, String... termStrings) { terms[i] = ConstantTerm.getInstance(termString); } } - return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms).toLiteral(); + return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms); + } + + public static Literal literal(String predicateName, int... termInts) { + return atom(predicateName, termInts).toLiteral(); + } + + public static Atom atom(String predicateName, int... termInts) { + Term[] terms = new Term[termInts.length]; + for (int i = 0; i < termInts.length; i++) { + terms[i] = ConstantTerm.getInstance(termInts[i]); + } + return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms); + } + + public static void printNoGoods(AtomStore atomStore, Collection noGoods) { + System.out.println(noGoods.stream().map(atomStore::noGoodToString).collect(Collectors.toSet())); } } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index c247de407..c1b7dc9a7 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -29,6 +29,7 @@ import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; +import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.junit.Before; import org.junit.Test; @@ -38,7 +39,7 @@ import java.util.Collections; import java.util.Map; -import static at.ac.tuwien.kr.alpha.TestUtil.literal; +import static at.ac.tuwien.kr.alpha.TestUtil.*; import static org.junit.Assert.*; /** @@ -46,12 +47,12 @@ */ public class NaiveGrounderTest { private static final ProgramParser PARSER = new ProgramParser(); - + @Before public void resetRuleIdGenerator() { NonGroundRule.ID_GENERATOR.resetGenerator(); } - + /** * Asserts that a ground rule whose positive body is not satisfied by the empty assignment * is grounded immediately. @@ -61,7 +62,7 @@ public void groundRuleAlreadyGround() { Program program = PARSER.parse("a :- not b. " + "b :- not a. " + "c :- b."); - + AtomStore atomStore = new AtomStoreImpl(); Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore); Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore)); @@ -81,7 +82,7 @@ public void groundRuleWithLongerBodyAlreadyGround() { + "b :- not a. " + "c :- b. " + "d :- b, c. "); - + AtomStore atomStore = new AtomStoreImpl(); Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore); Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore)); @@ -94,7 +95,7 @@ public void groundRuleWithLongerBodyAlreadyGround() { assertExistsNoGoodContaining(noGoods.values(), litCNeg); assertExistsNoGoodContaining(noGoods.values(), litDNeg); } - + /** * Asserts that a ground constraint whose positive body is not satisfied by the empty assignment * is grounded immediately. @@ -104,44 +105,94 @@ public void groundConstraintAlreadyGround() { Program program = PARSER.parse("a :- not b. " + "b :- not a. " + ":- b."); - + AtomStore atomStore = new AtomStoreImpl(); Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore); Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore)); int litB = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("b", 0)))); assertTrue(noGoods.containsValue(NoGood.fromConstraint(Arrays.asList(litB), Collections.emptyList()))); } - - @Test(expected=UnsupportedOperationException.class) + + @Test(expected = UnsupportedOperationException.class) public void avoidDeadEndsWithLaxGrounderHeuristic() { - RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), new Literal[] {literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")}, -1); - RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), new Literal[] {literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")}, -1); + RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), + new Literal[] {literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")}, -1); + RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), + new Literal[] {literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")}, -1); testDeadEnd(groundingOrderP1, groundingOrderQ1, false); } - + @Test public void noDeadEndWithLaxGrounderHeuristic() { - RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), new Literal[] {literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")}, -1); - RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), new Literal[] {literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")}, -1); + RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), + new Literal[] {literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")}, -1); + RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), + new Literal[] {literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")}, -1); testDeadEnd(groundingOrderP1, groundingOrderQ1, true); } - + private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder groundingOrderQ1, boolean expectNoGoods) { Program program = PARSER.parse("p1(1). q1(1). " + "x :- p1(X), p2(X), q1(Y), q2(Y). " - + "p2(X) :- something(X). " // this is to trick the grounder into believing that p2 and q2 might become true + + "p2(X) :- something(X). " // this is to trick the grounder into believing that p2 and q2 might become true + "q2(X) :- something(X). "); - + AtomStore atomStore = new AtomStoreImpl(); NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); - + NonGroundRule nonGroundRule = grounder.getNonGroundRule(0); nonGroundRule.groundingOrder.groundingOrders.put(literal("p1", "X"), groundingOrderP1); nonGroundRule.groundingOrder.groundingOrders.put(literal("q1", "Y"), groundingOrderQ1); - + TrailAssignment currentAssignment = new TrailAssignment(atomStore); Map noGoods = grounder.getNoGoods(currentAssignment); - System.out.println(noGoods); + printNoGoods(atomStore, noGoods.values()); + assertEquals(expectNoGoods, !noGoods.isEmpty()); + } + + @Test + public void testGroundingOfRuleSwitchedOffByFalsePositiveBody() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X). " + + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + testIfGrounderGroundsRule(program, ThriceTruth.FALSE, false); + } + + @Test + public void testGroundingOfRuleNotSwitchedOffByTruePositiveBody() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X). " + + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + testIfGrounderGroundsRule(program, ThriceTruth.TRUE, true); + } + + @Test + public void testGroundingOfRuleSwitchedOffByTrueNegativeBody() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), not b(X). " + + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + testIfGrounderGroundsRule(program, ThriceTruth.TRUE, false); + } + + @Test + public void testGroundingOfRuleNotSwitchedOffByFalseNegativeBody() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), not b(X). " + + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + testIfGrounderGroundsRule(program, ThriceTruth.FALSE, true); + } + + private void testIfGrounderGroundsRule(Program program, ThriceTruth bTruth, boolean expectNoGoods) { + AtomStore atomStore = new AtomStoreImpl(); + TrailAssignment currentAssignment = new TrailAssignment(atomStore); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); + + int b = atomStore.putIfAbsent(atom("b", 1)); + currentAssignment.growForMaxAtomId(); + currentAssignment.assign(b, bTruth); + + Map noGoods = grounder.getNoGoods(currentAssignment); + printNoGoods(atomStore, noGoods.values()); assertEquals(expectNoGoods, !noGoods.isEmpty()); } From d800cf0a58d5b6fa4ebe1b3d2e99ac3f391e5252 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 11 Jan 2019 17:14:48 +0100 Subject: [PATCH 12/66] Respect GrounderHeuristicConfiguration terminate binding if more than N positive body literals are unassigned --- src/main/java/at/ac/tuwien/kr/alpha/Main.java | 10 ++- .../kr/alpha/grounder/GrounderFactory.java | 38 ++++++++- .../kr/alpha/grounder/NaiveGrounder.java | 51 +++++++----- .../GrounderHeuristicsConfiguration.java | 30 +++++++- .../kr/alpha/grounder/NaiveGrounderTest.java | 77 +++++++++++++++++-- .../kr/alpha/solver/AggregatesTest.java | 5 +- 6 files changed, 176 insertions(+), 35 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Main.java b/src/main/java/at/ac/tuwien/kr/alpha/Main.java index cb2b2aefc..a53908078 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Main.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Main.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -27,10 +27,14 @@ */ package at.ac.tuwien.kr.alpha; -import at.ac.tuwien.kr.alpha.common.*; +import at.ac.tuwien.kr.alpha.common.AnswerSet; +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.grounder.Grounder; import at.ac.tuwien.kr.alpha.grounder.GrounderFactory; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import at.ac.tuwien.kr.alpha.solver.Solver; import at.ac.tuwien.kr.alpha.solver.SolverFactory; @@ -228,7 +232,7 @@ public static void main(String[] args) { final AtomStore atomStore = new AtomStoreImpl(); final Grounder grounder = GrounderFactory.getInstance( - commandLine.getOptionValue(OPT_GROUNDER, DEFAULT_GROUNDER), program, atomStore, filter, normalizationUseGrid + commandLine.getOptionValue(OPT_GROUNDER, DEFAULT_GROUNDER), program, atomStore, filter, new GrounderHeuristicsConfiguration(), normalizationUseGrid ); // NOTE: Using time as seed is fine as the internal heuristics diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java index e3df1aac0..dbb7ee90b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java @@ -1,20 +1,52 @@ +/** + * Copyright (c) 2016-2019, the Alpha Team. + * All rights reserved. + * + * Additional changes made by Siemens. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.grounder; import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.grounder.bridges.Bridge; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; public final class GrounderFactory { - public static Grounder getInstance(String name, Program program, AtomStore atomStore, java.util.function.Predicate filter, boolean useCountingGridNormalization, Bridge... bridges) { + public static Grounder getInstance(String name, Program program, AtomStore atomStore, java.util.function.Predicate filter, GrounderHeuristicsConfiguration heuristicsConfiguration, boolean useCountingGridNormalization, Bridge... bridges) { switch (name.toLowerCase()) { case "naive": - return new NaiveGrounder(program, atomStore, filter, useCountingGridNormalization, bridges); + return new NaiveGrounder(program, atomStore, filter, heuristicsConfiguration, useCountingGridNormalization, bridges); } throw new IllegalArgumentException("Unknown grounder requested."); } + public static Grounder getInstance(String name, Program program, AtomStore atomStore, GrounderHeuristicsConfiguration heuristicsConfiguration) { + return getInstance(name, program, atomStore, p -> true, heuristicsConfiguration, false); + } + public static Grounder getInstance(String name, Program program, AtomStore atomStore) { - return getInstance(name, program, atomStore, p -> true, false); + return getInstance(name, program, atomStore, p -> true, new GrounderHeuristicsConfiguration(), false); } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 5855ead98..335f0a7ac 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -47,6 +47,7 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import static at.ac.tuwien.kr.alpha.Util.oops; import static java.util.Collections.emptyList; @@ -77,7 +78,7 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean disableInstanceRemoval; private boolean useCountingGridNormalization; - private GrounderHeuristicsConfiguration heuristicsConfiguration = new GrounderHeuristicsConfiguration(); // TODO: make configurable from CLI + private GrounderHeuristicsConfiguration heuristicsConfiguration; // TODO: make configurable from CLI /** * If this configuration parameter is {@code true} (which it is by default), @@ -88,12 +89,17 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean stopBindingAtNonTruePositiveBody = true; public NaiveGrounder(Program program, AtomStore atomStore, Bridge... bridges) { - this(program, atomStore, p -> true, false, bridges); + this(program, atomStore, new GrounderHeuristicsConfiguration(), bridges); } - NaiveGrounder(Program program, AtomStore atomStore, java.util.function.Predicate filter, boolean useCountingGrid, Bridge... bridges) { + public NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsConfiguration heuristicsConfiguration, Bridge... bridges) { + this(program, atomStore, p -> true, heuristicsConfiguration, false, bridges); + } + + NaiveGrounder(Program program, AtomStore atomStore, java.util.function.Predicate filter, GrounderHeuristicsConfiguration heuristicsConfiguration, boolean useCountingGrid, Bridge... bridges) { super(filter, bridges); this.atomStore = atomStore; + this.heuristicsConfiguration = heuristicsConfiguration; programAnalysis = new ProgramAnalysis(program); analyzeUnjustified = new AnalyzeUnjustified(programAnalysis, atomStore, factsFromProgram); @@ -386,17 +392,18 @@ public int register(NoGood noGood) { } List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { - boolean laxGrounderHeuristic = true; // TODO + int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint()); + int remainingTolerance = tolerance >= 0 ? tolerance : Integer.MAX_VALUE; + return bindNextAtomInRule(rule, groundingOrder, orderPosition, new AtomicInteger(remainingTolerance), partialSubstitution, currentAssignment); + } + + private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + boolean laxGrounderHeuristic = heuristicsConfiguration.isLax(rule.isConstraint()); Literal[] literals = groundingOrder.getOtherLiterals(); // can contain positive and negative literals if (orderPosition == literals.length) { return singletonList(partialSubstitution); } - -// if (orderPosition >= groundingOrder.getPositionFromWhichAllVarsAreBound()) { - // TODO: now all vars are bound and we have to decide whether to continue binding or to terminate - // TODO: use parameters in heuristicsConfiguration to make this decision (maybe split literals into positive and negative to make it easier?) -// } Literal currentLiteral = literals[orderPosition]; Atom currentAtom = currentLiteral.getAtom(); @@ -411,14 +418,14 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro final List generatedSubstitutions = new ArrayList<>(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. - generatedSubstitutions.addAll(bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, substitution, currentAssignment)); + generatedSubstitutions.addAll(bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, new AtomicInteger(remainingTolerance.get()), substitution, currentAssignment)); } return generatedSubstitutions; } if (currentAtom instanceof EnumerationAtom) { // Get the enumeration value and add it to the current partialSubstitution. ((EnumerationAtom) currentAtom).addEnumerationToSubstitution(partialSubstitution); - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); } // check if partialVariableSubstitution already yields a ground atom @@ -432,7 +439,7 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro return emptyList(); } // Atom occurs negated in the rule, continue grounding - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); } if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() @@ -449,11 +456,12 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro final LinkedHashSet instances = factsFromProgram.get(substitute.getPredicate()); if (!(instances == null || !instances.contains(new Instance(substitute.getTerms())))) { // Ground literal holds, continue finding a variable substitution. - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); } // Atom is not a fact already. - if (storeAtomAndTerminateIfAtomDoesNotHold(currentAssignment, laxGrounderHeuristic, substitute)) { + if (storeAtomAndTerminateIfAtomDoesNotHold(substitute, currentAssignment, new AtomicInteger(remainingTolerance.get()), laxGrounderHeuristic)) { + // TODO: this has to be refactored -- remainingTolerance is "cloned" because the same method calls happens again further below return emptyList(); } } @@ -480,6 +488,7 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro // note: this is necessary in the case that the current atom has just been grounded and is not known by the working memory yet // we do not add the atom to the working memory so as not to trigger additional grounding // (but maybe the working memory will be redesigned in the future) + // TODO: this is a bit badly designed -- if substitute is already ground, we add the instance here just that the for loop below has an element to iterate, thereby doing some unnecessary work instances = singletonList(new Instance(substitute.getTerms())); } @@ -508,11 +517,11 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro } if (factsFromProgram.get(substitutedAtom.getPredicate()) == null || !factsFromProgram.get(substitutedAtom.getPredicate()).contains(new Instance(substitutedAtom.getTerms()))) { - if (storeAtomAndTerminateIfAtomDoesNotHold(currentAssignment, laxGrounderHeuristic, substitutedAtom)) { + if (storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, remainingTolerance, laxGrounderHeuristic)) { continue; } } - List boundSubstitutions = bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, unified, currentAssignment); + List boundSubstitutions = bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, unified, currentAssignment); generatedSubstitutions.addAll(boundSubstitutions); } @@ -527,18 +536,22 @@ private boolean isAlreadyAssignedTrue(Atom atom, Assignment currentAssignment) { return false; } - private boolean storeAtomAndTerminateIfAtomDoesNotHold(Assignment currentAssignment, boolean laxGrounderHeuristic, final Atom substitute) { + private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance, boolean laxGrounderHeuristic) { final int atomId = atomStore.putIfAbsent(substitute); if (currentAssignment != null) { ThriceTruth truth = currentAssignment.isAssigned(atomId) ? currentAssignment.getTruth(atomId) : null; if (truth == null || !truth.toBoolean()) { - // Atom currently does not hold, skip further grounding. - // TODO: investigate grounding heuristics for use here, i.e., ground anyways to avoid re-grounding in the future. + // Atom currently does not hold if (!disableInstanceRemoval && !laxGrounderHeuristic) { + // TODO: make more beautiful and general, without laxGrounderHeuristic removeAfterObtainingNewNoGoods.add(substitute); return true; } + if (truth == null && remainingTolerance.decrementAndGet() < 0) { + // terminate if more positive atoms are unsatisfied as tolerated by the heuristic + return true; + } if (truth != null && !truth.toBoolean()) { // terminate if positive body atom is assigned false return true; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java index 1e7dadd2d..cf319dd51 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java @@ -62,7 +62,7 @@ public GrounderHeuristicsConfiguration() { * @param toleranceConstraints * @param toleranceRules */ - public GrounderHeuristicsConfiguration(int toleranceConstraints, int toleranceRules) { + private GrounderHeuristicsConfiguration(int toleranceConstraints, int toleranceRules) { super(); this.toleranceConstraints = toleranceConstraints; this.toleranceRules = toleranceRules; @@ -82,4 +82,32 @@ public int getToleranceRules() { return toleranceRules; } + /** + * @param ruleIsConstraint {@code true} iff the parameter for constraints shall be returned + * @return {@link #getToleranceConstraints()} if {@code ruleIsConstraint}, otherwise {@link #getToleranceRules()} + */ + public int getTolerance(boolean ruleIsConstraint) { + return ruleIsConstraint ? getToleranceConstraints() : getToleranceRules(); + } + + /** + * @param ruleIsConstraint {@code true} iff the parameter for constraints shall be returned + * @return {@code true} iff the tolerance is not 0 + */ + public boolean isLax(boolean ruleIsConstraint) { + return getTolerance(ruleIsConstraint) != 0; + } + + public static GrounderHeuristicsConfiguration strict() { + return new GrounderHeuristicsConfiguration(0, 0); + } + + public static GrounderHeuristicsConfiguration lax(int toleranceConstraints, int toleranceRules) { + return new GrounderHeuristicsConfiguration(toleranceConstraints, toleranceRules); + } + + public static GrounderHeuristicsConfiguration lax() { + return new GrounderHeuristicsConfiguration(-1, -1); + } + } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index c1b7dc9a7..e26f7dd0c 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -28,6 +28,7 @@ import at.ac.tuwien.kr.alpha.common.*; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; @@ -44,6 +45,11 @@ /** * Tests {@link NaiveGrounder} + * + * Some test cases use atoms of the something/1 predicate to trick the grounder + * into believing that other atoms might become true. This is fragile because future implementations + * of preprocessing techniques might render this trick useless. + * TODO: make less fragile */ public class NaiveGrounderTest { private static final ProgramParser PARSER = new ProgramParser(); @@ -134,11 +140,11 @@ public void noDeadEndWithLaxGrounderHeuristic() { private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder groundingOrderQ1, boolean expectNoGoods) { Program program = PARSER.parse("p1(1). q1(1). " + "x :- p1(X), p2(X), q1(Y), q2(Y). " - + "p2(X) :- something(X). " // this is to trick the grounder into believing that p2 and q2 might become true + + "p2(X) :- something(X). " + "q2(X) :- something(X). "); AtomStore atomStore = new AtomStoreImpl(); - NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, GrounderHeuristicsConfiguration.lax()); NonGroundRule nonGroundRule = grounder.getNonGroundRule(0); nonGroundRule.groundingOrder.groundingOrders.put(literal("p1", "X"), groundingOrderP1); @@ -154,7 +160,7 @@ private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder public void testGroundingOfRuleSwitchedOffByFalsePositiveBody() { Program program = PARSER.parse("a(1). " + "c(X) :- a(X), b(X). " - + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + + "b(X) :- something(X). "); testIfGrounderGroundsRule(program, ThriceTruth.FALSE, false); } @@ -162,7 +168,7 @@ public void testGroundingOfRuleSwitchedOffByFalsePositiveBody() { public void testGroundingOfRuleNotSwitchedOffByTruePositiveBody() { Program program = PARSER.parse("a(1). " + "c(X) :- a(X), b(X). " - + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + + "b(X) :- something(X). "); testIfGrounderGroundsRule(program, ThriceTruth.TRUE, true); } @@ -170,7 +176,7 @@ public void testGroundingOfRuleNotSwitchedOffByTruePositiveBody() { public void testGroundingOfRuleSwitchedOffByTrueNegativeBody() { Program program = PARSER.parse("a(1). " + "c(X) :- a(X), not b(X). " - + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + + "b(X) :- something(X). "); testIfGrounderGroundsRule(program, ThriceTruth.TRUE, false); } @@ -178,14 +184,14 @@ public void testGroundingOfRuleSwitchedOffByTrueNegativeBody() { public void testGroundingOfRuleNotSwitchedOffByFalseNegativeBody() { Program program = PARSER.parse("a(1). " + "c(X) :- a(X), not b(X). " - + "b(X) :- something(X). "); // this is to trick the grounder into believing that b might become true + + "b(X) :- something(X). "); testIfGrounderGroundsRule(program, ThriceTruth.FALSE, true); } private void testIfGrounderGroundsRule(Program program, ThriceTruth bTruth, boolean expectNoGoods) { AtomStore atomStore = new AtomStoreImpl(); TrailAssignment currentAssignment = new TrailAssignment(atomStore); - NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, GrounderHeuristicsConfiguration.lax()); int b = atomStore.putIfAbsent(atom("b", 1)); currentAssignment.growForMaxAtomId(); @@ -195,6 +201,63 @@ private void testIfGrounderGroundsRule(Program program, ThriceTruth bTruth, bool printNoGoods(atomStore, noGoods.values()); assertEquals(expectNoGoods, !noGoods.isEmpty()); } + + @Test + public void testLaxGrounderHeuristicTolerance_0_reject() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X). " + + "b(X) :- something(X)."); + testLaxGrounderHeuristicTolerance(program, 0, 0, false); + } + + @Test + public void testLaxGrounderHeuristicTolerance_1_accept() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X). " + + "b(X) :- something(X)."); + testLaxGrounderHeuristicTolerance(program, 1, 0, true); + } + + @Test + public void testLaxGrounderHeuristicTolerance_1_reject() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X), b(X+1). " + + "b(X) :- something(X)."); + testLaxGrounderHeuristicTolerance(program, 1, 0, false); + } + + @Test + public void testLaxGrounderHeuristicTolerance_2_accept() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X), b(X+1). " + + "b(X) :- something(X)."); + testLaxGrounderHeuristicTolerance(program, 2, 0, true); + } + + @Test + public void testLaxGrounderHeuristicTolerance_2_reject() { + Program program = PARSER.parse("a(1). " + + "c(X) :- a(X), b(X), b(X+1), b(X+2). " + + "b(X) :- something(X)."); + testLaxGrounderHeuristicTolerance(program, 2, 0, false); + } + + private void testLaxGrounderHeuristicTolerance(Program program, int tolerance, int nTrueBs, boolean expectNoGoods) { + AtomStore atomStore = new AtomStoreImpl(); + TrailAssignment currentAssignment = new TrailAssignment(atomStore); + GrounderHeuristicsConfiguration heuristicConfiguration = GrounderHeuristicsConfiguration.lax(tolerance, tolerance); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, heuristicConfiguration); + + for (int i = 1; i <= nTrueBs; i++) { + int b = atomStore.putIfAbsent(atom("b", i)); + currentAssignment.growForMaxAtomId(); + currentAssignment.assign(b, ThriceTruth.TRUE); + } + + Map noGoods = grounder.getNoGoods(currentAssignment); + printNoGoods(atomStore, noGoods.values()); + assertEquals(expectNoGoods, !noGoods.isEmpty()); + } private void assertExistsNoGoodContaining(Collection noGoods, int literal) { for (NoGood noGood : noGoods) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/AggregatesTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/AggregatesTest.java index 2e9aec56b..a4d6a648b 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/AggregatesTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/AggregatesTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 Siemens AG + * Copyright (c) 2018-2019 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +29,7 @@ import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.grounder.GrounderFactory; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.grounder.transformation.CardinalityNormalization; import at.ac.tuwien.kr.alpha.grounder.transformation.SumNormalization; import org.junit.Test; @@ -165,7 +166,7 @@ public void testAggregate_Sum_GlobalVariable() throws IOException { @Override protected Solver getInstance(Program program) { AtomStore atomStore = new AtomStoreImpl(); - return getInstance(atomStore, GrounderFactory.getInstance(grounderName, program, atomStore, p->true, useCountingGridNormalization())); + return getInstance(atomStore, GrounderFactory.getInstance(grounderName, program, atomStore, p->true, new GrounderHeuristicsConfiguration(), useCountingGridNormalization())); } protected abstract boolean useCountingGridNormalization(); From 543a502b7b8d7a48044b97001c0ebefab34f2db2 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 14 Jan 2019 00:43:17 +0100 Subject: [PATCH 13/66] Merge branch 'grounder_heuristics' into domind_plus_grounder_heu # Conflicts: # src/main/java/at/ac/tuwien/kr/alpha/Main.java # src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java # src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java # src/test/java/at/ac/tuwien/kr/alpha/solver/AggregatesTest.java --- .../ac/tuwien/kr/alpha/grounder/GrounderFactory.java | 10 +++++----- .../at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 4 ++-- .../ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java index f14950ba0..f117cb2d7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/GrounderFactory.java @@ -1,19 +1,19 @@ /** * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. - * + * * Additional changes made by Siemens. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1) Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * 2) 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. - * + * * 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 diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index e10733b25..7ca3d17d3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -91,7 +91,7 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr */ private boolean stopBindingAtNonTruePositiveBody = true; - public NaiveGrounder(Program program, AtomStore atomStore, Bridge... bridges) { + public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternalChecks, Bridge... bridges) { this(program, atomStore, new GrounderHeuristicsConfiguration(), debugInternalChecks, bridges); } @@ -99,7 +99,7 @@ public NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsCon this(program, atomStore, p -> true, heuristicsConfiguration, false, debugInternalChecks, bridges); } - NaiveGrounder(Program program, AtomStore atomStore, java.util.function.Predicate filter, GrounderHeuristicsConfiguration heuristicsConfiguration, boolean useCountingGrid, Bridge... bridges) { + NaiveGrounder(Program program, AtomStore atomStore, java.util.function.Predicate filter, GrounderHeuristicsConfiguration heuristicsConfiguration, boolean useCountingGrid, boolean debugInternalChecks, Bridge... bridges) { super(filter, bridges); this.atomStore = atomStore; this.heuristicsConfiguration = heuristicsConfiguration; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 37a999e3b..b77ebe703 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -144,7 +144,7 @@ private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder + "q2(X) :- something(X). "); AtomStore atomStore = new AtomStoreImpl(); - NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, GrounderHeuristicsConfiguration.lax()); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, GrounderHeuristicsConfiguration.lax(), true); NonGroundRule nonGroundRule = grounder.getNonGroundRule(0); nonGroundRule.groundingOrder.groundingOrders.put(literal("p1", "X"), groundingOrderP1); @@ -191,7 +191,7 @@ public void testGroundingOfRuleNotSwitchedOffByFalseNegativeBody() { private void testIfGrounderGroundsRule(Program program, ThriceTruth bTruth, boolean expectNoGoods) { AtomStore atomStore = new AtomStoreImpl(); TrailAssignment currentAssignment = new TrailAssignment(atomStore); - NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, GrounderHeuristicsConfiguration.lax()); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, GrounderHeuristicsConfiguration.lax(), true); int b = atomStore.putIfAbsent(atom("b", 1)); currentAssignment.growForMaxAtomId(); @@ -246,7 +246,7 @@ private void testLaxGrounderHeuristicTolerance(Program program, int tolerance, i AtomStore atomStore = new AtomStoreImpl(); TrailAssignment currentAssignment = new TrailAssignment(atomStore); GrounderHeuristicsConfiguration heuristicConfiguration = GrounderHeuristicsConfiguration.lax(tolerance, tolerance); - NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, heuristicConfiguration); + NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, heuristicConfiguration, true); for (int i = 1; i <= nTrueBs; i++) { int b = atomStore.putIfAbsent(atom("b", i)); From f09eb9b617276c20105fd5e5c3e215ab74e2b855 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 14 Jan 2019 00:44:00 +0100 Subject: [PATCH 14/66] Strengthen NaiveGrounder.laxGrounderHeuristicCouldPotentiallyContinue (which is just a temporary workaround) --- .../at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 7ca3d17d3..fde8f04a7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -585,6 +585,10 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As return false; } + /** + * TODO: this is just a temporary workaround + * @param currentAssignment + */ private boolean laxGrounderHeuristicCouldPotentiallyContinue(Atom substitute, Substitution partialSubstitution, RuleGroundingOrder groundingOrder, int orderPosition) { final Set remainingVariables = new HashSet<>(); @@ -598,8 +602,11 @@ private boolean laxGrounderHeuristicCouldPotentiallyContinue(Atom substitute, Su } Literal[] remainingLiterals = Arrays.copyOfRange(groundingOrder.getOtherLiterals(), orderPosition + 1, groundingOrder.getOtherLiterals().length); for (Literal literal : remainingLiterals) { + if (literal.isNegated()) { + continue; + } for (VariableTerm var : literal.getBindingVariables()) { - if (remainingVariables.contains(var)) { + if (remainingVariables.contains(var) && remainingVariables.size() > 1) { return true; } } From ebd1e4f7e5fd78d28fe06696c44feebcb5b7dd34 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 14 Jan 2019 01:16:10 +0100 Subject: [PATCH 15/66] RuleGroundingOrder.pushBack() (experimental) --- .../kr/alpha/grounder/NaiveGrounder.java | 9 ++++-- .../kr/alpha/grounder/RuleGroundingOrder.java | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index fde8f04a7..08f11394c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -520,9 +520,14 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding // under the presence of a lax grounder heuristic - if (laxGrounderHeuristicCouldPotentiallyContinue(substitute, partialSubstitution, groundingOrder, orderPosition)) { - throw new UnsupportedOperationException("Lax grounder heuristic does not support escaping unfortunate grounding orders yet. Grounding order: " + groundingOrder); +// if (laxGrounderHeuristicCouldPotentiallyContinue(substitute, partialSubstitution, groundingOrder, orderPosition)) { +// throw new UnsupportedOperationException("Lax grounder heuristic does not support escaping unfortunate grounding orders yet. Grounding order: " + groundingOrder); +// } + //if (orderPosition >= groundingOrder.getPushedBackFrom()) { // TODO: could this miss potentially successful bindings? + if (groundingOrder.getPushedBackFrom() <= 0) { + return emptyList(); } + return bindNextAtomInRule(rule, groundingOrder.pushBack(orderPosition), orderPosition, remainingTolerance, partialSubstitution, currentAssignment); } ArrayList generatedSubstitutions = new ArrayList<>(); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java index e92f9adf9..556e175bd 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java @@ -35,6 +35,7 @@ public class RuleGroundingOrder { private Literal startingLiteral; private Literal[] otherLiterals; private int positionLastVarBound; + private int pushedBackFrom = -1; /** * @param startingLiteral @@ -69,6 +70,13 @@ public int getPositionFromWhichAllVarsAreBound() { return positionLastVarBound + 1; } + /** + * @return the pushedBackFrom + */ + public int getPushedBackFrom() { + return pushedBackFrom; + } + /* (non-Javadoc) * @see java.lang.Object#toString() */ @@ -90,4 +98,24 @@ public String toString() { return sb.toString(); } + /** + * @param orderPosition + * @return + */ + public RuleGroundingOrder pushBack(int orderPosition) { + // TODO: this ignores positionLastVarBound for now (because it is not used anyway) + Literal[] reorderedOtherLiterals = new Literal[otherLiterals.length]; + int i = 0; + for (; i < orderPosition; i++) { + reorderedOtherLiterals[i] = otherLiterals[i]; + } + for (; i < otherLiterals.length - 1; i++) { + reorderedOtherLiterals[i] = otherLiterals[i + 1]; + } + reorderedOtherLiterals[i] = otherLiterals[orderPosition]; + RuleGroundingOrder reorderedGroundingOrder = new RuleGroundingOrder(startingLiteral, reorderedOtherLiterals, positionLastVarBound); + reorderedGroundingOrder.pushedBackFrom = (this.pushedBackFrom >= 0 ? this.pushedBackFrom : otherLiterals.length) - 1; + return reorderedGroundingOrder; + } + } From 2b24a7870f2eaed15ce112e1a9d58444bf3b0dbc Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 15 Jan 2019 09:34:59 +0100 Subject: [PATCH 16/66] Do not abort grounding a rule in which a negative atom is true because this might lead to necessary rules not being grounded at all --- .../ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 12 ------------ .../tuwien/kr/alpha/grounder/NaiveGrounderTest.java | 2 ++ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 08f11394c..32aa0f5e7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -458,10 +458,6 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO if (substitute.isGround()) { // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { - if (isAlreadyAssignedTrue(substitute, currentAssignment)) { - // TODO: not grounding rules that are not applicable can lead to the problem that they are not even added when they are relevant later, at least under presence of aggregates - return emptyList(); - } // Atom occurs negated in the rule: continue grounding return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); } @@ -557,14 +553,6 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO return generatedSubstitutions; } - private boolean isAlreadyAssignedTrue(Atom atom, Assignment currentAssignment) { - if (currentAssignment != null && atom.isGround() && atomStore.contains(atom)) { - int atomId = atomStore.get(atom); - return currentAssignment.isAssigned(atomId) && currentAssignment.getTruth(atomId).toBoolean(); - } - return false; - } - private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance, boolean laxGrounderHeuristic) { final int atomId = atomStore.putIfAbsent(substitute); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index b77ebe703..9bcba4c8e 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -33,6 +33,7 @@ import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.Arrays; @@ -173,6 +174,7 @@ public void testGroundingOfRuleNotSwitchedOffByTruePositiveBody() { } @Test + @Ignore("Currently, rule grounding is not switched off by a true negative body atom") public void testGroundingOfRuleSwitchedOffByTrueNegativeBody() { Program program = PARSER.parse("a(1). " + "c(X) :- a(X), not b(X). " From ce53f0ad97d5732e57cfd7bb1e9c359990d888bf Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 15 Jan 2019 10:20:50 +0100 Subject: [PATCH 17/66] Ignore deprecated test case --- .../java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 9bcba4c8e..028b1e695 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -121,6 +121,7 @@ public void groundConstraintAlreadyGround() { } @Test(expected = UnsupportedOperationException.class) + @Ignore("Currently, NaiveGrounder tries to escape this situation instead of throwing an exception") public void avoidDeadEndsWithLaxGrounderHeuristic() { RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), new Literal[] {literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")}, -1); From d884d55dd3e10321b66290eb31bd672700914bf8 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 18 Jan 2019 09:05:04 +0100 Subject: [PATCH 18/66] Correct escaping dead ends in rule grounding order --- .../kr/alpha/common/terms/ArithmeticTerm.java | 31 ++++++- .../kr/alpha/grounder/NaiveGrounder.java | 90 ++++++++----------- .../kr/alpha/grounder/RuleGroundingOrder.java | 70 ++++++++------- .../alpha/grounder/RuleGroundingOrders.java | 4 +- .../kr/alpha/grounder/NaiveGrounderTest.java | 9 +- 5 files changed, 109 insertions(+), 95 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java index bdc74f578..628914ce3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java @@ -1,3 +1,30 @@ +/** + * Copyright (c) 2017-2019, the Alpha Team. + * All rights reserved. + * + * Additional changes made by Siemens. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.common.terms; import at.ac.tuwien.kr.alpha.common.Interner; @@ -61,7 +88,9 @@ public Term normalizeVariables(String renamePrefix, RenameCounter counter) { public static Integer evaluateGroundTerm(Term term) { if (!term.isGround()) { - throw new RuntimeException("Cannot evaluate arithmetic term since it is not ground: " + term); +// throw new RuntimeException("Cannot evaluate arithmetic term since it is not ground: " + term); + return null; + // TODO: maybe this has to be revised. In the case of lax grounder heuristics, it may be that we try to evaluate a non-ground term here, but an exception does not help } return evaluateGroundTermHelper(term); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 32aa0f5e7..9d7e7656f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -374,7 +374,11 @@ public Map getNoGoods(Assignment currentAssignment) { workingMemory.reset(); for (Atom removeAtom : removeAfterObtainingNewNoGoods) { final IndexedInstanceStorage storage = this.workingMemory.get(removeAtom, true); - storage.removeInstance(new Instance(removeAtom.getTerms())); + Instance instance = new Instance(removeAtom.getTerms()); + if (storage.containsInstance(instance)) { + // lax grounder heuristics may attempt to remove instances that are not yet in the working memory + storage.removeInstance(instance); + } } removeAfterObtainingNewNoGoods = new LinkedHashSet<>(); @@ -421,35 +425,47 @@ List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder gro return bindNextAtomInRule(rule, groundingOrder, orderPosition, new AtomicInteger(remainingTolerance), partialSubstitution, currentAssignment); } + private List advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + groundingOrder.considerUntilCurrentEnd(); + return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + } + + private List pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + RuleGroundingOrder modifiedGroundingOrder = groundingOrder.pushBack(orderPosition); + if (modifiedGroundingOrder == null) { + return emptyList(); + } + return bindNextAtomInRule(rule, modifiedGroundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + } + private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = heuristicsConfiguration.isLax(rule.isConstraint()); - Literal[] literals = groundingOrder.getOtherLiterals(); // can contain positive and negative literals - if (orderPosition == literals.length) { + Literal currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition); + if (currentLiteral == null) { return singletonList(partialSubstitution); } - - Literal currentLiteral = literals[orderPosition]; + Atom currentAtom = currentLiteral.getAtom(); if (currentLiteral instanceof FixedInterpretationLiteral) { // Generate all substitutions for the builtin/external/interval atom. final List substitutions = ((FixedInterpretationLiteral)currentLiteral.substitute(partialSubstitution)).getSubstitutions(partialSubstitution); - + if (substitutions.isEmpty()) { - return emptyList(); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); } - + final List generatedSubstitutions = new ArrayList<>(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. - generatedSubstitutions.addAll(bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, new AtomicInteger(remainingTolerance.get()), substitution, currentAssignment)); + generatedSubstitutions.addAll(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, new AtomicInteger(remainingTolerance.get()), substitution, currentAssignment)); } return generatedSubstitutions; } if (currentAtom instanceof EnumerationAtom) { // Get the enumeration value and add it to the current partialSubstitution. ((EnumerationAtom) currentAtom).addEnumerationToSubstitution(partialSubstitution); - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); } // check if partialVariableSubstitution already yields a ground atom @@ -459,7 +475,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { // Atom occurs negated in the rule: continue grounding - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); } if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() @@ -476,7 +492,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO final LinkedHashSet instances = factsFromProgram.get(substitute.getPredicate()); if (!(instances == null || !instances.contains(new Instance(substitute.getTerms())))) { // Ground literal holds, continue finding a variable substitution. - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); } // Atom is not a fact already. @@ -488,7 +504,11 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // substituted atom contains variables if (currentLiteral.isNegated()) { - throw oops("Current atom should be positive at this point but is not"); + if (laxGrounderHeuristic) { + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + } else { + throw oops("Current atom should be positive at this point but is not"); + } } IndexedInstanceStorage storage = workingMemory.get(currentAtom.getPredicate(), true); @@ -516,14 +536,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding // under the presence of a lax grounder heuristic -// if (laxGrounderHeuristicCouldPotentiallyContinue(substitute, partialSubstitution, groundingOrder, orderPosition)) { -// throw new UnsupportedOperationException("Lax grounder heuristic does not support escaping unfortunate grounding orders yet. Grounding order: " + groundingOrder); -// } - //if (orderPosition >= groundingOrder.getPushedBackFrom()) { // TODO: could this miss potentially successful bindings? - if (groundingOrder.getPushedBackFrom() <= 0) { - return emptyList(); - } - return bindNextAtomInRule(rule, groundingOrder.pushBack(orderPosition), orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); } ArrayList generatedSubstitutions = new ArrayList<>(); @@ -546,7 +559,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO continue; } } - List boundSubstitutions = bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, unified, currentAssignment); + List boundSubstitutions = advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, unified, currentAssignment); generatedSubstitutions.addAll(boundSubstitutions); } @@ -560,10 +573,8 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As ThriceTruth truth = currentAssignment.isAssigned(atomId) ? currentAssignment.getTruth(atomId) : null; if (truth == null || !truth.toBoolean()) { // Atom currently does not hold - if (!disableInstanceRemoval && !laxGrounderHeuristic) { - // TODO: make more beautiful and general, without laxGrounderHeuristic + if (!disableInstanceRemoval) { removeAfterObtainingNewNoGoods.add(substitute); - return true; } if (truth == null && remainingTolerance.decrementAndGet() < 0) { // terminate if more positive atoms are unsatisfied as tolerated by the heuristic @@ -578,35 +589,6 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As return false; } - /** - * TODO: this is just a temporary workaround - * @param currentAssignment - */ - private boolean laxGrounderHeuristicCouldPotentiallyContinue(Atom substitute, Substitution partialSubstitution, RuleGroundingOrder groundingOrder, - int orderPosition) { - final Set remainingVariables = new HashSet<>(); - for (VariableTerm var : substitute.getBindingVariables()) { - if (!partialSubstitution.isVariableSet(var)) { - remainingVariables.add(var); - } - } - if (remainingVariables.isEmpty()) { - return false; - } - Literal[] remainingLiterals = Arrays.copyOfRange(groundingOrder.getOtherLiterals(), orderPosition + 1, groundingOrder.getOtherLiterals().length); - for (Literal literal : remainingLiterals) { - if (literal.isNegated()) { - continue; - } - for (VariableTerm var : literal.getBindingVariables()) { - if (remainingVariables.contains(var) && remainingVariables.size() > 1) { - return true; - } - } - } - return false; - } - @Override public Pair, Map> getChoiceAtoms() { return choiceRecorder.getAndResetChoices(); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java index 556e175bd..e42c1de13 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java @@ -27,26 +27,30 @@ import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import java.util.ArrayList; +import java.util.List; + /** * A grounding order computed by {@link RuleGroundingOrders} for a specific {@link NonGroundRule} and a specific starting literal. */ public class RuleGroundingOrder { private Literal startingLiteral; - private Literal[] otherLiterals; + private List otherLiterals; private int positionLastVarBound; - private int pushedBackFrom = -1; + private int stopBindingAtOrderPosition; - /** - * @param startingLiteral - * @param otherLiterals - * @param positionLastVarBound - */ - RuleGroundingOrder(Literal startingLiteral, Literal[] otherLiterals, int positionLastVarBound) { + RuleGroundingOrder(Literal startingLiteral, List otherLiterals, int positionLastVarBound) { super(); this.startingLiteral = startingLiteral; this.otherLiterals = otherLiterals; this.positionLastVarBound = positionLastVarBound; + this.stopBindingAtOrderPosition = otherLiterals.size(); + } + + private RuleGroundingOrder(RuleGroundingOrder otherRuleGroundingOrder) { + this(otherRuleGroundingOrder.startingLiteral, new ArrayList<>(otherRuleGroundingOrder.otherLiterals), otherRuleGroundingOrder.positionLastVarBound); + this.stopBindingAtOrderPosition = otherRuleGroundingOrder.stopBindingAtOrderPosition; } /** @@ -55,12 +59,22 @@ public class RuleGroundingOrder { public Literal getStartingLiteral() { return startingLiteral; } - + /** - * @return the otherLiterals + * Returns the literal at the given position in the grounding order, + * except it is already known that this literal is not able to yield new bindings. + * + * A literal cannot yield new bindings if it has been copied to the end of the grounding order + * when no bindings could be found, and no bindings for other literals could be found in the meantime. + * + * @param orderPosition zero-based index into list of literals except the starting literal + * @return the literal at the given position, or {@code null} if it is already known that this literal is not able to yield new bindings */ - public Literal[] getOtherLiterals() { - return otherLiterals; + public Literal getLiteralAtOrderPosition(int orderPosition) { + if (orderPosition >= stopBindingAtOrderPosition) { + return null; + } + return otherLiterals.get(orderPosition); } /** @@ -70,13 +84,6 @@ public int getPositionFromWhichAllVarsAreBound() { return positionLastVarBound + 1; } - /** - * @return the pushedBackFrom - */ - public int getPushedBackFrom() { - return pushedBackFrom; - } - /* (non-Javadoc) * @see java.lang.Object#toString() */ @@ -85,12 +92,12 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append(startingLiteral); sb.append(" : "); - for (int i = 0; i < otherLiterals.length; i++) { + for (int i = 0; i < otherLiterals.size(); i++) { if (i == positionLastVarBound + 1) { sb.append("| "); } - sb.append(otherLiterals[i]); - if (i < otherLiterals.length - 1) { + sb.append(otherLiterals.get(i)); + if (i < otherLiterals.size() - 1) { sb.append(", "); } } @@ -103,19 +110,16 @@ public String toString() { * @return */ public RuleGroundingOrder pushBack(int orderPosition) { - // TODO: this ignores positionLastVarBound for now (because it is not used anyway) - Literal[] reorderedOtherLiterals = new Literal[otherLiterals.length]; - int i = 0; - for (; i < orderPosition; i++) { - reorderedOtherLiterals[i] = otherLiterals[i]; + if (orderPosition >= stopBindingAtOrderPosition - 1) { + return null; } - for (; i < otherLiterals.length - 1; i++) { - reorderedOtherLiterals[i] = otherLiterals[i + 1]; - } - reorderedOtherLiterals[i] = otherLiterals[orderPosition]; - RuleGroundingOrder reorderedGroundingOrder = new RuleGroundingOrder(startingLiteral, reorderedOtherLiterals, positionLastVarBound); - reorderedGroundingOrder.pushedBackFrom = (this.pushedBackFrom >= 0 ? this.pushedBackFrom : otherLiterals.length) - 1; + RuleGroundingOrder reorderedGroundingOrder = new RuleGroundingOrder(this); + reorderedGroundingOrder.otherLiterals.add(otherLiterals.get(orderPosition)); return reorderedGroundingOrder; } + + public void considerUntilCurrentEnd() { + this.stopBindingAtOrderPosition = this.otherLiterals.size(); + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java index fc872c2f3..0e384eec0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java @@ -185,9 +185,9 @@ private void computeGroundingOrder(Literal startingLiteral) { position++; } if (fixedGroundingInstantiation) { - fixedGroundingOrder = new RuleGroundingOrder(null, literalsOrder.toArray(new Literal[0]), positionLastVarBound); + fixedGroundingOrder = new RuleGroundingOrder(null, literalsOrder, positionLastVarBound); } - groundingOrders.put(startingLiteral, new RuleGroundingOrder(startingLiteral, literalsOrder.toArray(new Literal[0]), positionLastVarBound)); + groundingOrders.put(startingLiteral, new RuleGroundingOrder(startingLiteral, literalsOrder, positionLastVarBound)); } private Literal selectNextGroundingLiteral(LinkedHashSet remainingLiterals, Set boundVariables) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 028b1e695..9bd3a4b39 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -27,7 +27,6 @@ import at.ac.tuwien.kr.alpha.common.*; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; -import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; @@ -124,18 +123,18 @@ public void groundConstraintAlreadyGround() { @Ignore("Currently, NaiveGrounder tries to escape this situation instead of throwing an exception") public void avoidDeadEndsWithLaxGrounderHeuristic() { RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), - new Literal[] {literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")}, -1); + Arrays.asList(literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")), -1); RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), - new Literal[] {literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")}, -1); + Arrays.asList(literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")), -1); testDeadEnd(groundingOrderP1, groundingOrderQ1, false); } @Test public void noDeadEndWithLaxGrounderHeuristic() { RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), - new Literal[] {literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")}, -1); + Arrays.asList(literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")), -1); RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), - new Literal[] {literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")}, -1); + Arrays.asList(literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")), -1); testDeadEnd(groundingOrderP1, groundingOrderQ1, true); } From 684ea80cde98079879cdc354b24bec1193acd576 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 22 Jan 2019 10:40:50 +0100 Subject: [PATCH 19/66] Log message if rule is grounded with unassigned positive body atoms --- .../at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 9d7e7656f..3bf59d12d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -421,8 +421,15 @@ public int register(NoGood noGood) { List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint()); - int remainingTolerance = tolerance >= 0 ? tolerance : Integer.MAX_VALUE; - return bindNextAtomInRule(rule, groundingOrder, orderPosition, new AtomicInteger(remainingTolerance), partialSubstitution, currentAssignment); + if (tolerance < 0) { + tolerance = Integer.MAX_VALUE; + } + AtomicInteger remainingTolerance = new AtomicInteger(tolerance); + List generatedSubstitutions = bindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + if (remainingTolerance.get() < tolerance && !generatedSubstitutions.isEmpty()) { + LOGGER.info("Grounded rule in which " + (tolerance - remainingTolerance.get()) + " positive atoms are still unassigned: " + rule); + } + return generatedSubstitutions; } private List advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { From 3570d8d7ca09377a3f6affc1bb0ecfebda334d3e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 22 Jan 2019 12:50:34 +0100 Subject: [PATCH 20/66] Fix handling of (remaining)tolerance in NaiveGrounder#bindNextAtomInRule --- .../kr/alpha/grounder/NaiveGrounder.java | 105 ++++++++++++------ 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 3bf59d12d..ff43f4a18 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -52,7 +52,6 @@ import static at.ac.tuwien.kr.alpha.Util.oops; import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; -import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; /** @@ -311,8 +310,8 @@ private HashMap bootstrap() { for (NonGroundRule nonGroundRule : fixedRules) { // Generate NoGoods for all rules that have a fixed grounding. RuleGroundingOrder groundingOrder = nonGroundRule.groundingOrder.getFixedGroundingOrder(); - List substitutions = bindNextAtomInRule(nonGroundRule, groundingOrder, 0, new Substitution(), null); - groundAndRegister(nonGroundRule, substitutions, groundNogoods); + BindingResult bindingResult = bindNextAtomInRule(nonGroundRule, groundingOrder, 0, new Substitution(), null); + groundAndRegister(nonGroundRule, bindingResult.generatedSubstitutions, groundNogoods); } fixedRules = null; @@ -355,7 +354,7 @@ public Map getNoGoods(Assignment currentAssignment) { continue; } - final List substitutions = bindNextAtomInRule( + final BindingResult bindingResult = bindNextAtomInRule( nonGroundRule, nonGroundRule.groundingOrder.orderStartingFrom(firstBindingAtom.startingLiteral), 0, @@ -363,7 +362,7 @@ public Map getNoGoods(Assignment currentAssignment) { currentAssignment ); - groundAndRegister(nonGroundRule, substitutions, newNoGoods); + groundAndRegister(nonGroundRule, bindingResult.generatedSubstitutions, newNoGoods); } } @@ -419,38 +418,42 @@ public int register(NoGood noGood) { return registry.register(noGood); } - List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { + BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint()); if (tolerance < 0) { tolerance = Integer.MAX_VALUE; } - AtomicInteger remainingTolerance = new AtomicInteger(tolerance); - List generatedSubstitutions = bindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); - if (remainingTolerance.get() < tolerance && !generatedSubstitutions.isEmpty()) { - LOGGER.info("Grounded rule in which " + (tolerance - remainingTolerance.get()) + " positive atoms are still unassigned: " + rule); + BindingResult bindingResult = bindNextAtomInRule(rule, groundingOrder, orderPosition, tolerance, tolerance, partialSubstitution, currentAssignment); + if (LOGGER.isDebugEnabled()) { + for (int i = 0; i < bindingResult.size(); i++) { + Integer numberOfUnassignedPositiveBodyAtoms = bindingResult.numbersOfUnassignedPositiveBodyAtoms.get(i); + if (numberOfUnassignedPositiveBodyAtoms > 0) { + LOGGER.debug("Grounded rule in which " + numberOfUnassignedPositiveBodyAtoms + " positive atoms are still unassigned: " + rule + " (substitution: " + bindingResult.generatedSubstitutions.get(i) + ")"); + } + } } - return generatedSubstitutions; + return bindingResult; } - private List advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { groundingOrder.considerUntilCurrentEnd(); - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - private List pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { RuleGroundingOrder modifiedGroundingOrder = groundingOrder.pushBack(orderPosition); if (modifiedGroundingOrder == null) { - return emptyList(); + return BindingResult.empty(); } - return bindNextAtomInRule(rule, modifiedGroundingOrder, orderPosition + 1, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, AtomicInteger remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = heuristicsConfiguration.isLax(rule.isConstraint()); Literal currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition); if (currentLiteral == null) { - return singletonList(partialSubstitution); + return BindingResult.singleton(partialSubstitution, originalTolerance - remainingTolerance); } Atom currentAtom = currentLiteral.getAtom(); @@ -459,20 +462,20 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO final List substitutions = ((FixedInterpretationLiteral)currentLiteral.substitute(partialSubstitution)).getSubstitutions(partialSubstitution); if (substitutions.isEmpty()) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - final List generatedSubstitutions = new ArrayList<>(); + final BindingResult bindingResult = new BindingResult(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. - generatedSubstitutions.addAll(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, new AtomicInteger(remainingTolerance.get()), substitution, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); } - return generatedSubstitutions; + return bindingResult; } if (currentAtom instanceof EnumerationAtom) { // Get the enumeration value and add it to the current partialSubstitution. ((EnumerationAtom) currentAtom).addEnumerationToSubstitution(partialSubstitution); - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } // check if partialVariableSubstitution already yields a ground atom @@ -482,7 +485,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { // Atom occurs negated in the rule: continue grounding - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() @@ -491,7 +494,7 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO LOGGER.debug("Not aborting binding of rule " + rule + " because lax grounder heuristic is active"); } else { // Generate no variable substitution. - return emptyList(); + return BindingResult.empty(); } } @@ -499,20 +502,20 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO final LinkedHashSet instances = factsFromProgram.get(substitute.getPredicate()); if (!(instances == null || !instances.contains(new Instance(substitute.getTerms())))) { // Ground literal holds, continue finding a variable substitution. - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } // Atom is not a fact already. - if (storeAtomAndTerminateIfAtomDoesNotHold(substitute, currentAssignment, new AtomicInteger(remainingTolerance.get()), laxGrounderHeuristic)) { - // TODO: this has to be refactored -- remainingTolerance is "cloned" because the same method calls happens again further below - return emptyList(); + if (storeAtomAndTerminateIfAtomDoesNotHold(substitute, currentAssignment, new AtomicInteger(remainingTolerance), laxGrounderHeuristic)) { + // TODO: this has to be refactored -- remainingTolerance is not modified here because the same method calls happens again further below + return BindingResult.empty(); } } // substituted atom contains variables if (currentLiteral.isNegated()) { if (laxGrounderHeuristic) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } else { throw oops("Current atom should be positive at this point but is not"); } @@ -543,10 +546,10 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding // under the presence of a lax grounder heuristic - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - ArrayList generatedSubstitutions = new ArrayList<>(); + BindingResult bindingResult = new BindingResult(); for (Instance instance : instances) { // Check each instance if it matches with the atom. Substitution unified = Substitution.unify(substitute, instance, new Substitution(partialSubstitution)); @@ -561,16 +564,16 @@ private List bindNextAtomInRule(NonGroundRule rule, RuleGroundingO throw oops("Grounded atom should be ground but is not"); } + AtomicInteger atomicRemainingTolerance = new AtomicInteger(remainingTolerance); // TODO: done this way for call-by-reference, should be refactored if (factsFromProgram.get(substitutedAtom.getPredicate()) == null || !factsFromProgram.get(substitutedAtom.getPredicate()).contains(new Instance(substitutedAtom.getTerms()))) { - if (storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, remainingTolerance, laxGrounderHeuristic)) { + if (storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, atomicRemainingTolerance, laxGrounderHeuristic)) { continue; } } - List boundSubstitutions = advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, remainingTolerance, unified, currentAssignment); - generatedSubstitutions.addAll(boundSubstitutions); + bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); } - return generatedSubstitutions; + return bindingResult; } private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance, boolean laxGrounderHeuristic) { @@ -705,4 +708,34 @@ private static class FirstBindingAtom { this.startingLiteral = startingLiteral; } } + + private static class BindingResult { + List generatedSubstitutions = new ArrayList<>(); + List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>(); + + void add(Substitution generatedSubstitution, int numberOfUnassignedPositiveBodyAtoms) { + this.generatedSubstitutions.add(generatedSubstitution); + this.numbersOfUnassignedPositiveBodyAtoms.add(numberOfUnassignedPositiveBodyAtoms); + } + + public void add(BindingResult otherBindingResult) { + this.generatedSubstitutions.addAll(otherBindingResult.generatedSubstitutions); + this.numbersOfUnassignedPositiveBodyAtoms.addAll(otherBindingResult.numbersOfUnassignedPositiveBodyAtoms); + } + + int size() { + return generatedSubstitutions.size(); + } + + static BindingResult empty() { + return new BindingResult(); + } + + static BindingResult singleton(Substitution generatedSubstitution, int numberOfUnassignedPositiveBodyAtoms) { + BindingResult bindingResult = new BindingResult(); + bindingResult.add(generatedSubstitution, numberOfUnassignedPositiveBodyAtoms); + return bindingResult; + } + + } } From 802877a1dc7aec8c6c5cd90d808c8f24d4c38be2 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 22 Jan 2019 12:54:27 +0100 Subject: [PATCH 21/66] Fix: Terminate binding if FixedInterpretationLiteral cannot be satisfied --- .../java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index ff43f4a18..1ef8f465f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -462,7 +462,8 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder final List substitutions = ((FixedInterpretationLiteral)currentLiteral.substitute(partialSubstitution)).getSubstitutions(partialSubstitution); if (substitutions.isEmpty()) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + // if FixedInterpretationLiteral cannot be satisfied now, it will never be + return BindingResult.empty(); } final BindingResult bindingResult = new BindingResult(); From b768f28f6b8161626ed770195a28f1176a3c9441 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 24 Jan 2019 18:58:21 +0100 Subject: [PATCH 22/66] Make GrounderHeuristicConfiguration configurable from CLI --- src/main/java/at/ac/tuwien/kr/alpha/Main.java | 21 ++++- .../kr/alpha/grounder/NaiveGrounder.java | 1 + .../GrounderHeuristicsConfiguration.java | 34 ++++++-- .../kr/alpha/grounder/NaiveGrounderTest.java | 2 +- .../GrounderHeuristicConfigurationTest.java | 84 +++++++++++++++++++ 5 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Main.java b/src/main/java/at/ac/tuwien/kr/alpha/Main.java index 9633984c4..edf12185e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Main.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Main.java @@ -95,6 +95,11 @@ public class Main { private static final String OPT_MOMS_STRATEGY = "momsStrategy"; private static final String DEFAULT_MOMS_STRATEGY = MOMs.Strategy.CountBinaryWatches.name(); + + private static final String OPT_GROUNDER_TOLERANCE_CONSTRAINTS = "grounderToleranceConstraints"; + private static final String DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS = GrounderHeuristicsConfiguration.STRICT_STRING; + private static final String OPT_GROUNDER_TOLERANCE_RULES = "grounderToleranceRules"; + private static final String DEFAULT_GROUNDER_TOLERANCE_RULES = GrounderHeuristicsConfiguration.STRICT_STRING; private static final String DEFAULT_GROUNDER = "naive"; private static final String DEFAULT_SOLVER = "default"; @@ -178,6 +183,16 @@ public static void main(String[] args) { momsStrategyOption.setArgName("strategy"); options.addOption(momsStrategyOption); + Option grounderToleranceConstraintsOption = new Option("gtc", OPT_GROUNDER_TOLERANCE_CONSTRAINTS, true, "grounder tolerance for constraints"); + grounderToleranceConstraintsOption.setArgs(1); + grounderToleranceConstraintsOption.setArgName("tolerance"); + options.addOption(grounderToleranceConstraintsOption); + + Option grounderToleranceRulesOption = new Option("gtr", OPT_GROUNDER_TOLERANCE_RULES, true, "grounder tolerance for rules"); + grounderToleranceRulesOption.setArgs(1); + grounderToleranceRulesOption.setArgName("tolerance"); + options.addOption(grounderToleranceRulesOption); + Option quietOption = new Option("q", OPT_QUIET, false, "do not print answer sets"); options.addOption(quietOption); @@ -243,8 +258,12 @@ public static void main(String[] args) { } final AtomStore atomStore = new AtomStoreImpl(); + final String optGrounder = commandLine.getOptionValue(OPT_GROUNDER, DEFAULT_GROUNDER); + final String optGrounderToleranceConstraints = commandLine.getOptionValue(OPT_GROUNDER_TOLERANCE_CONSTRAINTS, DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS); + final String optGrounderToleranceRules = commandLine.getOptionValue(OPT_GROUNDER_TOLERANCE_RULES, DEFAULT_GROUNDER_TOLERANCE_RULES); + final GrounderHeuristicsConfiguration grounderHeuristicConfiguration = GrounderHeuristicsConfiguration.getInstance(optGrounderToleranceConstraints, optGrounderToleranceRules); final Grounder grounder = GrounderFactory.getInstance( - commandLine.getOptionValue(OPT_GROUNDER, DEFAULT_GROUNDER), program, atomStore, filter, new GrounderHeuristicsConfiguration(), normalizationUseGrid, debugInternalChecks + optGrounder, program, atomStore, filter, grounderHeuristicConfiguration, normalizationUseGrid, debugInternalChecks ); // NOTE: Using time as seed is fine as the internal heuristics diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 1ef8f465f..61e53269e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -102,6 +102,7 @@ public NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsCon super(filter, bridges); this.atomStore = atomStore; this.heuristicsConfiguration = heuristicsConfiguration; + LOGGER.debug("Grounder configuration: " + heuristicsConfiguration); programAnalysis = new ProgramAnalysis(program); analyzeUnjustified = new AnalyzeUnjustified(programAnalysis, atomStore, factsFromProgram); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java index cf319dd51..fae106b41 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java @@ -48,6 +48,11 @@ * */ public class GrounderHeuristicsConfiguration { + + public static final String STRICT_STRING = "strict"; + public static final int STRICT_INT = 0; + public static final String LAX_STRING = "lax"; + public static final int LAX_INT = -1; private int toleranceConstraints; private int toleranceRules; @@ -95,19 +100,38 @@ public int getTolerance(boolean ruleIsConstraint) { * @return {@code true} iff the tolerance is not 0 */ public boolean isLax(boolean ruleIsConstraint) { - return getTolerance(ruleIsConstraint) != 0; + return getTolerance(ruleIsConstraint) != STRICT_INT; } public static GrounderHeuristicsConfiguration strict() { - return new GrounderHeuristicsConfiguration(0, 0); + return new GrounderHeuristicsConfiguration(STRICT_INT, STRICT_INT); } - public static GrounderHeuristicsConfiguration lax(int toleranceConstraints, int toleranceRules) { + public static GrounderHeuristicsConfiguration lax() { + return new GrounderHeuristicsConfiguration(LAX_INT, LAX_INT); + } + + public static GrounderHeuristicsConfiguration getInstance(int toleranceConstraints, int toleranceRules) { return new GrounderHeuristicsConfiguration(toleranceConstraints, toleranceRules); } + + public static GrounderHeuristicsConfiguration getInstance(String toleranceConstraints, String toleranceRules) { + return getInstance(parseTolerance(toleranceConstraints), parseTolerance(toleranceRules)); + } + + private static int parseTolerance(String tolerance) { + if (STRICT_STRING.equalsIgnoreCase(tolerance)) { + return STRICT_INT; + } else if (LAX_STRING.equalsIgnoreCase(tolerance)) { + return LAX_INT; + } else { + return Integer.parseInt(tolerance); + } + } - public static GrounderHeuristicsConfiguration lax() { - return new GrounderHeuristicsConfiguration(-1, -1); + @Override + public String toString() { + return this.getClass().getSimpleName() + "(toleranceConstraints=" + toleranceConstraints + ",toleranceRules=" + toleranceRules + ")"; } } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 9bd3a4b39..a46962fc2 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -247,7 +247,7 @@ public void testLaxGrounderHeuristicTolerance_2_reject() { private void testLaxGrounderHeuristicTolerance(Program program, int tolerance, int nTrueBs, boolean expectNoGoods) { AtomStore atomStore = new AtomStoreImpl(); TrailAssignment currentAssignment = new TrailAssignment(atomStore); - GrounderHeuristicsConfiguration heuristicConfiguration = GrounderHeuristicsConfiguration.lax(tolerance, tolerance); + GrounderHeuristicsConfiguration heuristicConfiguration = GrounderHeuristicsConfiguration.getInstance(tolerance, tolerance); NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, heuristicConfiguration, true); for (int i = 1; i <= nTrueBs; i++) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java new file mode 100644 index 000000000..566896b94 --- /dev/null +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2019 Siemens AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.grounder.heuristics; + +import org.junit.Test; + +import static at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration.LAX_STRING; +import static at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration.STRICT_STRING; +import static org.junit.Assert.*; + +/** + * Tests {@link GrounderHeuristicConfiguration} + */ +public class GrounderHeuristicConfigurationTest { + + @Test + public void testGetInstanceStrictStrict() { + GrounderHeuristicsConfiguration grounderHeuristicsConfiguration = GrounderHeuristicsConfiguration.getInstance(STRICT_STRING, STRICT_STRING); + assertFalse(grounderHeuristicsConfiguration.isLax(true)); + assertFalse(grounderHeuristicsConfiguration.isLax(false)); + assertEquals(0, grounderHeuristicsConfiguration.getToleranceConstraints()); + assertEquals(0, grounderHeuristicsConfiguration.getToleranceRules()); + } + + @Test + public void testGetInstanceStrictLax() { + GrounderHeuristicsConfiguration grounderHeuristicsConfiguration = GrounderHeuristicsConfiguration.getInstance(STRICT_STRING, LAX_STRING); + assertFalse(grounderHeuristicsConfiguration.isLax(true)); + assertTrue(grounderHeuristicsConfiguration.isLax(false)); + assertEquals(0, grounderHeuristicsConfiguration.getToleranceConstraints()); + assertEquals(-1, grounderHeuristicsConfiguration.getToleranceRules()); + } + + @Test + public void testGetInstanceLaxStrict() { + GrounderHeuristicsConfiguration grounderHeuristicsConfiguration = GrounderHeuristicsConfiguration.getInstance(LAX_STRING, STRICT_STRING); + assertTrue(grounderHeuristicsConfiguration.isLax(true)); + assertFalse(grounderHeuristicsConfiguration.isLax(false)); + assertEquals(-1, grounderHeuristicsConfiguration.getToleranceConstraints()); + assertEquals(0, grounderHeuristicsConfiguration.getToleranceRules()); + } + + @Test + public void testGetInstanceLaxLax() { + GrounderHeuristicsConfiguration grounderHeuristicsConfiguration = GrounderHeuristicsConfiguration.getInstance(LAX_STRING, LAX_STRING); + assertTrue(grounderHeuristicsConfiguration.isLax(true)); + assertTrue(grounderHeuristicsConfiguration.isLax(false)); + assertEquals(-1, grounderHeuristicsConfiguration.getToleranceConstraints()); + assertEquals(-1, grounderHeuristicsConfiguration.getToleranceRules()); + } + + @Test + public void testGetInstanceIntInt() { + GrounderHeuristicsConfiguration grounderHeuristicsConfiguration = GrounderHeuristicsConfiguration.getInstance(5, 1); + assertTrue(grounderHeuristicsConfiguration.isLax(true)); + assertTrue(grounderHeuristicsConfiguration.isLax(false)); + assertEquals(5, grounderHeuristicsConfiguration.getToleranceConstraints()); + assertEquals(1, grounderHeuristicsConfiguration.getToleranceRules()); + } + +} From 02fdb194c19b5235c452ae70c6ec6d5b4768b060 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 1 Feb 2019 18:52:56 +0100 Subject: [PATCH 23/66] Fix merge --- .../kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java index d9f02c743..7e346a90b 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java @@ -183,7 +183,7 @@ public void justifyNegatedFactsRemovedFromReasons() { ":- not p(5)."; Program parsedProgram = parser.parse(program); AtomStore atomStore = new AtomStoreImpl(); - NaiveGrounder grounder = new NaiveGrounder(parsedProgram, atomStore); + NaiveGrounder grounder = new NaiveGrounder(parsedProgram, atomStore, true); grounder.getNoGoods(null); TrailAssignment assignment = new TrailAssignment(atomStore); int rId = atomStore.get(new BasicAtom(Predicate.getInstance("r", 0))); From df714e5722e57515ad14c537b2c65319108dbf33 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 11 Feb 2019 18:47:17 +0100 Subject: [PATCH 24/66] workaround for bug causing non-satisfiable comparison literals to be left in grounded rules --- .../alpha/common/atoms/ComparisonLiteral.java | 8 ++++++- .../kr/alpha/grounder/NaiveGrounder.java | 23 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonLiteral.java index c0c908ace..dae14827c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonLiteral.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017-2018, the Alpha Team. + * Copyright (c) 2017-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -173,6 +173,12 @@ public List getSubstitutions(Substitution partialSubstitution) { extendedSubstitution.put(variable, resultTerm); return Collections.singletonList(extendedSubstitution); } + + public boolean isLeftOrRightAssigning() { + final Term left = getTerms().get(0); + final Term right = getTerms().get(1); + return isNormalizedEquality && (assignable(left) && right.isGround() || assignable(right) && left.isGround()); + } private Term evaluateTerm(Term term) { // Evaluate arithmetics. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 9305bf77c..8e73ece0f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -29,13 +29,11 @@ import at.ac.tuwien.kr.alpha.common.*; import at.ac.tuwien.kr.alpha.common.NoGood.Type; -import at.ac.tuwien.kr.alpha.common.atoms.Atom; -import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; -import at.ac.tuwien.kr.alpha.common.atoms.FixedInterpretationLiteral; -import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.common.atoms.*; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.atoms.ChoiceAtom; import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationAtom; +import at.ac.tuwien.kr.alpha.grounder.atoms.IntervalLiteral; import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import at.ac.tuwien.kr.alpha.grounder.bridges.Bridge; import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; @@ -148,6 +146,7 @@ private void initializeFactsAndRules(Program program) { // Record the rule for later use NonGroundRule nonGroundRule = NonGroundRule.constructNonGroundRule(rule); knownNonGroundRules.put(nonGroundRule.getRuleId(), nonGroundRule); + LOGGER.debug("NonGroundRule #" + nonGroundRule.getRuleId() + ": " + nonGroundRule); // Record defining rules for each predicate. Atom headAtom = nonGroundRule.getHeadAtom(); @@ -419,6 +418,9 @@ public int register(NoGood noGood) { return registry.register(noGood); } + /** + * TODO: always called with orderPosition = 0 --> remove parameter + */ BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint()); if (tolerance < 0) { @@ -435,7 +437,7 @@ BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundin } return bindingResult; } - + private BindingResult advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { groundingOrder.considerUntilCurrentEnd(); return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); @@ -460,7 +462,16 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder Atom currentAtom = currentLiteral.getAtom(); if (currentLiteral instanceof FixedInterpretationLiteral) { // Generate all substitutions for the builtin/external/interval atom. - final List substitutions = ((FixedInterpretationLiteral)currentLiteral.substitute(partialSubstitution)).getSubstitutions(partialSubstitution); + FixedInterpretationLiteral substitutedLiteral = (FixedInterpretationLiteral)currentLiteral.substitute(partialSubstitution); + // TODO: this has to be improved before merging into master: + if (!substitutedLiteral.isGround() && + !(substitutedLiteral instanceof ComparisonLiteral && ((ComparisonLiteral)substitutedLiteral).isLeftOrRightAssigning()) && + !(substitutedLiteral instanceof IntervalLiteral && substitutedLiteral.getTerms().get(0).isGround()) && + !(substitutedLiteral instanceof ExternalLiteral) + ) { + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + } + final List substitutions = substitutedLiteral.getSubstitutions(partialSubstitution); if (substitutions.isEmpty()) { // if FixedInterpretationLiteral cannot be satisfied now, it will never be From 84d86b3437841cc2007bd09863e3aaa4bc3d8dac Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 4 Mar 2019 17:21:20 +0100 Subject: [PATCH 25/66] Statistics on numbers of NoGoods by type and cardinality Contributes to #113 --- .../tuwien/kr/alpha/solver/DefaultSolver.java | 7 +- .../kr/alpha/solver/NaiveNoGoodStore.java | 34 +++++ .../tuwien/kr/alpha/solver/NoGoodCounter.java | 119 ++++++++++++++++++ .../tuwien/kr/alpha/solver/NoGoodStore.java | 2 + .../alpha/solver/NoGoodStoreAlphaRoaming.java | 14 ++- 5 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java index 32dc6844e..1e0671080 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -55,7 +55,7 @@ /** * The new default solver employed in Alpha. - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. */ public class DefaultSolver extends AbstractSolver implements SolverMaintainingStatistics { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSolver.class); @@ -552,6 +552,9 @@ private void logStats() { LOGGER.info(heuristicToDecisionCounter.getKey() + ": " + heuristicToDecisionCounter.getValue()); } } + NoGoodCounter noGoodCounter = store.getNoGoodCounter(); + LOGGER.info("Number of NoGoods by type: " + noGoodCounter.getStatsByType()); + LOGGER.info("Number of NoGoods by cardinality: " + noGoodCounter.getStatsByCardinality()); } } } \ No newline at end of file diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStore.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStore.java index eeb2b9ef9..9ab2c6951 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStore.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStore.java @@ -1,3 +1,30 @@ +/** + * Copyright (c) 2017-2019, the Alpha Team. + * All rights reserved. + * + * Additional changes made by Siemens. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.solver; import at.ac.tuwien.kr.alpha.common.NoGood; @@ -14,6 +41,7 @@ public class NaiveNoGoodStore implements NoGoodStore { private HashMap delegate = new HashMap<>(); private final WritableAssignment assignment; + private final NoGoodCounter counter = new NoGoodCounter(); private boolean hasInferredAssignments; @@ -28,6 +56,7 @@ void clear() { @Override public ConflictCause add(int id, NoGood noGood) { + counter.count(noGood); if (assignment.violates(noGood)) { return new ConflictCause(noGood); } @@ -98,6 +127,11 @@ public void backtrack() { @Override public void growForMaxAtomId(int maxAtomId) { } + + @Override + public NoGoodCounter getNoGoodCounter() { + return counter; + } /** * Infer an assignment from a nogood if it is weakly unit. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java new file mode 100644 index 000000000..c477a47b8 --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2019 Siemens AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.solver; + +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.NoGood.Type; + +/** + * Maintains statistics on numbers of various types of {@link NoGood}s. + */ +public class NoGoodCounter { + + private static final int CARD_NARY = 0; + private static final int CARD_UNARY = 1; + private static final int CARD_BINARY = 2; + + private int[] countByType = new int[NoGood.Type.values().length]; + private int[] countByCardinality = new int[3]; + + /** + * Increases counters for the types of the given NoGood + * @param noGood + */ + void count(NoGood noGood) { + countByType[noGood.getType().ordinal()]++; + countByCardinality[getAbstractCardinality(noGood)]++; + } + + private int getAbstractCardinality(NoGood noGood) { + if (noGood.isUnary()) { + return CARD_UNARY; + } + if (noGood.isBinary()) { + return CARD_BINARY; + } + return CARD_NARY; + } + + /** + * @param type + * @return the number of nogoods of the given type + */ + public int getNumberOfNoGoods(NoGood.Type type) { + return countByType[type.ordinal()]; + } + + /** + * @return the number of unary nogoods + */ + public int getNumberOfUnaryNoGoods() { + return countByCardinality[CARD_UNARY]; + } + + /** + * @return the number of binary nogoods + */ + public int getNumberOfBinaryNoGoods() { + return countByCardinality[CARD_BINARY]; + } + + /** + * @return the number of nogoods that are neither unary nor binary + */ + public int getNumberOfNAryNoGoods() { + return countByCardinality[CARD_NARY]; + } + + /** + * @return {@code true} iff there is at least one binary nogood + */ + public boolean hasBinaryNoGoods() { + return countByCardinality[CARD_BINARY] > 0; + } + + /** + * @return a string giving statistics on numbers of nogoods by type + */ + public String getStatsByType() { + StringBuilder sb = new StringBuilder(); + for (Type type : NoGood.Type.values()) { + sb.append(type.name()); + sb.append(": "); + sb.append(countByType[type.ordinal()]); + sb.append(" "); + } + return sb.toString(); + } + + /** + * @return a string giving statistics on numbers of nogoods by cardinality + */ + public String getStatsByCardinality() { + return "unary: " + getNumberOfUnaryNoGoods() + " binary: " + getNumberOfBinaryNoGoods() + " larger: " + getNumberOfNAryNoGoods(); + } + +} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStore.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStore.java index 30fc4d260..58f5456b0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStore.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStore.java @@ -35,4 +35,6 @@ public interface NoGoodStore { void backtrack(); void growForMaxAtomId(int maxAtomId); + + NoGoodCounter getNoGoodCounter(); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoaming.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoaming.java index ec87da9dc..8a13b839c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoaming.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoaming.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2017, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -68,7 +68,8 @@ public class NoGoodStoreAlphaRoaming implements NoGoodStorePrivilegingBinaryNoGo private boolean checksEnabled; private boolean didPropagate; - private boolean hasBinaryNoGoods; + + private final NoGoodCounter counter = new NoGoodCounter(); public NoGoodStoreAlphaRoaming(WritableAssignment assignment, boolean checksEnabled) { this.assignment = assignment; @@ -126,6 +127,11 @@ public void growForMaxAtomId(int maxAtomId) { } this.maxAtomId = maxAtomId; } + + @Override + public NoGoodCounter getNoGoodCounter() { + return counter; + } private ArrayList watches(int literal) { return watches[literal]; @@ -148,6 +154,7 @@ private void addAlphaWatch(WatchedNoGood wng) { @Override public ConflictCause add(int id, NoGood noGood) { LOGGER.trace("Adding {}", noGood); + counter.count(noGood); if (noGood.isUnary()) { return addUnary(noGood); @@ -360,13 +367,12 @@ private ConflictCause addAndWatchBinary(final NoGood noGood) { if (conflictCause != null) { return conflictCause; } - hasBinaryNoGoods = true; return binaryWatches[b].add(noGood); } @Override public boolean hasBinaryNoGoods() { - return hasBinaryNoGoods; + return counter.hasBinaryNoGoods(); } private ConflictCause assignWeakComplement(final int literalIndex, final NoGoodInterface impliedBy, int decisionLevel) { From fede0666360fde626a04a05cd9659a667756f017 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 12 Mar 2019 23:11:14 +0100 Subject: [PATCH 26/66] Avoid debug message if debug not enabled --- .../java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 8e73ece0f..fbb597614 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -504,7 +504,9 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() && !workingMemory.get(currentAtom.getPredicate(), true).containsInstance(new Instance(substitute.getTerms()))) { if (laxGrounderHeuristic) { - LOGGER.debug("Not aborting binding of rule " + rule + " because lax grounder heuristic is active"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Not aborting binding of rule " + rule + " because lax grounder heuristic is active"); + } } else { // Generate no variable substitution. return BindingResult.empty(); From 3b85a8eb9d135f30b74dfbb35658a06bd658665d Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 13 Mar 2019 13:44:40 +0100 Subject: [PATCH 27/66] Reintroduce CLI parameters for grounding strategies lost during merge 6c7a49b4df62601baca8b2e6e09b9a1dc1c21410 --- .../java/at/ac/tuwien/kr/alpha/Alpha.java | 6 ++++- .../kr/alpha/config/CommandLineParser.java | 22 +++++++++++++++++++ .../tuwien/kr/alpha/config/SystemConfig.java | 21 ++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java b/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java index 439510106..94feca6e7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java @@ -36,6 +36,7 @@ import at.ac.tuwien.kr.alpha.config.SystemConfig; import at.ac.tuwien.kr.alpha.grounder.Grounder; import at.ac.tuwien.kr.alpha.grounder.GrounderFactory; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import at.ac.tuwien.kr.alpha.solver.Solver; import at.ac.tuwien.kr.alpha.solver.SolverFactory; @@ -115,9 +116,12 @@ public Solver prepareSolverFor(Program program) { HeuristicsConfigurationBuilder heuristicsConfigurationBuilder = HeuristicsConfiguration.builder(); heuristicsConfigurationBuilder.setHeuristic(this.config.getBranchingHeuristic()); heuristicsConfigurationBuilder.setMomsStrategy(this.config.getMomsStrategy()); + + GrounderHeuristicsConfiguration grounderHeuristicConfiguration = GrounderHeuristicsConfiguration + .getInstance(this.config.getGrounderToleranceConstraints(), this.config.getGrounderToleranceRules()); AtomStore atomStore = new AtomStoreImpl(); - Grounder grounder = GrounderFactory.getInstance(grounderName, program, atomStore, doDebugChecks); + Grounder grounder = GrounderFactory.getInstance(grounderName, program, atomStore, grounderHeuristicConfiguration, doDebugChecks); Solver solver = SolverFactory.getInstance(solverName, nogoodStoreName, atomStore, grounder, new Random(seed), heuristicsConfigurationBuilder.build(), doDebugChecks, disableJustificationSearch); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java b/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java index 260b7c100..bb6511171 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java @@ -100,6 +100,14 @@ public class CommandLineParser { private static final Option OPT_NORMALIZATION_GRID = Option.builder("ng").longOpt("normalizationCountingGrid") .desc("use counting grid normalization instead of sorting circuit for #count (default: " + SystemConfig.DEFAULT_USE_NORMALIZATION_GRID + ")") .build(); + private static final Option OPT_GROUNDER_TOLERANCE_CONSTRAINTS = Option.builder("gtc").longOpt("grounderToleranceConstraints") + .desc("grounder tolerance for constraints (default: " + SystemConfig.DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS + ")") + .hasArg().argName("tolerance") + .build(); + private static final Option OPT_GROUNDER_TOLERANCE_RULES = Option.builder("gtr").longOpt("grounderToleranceRules") + .desc("grounder tolerance for rules (default: " + SystemConfig.DEFAULT_GROUNDER_TOLERANCE_RULES + ")") + .hasArg().argName("tolerance") + .build(); private static final Options CLI_OPTS = new Options(); @@ -128,6 +136,8 @@ public class CommandLineParser { CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_STATS); CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_JUSTIFICATION); CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NORMALIZATION_GRID); + CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_GROUNDER_TOLERANCE_CONSTRAINTS); + CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_GROUNDER_TOLERANCE_RULES); } /* @@ -169,6 +179,8 @@ public CommandLineParser(String cmdLineSyntax, Consumer abortAction) { this.globalOptionHandlers.put(CommandLineParser.OPT_STATS.getOpt(), this::handleStats); this.globalOptionHandlers.put(CommandLineParser.OPT_NO_JUSTIFICATION.getOpt(), this::handleNoJustification); this.globalOptionHandlers.put(CommandLineParser.OPT_NORMALIZATION_GRID.getOpt(), this::handleNormalizationGrid); + this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_TOLERANCE_CONSTRAINTS.getOpt(), this::handleGrounderToleranceConstraints); + this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_TOLERANCE_RULES.getOpt(), this::handleGrounderToleranceRules); this.inputOptionHandlers.put(CommandLineParser.OPT_NUM_ANSWER_SETS.getOpt(), this::handleNumAnswerSets); this.inputOptionHandlers.put(CommandLineParser.OPT_INPUT.getOpt(), this::handleInput); @@ -335,5 +347,15 @@ private void handleNoJustification(Option opt, SystemConfig cfg) { private void handleNormalizationGrid(Option opt, SystemConfig cfg) { cfg.setUseNormalizationGrid(true); } + + private void handleGrounderToleranceConstraints(Option opt, SystemConfig cfg) { + String grounderToleranceConstraints = opt.getValue(SystemConfig.DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS); + cfg.setGrounderToleranceConstraints(grounderToleranceConstraints); + } + + private void handleGrounderToleranceRules(Option opt, SystemConfig cfg) { + String grounderToleranceRules = opt.getValue(SystemConfig.DEFAULT_GROUNDER_TOLERANCE_RULES); + cfg.setGrounderToleranceRules(grounderToleranceRules); + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java index ea3472dec..7ee0a29d5 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java @@ -27,6 +27,7 @@ */ package at.ac.tuwien.kr.alpha.config; +import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration; import at.ac.tuwien.kr.alpha.solver.heuristics.BranchingHeuristicFactory.Heuristic; import at.ac.tuwien.kr.alpha.solver.heuristics.MOMs; @@ -49,6 +50,8 @@ public class SystemConfig { public static final boolean DEFAULT_DEBUG_INTERNAL_CHECKS = false; public static final boolean DEFAULT_USE_NORMALIZATION_GRID = false; public static final boolean DEFAULT_SORT_ANSWER_SETS = false; + public static final String DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS = GrounderHeuristicsConfiguration.STRICT_STRING; + public static final String DEFAULT_GROUNDER_TOLERANCE_RULES = GrounderHeuristicsConfiguration.STRICT_STRING; private String grounderName = SystemConfig.DEFAULT_GROUNDER_NAME; private String solverName = SystemConfig.DEFAULT_SOLVER_NAME; @@ -63,6 +66,8 @@ public class SystemConfig { private boolean disableJustificationSearch = SystemConfig.DEFAULT_DISABLE_JUSTIFICATION_SEARCH; private boolean useNormalizationGrid = SystemConfig.DEFAULT_USE_NORMALIZATION_GRID; private boolean sortAnswerSets = SystemConfig.DEFAULT_SORT_ANSWER_SETS; + private String grounderToleranceConstraints = DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS; + private String grounderToleranceRules = DEFAULT_GROUNDER_TOLERANCE_RULES; public String getGrounderName() { return this.grounderName; @@ -176,4 +181,20 @@ public void setSortAnswerSets(boolean sortAnswerSets) { this.sortAnswerSets = sortAnswerSets; } + public String getGrounderToleranceConstraints() { + return grounderToleranceConstraints; + } + + public void setGrounderToleranceConstraints(String grounderToleranceConstraints) { + this.grounderToleranceConstraints = grounderToleranceConstraints; + } + + public String getGrounderToleranceRules() { + return grounderToleranceRules; + } + + public void setGrounderToleranceRules(String grounderToleranceRules) { + this.grounderToleranceRules = grounderToleranceRules; + } + } From 9a7c175402d3e73eab4ab67273fa25c9ec89945c Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 27 Mar 2019 13:28:36 +0100 Subject: [PATCH 28/66] Extend decision counters when heuristic is added to chain --- .../heuristics/ChainedBranchingHeuristics.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java index 69eabb511..49f5036d0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java @@ -1,17 +1,17 @@ /** * Copyright (c) 2018 Siemens AG * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1) Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. - * + * * 2) 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. - * + * * 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 @@ -28,10 +28,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import static at.ac.tuwien.kr.alpha.Util.oops; @@ -89,6 +86,9 @@ public void add(BranchingHeuristic element) { throw oops("Cycle detected in chain of branching heuristics"); } chain.add(element); + if (decisionCounters != null) { + decisionCounters = Arrays.copyOf(decisionCounters, decisionCounters.length + 1); + } } public static ChainedBranchingHeuristics chainOf(BranchingHeuristic... branchingHeuristics) { From 2edc783c36033a65891f782372b1ef97c478fbb7 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 15 Apr 2019 16:25:50 +0200 Subject: [PATCH 29/66] remove obsolete TODO --- src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index fbb597614..293722d29 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -78,7 +78,7 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean useCountingGridNormalization; private boolean debugInternalChecks; - private GrounderHeuristicsConfiguration heuristicsConfiguration; // TODO: make configurable from CLI + private GrounderHeuristicsConfiguration heuristicsConfiguration; /** * If this configuration parameter is {@code true} (which it is by default), From 7c11a3686cc8ca10ca14e8a10ad25bb21c403a84 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 09:12:14 +0200 Subject: [PATCH 30/66] Minor simplifications in NaiveGrounder --- .../kr/alpha/grounder/NaiveGrounder.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index eecaafcc6..54872088f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -310,7 +310,7 @@ private HashMap bootstrap() { for (NonGroundRule nonGroundRule : fixedRules) { // Generate NoGoods for all rules that have a fixed grounding. RuleGroundingOrder groundingOrder = nonGroundRule.groundingOrder.getFixedGroundingOrder(); - BindingResult bindingResult = bindNextAtomInRule(nonGroundRule, groundingOrder, 0, new Substitution(), null); + BindingResult bindingResult = bindNextAtomInRule(nonGroundRule, groundingOrder, new Substitution(), null); groundAndRegister(nonGroundRule, bindingResult.generatedSubstitutions, groundNogoods); } @@ -357,7 +357,6 @@ public Map getNoGoods(Assignment currentAssignment) { final BindingResult bindingResult = bindNextAtomInRule( nonGroundRule, nonGroundRule.groundingOrder.orderStartingFrom(firstBindingAtom.startingLiteral), - 0, unifier, currentAssignment ); @@ -418,15 +417,12 @@ public int register(NoGood noGood) { return registry.register(noGood); } - /** - * TODO: always called with orderPosition = 0 --> remove parameter - */ - BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, Substitution partialSubstitution, Assignment currentAssignment) { + BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, Substitution partialSubstitution, Assignment currentAssignment) { int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint()); if (tolerance < 0) { tolerance = Integer.MAX_VALUE; } - BindingResult bindingResult = bindNextAtomInRule(rule, groundingOrder, orderPosition, tolerance, tolerance, partialSubstitution, currentAssignment); + BindingResult bindingResult = bindNextAtomInRule(rule, groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); if (LOGGER.isDebugEnabled()) { for (int i = 0; i < bindingResult.size(); i++) { Integer numberOfUnassignedPositiveBodyAtoms = bindingResult.numbersOfUnassignedPositiveBodyAtoms.get(i); @@ -452,7 +448,7 @@ private BindingResult pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGrou } private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { - boolean laxGrounderHeuristic = heuristicsConfiguration.isLax(rule.isConstraint()); + boolean laxGrounderHeuristic = originalTolerance > 0; Literal currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition); if (currentLiteral == null) { @@ -521,7 +517,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder } // Atom is not a fact already. - if (storeAtomAndTerminateIfAtomDoesNotHold(substitute, currentAssignment, new AtomicInteger(remainingTolerance), laxGrounderHeuristic)) { + if (storeAtomAndTerminateIfAtomDoesNotHold(substitute, currentAssignment, new AtomicInteger(remainingTolerance))) { // TODO: this has to be refactored -- remainingTolerance is not modified here because the same method calls happens again further below return BindingResult.empty(); } @@ -581,7 +577,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder AtomicInteger atomicRemainingTolerance = new AtomicInteger(remainingTolerance); // TODO: done this way for call-by-reference, should be refactored if (factsFromProgram.get(substitutedAtom.getPredicate()) == null || !factsFromProgram.get(substitutedAtom.getPredicate()).contains(new Instance(substitutedAtom.getTerms()))) { - if (storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, atomicRemainingTolerance, laxGrounderHeuristic)) { + if (storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, atomicRemainingTolerance)) { continue; } } @@ -591,7 +587,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder return bindingResult; } - private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance, boolean laxGrounderHeuristic) { + private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance) { final int atomId = atomStore.putIfAbsent(substitute); if (currentAssignment != null) { From b93ae0fb278080351f58401b708d3c53c4098f56 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 09:34:38 +0200 Subject: [PATCH 31/66] Refactoring NaiveGrounder --- .../kr/alpha/grounder/NaiveGrounder.java | 36 +++++++++---------- .../kr/alpha/grounder/RuleGroundingOrder.java | 14 +++++--- .../alpha/grounder/RuleGroundingOrders.java | 4 +-- .../kr/alpha/grounder/NaiveGrounderTest.java | 8 ++--- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 54872088f..7a8d74309 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -422,7 +422,7 @@ BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundin if (tolerance < 0) { tolerance = Integer.MAX_VALUE; } - BindingResult bindingResult = bindNextAtomInRule(rule, groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); + BindingResult bindingResult = bindNextAtomInRule(groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); if (LOGGER.isDebugEnabled()) { for (int i = 0; i < bindingResult.size(); i++) { Integer numberOfUnassignedPositiveBodyAtoms = bindingResult.numbersOfUnassignedPositiveBodyAtoms.get(i); @@ -434,20 +434,20 @@ BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundin return bindingResult; } - private BindingResult advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult advanceAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { groundingOrder.considerUntilCurrentEnd(); - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - private BindingResult pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult pushBackAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { RuleGroundingOrder modifiedGroundingOrder = groundingOrder.pushBack(orderPosition); if (modifiedGroundingOrder == null) { return BindingResult.empty(); } - return bindNextAtomInRule(rule, modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = originalTolerance > 0; Literal currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition); @@ -465,7 +465,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder !(substitutedLiteral instanceof IntervalLiteral && substitutedLiteral.getTerms().get(0).isGround()) && !(substitutedLiteral instanceof ExternalLiteral) ) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } final List substitutions = substitutedLiteral.getSubstitutions(partialSubstitution); @@ -477,14 +477,14 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder final BindingResult bindingResult = new BindingResult(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. - bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); } return bindingResult; } if (currentAtom instanceof EnumerationAtom) { // Get the enumeration value and add it to the current partialSubstitution. ((EnumerationAtom) currentAtom).addEnumerationToSubstitution(partialSubstitution); - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } // check if partialVariableSubstitution already yields a ground atom @@ -494,16 +494,12 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { // Atom occurs negated in the rule: continue grounding - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - if (stopBindingAtNonTruePositiveBody && !rule.getRule().isGround() + if (stopBindingAtNonTruePositiveBody && !groundingOrder.isGround() && !workingMemory.get(currentAtom.getPredicate(), true).containsInstance(new Instance(substitute.getTerms()))) { - if (laxGrounderHeuristic) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Not aborting binding of rule " + rule + " because lax grounder heuristic is active"); - } - } else { + if (!laxGrounderHeuristic) { // Generate no variable substitution. return BindingResult.empty(); } @@ -513,7 +509,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder final LinkedHashSet instances = factsFromProgram.get(substitute.getPredicate()); if (!(instances == null || !instances.contains(new Instance(substitute.getTerms())))) { // Ground literal holds, continue finding a variable substitution. - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } // Atom is not a fact already. @@ -526,7 +522,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder // substituted atom contains variables if (currentLiteral.isNegated()) { if (laxGrounderHeuristic) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } else { throw oops("Current atom should be positive at this point but is not"); } @@ -557,7 +553,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding // under the presence of a lax grounder heuristic - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } BindingResult bindingResult = new BindingResult(); @@ -581,7 +577,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder continue; } } - bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); } return bindingResult; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java index e42c1de13..a17a0103f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java @@ -34,22 +34,24 @@ * A grounding order computed by {@link RuleGroundingOrders} for a specific {@link NonGroundRule} and a specific starting literal. */ public class RuleGroundingOrder { - + private Literal startingLiteral; private List otherLiterals; private int positionLastVarBound; private int stopBindingAtOrderPosition; + private final boolean ground; - RuleGroundingOrder(Literal startingLiteral, List otherLiterals, int positionLastVarBound) { + RuleGroundingOrder(Literal startingLiteral, List otherLiterals, int positionLastVarBound, boolean isGround) { super(); this.startingLiteral = startingLiteral; this.otherLiterals = otherLiterals; this.positionLastVarBound = positionLastVarBound; this.stopBindingAtOrderPosition = otherLiterals.size(); + this.ground = isGround; } private RuleGroundingOrder(RuleGroundingOrder otherRuleGroundingOrder) { - this(otherRuleGroundingOrder.startingLiteral, new ArrayList<>(otherRuleGroundingOrder.otherLiterals), otherRuleGroundingOrder.positionLastVarBound); + this(otherRuleGroundingOrder.startingLiteral, new ArrayList<>(otherRuleGroundingOrder.otherLiterals), otherRuleGroundingOrder.positionLastVarBound, otherRuleGroundingOrder.ground); this.stopBindingAtOrderPosition = otherRuleGroundingOrder.stopBindingAtOrderPosition; } @@ -83,7 +85,11 @@ public Literal getLiteralAtOrderPosition(int orderPosition) { public int getPositionFromWhichAllVarsAreBound() { return positionLastVarBound + 1; } - + + public boolean isGround() { + return ground; + } + /* (non-Javadoc) * @see java.lang.Object#toString() */ diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java index 0e384eec0..50fe9bada 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrders.java @@ -185,9 +185,9 @@ private void computeGroundingOrder(Literal startingLiteral) { position++; } if (fixedGroundingInstantiation) { - fixedGroundingOrder = new RuleGroundingOrder(null, literalsOrder, positionLastVarBound); + fixedGroundingOrder = new RuleGroundingOrder(null, literalsOrder, positionLastVarBound, nonGroundRule.getRule().isGround()); } - groundingOrders.put(startingLiteral, new RuleGroundingOrder(startingLiteral, literalsOrder, positionLastVarBound)); + groundingOrders.put(startingLiteral, new RuleGroundingOrder(startingLiteral, literalsOrder, positionLastVarBound, nonGroundRule.getRule().isGround())); } private Literal selectNextGroundingLiteral(LinkedHashSet remainingLiterals, Set boundVariables) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index a46962fc2..89e608f7d 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -123,18 +123,18 @@ public void groundConstraintAlreadyGround() { @Ignore("Currently, NaiveGrounder tries to escape this situation instead of throwing an exception") public void avoidDeadEndsWithLaxGrounderHeuristic() { RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), - Arrays.asList(literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")), -1); + Arrays.asList(literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")), -1, false); RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), - Arrays.asList(literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")), -1); + Arrays.asList(literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")), -1, false); testDeadEnd(groundingOrderP1, groundingOrderQ1, false); } @Test public void noDeadEndWithLaxGrounderHeuristic() { RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), - Arrays.asList(literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")), -1); + Arrays.asList(literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")), -1, false); RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), - Arrays.asList(literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")), -1); + Arrays.asList(literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")), -1, false); testDeadEnd(groundingOrderP1, groundingOrderQ1, true); } From 548760a78a27a0b8df49ab7cfb3ee1dd41632ab5 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 09:36:34 +0200 Subject: [PATCH 32/66] Refactoring NaiveGrounder --- .../tuwien/kr/alpha/grounder/NaiveGrounder.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 7a8d74309..e9ab3fef3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -79,14 +79,6 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean debugInternalChecks; private GrounderHeuristicsConfiguration heuristicsConfiguration; - - /** - * If this configuration parameter is {@code true} (which it is by default), - * the grounder stops grounding a rule if it contains a positive body atom which is not - * yet true, except if the whole rule is already ground. Is currently used only internally, - * but might be used for grounder heuristics and also set from the outside in the future. - */ - private boolean stopBindingAtNonTruePositiveBody = true; public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternalChecks, Bridge... bridges) { this(program, atomStore, new GrounderHeuristicsConfiguration(), debugInternalChecks, bridges); @@ -497,12 +489,10 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - if (stopBindingAtNonTruePositiveBody && !groundingOrder.isGround() + if (!groundingOrder.isGround() && remainingTolerance <= 0 && !workingMemory.get(currentAtom.getPredicate(), true).containsInstance(new Instance(substitute.getTerms()))) { - if (!laxGrounderHeuristic) { - // Generate no variable substitution. - return BindingResult.empty(); - } + // Generate no variable substitution. + return BindingResult.empty(); } // Check if atom is also assigned true. From b26aa81584ca98546c2f9bcaf1083cbd25d886fb Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 09:45:18 +0200 Subject: [PATCH 33/66] Refactoring NaiveGrounder as suggested by IntelliJ IDEA's code inspection --- .../kr/alpha/grounder/NaiveGrounder.java | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index e9ab3fef3..4903f08f0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -75,16 +75,16 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private ArrayList fixedRules = new ArrayList<>(); private LinkedHashSet removeAfterObtainingNewNoGoods = new LinkedHashSet<>(); private boolean disableInstanceRemoval; - private boolean useCountingGridNormalization; - private boolean debugInternalChecks; + private final boolean useCountingGridNormalization; + private final boolean debugInternalChecks; - private GrounderHeuristicsConfiguration heuristicsConfiguration; + private final GrounderHeuristicsConfiguration heuristicsConfiguration; public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternalChecks, Bridge... bridges) { this(program, atomStore, new GrounderHeuristicsConfiguration(), debugInternalChecks, bridges); } - public NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsConfiguration heuristicsConfiguration, boolean debugInternalChecks, Bridge... bridges) { + private NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsConfiguration heuristicsConfiguration, boolean debugInternalChecks, Bridge... bridges) { this(program, atomStore, p -> true, heuristicsConfiguration, false, debugInternalChecks, bridges); } @@ -409,7 +409,7 @@ public int register(NoGood noGood) { return registry.register(noGood); } - BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, Substitution partialSubstitution, Assignment currentAssignment) { int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint()); if (tolerance < 0) { tolerance = Integer.MAX_VALUE; @@ -587,10 +587,8 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As // terminate if more positive atoms are unsatisfied as tolerated by the heuristic return true; } - if (truth != null && !truth.toBoolean()) { - // terminate if positive body atom is assigned false - return true; - } + // terminate if positive body atom is assigned false + return truth != null && !truth.toBoolean(); } } return false; @@ -618,17 +616,6 @@ public void forgetAssignment(int[] atomIds) { throw new UnsupportedOperationException("Forgetting assignments is not implemented"); } - public void printCurrentlyKnownGroundRules() { - System.out.println("Printing known ground rules:"); - for (Map.Entry> ruleSubstitutionsEntry : knownGroundingSubstitutions.entrySet()) { - NonGroundRule nonGroundRule = ruleSubstitutionsEntry.getKey(); - HashSet substitutions = ruleSubstitutionsEntry.getValue(); - for (Substitution substitution : substitutions) { - System.out.println(groundAndPrintRule(nonGroundRule, substitution)); - } - } - } - public static String groundAndPrintRule(NonGroundRule rule, Substitution substitution) { StringBuilder ret = new StringBuilder(); if (!rule.isConstraint()) { @@ -704,8 +691,8 @@ private void checkTypesOfNoGoods(Collection newNoGoods) { } private static class FirstBindingAtom { - NonGroundRule rule; - Literal startingLiteral; + final NonGroundRule rule; + final Literal startingLiteral; FirstBindingAtom(NonGroundRule rule, Literal startingLiteral) { this.rule = rule; @@ -714,15 +701,15 @@ private static class FirstBindingAtom { } private static class BindingResult { - List generatedSubstitutions = new ArrayList<>(); - List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>(); + final List generatedSubstitutions = new ArrayList<>(); + final List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>(); void add(Substitution generatedSubstitution, int numberOfUnassignedPositiveBodyAtoms) { this.generatedSubstitutions.add(generatedSubstitution); this.numbersOfUnassignedPositiveBodyAtoms.add(numberOfUnassignedPositiveBodyAtoms); } - public void add(BindingResult otherBindingResult) { + void add(BindingResult otherBindingResult) { this.generatedSubstitutions.addAll(otherBindingResult.generatedSubstitutions); this.numbersOfUnassignedPositiveBodyAtoms.addAll(otherBindingResult.numbersOfUnassignedPositiveBodyAtoms); } From c0ef033dfbaca242a5909744c1ece6157516ebcd Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 10:42:04 +0200 Subject: [PATCH 34/66] Refactoring NaiveGrounder --- src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 4903f08f0..30cef26cc 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -497,7 +497,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int // Check if atom is also assigned true. final LinkedHashSet instances = factsFromProgram.get(substitute.getPredicate()); - if (!(instances == null || !instances.contains(new Instance(substitute.getTerms())))) { + if (instances != null && instances.contains(new Instance(substitute.getTerms()))) { // Ground literal holds, continue finding a variable substitution. return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } From 146874525c6f4f0764458b445aa5ac991555814e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 11:06:44 +0200 Subject: [PATCH 35/66] Fix control flow in NaiveGrounder.bindNextAtomInRule --- .../kr/alpha/grounder/NaiveGrounder.java | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 30cef26cc..d54301550 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -479,9 +479,10 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } + Collection instances = null; + // check if partialVariableSubstitution already yields a ground atom final Atom substitute = currentAtom.substitute(partialSubstitution); - if (substitute.isGround()) { // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { @@ -496,17 +497,14 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int } // Check if atom is also assigned true. - final LinkedHashSet instances = factsFromProgram.get(substitute.getPredicate()); - if (instances != null && instances.contains(new Instance(substitute.getTerms()))) { + final LinkedHashSet factInstances = factsFromProgram.get(substitute.getPredicate()); + if (factInstances != null && factInstances.contains(new Instance(substitute.getTerms()))) { // Ground literal holds, continue finding a variable substitution. return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } // Atom is not a fact already. - if (storeAtomAndTerminateIfAtomDoesNotHold(substitute, currentAssignment, new AtomicInteger(remainingTolerance))) { - // TODO: this has to be refactored -- remainingTolerance is not modified here because the same method calls happens again further below - return BindingResult.empty(); - } + instances = singletonList(new Instance(substitute.getTerms())); } // substituted atom contains variables @@ -518,25 +516,14 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int } } - IndexedInstanceStorage storage = workingMemory.get(currentAtom.getPredicate(), true); - Collection instances; - if (partialSubstitution.isEmpty()) { - if (currentLiteral.isGround()) { - instances = singletonList(new Instance(currentLiteral.getTerms())); - } else { + if (instances == null) { + IndexedInstanceStorage storage = workingMemory.get(currentAtom.getPredicate(), true); + if (partialSubstitution.isEmpty()) { // No variables are bound, but first atom in the body became recently true, consider all instances now. instances = storage.getAllInstances(); + } else { + instances = storage.getInstancesFromPartiallyGroundAtom(substitute); } - } else { - instances = storage.getInstancesFromPartiallyGroundAtom(substitute); - } - - if (instances.isEmpty() && laxGrounderHeuristic && substitute.isGround()) { - // note: this is necessary in the case that the current atom has just been grounded and is not known by the working memory yet - // we do not add the atom to the working memory so as not to trigger additional grounding - // (but maybe the working memory will be redesigned in the future) - // TODO: this is a bit badly designed -- if substitute is already ground, we add the instance here just that the for loop below has an element to iterate, thereby doing some unnecessary work - instances = singletonList(new Instance(substitute.getTerms())); } if (laxGrounderHeuristic && instances.isEmpty()) { From f04413bcb6fdf6bfa1b19f3c8d5fce99597185db Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 May 2019 11:12:42 +0200 Subject: [PATCH 36/66] Refactoring NaiveGrounder --- .../kr/alpha/grounder/NaiveGrounder.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index d54301550..288917db0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -517,13 +517,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int } if (instances == null) { - IndexedInstanceStorage storage = workingMemory.get(currentAtom.getPredicate(), true); - if (partialSubstitution.isEmpty()) { - // No variables are bound, but first atom in the body became recently true, consider all instances now. - instances = storage.getAllInstances(); - } else { - instances = storage.getInstancesFromPartiallyGroundAtom(substitute); - } + instances = getInstancesForSubstitute(substitute, partialSubstitution); } if (laxGrounderHeuristic && instances.isEmpty()) { @@ -533,6 +527,22 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } + return createBindings(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment, instances, substitute); + } + + private Collection getInstancesForSubstitute(Atom substitute, Substitution partialSubstitution) { + Collection instances; + IndexedInstanceStorage storage = workingMemory.get(substitute.getPredicate(), true); + if (partialSubstitution.isEmpty()) { + // No variables are bound, but first atom in the body became recently true, consider all instances now. + instances = storage.getAllInstances(); + } else { + instances = storage.getInstancesFromPartiallyGroundAtom(substitute); + } + return instances; + } + + private BindingResult createBindings(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment, Collection instances, Atom substitute) { BindingResult bindingResult = new BindingResult(); for (Instance instance : instances) { // Check each instance if it matches with the atom. From 37771bc9bb440f5db961ede6509a1433d30d1140 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 6 May 2019 11:22:11 +0200 Subject: [PATCH 37/66] Remove redundant check for facts in NaiveGrounder.bindNext... --- .../at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 288917db0..d58479199 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -496,14 +496,6 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int return BindingResult.empty(); } - // Check if atom is also assigned true. - final LinkedHashSet factInstances = factsFromProgram.get(substitute.getPredicate()); - if (factInstances != null && factInstances.contains(new Instance(substitute.getTerms()))) { - // Ground literal holds, continue finding a variable substitution. - return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); - } - - // Atom is not a fact already. instances = singletonList(new Instance(substitute.getTerms())); } From 92560da1b37c0ccb7864d66b2c221d9e90c8749e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 6 May 2019 11:23:06 +0200 Subject: [PATCH 38/66] Additional NaiveGrounderTest --- .../ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java index 89e608f7d..9bbc42e99 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java @@ -235,7 +235,7 @@ public void testLaxGrounderHeuristicTolerance_2_accept() { + "b(X) :- something(X)."); testLaxGrounderHeuristicTolerance(program, 2, 0, true); } - + @Test public void testLaxGrounderHeuristicTolerance_2_reject() { Program program = PARSER.parse("a(1). " @@ -244,6 +244,14 @@ public void testLaxGrounderHeuristicTolerance_2_reject() { testLaxGrounderHeuristicTolerance(program, 2, 0, false); } + @Test + public void testLaxGrounderHeuristicTolerance_2_accept_multiple_facts_of_same_variable() { + Program program = PARSER.parse("a(1). b(1). " + + "c(X) :- a(X), b(X), b(X+1), b(X+2). " + + "b(X) :- something(X)."); + testLaxGrounderHeuristicTolerance(program, 2, 0, true); + } + private void testLaxGrounderHeuristicTolerance(Program program, int tolerance, int nTrueBs, boolean expectNoGoods) { AtomStore atomStore = new AtomStoreImpl(); TrailAssignment currentAssignment = new TrailAssignment(atomStore); From d3f3797189fd1df20402ab6e329e31c7943fd807 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 May 2019 10:13:49 +0200 Subject: [PATCH 39/66] Simplify Substitution.equals(...) --- .../at/ac/tuwien/kr/alpha/grounder/Substitution.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java index 1f0fbd97e..b859cb501 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016-2018, the Alpha Team. + * Copyright (c) 2016-2019, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -41,7 +41,10 @@ import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.misc.ParseCancellationException; -import java.util.*; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; import static at.ac.tuwien.kr.alpha.Util.oops; @@ -224,7 +227,7 @@ public boolean equals(Object o) { Substitution that = (Substitution) o; - return substitution != null ? substitution.equals(that.substitution) : that.substitution == null; + return Objects.equals(substitution, that.substitution); } @Override From e89eb27b0fe3386ececb43cc7f0e7dd2b954dd89 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 May 2019 10:20:03 +0200 Subject: [PATCH 40/66] Avoid to ground constraints that have already been grounded --- .../kr/alpha/grounder/NaiveGrounder.java | 83 ++++++++++++------- 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index d58479199..4f58af28b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -41,6 +41,8 @@ import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis; import at.ac.tuwien.kr.alpha.grounder.transformation.*; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,9 +79,11 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean disableInstanceRemoval; private final boolean useCountingGridNormalization; private final boolean debugInternalChecks; - + private final GrounderHeuristicsConfiguration heuristicsConfiguration; + private final Multimap processedSubstitutions = HashMultimap.create(); + public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternalChecks, Bridge... bridges) { this(program, atomStore, new GrounderHeuristicsConfiguration(), debugInternalChecks, bridges); } @@ -107,7 +111,7 @@ private NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsCo final Set uniqueGroundRulePerGroundHead = getRulesWithUniqueHead(); choiceRecorder = new ChoiceRecorder(atomStore); noGoodGenerator = new NoGoodGenerator(atomStore, choiceRecorder, factsFromProgram, programAnalysis, uniqueGroundRulePerGroundHead); - + this.debugInternalChecks = debugInternalChecks; } @@ -379,17 +383,17 @@ public Map getNoGoods(Assignment currentAssignment) { } LOGGER.debug("{}", choiceRecorder); } - + if (debugInternalChecks) { checkTypesOfNoGoods(newNoGoods.values()); } - + return newNoGoods; } /** * Grounds the given {@code nonGroundRule} by applying the given {@code substitutions} and registers the nogoods generated during that process. - * + * * @param nonGroundRule * the rule to be grounded * @param substitutions @@ -414,7 +418,10 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder if (tolerance < 0) { tolerance = Integer.MAX_VALUE; } - BindingResult bindingResult = bindNextAtomInRule(groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); + BindingResult bindingResult = bindNextAtomInRule(rule, groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); + if (bindingResult.size() > 0) { + storeProcessedSubstitutions(rule, bindingResult.generatedSubstitutions); + } if (LOGGER.isDebugEnabled()) { for (int i = 0; i < bindingResult.size(); i++) { Integer numberOfUnassignedPositiveBodyAtoms = bindingResult.numbersOfUnassignedPositiveBodyAtoms.get(i); @@ -426,27 +433,41 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder return bindingResult; } - private BindingResult advanceAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private void storeProcessedSubstitutions(NonGroundRule rule, Collection generatedSubstitutions) { + if (rule.isConstraint()) { + processedSubstitutions.putAll(rule.getRuleId(), generatedSubstitutions); + } + } + + private boolean substitutionAlreadyProcessed(NonGroundRule rule, Substitution partialSubstitution) { + return rule.isConstraint() && processedSubstitutions.containsEntry(rule.getRuleId(), partialSubstitution); + } + + private BindingResult advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + if (rule.isConstraint() && substitutionAlreadyProcessed(rule, partialSubstitution)) { + return BindingResult.empty(); + } + groundingOrder.considerUntilCurrentEnd(); - return bindNextAtomInRule(groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - - private BindingResult pushBackAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + + private BindingResult pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { RuleGroundingOrder modifiedGroundingOrder = groundingOrder.pushBack(orderPosition); if (modifiedGroundingOrder == null) { return BindingResult.empty(); } - return bindNextAtomInRule(modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(rule, modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = originalTolerance > 0; Literal currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition); if (currentLiteral == null) { return BindingResult.singleton(partialSubstitution, originalTolerance - remainingTolerance); } - + Atom currentAtom = currentLiteral.getAtom(); if (currentLiteral instanceof FixedInterpretationLiteral) { // Generate all substitutions for the builtin/external/interval atom. @@ -457,26 +478,26 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int !(substitutedLiteral instanceof IntervalLiteral && substitutedLiteral.getTerms().get(0).isGround()) && !(substitutedLiteral instanceof ExternalLiteral) ) { - return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } final List substitutions = substitutedLiteral.getSubstitutions(partialSubstitution); - + if (substitutions.isEmpty()) { // if FixedInterpretationLiteral cannot be satisfied now, it will never be return BindingResult.empty(); } - + final BindingResult bindingResult = new BindingResult(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. - bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); } return bindingResult; } if (currentAtom instanceof EnumerationAtom) { // Get the enumeration value and add it to the current partialSubstitution. ((EnumerationAtom) currentAtom).addEnumerationToSubstitution(partialSubstitution); - return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } Collection instances = null; @@ -487,7 +508,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { // Atom occurs negated in the rule: continue grounding - return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } if (!groundingOrder.isGround() && remainingTolerance <= 0 @@ -498,11 +519,11 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int instances = singletonList(new Instance(substitute.getTerms())); } - + // substituted atom contains variables if (currentLiteral.isNegated()) { if (laxGrounderHeuristic) { - return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } else { throw oops("Current atom should be positive at this point but is not"); } @@ -511,15 +532,15 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int if (instances == null) { instances = getInstancesForSubstitute(substitute, partialSubstitution); } - + if (laxGrounderHeuristic && instances.isEmpty()) { // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding // under the presence of a lax grounder heuristic - return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - return createBindings(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment, instances, substitute); + return createBindings(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment, instances, substitute); } private Collection getInstancesForSubstitute(Atom substitute, Substitution partialSubstitution) { @@ -534,7 +555,7 @@ private Collection getInstancesForSubstitute(Atom substitute, Substitu return instances; } - private BindingResult createBindings(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment, Collection instances, Atom substitute) { + private BindingResult createBindings(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment, Collection instances, Atom substitute) { BindingResult bindingResult = new BindingResult(); for (Instance instance : instances) { // Check each instance if it matches with the atom. @@ -556,7 +577,7 @@ private BindingResult createBindings(RuleGroundingOrder groundingOrder, int orde continue; } } - bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); } return bindingResult; @@ -587,7 +608,7 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As public Pair, Map> getChoiceAtoms() { return choiceRecorder.getAndResetChoices(); } - + @Override public Map> getHeadsToBodies() { return choiceRecorder.getAndResetHeadsToBodies(); @@ -688,7 +709,7 @@ private static class FirstBindingAtom { this.startingLiteral = startingLiteral; } } - + private static class BindingResult { final List generatedSubstitutions = new ArrayList<>(); final List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>(); @@ -706,16 +727,16 @@ void add(BindingResult otherBindingResult) { int size() { return generatedSubstitutions.size(); } - + static BindingResult empty() { return new BindingResult(); } - + static BindingResult singleton(Substitution generatedSubstitution, int numberOfUnassignedPositiveBodyAtoms) { BindingResult bindingResult = new BindingResult(); bindingResult.add(generatedSubstitution, numberOfUnassignedPositiveBodyAtoms); return bindingResult; } - + } } From 71b557b3a4f1ad22193a5ca30c6ff8c3a912bb29 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 May 2019 10:21:20 +0200 Subject: [PATCH 41/66] Revert "Avoid to ground constraints that have already been grounded" Because overhead of this exceeds gains This reverts commit e89eb27b --- .../kr/alpha/grounder/NaiveGrounder.java | 83 +++++++------------ 1 file changed, 31 insertions(+), 52 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index 4f58af28b..d58479199 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -41,8 +41,6 @@ import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis; import at.ac.tuwien.kr.alpha.grounder.transformation.*; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,11 +77,9 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean disableInstanceRemoval; private final boolean useCountingGridNormalization; private final boolean debugInternalChecks; - + private final GrounderHeuristicsConfiguration heuristicsConfiguration; - private final Multimap processedSubstitutions = HashMultimap.create(); - public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternalChecks, Bridge... bridges) { this(program, atomStore, new GrounderHeuristicsConfiguration(), debugInternalChecks, bridges); } @@ -111,7 +107,7 @@ private NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsCo final Set uniqueGroundRulePerGroundHead = getRulesWithUniqueHead(); choiceRecorder = new ChoiceRecorder(atomStore); noGoodGenerator = new NoGoodGenerator(atomStore, choiceRecorder, factsFromProgram, programAnalysis, uniqueGroundRulePerGroundHead); - + this.debugInternalChecks = debugInternalChecks; } @@ -383,17 +379,17 @@ public Map getNoGoods(Assignment currentAssignment) { } LOGGER.debug("{}", choiceRecorder); } - + if (debugInternalChecks) { checkTypesOfNoGoods(newNoGoods.values()); } - + return newNoGoods; } /** * Grounds the given {@code nonGroundRule} by applying the given {@code substitutions} and registers the nogoods generated during that process. - * + * * @param nonGroundRule * the rule to be grounded * @param substitutions @@ -418,10 +414,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder if (tolerance < 0) { tolerance = Integer.MAX_VALUE; } - BindingResult bindingResult = bindNextAtomInRule(rule, groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); - if (bindingResult.size() > 0) { - storeProcessedSubstitutions(rule, bindingResult.generatedSubstitutions); - } + BindingResult bindingResult = bindNextAtomInRule(groundingOrder, 0, tolerance, tolerance, partialSubstitution, currentAssignment); if (LOGGER.isDebugEnabled()) { for (int i = 0; i < bindingResult.size(); i++) { Integer numberOfUnassignedPositiveBodyAtoms = bindingResult.numbersOfUnassignedPositiveBodyAtoms.get(i); @@ -433,41 +426,27 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder return bindingResult; } - private void storeProcessedSubstitutions(NonGroundRule rule, Collection generatedSubstitutions) { - if (rule.isConstraint()) { - processedSubstitutions.putAll(rule.getRuleId(), generatedSubstitutions); - } - } - - private boolean substitutionAlreadyProcessed(NonGroundRule rule, Substitution partialSubstitution) { - return rule.isConstraint() && processedSubstitutions.containsEntry(rule.getRuleId(), partialSubstitution); - } - - private BindingResult advanceAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { - if (rule.isConstraint() && substitutionAlreadyProcessed(rule, partialSubstitution)) { - return BindingResult.empty(); - } - + private BindingResult advanceAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { groundingOrder.considerUntilCurrentEnd(); - return bindNextAtomInRule(rule, groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - - private BindingResult pushBackAndBindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + + private BindingResult pushBackAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { RuleGroundingOrder modifiedGroundingOrder = groundingOrder.pushBack(orderPosition); if (modifiedGroundingOrder == null) { return BindingResult.empty(); } - return bindNextAtomInRule(rule, modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return bindNextAtomInRule(modifiedGroundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { + private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { boolean laxGrounderHeuristic = originalTolerance > 0; Literal currentLiteral = groundingOrder.getLiteralAtOrderPosition(orderPosition); if (currentLiteral == null) { return BindingResult.singleton(partialSubstitution, originalTolerance - remainingTolerance); } - + Atom currentAtom = currentLiteral.getAtom(); if (currentLiteral instanceof FixedInterpretationLiteral) { // Generate all substitutions for the builtin/external/interval atom. @@ -478,26 +457,26 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder !(substitutedLiteral instanceof IntervalLiteral && substitutedLiteral.getTerms().get(0).isGround()) && !(substitutedLiteral instanceof ExternalLiteral) ) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } final List substitutions = substitutedLiteral.getSubstitutions(partialSubstitution); - + if (substitutions.isEmpty()) { // if FixedInterpretationLiteral cannot be satisfied now, it will never be return BindingResult.empty(); } - + final BindingResult bindingResult = new BindingResult(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. - bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, substitution, currentAssignment)); } return bindingResult; } if (currentAtom instanceof EnumerationAtom) { // Get the enumeration value and add it to the current partialSubstitution. ((EnumerationAtom) currentAtom).addEnumerationToSubstitution(partialSubstitution); - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } Collection instances = null; @@ -508,7 +487,7 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder // Substituted atom is ground, in case it is positive, only ground if it also holds true if (currentLiteral.isNegated()) { // Atom occurs negated in the rule: continue grounding - return advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } if (!groundingOrder.isGround() && remainingTolerance <= 0 @@ -519,11 +498,11 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder instances = singletonList(new Instance(substitute.getTerms())); } - + // substituted atom contains variables if (currentLiteral.isNegated()) { if (laxGrounderHeuristic) { - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } else { throw oops("Current atom should be positive at this point but is not"); } @@ -532,15 +511,15 @@ private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder if (instances == null) { instances = getInstancesForSubstitute(substitute, partialSubstitution); } - + if (laxGrounderHeuristic && instances.isEmpty()) { // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding // under the presence of a lax grounder heuristic - return pushBackAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); + return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - return createBindings(rule, groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment, instances, substitute); + return createBindings(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment, instances, substitute); } private Collection getInstancesForSubstitute(Atom substitute, Substitution partialSubstitution) { @@ -555,7 +534,7 @@ private Collection getInstancesForSubstitute(Atom substitute, Substitu return instances; } - private BindingResult createBindings(NonGroundRule rule, RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment, Collection instances, Atom substitute) { + private BindingResult createBindings(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment, Collection instances, Atom substitute) { BindingResult bindingResult = new BindingResult(); for (Instance instance : instances) { // Check each instance if it matches with the atom. @@ -577,7 +556,7 @@ private BindingResult createBindings(NonGroundRule rule, RuleGroundingOrder grou continue; } } - bindingResult.add(advanceAndBindNextAtomInRule(rule, groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); + bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment)); } return bindingResult; @@ -608,7 +587,7 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As public Pair, Map> getChoiceAtoms() { return choiceRecorder.getAndResetChoices(); } - + @Override public Map> getHeadsToBodies() { return choiceRecorder.getAndResetHeadsToBodies(); @@ -709,7 +688,7 @@ private static class FirstBindingAtom { this.startingLiteral = startingLiteral; } } - + private static class BindingResult { final List generatedSubstitutions = new ArrayList<>(); final List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>(); @@ -727,16 +706,16 @@ void add(BindingResult otherBindingResult) { int size() { return generatedSubstitutions.size(); } - + static BindingResult empty() { return new BindingResult(); } - + static BindingResult singleton(Substitution generatedSubstitution, int numberOfUnassignedPositiveBodyAtoms) { BindingResult bindingResult = new BindingResult(); bindingResult.add(generatedSubstitution, numberOfUnassignedPositiveBodyAtoms); return bindingResult; } - + } } From 21f4782de1870c7e07321e164ed99e0fc3ea4cab Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 21 May 2019 19:27:51 +0200 Subject: [PATCH 42/66] Fix bug in accumulator grounder --- .../kr/alpha/grounder/NaiveGrounder.java | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java index d58479199..9a31af449 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java @@ -77,7 +77,7 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr private boolean disableInstanceRemoval; private final boolean useCountingGridNormalization; private final boolean debugInternalChecks; - + private final GrounderHeuristicsConfiguration heuristicsConfiguration; public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternalChecks, Bridge... bridges) { @@ -107,7 +107,7 @@ private NaiveGrounder(Program program, AtomStore atomStore, GrounderHeuristicsCo final Set uniqueGroundRulePerGroundHead = getRulesWithUniqueHead(); choiceRecorder = new ChoiceRecorder(atomStore); noGoodGenerator = new NoGoodGenerator(atomStore, choiceRecorder, factsFromProgram, programAnalysis, uniqueGroundRulePerGroundHead); - + this.debugInternalChecks = debugInternalChecks; } @@ -379,17 +379,17 @@ public Map getNoGoods(Assignment currentAssignment) { } LOGGER.debug("{}", choiceRecorder); } - + if (debugInternalChecks) { checkTypesOfNoGoods(newNoGoods.values()); } - + return newNoGoods; } /** * Grounds the given {@code nonGroundRule} by applying the given {@code substitutions} and registers the nogoods generated during that process. - * + * * @param nonGroundRule * the rule to be grounded * @param substitutions @@ -430,7 +430,7 @@ private BindingResult advanceAndBindNextAtomInRule(RuleGroundingOrder groundingO groundingOrder.considerUntilCurrentEnd(); return bindNextAtomInRule(groundingOrder, orderPosition + 1, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } - + private BindingResult pushBackAndBindNextAtomInRule(RuleGroundingOrder groundingOrder, int orderPosition, int originalTolerance, int remainingTolerance, Substitution partialSubstitution, Assignment currentAssignment) { RuleGroundingOrder modifiedGroundingOrder = groundingOrder.pushBack(orderPosition); if (modifiedGroundingOrder == null) { @@ -446,7 +446,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int if (currentLiteral == null) { return BindingResult.singleton(partialSubstitution, originalTolerance - remainingTolerance); } - + Atom currentAtom = currentLiteral.getAtom(); if (currentLiteral instanceof FixedInterpretationLiteral) { // Generate all substitutions for the builtin/external/interval atom. @@ -460,12 +460,12 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment); } final List substitutions = substitutedLiteral.getSubstitutions(partialSubstitution); - + if (substitutions.isEmpty()) { // if FixedInterpretationLiteral cannot be satisfied now, it will never be return BindingResult.empty(); } - + final BindingResult bindingResult = new BindingResult(); for (Substitution substitution : substitutions) { // Continue grounding with each of the generated values. @@ -498,7 +498,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int instances = singletonList(new Instance(substitute.getTerms())); } - + // substituted atom contains variables if (currentLiteral.isNegated()) { if (laxGrounderHeuristic) { @@ -511,7 +511,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int if (instances == null) { instances = getInstancesForSubstitute(substitute, partialSubstitution); } - + if (laxGrounderHeuristic && instances.isEmpty()) { // we have reached a point where we have to terminate binding, // but it might be possible that a different grounding order would allow us to continue binding @@ -563,13 +563,23 @@ private BindingResult createBindings(RuleGroundingOrder groundingOrder, int orde } private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance) { - final int atomId = atomStore.putIfAbsent(substitute); - - if (currentAssignment != null) { + if (currentAssignment != null) { // if we are not in bootstrapping + final int atomId = atomStore.putIfAbsent(substitute); ThriceTruth truth = currentAssignment.isAssigned(atomId) ? currentAssignment.getTruth(atomId) : null; - if (truth == null || !truth.toBoolean()) { - // Atom currently does not hold - if (!disableInstanceRemoval) { + + if (disableInstanceRemoval) { + final Instance instance = new Instance(substitute.getTerms()); + boolean isInWorkingMemory = workingMemory.get(substitute, true).containsInstance(instance); + if (isInWorkingMemory) { + return false; + } + if (truth != null && !truth.toBoolean() || remainingTolerance.decrementAndGet() < 0) { + // terminate if more positive atoms are unsatisfied as tolerated by the heuristic + return true; + } + } else { + if (truth == null || !truth.toBoolean()) { + // Atom currently does not hold removeAfterObtainingNewNoGoods.add(substitute); } if (truth == null && remainingTolerance.decrementAndGet() < 0) { @@ -587,7 +597,7 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As public Pair, Map> getChoiceAtoms() { return choiceRecorder.getAndResetChoices(); } - + @Override public Map> getHeadsToBodies() { return choiceRecorder.getAndResetHeadsToBodies(); @@ -688,7 +698,7 @@ private static class FirstBindingAtom { this.startingLiteral = startingLiteral; } } - + private static class BindingResult { final List generatedSubstitutions = new ArrayList<>(); final List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>(); @@ -706,16 +716,16 @@ void add(BindingResult otherBindingResult) { int size() { return generatedSubstitutions.size(); } - + static BindingResult empty() { return new BindingResult(); } - + static BindingResult singleton(Substitution generatedSubstitution, int numberOfUnassignedPositiveBodyAtoms) { BindingResult bindingResult = new BindingResult(); bindingResult.add(generatedSubstitution, numberOfUnassignedPositiveBodyAtoms); return bindingResult; } - + } } From 8b69a7ad6484bdff6686bfe607ca0eb5603ac8d8 Mon Sep 17 00:00:00 2001 From: Antonius Weinzierl Date: Mon, 30 Sep 2019 00:50:32 +0200 Subject: [PATCH 43/66] Add phase saving. - Assignment provides getLastValue (i.e. phase). - VSIDSWithPhaseSaving is VSIDS but uses saved phase from Assignment. - BranchingHeuristicFactory knows new VSIDSWithPhaseSaving heuristics. - HeapOfActiveAtoms polished, growForMaxAtomId added. - TrailAssignment saves the phase (last truth value) of variables. --- .../ac/tuwien/kr/alpha/common/Assignment.java | 7 + .../tuwien/kr/alpha/solver/DefaultSolver.java | 2 +- .../kr/alpha/solver/TrailAssignment.java | 12 +- .../heuristics/BranchingHeuristicFactory.java | 5 +- .../solver/heuristics/HeapOfActiveAtoms.java | 28 ++- .../heuristics/VSIDSWithPhaseSaving.java | 168 ++++++++++++++++++ 6 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Assignment.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Assignment.java index 638696fce..04fdc8288 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Assignment.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Assignment.java @@ -71,6 +71,13 @@ default ThriceTruth getTruth(int atom) { */ Antecedent getImpliedBy(int atom); + /** + * Returns the last truth value (i.e., phase) assigned to the atom. + * @param atom the atom + * @return the last truth value. + */ + boolean getLastValue(int atom); + /** * Determines if the given {@code noGood} is undefined in the current assignment. * diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java index 2ee2b11d4..91074801d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java @@ -281,7 +281,7 @@ private boolean justifyMbtAndBacktrack() { } ProgramAnalyzingGrounder analyzingGrounder = (ProgramAnalyzingGrounder) grounder; // Justify one MBT assigned atom. - Integer atomToJustify = assignment.getBasicAtomAssignedMBT(); + int atomToJustify = assignment.getBasicAtomAssignedMBT(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Searching for justification of {} / {}", atomToJustify, atomStore.atomToString(atomToJustify)); LOGGER.debug("Assignment is (TRUE part only): {}", translate(assignment.getTrueAssignments())); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java index 9c4640d01..d76166a7e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java @@ -49,7 +49,7 @@ */ public class TrailAssignment implements WritableAssignment, Checkable { private static final Logger LOGGER = LoggerFactory.getLogger(TrailAssignment.class); - public static final Antecedent CLOSING_INDICATOR_ANTECEDENT = new Antecedent() { + static final Antecedent CLOSING_INDICATOR_ANTECEDENT = new Antecedent() { int[] literals = new int[0]; @Override @@ -81,6 +81,7 @@ public void decreaseActivity() { private int[] strongDecisionLevels; private Antecedent[] impliedBy; private boolean[] callbackUponChange; + private boolean[] phase; private ArrayList outOfOrderLiterals = new ArrayList<>(); private int highestDecisionLevelContainingOutOfOrderLiterals; private ArrayList trail = new ArrayList<>(); @@ -101,6 +102,7 @@ public TrailAssignment(AtomStore atomStore, boolean checksEnabled) { this.strongDecisionLevels = new int[0]; this.impliedBy = new Antecedent[0]; this.callbackUponChange = new boolean[0]; + this.phase = new boolean[0]; this.trailIndicesOfDecisionLevels.add(0); nextPositionInTrail = 0; newAssignmentsIterator = 0; @@ -119,6 +121,7 @@ public void clear() { Arrays.fill(strongDecisionLevels, -1); Arrays.fill(impliedBy, null); Arrays.fill(callbackUponChange, false); + Arrays.fill(phase, false); outOfOrderLiterals = new ArrayList<>(); highestDecisionLevelContainingOutOfOrderLiterals = 0; trail = new ArrayList<>(); @@ -353,6 +356,7 @@ private ConflictCause assignWithTrail(int atom, ThriceTruth value, Antecedent im trail.add(atomToLiteral(atom, value.toBoolean())); values[atom] = (getDecisionLevel() << 2) | translateTruth(value); this.impliedBy[atom] = impliedBy; + this.phase[atom] = value.toBoolean(); // Adjust MBT counter. if (value == MBT) { mbtCount++; @@ -445,6 +449,11 @@ public Antecedent getImpliedBy(int atom) { return impliedBy[atom]; } + @Override + public boolean getLastValue(int atom) { + return phase[atom]; + } + @Override public Set getTrueAssignments() { Set result = new HashSet<>(); @@ -533,6 +542,7 @@ public void growForMaxAtomId() { strongDecisionLevels = Arrays.copyOf(strongDecisionLevels, newCapacity); Arrays.fill(strongDecisionLevels, oldLength, strongDecisionLevels.length, -1); impliedBy = Arrays.copyOf(impliedBy, newCapacity); + phase = Arrays.copyOf(phase, newCapacity); callbackUponChange = Arrays.copyOf(callbackUponChange, newCapacity); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java index b54cfdd53..740e016fd 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java @@ -56,7 +56,8 @@ public enum Heuristic { ALPHA_ACTIVE_RULE, ALPHA_HEAD_MBT, VSIDS, - GDD_VSIDS; + GDD_VSIDS, + VSIDS_PHASE_SAVING; /** * @return a comma-separated list of names of known heuristics @@ -117,6 +118,8 @@ private static BranchingHeuristic getInstanceWithoutReplay(HeuristicsConfigurati return new VSIDS(assignment, choiceManager, heuristicsConfiguration.getMomsStrategy()); case GDD_VSIDS: return new DependencyDrivenVSIDS(assignment, choiceManager, random, heuristicsConfiguration.getMomsStrategy()); + case VSIDS_PHASE_SAVING: + return new VSIDSWithPhaseSaving(assignment, choiceManager, heuristicsConfiguration.getMomsStrategy()); } throw new IllegalArgumentException("Unknown branching heuristic requested."); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java index 5ff32fc06..56db8f727 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java @@ -38,6 +38,7 @@ import java.util.Comparator; import java.util.PriorityQueue; +import static at.ac.tuwien.kr.alpha.Util.arrayGrowthSize; import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; /** @@ -56,8 +57,8 @@ public class HeapOfActiveAtoms { private static final double SCORE_EPSILON = 1E-100; private boolean[] incrementedActivityScores = new boolean[0]; - protected double[] activityScores = new double[0]; - protected final PriorityQueue heap = new PriorityQueue<>(new AtomActivityComparator().reversed()); + private double[] activityScores = new double[0]; + final PriorityQueue heap = new PriorityQueue<>(new AtomActivityComparator().reversed()); protected ChoiceManager choiceManager; private int decayPeriod; @@ -68,7 +69,7 @@ public class HeapOfActiveAtoms { private final MOMs moms; - public HeapOfActiveAtoms(int decayPeriod, double decayFactor, ChoiceManager choiceManager) { + HeapOfActiveAtoms(int decayPeriod, double decayFactor, ChoiceManager choiceManager) { this.decayPeriod = decayPeriod; this.decayFactor = decayFactor; this.choiceManager = choiceManager; @@ -144,7 +145,7 @@ protected void analyzeNewNoGood(NoGood newNoGood) { /** * Computes and stores initial activity values for the atoms occurring in the given nogood. */ - protected void initActivity(NoGood newNoGood) { + private void initActivity(NoGood newNoGood) { if (moms != null) { initActivityMOMs(newNoGood); } else { @@ -183,6 +184,19 @@ void growToCapacity(int newCapacity) { incrementedActivityScores = Arrays.copyOf(incrementedActivityScores, newCapacity); } + void growForMaxAtomId(int maxAtomId) { + // Grow arrays only if needed. + if (activityScores.length > maxAtomId) { + return; + } + int newCapacity = arrayGrowthSize(activityScores.length); + if (newCapacity < maxAtomId + 1) { + newCapacity = maxAtomId + 1; + } + activityScores = Arrays.copyOf(activityScores, newCapacity); + incrementedActivityScores = Arrays.copyOf(incrementedActivityScores, newCapacity); + } + private void initActivityNaive(NoGood newNoGood) { LOGGER.debug("Initializing activity scores naively"); for (Integer literal : newNoGood) { @@ -194,7 +208,7 @@ private void initActivityNaive(NoGood newNoGood) { /** * Returns the atom with the highest activity score and removes it from the heap. */ - public Integer getMostActiveAtom() { + Integer getMostActiveAtom() { return heap.poll(); } @@ -204,7 +218,7 @@ public Integer getMostActiveAtom() { * by adding to it the current activity increment times the increment factor. * If the new value exceeds a certain threshold, all activity scores are normalized. */ - public void incrementActivity(int atom) { + void incrementActivity(int atom) { incrementActivity(atom, currentActivityIncrement); } @@ -271,7 +285,7 @@ public void callbackOnChanged(int atom, boolean active) { } } - public void setMOMsStrategy(BinaryNoGoodPropagationEstimation.Strategy momsStrategy) { + void setMOMsStrategy(BinaryNoGoodPropagationEstimation.Strategy momsStrategy) { if (moms != null) { moms.setStrategy(momsStrategy); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java new file mode 100644 index 000000000..80512659d --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2018-2019 Siemens AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2) 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. + * + * 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 at.ac.tuwien.kr.alpha.solver.heuristics; + +import at.ac.tuwien.kr.alpha.common.Assignment; +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.solver.BinaryNoGoodPropagationEstimation; +import at.ac.tuwien.kr.alpha.solver.ChoiceManager; +import at.ac.tuwien.kr.alpha.solver.ThriceTruth; +import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; + +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; + +/** + * This implementation is like {@link VSIDS} but uses the saved phase for the truth of the chosen atom. + * + * Copyright (c) 2019, the Alpha Team. + */ +public class VSIDSWithPhaseSaving implements ActivityBasedBranchingHeuristic { + protected static final Logger LOGGER = LoggerFactory.getLogger(VSIDSWithPhaseSaving.class); + + private static final int DEFAULT_DECAY_PERIOD = 1; + + /** + * The default factor by which VSID's activity increment will be multiplied when the decay period has expired. + * The value is taken from clasp's tweety configuration which clasp uses by default. + */ + private static final double DEFAULT_DECAY_FACTOR = 1 / 0.92; + + protected final Assignment assignment; + protected final ChoiceManager choiceManager; + + private final HeapOfActiveAtoms heapOfActiveAtoms; + + private final Collection bufferedNoGoods = new ArrayList<>(); + + private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, HeapOfActiveAtoms heapOfActiveAtoms, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) { + this.assignment = assignment; + this.choiceManager = choiceManager; + this.heapOfActiveAtoms = heapOfActiveAtoms; + this.heapOfActiveAtoms.setMOMsStrategy(momsStrategy); + } + + private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, int decayPeriod, double decayFactor, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) { + this(assignment, choiceManager, new HeapOfActiveAtoms(decayPeriod, decayFactor, choiceManager), momsStrategy); + } + + VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) { + this(assignment, choiceManager, DEFAULT_DECAY_PERIOD, DEFAULT_DECAY_FACTOR, momsStrategy); + } + + @Override + public void violatedNoGood(NoGood violatedNoGood) { + } + + @Override + public void analyzedConflict(ConflictAnalysisResult analysisResult) { + ingestBufferedNoGoods(); //analysisResult may contain new atoms whose activity must be initialized + for (int resolutionAtom : analysisResult.resolutionAtoms) { + heapOfActiveAtoms.incrementActivity(resolutionAtom); + } + if (analysisResult.learnedNoGood != null) { + for (int literal : analysisResult.learnedNoGood) { + heapOfActiveAtoms.incrementActivity(atomOf(literal)); + } + } + heapOfActiveAtoms.decayIfTimeHasCome(); + } + + @Override + public void newNoGood(NoGood newNoGood) { + this.bufferedNoGoods.add(newNoGood); + } + + @Override + public void newNoGoods(Collection newNoGoods) { + this.bufferedNoGoods.addAll(newNoGoods); + } + + private void ingestBufferedNoGoods() { + heapOfActiveAtoms.newNoGoods(bufferedNoGoods); + bufferedNoGoods.clear(); + } + + /** + * {@link VSIDSWithPhaseSaving} works like {@link VSIDS} for selecting an atom but uses the saved phase to + * determine the truth value to choose. + */ + @Override + public int chooseLiteral() { + int atom = chooseAtom(); + if (atom == DEFAULT_CHOICE_ATOM) { + return DEFAULT_CHOICE_LITERAL; + } + boolean sign = chooseSign(atom); + return atomToLiteral(atom, sign); + } + + private int chooseAtom() { + ingestBufferedNoGoods(); + Integer mostActiveAtom; + while ((mostActiveAtom = heapOfActiveAtoms.getMostActiveAtom()) != null) { + if (choiceManager.isActiveChoiceAtom(mostActiveAtom)) { + return mostActiveAtom; + } + } + return DEFAULT_CHOICE_ATOM; + } + + /** + * Chooses a sign (truth value) to assign to the given atom; + * uses the last value (saved phase) to determine its truth value. + * + * @param atom + * the chosen atom + * @return the truth value to assign to the given atom + */ + private boolean chooseSign(int atom) { + if (assignment.getTruth(atom) == ThriceTruth.MBT) { + return true; + } + return assignment.getLastValue(atom); + } + + public void growForMaxAtomId(int maxAtomId) { + heapOfActiveAtoms.growForMaxAtomId(maxAtomId); + } + + @Override + public double getActivity(int literal) { + return heapOfActiveAtoms.getActivity(literal); + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } + +} From eeaa54de4f89d2739de1c3e330f6fd7e56e1cea1 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 3 Oct 2019 13:49:28 +0200 Subject: [PATCH 44/66] Fix merge ddda826e967ce6b38acc56bd41909a2c30cfe99a --- .../at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java | 5 +---- .../tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java | 5 +---- .../solver/heuristics/BranchingHeuristicFactoryTest.java | 5 +---- .../kr/alpha/solver/heuristics/ReplayHeuristicTest.java | 5 +---- .../at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java | 5 +---- 5 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java index 64f451bd0..33a069469 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java @@ -1,9 +1,6 @@ package at.ac.tuwien.kr.alpha.solver; -import at.ac.tuwien.kr.alpha.common.Assignment; -import at.ac.tuwien.kr.alpha.common.AtomStore; -import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; -import at.ac.tuwien.kr.alpha.common.AtomStoreTest; +import at.ac.tuwien.kr.alpha.common.*; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java index 088f31263..0f6d73acf 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java @@ -1,9 +1,6 @@ package at.ac.tuwien.kr.alpha.solver; -import at.ac.tuwien.kr.alpha.common.Assignment; -import at.ac.tuwien.kr.alpha.common.AtomStore; -import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; -import at.ac.tuwien.kr.alpha.common.AtomStoreTest; +import at.ac.tuwien.kr.alpha.common.*; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java index 2eb06b1c2..519f14169 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java @@ -27,10 +27,7 @@ import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; -import at.ac.tuwien.kr.alpha.solver.NoGoodStore; -import at.ac.tuwien.kr.alpha.solver.NoGoodStoreAlphaRoaming; -import at.ac.tuwien.kr.alpha.solver.TrailAssignment; -import at.ac.tuwien.kr.alpha.solver.WritableAssignment; +import at.ac.tuwien.kr.alpha.solver.*; import org.junit.Before; import org.junit.Test; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java index 6c78e02fd..81ec7bbd1 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java @@ -27,10 +27,7 @@ import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; -import at.ac.tuwien.kr.alpha.solver.NoGoodStore; -import at.ac.tuwien.kr.alpha.solver.NoGoodStoreAlphaRoaming; -import at.ac.tuwien.kr.alpha.solver.TrailAssignment; -import at.ac.tuwien.kr.alpha.solver.WritableAssignment; +import at.ac.tuwien.kr.alpha.solver.*; import org.junit.Before; import org.junit.Test; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java index c6d5be7e4..e4f4f714f 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java @@ -25,10 +25,7 @@ */ package at.ac.tuwien.kr.alpha.solver.heuristics; -import at.ac.tuwien.kr.alpha.common.AtomStore; -import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; -import at.ac.tuwien.kr.alpha.common.AtomStoreTest; -import at.ac.tuwien.kr.alpha.common.Literals; +import at.ac.tuwien.kr.alpha.common.*; import at.ac.tuwien.kr.alpha.solver.NoGoodStoreAlphaRoaming; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import at.ac.tuwien.kr.alpha.solver.WritableAssignment; From 2904653d9931e486f4b17b70ebe337fc428f5d52 Mon Sep 17 00:00:00 2001 From: Antonius Weinzierl Date: Sat, 5 Oct 2019 04:06:15 +0200 Subject: [PATCH 45/66] Adding restarts (default disabled). - CommandLineParser and SystemConfig have option to enable restarts. - DefaultSolver runs restarts if enabled. - Add MixedRestartStrategy combining Luby and dynamic (EMA) restarts. - Enhanced performance logs for conflicts and restarts --- .../kr/alpha/config/CommandLineParser.java | 9 +++ .../tuwien/kr/alpha/config/SystemConfig.java | 10 +++ .../tuwien/kr/alpha/solver/DefaultSolver.java | 26 +++++- .../alpha/solver/LearnedNoGoodDeletion.java | 7 ++ .../kr/alpha/solver/MixedRestartStrategy.java | 81 +++++++++++++++++++ .../kr/alpha/solver/PerformanceLog.java | 58 +++++++++---- 6 files changed, 173 insertions(+), 18 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java b/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java index 6efa8e4b4..26e1e786d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java @@ -107,6 +107,10 @@ public class CommandLineParser { .desc("disable the deletion of (learned, little active) nogoods (default: " + SystemConfig.DEFAULT_DISABLE_NOGOOD_DELETION + ")") .build(); + private static final Option OPT_ENABLE_RESTARTS = Option.builder("rs").longOpt("enableRestarts") + .desc("enable the usage of (dynamic and static) restarts (default: " + + SystemConfig.DEFAULT_ENABLE_RESTARTS + ")") + .build(); private static final Options CLI_OPTS = new Options(); @@ -137,6 +141,7 @@ public class CommandLineParser { CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_JUSTIFICATION); CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NORMALIZATION_GRID); CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_NOGOOD_DELETION); + CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_ENABLE_RESTARTS); } /* @@ -180,6 +185,7 @@ public CommandLineParser(String cmdLineSyntax, Consumer abortAction) { this.globalOptionHandlers.put(CommandLineParser.OPT_NO_JUSTIFICATION.getOpt(), this::handleNoJustification); this.globalOptionHandlers.put(CommandLineParser.OPT_NORMALIZATION_GRID.getOpt(), this::handleNormalizationGrid); this.globalOptionHandlers.put(CommandLineParser.OPT_NO_NOGOOD_DELETION.getOpt(), this::handleNoNoGoodDeletion); + this.globalOptionHandlers.put(CommandLineParser.OPT_ENABLE_RESTARTS.getOpt(), this::handleEnableRestarts); this.inputOptionHandlers.put(CommandLineParser.OPT_NUM_ANSWER_SETS.getOpt(), this::handleNumAnswerSets); this.inputOptionHandlers.put(CommandLineParser.OPT_INPUT.getOpt(), this::handleInput); @@ -359,4 +365,7 @@ private void handleNoNoGoodDeletion(Option opt, SystemConfig cfg) { cfg.setDisableNoGoodDeletion(true); } + private void handleEnableRestarts(Option opt, SystemConfig cfg) { + cfg.setRestartsEnabled(true); + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java index 6ffeeb8cf..c8c3c9b08 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java @@ -56,6 +56,7 @@ public class SystemConfig { public static final boolean DEFAULT_SORT_ANSWER_SETS = false; public static final List DEFAULT_REPLAY_CHOICES = Collections.emptyList(); public static final boolean DEFAULT_DISABLE_NOGOOD_DELETION = false; + public static final boolean DEFAULT_ENABLE_RESTARTS = false; private String grounderName = SystemConfig.DEFAULT_GROUNDER_NAME; private String solverName = SystemConfig.DEFAULT_SOLVER_NAME; @@ -72,6 +73,7 @@ public class SystemConfig { private boolean sortAnswerSets = SystemConfig.DEFAULT_SORT_ANSWER_SETS; private List replayChoices = SystemConfig.DEFAULT_REPLAY_CHOICES; private boolean disableNoGoodDeletion = SystemConfig.DEFAULT_DISABLE_NOGOOD_DELETION; + private boolean areRestartsEnabled = SystemConfig.DEFAULT_ENABLE_RESTARTS; public String getGrounderName() { return this.grounderName; @@ -205,4 +207,12 @@ public void setDisableNoGoodDeletion(boolean disableNoGoodDeletion) { this.disableNoGoodDeletion = disableNoGoodDeletion; } + public boolean areRestartsEnabled() { + return areRestartsEnabled; + } + + public void setRestartsEnabled(boolean areRestartsEnabled) { + this.areRestartsEnabled = areRestartsEnabled; + } + } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java index 91074801d..4bd785f47 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java @@ -67,6 +67,7 @@ public class DefaultSolver extends AbstractSolver implements SolverMaintainingSt private final WritableAssignment assignment; private final GroundConflictNoGoodLearner learner; + private final MixedRestartStrategy restartStrategy; private final BranchingHeuristic branchingHeuristic; @@ -86,10 +87,16 @@ public DefaultSolver(AtomStore atomStore, Grounder grounder, NoGoodStore store, this.store = store; this.choiceManager = new ChoiceManager(assignment, store); this.learner = new GroundConflictNoGoodLearner(assignment, atomStore); + LearnedNoGoodDeletion learnedNoGoodDeletion = this.store instanceof NoGoodStoreAlphaRoaming ? ((NoGoodStoreAlphaRoaming)store).getLearnedNoGoodDeletion() : null; + if (config.areRestartsEnabled() && this.store instanceof NoGoodStoreAlphaRoaming) { + this.restartStrategy = new MixedRestartStrategy(learnedNoGoodDeletion); + } else { + this.restartStrategy = null; + } this.branchingHeuristic = chainFallbackHeuristic(grounder, assignment, random, heuristicsConfiguration); this.disableJustifications = config.isDisableJustificationSearch(); this.disableNoGoodDeletion = config.isDisableNoGoodDeletion(); - this.performanceLog = new PerformanceLog(choiceManager, (TrailAssignment) assignment, 1000); + this.performanceLog = new PerformanceLog(choiceManager, (TrailAssignment) assignment, restartStrategy, learnedNoGoodDeletion, 1000); } private BranchingHeuristic chainFallbackHeuristic(Grounder grounder, WritableAssignment assignment, Random random, HeuristicsConfiguration heuristicsConfiguration) { @@ -148,9 +155,17 @@ protected boolean tryAdvance(Consumer action) { ConflictCause conflictCause = store.propagate(); didChange |= store.didPropagate(); LOGGER.trace("Assignment after propagation is: {}", assignment); - if (!disableNoGoodDeletion && conflictCause == null) { - // Run learned NoGood deletion strategy. - store.cleanupLearnedNoGoods(); + if (conflictCause == null) { + if (!disableNoGoodDeletion) { + // Run learned NoGood deletion strategy. + store.cleanupLearnedNoGoods(); + } + if (restartStrategy != null && restartStrategy.shouldRestart()) { + // Restart if necessary. + LOGGER.trace("Restarting search."); + choiceManager.backjump(0); + restartStrategy.onRestart(); + } } if (conflictCause != null) { // Learn from conflict. @@ -244,6 +259,9 @@ private boolean learnBackjumpAddFromConflict(ConflictCause conflictCause) { } branchingHeuristic.analyzedConflict(analysisResult); + if (restartStrategy != null) { + restartStrategy.newConflictWithLbd(analysisResult.lbd); + } if (analysisResult.learnedNoGood != null) { choiceManager.backjump(analysisResult.backjumpLevel); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletion.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletion.java index a17264654..d4fa14d4c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletion.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletion.java @@ -25,6 +25,7 @@ class LearnedNoGoodDeletion { private final NoGoodStoreAlphaRoaming store; private final Assignment assignment; private int conflictCounter; + private int totalConflictsCounter; private int cleanupCounter; private int numberOfDeletedNoGoods; @@ -36,6 +37,7 @@ class LearnedNoGoodDeletion { void reset() { learnedNoGoods.clear(); conflictCounter = 0; + totalConflictsCounter = 0; cleanupCounter = 0; numberOfDeletedNoGoods = 0; } @@ -62,6 +64,7 @@ boolean needToRunNoGoodDeletion() { } void runNoGoodDeletion() { + totalConflictsCounter += conflictCounter; conflictCounter = 0; cleanupCounter++; // Reset the sequence after enough growth cycles. @@ -113,4 +116,8 @@ private boolean isLocked(WatchedNoGood noGood, Assignment assignment) { public int getNumberOfDeletedNoGoods() { return numberOfDeletedNoGoods; } + + int getNumTotalConflicts() { + return totalConflictsCounter + conflictCounter; + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java new file mode 100644 index 000000000..5e8a552ce --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java @@ -0,0 +1,81 @@ +package at.ac.tuwien.kr.alpha.solver; + +import static at.ac.tuwien.kr.alpha.Util.oops; +import static at.ac.tuwien.kr.alpha.solver.NoGoodStore.LBD_NO_VALUE; + +/** + * A restart strategy that mixes dynamic restarts and Luby sequence-based restarts. + * + * Copyright (c) 2019, the Alpha Team. + */ +public class MixedRestartStrategy { + private final LearnedNoGoodDeletion learnedNoGoodDeletion; + + // Variables for dynamic restarts. + private long fast; + private long slow; + private long currentConflictsLimit; + private int numTotalRestarts; + + // Variables for Luby-based restarts. + private int un = 1; + private int vn = 1; + private static final int LUBY_FACTOR = 32; + private int lubyLimit = LUBY_FACTOR; + + + MixedRestartStrategy(LearnedNoGoodDeletion learnedNoGoodDeletion) { + this.learnedNoGoodDeletion = learnedNoGoodDeletion; + reset(); + } + + private void reset() { + fast = 0; + slow = 0; + currentConflictsLimit = 50; + numTotalRestarts = 0; + un = 1; + vn = 1; + } + + boolean shouldRestart() { + long numTotalConflicts = learnedNoGoodDeletion.getNumTotalConflicts(); + return (numTotalConflicts > currentConflictsLimit && fast / 125 > slow / 100) + || numTotalConflicts > lubyLimit; + } + + void onRestart() { + currentConflictsLimit = learnedNoGoodDeletion.getNumTotalConflicts() + 50; + nextLuby(); + numTotalRestarts++; + } + + void newConflictWithLbd(int lbd) { + /// Below is a fast 64-bit fixed point implementation of the following: + // slow += (lbd - slow)/(double)(1<<14); + // fast += (lbd - fast)/(double)(1<<5); + // See Armin Biere's POS'15 slides. + if (lbd == LBD_NO_VALUE) { + throw oops("Conflict has no LBD value."); + } + fast -= fast >> 5; + fast += lbd << (32 - 5); + slow -= slow >> 14; + slow += lbd << (32 - 14); + } + + int getTotalRestarts() { + return numTotalRestarts; + } + + private void nextLuby() { + // Compute Luby-sequence [Knuth'12]-style. + if ((un & -un) == vn) { + un++; + vn = 1; + } else { + vn <<= 1; + } + lubyLimit = vn * LUBY_FACTOR; + } +} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java index 9c4d74601..2e43dd50e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java @@ -28,25 +28,31 @@ import org.slf4j.Logger; /** - * Collects performance data (mainly number of decisions per second) and outputs them on demand. + * Collects performance data and outputs them on demand. + * + * Copyright (c) 2019, the Alpha Team. */ public class PerformanceLog { - private ChoiceManager choiceManager; - private TrailAssignment assignment; + private final ChoiceManager choiceManager; + private final TrailAssignment assignment; + private final MixedRestartStrategy restartStrategy; + private final LearnedNoGoodDeletion learnedNoGoodDeletion; private long msBetweenOutputs; private Long timeFirstEntry; private Long timeLastPerformanceLog; private int numberOfChoicesLastPerformanceLog; + private int numberOfRestartsLastPerformanceLog; + private int numberOfConflictsLastPerformanceLog; + private int numberOfDeletedNoGoodsLastPerformanceLog; - /** - * @param msBetweenOutputs - */ - public PerformanceLog(ChoiceManager choiceManager, TrailAssignment assignment, long msBetweenOutputs) { + public PerformanceLog(ChoiceManager choiceManager, TrailAssignment assignment, MixedRestartStrategy restartStrategy, LearnedNoGoodDeletion learnedNoGoodDeletion, long msBetweenOutputs) { super(); this.choiceManager = choiceManager; this.assignment = assignment; + this.restartStrategy = restartStrategy; + this.learnedNoGoodDeletion = learnedNoGoodDeletion; this.msBetweenOutputs = msBetweenOutputs; } @@ -60,14 +66,38 @@ public void initialize() { */ public void infoIfTimeForOutput(Logger logger) { long currentTime = System.currentTimeMillis(); + if (currentTime < timeLastPerformanceLog + msBetweenOutputs) { + return; + } int currentNumberOfChoices = choiceManager.getChoices(); - if (currentTime >= timeLastPerformanceLog + msBetweenOutputs) { - logger.info("Decisions in {}s: {}", (currentTime - timeLastPerformanceLog) / 1000.0f, currentNumberOfChoices - numberOfChoicesLastPerformanceLog); - timeLastPerformanceLog = currentTime; - numberOfChoicesLastPerformanceLog = currentNumberOfChoices; - float overallTime = (currentTime - timeFirstEntry) / 1000.0f; - float decisionsPerSec = currentNumberOfChoices / overallTime; - logger.info("Overall performance: {} decisions in {}s or {} decisions per sec. Overall replayed assignments: {}.", currentNumberOfChoices, overallTime, decisionsPerSec, assignment.replayCounter); + float timeSinceLastLog = (currentTime - timeLastPerformanceLog) / 1000.0f; + logger.info("Decisions in {}s: {}", timeSinceLastLog, currentNumberOfChoices - numberOfChoicesLastPerformanceLog); + timeLastPerformanceLog = currentTime; + numberOfChoicesLastPerformanceLog = currentNumberOfChoices; + float overallTime = (currentTime - timeFirstEntry) / 1000.0f; + float decisionsPerSec = currentNumberOfChoices / overallTime; + logger.info("Overall performance: {} decisions in {}s or {} decisions per sec. Overall replayed assignments: {}.", currentNumberOfChoices, overallTime, decisionsPerSec, assignment.replayCounter); + if (restartStrategy != null) { + int totalRestarts = restartStrategy.getTotalRestarts(); + int currentNumberOfRestarts = totalRestarts - numberOfRestartsLastPerformanceLog; + logStatsPerTime(logger, "Restarts", timeSinceLastLog, overallTime, totalRestarts, currentNumberOfRestarts); + numberOfRestartsLastPerformanceLog = totalRestarts; + } + if (learnedNoGoodDeletion != null) { + int totalConflicts = learnedNoGoodDeletion.getNumTotalConflicts(); + int currentNumConflicts = totalConflicts - numberOfConflictsLastPerformanceLog; + int totalDeletedNogoods = learnedNoGoodDeletion.getNumberOfDeletedNoGoods(); + int currenDeletedNoGoods = totalDeletedNogoods - numberOfDeletedNoGoodsLastPerformanceLog; + logStatsPerTime(logger, "Conflicts", timeSinceLastLog, overallTime, totalConflicts, currentNumConflicts); + logStatsPerTime(logger, "Deleted NoGoods", timeSinceLastLog, overallTime, totalDeletedNogoods, currenDeletedNoGoods); + numberOfConflictsLastPerformanceLog = totalConflicts; + numberOfDeletedNoGoodsLastPerformanceLog = totalDeletedNogoods; } } + + private void logStatsPerTime(Logger logger, String statName, float timeSinceLastLog, float overallTime, int total, int differenceSinceLast) { + logger.info(statName + " in {}s: {}", timeSinceLastLog, differenceSinceLast); + logger.info("Overall performance: {} " + statName + " in {}s or {} " + statName + " per sec", total, overallTime, total / overallTime); + + } } \ No newline at end of file From af19a1494d35d5706f2759426af480ac522ea680 Mon Sep 17 00:00:00 2001 From: Antonius Weinzierl Date: Mon, 7 Oct 2019 14:02:28 +0200 Subject: [PATCH 46/66] Enhance performance logs to show quality of heuristic decisions. - ChainedBranchingHeuristics gives access to first-run heuristics. - HeapOfActiveAtoms reports activity increment for normalization. - VSIDSWithPhaseSaving logs * the overall, normalized decrease in activity of the seelected atoms, * the number of most-active choices thrown away because they were not active choice points at the time. - PerformanceLog prints heuristics information if DefaultSolver is runnning with VSIDSWithPhaseSaving --- .../tuwien/kr/alpha/solver/DefaultSolver.java | 2 +- .../kr/alpha/solver/PerformanceLog.java | 17 +++++++++++++- .../ChainedBranchingHeuristics.java | 4 ++++ .../solver/heuristics/HeapOfActiveAtoms.java | 4 ++++ .../heuristics/VSIDSWithPhaseSaving.java | 23 +++++++++++++++++-- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java index 4bd785f47..a47ceea44 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java @@ -96,7 +96,7 @@ public DefaultSolver(AtomStore atomStore, Grounder grounder, NoGoodStore store, this.branchingHeuristic = chainFallbackHeuristic(grounder, assignment, random, heuristicsConfiguration); this.disableJustifications = config.isDisableJustificationSearch(); this.disableNoGoodDeletion = config.isDisableNoGoodDeletion(); - this.performanceLog = new PerformanceLog(choiceManager, (TrailAssignment) assignment, restartStrategy, learnedNoGoodDeletion, 1000); + this.performanceLog = new PerformanceLog(choiceManager, (TrailAssignment) assignment, restartStrategy, learnedNoGoodDeletion, branchingHeuristic, 1000); } private BranchingHeuristic chainFallbackHeuristic(Grounder grounder, WritableAssignment assignment, Random random, HeuristicsConfiguration heuristicsConfiguration) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java index 2e43dd50e..28b9b14fe 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java @@ -25,6 +25,9 @@ */ package at.ac.tuwien.kr.alpha.solver; +import at.ac.tuwien.kr.alpha.solver.heuristics.BranchingHeuristic; +import at.ac.tuwien.kr.alpha.solver.heuristics.ChainedBranchingHeuristics; +import at.ac.tuwien.kr.alpha.solver.heuristics.VSIDSWithPhaseSaving; import org.slf4j.Logger; /** @@ -38,6 +41,7 @@ public class PerformanceLog { private final TrailAssignment assignment; private final MixedRestartStrategy restartStrategy; private final LearnedNoGoodDeletion learnedNoGoodDeletion; + private final BranchingHeuristic branchingHeuristic; private long msBetweenOutputs; private Long timeFirstEntry; @@ -47,12 +51,13 @@ public class PerformanceLog { private int numberOfConflictsLastPerformanceLog; private int numberOfDeletedNoGoodsLastPerformanceLog; - public PerformanceLog(ChoiceManager choiceManager, TrailAssignment assignment, MixedRestartStrategy restartStrategy, LearnedNoGoodDeletion learnedNoGoodDeletion, long msBetweenOutputs) { + public PerformanceLog(ChoiceManager choiceManager, TrailAssignment assignment, MixedRestartStrategy restartStrategy, LearnedNoGoodDeletion learnedNoGoodDeletion, BranchingHeuristic branchingHeuristic, long msBetweenOutputs) { super(); this.choiceManager = choiceManager; this.assignment = assignment; this.restartStrategy = restartStrategy; this.learnedNoGoodDeletion = learnedNoGoodDeletion; + this.branchingHeuristic = branchingHeuristic; this.msBetweenOutputs = msBetweenOutputs; } @@ -83,6 +88,16 @@ public void infoIfTimeForOutput(Logger logger) { logStatsPerTime(logger, "Restarts", timeSinceLastLog, overallTime, totalRestarts, currentNumberOfRestarts); numberOfRestartsLastPerformanceLog = totalRestarts; } + if (branchingHeuristic != null && branchingHeuristic instanceof ChainedBranchingHeuristics) { + BranchingHeuristic firstHeuristic = ((ChainedBranchingHeuristics) branchingHeuristic).getFirstElement(); + if (firstHeuristic instanceof VSIDSWithPhaseSaving) { + VSIDSWithPhaseSaving vsidsWithPhaseSaving = (VSIDSWithPhaseSaving) firstHeuristic; + long numThrownAway = vsidsWithPhaseSaving.getNumThrownAway(); + double activityDecrease = vsidsWithPhaseSaving.getActivityDecrease(); + logger.info("Heuristic threw away {} preferred choices.", numThrownAway); + logger.info("Atom activity decreased overall by {} or {} per choice on average", activityDecrease, activityDecrease / currentNumberOfChoices); + } + } if (learnedNoGoodDeletion != null) { int totalConflicts = learnedNoGoodDeletion.getNumTotalConflicts(); int currentNumConflicts = totalConflicts - numberOfConflictsLastPerformanceLog; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java index b8cfb0919..9a121f348 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ChainedBranchingHeuristics.java @@ -106,6 +106,10 @@ public static ChainedBranchingHeuristics chainOf(BranchingHeuristic... branching return new ChainedBranchingHeuristics(branchingHeuristics); } + public BranchingHeuristic getFirstElement() { + return chain.get(0); + } + /** * Returns a mapping from individual heuristics to number of decisions made by them. */ diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java index 56db8f727..acadd6920 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java @@ -103,6 +103,10 @@ public double getDecayFactor() { return decayFactor; } + double getCurrentActivityIncrement() { + return currentActivityIncrement; + } + /** * @see #getDecayFactor() */ diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java index 80512659d..dddcd5f3d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java @@ -58,11 +58,12 @@ public class VSIDSWithPhaseSaving implements ActivityBasedBranchingHeuristic { protected final Assignment assignment; protected final ChoiceManager choiceManager; - private final HeapOfActiveAtoms heapOfActiveAtoms; - private final Collection bufferedNoGoods = new ArrayList<>(); + private double activityDecrease; + private long numThrownAway; + private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, HeapOfActiveAtoms heapOfActiveAtoms, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) { this.assignment = assignment; this.choiceManager = choiceManager; @@ -111,6 +112,14 @@ private void ingestBufferedNoGoods() { bufferedNoGoods.clear(); } + public double getActivityDecrease() { + return activityDecrease; + } + + public long getNumThrownAway() { + return numThrownAway; + } + /** * {@link VSIDSWithPhaseSaving} works like {@link VSIDS} for selecting an atom but uses the saved phase to * determine the truth value to choose. @@ -128,10 +137,20 @@ public int chooseLiteral() { private int chooseAtom() { ingestBufferedNoGoods(); Integer mostActiveAtom; + double maxActivity = 0.0f; while ((mostActiveAtom = heapOfActiveAtoms.getMostActiveAtom()) != null) { + double activity = heapOfActiveAtoms.getActivity(atomToLiteral(mostActiveAtom)); + if (activity > maxActivity) { + maxActivity = activity; + } if (choiceManager.isActiveChoiceAtom(mostActiveAtom)) { + if (maxActivity > activity) { + double lostActitivyNormalized = (maxActivity - activity) / heapOfActiveAtoms.getCurrentActivityIncrement(); + activityDecrease += lostActitivyNormalized; + } return mostActiveAtom; } + numThrownAway++; } return DEFAULT_CHOICE_ATOM; } From 5179a1a3b4631ae747ce21f99e4fd3dee897ea5b Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 15 Oct 2019 10:19:03 +0200 Subject: [PATCH 47/66] Use constants instead of magic numbers. --- .../grounder/heuristics/GrounderHeuristicsConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java index fae106b41..501d61b17 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java @@ -59,8 +59,8 @@ public class GrounderHeuristicsConfiguration { public GrounderHeuristicsConfiguration() { super(); - this.toleranceConstraints = 0; - this.toleranceRules = 0; + this.toleranceConstraints = STRICT_INT; + this.toleranceRules = STRICT_INT; } /** From 567cc8e33227373a3a0837139a6ac41512e48332 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 15 Oct 2019 11:51:54 +0200 Subject: [PATCH 48/66] Make accumulator grounding strategy configurable via CLI (-dir / --disableInstanceRemoval) --- src/main/java/at/ac/tuwien/kr/alpha/Alpha.java | 1 + .../kr/alpha/config/CommandLineParser.java | 9 +++++++++ .../ac/tuwien/kr/alpha/config/SystemConfig.java | 9 +++++++++ .../tuwien/kr/alpha/grounder/NaiveGrounder.java | 3 +-- .../GrounderHeuristicsConfiguration.java | 16 ++++++++++++++-- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java b/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java index 8a76389c1..a355ca45a 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Alpha.java @@ -106,6 +106,7 @@ public Solver prepareSolverFor(Program program, java.util.function.Predicate
 abortAction) {
 		this.globalOptionHandlers.put(CommandLineParser.OPT_NO_NOGOOD_DELETION.getOpt(), this::handleNoNoGoodDeletion);
 		this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_TOLERANCE_CONSTRAINTS.getOpt(), this::handleGrounderToleranceConstraints);
 		this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_TOLERANCE_RULES.getOpt(), this::handleGrounderToleranceRules);
+		this.globalOptionHandlers.put(CommandLineParser.OPT_NO_INSTANCE_REMOVAL.getOpt(), this::handleNoInstanceRemoval);
 
 		this.inputOptionHandlers.put(CommandLineParser.OPT_NUM_ANSWER_SETS.getOpt(), this::handleNumAnswerSets);
 		this.inputOptionHandlers.put(CommandLineParser.OPT_INPUT.getOpt(), this::handleInput);
@@ -381,4 +386,8 @@ private void handleGrounderToleranceRules(Option opt, SystemConfig cfg) {
 		cfg.setGrounderToleranceRules(grounderToleranceRules);
 	}
 
+	private void handleNoInstanceRemoval(Option opt, SystemConfig cfg) {
+		cfg.setDisableInstanceRemoval(true);
+	}
+
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java
index 0700a6f14..6320219d0 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java
@@ -59,6 +59,7 @@ public class SystemConfig {
 	public static final boolean DEFAULT_DISABLE_NOGOOD_DELETION = false;
 	public static final String DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS = GrounderHeuristicsConfiguration.STRICT_STRING;
 	public static final String DEFAULT_GROUNDER_TOLERANCE_RULES = GrounderHeuristicsConfiguration.STRICT_STRING;
+	public static final boolean DEFAULT_DISABLE_INSTANCE_REMOVAL = false;
 
 	private String grounderName = SystemConfig.DEFAULT_GROUNDER_NAME;
 	private String solverName = SystemConfig.DEFAULT_SOLVER_NAME;
@@ -77,6 +78,7 @@ public class SystemConfig {
 	private boolean disableNoGoodDeletion = SystemConfig.DEFAULT_DISABLE_NOGOOD_DELETION;
 	private String grounderToleranceConstraints = DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS;
 	private String grounderToleranceRules = DEFAULT_GROUNDER_TOLERANCE_RULES;
+	private boolean disableInstanceRemoval = DEFAULT_DISABLE_INSTANCE_REMOVAL;
 
 	public String getGrounderName() {
 		return this.grounderName;
@@ -226,4 +228,11 @@ public void setGrounderToleranceRules(String grounderToleranceRules) {
 		this.grounderToleranceRules = grounderToleranceRules;
 	}
 
+	public boolean isDisableInstanceRemoval() {
+		return disableInstanceRemoval;
+	}
+
+	public void setDisableInstanceRemoval(boolean disableInstanceRemoval) {
+		this.disableInstanceRemoval = disableInstanceRemoval;
+	}
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
index 9a31af449..f3d3f297c 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
@@ -74,7 +74,6 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr
 
 	private ArrayList fixedRules = new ArrayList<>();
 	private LinkedHashSet removeAfterObtainingNewNoGoods = new LinkedHashSet<>();
-	private boolean disableInstanceRemoval;
 	private final boolean useCountingGridNormalization;
 	private final boolean debugInternalChecks;
 
@@ -567,7 +566,7 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As
 			final int atomId = atomStore.putIfAbsent(substitute);
 			ThriceTruth truth = currentAssignment.isAssigned(atomId) ? currentAssignment.getTruth(atomId) : null;
 
-			if (disableInstanceRemoval) {
+			if (heuristicsConfiguration.isDisableInstanceRemoval()) {
 				final Instance instance = new Instance(substitute.getTerms());
 				boolean isInWorkingMemory = workingMemory.get(substitute, true).containsInstance(instance);
 				if (isInWorkingMemory) {
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java
index 501d61b17..5cc1fa448 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicsConfiguration.java
@@ -46,6 +46,9 @@
  * The default value for both parameters is {@code 0}, which means that only those rules and constraints are
  * grounded whose positive body is already satisfied.
  *
+ * The additional parameter {@link #disableInstanceRemoval} is a switch for the accumulator grounding strategy
+ * which disables the removal of instances from the grounder memory in certain cases.
+ *
  */
 public class GrounderHeuristicsConfiguration {
 
@@ -56,6 +59,7 @@ public class GrounderHeuristicsConfiguration {
 	
 	private int toleranceConstraints;
 	private int toleranceRules;
+	private boolean disableInstanceRemoval;
 	
 	public GrounderHeuristicsConfiguration() {
 		super();
@@ -102,7 +106,15 @@ public int getTolerance(boolean ruleIsConstraint) {
 	public boolean isLax(boolean ruleIsConstraint) {
 		return getTolerance(ruleIsConstraint) != STRICT_INT;
 	}
-	
+
+	public boolean isDisableInstanceRemoval() {
+		return disableInstanceRemoval;
+	}
+
+	public void setDisableInstanceRemoval(boolean disableInstanceRemoval) {
+		this.disableInstanceRemoval = disableInstanceRemoval;
+	}
+
 	public static GrounderHeuristicsConfiguration strict() {
 		return new GrounderHeuristicsConfiguration(STRICT_INT, STRICT_INT);
 	}
@@ -131,7 +143,7 @@ private static int parseTolerance(String tolerance) {
 	
 	@Override
 	public String toString() {
-		return this.getClass().getSimpleName() + "(toleranceConstraints=" + toleranceConstraints + ",toleranceRules=" + toleranceRules + ")";
+		return this.getClass().getSimpleName() + "(toleranceConstraints=" + toleranceConstraints + ",toleranceRules=" + toleranceRules + ",disableInstanceRemoval=" + disableInstanceRemoval + ")";
 	}
 
 }

From e479e44cbc07a06c9fc497a1f0eb9e4ec6754220 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Tue, 15 Oct 2019 13:13:13 +0200
Subject: [PATCH 49/66] Test several grounder configurations.

---
 .../kr/alpha/solver/AbstractSolverTests.java  | 26 ++++++++++++++++---
 1 file changed, 22 insertions(+), 4 deletions(-)

diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java
index 9fc0ec829..4a0bd9e22 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java
@@ -91,7 +91,7 @@ private static String[] getProperty(String subKey, String def) {
 		return System.getProperty("test." + subKey, def).split(",");
 	}
 
-	@Parameters(name = "{0}/{1}/{2}/{3}")
+	@Parameters(name = "{0}/{1}/{2}/{3}/seed={4}/checks={5}/gtc={6}/gtr={7}/dir={8}")
 	public static Collection parameters() {
 		// Check whether we are running in a CI environment.
 		boolean ci = Boolean.valueOf(System.getenv("CI"));
@@ -100,6 +100,9 @@ public static Collection parameters() {
 		String[] grounders = getProperty("grounders", "naive");
 		String[] stores = getProperty("stores", ci ? "alpharoaming,naive" : "alpharoaming");
 		String[] heuristics = getProperty("heuristics", ci ? "ALL" : "NAIVE,VSIDS");
+		String[] gtcValues = getProperty("grounderToleranceConstraints", ci ? "strict,1,lax" : "strict,lax");
+		String[] gtrValues = getProperty("grounderToleranceRules", ci ? "strict,1,lax" : "strict");
+		String[] dirValues = getProperty("disableInstanceRemoval", ci ? "false,true" : "false");
 
 		// "ALL" is a magic value that will be expanded to contain all heuristics.
 		if ("ALL".equals(heuristics[0])) {
@@ -128,9 +131,15 @@ public static Collection parameters() {
 			for (String grounder : grounders) {
 				for (String store : stores) {
 					for (String heuristic : heuristics) {
-						factories.add(new Object[]{
-							solver, grounder, store, BranchingHeuristicFactory.Heuristic.valueOf(heuristic), seed, checks
-						});
+						for (String gtc : gtcValues) {
+							for (String gtr : gtrValues) {
+								for (String dir : dirValues) {
+									factories.add(new Object[]{
+											solver, grounder, store, BranchingHeuristicFactory.Heuristic.valueOf(heuristic), seed, checks, gtc, gtr, Boolean.valueOf(dir)
+									});
+								}
+							}
+						}
 					}
 				}
 			}
@@ -157,6 +166,15 @@ public static Collection parameters() {
 	@Parameter(5)
 	public boolean checks;
 
+	@Parameter(6)
+	public String grounderToleranceConstraints;
+
+	@Parameter(7)
+	public String grounderToleranceRules;
+
+	@Parameter(8)
+	public boolean disableInstanceRemoval;
+
 	protected Solver getInstance(AtomStore atomStore, Grounder grounder) {
 		return SolverFactory.getInstance(buildSystemConfig(), atomStore, grounder);
 	}

From 66c653c42da48f162131d827253077412b621065 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Tue, 15 Oct 2019 15:12:13 +0200
Subject: [PATCH 50/66] Refactor
 NaiveGrounder#storeAtomAndTerminateIfAtomDoesNotHold

---
 .../kr/alpha/grounder/NaiveGrounder.java      | 40 +++++++++++++------
 1 file changed, 28 insertions(+), 12 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
index f3d3f297c..7856d853a 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
@@ -46,7 +46,6 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import static at.ac.tuwien.kr.alpha.Util.oops;
 import static at.ac.tuwien.kr.alpha.common.Literals.atomOf;
@@ -549,19 +548,23 @@ private BindingResult createBindings(RuleGroundingOrder groundingOrder, int orde
 				throw oops("Grounded atom should be ground but is not");
 			}
 
-			AtomicInteger atomicRemainingTolerance = new AtomicInteger(remainingTolerance);	// TODO: done this way for call-by-reference, should be refactored
 			if (factsFromProgram.get(substitutedAtom.getPredicate()) == null || !factsFromProgram.get(substitutedAtom.getPredicate()).contains(new Instance(substitutedAtom.getTerms()))) {
-				if (storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, atomicRemainingTolerance)) {
+				final TerminateOrTolerate terminateOrTolerate = storeAtomAndTerminateIfAtomDoesNotHold(substitutedAtom, currentAssignment, remainingTolerance);
+				if (terminateOrTolerate.decrementTolerance) {
+					remainingTolerance--;
+				}
+				if (terminateOrTolerate.terminate) {
 					continue;
 				}
 			}
-			bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, atomicRemainingTolerance.get(), unified, currentAssignment));
+			bindingResult.add(advanceAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, unified, currentAssignment));
 		}
 
 		return bindingResult;
 	}
 
-	private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, Assignment currentAssignment, AtomicInteger remainingTolerance) {
+	private TerminateOrTolerate storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, final Assignment currentAssignment, final int remainingTolerance) {
+		int decrementedTolerance = remainingTolerance;
 		if (currentAssignment != null) { // if we are not in bootstrapping
 			final int atomId = atomStore.putIfAbsent(substitute);
 			ThriceTruth truth = currentAssignment.isAssigned(atomId) ? currentAssignment.getTruth(atomId) : null;
@@ -570,26 +573,39 @@ private boolean storeAtomAndTerminateIfAtomDoesNotHold(final Atom substitute, As
 				final Instance instance = new Instance(substitute.getTerms());
 				boolean isInWorkingMemory = workingMemory.get(substitute, true).containsInstance(instance);
 				if (isInWorkingMemory) {
-					return false;
+					return new TerminateOrTolerate(false, false);
+				}
+				if (truth != null && !truth.toBoolean()) {
+					return new TerminateOrTolerate(true, false);
 				}
-				if (truth != null && !truth.toBoolean() || remainingTolerance.decrementAndGet() < 0) {
+				if (--decrementedTolerance < 0) {
 					// terminate if more positive atoms are unsatisfied as tolerated by the heuristic
-					return true;
+					return new TerminateOrTolerate(true, true);
 				}
 			} else {
 				if (truth == null || !truth.toBoolean()) {
 					// Atom currently does not hold
 					removeAfterObtainingNewNoGoods.add(substitute);
 				}
-				if (truth == null && remainingTolerance.decrementAndGet() < 0) {
+				if (truth == null && --decrementedTolerance < 0) {
 					// terminate if more positive atoms are unsatisfied as tolerated by the heuristic
-					return true;
+					return new TerminateOrTolerate(true, true);
 				}
 				// terminate if positive body atom is assigned false
-				return truth != null && !truth.toBoolean();
+				return new TerminateOrTolerate(truth != null && !truth.toBoolean(), decrementedTolerance < remainingTolerance);
 			}
 		}
-		return false;
+		return new TerminateOrTolerate(false, decrementedTolerance < remainingTolerance);
+	}
+
+	private static class TerminateOrTolerate {
+		boolean terminate;
+		boolean decrementTolerance;
+
+		TerminateOrTolerate(boolean terminate, boolean decrementTolerance) {
+			this.terminate = terminate;
+			this.decrementTolerance = decrementTolerance;
+		}
 	}
 
 	@Override

From cbe56aac12eea29bb301ffd1c441c53efc72fcba Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Tue, 15 Oct 2019 16:17:41 +0200
Subject: [PATCH 51/66] Fix JavaDoc

---
 .../java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java
index a17a0103f..6504a7bcc 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java
@@ -80,7 +80,7 @@ public Literal getLiteralAtOrderPosition(int orderPosition) {
 	}
 
 	/**
-	 * @return the position in {@link #getOtherLiterals()} from which on all variables are bound
+	 * @return the zero-based position from which on all variables are bound in list of literals except the starting literal
 	 */
 	public int getPositionFromWhichAllVarsAreBound() {
 		return positionLastVarBound + 1;

From 41f52ac2443e591425905ae7f13797ac1e6f2582 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Tue, 15 Oct 2019 16:20:09 +0200
Subject: [PATCH 52/66] Improve NaiveGrounderTest

---
 .../kr/alpha/grounder/NaiveGrounder.java      |  6 +-
 .../kr/alpha/grounder/NaiveGrounderTest.java  | 72 +++++++++++--------
 2 files changed, 46 insertions(+), 32 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
index 7856d853a..80fa10758 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
@@ -289,7 +289,7 @@ public AnswerSet assignmentToAnswerSet(Iterable trueAtoms) {
 	 * Prepares facts of the input program for joining and derives all NoGoods representing ground rules. May only be called once.
 	 * @return
 	 */
-	private HashMap bootstrap() {
+	HashMap bootstrap() {
 		final HashMap groundNogoods = new LinkedHashMap<>();
 
 		for (Predicate predicate : factsFromProgram.keySet()) {
@@ -407,7 +407,7 @@ public int register(NoGood noGood) {
 		return registry.register(noGood);
 	}
 
-	private BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, Substitution partialSubstitution, Assignment currentAssignment) {
+	BindingResult bindNextAtomInRule(NonGroundRule rule, RuleGroundingOrder groundingOrder, Substitution partialSubstitution, Assignment currentAssignment) {
 		int tolerance = heuristicsConfiguration.getTolerance(rule.isConstraint());
 		if (tolerance < 0) {
 			tolerance = Integer.MAX_VALUE;
@@ -714,7 +714,7 @@ private static class FirstBindingAtom {
 		}
 	}
 
-	private static class BindingResult {
+	static class BindingResult {
 		final List generatedSubstitutions = new ArrayList<>();
 		final List numbersOfUnassignedPositiveBodyAtoms = new ArrayList<>();
 		
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
index 00fedf250..32d11c0ac 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
@@ -27,6 +27,8 @@
 
 import at.ac.tuwien.kr.alpha.common.*;
 import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom;
+import at.ac.tuwien.kr.alpha.common.atoms.Literal;
+import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm;
 import at.ac.tuwien.kr.alpha.grounder.heuristics.GrounderHeuristicsConfiguration;
 import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser;
 import at.ac.tuwien.kr.alpha.solver.ThriceTruth;
@@ -40,7 +42,8 @@
 import java.util.Collections;
 import java.util.Map;
 
-import static at.ac.tuwien.kr.alpha.TestUtil.*;
+import static at.ac.tuwien.kr.alpha.TestUtil.atom;
+import static at.ac.tuwien.kr.alpha.TestUtil.literal;
 import static org.junit.Assert.*;
 
 /**
@@ -49,7 +52,7 @@
  * Some test cases use atoms of the something/1 predicate to trick the grounder
  * into believing that other atoms might become true. This is fragile because future implementations
  * of preprocessing techniques might render this trick useless.
- * TODO: make less fragile
+ * If unit tests in this class begin to fail due to such improvements to preprocessing, this issue must be addressed.
  */
 public class NaiveGrounderTest {
 	private static final ProgramParser PARSER = new ProgramParser();
@@ -116,17 +119,16 @@ public void groundConstraintAlreadyGround() {
 		Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true);
 		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore));
 		int litB = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("b", 0))));
-		assertTrue(noGoods.containsValue(NoGood.fromConstraint(Arrays.asList(litB), Collections.emptyList())));
+		assertTrue(noGoods.containsValue(NoGood.fromConstraint(Collections.singletonList(litB), Collections.emptyList())));
 	}
 
-	@Test(expected = UnsupportedOperationException.class)
-	@Ignore("Currently, NaiveGrounder tries to escape this situation instead of throwing an exception")
+	@Test
 	public void avoidDeadEndsWithLaxGrounderHeuristic() {
 		RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"),
 				Arrays.asList(literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")), -1, false);
 		RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"),
 				Arrays.asList(literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")), -1, false);
-		testDeadEnd(groundingOrderP1, groundingOrderQ1, false);
+		testDeadEnd(groundingOrderP1, groundingOrderQ1, true);
 	}
 
 	@Test
@@ -148,13 +150,20 @@ private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder
 		NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, p -> true, GrounderHeuristicsConfiguration.lax(), true);
 
 		NonGroundRule nonGroundRule = grounder.getNonGroundRule(0);
-		nonGroundRule.groundingOrder.groundingOrders.put(literal("p1", "X"), groundingOrderP1);
-		nonGroundRule.groundingOrder.groundingOrders.put(literal("q1", "Y"), groundingOrderQ1);
+		final Literal litP1X = literal("p1", "X");
+		final Literal litQ1Y = literal("q1", "Y");
+		nonGroundRule.groundingOrder.groundingOrders.put(litP1X, groundingOrderP1);
+		nonGroundRule.groundingOrder.groundingOrders.put(litQ1Y, groundingOrderQ1);
 
+		grounder.bootstrap();
 		TrailAssignment currentAssignment = new TrailAssignment(atomStore);
-		Map noGoods = grounder.getNoGoods(currentAssignment);
-		printNoGoods(atomStore, noGoods.values());
-		assertEquals(expectNoGoods, !noGoods.isEmpty());
+		final Substitution substP1X1 = Substitution.unify(litP1X, new Instance(ConstantTerm.getInstance(1)), new Substitution());
+		final Substitution substQ1Y1 = Substitution.unify(litQ1Y, new Instance(ConstantTerm.getInstance(1)), new Substitution());
+		final NaiveGrounder.BindingResult bindingResultP1 = grounder.bindNextAtomInRule(nonGroundRule, groundingOrderP1, substP1X1, currentAssignment);
+		final NaiveGrounder.BindingResult bindingResultQ1 = grounder.bindNextAtomInRule(nonGroundRule, groundingOrderQ1, substQ1Y1, currentAssignment);
+
+		assertEquals(expectNoGoods, bindingResultP1.size() > 0);
+		assertEquals(expectNoGoods, bindingResultQ1.size() > 0);
 	}
 
 	@Test
@@ -162,7 +171,7 @@ public void testGroundingOfRuleSwitchedOffByFalsePositiveBody() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X). "
 				+ "b(X) :- something(X). ");
-		testIfGrounderGroundsRule(program, ThriceTruth.FALSE, false);
+		testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.FALSE, false);
 	}
 
 	@Test
@@ -170,7 +179,7 @@ public void testGroundingOfRuleNotSwitchedOffByTruePositiveBody() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X). "
 				+ "b(X) :- something(X). ");
-		testIfGrounderGroundsRule(program, ThriceTruth.TRUE, true);
+		testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.TRUE, true);
 	}
 
 	@Test
@@ -179,7 +188,7 @@ public void testGroundingOfRuleSwitchedOffByTrueNegativeBody() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), not b(X). "
 				+ "b(X) :- something(X). ");
-		testIfGrounderGroundsRule(program, ThriceTruth.TRUE, false);
+		testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.TRUE, false);
 	}
 
 	@Test
@@ -187,10 +196,11 @@ public void testGroundingOfRuleNotSwitchedOffByFalseNegativeBody() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), not b(X). "
 				+ "b(X) :- something(X). ");
-		testIfGrounderGroundsRule(program, ThriceTruth.FALSE, true);
+
+		testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.FALSE, true);
 	}
 
-	private void testIfGrounderGroundsRule(Program program, ThriceTruth bTruth, boolean expectNoGoods) {
+	private void testIfGrounderGroundsRule(Program program, int ruleID, Literal startingLiteral, int startingInstance, ThriceTruth bTruth, boolean expectNoGoods) {
 		AtomStore atomStore = new AtomStoreImpl();
 		TrailAssignment currentAssignment = new TrailAssignment(atomStore);
 		NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, p -> true, GrounderHeuristicsConfiguration.lax(), true);
@@ -199,9 +209,11 @@ private void testIfGrounderGroundsRule(Program program, ThriceTruth bTruth, bool
 		currentAssignment.growForMaxAtomId();
 		currentAssignment.assign(b, bTruth);
 
-		Map noGoods = grounder.getNoGoods(currentAssignment);
-		printNoGoods(atomStore, noGoods.values());
-		assertEquals(expectNoGoods, !noGoods.isEmpty());
+		grounder.bootstrap();
+		final NonGroundRule nonGroundRule = grounder.getNonGroundRule(ruleID);
+		final Substitution substStartingLiteral = Substitution.unify(startingLiteral, new Instance(ConstantTerm.getInstance(startingInstance)), new Substitution());
+		final NaiveGrounder.BindingResult bindingResult = grounder.bindNextAtomInRule(nonGroundRule, nonGroundRule.groundingOrder.groundingOrders.get(startingLiteral), substStartingLiteral, currentAssignment);
+		assertEquals(expectNoGoods, bindingResult.size() > 0);
 	}
 	
 	@Test
@@ -209,7 +221,7 @@ public void testLaxGrounderHeuristicTolerance_0_reject() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X). "
 				+ "b(X) :- something(X).");
-		testLaxGrounderHeuristicTolerance(program, 0, 0, false);
+		testLaxGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 0, 0, false);
 	}
 	
 	@Test
@@ -217,7 +229,7 @@ public void testLaxGrounderHeuristicTolerance_1_accept() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X). "
 				+ "b(X) :- something(X).");
-		testLaxGrounderHeuristicTolerance(program, 1, 0, true);
+		testLaxGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 1, 0, true);
 	}
 	
 	@Test
@@ -225,7 +237,7 @@ public void testLaxGrounderHeuristicTolerance_1_reject() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X), b(X+1). "
 				+ "b(X) :- something(X).");
-		testLaxGrounderHeuristicTolerance(program, 1, 0, false);
+		testLaxGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 1, 0, false);
 	}
 	
 	@Test
@@ -233,7 +245,7 @@ public void testLaxGrounderHeuristicTolerance_2_accept() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X), b(X+1). "
 				+ "b(X) :- something(X).");
-		testLaxGrounderHeuristicTolerance(program, 2, 0, true);
+		testLaxGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 2, 0, true);
 	}
 
 	@Test
@@ -241,7 +253,7 @@ public void testLaxGrounderHeuristicTolerance_2_reject() {
 		Program program = PARSER.parse("a(1). "
 				+ "c(X) :- a(X), b(X), b(X+1), b(X+2). "
 				+ "b(X) :- something(X).");
-		testLaxGrounderHeuristicTolerance(program, 2, 0, false);
+		testLaxGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 2, 0, false);
 	}
 
 	@Test
@@ -249,10 +261,10 @@ public void testLaxGrounderHeuristicTolerance_2_accept_multiple_facts_of_same_va
 		Program program = PARSER.parse("a(1). b(1). "
 				+ "c(X) :- a(X), b(X), b(X+1), b(X+2). "
 				+ "b(X) :- something(X).");
-		testLaxGrounderHeuristicTolerance(program, 2, 0, true);
+		testLaxGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 2, 0, true);
 	}
 
-	private void testLaxGrounderHeuristicTolerance(Program program, int tolerance, int nTrueBs, boolean expectNoGoods) {
+	private void testLaxGrounderHeuristicTolerance(Program program, int ruleID, Literal startingLiteral, int startingInstance, int tolerance, int nTrueBs, boolean expectNoGoods) {
 		AtomStore atomStore = new AtomStoreImpl();
 		TrailAssignment currentAssignment = new TrailAssignment(atomStore);
 		GrounderHeuristicsConfiguration heuristicConfiguration = GrounderHeuristicsConfiguration.getInstance(tolerance, tolerance);
@@ -264,9 +276,11 @@ private void testLaxGrounderHeuristicTolerance(Program program, int tolerance, i
 			currentAssignment.assign(b, ThriceTruth.TRUE);
 		}
 
-		Map noGoods = grounder.getNoGoods(currentAssignment);
-		printNoGoods(atomStore, noGoods.values());
-		assertEquals(expectNoGoods, !noGoods.isEmpty());
+		grounder.bootstrap();
+		final NonGroundRule nonGroundRule = grounder.getNonGroundRule(ruleID);
+		final Substitution substStartingLiteral = Substitution.unify(startingLiteral, new Instance(ConstantTerm.getInstance(startingInstance)), new Substitution());
+		final NaiveGrounder.BindingResult bindingResult = grounder.bindNextAtomInRule(nonGroundRule, nonGroundRule.groundingOrder.groundingOrders.get(startingLiteral), substStartingLiteral, currentAssignment);
+		assertEquals(expectNoGoods, bindingResult.size() > 0);
 	}
 
 	private void assertExistsNoGoodContaining(Collection noGoods, int literal) {

From cf59e9428cd42d10ecacee0614257c91103ac550 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Wed, 16 Oct 2019 11:24:33 +0200
Subject: [PATCH 53/66] Reduce load of unit tests (on CI).

---
 .../kr/alpha/solver/AbstractSolverTests.java   |  4 ++--
 .../tuwien/kr/alpha/solver/PigeonHoleTest.java | 18 +++++++++++++-----
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java
index 4a0bd9e22..96a819b3b 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/AbstractSolverTests.java
@@ -100,8 +100,8 @@ public static Collection parameters() {
 		String[] grounders = getProperty("grounders", "naive");
 		String[] stores = getProperty("stores", ci ? "alpharoaming,naive" : "alpharoaming");
 		String[] heuristics = getProperty("heuristics", ci ? "ALL" : "NAIVE,VSIDS");
-		String[] gtcValues = getProperty("grounderToleranceConstraints", ci ? "strict,1,lax" : "strict,lax");
-		String[] gtrValues = getProperty("grounderToleranceRules", ci ? "strict,1,lax" : "strict");
+		String[] gtcValues = getProperty("grounderToleranceConstraints", "strict,lax");
+		String[] gtrValues = getProperty("grounderToleranceRules", "strict");
 		String[] dirValues = getProperty("disableInstanceRemoval", ci ? "false,true" : "false");
 
 		// "ALL" is a magic value that will be expanded to contain all heuristics.
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java
index 418412e2d..2febab2dc 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java
@@ -1,17 +1,17 @@
 /**
  * Copyright (c) 2017 Siemens AG
  * All rights reserved.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
- * 
+ *
  * 1) Redistributions of source code must retain the above copyright notice, this
  *    list of conditions and the following disclaimer.
- * 
+ *
  * 2) 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.
- * 
+ *
  * 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
@@ -26,6 +26,7 @@
 package at.ac.tuwien.kr.alpha.solver;
 
 import at.ac.tuwien.kr.alpha.common.AnswerSet;
+import at.ac.tuwien.kr.alpha.solver.heuristics.BranchingHeuristicFactory;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -36,44 +37,51 @@
 import java.util.stream.Collectors;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 /**
  * Tests {@link AbstractSolver} using some pigeon-hole test cases (see https://en.wikipedia.org/wiki/Pigeonhole_principle).
- *
  */
 public class PigeonHoleTest extends AbstractSolverTests {
 	@Test(timeout = 1000)
 	public void test2Pigeons2Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(2, 2);
 	}
 
 	@Test(timeout = 1000)
 	public void test3Pigeons2Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(3, 2);
 	}
 
 	@Test(timeout = 1000)
 	public void test2Pigeons3Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(2, 3);
 	}
 
 	@Test(timeout = 1000)
 	public void test3Pigeons3Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(3, 3);
 	}
 
 	@Test(timeout = 3000)
 	public void test4Pigeons3Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(4, 3);
 	}
 
 	@Test(timeout = 3000)
 	public void test3Pigeons4Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(3, 4);
 	}
 
 	@Test(timeout = 3000)
 	public void test4Pigeons4Holes() throws IOException {
+		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(4, 4);
 	}
 

From 8a4c93cea81feb6239e596ab7afbebab34dd67eb Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Wed, 16 Oct 2019 11:31:21 +0200
Subject: [PATCH 54/66] Increase PigeonHoleTest time limits.

---
 .../ac/tuwien/kr/alpha/solver/PigeonHoleTest.java  | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java
index 2febab2dc..48c9857b9 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/PigeonHoleTest.java
@@ -43,43 +43,43 @@
  * Tests {@link AbstractSolver} using some pigeon-hole test cases (see https://en.wikipedia.org/wiki/Pigeonhole_principle).
  */
 public class PigeonHoleTest extends AbstractSolverTests {
-	@Test(timeout = 1000)
+	@Test(timeout = 5000)
 	public void test2Pigeons2Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(2, 2);
 	}
 
-	@Test(timeout = 1000)
+	@Test(timeout = 5000)
 	public void test3Pigeons2Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(3, 2);
 	}
 
-	@Test(timeout = 1000)
+	@Test(timeout = 5000)
 	public void test2Pigeons3Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(2, 3);
 	}
 
-	@Test(timeout = 1000)
+	@Test(timeout = 10000)
 	public void test3Pigeons3Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(3, 3);
 	}
 
-	@Test(timeout = 3000)
+	@Test(timeout = 10000)
 	public void test4Pigeons3Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(4, 3);
 	}
 
-	@Test(timeout = 3000)
+	@Test(timeout = 10000)
 	public void test3Pigeons4Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(3, 4);
 	}
 
-	@Test(timeout = 3000)
+	@Test(timeout = 10000)
 	public void test4Pigeons4Holes() throws IOException {
 		assumeTrue(heuristic == BranchingHeuristicFactory.Heuristic.VSIDS);
 		testPigeonsHoles(4, 4);

From 313b10e2c9f569535d6bb954494a2dd946a4f02d Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Wed, 16 Oct 2019 12:56:21 +0200
Subject: [PATCH 55/66] Refactor NaiveGrounder#bindNextAtomInRule.

---
 .../ac/tuwien/kr/alpha/grounder/NaiveGrounder.java | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
index 80fa10758..35d78a873 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
@@ -449,12 +449,7 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int
 		if (currentLiteral instanceof FixedInterpretationLiteral) {
 			// Generate all substitutions for the builtin/external/interval atom.
 			FixedInterpretationLiteral substitutedLiteral = (FixedInterpretationLiteral)currentLiteral.substitute(partialSubstitution);
-			// TODO: this has to be improved before merging into master:
-			if (!substitutedLiteral.isGround() &&
-					!(substitutedLiteral instanceof ComparisonLiteral && ((ComparisonLiteral)substitutedLiteral).isLeftOrRightAssigning()) &&
-					!(substitutedLiteral instanceof IntervalLiteral && substitutedLiteral.getTerms().get(0).isGround()) &&
-					!(substitutedLiteral instanceof ExternalLiteral)
-					) {
+			if (shallPushBackFixedInterpretationLiteral(substitutedLiteral)) {
 				return pushBackAndBindNextAtomInRule(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment);
 			}
 			final List substitutions = substitutedLiteral.getSubstitutions(partialSubstitution);
@@ -520,6 +515,13 @@ private BindingResult bindNextAtomInRule(RuleGroundingOrder groundingOrder, int
 		return createBindings(groundingOrder, orderPosition, originalTolerance, remainingTolerance, partialSubstitution, currentAssignment, instances, substitute);
 	}
 
+	private boolean shallPushBackFixedInterpretationLiteral(FixedInterpretationLiteral substitutedLiteral) {
+		return !(substitutedLiteral.isGround() ||
+				(substitutedLiteral instanceof ComparisonLiteral && ((ComparisonLiteral)substitutedLiteral).isLeftOrRightAssigning()) ||
+				(substitutedLiteral instanceof IntervalLiteral && substitutedLiteral.getTerms().get(0).isGround()) ||
+				(substitutedLiteral instanceof ExternalLiteral));
+	}
+
 	private Collection getInstancesForSubstitute(Atom substitute, Substitution partialSubstitution) {
 		Collection instances;
 		IndexedInstanceStorage storage = workingMemory.get(substitute.getPredicate(), true);

From 272e5ec77c2b0ad5b906d078bc5a6a26f68508b1 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Wed, 16 Oct 2019 12:57:43 +0200
Subject: [PATCH 56/66] Undo change in ArithmeticTerm#evaluateGroundTerm

that is apparently not necessary.
---
 .../at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java    | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java
index 202d7cdb6..cf01f5f6e 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ArithmeticTerm.java
@@ -93,9 +93,7 @@ public Term normalizeVariables(String renamePrefix, RenameCounter counter) {
 
 	public static Integer evaluateGroundTerm(Term term) {
 		if (!term.isGround()) {
-//			throw new RuntimeException("Cannot evaluate arithmetic term since it is not ground: " + term);
-			return null;
-			// TODO: maybe this has to be revised. In the case of lax grounder heuristics, it may be that we try to evaluate a non-ground term here, but an exception does not help
+			throw new RuntimeException("Cannot evaluate arithmetic term since it is not ground: " + term);
 		}
 		return evaluateGroundTermHelper(term);
 	}

From 1591dfb1c3a155eb818fccab95ddf7b04a61140a Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Wed, 16 Oct 2019 13:03:20 +0200
Subject: [PATCH 57/66] Give Travis more time to build.

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index f9d534d9d..f319d099c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -45,7 +45,7 @@ install:
   - true
 
 script:
-  - ./gradlew build --scan --stacktrace
+  - travis_wait ./gradlew build --scan --stacktrace
 
 after_success:
   - ./gradlew jacocoTestReport coveralls

From 9fb85703b3f60274e57d86644dfbde90c14305e1 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Thu, 17 Oct 2019 11:25:18 +0200
Subject: [PATCH 58/66] Remove unused method.

---
 .../at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java
index 6504a7bcc..a7e780b2e 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrder.java
@@ -54,13 +54,6 @@ private RuleGroundingOrder(RuleGroundingOrder otherRuleGroundingOrder) {
 		this(otherRuleGroundingOrder.startingLiteral, new ArrayList<>(otherRuleGroundingOrder.otherLiterals), otherRuleGroundingOrder.positionLastVarBound, otherRuleGroundingOrder.ground);
 		this.stopBindingAtOrderPosition = otherRuleGroundingOrder.stopBindingAtOrderPosition;
 	}
-
-	/**
-	 * @return the startingLiteral
-	 */
-	public Literal getStartingLiteral() {
-		return startingLiteral;
-	}
 	
 	/**
 	 * Returns the literal at the given position in the grounding order,

From fbdf7f6f0600b0d4a771c095a97beb1b45a2fd5b Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Thu, 17 Oct 2019 11:33:34 +0200
Subject: [PATCH 59/66] Correct log level.

---
 src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
index 6dc7c3f38..6532d9ed6 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
@@ -564,8 +564,8 @@ private void logStats() {
 				}
 			}
 			NoGoodCounter noGoodCounter = store.getNoGoodCounter();
-			LOGGER.info("Number of NoGoods by type: " + noGoodCounter.getStatsByType());
-			LOGGER.info("Number of NoGoods by cardinality: " + noGoodCounter.getStatsByCardinality());
+			LOGGER.debug("Number of NoGoods by type: " + noGoodCounter.getStatsByType());
+			LOGGER.debug("Number of NoGoods by cardinality: " + noGoodCounter.getStatsByCardinality());
 		}
 	}
 }
\ No newline at end of file

From 7edca34ac15a46ff521c4b5b223dd879875ed507 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Thu, 17 Oct 2019 13:17:20 +0200
Subject: [PATCH 60/66] Add more unit tests to increase coverage.

---
 .../tuwien/kr/alpha/solver/DefaultSolver.java |  5 ++
 .../tuwien/kr/alpha/solver/NoGoodCounter.java | 14 ++--
 .../solver/SolverMaintainingStatistics.java   |  2 +
 .../alpha/config/CommandLineParserTest.java   | 73 +++++++++++++++----
 .../grounder/RuleGroundingOrderTest.java      | 22 +++++-
 .../GrounderHeuristicConfigurationTest.java   | 11 ++-
 .../alpha/solver/SolverStatisticsTests.java   | 57 +++++++++++++--
 7 files changed, 149 insertions(+), 35 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
index 6532d9ed6..470dc445e 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
@@ -554,6 +554,11 @@ public int getNumberOfDeletedNoGoods() {
 		return ((NoGoodStoreAlphaRoaming)store).getLearnedNoGoodDeletion().getNumberOfDeletedNoGoods();
 	}
 
+	@Override
+	public NoGoodCounter getNoGoodCounter() {
+		return store.getNoGoodCounter();
+	}
+
 	private void logStats() {
 		if (LOGGER.isDebugEnabled()) {
 			LOGGER.debug(getStatisticsString());
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java
index c477a47b8..cba561e2c 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/NoGoodCounter.java
@@ -28,6 +28,9 @@
 import at.ac.tuwien.kr.alpha.common.NoGood;
 import at.ac.tuwien.kr.alpha.common.NoGood.Type;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Maintains statistics on numbers of various types of {@link NoGood}s.
  */
@@ -99,14 +102,11 @@ public boolean hasBinaryNoGoods() {
 	 * @return a string giving statistics on numbers of nogoods by type
 	 */
 	public String getStatsByType() {
-		StringBuilder sb = new StringBuilder();
-		for (Type type : NoGood.Type.values()) {
-			sb.append(type.name());
-			sb.append(": ");
-			sb.append(countByType[type.ordinal()]);
-			sb.append(" ");
+		List statsList = new ArrayList<>(Type.values().length);
+		for (Type type : Type.values()) {
+			statsList.add(type.name() + ": " + countByType[type.ordinal()]);
 		}
-		return sb.toString();
+		return String.join(" ", statsList);
 	}
 
 	/**
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverMaintainingStatistics.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverMaintainingStatistics.java
index 861830582..0c29abef7 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverMaintainingStatistics.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverMaintainingStatistics.java
@@ -46,6 +46,8 @@ public interface SolverMaintainingStatistics {
 	 */
 	int getNumberOfConflictsAfterClosing();
 
+	NoGoodCounter getNoGoodCounter();
+
 	default String getStatisticsString() {
 		return "g=" + getNumberOfChoices() + ", bt=" + getNumberOfBacktracks() + ", bj=" + getNumberOfBackjumps() + ", bt_within_bj="
 				+ getNumberOfBacktracksWithinBackjumps() + ", mbt=" + getNumberOfBacktracksDueToRemnantMBTs() + ", cac=" + getNumberOfConflictsAfterClosing()
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/config/CommandLineParserTest.java b/src/test/java/at/ac/tuwien/kr/alpha/config/CommandLineParserTest.java
index 4bb6623b8..22c093ec0 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/config/CommandLineParserTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/config/CommandLineParserTest.java
@@ -28,13 +28,19 @@
 package at.ac.tuwien.kr.alpha.config;
 
 import org.apache.commons.cli.ParseException;
-import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.Arrays;
+import java.util.function.Consumer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class CommandLineParserTest {
 
+	private static final String DEFAULT_COMMAND_LINE = "java -jar Alpha-bundled.jar";
+	private static final Consumer DEFAULT_ABORT_ACTION = (msg) -> { };
+
 	/**
 	 * Tests that a help message is written to the consumer configured in the
 	 * parser.
@@ -44,67 +50,102 @@ public class CommandLineParserTest {
 	@Test
 	public void help() throws ParseException {
 		StringBuilder bld = new StringBuilder();
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> bld.append(msg));
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, (msg) -> bld.append(msg));
 		parser.parseCommandLine(new String[] {"-h"});
-		Assert.assertTrue(!(bld.toString().isEmpty()));
+		assertTrue(!(bld.toString().isEmpty()));
 	}
 
 	@Test
 	public void basicUsageWithFile() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		AlphaConfig ctx = parser.parseCommandLine(new String[] {"-i", "someFile.asp", "-i", "someOtherFile.asp"});
-		Assert.assertEquals(Arrays.asList(new String[] {"someFile.asp", "someOtherFile.asp"}), ctx.getInputConfig().getFiles());
+		assertEquals(Arrays.asList(new String[] {"someFile.asp", "someOtherFile.asp"}), ctx.getInputConfig().getFiles());
 	}
 
 	@Test
 	public void basicUsageWithString() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		AlphaConfig ctx = parser.parseCommandLine(new String[] {"-str", "b :- a.", "-str", "c :- a, b."});
-		Assert.assertEquals(Arrays.asList(new String[] {"b :- a.", "c :- a, b."}), ctx.getInputConfig().getAspStrings());
+		assertEquals(Arrays.asList(new String[] {"b :- a.", "c :- a, b."}), ctx.getInputConfig().getAspStrings());
 	}
 
 	@Test(expected = ParseException.class)
 	public void invalidUsageNoInput() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		parser.parseCommandLine(new String[] {});
 	}
 
 	@Test
 	public void moreThanOneInputSource() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		parser.parseCommandLine(new String[] {"-i", "a.b", "-i", "b.c", "-str", "aString."});
 	}
 
 	@Test(expected = ParseException.class)
 	public void invalidUsageMissingInputFlag() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		parser.parseCommandLine(new String[] {"-i", "a.b", "b.c"});
 	}
 
 	@Test
 	public void numAnswerSets() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java-jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		AlphaConfig ctx = parser.parseCommandLine(new String[] {"-str", "aString.", "-n", "00435"});
-		Assert.assertEquals(435, ctx.getInputConfig().getNumAnswerSets());
+		assertEquals(435, ctx.getInputConfig().getNumAnswerSets());
 	}
 	
 	@Test(expected = ParseException.class)
 	public void noInputGiven() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java -jar Alpha--bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		parser.parseCommandLine(new String[] {});
 	}
 
 	@Test
 	public void replay() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java -jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-rc", "\"1,2, 3\""});
-		Assert.assertEquals(Arrays.asList(1, 2, 3), alphaConfig.getAlphaConfig().getReplayChoices());
+		assertEquals(Arrays.asList(1, 2, 3), alphaConfig.getAlphaConfig().getReplayChoices());
 	}
 
 	@Test(expected = ParseException.class)
 	public void replayWithNonNumericLiteral() throws ParseException {
-		CommandLineParser parser = new CommandLineParser("java -jar Alpha-bundled.jar", (msg) -> { });
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
 		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-rc", "\"1, 2, x\""});
 	}
 
+	@Test
+	public void grounderToleranceConstraints_numeric() throws ParseException {
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
+		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-gtc", "-1"});
+		assertEquals("-1", alphaConfig.getAlphaConfig().getGrounderToleranceConstraints());
+	}
+
+	@Test
+	public void grounderToleranceConstraints_string() throws ParseException {
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
+		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-gtc", "strict"});
+		assertEquals("strict", alphaConfig.getAlphaConfig().getGrounderToleranceConstraints());
+	}
+
+	@Test
+	public void grounderToleranceRules_numeric() throws ParseException {
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
+		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-gtr", "1"});
+		assertEquals("1", alphaConfig.getAlphaConfig().getGrounderToleranceRules());
+	}
+
+	@Test
+	public void grounderToleranceRules_string() throws ParseException {
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
+		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-gtr", "lax"});
+		assertEquals("lax", alphaConfig.getAlphaConfig().getGrounderToleranceRules());
+	}
+
+	@Test
+	public void noInstanceRemoval() throws ParseException {
+		CommandLineParser parser = new CommandLineParser(DEFAULT_COMMAND_LINE, DEFAULT_ABORT_ACTION);
+		AlphaConfig alphaConfig = parser.parseCommandLine(new String[]{"-str", "aString.", "-dir"});
+		assertTrue(alphaConfig.getAlphaConfig().isDisableInstanceRemoval());
+	}
+
 }
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java
index 4d35dcde0..8c6b53c41 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/RuleGroundingOrderTest.java
@@ -37,8 +37,7 @@
 import java.io.IOException;
 
 import static at.ac.tuwien.kr.alpha.TestUtil.literal;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 /**
  * Copyright (c) 2017-2019, the Alpha Team.
@@ -96,7 +95,7 @@ public void testPositionFromWhichAllVarsAreBound_simpleNonGround() {
 			assertEquals(0, rgo0.orderStartingFrom(startingLiteral).getPositionFromWhichAllVarsAreBound());
 		}
 	}
-	
+
 	@Test
 	public void testPositionFromWhichAllVarsAreBound_longerSimpleNonGround() {
 		Program program = parser.parse("a(X) :- b(X), c(X), d(X), not e(X).");
@@ -106,7 +105,22 @@ public void testPositionFromWhichAllVarsAreBound_longerSimpleNonGround() {
 			assertEquals(0, rgo0.orderStartingFrom(startingLiteral).getPositionFromWhichAllVarsAreBound());
 		}
 	}
-	
+
+	@Test
+	public void testToString_longerSimpleNonGround() {
+		Program program = parser.parse("a(X) :- b(X), c(X), d(X), not e(X).");
+		RuleGroundingOrders rgo0 = computeGroundingOrdersForRule(program, 0);
+		assertEquals(3, rgo0.getStartingLiterals().size());
+		for (Literal startingLiteral : rgo0.getStartingLiterals()) {
+			switch (startingLiteral.getPredicate().getName()) {
+				case "b": assertEquals("b(X) : | c(X), d(X), not e(X)", rgo0.orderStartingFrom(startingLiteral).toString()); break;
+				case "c": assertEquals("c(X) : | b(X), d(X), not e(X)", rgo0.orderStartingFrom(startingLiteral).toString()); break;
+				case "d": assertEquals("d(X) : | b(X), c(X), not e(X)", rgo0.orderStartingFrom(startingLiteral).toString()); break;
+				default: fail("Unexpected starting literal: " + startingLiteral);
+			}
+		}
+	}
+
 	@Test
 	public void testPositionFromWhichAllVarsAreBound_joinedNonGround() {
 		Program program = parser.parse("a(X) :- b(X), c(X,Y), d(X,Z), not e(X).");
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java
index 566896b94..6f9e2240c 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/heuristics/GrounderHeuristicConfigurationTest.java
@@ -32,7 +32,7 @@
 import static org.junit.Assert.*;
 
 /**
- * Tests {@link GrounderHeuristicConfiguration}
+ * Tests {@link GrounderHeuristicsConfiguration}
  */
 public class GrounderHeuristicConfigurationTest {
 
@@ -81,4 +81,13 @@ public void testGetInstanceIntInt() {
 		assertEquals(1, grounderHeuristicsConfiguration.getToleranceRules());
 	}
 
+	@Test
+	public void testGetInstanceStringIntStringInt() {
+		GrounderHeuristicsConfiguration grounderHeuristicsConfiguration = GrounderHeuristicsConfiguration.getInstance("5", "1");
+		assertTrue(grounderHeuristicsConfiguration.isLax(true));
+		assertTrue(grounderHeuristicsConfiguration.isLax(false));
+		assertEquals(5, grounderHeuristicsConfiguration.getToleranceConstraints());
+		assertEquals(1, grounderHeuristicsConfiguration.getToleranceRules());
+	}
+
 }
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/SolverStatisticsTests.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/SolverStatisticsTests.java
index a4c7999aa..706ee08ee 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/SolverStatisticsTests.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/SolverStatisticsTests.java
@@ -31,32 +31,75 @@
 import java.util.Set;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 public class SolverStatisticsTests extends AbstractSolverTests {
 
 	@Test
 	public void checkStatsStringZeroChoices() {
 		Solver solver = getInstance("a.");
+		assumeTrue(solver instanceof SolverMaintainingStatistics);
 		collectAnswerSetsAndCheckStats(solver, 1, 0, 0, 0, 0, 0, 0, 0);
 	}
 
 	@Test
 	public void checkStatsStringOneChoice() {
 		Solver solver = getInstance("a :- not b. b :- not a.");
+		assumeTrue(solver instanceof SolverMaintainingStatistics);
 		collectAnswerSetsAndCheckStats(solver, 2, 1, 1, 1, 1, 0, 0, 0);
 	}
 
+	@Test
+	public void checkNoGoodCounterStatsByTypeZeroChoices() {
+		Solver solver = getInstance("a.");
+		assumeTrue(solver instanceof SolverMaintainingStatistics);
+		collectAnswerSetsAndCheckNoGoodCounterStatsByType(solver, 0, 0, 0, 0);
+	}
+
+	@Test
+	public void checkNoGoodCounterStatsByTypeOneChoice() {
+		Solver solver = getInstance("a :- not b. b :- not a.");
+		assumeTrue(solver instanceof SolverMaintainingStatistics);
+		collectAnswerSetsAndCheckNoGoodCounterStatsByType(solver, 7, 2, 0, 4);
+	}
+
+	@Test
+	public void checkNoGoodCounterStatsByCardinalityZeroChoices() {
+		Solver solver = getInstance("a.");
+		assumeTrue(solver instanceof SolverMaintainingStatistics);
+		collectAnswerSetsAndCheckNoGoodCounterStatsByCardinality(solver, 0, 0, 0);
+	}
+
+	@Test
+	public void checkNoGoodCounterStatsByCardinalityOneChoice() {
+		Solver solver = getInstance("a :- not b. b :- not a.");
+		assumeTrue(solver instanceof SolverMaintainingStatistics);
+		collectAnswerSetsAndCheckNoGoodCounterStatsByCardinality(solver, 3, 10, 0);
+	}
+
 	private void collectAnswerSetsAndCheckStats(Solver solver, int expectedNumberOfAnswerSets, int expectedNumberOfGuesses, int expectedTotalNumberOfBacktracks,
 			int expectedNumberOfBacktracksWithinBackjumps, int expectedNumberOfBackjumps, int expectedNumberOfMBTs, int expectedNumberOfConflictsAfterClosing, int expectedNumberOfDeletedNoGoods) {
 		Set answerSets = solver.collectSet();
 		assertEquals(expectedNumberOfAnswerSets, answerSets.size());
-		if (solver instanceof SolverMaintainingStatistics) {
-			SolverMaintainingStatistics solverMaintainingStatistics = (SolverMaintainingStatistics) solver;
-			assertEquals(
-					String.format("g=%d, bt=%d, bj=%d, bt_within_bj=%d, mbt=%d, cac=%d, del_ng=%d", expectedNumberOfGuesses, expectedTotalNumberOfBacktracks, expectedNumberOfBackjumps,
-							expectedNumberOfBacktracksWithinBackjumps, expectedNumberOfMBTs, expectedNumberOfConflictsAfterClosing, expectedNumberOfDeletedNoGoods),
-					solverMaintainingStatistics.getStatisticsString());
-		}
+		SolverMaintainingStatistics solverMaintainingStatistics = (SolverMaintainingStatistics) solver;
+		assertEquals(
+				String.format("g=%d, bt=%d, bj=%d, bt_within_bj=%d, mbt=%d, cac=%d, del_ng=%d", expectedNumberOfGuesses, expectedTotalNumberOfBacktracks, expectedNumberOfBackjumps,
+						expectedNumberOfBacktracksWithinBackjumps, expectedNumberOfMBTs, expectedNumberOfConflictsAfterClosing, expectedNumberOfDeletedNoGoods),
+				solverMaintainingStatistics.getStatisticsString());
+	}
+
+	private void collectAnswerSetsAndCheckNoGoodCounterStatsByType(Solver solver, int expectedNumberOfStaticNoGoods, int expectedNumberOfSupportNoGoods, int expectedNumberOfLearntNoGoods, int expectedNumberOfInternalNoGoods) {
+		solver.collectSet();
+		SolverMaintainingStatistics solverMaintainingStatistics = (SolverMaintainingStatistics) solver;
+		final NoGoodCounter noGoodCounter =  solverMaintainingStatistics.getNoGoodCounter();
+		assertEquals("STATIC: " + expectedNumberOfStaticNoGoods + " SUPPORT: " + expectedNumberOfSupportNoGoods + " LEARNT: " + expectedNumberOfLearntNoGoods + " INTERNAL: " + expectedNumberOfInternalNoGoods, noGoodCounter.getStatsByType());
+	}
+
+	private void collectAnswerSetsAndCheckNoGoodCounterStatsByCardinality(Solver solver, int expectedNumberOfUnaryNoGoods, int expectedNumberOfBinaryNoGoods, int expectedNumberOfNAryNoGoods) {
+		solver.collectSet();
+		SolverMaintainingStatistics solverMaintainingStatistics = (SolverMaintainingStatistics) solver;
+		final NoGoodCounter noGoodCounter =  solverMaintainingStatistics.getNoGoodCounter();
+		assertEquals("unary: " + expectedNumberOfUnaryNoGoods + " binary: " + expectedNumberOfBinaryNoGoods + " larger: " + expectedNumberOfNAryNoGoods, noGoodCounter.getStatsByCardinality());
 	}
 
 }

From c3ac9780830005225625f509af61a77b771d3e99 Mon Sep 17 00:00:00 2001
From: Antonius Weinzierl 
Date: Wed, 23 Oct 2019 04:33:52 +0200
Subject: [PATCH 61/66] Add options for initial phase settings; add atom
 dependency to VSIDS with phase saving.

- CommandLineParser and SystemConfig accept initial phase settings.
- AtomChoiceRelation stores relation between ordinary atoms and the
  choice points that influence them.
- NaiveGrounder and ProgramAnalyzingGrounder provide AtomChoiceRelation.
- NoGoodGenerator fills AtomChoiceRelation.
- BranchingHeuristicFactory sets AtomChoiceRelation for
  VSIDSWithPhaseSaving.
- PhaseInitializerFactory provides different initial phase settings.
- VSIDSWithPhaseSaving uses AtomChoiceRelation for activity increments.
- SolverFactory sets chosen phase initializer.
- TrailAssignment considers initial phase value, if phase was not set.
- Tests set a phase initializer if needed.
---
 .../kr/alpha/config/CommandLineParser.java    |  9 +++
 .../tuwien/kr/alpha/config/SystemConfig.java  | 10 ++++
 .../kr/alpha/grounder/NaiveGrounder.java      |  8 ++-
 .../kr/alpha/grounder/NoGoodGenerator.java    | 14 ++++-
 .../grounder/ProgramAnalyzingGrounder.java    |  7 +++
 .../structure/AtomChoiceRelation.java         | 31 ++++++++++
 .../tuwien/kr/alpha/solver/DefaultSolver.java |  1 +
 .../tuwien/kr/alpha/solver/SolverFactory.java |  5 +-
 .../kr/alpha/solver/TrailAssignment.java      | 19 +++++--
 .../heuristics/BranchingHeuristicFactory.java |  5 +-
 .../heuristics/PhaseInitializerFactory.java   | 57 +++++++++++++++++++
 .../heuristics/VSIDSWithPhaseSaving.java      | 34 ++++++++---
 .../kr/alpha/grounder/NaiveGrounderTest.java  |  7 ++-
 .../structure/AnalyzeUnjustifiedTest.java     |  9 +--
 .../kr/alpha/solver/ChoiceManagerTests.java   |  3 +-
 .../solver/LearnedNoGoodDeletionTest.java     |  3 +-
 .../kr/alpha/solver/NaiveNoGoodStoreTest.java |  3 +-
 .../solver/NoGoodStoreAlphaRoamingTest.java   |  3 +-
 .../kr/alpha/solver/TrailAssignmentTest.java  |  3 +-
 .../AlphaHeuristicTestAssumptions.java        |  2 +-
 .../alpha/solver/heuristics/BerkMinTest.java  |  2 +-
 .../BranchingHeuristicFactoryTest.java        |  2 +-
 .../heuristics/HeapOfActiveAtomsTest.java     |  2 +-
 .../heuristics/ReplayHeuristicTest.java       |  2 +-
 .../kr/alpha/solver/heuristics/VSIDSTest.java |  2 +-
 .../GroundConflictNoGoodLearnerTest.java      |  3 +-
 26 files changed, 212 insertions(+), 34 deletions(-)
 create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AtomChoiceRelation.java
 create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/PhaseInitializerFactory.java

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java b/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java
index 26e1e786d..a5541916c 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/config/CommandLineParser.java
@@ -111,6 +111,9 @@ public class CommandLineParser {
 		.desc("enable the usage of (dynamic and static) restarts (default: "
 			+ SystemConfig.DEFAULT_ENABLE_RESTARTS + ")")
 		.build();
+	private static final Option OPT_INITIAL_PHASE = Option.builder("ph").longOpt("initialPhase").hasArg(true).argName("initializer")
+		.desc("set the initial phase [ alltrue | allfalse | random ] (default: " + SystemConfig.DEFAULT_PHASE_INITIALIZER + ")")
+		.build();
 
 	private static final Options CLI_OPTS = new Options();
 
@@ -142,6 +145,7 @@ public class CommandLineParser {
 		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NORMALIZATION_GRID);
 		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_NOGOOD_DELETION);
 		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_ENABLE_RESTARTS);
+		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_INITIAL_PHASE);
 	}
 
 	/*
@@ -186,6 +190,7 @@ public CommandLineParser(String cmdLineSyntax, Consumer abortAction) {
 		this.globalOptionHandlers.put(CommandLineParser.OPT_NORMALIZATION_GRID.getOpt(), this::handleNormalizationGrid);
 		this.globalOptionHandlers.put(CommandLineParser.OPT_NO_NOGOOD_DELETION.getOpt(), this::handleNoNoGoodDeletion);
 		this.globalOptionHandlers.put(CommandLineParser.OPT_ENABLE_RESTARTS.getOpt(), this::handleEnableRestarts);
+		this.globalOptionHandlers.put(CommandLineParser.OPT_INITIAL_PHASE.getOpt(), this::handleInitialPhase);
 
 		this.inputOptionHandlers.put(CommandLineParser.OPT_NUM_ANSWER_SETS.getOpt(), this::handleNumAnswerSets);
 		this.inputOptionHandlers.put(CommandLineParser.OPT_INPUT.getOpt(), this::handleInput);
@@ -368,4 +373,8 @@ private void handleNoNoGoodDeletion(Option opt, SystemConfig cfg) {
 	private void handleEnableRestarts(Option opt, SystemConfig cfg) {
 		cfg.setRestartsEnabled(true);
 	}
+
+	private void handleInitialPhase(Option opt, SystemConfig cfg) {
+		cfg.setPhaseInitializer(opt.getValue(SystemConfig.DEFAULT_PHASE_INITIALIZER));
+	}
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java
index c8c3c9b08..5fceeddb3 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/config/SystemConfig.java
@@ -57,6 +57,7 @@ public class SystemConfig {
 	public static final List DEFAULT_REPLAY_CHOICES = Collections.emptyList();
 	public static final boolean DEFAULT_DISABLE_NOGOOD_DELETION = false;
 	public static final boolean DEFAULT_ENABLE_RESTARTS = false;
+	public static final String DEFAULT_PHASE_INITIALIZER = "random";
 
 	private String grounderName = SystemConfig.DEFAULT_GROUNDER_NAME;
 	private String solverName = SystemConfig.DEFAULT_SOLVER_NAME;
@@ -74,6 +75,7 @@ public class SystemConfig {
 	private List replayChoices = SystemConfig.DEFAULT_REPLAY_CHOICES;
 	private boolean disableNoGoodDeletion = SystemConfig.DEFAULT_DISABLE_NOGOOD_DELETION;
 	private boolean areRestartsEnabled = SystemConfig.DEFAULT_ENABLE_RESTARTS;
+	private String phaseInitializer = SystemConfig.DEFAULT_PHASE_INITIALIZER;
 
 	public String getGrounderName() {
 		return this.grounderName;
@@ -215,4 +217,12 @@ public void setRestartsEnabled(boolean areRestartsEnabled) {
 		this.areRestartsEnabled = areRestartsEnabled;
 	}
 
+	public String getPhaseInitializerName() {
+		return phaseInitializer;
+	}
+
+	public void  setPhaseInitializer(String phaseInitializer) {
+		this.phaseInitializer = phaseInitializer;
+	}
+
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
index 387f86a4e..768403d92 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
@@ -39,6 +39,7 @@
 import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom;
 import at.ac.tuwien.kr.alpha.grounder.bridges.Bridge;
 import at.ac.tuwien.kr.alpha.grounder.structure.AnalyzeUnjustified;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis;
 import at.ac.tuwien.kr.alpha.grounder.transformation.*;
 import at.ac.tuwien.kr.alpha.solver.ThriceTruth;
@@ -72,6 +73,7 @@ public class NaiveGrounder extends BridgedGrounder implements ProgramAnalyzingGr
 	private final Map> rulesUsingPredicateWorkingMemory = new HashMap<>();
 	private final Map> knownGroundingSubstitutions = new HashMap<>();
 	private final Map knownNonGroundRules = new HashMap<>();
+	private final AtomChoiceRelation atomChoiceRelation = new AtomChoiceRelation();
 
 	private ArrayList fixedRules = new ArrayList<>();
 	private LinkedHashSet removeAfterObtainingNewNoGoods = new LinkedHashSet<>();
@@ -108,11 +110,15 @@ public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternal
 
 		final Set uniqueGroundRulePerGroundHead = getRulesWithUniqueHead();
 		choiceRecorder = new ChoiceRecorder(atomStore);
-		noGoodGenerator = new NoGoodGenerator(atomStore, choiceRecorder, factsFromProgram, programAnalysis, uniqueGroundRulePerGroundHead);
+		noGoodGenerator = new NoGoodGenerator(atomStore, choiceRecorder, factsFromProgram, programAnalysis, uniqueGroundRulePerGroundHead, atomChoiceRelation);
 		
 		this.debugInternalChecks = debugInternalChecks;
 	}
 
+	public AtomChoiceRelation getAtomChoiceRelation() {
+		return atomChoiceRelation;
+	}
+
 	private void initializeFactsAndRules(Program program) {
 		// initialize all facts
 		for (Atom fact : program.getFacts()) {
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGenerator.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGenerator.java
index 72500f499..7a1e015ff 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGenerator.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGenerator.java
@@ -34,6 +34,7 @@
 import at.ac.tuwien.kr.alpha.common.atoms.FixedInterpretationLiteral;
 import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationAtom;
 import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis;
 
 import java.util.*;
@@ -52,13 +53,15 @@ public class NoGoodGenerator {
 	private final Map> factsFromProgram;
 	private final ProgramAnalysis programAnalysis;
 	private final Set uniqueGroundRulePerGroundHead;
+	private final AtomChoiceRelation atomChoiceRelation;
 
-	NoGoodGenerator(AtomStore atomStore, ChoiceRecorder recorder, Map> factsFromProgram, ProgramAnalysis programAnalysis, Set uniqueGroundRulePerGroundHead) {
+	NoGoodGenerator(AtomStore atomStore, ChoiceRecorder recorder, Map> factsFromProgram, ProgramAnalysis programAnalysis, Set uniqueGroundRulePerGroundHead, AtomChoiceRelation atomChoiceRelation) {
 		this.atomStore = atomStore;
 		this.choiceRecorder = recorder;
 		this.factsFromProgram = factsFromProgram;
 		this.programAnalysis = programAnalysis;
 		this.uniqueGroundRulePerGroundHead = uniqueGroundRulePerGroundHead;
+		this.atomChoiceRelation = atomChoiceRelation;
 	}
 
 	/**
@@ -126,6 +129,15 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround
 			result.addAll(choiceRecorder.generateChoiceNoGoods(posLiterals, negLiterals, bodyRepresentingLiteral));
 		}
 
+		// Record atom-choiceAtom relationships for rules that are choice points.
+		if (!negLiterals.isEmpty()) {
+			atomChoiceRelation.growForMaxAtomId(atomStore.getMaxAtomId());
+			atomChoiceRelation.addRelation(headId, atomOf(bodyRepresentingLiteral));
+			for (Integer negLiteral : negLiterals) {
+				atomChoiceRelation.addRelation(atomOf(negLiteral), atomOf(bodyRepresentingLiteral));
+			}
+		}
+
 		return result;
 	}
 
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/ProgramAnalyzingGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/ProgramAnalyzingGrounder.java
index 21295d1e7..57ba291e1 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/ProgramAnalyzingGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/ProgramAnalyzingGrounder.java
@@ -3,6 +3,7 @@
 import at.ac.tuwien.kr.alpha.common.Assignment;
 import at.ac.tuwien.kr.alpha.common.atoms.Atom;
 import at.ac.tuwien.kr.alpha.common.atoms.Literal;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 
 import java.util.Set;
 
@@ -32,4 +33,10 @@ public interface ProgramAnalyzingGrounder extends Grounder {
 	 * @return the corresponding NonGroundRule.
 	 */
 	NonGroundRule getNonGroundRule(Integer ruleId);
+
+	/**
+	 * Provides relationship information between atoms and choice points influencing their truth values.
+	 * @return the {@link AtomChoiceRelation}.
+	 */
+	AtomChoiceRelation getAtomChoiceRelation();
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AtomChoiceRelation.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AtomChoiceRelation.java
new file mode 100644
index 000000000..8db7941e2
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AtomChoiceRelation.java
@@ -0,0 +1,31 @@
+package at.ac.tuwien.kr.alpha.grounder.structure;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Stores and provides relationships between ordinary atoms and those that represent choice points.
+ *
+ * Copyright (c) 2019, the Alpha Team.
+ */
+public class AtomChoiceRelation {
+	private final ArrayList> atomToChoiceAtoms = new ArrayList<>();
+
+	public void addRelation(int atom, int bodyRepresentingAtom) {
+		while (atom > atomToChoiceAtoms.size() - 1) {
+			atomToChoiceAtoms.add(new ArrayList<>());
+		}
+		atomToChoiceAtoms.get(atom).add(bodyRepresentingAtom);
+	}
+
+	public List getRelatedChoiceAtoms(int atom) {
+		return Collections.unmodifiableList(atomToChoiceAtoms.get(atom));
+	}
+
+	public void growForMaxAtomId(int maxAtomId) {
+		while (maxAtomId > atomToChoiceAtoms.size() - 1) {
+			atomToChoiceAtoms.add(new ArrayList<>());
+		}
+	}
+}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
index a47ceea44..9b0d3980c 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
@@ -165,6 +165,7 @@ protected boolean tryAdvance(Consumer action) {
 					LOGGER.trace("Restarting search.");
 					choiceManager.backjump(0);
 					restartStrategy.onRestart();
+					continue;
 				}
 			}
 			if (conflictCause != null) {
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverFactory.java
index 36bbe668d..755ad8012 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverFactory.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/SolverFactory.java
@@ -32,6 +32,7 @@
 import at.ac.tuwien.kr.alpha.grounder.Grounder;
 import at.ac.tuwien.kr.alpha.solver.heuristics.HeuristicsConfiguration;
 import at.ac.tuwien.kr.alpha.solver.heuristics.HeuristicsConfigurationBuilder;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 
 import java.util.Random;
 
@@ -42,7 +43,9 @@ public static Solver getInstance(SystemConfig config, AtomStore atomStore, Groun
 		final Random random = new Random(config.getSeed());
 		final boolean debugInternalChecks = config.isDebugInternalChecks();
 		final HeuristicsConfiguration heuristicsConfiguration = buildHeuristicsConfiguration(config);
-		final WritableAssignment assignment = new TrailAssignment(atomStore, debugInternalChecks);
+		final PhaseInitializerFactory.PhaseInitializer phaseInitializer =
+			PhaseInitializerFactory.getInstance(config.getPhaseInitializerName(), random);
+		final WritableAssignment assignment = new TrailAssignment(atomStore, phaseInitializer, debugInternalChecks);
 
 		NoGoodStore store;
 
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java
index d76166a7e..da49f4f21 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/TrailAssignment.java
@@ -30,6 +30,7 @@
 import at.ac.tuwien.kr.alpha.common.Assignment;
 import at.ac.tuwien.kr.alpha.common.AtomStore;
 import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,6 +70,7 @@ public void decreaseActivity() {
 
 	private final AtomStore atomStore;
 	private ChoiceManager choiceManagerCallback;
+	private final PhaseInitializerFactory.PhaseInitializer phaseInitializer;
 
 	/**
 	 * Contains for each known atom a value whose two least
@@ -82,6 +84,7 @@ public void decreaseActivity() {
 	private Antecedent[] impliedBy;
 	private boolean[] callbackUponChange;
 	private boolean[] phase;
+	private boolean[] hasPhaseSet;
 	private ArrayList outOfOrderLiterals = new ArrayList<>();
 	private int highestDecisionLevelContainingOutOfOrderLiterals;
 	private ArrayList trail = new ArrayList<>();
@@ -95,7 +98,8 @@ public void decreaseActivity() {
 	private boolean checksEnabled;
 	int replayCounter;
 
-	public TrailAssignment(AtomStore atomStore, boolean checksEnabled) {
+	public TrailAssignment(AtomStore atomStore, PhaseInitializerFactory.PhaseInitializer phaseInitializer, boolean checksEnabled) {
+		this.phaseInitializer = phaseInitializer;
 		this.checksEnabled = checksEnabled;
 		this.atomStore = atomStore;
 		this.values = new int[0];
@@ -103,6 +107,7 @@ public TrailAssignment(AtomStore atomStore, boolean checksEnabled) {
 		this.impliedBy = new Antecedent[0];
 		this.callbackUponChange = new boolean[0];
 		this.phase = new boolean[0];
+		this.hasPhaseSet = new boolean[0];
 		this.trailIndicesOfDecisionLevels.add(0);
 		nextPositionInTrail = 0;
 		newAssignmentsIterator = 0;
@@ -110,8 +115,8 @@ public TrailAssignment(AtomStore atomStore, boolean checksEnabled) {
 		assignmentsForChoicePosition = 0;
 	}
 
-	public TrailAssignment(AtomStore atomStore) {
-		this(atomStore, false);
+	public TrailAssignment(AtomStore atomStore, PhaseInitializerFactory.PhaseInitializer phaseInitializer) {
+		this(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue(), false);
 	}
 
 	@Override
@@ -122,6 +127,7 @@ public void clear() {
 		Arrays.fill(impliedBy, null);
 		Arrays.fill(callbackUponChange, false);
 		Arrays.fill(phase, false);
+		Arrays.fill(hasPhaseSet, false);
 		outOfOrderLiterals = new ArrayList<>();
 		highestDecisionLevelContainingOutOfOrderLiterals = 0;
 		trail = new ArrayList<>();
@@ -357,6 +363,7 @@ private ConflictCause assignWithTrail(int atom, ThriceTruth value, Antecedent im
 			values[atom] = (getDecisionLevel() << 2) | translateTruth(value);
 			this.impliedBy[atom] = impliedBy;
 			this.phase[atom] = value.toBoolean();
+			this.hasPhaseSet[atom] = true;
 			// Adjust MBT counter.
 			if (value == MBT) {
 				mbtCount++;
@@ -451,7 +458,10 @@ public Antecedent getImpliedBy(int atom) {
 
 	@Override
 	public boolean getLastValue(int atom) {
-		return phase[atom];
+		if (hasPhaseSet[atom]) {
+			return phase[atom];
+		}
+		return phaseInitializer.getNextInitialPhase();
 	}
 
 	@Override
@@ -543,6 +553,7 @@ public void growForMaxAtomId() {
 		Arrays.fill(strongDecisionLevels, oldLength, strongDecisionLevels.length, -1);
 		impliedBy = Arrays.copyOf(impliedBy, newCapacity);
 		phase = Arrays.copyOf(phase, newCapacity);
+		hasPhaseSet = Arrays.copyOf(hasPhaseSet, newCapacity);
 		callbackUponChange = Arrays.copyOf(callbackUponChange, newCapacity);
 	}
 
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java
index 740e016fd..6d56a9ed9 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java
@@ -26,6 +26,8 @@
 package at.ac.tuwien.kr.alpha.solver.heuristics;
 
 import at.ac.tuwien.kr.alpha.grounder.Grounder;
+import at.ac.tuwien.kr.alpha.grounder.ProgramAnalyzingGrounder;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import at.ac.tuwien.kr.alpha.solver.ChoiceManager;
 import at.ac.tuwien.kr.alpha.solver.WritableAssignment;
 import at.ac.tuwien.kr.alpha.solver.heuristics.activity.BodyActivityProviderFactory.BodyActivityType;
@@ -119,7 +121,8 @@ private static BranchingHeuristic getInstanceWithoutReplay(HeuristicsConfigurati
 		case GDD_VSIDS:
 			return new DependencyDrivenVSIDS(assignment, choiceManager, random, heuristicsConfiguration.getMomsStrategy());
 		case VSIDS_PHASE_SAVING:
-			return new VSIDSWithPhaseSaving(assignment, choiceManager, heuristicsConfiguration.getMomsStrategy());
+			AtomChoiceRelation atomChoiceRelation = grounder instanceof ProgramAnalyzingGrounder ? ((ProgramAnalyzingGrounder) grounder).getAtomChoiceRelation() : null;
+			return new VSIDSWithPhaseSaving(assignment, choiceManager, atomChoiceRelation, heuristicsConfiguration.getMomsStrategy());
 		}
 		throw new IllegalArgumentException("Unknown branching heuristic requested.");
 	}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/PhaseInitializerFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/PhaseInitializerFactory.java
new file mode 100644
index 000000000..095f1435f
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/PhaseInitializerFactory.java
@@ -0,0 +1,57 @@
+package at.ac.tuwien.kr.alpha.solver.heuristics;
+
+import java.util.Random;
+
+/**
+ * Factory returning atom phase initializers, which determine the initial phase given to atoms that were previously
+ * unassigned.
+ *
+ * Copyright (c) 2019, the Alpha Team.
+ */
+public abstract class PhaseInitializerFactory {
+
+	public abstract static class PhaseInitializer {
+		public abstract boolean getNextInitialPhase();
+	}
+
+	public static PhaseInitializer getInstance(String phaseInitializerName, Random random) {
+		switch (phaseInitializerName.toLowerCase()) {
+			case "alltrue":
+				return getPhaseInitializerAllTrue();
+			case "allfalse":
+				return getPhaseInitializerAllFalse();
+			case "random":
+				return getPhaseInitializerRandom(random);
+			default:
+				throw new IllegalArgumentException("Unknown phase initializer requested:" + phaseInitializerName);
+		}
+	}
+
+	public static PhaseInitializer getPhaseInitializerAllTrue() {
+		return new PhaseInitializer() {
+			@Override
+			public boolean getNextInitialPhase() {
+				return true;
+			}
+		};
+	}
+
+	private static PhaseInitializer getPhaseInitializerAllFalse() {
+		return new PhaseInitializer() {
+			@Override
+			public boolean getNextInitialPhase() {
+				return false;
+			}
+		};
+	}
+
+	private static PhaseInitializer getPhaseInitializerRandom(Random random) {
+		return new PhaseInitializer() {
+			private final Random rand = random;
+			@Override
+			public boolean getNextInitialPhase() {
+				return rand.nextBoolean();
+			}
+		};
+	}
+}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
index dddcd5f3d..7de6b294b 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
@@ -27,6 +27,7 @@
 
 import at.ac.tuwien.kr.alpha.common.Assignment;
 import at.ac.tuwien.kr.alpha.common.NoGood;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import at.ac.tuwien.kr.alpha.solver.BinaryNoGoodPropagationEstimation;
 import at.ac.tuwien.kr.alpha.solver.ChoiceManager;
 import at.ac.tuwien.kr.alpha.solver.ThriceTruth;
@@ -37,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 
+import static at.ac.tuwien.kr.alpha.Util.oops;
 import static at.ac.tuwien.kr.alpha.common.Literals.atomOf;
 import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral;
 
@@ -58,25 +60,27 @@ public class VSIDSWithPhaseSaving implements ActivityBasedBranchingHeuristic {
 
 	protected final Assignment assignment;
 	protected final ChoiceManager choiceManager;
+	private final AtomChoiceRelation atomChoiceRelation;
 	private final HeapOfActiveAtoms heapOfActiveAtoms;
 	private final Collection bufferedNoGoods = new ArrayList<>();
 
 	private double activityDecrease;
 	private long numThrownAway;
 
-	private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, HeapOfActiveAtoms heapOfActiveAtoms, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
+	private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation, HeapOfActiveAtoms heapOfActiveAtoms, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
 		this.assignment = assignment;
 		this.choiceManager = choiceManager;
+		this.atomChoiceRelation = atomChoiceRelation;
 		this.heapOfActiveAtoms = heapOfActiveAtoms;
 		this.heapOfActiveAtoms.setMOMsStrategy(momsStrategy);
 	}
 
-	private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, int decayPeriod, double decayFactor, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
-		this(assignment, choiceManager, new HeapOfActiveAtoms(decayPeriod, decayFactor, choiceManager),  momsStrategy);
+	private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation, int decayPeriod, double decayFactor, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
+		this(assignment, choiceManager, atomChoiceRelation, new HeapOfActiveAtoms(decayPeriod, decayFactor, choiceManager),  momsStrategy);
 	}
 
-	VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
-		this(assignment, choiceManager, DEFAULT_DECAY_PERIOD, DEFAULT_DECAY_FACTOR,  momsStrategy);
+	VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
+		this(assignment, choiceManager, atomChoiceRelation, DEFAULT_DECAY_PERIOD, DEFAULT_DECAY_FACTOR,  momsStrategy);
 	}
 
 	@Override
@@ -87,16 +91,29 @@ public void violatedNoGood(NoGood violatedNoGood) {
 	public void analyzedConflict(ConflictAnalysisResult analysisResult) {
 		ingestBufferedNoGoods();	//analysisResult may contain new atoms whose activity must be initialized
 		for (int resolutionAtom : analysisResult.resolutionAtoms) {
-			heapOfActiveAtoms.incrementActivity(resolutionAtom);
+			incrementActivityOfRelatedChoiceAtoms(resolutionAtom);
 		}
 		if (analysisResult.learnedNoGood != null) {
 			for (int literal : analysisResult.learnedNoGood) {
-				heapOfActiveAtoms.incrementActivity(atomOf(literal));
+				incrementActivityOfRelatedChoiceAtoms(atomOf(literal));
 			}
 		}
 		heapOfActiveAtoms.decayIfTimeHasCome();
 	}
 
+	private void incrementActivityOfRelatedChoiceAtoms(int toAtom) {
+		if (atomChoiceRelation == null) {
+			heapOfActiveAtoms.incrementActivity(toAtom);
+			return;
+		}
+		for (Integer relatedChoiceAtom : atomChoiceRelation.getRelatedChoiceAtoms(toAtom)) {
+			if (!choiceManager.isAtomChoice(relatedChoiceAtom)) {
+				throw oops("Related atom is no choice.");
+			}
+			heapOfActiveAtoms.incrementActivity(relatedChoiceAtom);
+		}
+	}
+
 	@Override
 	public void newNoGood(NoGood newNoGood) {
 		this.bufferedNoGoods.add(newNoGood);
@@ -172,6 +189,9 @@ private boolean chooseSign(int atom) {
 
 	public void growForMaxAtomId(int maxAtomId) {
 		heapOfActiveAtoms.growForMaxAtomId(maxAtomId);
+		if (atomChoiceRelation != null) {
+			atomChoiceRelation.growForMaxAtomId(maxAtomId);
+		}
 	}
 
 	@Override
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
index aadf8c8f0..117d55d92 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
@@ -29,6 +29,7 @@
 import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom;
 import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser;
 import at.ac.tuwien.kr.alpha.solver.TrailAssignment;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -57,7 +58,7 @@ public void groundRuleAlreadyGround() {
 		
 		AtomStore atomStore = new AtomStoreImpl();
 		Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true);
-		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore));
+		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue()));
 		int litCNeg = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("c", 0))), false);
 		int litB = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("b", 0))));
 		assertExistsNoGoodContaining(noGoods.values(), litCNeg);
@@ -77,7 +78,7 @@ public void groundRuleWithLongerBodyAlreadyGround() {
 		
 		AtomStore atomStore = new AtomStoreImpl();
 		Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true);
-		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore));
+		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue()));
 		int litANeg = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("a", 0))), false);
 		int litBNeg = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("b", 0))), false);
 		int litCNeg = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("c", 0))), false);
@@ -100,7 +101,7 @@ public void groundConstraintAlreadyGround() {
 		
 		AtomStore atomStore = new AtomStoreImpl();
 		Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true);
-		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore));
+		Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue()));
 		int litB = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("b", 0))));
 		assertTrue(noGoods.containsValue(NoGood.fromConstraint(Arrays.asList(litB), Collections.emptyList())));
 	}
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java
index efe8b1baf..7a23ef780 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustifiedTest.java
@@ -39,6 +39,7 @@
 import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser;
 import at.ac.tuwien.kr.alpha.solver.ThriceTruth;
 import at.ac.tuwien.kr.alpha.solver.TrailAssignment;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -67,7 +68,7 @@ public void justifySimpleRules() {
 		AtomStore atomStore = new AtomStoreImpl();
 		NaiveGrounder grounder = new NaiveGrounder(parsedProgram, atomStore, true);
 		grounder.getNoGoods(null);
-		TrailAssignment assignment = new TrailAssignment(atomStore);
+		TrailAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		int rId = atomStore.get(new BasicAtom(Predicate.getInstance("r", 0)));
 		int nrId = atomStore.get(new BasicAtom(Predicate.getInstance("nr", 0)));
 		assignment.growForMaxAtomId();
@@ -92,7 +93,7 @@ public void justifyLargerRules() {
 		AtomStore atomStore = new AtomStoreImpl();
 		NaiveGrounder grounder = new NaiveGrounder(parsedProgram, atomStore, true);
 		grounder.getNoGoods(null);
-		TrailAssignment assignment = new TrailAssignment(atomStore);
+		TrailAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		Atom p1 = parser.parse("p(1).").getFacts().get(0);
 		Atom r2 = parser.parse("r(2).").getFacts().get(0);
 		Atom s12 = parser.parse("s(1,2).").getFacts().get(0);
@@ -129,7 +130,7 @@ public void justifyMultipleReasons() {
 		AtomStore atomStore = new AtomStoreImpl();
 		NaiveGrounder grounder = new NaiveGrounder(parsedProgram, atomStore, true);
 		grounder.getNoGoods(null);
-		TrailAssignment assignment = new TrailAssignment(atomStore);
+		TrailAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		Atom qa = parser.parse("q(a).").getFacts().get(0);
 		Atom qb = parser.parse("q(b).").getFacts().get(0);
 		Atom qc = parser.parse("q(c).").getFacts().get(0);
@@ -185,7 +186,7 @@ public void justifyNegatedFactsRemovedFromReasons() {
 		AtomStore atomStore = new AtomStoreImpl();
 		NaiveGrounder grounder = new NaiveGrounder(parsedProgram, atomStore, true);
 		grounder.getNoGoods(null);
-		TrailAssignment assignment = new TrailAssignment(atomStore);
+		TrailAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		int rId = atomStore.get(new BasicAtom(Predicate.getInstance("r", 0)));
 		int nrId = atomStore.get(new BasicAtom(Predicate.getInstance("nr", 0)));
 		assignment.growForMaxAtomId();
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/ChoiceManagerTests.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/ChoiceManagerTests.java
index 682ddb425..49e06f571 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/ChoiceManagerTests.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/ChoiceManagerTests.java
@@ -33,6 +33,7 @@
 import at.ac.tuwien.kr.alpha.grounder.NaiveGrounder;
 import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom;
 import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -53,7 +54,7 @@ public void setUp() throws IOException {
 		Program parsedProgram = new ProgramParser().parse(testProgram);
 		this.atomStore = new AtomStoreImpl();
 		this.grounder = new NaiveGrounder(parsedProgram, atomStore, true);
-		WritableAssignment assignment = new TrailAssignment(atomStore);
+		WritableAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		NoGoodStore store = new NoGoodStoreAlphaRoaming(assignment);
 		this.choiceManager = new ChoiceManager(assignment, store);
 	}
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletionTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletionTest.java
index 1d9f4cbc5..2ec460f14 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletionTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/LearnedNoGoodDeletionTest.java
@@ -31,6 +31,7 @@
 import at.ac.tuwien.kr.alpha.common.AtomStoreImpl;
 import at.ac.tuwien.kr.alpha.common.AtomStoreTest;
 import at.ac.tuwien.kr.alpha.common.NoGood;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -47,7 +48,7 @@ public class LearnedNoGoodDeletionTest {
 	public LearnedNoGoodDeletionTest() {
 		AtomStore atomStore = new AtomStoreImpl();
 		AtomStoreTest.fillAtomStore(atomStore, 200);
-		WritableAssignment assignment = new TrailAssignment(atomStore);
+		WritableAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		assignment.growForMaxAtomId();
 		store = new NoGoodStoreAlphaRoaming(assignment);
 		learnedNoGoodDeletion = store.getLearnedNoGoodDeletion();
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java
index 33a069469..5bc9477ca 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/NaiveNoGoodStoreTest.java
@@ -1,6 +1,7 @@
 package at.ac.tuwien.kr.alpha.solver;
 
 import at.ac.tuwien.kr.alpha.common.*;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -22,7 +23,7 @@ public class NaiveNoGoodStoreTest {
 
 	public NaiveNoGoodStoreTest() {
 		atomStore = new AtomStoreImpl();
-		assignment = new TrailAssignment(atomStore);
+		assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		store = new NaiveNoGoodStore(assignment);
 	}
 
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java
index 0f6d73acf..20f18ce43 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/NoGoodStoreAlphaRoamingTest.java
@@ -1,6 +1,7 @@
 package at.ac.tuwien.kr.alpha.solver;
 
 import at.ac.tuwien.kr.alpha.common.*;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -24,7 +25,7 @@ public class NoGoodStoreAlphaRoamingTest {
 	public NoGoodStoreAlphaRoamingTest() {
 		atomStore = new AtomStoreImpl();
 		AtomStoreTest.fillAtomStore(atomStore, 200);
-		assignment = new TrailAssignment(atomStore);
+		assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		assignment.growForMaxAtomId();
 		store = new NoGoodStoreAlphaRoaming(assignment);
 	}
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/TrailAssignmentTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/TrailAssignmentTest.java
index 119e3a0db..f90c7bc05 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/TrailAssignmentTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/TrailAssignmentTest.java
@@ -31,6 +31,7 @@
 import at.ac.tuwien.kr.alpha.common.AtomStore;
 import at.ac.tuwien.kr.alpha.common.AtomStoreImpl;
 import at.ac.tuwien.kr.alpha.common.AtomStoreTest;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -51,7 +52,7 @@ public class TrailAssignmentTest {
 	public TrailAssignmentTest() {
 		AtomStore atomStore = new AtomStoreImpl();
 		AtomStoreTest.fillAtomStore(atomStore, 20);
-		assignment = new TrailAssignment(atomStore);
+		assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 	}
 
 	@Before
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeuristicTestAssumptions.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeuristicTestAssumptions.java
index ab891ae23..4c325280e 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeuristicTestAssumptions.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeuristicTestAssumptions.java
@@ -73,7 +73,7 @@ public void setUp() throws IOException {
 		Program parsedProgram = new ProgramParser().parse(testProgram);
 		this.atomStore = new AtomStoreImpl();
 		this.grounder = new NaiveGrounder(parsedProgram, atomStore, true);
-		this.assignment = new TrailAssignment(atomStore);
+		this.assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		this.choiceManager = new TestableChoiceManager(assignment, new NaiveNoGoodStore(assignment));
 	}
 
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMinTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMinTest.java
index 29227058f..5d660946e 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMinTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMinTest.java
@@ -62,7 +62,7 @@ public class BerkMinTest {
 	public void setUp() {
 		AtomStore atomStore = new AtomStoreImpl();
 		AtomStoreTest.fillAtomStore(atomStore, 2);
-		WritableAssignment assignment = new TrailAssignment(atomStore);
+		WritableAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		assignment.growForMaxAtomId();
 		this.berkmin = new BerkMin(
 			assignment,
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java
index 519f14169..8254e889d 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactoryTest.java
@@ -48,7 +48,7 @@ public class BranchingHeuristicFactoryTest {
 	@Before
 	public void setUp() {
 		AtomStore atomStore = new AtomStoreImpl();
-		WritableAssignment assignment = new TrailAssignment(atomStore);
+		WritableAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		NoGoodStore store = new NoGoodStoreAlphaRoaming(assignment, debugInternalChecks);
 		this.choiceManager = new ChoiceManager(assignment, store);
 	}
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtomsTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtomsTest.java
index a73882ed5..a8955bd78 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtomsTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtomsTest.java
@@ -55,7 +55,7 @@ public class HeapOfActiveAtomsTest {
 	@Before
 	public void setUp() {
 		atomStore = new AtomStoreImpl();
-		assignment = new TrailAssignment(atomStore);
+		assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		noGoodStore = new NoGoodStoreAlphaRoaming(assignment);
 		ChoiceManager choiceManager = new PseudoChoiceManager(assignment, noGoodStore);
 		this.vsids = new VSIDS(assignment, choiceManager, MOMs.DEFAULT_STRATEGY);
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java
index 81ec7bbd1..d6b8796a5 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristicTest.java
@@ -48,7 +48,7 @@ public class ReplayHeuristicTest {
 	@Before
 	public void setUp() {
 		AtomStore atomStore = new AtomStoreImpl();
-		WritableAssignment assignment = new TrailAssignment(atomStore);
+		WritableAssignment assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		NoGoodStore store = new NoGoodStoreAlphaRoaming(assignment, debugInternalChecks);
 		this.choiceManager = new PseudoChoiceManager(assignment, store);
 	}
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java
index e4f4f714f..71f10e96c 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSTest.java
@@ -68,7 +68,7 @@ public class VSIDSTest {
 	public void setUp() {
 		atomStore = new AtomStoreImpl();
 		AtomStoreTest.fillAtomStore(atomStore, 4);
-		assignment = new TrailAssignment(atomStore);
+		assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		assignment.growForMaxAtomId();
 		noGoodStore = new NoGoodStoreAlphaRoaming(assignment);
 		this.vsids = new VSIDS(
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearnerTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearnerTest.java
index 238894bbd..9ec7ecfdf 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearnerTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearnerTest.java
@@ -5,6 +5,7 @@
 import at.ac.tuwien.kr.alpha.common.AtomStoreTest;
 import at.ac.tuwien.kr.alpha.common.NoGood;
 import at.ac.tuwien.kr.alpha.solver.*;
+import at.ac.tuwien.kr.alpha.solver.heuristics.PhaseInitializerFactory;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -24,7 +25,7 @@ public class GroundConflictNoGoodLearnerTest {
 	public GroundConflictNoGoodLearnerTest() {
 		atomStore = new AtomStoreImpl();
 		AtomStoreTest.fillAtomStore(atomStore, 20);
-		this.assignment = new TrailAssignment(atomStore);
+		this.assignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		this.assignment.growForMaxAtomId();
 		this.store = new NoGoodStoreAlphaRoaming(assignment);
 		this.store.growForMaxAtomId(20);

From 78b7e89d383a9435e85ad512d8ff647387114822 Mon Sep 17 00:00:00 2001
From: Antonius Weinzierl 
Date: Thu, 7 Nov 2019 03:32:53 +0100
Subject: [PATCH 62/66] Add HeapOfRelatedChoiceAtoms recording only choice
 points, improve logs.

- In HeapOfActiveAtoms several members/methods package-private now to
  allow overriding.
- Add HeapOfRelatedChoiceAtoms using AtomChoiceRelation to only record
  and initialize choice points.
- Moved ChoiceManager update in DefaultSolver to correctly know which
  atoms are choice points.
- Improved logging in VSIDSWithPhaseSaving and PerformanceLog.
---
 .../kr/alpha/grounder/ChoiceRecorder.java     |  2 +-
 .../tuwien/kr/alpha/solver/DefaultSolver.java |  2 +-
 .../kr/alpha/solver/PerformanceLog.java       |  4 +-
 .../solver/heuristics/HeapOfActiveAtoms.java  | 16 +++----
 .../heuristics/HeapOfRelatedChoiceAtoms.java  | 42 +++++++++++++++++++
 .../heuristics/VSIDSWithPhaseSaving.java      | 17 +++++++-
 6 files changed, 71 insertions(+), 12 deletions(-)
 create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfRelatedChoiceAtoms.java

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/ChoiceRecorder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/ChoiceRecorder.java
index c807e0bae..d4b223a60 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/ChoiceRecorder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/ChoiceRecorder.java
@@ -121,7 +121,7 @@ public String toString() {
 		}
 		sb.append(" disablers: ");
 		for (Map.Entry disablers : newChoiceAtoms.getRight().entrySet()) {
-			sb.append(disablers.getKey()).append("/").append(disablers.getValue());
+			sb.append(disablers.getKey()).append("/").append(disablers.getValue()).append(", ");
 		}
 		return sb.append("]").toString();
 	}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
index 9b0d3980c..c59a8d16e 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java
@@ -468,6 +468,7 @@ private boolean ingest(Map obtained) {
 		int maxAtomId = atomStore.getMaxAtomId();
 		store.growForMaxAtomId(maxAtomId);
 		branchingHeuristic.growForMaxAtomId(maxAtomId);
+		choiceManager.addChoiceInformation(grounder.getChoiceAtoms(), grounder.getHeadsToBodies());
 		branchingHeuristic.newNoGoods(obtained.values());
 
 		LinkedList> noGoodsToAdd = new LinkedList<>(obtained.entrySet());
@@ -517,7 +518,6 @@ private NoGood fixContradiction(Map.Entry noGoodEntry, Conflict
 	}
 
 	private boolean choose() {
-		choiceManager.addChoiceInformation(grounder.getChoiceAtoms(), grounder.getHeadsToBodies());
 		choiceManager.updateAssignments();
 
 		// Hint: for custom heuristics, evaluate them here and pick a value if the heuristics suggests one.
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java
index 28b9b14fe..4cea57caa 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java
@@ -93,8 +93,10 @@ public void infoIfTimeForOutput(Logger logger) {
 			if (firstHeuristic instanceof VSIDSWithPhaseSaving) {
 				VSIDSWithPhaseSaving vsidsWithPhaseSaving = (VSIDSWithPhaseSaving) firstHeuristic;
 				long numThrownAway = vsidsWithPhaseSaving.getNumThrownAway();
+				long numNoChoicePoint = vsidsWithPhaseSaving.getNumNoChoicePoint();
+				long numNotActiveChoicePoint = vsidsWithPhaseSaving.getNumNotActiveChoicePoint();
 				double activityDecrease = vsidsWithPhaseSaving.getActivityDecrease();
-				logger.info("Heuristic threw away {} preferred choices.", numThrownAway);
+				logger.info("Heuristic threw away {} preferred choices ({} no choice, {} not active choice points).", numThrownAway, numNoChoicePoint, numNotActiveChoicePoint);
 				logger.info("Atom activity decreased overall by {} or {} per choice on average", activityDecrease, activityDecrease / currentNumberOfChoices);
 			}
 		}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java
index acadd6920..b9c65515d 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java
@@ -54,10 +54,10 @@ public class HeapOfActiveAtoms {
 
 	private static final double NORMALIZATION_THRESHOLD = 1E100;
 	private static final double INCREMENT_TO_AVOID_DENORMALS = Double.MIN_VALUE * NORMALIZATION_THRESHOLD;
-	private static final double SCORE_EPSILON = 1E-100;
+	static final double SCORE_EPSILON = 1E-100;
 
-	private boolean[] incrementedActivityScores = new boolean[0];
-	private double[] activityScores = new double[0];
+	boolean[] incrementedActivityScores = new boolean[0];
+	double[] activityScores = new double[0];
 	final PriorityQueue heap = new PriorityQueue<>(new AtomActivityComparator().reversed());
 
 	protected ChoiceManager choiceManager;
@@ -65,9 +65,9 @@ public class HeapOfActiveAtoms {
 	private double decayFactor;
 	private int stepsSinceLastDecay;
 	private double currentActivityIncrement = 1.0;
-	private int numberOfNormalizations;
+	int numberOfNormalizations;
 	
-	private final MOMs moms;
+	final MOMs moms;
 
 	HeapOfActiveAtoms(int decayPeriod, double decayFactor, ChoiceManager choiceManager) {
 		this.decayPeriod = decayPeriod;
@@ -164,7 +164,7 @@ private void initActivity(NoGood newNoGood) {
 	 * 1.01 is added to avoid computing the logarithm of a number between 0 and 1 (input scores have to be greater or equal to 0!)
 	 * @param newNoGood a new nogood, the atoms occurring in which will be initialized
 	 */
-	private void initActivityMOMs(NoGood newNoGood) {
+	protected void initActivityMOMs(NoGood newNoGood) {
 		LOGGER.debug("Initializing activity scores with MOMs");
 		for (int literal : newNoGood) {
 			int atom = atomOf(literal);
@@ -233,7 +233,7 @@ protected void incrementActivity(int atom, double increment) {
 		incrementedActivityScores[atom] = true;
 	}
 
-	private void setActivity(int atom, double newActivity) {
+	void setActivity(int atom, double newActivity) {
 		activityScores[atom] = newActivity;
 		LOGGER.trace("Activity of atom {} set to {}", atom, newActivity);
 
@@ -258,7 +258,7 @@ private void normalizeActivityScores() {
 		}
 	}
 
-	private double normalizeNewActivityScore(double newActivity) {
+	double normalizeNewActivityScore(double newActivity) {
 		for (int i = 0; i < numberOfNormalizations; i++) {
 			newActivity = (newActivity + INCREMENT_TO_AVOID_DENORMALS) / NORMALIZATION_THRESHOLD;
 		}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfRelatedChoiceAtoms.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfRelatedChoiceAtoms.java
new file mode 100644
index 000000000..28dc52f31
--- /dev/null
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfRelatedChoiceAtoms.java
@@ -0,0 +1,42 @@
+package at.ac.tuwien.kr.alpha.solver.heuristics;
+
+import at.ac.tuwien.kr.alpha.common.NoGood;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
+import at.ac.tuwien.kr.alpha.solver.ChoiceManager;
+
+import static at.ac.tuwien.kr.alpha.common.Literals.atomOf;
+
+/**
+ * A heap of active choice points that uses {@link AtomChoiceRelation} for initializing activities of related choice points.
+ *
+ * Copyright (c) 2019, the Alpha Team.
+ */
+public class HeapOfRelatedChoiceAtoms extends HeapOfActiveAtoms {
+	private final AtomChoiceRelation atomChoiceRelation;
+
+	HeapOfRelatedChoiceAtoms(int decayPeriod, double decayFactor, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation) {
+		super(decayPeriod, decayFactor, choiceManager);
+		this.atomChoiceRelation = atomChoiceRelation;
+	}
+
+	@Override
+	protected void initActivityMOMs(NoGood newNoGood) {
+		LOGGER.debug("Initializing activity scores with MOMs");
+		for (int literal : newNoGood) {
+			for (Integer relatedChoiceAtom : atomChoiceRelation.getRelatedChoiceAtoms(atomOf(literal))) {
+				if (!incrementedActivityScores[relatedChoiceAtom]) { // update initial value as long as not incremented yet by VSIDS
+					double score = moms.getScore(relatedChoiceAtom);
+					if (score > 0.0) {
+						double newActivity = 1 - 1 / (Math.log(score + 1.01));
+						if (newActivity - activityScores[relatedChoiceAtom] > SCORE_EPSILON) {        // avoid computation overhead if score does not increase
+							if (numberOfNormalizations > 0) {
+								newActivity = normalizeNewActivityScore(newActivity);
+							}
+							setActivity(relatedChoiceAtom, newActivity);
+						}
+					}
+				}
+			}
+		}
+	}
+}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
index 7de6b294b..a13c9e301 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
@@ -66,6 +66,8 @@ public class VSIDSWithPhaseSaving implements ActivityBasedBranchingHeuristic {
 
 	private double activityDecrease;
 	private long numThrownAway;
+	private long numNoChoicePoint;
+	private long numNotActiveChoicePoint;
 
 	private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation, HeapOfActiveAtoms heapOfActiveAtoms, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
 		this.assignment = assignment;
@@ -76,7 +78,7 @@ private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager,
 	}
 
 	private VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation, int decayPeriod, double decayFactor, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
-		this(assignment, choiceManager, atomChoiceRelation, new HeapOfActiveAtoms(decayPeriod, decayFactor, choiceManager),  momsStrategy);
+		this(assignment, choiceManager, atomChoiceRelation, new HeapOfRelatedChoiceAtoms(decayPeriod, decayFactor, choiceManager, atomChoiceRelation),  momsStrategy);
 	}
 
 	VSIDSWithPhaseSaving(Assignment assignment, ChoiceManager choiceManager, AtomChoiceRelation atomChoiceRelation, BinaryNoGoodPropagationEstimation.Strategy momsStrategy) {
@@ -137,6 +139,14 @@ public long getNumThrownAway() {
 		return numThrownAway;
 	}
 
+	public long getNumNoChoicePoint() {
+		return numNoChoicePoint;
+	}
+
+	public long getNumNotActiveChoicePoint() {
+		return numNotActiveChoicePoint;
+	}
+
 	/**
 	 * {@link VSIDSWithPhaseSaving} works like {@link VSIDS} for selecting an atom but uses the saved phase to
 	 * determine the truth value to choose.
@@ -167,6 +177,11 @@ private int chooseAtom() {
 				}
 				return mostActiveAtom;
 			}
+			if (choiceManager.isAtomChoice(mostActiveAtom)) {
+				numNotActiveChoicePoint++;
+			} else {
+				numNoChoicePoint++;
+			}
 			numThrownAway++;
 		}
 		return DEFAULT_CHOICE_ATOM;

From 6143e82e801caeb700866f4bfd0d2df21454f357 Mon Sep 17 00:00:00 2001
From: Antonius Weinzierl 
Date: Tue, 12 Nov 2019 01:29:55 +0100
Subject: [PATCH 63/66] Fix bug in AlphaHeadMustBeTrueHeuristic.

---
 .../alpha/solver/heuristics/AlphaHeadMustBeTrueHeuristic.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeadMustBeTrueHeuristic.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeadMustBeTrueHeuristic.java
index 52df83cd4..06d8cf20e 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeadMustBeTrueHeuristic.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/AlphaHeadMustBeTrueHeuristic.java
@@ -36,6 +36,8 @@
 import java.util.Set;
 import java.util.stream.Stream;
 
+import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral;
+
 /**
  * A variant of {@link DependencyDrivenHeuristic} that prefers to choose atoms representing bodies of rules whose heads
  * are assigned {@link at.ac.tuwien.kr.alpha.solver.ThriceTruth#MBT}.
@@ -60,7 +62,7 @@ public int chooseLiteral() {
 				.max(Comparator.comparingDouble(bodyActivity::get));
 		if (mostActiveBody.isPresent()) {
 			rememberedAtom = mostActiveBody.get();
-			return rememberedAtom;
+			return atomToLiteral(rememberedAtom);
 		}
 		return super.chooseLiteral();
 	}

From cd6ea23e4f7dde7c4d93aa458fbe05c11d105ba8 Mon Sep 17 00:00:00 2001
From: Antonius Weinzierl 
Date: Tue, 12 Nov 2019 02:00:21 +0100
Subject: [PATCH 64/66] Fix Luby restart sequence computation.

---
 .../java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java
index 5e8a552ce..56cf97125 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/MixedRestartStrategy.java
@@ -76,6 +76,6 @@ private void nextLuby() {
 		} else {
 			vn <<= 1;
 		}
-		lubyLimit = vn * LUBY_FACTOR;
+		lubyLimit = vn * LUBY_FACTOR + learnedNoGoodDeletion.getNumTotalConflicts();
 	}
 }

From e83c8661ca887e5e4527ac2f81a99e73d7c8bd41 Mon Sep 17 00:00:00 2001
From: Antonius Weinzierl 
Date: Tue, 12 Nov 2019 04:44:13 +0100
Subject: [PATCH 65/66] Make AtomChoiceRelation a mandatory Grounder component.

- Move getAtomChoiceRelation() from NaiveGrounder to Grounder, implement
  method also in DummyGrounder and ChoiceGrounder.
- HeapOfActiveAtoms keeps track of literals occurring in the heap.
- More logging stats from VSIDSWithPhaseSaving.
- ChoiceInfluenceManager: simplified callbackOnChange
---
 .../ac/tuwien/kr/alpha/grounder/Grounder.java |  8 ++++
 .../kr/alpha/grounder/NaiveGrounder.java      |  1 +
 .../alpha/solver/ChoiceInfluenceManager.java  | 10 +++--
 .../kr/alpha/solver/PerformanceLog.java       |  3 +-
 .../heuristics/BranchingHeuristicFactory.java |  3 +-
 .../solver/heuristics/HeapOfActiveAtoms.java  | 37 +++++++++++++------
 .../heuristics/VSIDSWithPhaseSaving.java      |  4 ++
 .../kr/alpha/grounder/ChoiceGrounder.java     | 11 ++++++
 .../kr/alpha/grounder/DummyGrounder.java      |  7 ++++
 9 files changed, 65 insertions(+), 19 deletions(-)

diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Grounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/Grounder.java
index cee82381e..89503f40f 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Grounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/Grounder.java
@@ -31,6 +31,7 @@
 import at.ac.tuwien.kr.alpha.common.Assignment;
 import at.ac.tuwien.kr.alpha.common.NoGood;
 import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import org.apache.commons.lang3.tuple.Pair;
 
 import java.util.Iterator;
@@ -79,4 +80,11 @@ public interface Grounder {
 	 * @return
 	 */
 	int register(NoGood noGood);
+
+
+	/**
+	 * Returns the relation between atoms and choices in form of an {@link AtomChoiceRelation} object.
+	 * @return the {@link AtomChoiceRelation} of the grounded program parts.
+	 */
+	AtomChoiceRelation getAtomChoiceRelation();
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
index 768403d92..7134a6dbe 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounder.java
@@ -115,6 +115,7 @@ public NaiveGrounder(Program program, AtomStore atomStore, boolean debugInternal
 		this.debugInternalChecks = debugInternalChecks;
 	}
 
+	@Override
 	public AtomChoiceRelation getAtomChoiceRelation() {
 		return atomChoiceRelation;
 	}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/ChoiceInfluenceManager.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/ChoiceInfluenceManager.java
index c3111fd84..7ae3cfe7c 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/ChoiceInfluenceManager.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/ChoiceInfluenceManager.java
@@ -196,8 +196,10 @@ void recomputeActive() {
 				changed = true;
 				LOGGER.debug("Deactivating choice point for atom {}", this.atom);
 			}
-			if (changed && activityListener != null) {
-				activityListener.callbackOnChanged(atom, isActive);
+			if (changed
+				//&& isActive // FIXME: not sure if this should be in, apparently bugs VSIDS.
+				&& activityListener != null) {
+				activityListener.callbackOnChange(atom);
 			}
 		}
 
@@ -207,8 +209,8 @@ public String toString() {
 		}
 	}
 	
-	public static interface ActivityListener {
-		void callbackOnChanged(int atom, boolean active);
+	public interface ActivityListener {
+		void callbackOnChange(int atom);
 	}
 
 }
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java
index 4cea57caa..a84b45c69 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/PerformanceLog.java
@@ -96,7 +96,8 @@ public void infoIfTimeForOutput(Logger logger) {
 				long numNoChoicePoint = vsidsWithPhaseSaving.getNumNoChoicePoint();
 				long numNotActiveChoicePoint = vsidsWithPhaseSaving.getNumNotActiveChoicePoint();
 				double activityDecrease = vsidsWithPhaseSaving.getActivityDecrease();
-				logger.info("Heuristic threw away {} preferred choices ({} no choice, {} not active choice points).", numThrownAway, numNoChoicePoint, numNotActiveChoicePoint);
+				logger.info("Heuristic threw away {} preferred choices ({} no choice, {} not active choice points) averaging {} thrown away per choice done.", numThrownAway, numNoChoicePoint, numNotActiveChoicePoint, (float) numThrownAway / currentNumberOfChoices);
+				logger.info("HeapOfActiveAtoms had {} choices added due to increased activity.", vsidsWithPhaseSaving.getNumAddedToHeapByActivity());
 				logger.info("Atom activity decreased overall by {} or {} per choice on average", activityDecrease, activityDecrease / currentNumberOfChoices);
 			}
 		}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java
index 6d56a9ed9..93a23f018 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristicFactory.java
@@ -26,7 +26,6 @@
 package at.ac.tuwien.kr.alpha.solver.heuristics;
 
 import at.ac.tuwien.kr.alpha.grounder.Grounder;
-import at.ac.tuwien.kr.alpha.grounder.ProgramAnalyzingGrounder;
 import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import at.ac.tuwien.kr.alpha.solver.ChoiceManager;
 import at.ac.tuwien.kr.alpha.solver.WritableAssignment;
@@ -121,7 +120,7 @@ private static BranchingHeuristic getInstanceWithoutReplay(HeuristicsConfigurati
 		case GDD_VSIDS:
 			return new DependencyDrivenVSIDS(assignment, choiceManager, random, heuristicsConfiguration.getMomsStrategy());
 		case VSIDS_PHASE_SAVING:
-			AtomChoiceRelation atomChoiceRelation = grounder instanceof ProgramAnalyzingGrounder ? ((ProgramAnalyzingGrounder) grounder).getAtomChoiceRelation() : null;
+			AtomChoiceRelation atomChoiceRelation = grounder.getAtomChoiceRelation();
 			return new VSIDSWithPhaseSaving(assignment, choiceManager, atomChoiceRelation, heuristicsConfiguration.getMomsStrategy());
 		}
 		throw new IllegalArgumentException("Unknown branching heuristic requested.");
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java
index b9c65515d..5182c0948 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/HeapOfActiveAtoms.java
@@ -57,6 +57,7 @@ public class HeapOfActiveAtoms {
 	static final double SCORE_EPSILON = 1E-100;
 
 	boolean[] incrementedActivityScores = new boolean[0];
+	boolean[] occursInHeap = new boolean[0];
 	double[] activityScores = new double[0];
 	final PriorityQueue heap = new PriorityQueue<>(new AtomActivityComparator().reversed());
 
@@ -66,6 +67,7 @@ public class HeapOfActiveAtoms {
 	private int stepsSinceLastDecay;
 	private double currentActivityIncrement = 1.0;
 	int numberOfNormalizations;
+	private long numAddedToHeapByActivity;
 	
 	final MOMs moms;
 
@@ -186,6 +188,7 @@ protected void initActivityMOMs(NoGood newNoGood) {
 	void growToCapacity(int newCapacity) {
 		activityScores = Arrays.copyOf(activityScores, newCapacity);
 		incrementedActivityScores = Arrays.copyOf(incrementedActivityScores, newCapacity);
+		occursInHeap = Arrays.copyOf(occursInHeap, newCapacity);
 	}
 
 	void growForMaxAtomId(int maxAtomId) {
@@ -197,8 +200,7 @@ void growForMaxAtomId(int maxAtomId) {
 		if (newCapacity < maxAtomId + 1) {
 			newCapacity = maxAtomId + 1;
 		}
-		activityScores = Arrays.copyOf(activityScores, newCapacity);
-		incrementedActivityScores = Arrays.copyOf(incrementedActivityScores, newCapacity);
+		growToCapacity(newCapacity);
 	}
 
 	private void initActivityNaive(NoGood newNoGood) {
@@ -213,7 +215,11 @@ private void initActivityNaive(NoGood newNoGood) {
 	 * Returns the atom with the highest activity score and removes it from the heap.
 	 */
 	Integer getMostActiveAtom() {
-		return heap.poll();
+		Integer mostActiveAtom = heap.poll();
+		if (mostActiveAtom != null) {
+			occursInHeap[mostActiveAtom] = false;
+		}
+		return mostActiveAtom;
 	}
 
 	/**
@@ -241,7 +247,11 @@ void setActivity(int atom, double newActivity) {
 			normalizeActivityScores();
 		}
 
-		heap.add(atom); // ignores the fact that atom may already be in the heap for performance reasons (may be revised in future)
+		if (choiceManager.isActiveChoiceAtom(atom)) {
+			numAddedToHeapByActivity++;
+			occursInHeap[atom] = true;
+			heap.add(atom); // ignores the fact that atom may already be in the heap for performance reasons (may be revised in future)
+		}
 	}
 
 	/**
@@ -265,6 +275,10 @@ private void normalizeActivityScores() {
 		return newActivity;
 	}
 
+	long getNumAddedToHeapByActivity() {
+		return numAddedToHeapByActivity;
+	}
+
 	private class AtomActivityComparator implements Comparator {
 
 		@Override
@@ -277,14 +291,13 @@ public int compare(Integer a1, Integer a2) {
 	private class ChoicePointActivityListener implements ChoiceInfluenceManager.ActivityListener {
 
 		@Override
-		public void callbackOnChanged(int atom, boolean active) {
-			if (active && choiceManager.isActiveChoiceAtom(atom)) {
-				if (atom < activityScores.length) {
-					/* if atom has no activity score, probably the atom is still being buffered
-					   by DependencyDrivenVSIDSHeuristic and will get an initial activity
-					   when the buffer is ingested */
-					heap.add(atom);
-				}
+		public void callbackOnChange(int atom) {
+			if (!occursInHeap[atom] && atom < activityScores.length) {
+				/* if atom has no activity score, probably the atom is still being buffered
+				   by DependencyDrivenVSIDSHeuristic and will get an initial activity
+				   when the buffer is ingested */
+				occursInHeap[atom] = true;
+				heap.add(atom);
 			}
 		}
 	}
diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
index a13c9e301..c5d35da34 100644
--- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
+++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDSWithPhaseSaving.java
@@ -147,6 +147,10 @@ public long getNumNotActiveChoicePoint() {
 		return numNotActiveChoicePoint;
 	}
 
+	public long getNumAddedToHeapByActivity() {
+		return heapOfActiveAtoms.getNumAddedToHeapByActivity();
+	}
+
 	/**
 	 * {@link VSIDSWithPhaseSaving} works like {@link VSIDS} for selecting an atom but uses the saved phase to
 	 * determine the truth value to choose.
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/ChoiceGrounder.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/ChoiceGrounder.java
index 30df8b212..5583b3617 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/ChoiceGrounder.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/ChoiceGrounder.java
@@ -32,6 +32,7 @@
 import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom;
 import at.ac.tuwien.kr.alpha.grounder.atoms.ChoiceAtom;
 import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 
@@ -102,6 +103,7 @@ public class ChoiceGrounder implements Grounder {
 	private static Atom atomEnBR2 = ChoiceAtom.on(2);
 	private static Atom atomDisBR1 = ChoiceAtom.off(3);
 	private static Atom atomDisBR2 = ChoiceAtom.off(4);
+	private static AtomChoiceRelation atomChoiceRelation = new AtomChoiceRelation();
 	private final AtomStore atomStore;
 	private boolean returnedAllNogoods;
 
@@ -115,6 +117,10 @@ public ChoiceGrounder(AtomStore atomStore, java.util.function.Predicate it) {
 	public void forgetAssignment(int[] atomIds) {
 	}
 
+	@Override
+	public AtomChoiceRelation getAtomChoiceRelation() {
+		return atomChoiceRelation;
+	}
+
 	private int solverDerivedNoGoodIdCounter = 20;
 	private Map solverDerivedNoGoods = new HashMap<>();
 
diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/DummyGrounder.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/DummyGrounder.java
index 418eb0ae6..621ee195a 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/DummyGrounder.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/DummyGrounder.java
@@ -31,6 +31,7 @@
 import at.ac.tuwien.kr.alpha.common.atoms.Atom;
 import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom;
 import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom;
+import at.ac.tuwien.kr.alpha.grounder.structure.AtomChoiceRelation;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 
@@ -74,6 +75,7 @@ public class DummyGrounder implements Grounder {
 	private static Atom atomCC = new BasicAtom(Predicate.getInstance("c", 0));
 	private static Rule ruleABC = new Rule(new DisjunctiveHead(Collections.singletonList(atomCC)), Arrays.asList(atomAA.toLiteral(), atomBB.toLiteral()));
 	private static Atom rule1 = new RuleAtom(NonGroundRule.constructNonGroundRule(ruleABC), new Substitution());
+	private static AtomChoiceRelation atomChoiceRelation = new AtomChoiceRelation();
 	private Set returnedNogoods = new HashSet<>();
 
 	public DummyGrounder(AtomStore atomStore) {
@@ -104,6 +106,11 @@ public int register(NoGood noGood) {
 		return solverDerivedNoGoods.get(noGood);
 	}
 
+	@Override
+	public AtomChoiceRelation getAtomChoiceRelation() {
+		return atomChoiceRelation;
+	}
+
 	@Override
 	public AnswerSet assignmentToAnswerSet(Iterable trueAtoms) {
 		// Note: This grounder only deals with 0-ary predicates, i.e., every atom is a predicate and there is

From a116cf21fbbf5586f9eb3f4300b0dfb9b768b626 Mon Sep 17 00:00:00 2001
From: Richard Taupe 
Date: Wed, 13 Nov 2019 08:26:24 +0100
Subject: [PATCH 66/66] fix merge 184cbd7f85d5944d0d8893c3580bf4dbb1e5a1cd

---
 .../at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
index 50057563f..09236b940 100644
--- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
+++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NaiveGrounderTest.java
@@ -157,7 +157,7 @@ private void testDeadEnd(RuleGroundingOrder groundingOrderP1, RuleGroundingOrder
 		nonGroundRule.groundingOrder.groundingOrders.put(litQ1Y, groundingOrderQ1);
 
 		grounder.bootstrap();
-		TrailAssignment currentAssignment = new TrailAssignment(atomStore);
+		TrailAssignment currentAssignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		final Substitution substP1X1 = Substitution.unify(litP1X, new Instance(ConstantTerm.getInstance(1)), new Substitution());
 		final Substitution substQ1Y1 = Substitution.unify(litQ1Y, new Instance(ConstantTerm.getInstance(1)), new Substitution());
 		final NaiveGrounder.BindingResult bindingResultP1 = grounder.bindNextAtomInRule(nonGroundRule, groundingOrderP1, substP1X1, currentAssignment);
@@ -203,7 +203,7 @@ public void testGroundingOfRuleNotSwitchedOffByFalseNegativeBody() {
 
 	private void testIfGrounderGroundsRule(Program program, int ruleID, Literal startingLiteral, int startingInstance, ThriceTruth bTruth, boolean expectNoGoods) {
 		AtomStore atomStore = new AtomStoreImpl();
-		TrailAssignment currentAssignment = new TrailAssignment(atomStore);
+		TrailAssignment currentAssignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, p -> true, GrounderHeuristicsConfiguration.lax(), true);
 
 		int b = atomStore.putIfAbsent(atom("b", 1));
@@ -267,7 +267,7 @@ public void testLaxGrounderHeuristicTolerance_2_accept_multiple_facts_of_same_va
 
 	private void testLaxGrounderHeuristicTolerance(Program program, int ruleID, Literal startingLiteral, int startingInstance, int tolerance, int nTrueBs, boolean expectNoGoods) {
 		AtomStore atomStore = new AtomStoreImpl();
-		TrailAssignment currentAssignment = new TrailAssignment(atomStore);
+		TrailAssignment currentAssignment = new TrailAssignment(atomStore, PhaseInitializerFactory.getPhaseInitializerAllTrue());
 		GrounderHeuristicsConfiguration heuristicConfiguration = GrounderHeuristicsConfiguration.getInstance(tolerance, tolerance);
 		NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, p -> true, heuristicConfiguration, true);