From 5e41ba82f96a895ecb2555a098f882abeccfd613 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 20 Mar 2020 13:19:56 +0100 Subject: [PATCH 01/55] Add references to original nogoods to Antecedents --- .../at/ac/tuwien/kr/alpha/common/NoGood.java | 20 ++++++++-- .../ac/tuwien/kr/alpha/solver/Antecedent.java | 39 ++++++++++++++++++- .../tuwien/kr/alpha/solver/WatchedNoGood.java | 7 ++++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java index cf452fa8f..817e8be32 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java @@ -35,8 +35,15 @@ import java.util.stream.IntStream; import static at.ac.tuwien.kr.alpha.Util.oops; -import static at.ac.tuwien.kr.alpha.common.Literals.*; -import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.*; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.isNegated; +import static at.ac.tuwien.kr.alpha.common.Literals.isPositive; +import static at.ac.tuwien.kr.alpha.common.Literals.negateLiteral; +import static at.ac.tuwien.kr.alpha.common.Literals.positiveLiteral; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.INTERNAL; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.LEARNT; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.SUPPORT; public class NoGood implements NoGoodInterface, Comparable { public static final int HEAD = 0; @@ -144,6 +151,8 @@ public int size() { public Antecedent asAntecedent() { return new Antecedent() { + private final NoGood originalNoGood = NoGood.this; + @Override public int[] getReasonLiterals() { return NoGood.this.literals; // Beware: returned array must not be modified! @@ -156,7 +165,12 @@ public void bumpActivity() { @Override public void decreaseActivity() { } - }; + + @Override + public NoGood getOriginalNoGood() { + return originalNoGood; + } + }; } public NoGood withoutHead() { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java index c841ecc0e..6db35070b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java @@ -1,10 +1,39 @@ +/* + * Copyright (c) 2019-2020, 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; + /** * An interface to reasons of implications as used internally by the solver. This is a lightweight {@link at.ac.tuwien.kr.alpha.common.NoGood} that only * provides an array of literals (in some order) and has an activity that may change. * - * Copyright (c) 2019, the Alpha Team. + * Copyright (c) 2019-2020, the Alpha Team. */ public interface Antecedent { @@ -14,4 +43,12 @@ public interface Antecedent { void decreaseActivity(); + /** + * Gets the {@link NoGood} from which this Antecedent has been created + * @return the original nogood, if known, else {@code null}. + */ + default NoGood getOriginalNoGood() { + return null; + } + } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java index 4395d9ed5..d809b85f8 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java @@ -42,6 +42,7 @@ public final class WatchedNoGood implements NoGoodInterface, Antecedent { private int head; private final Type type; private boolean isLbdLessOrEqual2; + private final NoGood originalNoGood; WatchedNoGood(NoGood noGood, int a, int b, int alpha) { if (noGood.size() < 3) { @@ -63,6 +64,7 @@ public final class WatchedNoGood implements NoGoodInterface, Antecedent { swap(1, b); } this.type = noGood.getType(); + this.originalNoGood = noGood; } private void checkPointers(int a, int b, int alpha) { @@ -161,6 +163,11 @@ public Integer next() { }; } + @Override + public NoGood getOriginalNoGood() { + return originalNoGood; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); From a5c6a9b47288e58d7420b73b6c3973311be5d196 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 20 Mar 2020 14:46:47 +0100 Subject: [PATCH 02/55] Introduce NonGroundNoGood --- .../ac/tuwien/kr/alpha/common/AtomStore.java | 2 +- .../at/ac/tuwien/kr/alpha/common/NoGood.java | 22 +-- .../kr/alpha/common/NoGoodInterface.java | 12 +- .../kr/alpha/common/NonGroundNoGood.java | 145 ++++++++++++++++++ .../kr/alpha/grounder/NonGroundRule.java | 11 +- .../atoms/BodyRepresentingLiteral.java | 69 +++++++++ .../kr/alpha/grounder/atoms/RuleAtom.java | 9 +- .../tuwien/kr/alpha/solver/DefaultSolver.java | 4 +- .../kr/alpha/solver/NaiveNoGoodStore.java | 16 +- .../tuwien/kr/alpha/solver/NoGoodCounter.java | 14 +- .../tuwien/kr/alpha/solver/NoGoodStore.java | 29 +++- .../alpha/solver/NoGoodStoreAlphaRoaming.java | 8 +- .../solver/SolverMaintainingStatistics.java | 6 +- .../tuwien/kr/alpha/solver/WatchedNoGood.java | 6 +- .../ac/tuwien/kr/alpha/common/NoGoodTest.java | 33 +++- .../solver/LearnedNoGoodDeletionTest.java | 19 ++- .../alpha/solver/SolverStatisticsTests.java | 8 +- 17 files changed, 353 insertions(+), 60 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/AtomStore.java b/src/main/java/at/ac/tuwien/kr/alpha/common/AtomStore.java index ec8278b82..02cf91297 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/AtomStore.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/AtomStore.java @@ -94,7 +94,7 @@ default String literalToString(int literal) { * @param noGood the nogood to translate * @return the string representation of the NoGood. */ - default String noGoodToString(T noGood) { + default > String noGoodToString(T noGood) { StringBuilder sb = new StringBuilder(); if (noGood.hasHead()) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java index 817e8be32..5b0bfaca9 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java @@ -45,13 +45,13 @@ import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.SUPPORT; -public class NoGood implements NoGoodInterface, Comparable { - public static final int HEAD = 0; +public class NoGood implements NoGoodInterface, Comparable { public static final NoGood UNSAT = new NoGood(); protected final int[] literals; private final boolean head; private final Type type; + private NonGroundNoGood nonGroundNoGood; public NoGood(int... literals) { this(STATIC, literals, false); @@ -64,7 +64,7 @@ public NoGood(Type type, int... literals) { private NoGood(Type type, int[] literals, boolean head) { this.type = type; this.head = head; - if (head && !isNegated(literals[0])) { + if (head && !isNegated(literals[HEAD])) { throw oops("Head is not negative"); } @@ -87,6 +87,7 @@ protected NoGood(NoGood noGood) { this.literals = noGood.literals.clone(); this.head = noGood.head; this.type = noGood.type; + this.nonGroundNoGood = noGood.nonGroundNoGood; } public static NoGood learnt(int... literals) { @@ -185,7 +186,7 @@ public int getPositiveLiteral(int index) { } @Override - public int getLiteral(int index) { + public Integer getLiteral(int index) { return literals[index]; } @@ -194,16 +195,19 @@ public boolean hasHead() { return head; } - @Override - public int getHead() { - return getLiteral(HEAD); - } - @Override public Type getType() { return type; } + public NonGroundNoGood getNonGroundNoGood() { + return nonGroundNoGood; + } + + public void setNonGroundNoGood(NonGroundNoGood nonGroundNoGood) { + this.nonGroundNoGood = nonGroundNoGood; + } + @Override public Iterator iterator() { return new Iterator() { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGoodInterface.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGoodInterface.java index 95157630e..eb43cb575 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGoodInterface.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGoodInterface.java @@ -29,14 +29,16 @@ import at.ac.tuwien.kr.alpha.solver.Antecedent; -public interface NoGoodInterface extends Iterable { +public interface NoGoodInterface extends Iterable { + + int HEAD = 0; /** * Returns the literal at the given index. * @param index the index position within the NoGood. * @return the literal at the index. */ - int getLiteral(int index); + T getLiteral(int index); /** * Returns whether the NoGood has a head. @@ -46,9 +48,11 @@ public interface NoGoodInterface extends Iterable { /** * Returns the head literal of the NoGood, if present. - * @return the head literal if the NoGood has a head, otherwise an arbitrary integer. + * @return the head literal if the NoGood has a head, otherwise an arbitrary literal. */ - int getHead(); + default T getHead() { + return getLiteral(HEAD); + } /** * Returns the size, i.e., number of literals, in the NoGood. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java new file mode 100644 index 000000000..3717d4488 --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 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.common; + +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.solver.Antecedent; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static at.ac.tuwien.kr.alpha.Util.oops; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; + +/** + * Represents a non-ground nogood that corresponds to several ground nogoods generated from it. + * + * This class assumes the following to hold without checking it: + *
    + *
  • no literal appears twice (different variable names are OK, if different literals correspond to different ground literals)
  • + *
  • the non-ground literals here appear in the same order as the corresponding ground literals in the corresponding ground nogood
  • + *
  • any literals not present in the corresponding ground nogood (e.g., arithmetic literals removed by the grounder) + * appear after all literals present in the ground nogood (i.e., if the ground nogood contains N literals, then + * the first N literals in the non-ground nogood correspond directly to ground literals at the same positions in + * the ground nogood, and all positions from N+1 till the end are for literals not present in the ground nogood)
  • + *
+ */ +public class NonGroundNoGood implements NoGoodInterface { + + protected final List literals; + private final boolean head; + private final Type type; + + public NonGroundNoGood(List literals) { + this(STATIC, literals, false); + } + + public NonGroundNoGood(Type type, List literals) { + this(type, literals, false); + } + + private NonGroundNoGood(Type type, List literals, boolean head) { + this.type = type; + this.head = head; + if (head && literals.get(HEAD).isNegated()) { + throw oops("Head is not negative"); + } + this.literals = literals; + } + + public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Literal... literals) { + return new NonGroundNoGood(groundNoGood.getType(), Arrays.asList(literals)); + } + + @Override + public Literal getLiteral(int index) { + return literals.get(index); + } + + @Override + public boolean hasHead() { + return head; + } + + @Override + public int size() { + return literals.size(); + } + + @Override + public Antecedent asAntecedent() { + throw new UnsupportedOperationException("Non-ground nogood cannot be represented as an antecedent"); + } + + @Override + public Type getType() { + return type; + } + + @Override + public Iterator iterator() { + return literals.iterator(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NonGroundNoGood that = (NonGroundNoGood) o; + return head == that.head && + literals.equals(that.literals) && + type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(literals, head, type); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + if (head) { + sb.append("*"); + } + + sb.append("{ "); + + for (Literal literal : literals) { + sb.append(literal.isNegated() ? "-" : "+"); + sb.append(literal.getAtom().toString()); + sb.append(" "); + } + + sb.append("}"); + + return sb.toString(); + } +} 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 f9cdbdf05..0caff1a70 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-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -32,6 +32,7 @@ import at.ac.tuwien.kr.alpha.common.Rule; 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.atoms.RuleAtom; import java.util.ArrayList; import java.util.Collections; @@ -53,6 +54,7 @@ public class NonGroundRule { private final List bodyAtomsPositive; private final List bodyAtomsNegative; private final Atom headAtom; + private final RuleAtom nonGroundRuleAtom; final RuleGroundingOrders groundingOrder; @@ -69,6 +71,7 @@ private NonGroundRule(Rule rule, int ruleId, List bodyAtomsPositive, List< this.bodyAtomsNegative = Collections.unmodifiableList(bodyAtomsNegative); this.headAtom = headAtom; + this.nonGroundRuleAtom = new RuleAtom(this, new Substitution()); checkSafety(); this.groundingOrder = new RuleGroundingOrders(this); @@ -163,4 +166,8 @@ public List getBodyAtomsNegative() { public Atom getHeadAtom() { return headAtom; } + + public RuleAtom getNonGroundRuleAtom() { + return nonGroundRuleAtom; + } } \ No newline at end of file diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java new file mode 100644 index 000000000..7e816147c --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 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.atoms; + +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; +import at.ac.tuwien.kr.alpha.grounder.Substitution; + +import java.util.Collections; +import java.util.Set; + +/** + * A literal containing a {@link RuleAtom} (only to be used internally, e.g., in {@link at.ac.tuwien.kr.alpha.common.NonGroundNoGood}s) + */ +public class BodyRepresentingLiteral extends Literal { + + public BodyRepresentingLiteral(RuleAtom atom, boolean positive) { + super(atom, positive); + } + + @Override + public RuleAtom getAtom() { + return (RuleAtom) super.getAtom(); + } + + @Override + public BodyRepresentingLiteral negate() { + return new BodyRepresentingLiteral(getAtom(), !positive); + } + + @Override + public BodyRepresentingLiteral substitute(Substitution substitution) { + return this; // a BodyRepresentingLiteral is currently always ground + } + + @Override + public Set getBindingVariables() { + return Collections.emptySet(); + } + + @Override + public Set getNonBindingVariables() { + return Collections.emptySet(); + } +} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java index bd10713d6..d8cff743b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2018, the Alpha Team. +/* + * Copyright (c) 2016-2018, 2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -29,7 +29,6 @@ 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.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.grounder.NonGroundRule; @@ -84,8 +83,8 @@ public boolean isGround() { } @Override - public Literal toLiteral(boolean positive) { - throw new UnsupportedOperationException("RuleAtom cannot be literalized"); + public BodyRepresentingLiteral toLiteral(boolean positive) { + return new BodyRepresentingLiteral(this, positive); } @Override 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 c8e9391da..84c19b2aa 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 @@ -575,7 +575,7 @@ public int getNumberOfDeletedNoGoods() { } @Override - public NoGoodCounter getNoGoodCounter() { + public NoGoodCounter getNoGoodCounter() { return store.getNoGoodCounter(); } @@ -588,7 +588,7 @@ private void logStats() { LOGGER.debug("{}: {}", heuristicToDecisionCounter.getKey(), heuristicToDecisionCounter.getValue()); } } - NoGoodCounter noGoodCounter = store.getNoGoodCounter(); + final NoGoodCounter noGoodCounter = store.getNoGoodCounter(); LOGGER.debug("Number of NoGoods by type: {}", noGoodCounter.getStatsByType()); LOGGER.debug("Number of NoGoods by cardinality: {}", noGoodCounter.getStatsByCardinality()); AtomCounter atomCounter = atomStore.getAtomCounter(); 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 28af2719c..57208a832 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,5 +1,5 @@ -/** - * Copyright (c) 2017-2019, the Alpha Team. +/* + * Copyright (c) 2017-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -33,15 +33,19 @@ import java.util.HashMap; -import static at.ac.tuwien.kr.alpha.common.Literals.*; -import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.*; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.isNegated; +import static at.ac.tuwien.kr.alpha.common.Literals.isPositive; +import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.FALSE; +import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.MBT; +import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.TRUE; public class NaiveNoGoodStore implements NoGoodStore { private static final Logger LOGGER = LoggerFactory.getLogger(NaiveNoGoodStore.class); private HashMap delegate = new HashMap<>(); private final WritableAssignment assignment; - private final NoGoodCounter counter = new NoGoodCounter(); + private final NoGoodCounter counter = new NoGoodCounter<>(); private boolean hasInferredAssignments; @@ -134,7 +138,7 @@ public void growForMaxAtomId(int maxAtomId) { } @Override - public NoGoodCounter getNoGoodCounter() { + public NoGoodCounter getNoGoodCounter() { return counter; } 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 432419ab1..b3d0c53ef 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2019 Siemens AG +/* + * Copyright (c) 2019-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,8 +26,8 @@ package at.ac.tuwien.kr.alpha.solver; import at.ac.tuwien.kr.alpha.common.NoGood; -import at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type; import at.ac.tuwien.kr.alpha.common.NoGoodInterface; +import at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type; import java.util.ArrayList; import java.util.List; @@ -35,7 +35,7 @@ /** * Maintains statistics on numbers of various types of {@link NoGood}s. */ -public class NoGoodCounter { +public class NoGoodCounter { private static final int CARD_NARY = 0; private static final int CARD_UNARY = 1; @@ -48,7 +48,7 @@ public class NoGoodCounter { * Increases counters for the types of the given NoGood * @param noGood */ - void add(NoGoodInterface noGood) { + void add(NoGoodInterface noGood) { countByType[noGood.getType().ordinal()]++; countByCardinality[getAbstractCardinality(noGood)]++; } @@ -57,12 +57,12 @@ void add(NoGoodInterface noGood) { * Decreases counters for the types of the given NoGood * @param noGood */ - void remove(NoGoodInterface noGood) { + void remove(NoGoodInterface noGood) { countByType[noGood.getType().ordinal()]--; countByCardinality[getAbstractCardinality(noGood)]--; } - private int getAbstractCardinality(NoGoodInterface noGood) { + private int getAbstractCardinality(NoGoodInterface noGood) { if (noGood.isUnary()) { return CARD_UNARY; } 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 7cd516b40..b83922672 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 @@ -1,3 +1,30 @@ +/* + * Copyright (c) 2016-2020, 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; @@ -54,5 +81,5 @@ public interface NoGoodStore { */ void cleanupLearnedNoGoods(); - NoGoodCounter getNoGoodCounter(); + 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 1fb97957c..08140701d 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 @@ -83,7 +83,7 @@ public class NoGoodStoreAlphaRoaming implements NoGoodStore, BinaryNoGoodPropaga private boolean didPropagate; private boolean hasBinaryNoGoods; - private final NoGoodCounter counter = new NoGoodCounter(); + private final NoGoodCounter counter = new NoGoodCounter<>(); public NoGoodStoreAlphaRoaming(WritableAssignment assignment, boolean checksEnabled) { this.assignment = assignment; @@ -149,7 +149,7 @@ public void growForMaxAtomId(int maxAtomId) { } @Override - public NoGoodCounter getNoGoodCounter() { + public NoGoodCounter getNoGoodCounter() { return counter; } @@ -426,13 +426,13 @@ private ConflictCause addAndWatchBinary(final NoGood noGood) { return binaryWatches[b].add(noGood); } - private ConflictCause assignWeakComplement(final int literalIndex, final NoGoodInterface impliedBy, int decisionLevel) { + private ConflictCause assignWeakComplement(final int literalIndex, final NoGoodInterface impliedBy, int decisionLevel) { final int literal = impliedBy.getLiteral(literalIndex); ThriceTruth truth = isNegated(literal) ? MBT : FALSE; return assignTruth(atomOf(literal), truth, impliedBy.asAntecedent(), decisionLevel); } - private ConflictCause assignStrongComplement(final NoGoodInterface impliedBy, int decisionLevel) { + private ConflictCause assignStrongComplement(final NoGoodInterface impliedBy, int decisionLevel) { return assignTruth(atomOf(impliedBy.getHead()), TRUE, impliedBy.asAntecedent(), decisionLevel); } 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 0c29abef7..2d2fddcff 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-2019 Siemens AG +/* + * Copyright (c) 2017-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,7 @@ public interface SolverMaintainingStatistics { */ int getNumberOfConflictsAfterClosing(); - NoGoodCounter getNoGoodCounter(); + NoGoodCounter getNoGoodCounter(); default String getStatisticsString() { return "g=" + getNumberOfChoices() + ", bt=" + getNumberOfBacktracks() + ", bj=" + getNumberOfBackjumps() + ", bt_within_bj=" diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java index d809b85f8..155af2b7f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/WatchedNoGood.java @@ -35,7 +35,7 @@ import static at.ac.tuwien.kr.alpha.Util.oops; import static at.ac.tuwien.kr.alpha.common.Literals.literalToString; -public final class WatchedNoGood implements NoGoodInterface, Antecedent { +public final class WatchedNoGood implements NoGoodInterface, Antecedent { private int activity; private final int[] literals; private int alpha; @@ -101,7 +101,7 @@ public boolean hasHead() { } @Override - public int getHead() { + public Integer getHead() { return literals[head]; } @@ -117,7 +117,7 @@ void setWatch(int index, int value) { } @Override - public int getLiteral(int index) { + public Integer getLiteral(int index) { return literals[index]; } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/NoGoodTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/NoGoodTest.java index b8262aa55..1f7eb9588 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/NoGoodTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/NoGoodTest.java @@ -1,3 +1,30 @@ +/* + * Copyright (c) 2016-2020, 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; import org.junit.Test; @@ -8,7 +35,9 @@ import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; import static at.ac.tuwien.kr.alpha.common.Literals.atomToNegatedLiteral; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class NoGoodTest { /** @@ -26,7 +55,7 @@ public static int[] fromOldLiterals(int... literals) { return newLiterals; } - public static int fromOldLiterals(int literal) { + public static Integer fromOldLiterals(int literal) { return literal >= 0 ? atomToLiteral(literal) : atomToNegatedLiteral(-literal); } 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 ab6afb5eb..eb45ac662 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2019, the Alpha Team. +/* + * Copyright (c) 2019-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -27,18 +27,23 @@ */ package at.ac.tuwien.kr.alpha.solver; -import at.ac.tuwien.kr.alpha.common.*; +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.NoGood; +import at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type; import org.junit.Before; import org.junit.Test; -import at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type; - import java.util.HashMap; import java.util.List; import java.util.Map; import static at.ac.tuwien.kr.alpha.common.NoGoodTest.fromOldLiterals; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; public class LearnedNoGoodDeletionTest { @@ -140,7 +145,7 @@ public void testDeletionReducesNumberOfLearntNoGoods() { private Map countNoGoodsByType(NoGoodStore store) { final Map counters = new HashMap<>(); - final NoGoodCounter noGoodCounter = store.getNoGoodCounter(); + final NoGoodCounter noGoodCounter = store.getNoGoodCounter(); for (Type type : Type.values()) { counters.put(type, noGoodCounter.getNumberOfNoGoods(type)); } 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 a5f9df004..92ab83905 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-2019 Siemens AG +/* + * Copyright (c) 2017-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -88,14 +88,14 @@ private void collectAnswerSetsAndCheckStats(Solver solver, int expectedNumberOfA private void collectAnswerSetsAndCheckNoGoodCounterStatsByType(Solver solver, int expectedNumberOfStaticNoGoods, int expectedNumberOfSupportNoGoods, int expectedNumberOfLearntNoGoods, int expectedNumberOfInternalNoGoods) { solver.collectSet(); SolverMaintainingStatistics solverMaintainingStatistics = (SolverMaintainingStatistics) solver; - final NoGoodCounter noGoodCounter = solverMaintainingStatistics.getNoGoodCounter(); + 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(); + final NoGoodCounter noGoodCounter = solverMaintainingStatistics.getNoGoodCounter(); assertEquals("unary: " + expectedNumberOfUnaryNoGoods + " binary: " + expectedNumberOfBinaryNoGoods + " larger: " + expectedNumberOfNAryNoGoods, noGoodCounter.getStatsByCardinality()); } From 0cf2dbc2e6ed3b67e4d8bad68fdffda00a5571ea Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 20 Mar 2020 14:49:38 +0100 Subject: [PATCH 03/55] Generate first non-ground nogood (for head of rule) --- .../kr/alpha/grounder/NoGoodGenerator.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) 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..bf85a3558 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 @@ -29,6 +29,7 @@ import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; 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.FixedInterpretationLiteral; @@ -36,9 +37,16 @@ import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import at.ac.tuwien.kr.alpha.grounder.structure.ProgramAnalysis; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static at.ac.tuwien.kr.alpha.common.Literals.*; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; +import static at.ac.tuwien.kr.alpha.common.Literals.negateLiteral; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; @@ -53,6 +61,8 @@ public class NoGoodGenerator { private final ProgramAnalysis programAnalysis; private final Set uniqueGroundRulePerGroundHead; + private final boolean conflictGeneralisationEnabled = true; // TODO: make parameterisable + NoGoodGenerator(AtomStore atomStore, ChoiceRecorder recorder, Map> factsFromProgram, ProgramAnalysis programAnalysis, Set uniqueGroundRulePerGroundHead) { this.atomStore = atomStore; this.choiceRecorder = recorder; @@ -106,7 +116,13 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround choiceRecorder.addHeadToBody(headId, atomOf(bodyRepresentingLiteral)); // Create a nogood for the head. - result.add(NoGood.headFirst(negateLiteral(headLiteral), bodyRepresentingLiteral)); + final NoGood ngHead = NoGood.headFirst(negateLiteral(headLiteral), bodyRepresentingLiteral); + result.add(ngHead); + if (conflictGeneralisationEnabled) { + ngHead.setNonGroundNoGood(NonGroundNoGood.forGroundNoGood(ngHead, + nonGroundRule.getHeadAtom().toLiteral(false), + nonGroundRule.getNonGroundRuleAtom().toLiteral())); + } final NoGood ruleBody = NoGood.fromBody(posLiterals, negLiterals, bodyRepresentingLiteral); result.add(ruleBody); From 16ca3fe023cfc28c7150f32d3c2379f51307e3da Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 23 Mar 2020 11:16:10 +0100 Subject: [PATCH 04/55] Generate non-ground nogoods for static rule nogoods --- .../kr/alpha/common/NonGroundNoGood.java | 51 +++++--- .../kr/alpha/grounder/NoGoodGenerator.java | 115 ++++++++++++++---- .../alpha/grounder/NoGoodGeneratorTest.java | 69 ++++++++++- 3 files changed, 187 insertions(+), 48 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 3717d4488..ee012c041 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -26,15 +26,22 @@ package at.ac.tuwien.kr.alpha.common; +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.NoGoodGenerator; import at.ac.tuwien.kr.alpha.solver.Antecedent; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; +import static at.ac.tuwien.kr.alpha.Util.join; 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.isPositive; import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; /** @@ -67,14 +74,32 @@ public NonGroundNoGood(Type type, List literals) { private NonGroundNoGood(Type type, List literals, boolean head) { this.type = type; this.head = head; - if (head && literals.get(HEAD).isNegated()) { + if (head && !literals.get(HEAD).isNegated()) { throw oops("Head is not negative"); } this.literals = literals; } - public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Literal... literals) { - return new NonGroundNoGood(groundNoGood.getType(), Arrays.asList(literals)); + public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Map atomMapping) { + final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); + return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); + } + + public static NonGroundNoGood fromBody(NoGood groundNoGood, NoGoodGenerator.CollectedLiterals posLiterals, NoGoodGenerator.CollectedLiterals negLiterals, Literal nonGroundBodyRepresentingLiteral, Map atomMapping) { + final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); + literals.addAll(posLiterals.getSkippedFacts()); + literals.addAll(posLiterals.getSkippedFixedInterpretationLiterals()); + literals.addAll(negLiterals.getSkippedFacts().stream().map(Literal::negate).collect(Collectors.toList())); + literals.addAll(negLiterals.getSkippedFixedInterpretationLiterals().stream().map(Literal::negate).collect(Collectors.toList())); + return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); + } + + private static List literalsForGroundNoGood(NoGood groundNoGood, Map atomMapping) { + final List literals = new ArrayList<>(groundNoGood.size()); + for (int groundLiteral : groundNoGood) { + literals.add(atomMapping.get(atomOf(groundLiteral)).toLiteral(isPositive(groundLiteral))); + } + return literals; } @Override @@ -124,22 +149,10 @@ public int hashCode() { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - - if (head) { - sb.append("*"); - } - - sb.append("{ "); - + final List stringLiterals = new ArrayList<>(literals.size()); for (Literal literal : literals) { - sb.append(literal.isNegated() ? "-" : "+"); - sb.append(literal.getAtom().toString()); - sb.append(" "); + stringLiterals.add((literal.isNegated() ? "-" : "+") + "(" + literal.getAtom() + ")"); } - - sb.append("}"); - - return sb.toString(); + return (head ? "*" : "") + join("{ ", stringLiterals, ", ", " }"); } } 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 bf85a3558..19825ec92 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-2018, the Alpha Team. +/* + * Copyright (c) 2017-2018, 2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -33,11 +33,14 @@ 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.FixedInterpretationLiteral; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; 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.ProgramAnalysis; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -52,7 +55,7 @@ /** * Class to generate ground NoGoods out of non-ground rules and grounding substitutions. - * Copyright (c) 2017-2018, the Alpha Team. + * Copyright (c) 2017-2018, 2020, the Alpha Team. */ public class NoGoodGenerator { private final AtomStore atomStore; @@ -82,8 +85,8 @@ public class NoGoodGenerator { * @return a list of the NoGoods corresponding to the ground rule. */ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGroundRule, final Substitution substitution) { - final List posLiterals = collectPosLiterals(nonGroundRule, substitution); - final List negLiterals = collectNegLiterals(nonGroundRule, substitution); + final CollectedLiterals posLiterals = collectPosLiterals(nonGroundRule, substitution); + final CollectedLiterals negLiterals = collectNegLiterals(nonGroundRule, substitution); if (posLiterals == null || negLiterals == null) { return emptyList(); @@ -91,7 +94,7 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround // A constraint is represented by exactly one nogood. if (nonGroundRule.isConstraint()) { - return singletonList(NoGood.fromConstraint(posLiterals, negLiterals)); + return singletonList(NoGood.fromConstraint(posLiterals.collectedGroundLiterals, negLiterals.collectedGroundLiterals)); } final List result = new ArrayList<>(); @@ -110,43 +113,64 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround return emptyList(); } - final int bodyRepresentingLiteral = atomToLiteral(atomStore.putIfAbsent(bodyAtom)); + final int bodyRepresentingAtom = atomStore.putIfAbsent(bodyAtom); + final int bodyRepresentingLiteral = atomToLiteral(bodyRepresentingAtom); + final RuleAtom nonGroundBodyRepresentingAtom = nonGroundRule.getNonGroundRuleAtom(); + final Literal nonGroundBodyRepresentingLiteral = nonGroundBodyRepresentingAtom.toLiteral(); final int headLiteral = atomToLiteral(atomStore.putIfAbsent(nonGroundRule.getHeadAtom().substitute(substitution))); + final Map mapGroundToNonGroundAtoms; + if (conflictGeneralisationEnabled) { + mapGroundToNonGroundAtoms = new HashMap<>(); + mapGroundToNonGroundAtoms.put(headId, nonGroundRule.getHeadAtom()); + mapGroundToNonGroundAtoms.put(bodyRepresentingAtom, nonGroundBodyRepresentingAtom); + mapGroundToNonGroundAtoms.putAll(posLiterals.getAtomMapping()); + mapGroundToNonGroundAtoms.putAll(negLiterals.getAtomMapping()); + } + choiceRecorder.addHeadToBody(headId, atomOf(bodyRepresentingLiteral)); // Create a nogood for the head. final NoGood ngHead = NoGood.headFirst(negateLiteral(headLiteral), bodyRepresentingLiteral); result.add(ngHead); if (conflictGeneralisationEnabled) { - ngHead.setNonGroundNoGood(NonGroundNoGood.forGroundNoGood(ngHead, - nonGroundRule.getHeadAtom().toLiteral(false), - nonGroundRule.getNonGroundRuleAtom().toLiteral())); + ngHead.setNonGroundNoGood(NonGroundNoGood.forGroundNoGood(ngHead, mapGroundToNonGroundAtoms)); } - final NoGood ruleBody = NoGood.fromBody(posLiterals, negLiterals, bodyRepresentingLiteral); - result.add(ruleBody); + final NoGood ngWholeBody = NoGood.fromBody(posLiterals.collectedGroundLiterals, negLiterals.collectedGroundLiterals, bodyRepresentingLiteral); + if (conflictGeneralisationEnabled) { + ngWholeBody.setNonGroundNoGood(NonGroundNoGood.fromBody(ngWholeBody, posLiterals, negLiterals, nonGroundBodyRepresentingLiteral, mapGroundToNonGroundAtoms)); + } + result.add(ngWholeBody); // Nogoods such that the atom representing the body is true iff the body is true. - for (int j = 1; j < ruleBody.size(); j++) { - result.add(new NoGood(bodyRepresentingLiteral, negateLiteral(ruleBody.getLiteral(j)))); + for (int j = 1; j < ngWholeBody.size(); j++) { + final NoGood ngOneBodyLiteral = new NoGood(bodyRepresentingLiteral, negateLiteral(ngWholeBody.getLiteral(j))); + result.add(ngOneBodyLiteral); + if (conflictGeneralisationEnabled) { + ngOneBodyLiteral.setNonGroundNoGood(NonGroundNoGood.forGroundNoGood(ngOneBodyLiteral, mapGroundToNonGroundAtoms)); + } } // If the rule head is unique, add support. if (uniqueGroundRulePerGroundHead.contains(nonGroundRule)) { - result.add(NoGood.support(headLiteral, bodyRepresentingLiteral)); + final NoGood ngSupport = NoGood.support(headLiteral, bodyRepresentingLiteral); + if (conflictGeneralisationEnabled) { + ngSupport.setNonGroundNoGood(NonGroundNoGood.forGroundNoGood(ngSupport, mapGroundToNonGroundAtoms)); + } + result.add(ngSupport); } // If the body of the rule contains negation, add choices. - if (!negLiterals.isEmpty()) { - result.addAll(choiceRecorder.generateChoiceNoGoods(posLiterals, negLiterals, bodyRepresentingLiteral)); + if (!negLiterals.collectedGroundLiterals.isEmpty()) { + result.addAll(choiceRecorder.generateChoiceNoGoods(posLiterals.collectedGroundLiterals, negLiterals.collectedGroundLiterals, bodyRepresentingLiteral)); } return result; } - List collectNegLiterals(final NonGroundRule nonGroundRule, final Substitution substitution) { - final List bodyLiteralsNegative = new ArrayList<>(); + CollectedLiterals collectNegLiterals(final NonGroundRule nonGroundRule, final Substitution substitution) { + final CollectedLiterals collectedLiterals = new CollectedLiterals(); for (Atom atom : nonGroundRule.getBodyAtomsNegative()) { Atom groundAtom = atom.substitute(substitution); @@ -162,23 +186,27 @@ List collectNegLiterals(final NonGroundRule nonGroundRule, final Substi continue; } - bodyLiteralsNegative.add(atomToLiteral(atomStore.putIfAbsent(groundAtom))); + collectedLiterals.collectedGroundLiterals.add(atomToLiteral(atomStore.putIfAbsent(groundAtom))); + collectedLiterals.correspondingNonGroundLiterals.add(atom.toLiteral()); } - return bodyLiteralsNegative; + return collectedLiterals; } - private List collectPosLiterals(final NonGroundRule nonGroundRule, final Substitution substitution) { - final List bodyLiteralsPositive = new ArrayList<>(); + private CollectedLiterals collectPosLiterals(final NonGroundRule nonGroundRule, final Substitution substitution) { + final CollectedLiterals collectedLiterals = new CollectedLiterals(); for (Atom atom : nonGroundRule.getBodyAtomsPositive()) { - if (atom.toLiteral() instanceof FixedInterpretationLiteral) { + final Literal literal = atom.toLiteral(); + if (literal instanceof FixedInterpretationLiteral) { // TODO: conversion of atom to literal is ugly. NonGroundRule could manage atoms instead of literals, cf. FIXME there // Atom has fixed interpretation, hence was checked earlier that it // evaluates to true under the given substitution. // FixedInterpretationAtoms need not be shown to the solver, skip it. + collectedLiterals.skippedFixedInterpretationLiterals.add(literal); continue; } // Skip the special enumeration atom. if (atom instanceof EnumerationAtom) { + // TODO: ??? continue; } @@ -189,6 +217,7 @@ private List collectPosLiterals(final NonGroundRule nonGroundRule, fina Set factInstances = factsFromProgram.get(groundAtom.getPredicate()); if (factInstances != null && factInstances.contains(new Instance(groundAtom.getTerms()))) { // Skip positive atoms that are always true. + collectedLiterals.skippedFacts.add(literal); continue; } @@ -197,13 +226,47 @@ private List collectPosLiterals(final NonGroundRule nonGroundRule, fina return null; } - bodyLiteralsPositive.add(atomToLiteral(atomStore.putIfAbsent(groundAtom))); + collectedLiterals.collectedGroundLiterals.add(atomToLiteral(atomStore.putIfAbsent(groundAtom))); + collectedLiterals.correspondingNonGroundLiterals.add(literal); } - return bodyLiteralsPositive; + return collectedLiterals; } private boolean existsRuleWithPredicateInHead(final Predicate predicate) { final HashSet definingRules = programAnalysis.getPredicateDefiningRules().get(predicate); return definingRules != null && !definingRules.isEmpty(); } + + public static class CollectedLiterals { + private final List collectedGroundLiterals = new ArrayList<>(); + private final List correspondingNonGroundLiterals = new ArrayList<>(); + private final List skippedFixedInterpretationLiterals = new ArrayList<>(); + private final List skippedFacts = new ArrayList<>(); + + public List getCollectedGroundLiterals() { + return Collections.unmodifiableList(collectedGroundLiterals); + } + + public List getCorrespondingNonGroundLiterals() { + return Collections.unmodifiableList(correspondingNonGroundLiterals); + } + + public List getSkippedFixedInterpretationLiterals() { + return Collections.unmodifiableList(skippedFixedInterpretationLiterals); + } + + public List getSkippedFacts() { + return Collections.unmodifiableList(skippedFacts); + } + + public Map getAtomMapping() { + final Map atomMapping = new HashMap<>(); + for (int i = 0; i < collectedGroundLiterals.size(); i++) { + final int groundAtom = atomOf(collectedGroundLiterals.get(i)); + final Atom nonGroundAtom = correspondingNonGroundLiterals.get(i).getAtom(); + atomMapping.put(groundAtom, nonGroundAtom); + } + return atomMapping; + } + } } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java index 14ee4374d..dca1b0ba1 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2018 Siemens AG +/* + * Copyright (c) 2018, 2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,16 +27,20 @@ import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; +import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.common.Rule; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; 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.RuleAtom; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import org.junit.Test; import java.util.List; import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.isNegated; import static org.junit.Assert.assertEquals; /** @@ -70,10 +74,69 @@ public void collectNeg_ContainsOnlyPositiveLiterals() { Substitution substitution = new Substitution(); substitution.unifyTerms(X, A); substitution.unifyTerms(Y, B); - List collectedNeg = noGoodGenerator.collectNegLiterals(nonGroundRule, substitution); + List collectedNeg = noGoodGenerator.collectNegLiterals(nonGroundRule, substitution).getCollectedGroundLiterals(); assertEquals(1, collectedNeg.size()); String negAtomString = atomStore.atomToString(atomOf(collectedNeg.get(0))); assertEquals("q(a, b)", negAtomString); } + @Test + public void testNonGroundNoGoods_normalRuleWithArithmetics() { + final Program program = PARSER.parse("p(1,1). " + + "{ p(X1,Y) } :- p(X,Y), X1=X+1. " + + "q(X,Y) :- p(X,Y), X1=X+1, not p(X1,Y)."); + final Rule rule = program.getRules().get(1); + final AtomStore atomStore = new AtomStoreImpl(); + final Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true); + final NoGoodGenerator noGoodGenerator = ((NaiveGrounder)grounder).noGoodGenerator; + final NonGroundRule nonGroundRule = NonGroundRule.constructNonGroundRule(rule); + final Substitution substitution = new Substitution(); + + substitution.put(VariableTerm.getInstance("X"), ConstantTerm.getInstance(2)); + substitution.put(VariableTerm.getInstance("X1"), ConstantTerm.getInstance(3)); + substitution.put(VariableTerm.getInstance("Y"), ConstantTerm.getInstance(1)); + final List noGoods = noGoodGenerator.generateNoGoodsFromGroundSubstitution(nonGroundRule, substitution); + + assertEquals(6, noGoods.size()); + int seenExpected = 0; + for (NoGood noGood : noGoods) { + final boolean hasHead = noGood.hasHead(); + final int headAtom = atomOf(noGood.getHead()); + final Integer firstLiteral = noGood.getLiteral(0); + final String groundNoGoodToString = atomStore.noGoodToString(noGood); + final String nonGroundNoGoodToString = noGood.getNonGroundNoGood() == null ? null : noGood.getNonGroundNoGood().toString(); + if (hasHead && atomStore.get(headAtom).getPredicate().getName().equals("q")) { + // head to body + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "*{ -(q(X, Y)), +(_R_(\"3\",\"{}\")) }", nonGroundNoGoodToString); + } else if (hasHead && atomStore.get(headAtom) instanceof RuleAtom) { + // body-representing atom to full body + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "*{ -(_R_(\"3\",\"{}\")), +(p(X, Y)), -(p(X1, Y)), +(X1 = X + 1) }", nonGroundNoGoodToString); + } else if (!hasHead && isNegated(firstLiteral)) { + // positive body atom to body-representing atom + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ -(p(X, Y)), +(_R_(\"3\",\"{}\")) }", nonGroundNoGoodToString); + } else if (!hasHead && !isNegated(firstLiteral)) { + // negative body atom to body-representing atom + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ +(p(X1, Y)), +(_R_(\"3\",\"{}\")) }", nonGroundNoGoodToString); + } else if (hasHead && atomStore.get(atomOf(firstLiteral)).getPredicate().equals(ChoiceAtom.OFF)) { + // ChoiceOff + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, null, nonGroundNoGoodToString); + } else if (hasHead && atomStore.get(atomOf(firstLiteral)).getPredicate().equals(ChoiceAtom.ON)) { + // ChoiceOn + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, null, nonGroundNoGoodToString); + } else { + continue; + } + seenExpected++; + } + assertEquals(6, seenExpected); + } + + private void expectNonGroundNoGoodForGroundNoGood(String groundNoGoodToString, String expectedNonGroundNoGoodToString, String nonGroundNoGoodToString) { + assertEquals("Unexpected non-ground nogood for ground nogood " + groundNoGoodToString, expectedNonGroundNoGoodToString, nonGroundNoGoodToString); + } + + // TODO: enumeration atom + + // TODO: constraint + } From 2d8689d43ac2779bfbb60ade68df9062add116b0 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 23 Mar 2020 11:47:17 +0100 Subject: [PATCH 05/55] Reset rule IDs before running tests --- .../at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java index dca1b0ba1..fe915b4e9 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java @@ -35,6 +35,7 @@ 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.parser.ProgramParser; +import org.junit.Before; import org.junit.Test; import java.util.List; @@ -54,6 +55,11 @@ public class NoGoodGeneratorTest { private static final VariableTerm X = VariableTerm.getInstance("X"); private static final VariableTerm Y = VariableTerm.getInstance("Y"); + + @Before + public void setUp() { + NonGroundRule.ID_GENERATOR.resetGenerator(); + } /** * Calls {@link NoGoodGenerator#collectNegLiterals(NonGroundRule, Substitution)}, From ff97640e7d3c1493b2bedfe1465aabecad76f619 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 23 Mar 2020 11:47:51 +0100 Subject: [PATCH 06/55] Satisfy Checkstyle --- .../at/ac/tuwien/kr/alpha/common/NoGood.java | 22 +- .../kr/alpha/common/NonGroundNoGood.java | 198 +++++++++--------- .../atoms/BodyRepresentingLiteral.java | 46 ++-- 3 files changed, 135 insertions(+), 131 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java index 5b0bfaca9..225ee0a35 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java @@ -56,11 +56,11 @@ public class NoGood implements NoGoodInterface, Comparable { public NoGood(int... literals) { this(STATIC, literals, false); } - + public NoGood(Type type, int... literals) { this(type, literals, false); } - + private NoGood(Type type, int[] literals, boolean head) { this.type = type; this.head = head; @@ -89,7 +89,7 @@ protected NoGood(NoGood noGood) { this.type = noGood.type; this.nonGroundNoGood = noGood.nonGroundNoGood; } - + public static NoGood learnt(int... literals) { return new NoGood(LEARNT, literals); } @@ -101,7 +101,7 @@ public static NoGood headFirst(int... literals) { public static NoGood headFirstInternal(int... literals) { return headFirst(INTERNAL, literals); } - + public static NoGood headFirst(Type type, int... literals) { return new NoGood(type, literals, true); } @@ -117,11 +117,11 @@ public static NoGood support(int headLiteral, int bodyRepresentingLiteral) { public static NoGood fromConstraint(List posLiterals, List negLiterals) { return new NoGood(addPosNeg(new int[posLiterals.size() + negLiterals.size()], posLiterals, negLiterals, 0)); } - + public static NoGood fromBody(List posLiterals, List negLiterals, int bodyRepresentingLiteral) { return fromBody(STATIC, posLiterals, negLiterals, bodyRepresentingLiteral); } - + public static NoGood fromBodyInternal(List posLiterals, List negLiterals, int bodyRepresentingLiteral) { return fromBody(INTERNAL, posLiterals, negLiterals, bodyRepresentingLiteral); } @@ -167,11 +167,11 @@ public void bumpActivity() { public void decreaseActivity() { } - @Override - public NoGood getOriginalNoGood() { - return originalNoGood; - } - }; + @Override + public NoGood getOriginalNoGood() { + return originalNoGood; + } + }; } public NoGood withoutHead() { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index ee012c041..281491f6c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -46,7 +46,7 @@ /** * Represents a non-ground nogood that corresponds to several ground nogoods generated from it. - * + *

* This class assumes the following to hold without checking it: *

    *
  • no literal appears twice (different variable names are OK, if different literals correspond to different ground literals)
  • @@ -59,100 +59,104 @@ */ public class NonGroundNoGood implements NoGoodInterface { - protected final List literals; - private final boolean head; - private final Type type; - - public NonGroundNoGood(List literals) { - this(STATIC, literals, false); - } - - public NonGroundNoGood(Type type, List literals) { - this(type, literals, false); - } - - private NonGroundNoGood(Type type, List literals, boolean head) { - this.type = type; - this.head = head; - if (head && !literals.get(HEAD).isNegated()) { - throw oops("Head is not negative"); - } - this.literals = literals; - } - - public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Map atomMapping) { - final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); - return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); - } - - public static NonGroundNoGood fromBody(NoGood groundNoGood, NoGoodGenerator.CollectedLiterals posLiterals, NoGoodGenerator.CollectedLiterals negLiterals, Literal nonGroundBodyRepresentingLiteral, Map atomMapping) { - final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); - literals.addAll(posLiterals.getSkippedFacts()); - literals.addAll(posLiterals.getSkippedFixedInterpretationLiterals()); - literals.addAll(negLiterals.getSkippedFacts().stream().map(Literal::negate).collect(Collectors.toList())); - literals.addAll(negLiterals.getSkippedFixedInterpretationLiterals().stream().map(Literal::negate).collect(Collectors.toList())); - return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); - } - - private static List literalsForGroundNoGood(NoGood groundNoGood, Map atomMapping) { - final List literals = new ArrayList<>(groundNoGood.size()); - for (int groundLiteral : groundNoGood) { - literals.add(atomMapping.get(atomOf(groundLiteral)).toLiteral(isPositive(groundLiteral))); - } - return literals; - } - - @Override - public Literal getLiteral(int index) { - return literals.get(index); - } - - @Override - public boolean hasHead() { - return head; - } - - @Override - public int size() { - return literals.size(); - } - - @Override - public Antecedent asAntecedent() { - throw new UnsupportedOperationException("Non-ground nogood cannot be represented as an antecedent"); - } - - @Override - public Type getType() { - return type; - } - - @Override - public Iterator iterator() { - return literals.iterator(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - NonGroundNoGood that = (NonGroundNoGood) o; - return head == that.head && - literals.equals(that.literals) && - type == that.type; - } - - @Override - public int hashCode() { - return Objects.hash(literals, head, type); - } - - @Override - public String toString() { - final List stringLiterals = new ArrayList<>(literals.size()); - for (Literal literal : literals) { - stringLiterals.add((literal.isNegated() ? "-" : "+") + "(" + literal.getAtom() + ")"); - } - return (head ? "*" : "") + join("{ ", stringLiterals, ", ", " }"); - } + protected final List literals; + private final boolean head; + private final Type type; + + public NonGroundNoGood(List literals) { + this(STATIC, literals, false); + } + + public NonGroundNoGood(Type type, List literals) { + this(type, literals, false); + } + + private NonGroundNoGood(Type type, List literals, boolean head) { + this.type = type; + this.head = head; + if (head && !literals.get(HEAD).isNegated()) { + throw oops("Head is not negative"); + } + this.literals = literals; + } + + public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Map atomMapping) { + final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); + return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); + } + + public static NonGroundNoGood fromBody(NoGood groundNoGood, NoGoodGenerator.CollectedLiterals posLiterals, NoGoodGenerator.CollectedLiterals negLiterals, Literal nonGroundBodyRepresentingLiteral, Map atomMapping) { + final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); + literals.addAll(posLiterals.getSkippedFacts()); + literals.addAll(posLiterals.getSkippedFixedInterpretationLiterals()); + literals.addAll(negLiterals.getSkippedFacts().stream().map(Literal::negate).collect(Collectors.toList())); + literals.addAll(negLiterals.getSkippedFixedInterpretationLiterals().stream().map(Literal::negate).collect(Collectors.toList())); + return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); + } + + private static List literalsForGroundNoGood(NoGood groundNoGood, Map atomMapping) { + final List literals = new ArrayList<>(groundNoGood.size()); + for (int groundLiteral : groundNoGood) { + literals.add(atomMapping.get(atomOf(groundLiteral)).toLiteral(isPositive(groundLiteral))); + } + return literals; + } + + @Override + public Literal getLiteral(int index) { + return literals.get(index); + } + + @Override + public boolean hasHead() { + return head; + } + + @Override + public int size() { + return literals.size(); + } + + @Override + public Antecedent asAntecedent() { + throw new UnsupportedOperationException("Non-ground nogood cannot be represented as an antecedent"); + } + + @Override + public Type getType() { + return type; + } + + @Override + public Iterator iterator() { + return literals.iterator(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + NonGroundNoGood that = (NonGroundNoGood) o; + return head == that.head && + literals.equals(that.literals) && + type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(literals, head, type); + } + + @Override + public String toString() { + final List stringLiterals = new ArrayList<>(literals.size()); + for (Literal literal : literals) { + stringLiterals.add((literal.isNegated() ? "-" : "+") + "(" + literal.getAtom() + ")"); + } + return (head ? "*" : "") + join("{ ", stringLiterals, ", ", " }"); + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java index 7e816147c..d6bde6649 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java @@ -38,32 +38,32 @@ */ public class BodyRepresentingLiteral extends Literal { - public BodyRepresentingLiteral(RuleAtom atom, boolean positive) { - super(atom, positive); - } + public BodyRepresentingLiteral(RuleAtom atom, boolean positive) { + super(atom, positive); + } - @Override - public RuleAtom getAtom() { - return (RuleAtom) super.getAtom(); - } + @Override + public RuleAtom getAtom() { + return (RuleAtom) super.getAtom(); + } - @Override - public BodyRepresentingLiteral negate() { - return new BodyRepresentingLiteral(getAtom(), !positive); - } + @Override + public BodyRepresentingLiteral negate() { + return new BodyRepresentingLiteral(getAtom(), !positive); + } - @Override - public BodyRepresentingLiteral substitute(Substitution substitution) { - return this; // a BodyRepresentingLiteral is currently always ground - } + @Override + public BodyRepresentingLiteral substitute(Substitution substitution) { + return this; // a BodyRepresentingLiteral is currently always ground + } - @Override - public Set getBindingVariables() { - return Collections.emptySet(); - } + @Override + public Set getBindingVariables() { + return Collections.emptySet(); + } - @Override - public Set getNonBindingVariables() { - return Collections.emptySet(); - } + @Override + public Set getNonBindingVariables() { + return Collections.emptySet(); + } } From 46b9883a7bc9beba5f9b9b6c25ecc01de93bcfe8 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 23 Mar 2020 12:05:03 +0100 Subject: [PATCH 07/55] Generate non-ground nogoods for static constraint nogoods --- .../kr/alpha/common/NonGroundNoGood.java | 2 +- .../kr/alpha/grounder/NoGoodGenerator.java | 23 +++++++++------ .../alpha/grounder/NoGoodGeneratorTest.java | 28 ++++++++++++++++--- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 281491f6c..adf6c6d8c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -85,7 +85,7 @@ public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Map atomMapping) { + public static NonGroundNoGood fromBody(NoGood groundNoGood, NoGoodGenerator.CollectedLiterals posLiterals, NoGoodGenerator.CollectedLiterals negLiterals, Map atomMapping) { final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); literals.addAll(posLiterals.getSkippedFacts()); literals.addAll(posLiterals.getSkippedFixedInterpretationLiterals()); 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 19825ec92..89b2370cf 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 @@ -92,9 +92,20 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround return emptyList(); } + final Map mapGroundToNonGroundAtoms; + if (conflictGeneralisationEnabled) { + mapGroundToNonGroundAtoms = new HashMap<>(); + mapGroundToNonGroundAtoms.putAll(posLiterals.getAtomMapping()); + mapGroundToNonGroundAtoms.putAll(negLiterals.getAtomMapping()); + } + // A constraint is represented by exactly one nogood. if (nonGroundRule.isConstraint()) { - return singletonList(NoGood.fromConstraint(posLiterals.collectedGroundLiterals, negLiterals.collectedGroundLiterals)); + final NoGood ngConstraint = NoGood.fromConstraint(posLiterals.collectedGroundLiterals, negLiterals.collectedGroundLiterals); + if (conflictGeneralisationEnabled) { + ngConstraint.setNonGroundNoGood(NonGroundNoGood.fromBody(ngConstraint, posLiterals, negLiterals, mapGroundToNonGroundAtoms)); + } + return singletonList(ngConstraint); } final List result = new ArrayList<>(); @@ -115,17 +126,11 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround final int bodyRepresentingAtom = atomStore.putIfAbsent(bodyAtom); final int bodyRepresentingLiteral = atomToLiteral(bodyRepresentingAtom); - final RuleAtom nonGroundBodyRepresentingAtom = nonGroundRule.getNonGroundRuleAtom(); - final Literal nonGroundBodyRepresentingLiteral = nonGroundBodyRepresentingAtom.toLiteral(); final int headLiteral = atomToLiteral(atomStore.putIfAbsent(nonGroundRule.getHeadAtom().substitute(substitution))); - final Map mapGroundToNonGroundAtoms; if (conflictGeneralisationEnabled) { - mapGroundToNonGroundAtoms = new HashMap<>(); mapGroundToNonGroundAtoms.put(headId, nonGroundRule.getHeadAtom()); - mapGroundToNonGroundAtoms.put(bodyRepresentingAtom, nonGroundBodyRepresentingAtom); - mapGroundToNonGroundAtoms.putAll(posLiterals.getAtomMapping()); - mapGroundToNonGroundAtoms.putAll(negLiterals.getAtomMapping()); + mapGroundToNonGroundAtoms.put(bodyRepresentingAtom, nonGroundRule.getNonGroundRuleAtom()); } choiceRecorder.addHeadToBody(headId, atomOf(bodyRepresentingLiteral)); @@ -139,7 +144,7 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround final NoGood ngWholeBody = NoGood.fromBody(posLiterals.collectedGroundLiterals, negLiterals.collectedGroundLiterals, bodyRepresentingLiteral); if (conflictGeneralisationEnabled) { - ngWholeBody.setNonGroundNoGood(NonGroundNoGood.fromBody(ngWholeBody, posLiterals, negLiterals, nonGroundBodyRepresentingLiteral, mapGroundToNonGroundAtoms)); + ngWholeBody.setNonGroundNoGood(NonGroundNoGood.fromBody(ngWholeBody, posLiterals, negLiterals, mapGroundToNonGroundAtoms)); } result.add(ngWholeBody); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java index fe915b4e9..05395a476 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java @@ -137,12 +137,32 @@ public void testNonGroundNoGoods_normalRuleWithArithmetics() { assertEquals(6, seenExpected); } + @Test + public void testNonGroundNoGoods_constraint() { + final Program program = PARSER.parse("p(1,1). " + + "{ p(X1,Y) } :- p(X,Y), X1=X+1. " + + ":- p(X,Y), X1=X+1, not p(X1,Y)."); + final Rule rule = program.getRules().get(1); + final AtomStore atomStore = new AtomStoreImpl(); + final Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true); + final NoGoodGenerator noGoodGenerator = ((NaiveGrounder)grounder).noGoodGenerator; + final NonGroundRule nonGroundRule = NonGroundRule.constructNonGroundRule(rule); + final Substitution substitution = new Substitution(); + + substitution.put(VariableTerm.getInstance("X"), ConstantTerm.getInstance(2)); + substitution.put(VariableTerm.getInstance("X1"), ConstantTerm.getInstance(3)); + substitution.put(VariableTerm.getInstance("Y"), ConstantTerm.getInstance(1)); + final List noGoods = noGoodGenerator.generateNoGoodsFromGroundSubstitution(nonGroundRule, substitution); + + assertEquals(1, noGoods.size()); + final NoGood noGood = noGoods.get(0); + final String groundNoGoodToString = atomStore.noGoodToString(noGood); + final String nonGroundNoGoodToString = noGood.getNonGroundNoGood() == null ? null : noGood.getNonGroundNoGood().toString(); + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ +(p(X, Y)), -(p(X1, Y)), +(X1 = X + 1) }", nonGroundNoGoodToString); + } + private void expectNonGroundNoGoodForGroundNoGood(String groundNoGoodToString, String expectedNonGroundNoGoodToString, String nonGroundNoGoodToString) { assertEquals("Unexpected non-ground nogood for ground nogood " + groundNoGoodToString, expectedNonGroundNoGoodToString, nonGroundNoGoodToString); } - // TODO: enumeration atom - - // TODO: constraint - } From b545c2704bbbe952f2a1dff9c4bfa11028b11234 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 23 Mar 2020 15:24:57 +0100 Subject: [PATCH 08/55] Remember original nogoods in binary watch list --- .../alpha/solver/NoGoodStoreAlphaRoaming.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 08140701d..2cb9e0936 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 @@ -85,6 +85,8 @@ public class NoGoodStoreAlphaRoaming implements NoGoodStore, BinaryNoGoodPropaga private final NoGoodCounter counter = new NoGoodCounter<>(); + private final boolean conflictGeneralisationEnabled = true; // TODO: make parameterisable + public NoGoodStoreAlphaRoaming(WritableAssignment assignment, boolean checksEnabled) { this.assignment = assignment; this.checksEnabled = checksEnabled; @@ -679,6 +681,8 @@ class BinaryWatchList implements ShallowAntecedent { private int noGoodsWithHeadSize; private final int forLiteral; + private final Map originalNoGoods = new HashMap<>(); + private BinaryWatchList(int forLiteral) { this.forLiteral = forLiteral; } @@ -703,6 +707,7 @@ private ConflictCause addHeadedNoGood(NoGood noGood) { throw oops("NoGood has wrong head."); } noGoodsWithHead[noGoodsWithHeadSize++] = otherLiteral; + addOriginalNoGoodIfConflictGeneralisationEnabled(otherLiteral, noGood); // Assign (weakly) otherLiteral if the newly added NoGood is unit. ThriceTruth literalTruth = assignment.getTruth(atomOf(forLiteral)); if (literalTruth != null && literalTruth.toBoolean() == isPositive(forLiteral)) { @@ -726,6 +731,7 @@ private ConflictCause addOrdinaryNoGood(NoGood noGood) { } int otherLiteral = noGood.getLiteral(0) == forLiteral ? noGood.getLiteral(1) : noGood.getLiteral(0); noGoodsWithoutHead[noGoodsWithoutHeadSize++] = otherLiteral; + addOriginalNoGoodIfConflictGeneralisationEnabled(otherLiteral, noGood); // Assign otherLiteral if the newly added NoGood is unit. ThriceTruth literalTruth = assignment.getTruth(atomOf(forLiteral)); if (literalTruth != null && literalTruth.toBoolean() == isPositive(forLiteral)) { @@ -765,6 +771,17 @@ ConflictCause propagateStrongly() { } return null; } + + private void addOriginalNoGoodIfConflictGeneralisationEnabled(int otherLiteral, NoGood noGood) { + if (conflictGeneralisationEnabled) { + final NoGood originalNoGood = originalNoGoods.get(otherLiteral); + if (originalNoGood != null) { + throw oops("Original nogood already stored: " + originalNoGood); + } else { + originalNoGoods.put(otherLiteral, noGood); + } + } + } public int size() { return noGoodsWithHeadSize + noGoodsWithoutHeadSize; @@ -801,6 +818,11 @@ public void bumpActivity() { public void decreaseActivity() { } + @Override + public NoGood getOriginalNoGood() { + return originalNoGoods.get(literals[0]); + } + @Override public String toString() { return "{" + literalToString(literals[0]) + ", " + literalToString(literals[1]) + "}"; From 95e59141e3c89239dfa0310919162ba6995f9b95 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 23 Mar 2020 18:21:16 +0100 Subject: [PATCH 09/55] Learn non-ground nogoods (preliminary implementation) --- .../ac/tuwien/kr/alpha/common/Literals.java | 19 ++++- .../at/ac/tuwien/kr/alpha/common/NoGood.java | 9 +++ .../kr/alpha/common/NonGroundNoGood.java | 6 ++ .../learning/GroundConflictNoGoodLearner.java | 79 +++++++++++++++++-- 4 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Literals.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Literals.java index 8c703a54b..7a260a09b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Literals.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Literals.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -80,4 +80,19 @@ public static int positiveLiteral(int literal) { public static String literalToString(int literal) { return (isPositive(literal) ? "+" : "-") + atomOf(literal); } + + /** + * Returns the index of the first position at which a literal of the given atom occurs in the given array of literals. + * @param atom the atom to look for + * @param literals an array of literals + * @return the first index where the atom occurs, or -1 if it does not occur + */ + public static int findAtomInLiterals(int atom, int[] literals) { + for (int i = 0; i < literals.length; i++) { + if (atomOf(literals[i]) == atom) { + return i; + } + } + return -1; + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java index 225ee0a35..8bd5c9743 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NoGood.java @@ -229,6 +229,15 @@ public IntStream stream() { return Arrays.stream(literals); } + public int indexOf(int literal) { + for (int i = 0; i < literals.length; i++) { + if (literals[i] == literal) { + return i; + } + } + return -1; + } + @Override public int compareTo(NoGood o) { if (o == null) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index adf6c6d8c..9ffb6951b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -32,6 +32,7 @@ import at.ac.tuwien.kr.alpha.solver.Antecedent; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,6 +43,7 @@ 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.isPositive; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.LEARNT; import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; /** @@ -94,6 +96,10 @@ public static NonGroundNoGood fromBody(NoGood groundNoGood, NoGoodGenerator.Coll return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); } + public static NonGroundNoGood learnt(Collection literals) { + return new NonGroundNoGood(LEARNT, new ArrayList<>(literals)); + } + private static List literalsForGroundNoGood(NoGood groundNoGood, Map atomMapping) { final List literals = new ArrayList<>(groundNoGood.size()); for (int groundLiteral : groundNoGood) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 8d59f2cbb..68ec15138 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2018, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -30,17 +30,27 @@ import at.ac.tuwien.kr.alpha.common.Assignment; import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.solver.Antecedent; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import static at.ac.tuwien.kr.alpha.Util.oops; -import static at.ac.tuwien.kr.alpha.common.Literals.*; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; +import static at.ac.tuwien.kr.alpha.common.Literals.findAtomInLiterals; +import static at.ac.tuwien.kr.alpha.common.Literals.isPositive; import static at.ac.tuwien.kr.alpha.solver.NoGoodStore.LBD_NO_VALUE; /** @@ -54,6 +64,8 @@ public class GroundConflictNoGoodLearner { private final Assignment assignment; private final AtomStore atomStore; + private final boolean conflictGeneralisationEnabled = true; // TODO: make parameterisable + public int computeConflictFreeBackjumpingLevel(NoGood violatedNoGood) { int highestDecisionLevel = -1; int secondHighestDecisionLevel = -1; @@ -173,10 +185,14 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { int numLiteralsInConflictLevel = 0; List resolutionLiterals = new ArrayList<>(); List resolutionAtoms = new ArrayList<>(); + List nonGroundResolutionLiterals = new ArrayList<>(); int currentDecisionLevel = assignment.getDecisionLevel(); Set seenAtoms = new HashSet<>(); // NOTE: other solvers use a global array for seen atoms, this might be slightly faster (initial tests with local arrays showed no significant improvement). Set processedAtoms = new HashSet<>(); // Since trail contains 2 entries for MBT->TRUE assigned atoms, explicitly record which seen atoms have ben processed to avoid processing seen atoms twice. int[] currentConflictReason = conflictReason.getReasonLiterals(); + NoGood currentOriginalNoGood = conflictReason.getOriginalNoGood(); + NonGroundNoGood currentOriginalNonGroundNoGood = currentOriginalNoGood.getNonGroundNoGood(); + Literal lastNonGroundLiteral = null; int backjumpLevel = -1; conflictReason.bumpActivity(); TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); @@ -194,11 +210,38 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { // Seen atoms have already been dealt with. if (!seenAtoms.contains(atomOf(literal))) { seenAtoms.add(atomOf(literal)); + + Literal nonGroundLiteral = null; + if (conflictGeneralisationEnabled) { + if (currentOriginalNoGood == null) { + nonGroundResolutionLiterals = null; + LOGGER.warn("Cannot generalise conflict because original nogood unknown for " + reasonsToString(currentConflictReason)); + } else if (nonGroundResolutionLiterals != null) { + if (currentOriginalNonGroundNoGood == null) { + nonGroundResolutionLiterals = null; + LOGGER.warn("Cannot generalise conflict because non-ground nogood unknown for " + atomStore.noGoodToString(currentOriginalNoGood)); + } else { + // index must be looked up because literals may have different order in antecedent than in original nogood: + final int indexOfLiteralInOriginalNogood = currentOriginalNoGood.indexOf(literal); + final Literal nonGroundResolutionLiteral = currentOriginalNonGroundNoGood.getLiteral(indexOfLiteralInOriginalNogood); + nonGroundLiteral = nonGroundResolutionLiteral; + if (!nonGroundResolutionLiteral.getPredicate().equals(atomStore.get(atomOf(literal)).getPredicate())) { + throw oops("Wrong non-ground literal assigned to ground literal"); + // TODO: execute this check only if internal checks enabled (?) + } + } + } + } + int literalDecisionLevel = assignment.getWeakDecisionLevel(atomOf(literal)); if (literalDecisionLevel == currentDecisionLevel) { numLiteralsInConflictLevel++; + lastNonGroundLiteral = nonGroundLiteral; // TODO: does this work in general? I don´t understand yet if the last literal seen here is always the 1UIP. } else { resolutionLiterals.add(literal); + if (nonGroundLiteral != null) { + nonGroundResolutionLiterals.add(nonGroundLiteral); + } if (literalDecisionLevel > backjumpLevel) { backjumpLevel = literalDecisionLevel; } @@ -206,6 +249,9 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { resolutionAtoms.add(atomOf(literal)); } } + if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null && currentOriginalNonGroundNoGood != null) { + nonGroundResolutionLiterals.addAll(getAdditionalLiterals(currentOriginalNonGroundNoGood, currentConflictReason.length)); + } if (LOGGER.isTraceEnabled()) { LOGGER.trace("LiteralsInConflictLevel now: {}", numLiteralsInConflictLevel); LOGGER.trace("Seen atoms are {}.", seenAtoms); @@ -222,18 +268,33 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { Antecedent impliedBy = assignment.getImpliedBy(nextAtom); if (impliedBy != null) { currentConflictReason = impliedBy.getReasonLiterals(); + currentOriginalNoGood = impliedBy.getOriginalNoGood(); + currentOriginalNonGroundNoGood = currentOriginalNoGood == null ? null : currentOriginalNoGood.getNonGroundNoGood(); impliedBy.bumpActivity(); - } + } // TODO: what if impliedBy == null? Will we go through the loop again with the same conflict reason? Is this correct? processedAtoms.add(nextAtom); } while (numLiteralsInConflictLevel-- > 1); // Add the 1UIP literal. resolutionLiterals.add(atomToLiteral(nextAtom, assignment.getTruth(nextAtom).toBoolean())); + if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null) { + if (currentOriginalNonGroundNoGood != null) { + nonGroundResolutionLiterals.add(currentOriginalNonGroundNoGood.getLiteral(findAtomInLiterals(nextAtom, currentConflictReason))); + nonGroundResolutionLiterals.addAll(getAdditionalLiterals(currentOriginalNonGroundNoGood, currentConflictReason.length)); + } else if (lastNonGroundLiteral != null) { + nonGroundResolutionLiterals.add(lastNonGroundLiteral); + } + } int[] learnedLiterals = new int[resolutionLiterals.size()]; int i = 0; for (Integer resolutionLiteral : resolutionLiterals) { learnedLiterals[i++] = resolutionLiteral; } + if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null && !nonGroundResolutionLiterals.isEmpty()) { + final NonGroundNoGood learnedNonGroundNoGood = NonGroundNoGood.learnt(nonGroundResolutionLiterals); + LOGGER.info("Learnt non-ground nogood: " + learnedNonGroundNoGood); + // TODO: do something more with it + } NoGood learnedNoGood = NoGood.learnt(learnedLiterals); if (LOGGER.isTraceEnabled()) { @@ -254,6 +315,14 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { return new ConflictAnalysisResult(learnedNoGood, backjumpingDecisionLevel, resolutionAtoms, computeLBD(learnedLiterals)); } + private List getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { + final List result = new ArrayList<>(nonGroundNoGood.size() - numberOfAlreadyConsideredLiterals); + for (int i = numberOfAlreadyConsideredLiterals; i < nonGroundNoGood.size(); i++) { + result.add(nonGroundNoGood.getLiteral(i)); + } + return result; + } + private int computeLBD(int[] literals) { HashSet occurringDecisionLevels = new HashSet<>(); for (int literal : literals) { From 350e7ab43996c01520ce7b4f42f8d851eb865f19 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 24 Mar 2020 10:06:43 +0100 Subject: [PATCH 10/55] Include variables in non-ground rule atom --- .../kr/alpha/grounder/NoGoodGenerator.java | 2 +- .../kr/alpha/grounder/NonGroundRule.java | 20 +++++++- .../kr/alpha/grounder/atoms/RuleAtom.java | 51 +++++++++++++++---- .../kr/alpha/grounder/ChoiceGrounder.java | 28 +++++++--- .../kr/alpha/grounder/DummyGrounder.java | 26 ++++++++-- .../alpha/grounder/NoGoodGeneratorTest.java | 8 +-- .../kr/alpha/grounder/SubstitutionTest.java | 6 +-- .../kr/alpha/solver/AtomCounterTests.java | 6 +-- 8 files changed, 114 insertions(+), 33 deletions(-) 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 89b2370cf..2fe6b6054 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 @@ -114,7 +114,7 @@ List generateNoGoodsFromGroundSubstitution(final NonGroundRule nonGround final int headId = atomStore.putIfAbsent(groundHeadAtom); // Prepare atom representing the rule body. - final RuleAtom bodyAtom = new RuleAtom(nonGroundRule, substitution); + final RuleAtom bodyAtom = RuleAtom.ground(nonGroundRule, substitution); // Check uniqueness of ground rule by testing whether the // body representing atom already has an id. 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 0caff1a70..810d8bce4 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 @@ -32,18 +32,20 @@ import at.ac.tuwien.kr.alpha.common.Rule; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static at.ac.tuwien.kr.alpha.Util.join; import static at.ac.tuwien.kr.alpha.Util.oops; /** * Represents a non-ground rule or a constraint for the semi-naive grounder. - * Copyright (c) 2017-2018, the Alpha Team. */ public class NonGroundRule { static final IntIdGenerator ID_GENERATOR = new IntIdGenerator(); @@ -71,7 +73,7 @@ private NonGroundRule(Rule rule, int ruleId, List bodyAtomsPositive, List< this.bodyAtomsNegative = Collections.unmodifiableList(bodyAtomsNegative); this.headAtom = headAtom; - this.nonGroundRuleAtom = new RuleAtom(this, new Substitution()); + this.nonGroundRuleAtom = RuleAtom.nonGround(this); checkSafety(); this.groundingOrder = new RuleGroundingOrders(this); @@ -123,6 +125,20 @@ public List getOccurringPredicates() { return predicateList; } + public Set getOccurringVariables() { + final Set occurringVariables = new HashSet<>(); + for (Atom posAtom : bodyAtomsPositive) { + occurringVariables.addAll(posAtom.getOccurringVariables()); + } + for (Atom negAtom : bodyAtomsNegative) { + occurringVariables.addAll(negAtom.getOccurringVariables()); + } + if (!isConstraint()) { + occurringVariables.addAll(headAtom.getOccurringVariables()); + } + return occurringVariables; + } + /** * Checks whether a rule is safe. A rule is safe iff all negated variables and all variables occurring in the * head also occur in the positive body). diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java index d8cff743b..4c885f501 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java @@ -29,13 +29,18 @@ import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.atoms.Atom; -import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; +import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; +import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; import at.ac.tuwien.kr.alpha.grounder.Substitution; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import static at.ac.tuwien.kr.alpha.common.terms.ConstantTerm.getInstance; @@ -46,21 +51,44 @@ public class RuleAtom implements Atom { public static final Predicate PREDICATE = Predicate.getInstance("_R_", 2, true, true); - private final List> terms; + private final List terms; + private final boolean ground; - private RuleAtom(List> terms) { + private RuleAtom(List terms, boolean ground) { if (terms.size() != 2) { throw new IllegalArgumentException(); } this.terms = terms; + this.ground = ground; } - public RuleAtom(NonGroundRule nonGroundRule, Substitution substitution) { - this(Arrays.asList( + /** + * Constructs a {@link RuleAtom} representing a ground rule. + * @param nonGroundRule a rule + * @param substitution a substitution that makes the rule ground + * @return a rule atom representing the ground rule + */ + public static RuleAtom ground(NonGroundRule nonGroundRule, Substitution substitution) { + return new RuleAtom(Arrays.asList( getInstance(Integer.toString(nonGroundRule.getRuleId())), getInstance(substitution.toString()) - )); + ), true); + } + + /** + * Constructs a {@link RuleAtom} representing a non-ground rule (to be used in {@link at.ac.tuwien.kr.alpha.common.NonGroundNoGood}s, for example). + * @param nonGroundRule a rule + * @return a rule atom representing the non-ground rule which contains all the variables occurring in the rule + */ + public static RuleAtom nonGround(NonGroundRule nonGroundRule) { + final Set occurringVariables = nonGroundRule.getOccurringVariables(); + final List sortedVariables = new ArrayList<>(occurringVariables); + Collections.sort(sortedVariables); + return new RuleAtom(Arrays.asList( + getInstance(Integer.toString(nonGroundRule.getRuleId())), + FunctionTerm.getInstance("", sortedVariables) + ), false); } @Override @@ -78,8 +106,7 @@ public List getTerms() { @Override public boolean isGround() { - // NOTE: Both terms are ConstantTerms, which are ground by definition. - return true; + return ground; } @Override @@ -89,7 +116,13 @@ public BodyRepresentingLiteral toLiteral(boolean positive) { @Override public Atom substitute(Substitution substitution) { - return this; + if (ground) { + return this; + } else { + return new RuleAtom(terms.stream() + .map(t -> t.substitute(substitution)) + .collect(Collectors.toList()), false); + } } @Override 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 2270c16b2..d9bd9442b 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -27,7 +27,16 @@ */ package at.ac.tuwien.kr.alpha.grounder; -import at.ac.tuwien.kr.alpha.common.*; +import at.ac.tuwien.kr.alpha.common.AnswerSet; +import at.ac.tuwien.kr.alpha.common.AnswerSetBuilder; +import at.ac.tuwien.kr.alpha.common.Assignment; +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.BasicAnswerSet; +import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; +import at.ac.tuwien.kr.alpha.common.IntIterator; +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Rule; 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.ChoiceAtom; @@ -35,7 +44,14 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.stream.Stream; import static at.ac.tuwien.kr.alpha.Util.entriesToMap; @@ -96,8 +112,8 @@ public class ChoiceGrounder implements Grounder { private static Atom atomBB = new BasicAtom(Predicate.getInstance("bb", 0)); private static Rule ruleAA = new Rule(new DisjunctiveHead(Collections.singletonList(atomAA)), Collections.singletonList(new BasicAtom(Predicate.getInstance("bb", 0)).toLiteral(false))); private static Rule ruleBB = new Rule(new DisjunctiveHead(Collections.singletonList(atomBB)), Collections.singletonList(new BasicAtom(Predicate.getInstance("aa", 0)).toLiteral(false))); - private static Atom rule1 = new RuleAtom(NonGroundRule.constructNonGroundRule(ruleAA), new Substitution()); - private static Atom rule2 = new RuleAtom(NonGroundRule.constructNonGroundRule(ruleBB), new Substitution()); + private static Atom rule1 = RuleAtom.ground(NonGroundRule.constructNonGroundRule(ruleAA), new Substitution()); + private static Atom rule2 = RuleAtom.ground(NonGroundRule.constructNonGroundRule(ruleBB), new Substitution()); private static Atom atomEnBR1 = ChoiceAtom.on(1); private static Atom atomEnBR2 = ChoiceAtom.on(2); private static Atom atomDisBR1 = ChoiceAtom.off(3); 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 e9fad2622..d3de2e5a9 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -27,14 +27,30 @@ */ package at.ac.tuwien.kr.alpha.grounder; -import at.ac.tuwien.kr.alpha.common.*; +import at.ac.tuwien.kr.alpha.common.AnswerSet; +import at.ac.tuwien.kr.alpha.common.AnswerSetBuilder; +import at.ac.tuwien.kr.alpha.common.Assignment; +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.BasicAnswerSet; +import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; +import at.ac.tuwien.kr.alpha.common.IntIterator; +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Rule; 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 org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.stream.Stream; import static at.ac.tuwien.kr.alpha.Util.entriesToMap; @@ -73,7 +89,7 @@ public class DummyGrounder implements Grounder { private static Atom atomBB = new BasicAtom(Predicate.getInstance("b", 0)); 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 Atom rule1 = RuleAtom.ground(NonGroundRule.constructNonGroundRule(ruleABC), new Substitution()); private Set returnedNogoods = new HashSet<>(); public DummyGrounder(AtomStore atomStore) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java index 05395a476..cacf844d3 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java @@ -113,16 +113,16 @@ public void testNonGroundNoGoods_normalRuleWithArithmetics() { final String nonGroundNoGoodToString = noGood.getNonGroundNoGood() == null ? null : noGood.getNonGroundNoGood().toString(); if (hasHead && atomStore.get(headAtom).getPredicate().getName().equals("q")) { // head to body - expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "*{ -(q(X, Y)), +(_R_(\"3\",\"{}\")) }", nonGroundNoGoodToString); + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "*{ -(q(X, Y)), +(_R_(\"3\",(X, X1, Y))) }", nonGroundNoGoodToString); } else if (hasHead && atomStore.get(headAtom) instanceof RuleAtom) { // body-representing atom to full body - expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "*{ -(_R_(\"3\",\"{}\")), +(p(X, Y)), -(p(X1, Y)), +(X1 = X + 1) }", nonGroundNoGoodToString); + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "*{ -(_R_(\"3\",(X, X1, Y))), +(p(X, Y)), -(p(X1, Y)), +(X1 = X + 1) }", nonGroundNoGoodToString); } else if (!hasHead && isNegated(firstLiteral)) { // positive body atom to body-representing atom - expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ -(p(X, Y)), +(_R_(\"3\",\"{}\")) }", nonGroundNoGoodToString); + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ -(p(X, Y)), +(_R_(\"3\",(X, X1, Y))) }", nonGroundNoGoodToString); } else if (!hasHead && !isNegated(firstLiteral)) { // negative body atom to body-representing atom - expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ +(p(X1, Y)), +(_R_(\"3\",\"{}\")) }", nonGroundNoGoodToString); + expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, "{ +(p(X1, Y)), +(_R_(\"3\",(X, X1, Y))) }", nonGroundNoGoodToString); } else if (hasHead && atomStore.get(atomOf(firstLiteral)).getPredicate().equals(ChoiceAtom.OFF)) { // ChoiceOff expectNonGroundNoGoodForGroundNoGood(groundNoGoodToString, null, nonGroundNoGoodToString); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java index 89093c149..c037c30c1 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2018, the Alpha Team. +/* + * Copyright (c) 2016-2018, 2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -141,7 +141,7 @@ public void substitutionFromString() { Substitution substitution = new Substitution(); substitution.unifyTerms(X, A); substitution.unifyTerms(Y, B); - RuleAtom ruleAtom = new RuleAtom(nonGroundRule, substitution); + RuleAtom ruleAtom = RuleAtom.ground(nonGroundRule, substitution); String substitutionString = (String) ((ConstantTerm) ruleAtom.getTerms().get(1)).getObject(); Substitution fromString = Substitution.fromString(substitutionString); assertTrue(substitution.equals(fromString)); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java index d3729a8ad..51aabfb18 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2019 Siemens AG +/* + * Copyright (c) 2019-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -120,7 +120,7 @@ private void createChoiceAtom() { private void createRuleAtom() { Atom atomAA = new BasicAtom(Predicate.getInstance("aa", 0)); Rule ruleAA = new Rule(new DisjunctiveHead(Collections.singletonList(atomAA)), Collections.singletonList(new BasicAtom(Predicate.getInstance("bb", 0)).toLiteral(false))); - atomStore.putIfAbsent(new RuleAtom(NonGroundRule.constructNonGroundRule(ruleAA), new Substitution())); + atomStore.putIfAbsent(RuleAtom.ground(NonGroundRule.constructNonGroundRule(ruleAA), new Substitution())); } private void expectGetNumberOfAtoms(AtomCounter atomCounter, Class classOfAtoms, int expectedNumber) { From 674aa8f88a0fe0bcdccb5e879b24f320a8a87197 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 24 Mar 2020 10:30:04 +0100 Subject: [PATCH 11/55] Fix: do not try to find atom in conflict reason if atom not implied by conflict reason --- .../kr/alpha/solver/learning/GroundConflictNoGoodLearner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 68ec15138..b8658efd6 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -272,12 +272,13 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { currentOriginalNonGroundNoGood = currentOriginalNoGood == null ? null : currentOriginalNoGood.getNonGroundNoGood(); impliedBy.bumpActivity(); } // TODO: what if impliedBy == null? Will we go through the loop again with the same conflict reason? Is this correct? + // (note: in AlphaTest.problematicRun_3col_1119654162577372 it happens that impliedBy is null, then we exit the loop but then currentConflictReason does not contain a literal of nextAtom processedAtoms.add(nextAtom); } while (numLiteralsInConflictLevel-- > 1); // Add the 1UIP literal. resolutionLiterals.add(atomToLiteral(nextAtom, assignment.getTruth(nextAtom).toBoolean())); if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null) { - if (currentOriginalNonGroundNoGood != null) { + if (assignment.getImpliedBy(nextAtom) != null && currentOriginalNonGroundNoGood != null) { nonGroundResolutionLiterals.add(currentOriginalNonGroundNoGood.getLiteral(findAtomInLiterals(nextAtom, currentConflictReason))); nonGroundResolutionLiterals.addAll(getAdditionalLiterals(currentOriginalNonGroundNoGood, currentConflictReason.length)); } else if (lastNonGroundLiteral != null) { From e628068dae69e485e99644e1e1104ad83e450783 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 09:31:56 +0100 Subject: [PATCH 12/55] Do not add the same original nogood twice to binary watch list --- .../kr/alpha/solver/NoGoodStoreAlphaRoaming.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 2cb9e0936..164115479 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 @@ -775,8 +775,14 @@ ConflictCause propagateStrongly() { private void addOriginalNoGoodIfConflictGeneralisationEnabled(int otherLiteral, NoGood noGood) { if (conflictGeneralisationEnabled) { final NoGood originalNoGood = originalNoGoods.get(otherLiteral); - if (originalNoGood != null) { - throw oops("Original nogood already stored: " + originalNoGood); + if (originalNoGood != null && !originalNoGood.equals(noGood)) { + if (checksEnabled && !originalNoGood.withoutHead().equals(noGood.withoutHead())) { + throw oops("Unexpected nogood for literals " + literalToString(forLiteral) + " and " + literalToString(otherLiteral) + ": " + noGood + " (already stored: " + originalNoGood + ")"); + } + if (noGood.hasHead()) { + // if two nogoods have the same literals, prefer the one with head because it contains more information + originalNoGoods.put(otherLiteral, noGood); + } } else { originalNoGoods.put(otherLiteral, noGood); } From e84056f156c123c48f82064bd3bab6290d01fd42 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 09:32:48 +0100 Subject: [PATCH 13/55] Remove faulty code from conflict generalisation --- .../learning/GroundConflictNoGoodLearner.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index b8658efd6..49ea25819 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -49,7 +49,6 @@ 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; -import static at.ac.tuwien.kr.alpha.common.Literals.findAtomInLiterals; import static at.ac.tuwien.kr.alpha.common.Literals.isPositive; import static at.ac.tuwien.kr.alpha.solver.NoGoodStore.LBD_NO_VALUE; @@ -192,7 +191,6 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { int[] currentConflictReason = conflictReason.getReasonLiterals(); NoGood currentOriginalNoGood = conflictReason.getOriginalNoGood(); NonGroundNoGood currentOriginalNonGroundNoGood = currentOriginalNoGood.getNonGroundNoGood(); - Literal lastNonGroundLiteral = null; int backjumpLevel = -1; conflictReason.bumpActivity(); TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); @@ -236,7 +234,6 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { int literalDecisionLevel = assignment.getWeakDecisionLevel(atomOf(literal)); if (literalDecisionLevel == currentDecisionLevel) { numLiteralsInConflictLevel++; - lastNonGroundLiteral = nonGroundLiteral; // TODO: does this work in general? I don´t understand yet if the last literal seen here is always the 1UIP. } else { resolutionLiterals.add(literal); if (nonGroundLiteral != null) { @@ -271,20 +268,12 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { currentOriginalNoGood = impliedBy.getOriginalNoGood(); currentOriginalNonGroundNoGood = currentOriginalNoGood == null ? null : currentOriginalNoGood.getNonGroundNoGood(); impliedBy.bumpActivity(); - } // TODO: what if impliedBy == null? Will we go through the loop again with the same conflict reason? Is this correct? - // (note: in AlphaTest.problematicRun_3col_1119654162577372 it happens that impliedBy is null, then we exit the loop but then currentConflictReason does not contain a literal of nextAtom + } processedAtoms.add(nextAtom); } while (numLiteralsInConflictLevel-- > 1); // Add the 1UIP literal. resolutionLiterals.add(atomToLiteral(nextAtom, assignment.getTruth(nextAtom).toBoolean())); - if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null) { - if (assignment.getImpliedBy(nextAtom) != null && currentOriginalNonGroundNoGood != null) { - nonGroundResolutionLiterals.add(currentOriginalNonGroundNoGood.getLiteral(findAtomInLiterals(nextAtom, currentConflictReason))); - nonGroundResolutionLiterals.addAll(getAdditionalLiterals(currentOriginalNonGroundNoGood, currentConflictReason.length)); - } else if (lastNonGroundLiteral != null) { - nonGroundResolutionLiterals.add(lastNonGroundLiteral); - } - } + // TODO: find and add non-ground 1UIP literal int[] learnedLiterals = new int[resolutionLiterals.size()]; int i = 0; From 2f200211af7ccc2217056e8027f9fde85ffadb15 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 15:17:14 +0100 Subject: [PATCH 14/55] Skeleton for separate conflict analysis method that generalises conflicts --- .../learning/GroundConflictNoGoodLearner.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 49ea25819..5b1406d68 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -134,6 +134,9 @@ public GroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore) { public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood) { LOGGER.trace("Analyzing violated nogood: {}", violatedNoGood); + if (conflictGeneralisationEnabled) { + return analyzeConflictingNoGoodAndGeneraliseConflict(violatedNoGood); + } return analyzeTrailBased(violatedNoGood); } @@ -305,6 +308,21 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { return new ConflictAnalysisResult(learnedNoGood, backjumpingDecisionLevel, resolutionAtoms, computeLBD(learnedLiterals)); } + /** + * Analyzes a conflict and learns both a ground nogood (if possible) and one or more non-ground nogoods (if possible). + * + * This method also contains an implementation of first UIP learning that is redundant to the one in {@link #analyzeTrailBased(Antecedent)} on purpose: + * While the other implementation is designed for efficiency, this one is designed to be easily understood such that + * the connection to conflict generalisation (non-ground conflict learning) becomes apparent. + * This implementation also uses the other one internally to check the correctness of learned ground nogoods. + * + * @param violatedNoGood the violated nogood to start analysis from + * @return an analysis result, possibly including a learned ground nogood and one or more learned non-ground nogoods + */ + private ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent violatedNoGood) { + return null; // TODO: implement + } + private List getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { final List result = new ArrayList<>(nonGroundNoGood.size() - numberOfAlreadyConsideredLiterals); for (int i = numberOfAlreadyConsideredLiterals; i < nonGroundNoGood.size(); i++) { From d7c1b256261ce954883e48765d0749d803a552b9 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 15:21:35 +0100 Subject: [PATCH 15/55] Remove conflict generalisation code from #analyzeTrailBased --- .../learning/GroundConflictNoGoodLearner.java | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 5b1406d68..4b95bf998 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -187,13 +187,10 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { int numLiteralsInConflictLevel = 0; List resolutionLiterals = new ArrayList<>(); List resolutionAtoms = new ArrayList<>(); - List nonGroundResolutionLiterals = new ArrayList<>(); int currentDecisionLevel = assignment.getDecisionLevel(); Set seenAtoms = new HashSet<>(); // NOTE: other solvers use a global array for seen atoms, this might be slightly faster (initial tests with local arrays showed no significant improvement). Set processedAtoms = new HashSet<>(); // Since trail contains 2 entries for MBT->TRUE assigned atoms, explicitly record which seen atoms have ben processed to avoid processing seen atoms twice. int[] currentConflictReason = conflictReason.getReasonLiterals(); - NoGood currentOriginalNoGood = conflictReason.getOriginalNoGood(); - NonGroundNoGood currentOriginalNonGroundNoGood = currentOriginalNoGood.getNonGroundNoGood(); int backjumpLevel = -1; conflictReason.bumpActivity(); TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); @@ -211,37 +208,11 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { // Seen atoms have already been dealt with. if (!seenAtoms.contains(atomOf(literal))) { seenAtoms.add(atomOf(literal)); - - Literal nonGroundLiteral = null; - if (conflictGeneralisationEnabled) { - if (currentOriginalNoGood == null) { - nonGroundResolutionLiterals = null; - LOGGER.warn("Cannot generalise conflict because original nogood unknown for " + reasonsToString(currentConflictReason)); - } else if (nonGroundResolutionLiterals != null) { - if (currentOriginalNonGroundNoGood == null) { - nonGroundResolutionLiterals = null; - LOGGER.warn("Cannot generalise conflict because non-ground nogood unknown for " + atomStore.noGoodToString(currentOriginalNoGood)); - } else { - // index must be looked up because literals may have different order in antecedent than in original nogood: - final int indexOfLiteralInOriginalNogood = currentOriginalNoGood.indexOf(literal); - final Literal nonGroundResolutionLiteral = currentOriginalNonGroundNoGood.getLiteral(indexOfLiteralInOriginalNogood); - nonGroundLiteral = nonGroundResolutionLiteral; - if (!nonGroundResolutionLiteral.getPredicate().equals(atomStore.get(atomOf(literal)).getPredicate())) { - throw oops("Wrong non-ground literal assigned to ground literal"); - // TODO: execute this check only if internal checks enabled (?) - } - } - } - } - int literalDecisionLevel = assignment.getWeakDecisionLevel(atomOf(literal)); if (literalDecisionLevel == currentDecisionLevel) { numLiteralsInConflictLevel++; } else { resolutionLiterals.add(literal); - if (nonGroundLiteral != null) { - nonGroundResolutionLiterals.add(nonGroundLiteral); - } if (literalDecisionLevel > backjumpLevel) { backjumpLevel = literalDecisionLevel; } @@ -249,9 +220,6 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { resolutionAtoms.add(atomOf(literal)); } } - if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null && currentOriginalNonGroundNoGood != null) { - nonGroundResolutionLiterals.addAll(getAdditionalLiterals(currentOriginalNonGroundNoGood, currentConflictReason.length)); - } if (LOGGER.isTraceEnabled()) { LOGGER.trace("LiteralsInConflictLevel now: {}", numLiteralsInConflictLevel); LOGGER.trace("Seen atoms are {}.", seenAtoms); @@ -268,26 +236,18 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { Antecedent impliedBy = assignment.getImpliedBy(nextAtom); if (impliedBy != null) { currentConflictReason = impliedBy.getReasonLiterals(); - currentOriginalNoGood = impliedBy.getOriginalNoGood(); - currentOriginalNonGroundNoGood = currentOriginalNoGood == null ? null : currentOriginalNoGood.getNonGroundNoGood(); impliedBy.bumpActivity(); } processedAtoms.add(nextAtom); } while (numLiteralsInConflictLevel-- > 1); // Add the 1UIP literal. resolutionLiterals.add(atomToLiteral(nextAtom, assignment.getTruth(nextAtom).toBoolean())); - // TODO: find and add non-ground 1UIP literal int[] learnedLiterals = new int[resolutionLiterals.size()]; int i = 0; for (Integer resolutionLiteral : resolutionLiterals) { learnedLiterals[i++] = resolutionLiteral; } - if (conflictGeneralisationEnabled && nonGroundResolutionLiterals != null && !nonGroundResolutionLiterals.isEmpty()) { - final NonGroundNoGood learnedNonGroundNoGood = NonGroundNoGood.learnt(nonGroundResolutionLiterals); - LOGGER.info("Learnt non-ground nogood: " + learnedNonGroundNoGood); - // TODO: do something more with it - } NoGood learnedNoGood = NoGood.learnt(learnedLiterals); if (LOGGER.isTraceEnabled()) { From ecd6bd4f59e2741f32d2987be4adeb836cb3dbed Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 17:59:10 +0100 Subject: [PATCH 16/55] Extract ConflictAnalysisResult to separate file --- .../tuwien/kr/alpha/solver/DefaultSolver.java | 7 +- .../kr/alpha/solver/heuristics/BerkMin.java | 13 +++- .../solver/heuristics/BranchingHeuristic.java | 8 +- .../ChainedBranchingHeuristics.java | 12 ++- .../heuristics/DependencyDrivenHeuristic.java | 21 ++++-- .../solver/heuristics/NaiveHeuristic.java | 6 +- .../solver/heuristics/ReplayHeuristic.java | 6 +- .../kr/alpha/solver/heuristics/VSIDS.java | 10 ++- .../learning/ConflictAnalysisResult.java | 74 +++++++++++++++++++ .../learning/GroundConflictNoGoodLearner.java | 39 ---------- .../alpha/solver/heuristics/BerkMinTest.java | 8 +- .../kr/alpha/solver/heuristics/VSIDSTest.java | 12 ++- .../GroundConflictNoGoodLearnerTest.java | 18 ++++- 13 files changed, 152 insertions(+), 82 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.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 84c19b2aa..a3498fec8 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 @@ -48,6 +48,7 @@ import at.ac.tuwien.kr.alpha.solver.heuristics.ChainedBranchingHeuristics; import at.ac.tuwien.kr.alpha.solver.heuristics.HeuristicsConfiguration; import at.ac.tuwien.kr.alpha.solver.heuristics.NaiveHeuristic; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,7 +72,7 @@ import static at.ac.tuwien.kr.alpha.solver.NoGoodStore.LBD_NO_VALUE; import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.MBT; import static at.ac.tuwien.kr.alpha.solver.heuristics.BranchingHeuristic.DEFAULT_CHOICE_LITERAL; -import static at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult.UNSAT; +import static at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult.UNSAT; /** * The new default solver employed in Alpha. @@ -253,7 +254,7 @@ private boolean addAndBackjumpIfNecessary(int noGoodId, NoGood noGood, int lbd) * @return false iff the analysis result shows that the set of NoGoods is unsatisfiable. */ private boolean learnBackjumpAddFromConflict(ConflictCause conflictCause) { - GroundConflictNoGoodLearner.ConflictAnalysisResult analysisResult = learner.analyzeConflictingNoGood(conflictCause.getAntecedent()); + ConflictAnalysisResult analysisResult = learner.analyzeConflictingNoGood(conflictCause.getAntecedent()); LOGGER.debug("Analysis result: {}", analysisResult); @@ -496,7 +497,7 @@ private boolean ingest(Map obtained) { private NoGood fixContradiction(Map.Entry noGoodEntry, ConflictCause conflictCause) { LOGGER.debug("Attempting to fix violation of {} caused by {}", noGoodEntry.getValue(), conflictCause); - GroundConflictNoGoodLearner.ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictFromAddingNoGood(conflictCause.getAntecedent()); + ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictFromAddingNoGood(conflictCause.getAntecedent()); if (conflictAnalysisResult == UNSAT) { return NoGood.UNSAT; } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMin.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMin.java index 48e523932..d01f623e5 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMin.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BerkMin.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2019 Siemens AG +/* + * Copyright (c) 2016-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,11 +30,16 @@ import at.ac.tuwien.kr.alpha.common.NoGood; 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 at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayDeque; +import java.util.Comparator; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; import java.util.stream.Stream; import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristic.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristic.java index 78be2e6de..8fa3f6f43 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristic.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/BranchingHeuristic.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016, 2018-2019 Siemens AG +/* + * Copyright (c) 2016, 2018-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,7 +26,7 @@ package at.ac.tuwien.kr.alpha.solver.heuristics; import at.ac.tuwien.kr.alpha.common.NoGood; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import java.util.Collection; @@ -56,7 +56,7 @@ public interface BranchingHeuristic { * * @param analysisResult */ - void analyzedConflict(GroundConflictNoGoodLearner.ConflictAnalysisResult analysisResult); + void analyzedConflict(ConflictAnalysisResult analysisResult); /** * Stores a newly grounded {@link NoGood} and updates associated activity counters. 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 6d6eb8761..902f1dc85 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,5 +1,5 @@ -/** - * Copyright (c) 2018-2019 Siemens AG +/* + * Copyright (c) 2018-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,9 +26,13 @@ package at.ac.tuwien.kr.alpha.solver.heuristics; import at.ac.tuwien.kr.alpha.common.NoGood; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import static at.ac.tuwien.kr.alpha.Util.oops; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/DependencyDrivenHeuristic.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/DependencyDrivenHeuristic.java index e24f29da0..95739de0d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/DependencyDrivenHeuristic.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/DependencyDrivenHeuristic.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-2019 Siemens AG +/* + * Copyright (c) 2017-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,17 +33,28 @@ import at.ac.tuwien.kr.alpha.solver.heuristics.activity.BodyActivityProvider; import at.ac.tuwien.kr.alpha.solver.heuristics.activity.BodyActivityProviderFactory; import at.ac.tuwien.kr.alpha.solver.heuristics.activity.BodyActivityProviderFactory.BodyActivityType; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; -import static at.ac.tuwien.kr.alpha.common.Literals.*; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; +import static at.ac.tuwien.kr.alpha.common.Literals.isNegated; import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.FALSE; import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.TRUE; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/NaiveHeuristic.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/NaiveHeuristic.java index 506cc345c..f2613c186 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/NaiveHeuristic.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/NaiveHeuristic.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2018, the Alpha Team. +/* + * Copyright (c) 2016-2018, 2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -29,7 +29,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.solver.ChoiceManager; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import java.util.Collection; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristic.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristic.java index cda9f35ac..17842b8a5 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristic.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/ReplayHeuristic.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2019 Siemens AG +/* + * Copyright (c) 2019-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,7 +28,7 @@ import at.ac.tuwien.kr.alpha.common.Literals; import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.solver.ChoiceManager; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import java.util.Iterator; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDS.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDS.java index 16b5f0daa..0f30e1fa6 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDS.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/heuristics/VSIDS.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2018-2019 Siemens AG +/* + * Copyright (c) 2018-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,7 +31,7 @@ import at.ac.tuwien.kr.alpha.solver.ChoiceManager; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import at.ac.tuwien.kr.alpha.solver.heuristics.activity.BodyActivityProvider; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.slf4j.Logger; @@ -42,7 +42,9 @@ import java.util.Collection; import static at.ac.tuwien.kr.alpha.Util.arrayGrowthSize; -import static at.ac.tuwien.kr.alpha.common.Literals.*; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; +import static at.ac.tuwien.kr.alpha.common.Literals.isPositive; /** * This implementation is inspired by the VSIDS implementation in clasp. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java new file mode 100644 index 000000000..94c9d0813 --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016-2020, 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.learning; + +import at.ac.tuwien.kr.alpha.common.NoGood; + +import java.util.Collection; + +import static at.ac.tuwien.kr.alpha.Util.oops; +import static at.ac.tuwien.kr.alpha.solver.NoGoodStore.LBD_NO_VALUE; + +public class ConflictAnalysisResult { + public static final ConflictAnalysisResult UNSAT = new ConflictAnalysisResult(); + + public final NoGood learnedNoGood; + public final int backjumpLevel; + public final Collection resolutionAtoms; + public final int lbd; + + private ConflictAnalysisResult() { + learnedNoGood = null; + backjumpLevel = -1; + resolutionAtoms = null; + lbd = LBD_NO_VALUE; + } + + public ConflictAnalysisResult(NoGood learnedNoGood, int backjumpLevel, Collection resolutionAtoms) { + this(learnedNoGood, backjumpLevel, resolutionAtoms, LBD_NO_VALUE); + } + + public ConflictAnalysisResult(NoGood learnedNoGood, int backjumpLevel, Collection resolutionAtoms, int lbd) { + if (backjumpLevel < 0) { + throw oops("Backjumping level is smaller than 0"); + } + + this.learnedNoGood = learnedNoGood; + this.backjumpLevel = backjumpLevel; + this.resolutionAtoms = resolutionAtoms; + this.lbd = lbd; + } + + @Override + public String toString() { + if (this == UNSAT) { + return "UNSATISFIABLE"; + } + return learnedNoGood + "@" + backjumpLevel; + } +} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 62fdafef1..6a0a7bc2f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -88,45 +88,6 @@ public int computeConflictFreeBackjumpingLevel(NoGood violatedNoGood) { return highestDecisionLevel - 1; } - public static class ConflictAnalysisResult { - public static final ConflictAnalysisResult UNSAT = new ConflictAnalysisResult(); - - public final NoGood learnedNoGood; - public final int backjumpLevel; - public final Collection resolutionAtoms; - public final int lbd; - - private ConflictAnalysisResult() { - learnedNoGood = null; - backjumpLevel = -1; - resolutionAtoms = null; - lbd = LBD_NO_VALUE; - } - - public ConflictAnalysisResult(NoGood learnedNoGood, int backjumpLevel, Collection resolutionAtoms) { - this(learnedNoGood, backjumpLevel, resolutionAtoms, LBD_NO_VALUE); - } - - public ConflictAnalysisResult(NoGood learnedNoGood, int backjumpLevel, Collection resolutionAtoms, int lbd) { - if (backjumpLevel < 0) { - throw oops("Backjumping level is smaller than 0"); - } - - this.learnedNoGood = learnedNoGood; - this.backjumpLevel = backjumpLevel; - this.resolutionAtoms = resolutionAtoms; - this.lbd = lbd; - } - - @Override - public String toString() { - if (this == UNSAT) { - return "UNSATISFIABLE"; - } - return learnedNoGood + "@" + backjumpLevel; - } - } - public GroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore) { this.assignment = assignment; this.atomStore = atomStore; 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..6a32d0638 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2018 Siemens AG +/* + * Copyright (c) 2016-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ import at.ac.tuwien.kr.alpha.solver.NaiveNoGoodStore; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import at.ac.tuwien.kr.alpha.solver.WritableAssignment; -import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import org.junit.Before; import org.junit.Test; @@ -45,8 +45,6 @@ /** * Tests {@link BerkMin}. - * - * Copyright (c) 2016 Siemens AG * */ public class BerkMinTest { 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..8e1e60261 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 @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2018-2019 Siemens AG +/* + * Copyright (c) 2018-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,11 +25,15 @@ */ package at.ac.tuwien.kr.alpha.solver.heuristics; -import at.ac.tuwien.kr.alpha.common.*; +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.NoGood; 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.learning.GroundConflictNoGoodLearner.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; import org.junit.Before; import org.junit.Test; 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..3d1e93242 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 @@ -4,13 +4,23 @@ 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.*; +import at.ac.tuwien.kr.alpha.solver.Antecedent; +import at.ac.tuwien.kr.alpha.solver.ConflictCause; +import at.ac.tuwien.kr.alpha.solver.NoGoodStore; +import at.ac.tuwien.kr.alpha.solver.NoGoodStoreAlphaRoaming; +import at.ac.tuwien.kr.alpha.solver.ThriceTruth; +import at.ac.tuwien.kr.alpha.solver.TrailAssignment; +import at.ac.tuwien.kr.alpha.solver.WritableAssignment; import org.junit.Ignore; import org.junit.Test; import static at.ac.tuwien.kr.alpha.common.NoGoodTest.fromOldLiterals; import static at.ac.tuwien.kr.alpha.solver.AntecedentTest.antecedentsEquals; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** * Copyright (c) 2016-2019, the Alpha Team. @@ -61,7 +71,7 @@ public void smallConflictNonTrivial1UIP() { Antecedent violatedNoGood = conflictCause.getAntecedent(); assertNotNull(violatedNoGood); assertTrue(antecedentsEquals(violatedNoGood, n5.asAntecedent()) || antecedentsEquals(violatedNoGood, n7.asAntecedent())); - GroundConflictNoGoodLearner.ConflictAnalysisResult analysisResult = learner.analyzeConflictingNoGood(conflictCause.getAntecedent()); + ConflictAnalysisResult analysisResult = learner.analyzeConflictingNoGood(conflictCause.getAntecedent()); NoGood learnedNoGood = analysisResult.learnedNoGood; assertEquals(new NoGood(fromOldLiterals(1, -8)), learnedNoGood); int backjumpingDecisionLevel = analysisResult.backjumpLevel; @@ -83,7 +93,7 @@ public void subCurrentDLPropagationWithChoiceCauseOfConflict() { ConflictCause conflictCause = store.add(11, n2); assertNotNull(conflictCause); assertNotNull(conflictCause.getAntecedent()); - GroundConflictNoGoodLearner.ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictingNoGood(conflictCause.getAntecedent()); + ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictingNoGood(conflictCause.getAntecedent()); assertNull(conflictAnalysisResult.learnedNoGood); assertEquals(2, conflictAnalysisResult.backjumpLevel); From d19f95aabaaab851f74fe46f53de23b23c14cb6e Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 18:03:01 +0100 Subject: [PATCH 17/55] Re-implement CDNL without 1UIP (for upcoming conflict generalisation) --- src/main/java/at/ac/tuwien/kr/alpha/Util.java | 25 +++- .../learning/GroundConflictNoGoodLearner.java | 46 ++---- .../NonGroundConflictNoGoodLearner.java | 124 ++++++++++++++++ .../NonGroundConflictNoGoodLearnerTest.java | 140 ++++++++++++++++++ 4 files changed, 302 insertions(+), 33 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java create mode 100644 src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Util.java b/src/main/java/at/ac/tuwien/kr/alpha/Util.java index 358012221..ee55dc12e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Util.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Util.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -33,8 +33,11 @@ import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import java.util.AbstractMap; +import java.util.Collection; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import java.util.SortedSet; import java.util.StringJoiner; import java.util.stream.Collector; @@ -117,4 +120,22 @@ public static int arrayGrowthSize(int oldSize) { // Growth factor is 1.5. return oldSize + (oldSize >> 1); } + + public static Set intArrayToLinkedHashSet(int[] array) { + final Set set = new LinkedHashSet<>(array.length); + for (int element : array) { + set.add(element); + } + return set; + } + + public static int[] collectionToIntArray(Collection collection) { + final int[] array = new int[collection.size()]; + int i = 0; + for (Integer element : collection) { + array[i++] = element; + } + return array; + } + } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 6a0a7bc2f..36f92c95a 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -30,14 +30,13 @@ import at.ac.tuwien.kr.alpha.common.Assignment; import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; -import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; -import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.solver.Antecedent; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -63,8 +62,6 @@ public class GroundConflictNoGoodLearner { private final Assignment assignment; private final AtomStore atomStore; - private final boolean conflictGeneralisationEnabled = true; // TODO: make parameterisable - public int computeConflictFreeBackjumpingLevel(NoGood violatedNoGood) { int highestDecisionLevel = -1; int secondHighestDecisionLevel = -1; @@ -95,9 +92,6 @@ public GroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore) { public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood) { LOGGER.trace("Analyzing violated nogood: {}", violatedNoGood); - if (conflictGeneralisationEnabled) { - return analyzeConflictingNoGoodAndGeneraliseConflict(violatedNoGood); - } return analyzeTrailBased(violatedNoGood); } @@ -140,6 +134,10 @@ private String reasonsToString(int[] reasons) { } private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { + return analyzeTrailBased(conflictReason, true); + } + + ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason, boolean minimizeLearnedNoGood) { LOGGER.trace("Analyzing trail based."); if (assignment.getDecisionLevel() == 0) { LOGGER.trace("Conflict on decision level 0."); @@ -204,7 +202,16 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { // Add the 1UIP literal. resolutionLiterals.add(atomToLiteral(nextAtom, assignment.getTruth(nextAtom).toBoolean())); - int[] learnedLiterals = minimizeLearnedLiterals(resolutionLiterals, seenAtoms); + final int[] learnedLiterals; + if (minimizeLearnedNoGood) { + learnedLiterals = minimizeLearnedLiterals(resolutionLiterals, seenAtoms); + } else { + learnedLiterals = new int[resolutionLiterals.size()]; + int i = 0; + for (Integer resolutionLiteral : resolutionLiterals) { + learnedLiterals[i++] = resolutionLiteral; + } + } NoGood learnedNoGood = NoGood.learnt(learnedLiterals); if (LOGGER.isTraceEnabled()) { @@ -256,29 +263,6 @@ private int[] minimizeLearnedLiterals(List resolutionLiterals, Set getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { - final List result = new ArrayList<>(nonGroundNoGood.size() - numberOfAlreadyConsideredLiterals); - for (int i = numberOfAlreadyConsideredLiterals; i < nonGroundNoGood.size(); i++) { - result.add(nonGroundNoGood.getLiteral(i)); - } - return result; - } - private int computeLBD(int[] literals) { HashSet occurringDecisionLevels = new HashSet<>(); for (int literal : literals) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java new file mode 100644 index 000000000..88f55d3ea --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020 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.learning; + +import at.ac.tuwien.kr.alpha.common.Assignment; +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.solver.Antecedent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static at.ac.tuwien.kr.alpha.Util.collectionToIntArray; +import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; +import static at.ac.tuwien.kr.alpha.common.Literals.atomOf; +import static at.ac.tuwien.kr.alpha.common.Literals.negateLiteral; + +/** + * Conflict-driven learning on ground clauses that also learns non-ground nogoods. + */ +public class NonGroundConflictNoGoodLearner { + private static final Logger LOGGER = LoggerFactory.getLogger(NonGroundConflictNoGoodLearner.class); + + private final Assignment assignment; + private final AtomStore atomStore; + + public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore) { + this.assignment = assignment; + this.atomStore = atomStore; + } + + /** + * Analyzes a conflict and learns both a ground nogood (if possible) and one or more non-ground nogoods (if possible). + * + * This method also contains an implementation of first UIP learning that is redundant to the one in + * {@link GroundConflictNoGoodLearner#analyzeTrailBased(Antecedent, boolean)} on purpose: + * While the other implementation is designed for efficiency, this one is designed to be easily understood such that + * the connection to conflict generalisation (non-ground conflict learning) becomes apparent. + * This implementation also uses the other one internally to check the correctness of learned ground nogoods. + * + * @param violatedNoGood the violated nogood to start analysis from + * @return an analysis result, possibly including a learned ground nogood and one or more learned non-ground nogoods + */ + ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent violatedNoGood) { + Set currentNoGood; + Set resolvent = intArrayToLinkedHashSet(violatedNoGood.getReasonLiterals()); + do { + currentNoGood = resolvent; + resolvent = makeOneResolutionStep(currentNoGood); + } while (resolvent != null); + return new ConflictAnalysisResult(NoGood.learnt(collectionToIntArray(currentNoGood)), 0, Collections.emptyList()); // TODO + } + + /** + * Resolves the current nogood with the antecedent of one of its literals assigned on the current decision level. + * @param currentNoGood the nogood to resolve with the antecedent of one of its literals + * @return the resolvent (as a set of literals), or {@code null} if no further resolution step is possible + */ + private Set makeOneResolutionStep(Set currentNoGood) { + final int currentDecisionLevel = assignment.getDecisionLevel(); + for (int literal : currentNoGood) { + final int atom = atomOf(literal); + if (assignment.getWeakDecisionLevel(atom) == currentDecisionLevel && assignment.getImpliedBy(atom) != null) { + return resolve(currentNoGood, literal, intArrayToLinkedHashSet(assignment.getImpliedBy(atom).getReasonLiterals())); + } + } + return null; + } + + private Set resolve(Set noGood1, int literalInNoGood1, Set noGood2) { + final int literalInNoGood2 = negateLiteral(literalInNoGood1); + final Set resolvent = new LinkedHashSet<>(); + for (int literal : noGood1) { + if (literal != literalInNoGood1) { + resolvent.add(literal); + } + } + for (int literal : noGood2) { + if (literal != literalInNoGood2) { + resolvent.add(literal); + } + } + return resolvent; + } + + private List getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { + final List result = new ArrayList<>(nonGroundNoGood.size() - numberOfAlreadyConsideredLiterals); + for (int i = numberOfAlreadyConsideredLiterals; i < nonGroundNoGood.size(); i++) { + result.add(nonGroundNoGood.getLiteral(i)); + } + return result; + } + +} diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java new file mode 100644 index 000000000..0b3e1f452 --- /dev/null +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2020 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.learning; + +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; +import at.ac.tuwien.kr.alpha.solver.Antecedent; +import at.ac.tuwien.kr.alpha.solver.ConflictCause; +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 org.junit.Before; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; +import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; +import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.FALSE; +import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.TRUE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Tests {@link NonGroundConflictNoGoodLearner}. + */ +public class NonGroundConflictNoGoodLearnerTest { + + private WritableAssignment assignment; + private NoGoodStore store; + private AtomStore atomStore; + + @Before + public void setUp() { + atomStore = new AtomStoreImpl(); + this.assignment = new TrailAssignment(atomStore); + this.store = new NoGoodStoreAlphaRoaming(assignment); + } + + /** + * This is example 4.2.4 from: + * Joao Marques-Silva, Ines Lynce and Sharad Malik: Conflict-Driven Clause Learning SAT Solvers + * in: Armin Biere, Marijn Heule, Hans van Maaren and Toby Walsh (Eds.): Handbook of Satisfiability + */ + @Test + public void exampleFromSatisfiabilityHandbook() { + final NonGroundConflictNoGoodLearner learner = new NonGroundConflictNoGoodLearner(assignment, atomStore); + int x1 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x1", 0))); + int x2 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x2", 0))); + int x3 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x3", 0))); + int x4 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x4", 0))); + int x5 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x5", 0))); + int x6 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x6", 0))); + int x21 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x21", 0))); + int x31 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x31", 0))); + int unrelated1 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("unrelated1", 0))); + int unrelated4 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("unrelated4", 0))); + final NoGood ng1 = new NoGood(atomToLiteral(x1, false), atomToLiteral(x31, false), atomToLiteral(x2, true)); + final NoGood ng2 = new NoGood(atomToLiteral(x1, false), atomToLiteral(x3, true)); + final NoGood ng3 = NoGood.headFirst(atomToLiteral(x4, false), atomToLiteral(x2, false), atomToLiteral(x3, false)); + final NoGood ng4 = new NoGood(atomToLiteral(x4, true), atomToLiteral(x5, true)); + final NoGood ng5 = new NoGood(atomToLiteral(x21, false), atomToLiteral(x4, true), atomToLiteral(x6, true)); + final NoGood ng6 = new NoGood(atomToLiteral(x5, false), atomToLiteral(x6, false)); + + store.growForMaxAtomId(atomStore.getMaxAtomId()); + this.assignment.growForMaxAtomId(); + + store.add(1, ng1); + store.add(2, ng2); + store.add(3, ng3); + store.add(4, ng4); + store.add(5, ng5); + store.add(6, ng6); + + assertEquals(0, assignment.getDecisionLevel()); + assignment.choose(unrelated1, TRUE); + ConflictCause conflictCause = store.propagate(); + assertNull(conflictCause); + assertEquals(1, assignment.getDecisionLevel()); + assignment.choose(x21, FALSE); + conflictCause = store.propagate(); + assertNull(conflictCause); + assertEquals(2, assignment.getDecisionLevel()); + assignment.choose(x31, FALSE); + conflictCause = store.propagate(); + assertNull(conflictCause); + assertEquals(3, assignment.getDecisionLevel()); + assignment.choose(unrelated4, TRUE); + conflictCause = store.propagate(); + assertNull(conflictCause); + assertEquals(4, assignment.getDecisionLevel()); + assignment.choose(x1, FALSE); + assertEquals(5, assignment.getDecisionLevel()); + conflictCause = store.propagate(); + assertEquals(FALSE, assignment.get(x2).getTruth()); + assertEquals(FALSE, assignment.get(x3).getTruth()); + assertEquals(TRUE, assignment.get(x4).getTruth()); + assertEquals(FALSE, assignment.get(x5).getTruth()); + assertEquals(FALSE, assignment.get(x6).getTruth()); + assertNotNull(conflictCause); + final Antecedent antecedent = conflictCause.getAntecedent(); + assertEquals(ng6, antecedent.getOriginalNoGood()); + + final ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictingNoGoodAndGeneraliseConflict(antecedent); + final Set expectedLearnedNoGood = new HashSet<>(); + expectedLearnedNoGood.add(atomToLiteral(x1, false)); + expectedLearnedNoGood.add(atomToLiteral(x21, false)); + expectedLearnedNoGood.add(atomToLiteral(x31, false)); + assertEquals(expectedLearnedNoGood, intArrayToLinkedHashSet(conflictAnalysisResult.learnedNoGood.asAntecedent().getReasonLiterals())); + } +} \ No newline at end of file From 5c628ea153cca605d16237e38e0e338a033722a1 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 18:35:03 +0100 Subject: [PATCH 18/55] Learn nogoods on all UIPs --- .../learning/ConflictAnalysisResult.java | 24 +++++++++++ .../NonGroundConflictNoGoodLearner.java | 40 ++++++++++++++++++- .../NonGroundConflictNoGoodLearnerTest.java | 14 ++++++- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java index 94c9d0813..2ca45e60d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java @@ -30,6 +30,8 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import java.util.Collection; +import java.util.Collections; +import java.util.List; import static at.ac.tuwien.kr.alpha.Util.oops; import static at.ac.tuwien.kr.alpha.solver.NoGoodStore.LBD_NO_VALUE; @@ -42,6 +44,8 @@ public class ConflictAnalysisResult { public final Collection resolutionAtoms; public final int lbd; + private List additionalLearnedNoGoods; + private ConflictAnalysisResult() { learnedNoGood = null; backjumpLevel = -1; @@ -64,6 +68,26 @@ public ConflictAnalysisResult(NoGood learnedNoGood, int backjumpLevel, Collectio this.lbd = lbd; } + /** + * Adds nogoods that have been learned additionally to the primary learned nogood on non-first UIPs. + * @param additionalLearnedNoGoods the nogoods learnt on non-first UIPs + */ + void addLearnedNoGoods(List additionalLearnedNoGoods) { + if (this.additionalLearnedNoGoods == null) { + this.additionalLearnedNoGoods = additionalLearnedNoGoods; + } else { + this.additionalLearnedNoGoods.addAll(additionalLearnedNoGoods); + } + } + + /** + * Gets a list of nogoods that have been learned additionally to the primary learned nogood on non-first UIPs. + * @return a list of additional learned nogoods + */ + List getAdditionalLearnedNoGoods() { + return additionalLearnedNoGoods == null ? Collections.emptyList() : Collections.unmodifiableList(additionalLearnedNoGoods); + } + @Override public String toString() { if (this == UNSAT) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 88f55d3ea..35226a512 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -42,6 +42,7 @@ import static at.ac.tuwien.kr.alpha.Util.collectionToIntArray; import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; +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.negateLiteral; @@ -72,13 +73,28 @@ public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore * @return an analysis result, possibly including a learned ground nogood and one or more learned non-ground nogoods */ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent violatedNoGood) { + NoGood firstLearnedNoGood = null; + List additionalLearnedNoGoods = new ArrayList<>(); Set currentNoGood; Set resolvent = intArrayToLinkedHashSet(violatedNoGood.getReasonLiterals()); do { currentNoGood = resolvent; resolvent = makeOneResolutionStep(currentNoGood); + if (resolvent != null && containsUIP(resolvent)) { + final NoGood learntNoGood = createLearntNoGood(resolvent); + if (firstLearnedNoGood == null) { + firstLearnedNoGood = learntNoGood; + } else { + additionalLearnedNoGoods.add(learntNoGood); + } + } } while (resolvent != null); - return new ConflictAnalysisResult(NoGood.learnt(collectionToIntArray(currentNoGood)), 0, Collections.emptyList()); // TODO + // TODO: enhance conflictAnalysisResult with data from ground analysis + final ConflictAnalysisResult conflictAnalysisResult = new ConflictAnalysisResult(firstLearnedNoGood, 0, Collections.emptyList()); + if (!additionalLearnedNoGoods.isEmpty()) { + conflictAnalysisResult.addLearnedNoGoods(additionalLearnedNoGoods); + } + return conflictAnalysisResult; } /** @@ -113,6 +129,28 @@ private Set resolve(Set noGood1, int literalInNoGood1, Set resolvent) { + final int currentDecisionLevel = assignment.getDecisionLevel(); + boolean containsLiteralOnCurrentDecisionLevel = false; + for (Integer literal : resolvent) { + if (assignment.getWeakDecisionLevel(atomOf(literal)) == currentDecisionLevel) { + if (containsLiteralOnCurrentDecisionLevel) { + return false; + } else { + containsLiteralOnCurrentDecisionLevel = true; + } + } + } + if (!containsLiteralOnCurrentDecisionLevel) { + throw oops("Resolvent does not contain any literal from the current decsion level: " + resolvent); + } + return true; + } + + private NoGood createLearntNoGood(Set resolvent) { + return NoGood.learnt(collectionToIntArray(resolvent)); + } + private List getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { final List result = new ArrayList<>(nonGroundNoGood.size() - numberOfAlreadyConsideredLiterals); for (int i = numberOfAlreadyConsideredLiterals; i < nonGroundNoGood.size(); i++) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java index 0b3e1f452..5e45b1c09 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java @@ -40,6 +40,7 @@ import org.junit.Test; import java.util.HashSet; +import java.util.List; import java.util.Set; import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; @@ -131,10 +132,19 @@ public void exampleFromSatisfiabilityHandbook() { assertEquals(ng6, antecedent.getOriginalNoGood()); final ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictingNoGoodAndGeneraliseConflict(antecedent); - final Set expectedLearnedNoGood = new HashSet<>(); + Set expectedLearnedNoGood = new HashSet<>(); + expectedLearnedNoGood.add(atomToLiteral(x4, true)); + expectedLearnedNoGood.add(atomToLiteral(x21, false)); + assert conflictAnalysisResult.learnedNoGood != null; + assertEquals(expectedLearnedNoGood, intArrayToLinkedHashSet(conflictAnalysisResult.learnedNoGood.asAntecedent().getReasonLiterals())); + + final List additionalLearnedNoGoods = conflictAnalysisResult.getAdditionalLearnedNoGoods(); + assertEquals(1, additionalLearnedNoGoods.size()); + expectedLearnedNoGood = new HashSet<>(); expectedLearnedNoGood.add(atomToLiteral(x1, false)); expectedLearnedNoGood.add(atomToLiteral(x21, false)); expectedLearnedNoGood.add(atomToLiteral(x31, false)); - assertEquals(expectedLearnedNoGood, intArrayToLinkedHashSet(conflictAnalysisResult.learnedNoGood.asAntecedent().getReasonLiterals())); + System.out.println(atomStore.noGoodToString(additionalLearnedNoGoods.get(0))); + assertEquals(expectedLearnedNoGood, intArrayToLinkedHashSet(additionalLearnedNoGoods.get(0).asAntecedent().getReasonLiterals())); } } \ No newline at end of file From bc1957eff3f91b4bd1e21a14dfae4698bd745f16 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 25 Mar 2020 19:01:01 +0100 Subject: [PATCH 19/55] Use underlying ground learner in non-ground learner --- .../tuwien/kr/alpha/solver/DefaultSolver.java | 9 +++- .../learning/ConflictNoGoodLearner.java | 40 +++++++++++++++++ .../learning/GroundConflictNoGoodLearner.java | 5 ++- .../NonGroundConflictNoGoodLearner.java | 43 ++++++++++++++----- .../NonGroundConflictNoGoodLearnerTest.java | 3 +- 5 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictNoGoodLearner.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 a3498fec8..bcab0d7db 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 @@ -49,7 +49,9 @@ import at.ac.tuwien.kr.alpha.solver.heuristics.HeuristicsConfiguration; import at.ac.tuwien.kr.alpha.solver.heuristics.NaiveHeuristic; import at.ac.tuwien.kr.alpha.solver.learning.ConflictAnalysisResult; +import at.ac.tuwien.kr.alpha.solver.learning.ConflictNoGoodLearner; import at.ac.tuwien.kr.alpha.solver.learning.GroundConflictNoGoodLearner; +import at.ac.tuwien.kr.alpha.solver.learning.NonGroundConflictNoGoodLearner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,7 +88,7 @@ public class DefaultSolver extends AbstractSolver implements SolverMaintainingSt private final ChoiceManager choiceManager; private final WritableAssignment assignment; - private final GroundConflictNoGoodLearner learner; + private final ConflictNoGoodLearner learner; private final BranchingHeuristic branchingHeuristic; @@ -98,6 +100,8 @@ public class DefaultSolver extends AbstractSolver implements SolverMaintainingSt private final boolean disableNoGoodDeletion; private final PerformanceLog performanceLog; + + private final boolean conflictGeneralisationEnabled = true; // TODO: make parameterisable public DefaultSolver(AtomStore atomStore, Grounder grounder, NoGoodStore store, WritableAssignment assignment, Random random, SystemConfig config, HeuristicsConfiguration heuristicsConfiguration) { super(atomStore, grounder); @@ -105,7 +109,8 @@ public DefaultSolver(AtomStore atomStore, Grounder grounder, NoGoodStore store, this.assignment = assignment; this.store = store; this.choiceManager = new ChoiceManager(assignment, store); - this.learner = new GroundConflictNoGoodLearner(assignment, atomStore); + final GroundConflictNoGoodLearner groundLearner = new GroundConflictNoGoodLearner(assignment, atomStore); + this.learner = conflictGeneralisationEnabled ? new NonGroundConflictNoGoodLearner(assignment, groundLearner) : groundLearner; this.branchingHeuristic = chainFallbackHeuristic(grounder, assignment, random, heuristicsConfiguration); this.disableJustifications = config.isDisableJustificationSearch(); this.disableNoGoodDeletion = config.isDisableNoGoodDeletion(); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictNoGoodLearner.java new file mode 100644 index 000000000..78327ddaf --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictNoGoodLearner.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2020, 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.learning; + +import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.solver.Antecedent; + +public interface ConflictNoGoodLearner { + + int computeConflictFreeBackjumpingLevel(NoGood noGood); + + ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood); + + ConflictAnalysisResult analyzeConflictFromAddingNoGood(Antecedent antecedent); +} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 36f92c95a..482b35169 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -56,12 +56,13 @@ * * Copyright (c) 2016-2020, the Alpha Team. */ -public class GroundConflictNoGoodLearner { +public class GroundConflictNoGoodLearner implements ConflictNoGoodLearner { private static final Logger LOGGER = LoggerFactory.getLogger(GroundConflictNoGoodLearner.class); private final Assignment assignment; private final AtomStore atomStore; + @Override public int computeConflictFreeBackjumpingLevel(NoGood violatedNoGood) { int highestDecisionLevel = -1; int secondHighestDecisionLevel = -1; @@ -90,11 +91,13 @@ public GroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore) { this.atomStore = atomStore; } + @Override public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood) { LOGGER.trace("Analyzing violated nogood: {}", violatedNoGood); return analyzeTrailBased(violatedNoGood); } + @Override public ConflictAnalysisResult analyzeConflictFromAddingNoGood(Antecedent violatedNoGood) { LOGGER.trace("Analyzing conflict caused by adding the (violated) nogood: {}", violatedNoGood); // Simply compute appropriate backjumping level. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 35226a512..db53e7fd7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -26,7 +26,6 @@ package at.ac.tuwien.kr.alpha.solver.learning; import at.ac.tuwien.kr.alpha.common.Assignment; -import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; import at.ac.tuwien.kr.alpha.common.atoms.Literal; @@ -35,9 +34,9 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import static at.ac.tuwien.kr.alpha.Util.collectionToIntArray; @@ -48,16 +47,37 @@ /** * Conflict-driven learning on ground clauses that also learns non-ground nogoods. + * + * This class builds upon an underlying {@link GroundConflictNoGoodLearner}, to which it acts as a proxy for all operations + * not implemented on the non-ground level. + * The non-ground learning capabilities implemented here are not designed for efficiency (yet), so this class should only + * be used in an "offline" conflict generalisation mode. */ -public class NonGroundConflictNoGoodLearner { +public class NonGroundConflictNoGoodLearner implements ConflictNoGoodLearner { private static final Logger LOGGER = LoggerFactory.getLogger(NonGroundConflictNoGoodLearner.class); private final Assignment assignment; - private final AtomStore atomStore; + private final GroundConflictNoGoodLearner groundLearner; - public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore) { + public NonGroundConflictNoGoodLearner(Assignment assignment, GroundConflictNoGoodLearner groundLearner) { this.assignment = assignment; - this.atomStore = atomStore; + this.groundLearner = groundLearner; + } + + @Override + public int computeConflictFreeBackjumpingLevel(NoGood noGood) { + return groundLearner.computeConflictFreeBackjumpingLevel(noGood); + } + + @Override + public ConflictAnalysisResult analyzeConflictFromAddingNoGood(Antecedent antecedent) { + return groundLearner.analyzeConflictFromAddingNoGood(antecedent); + } + + @Override + public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood) { + LOGGER.trace("Analyzing violated nogood: {}", violatedNoGood); + return analyzeConflictingNoGoodAndGeneraliseConflict(violatedNoGood); } /** @@ -89,12 +109,15 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent } } } while (resolvent != null); - // TODO: enhance conflictAnalysisResult with data from ground analysis - final ConflictAnalysisResult conflictAnalysisResult = new ConflictAnalysisResult(firstLearnedNoGood, 0, Collections.emptyList()); + final ConflictAnalysisResult groundAnalysisResultNotMinimized = groundLearner.analyzeTrailBased(violatedNoGood, false); + if (!Objects.equals(groundAnalysisResultNotMinimized.learnedNoGood, firstLearnedNoGood)) { + throw oops("Learned nogood is not the same as the one computed by ground analysis"); + } + final ConflictAnalysisResult analysisResult = groundLearner.analyzeConflictingNoGood(violatedNoGood); if (!additionalLearnedNoGoods.isEmpty()) { - conflictAnalysisResult.addLearnedNoGoods(additionalLearnedNoGoods); + analysisResult.addLearnedNoGoods(additionalLearnedNoGoods); } - return conflictAnalysisResult; + return analysisResult; } /** diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java index 5e45b1c09..cc6571e9a 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java @@ -74,7 +74,8 @@ public void setUp() { */ @Test public void exampleFromSatisfiabilityHandbook() { - final NonGroundConflictNoGoodLearner learner = new NonGroundConflictNoGoodLearner(assignment, atomStore); + final GroundConflictNoGoodLearner groundLearner = new GroundConflictNoGoodLearner(assignment, atomStore); + final NonGroundConflictNoGoodLearner learner = new NonGroundConflictNoGoodLearner(assignment, groundLearner); int x1 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x1", 0))); int x2 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x2", 0))); int x3 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x3", 0))); From a37eceb7d9f5f566c40cb9b56793ca2572f66859 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 27 Mar 2020 11:54:05 +0100 Subject: [PATCH 20/55] Fix re-implementation of 1UIP learning by using trail walker (before, we went to far in some cases and did too many resolutions when computing the 1UIP) --- .../NonGroundConflictNoGoodLearner.java | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index db53e7fd7..82ac80999 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -30,6 +30,7 @@ import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.solver.Antecedent; +import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,13 +94,14 @@ public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood * @return an analysis result, possibly including a learned ground nogood and one or more learned non-ground nogoods */ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent violatedNoGood) { + final TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); NoGood firstLearnedNoGood = null; List additionalLearnedNoGoods = new ArrayList<>(); Set currentNoGood; Set resolvent = intArrayToLinkedHashSet(violatedNoGood.getReasonLiterals()); do { currentNoGood = resolvent; - resolvent = makeOneResolutionStep(currentNoGood); + resolvent = makeOneResolutionStep(currentNoGood, trailWalker); if (resolvent != null && containsUIP(resolvent)) { final NoGood learntNoGood = createLearntNoGood(resolvent); if (firstLearnedNoGood == null) { @@ -123,17 +125,26 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent /** * Resolves the current nogood with the antecedent of one of its literals assigned on the current decision level. * @param currentNoGood the nogood to resolve with the antecedent of one of its literals + * @param trailWalker a backwards-walker on the trail * @return the resolvent (as a set of literals), or {@code null} if no further resolution step is possible */ - private Set makeOneResolutionStep(Set currentNoGood) { - final int currentDecisionLevel = assignment.getDecisionLevel(); - for (int literal : currentNoGood) { - final int atom = atomOf(literal); - if (assignment.getWeakDecisionLevel(atom) == currentDecisionLevel && assignment.getImpliedBy(atom) != null) { - return resolve(currentNoGood, literal, intArrayToLinkedHashSet(assignment.getImpliedBy(atom).getReasonLiterals())); - } + private Set makeOneResolutionStep(Set currentNoGood, TrailAssignment.TrailBackwardsWalker trailWalker) { + if (!containsLiteralForResolution(currentNoGood)) { + return null; } - return null; + final int literal = findNextLiteralToResolve(currentNoGood, trailWalker); + return resolve(currentNoGood, literal, intArrayToLinkedHashSet(assignment.getImpliedBy(atomOf(literal)).getReasonLiterals())); + } + + private Integer findNextLiteralToResolve(Set noGood, TrailAssignment.TrailBackwardsWalker trailWalker) { + final int currentDecisionLevel = assignment.getDecisionLevel(); + int literal; + int atom; + do { + literal = trailWalker.getNextLowerLiteral(); + atom = atomOf(literal); + } while (assignment.getWeakDecisionLevel(atom) != currentDecisionLevel || assignment.getImpliedBy(atom) == null || !noGood.contains(literal)); + return literal; } private Set resolve(Set noGood1, int literalInNoGood1, Set noGood2) { @@ -152,6 +163,17 @@ private Set resolve(Set noGood1, int literalInNoGood1, Set noGood) { + final int currentDecisionLevel = assignment.getDecisionLevel(); + for (Integer literal : noGood) { + final int atom = atomOf(literal); + if (assignment.getWeakDecisionLevel(atom) == currentDecisionLevel && assignment.getImpliedBy(atom) != null) { + return true; + } + } + return false; + } + private boolean containsUIP(Set resolvent) { final int currentDecisionLevel = assignment.getDecisionLevel(); boolean containsLiteralOnCurrentDecisionLevel = false; @@ -164,10 +186,8 @@ private boolean containsUIP(Set resolvent) { } } } - if (!containsLiteralOnCurrentDecisionLevel) { - throw oops("Resolvent does not contain any literal from the current decsion level: " + resolvent); - } - return true; + // TODO: is it problematic if containsLiteralOnCurrentDecisionLevel == false? + return containsLiteralOnCurrentDecisionLevel; } private NoGood createLearntNoGood(Set resolvent) { From af1160efd69f92159ca8d86507fb486ab520bad3 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 27 Mar 2020 12:18:01 +0100 Subject: [PATCH 21/55] If current DL = 0, learn nothing and return UNSAT --- .../alpha/solver/learning/NonGroundConflictNoGoodLearner.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 82ac80999..26124ab8a 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -94,6 +94,10 @@ public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood * @return an analysis result, possibly including a learned ground nogood and one or more learned non-ground nogoods */ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent violatedNoGood) { + if (assignment.getDecisionLevel() == 0) { + LOGGER.trace("Conflict on decision level 0."); + return ConflictAnalysisResult.UNSAT; + } final TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); NoGood firstLearnedNoGood = null; List additionalLearnedNoGoods = new ArrayList<>(); From 0cb9bcc3d14059fe654698cc2217320dcd185b7d Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 31 Mar 2020 10:19:27 +0200 Subject: [PATCH 22/55] 1UIP learning: consider sophisticated techniques to prove UNSAT --- .../learning/GroundConflictNoGoodLearner.java | 27 ++++++++++++++----- .../NonGroundConflictNoGoodLearner.java | 3 ++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java index 482b35169..f42518f57 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/GroundConflictNoGoodLearner.java @@ -140,7 +140,18 @@ private ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason) { return analyzeTrailBased(conflictReason, true); } - ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason, boolean minimizeLearnedNoGood) { + /** + * Analyzes a conflict based on the trail assignment. + * @param conflictReason the violated nogood + * @param optimizeAnalysisResult if {@code true}, the analysis result will be optimized in two ways: + *
      + *
    1. if the learned nogood is not assigning due to out-of-order assigned literals + * and thus the program can be proven to be UNSAT, UNSAT will be returned
    2. + *
    3. the learned nogood will be minimized by local clause minimization
    4. + *
    + * @return an instance of {@link ConflictAnalysisResult} that may contain a learned nogood or the information that the problem is UNSAT. + */ + ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason, boolean optimizeAnalysisResult) { LOGGER.trace("Analyzing trail based."); if (assignment.getDecisionLevel() == 0) { LOGGER.trace("Conflict on decision level 0."); @@ -206,7 +217,7 @@ ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason, boolean mini resolutionLiterals.add(atomToLiteral(nextAtom, assignment.getTruth(nextAtom).toBoolean())); final int[] learnedLiterals; - if (minimizeLearnedNoGood) { + if (optimizeAnalysisResult) { learnedLiterals = minimizeLearnedLiterals(resolutionLiterals, seenAtoms); } else { learnedLiterals = new int[resolutionLiterals.size()]; @@ -223,10 +234,14 @@ ConflictAnalysisResult analyzeTrailBased(Antecedent conflictReason, boolean mini int backjumpingDecisionLevel = computeBackjumpingDecisionLevel(learnedNoGood); if (backjumpingDecisionLevel < 0) { - // Due to out-of-order assigned literals, the learned nogood may be not assigning. - backjumpingDecisionLevel = computeConflictFreeBackjumpingLevel(learnedNoGood); - if (backjumpingDecisionLevel < 0) { - return ConflictAnalysisResult.UNSAT; + if (optimizeAnalysisResult) { + // Due to out-of-order assigned literals, the learned nogood may be not assigning. + backjumpingDecisionLevel = computeConflictFreeBackjumpingLevel(learnedNoGood); + if (backjumpingDecisionLevel < 0) { + return ConflictAnalysisResult.UNSAT; + } + } else { + backjumpingDecisionLevel = 0; } } if (LOGGER.isTraceEnabled()) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 26124ab8a..2324047b2 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -120,7 +120,8 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent throw oops("Learned nogood is not the same as the one computed by ground analysis"); } final ConflictAnalysisResult analysisResult = groundLearner.analyzeConflictingNoGood(violatedNoGood); - if (!additionalLearnedNoGoods.isEmpty()) { + // if backJumpLevel < 0, then problem is UNSAT (ground analysis result does additional checks for this) + if (!additionalLearnedNoGoods.isEmpty() && analysisResult.backjumpLevel >= 0) { analysisResult.addLearnedNoGoods(additionalLearnedNoGoods); } return analysisResult; From 0cfa69c48fb760bc368d5ced88e93c6ea091ece0 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 1 Apr 2020 14:56:15 +0200 Subject: [PATCH 23/55] Make variable names unique in nogoods before resolution --- .../kr/alpha/common/NonGroundNoGood.java | 13 ++- .../kr/alpha/common/UniqueVariableNames.java | 74 ++++++++++++ .../kr/alpha/grounder/Substitution.java | 2 +- .../atoms/BodyRepresentingLiteral.java | 2 +- .../kr/alpha/grounder/atoms/RuleAtom.java | 2 +- .../alpha/common/UniqueVariableNamesTest.java | 106 ++++++++++++++++++ 6 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java create mode 100644 src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 9ffb6951b..cc00481e2 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -28,15 +28,18 @@ import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.NoGoodGenerator; import at.ac.tuwien.kr.alpha.solver.Antecedent; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import static at.ac.tuwien.kr.alpha.Util.join; @@ -73,7 +76,7 @@ public NonGroundNoGood(Type type, List literals) { this(type, literals, false); } - private NonGroundNoGood(Type type, List literals, boolean head) { + NonGroundNoGood(Type type, List literals, boolean head) { this.type = type; this.head = head; if (head && !literals.get(HEAD).isNegated()) { @@ -123,6 +126,14 @@ public int size() { return literals.size(); } + public Set getOccurringVariables() { + final Set occurringVariables = new HashSet<>(); + for (Literal literal : literals) { + occurringVariables.addAll(literal.getOccurringVariables()); + } + return occurringVariables; + } + @Override public Antecedent asAntecedent() { throw new UnsupportedOperationException("Non-ground nogood cannot be represented as an antecedent"); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java new file mode 100644 index 000000000..29ae39bff --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020 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.common; + +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.Substitution; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public class UniqueVariableNames { + + private final Map variablesToOccurrences = new HashMap<>(); + + public NonGroundNoGood makeVariableNamesUnique(NonGroundNoGood noGood) { + final Substitution substitution = renameVariablesIfAlreadyUsed(noGood.getOccurringVariables()); + if (substitution.isEmpty()) { + return noGood; + } + final List newLiterals = new ArrayList<>(noGood.size()); + for (Literal literal : noGood) { + newLiterals.add(literal.substitute(substitution)); + } + return new NonGroundNoGood(noGood.getType(), newLiterals, noGood.hasHead()); + + } + + private Substitution renameVariablesIfAlreadyUsed(Set variables) { + final TreeMap substitution = new TreeMap<>(); + for (VariableTerm variable : variables) { + if (variablesToOccurrences.containsKey(variable)) { + VariableTerm newVariable; + do { + newVariable = VariableTerm.getInstance(variable.toString() + "_" + (variablesToOccurrences.computeIfPresent(variable, (v,o) -> o+1))); + } while (variablesToOccurrences.containsKey(newVariable)); + substitution.put(variable, newVariable); + } else { + variablesToOccurrences.put(variable, 0); + } + } + return new Substitution(substitution); + } + +} 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 b859cb501..5f3fac98a 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 @@ -53,7 +53,7 @@ public class Substitution { protected TreeMap substitution; - private Substitution(TreeMap substitution) { + public Substitution(TreeMap substitution) { // FIXME if (substitution == null) { throw oops("Substitution is null."); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java index d6bde6649..cb7875cc8 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java @@ -54,7 +54,7 @@ public BodyRepresentingLiteral negate() { @Override public BodyRepresentingLiteral substitute(Substitution substitution) { - return this; // a BodyRepresentingLiteral is currently always ground + return new BodyRepresentingLiteral(getAtom().substitute(substitution), positive); } @Override diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java index 4c885f501..b5347887c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java @@ -115,7 +115,7 @@ public BodyRepresentingLiteral toLiteral(boolean positive) { } @Override - public Atom substitute(Substitution substitution) { + public RuleAtom substitute(Substitution substitution) { if (ground) { return this; } else { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java new file mode 100644 index 000000000..2eea8aded --- /dev/null +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020 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.common; + +import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; +import at.ac.tuwien.kr.alpha.common.atoms.BasicLiteral; +import at.ac.tuwien.kr.alpha.common.atoms.ComparisonAtom; +import at.ac.tuwien.kr.alpha.common.atoms.ComparisonLiteral; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm; +import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; +import at.ac.tuwien.kr.alpha.common.terms.Term; +import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static at.ac.tuwien.kr.alpha.common.ComparisonOperator.EQ; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.LEARNT; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; +import static at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm.ArithmeticOperator.PLUS; +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link UniqueVariableNames} + */ +public class UniqueVariableNamesTest { + + private UniqueVariableNames uniqueVariableNames; + + @Before + public void setUp() { + this.uniqueVariableNames = new UniqueVariableNames(); + } + + @Test + public void testRenameInSecondNoGood() { + final Predicate predA = Predicate.getInstance("a", 2); + final Predicate predB = Predicate.getInstance("b", 2); + final VariableTerm varX = VariableTerm.getInstance("X"); + final VariableTerm varY = VariableTerm.getInstance("Y"); + final VariableTerm varY1 = VariableTerm.getInstance("Y_1"); + final VariableTerm varY2 = VariableTerm.getInstance("Y_2"); + final VariableTerm varZ = VariableTerm.getInstance("Z"); + + final List literals1 = new ArrayList<>(); + literals1.add(lit(true, predA, varX, varY)); + literals1.add(lit(false, predA, varX, varY1)); + literals1.add(comp(varY1, plus(varY, ConstantTerm.getInstance(1)), EQ)); + + final List literals2 = new ArrayList<>(); + literals2.add(lit(true, predB, varY, varZ)); + + final List expectedModifiedLiterals2 = new ArrayList<>(); + expectedModifiedLiterals2.add(lit(true, predB, varY2, varZ)); + + final NonGroundNoGood noGood1 = new NonGroundNoGood(LEARNT, literals1, false); + final NonGroundNoGood noGood2 = new NonGroundNoGood(STATIC, literals2, false); + final NonGroundNoGood expectedModifiedNoGood2 = new NonGroundNoGood(STATIC, expectedModifiedLiterals2, false); + + final NonGroundNoGood modifiedNoGood1 = uniqueVariableNames.makeVariableNamesUnique(noGood1); + final NonGroundNoGood modifiedNoGood2 = uniqueVariableNames.makeVariableNamesUnique(noGood2); + + assertEquals(noGood1, modifiedNoGood1); + assertEquals(expectedModifiedNoGood2, modifiedNoGood2); + } + + private BasicLiteral lit(boolean positive, Predicate predicate, Term... terms) { + return new BasicAtom(predicate, terms).toLiteral(positive); + } + + private ComparisonLiteral comp(Term term1, Term term2, ComparisonOperator operator) { + return new ComparisonAtom(term1, term2, operator).toLiteral(true); + } + + private Term plus(VariableTerm term1, Term term2) { + return ArithmeticTerm.getInstance(term1, PLUS, term2); + } + +} From e838da0f0328910b588c0d013883a9eddda38e99 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 1 Apr 2020 15:12:03 +0200 Subject: [PATCH 24/55] Move substitution stuff from grounder to common --- .../at/ac/tuwien/kr/alpha/common/Rule.java | 1 - .../{grounder => common}/Substitution.java | 3 ++- .../{grounder => common}/Unification.java | 15 ++++++------- .../alpha/{grounder => common}/Unifier.java | 2 +- .../kr/alpha/common/UniqueVariableNames.java | 1 - .../kr/alpha/common/atoms/AggregateAtom.java | 2 +- .../alpha/common/atoms/AggregateLiteral.java | 2 +- .../ac/tuwien/kr/alpha/common/atoms/Atom.java | 4 ++-- .../kr/alpha/common/atoms/BasicAtom.java | 6 ++++-- .../kr/alpha/common/atoms/BasicLiteral.java | 2 +- .../kr/alpha/common/atoms/ComparisonAtom.java | 2 +- .../alpha/common/atoms/ComparisonLiteral.java | 8 +++++-- .../kr/alpha/common/atoms/ExternalAtom.java | 10 ++++----- .../alpha/common/atoms/ExternalLiteral.java | 8 +++++-- .../atoms/FixedInterpretationLiteral.java | 2 +- .../tuwien/kr/alpha/common/atoms/Literal.java | 2 +- .../kr/alpha/common/terms/ArithmeticTerm.java | 2 +- .../kr/alpha/common/terms/ConstantTerm.java | 2 +- .../kr/alpha/common/terms/FunctionTerm.java | 8 +++++-- .../kr/alpha/common/terms/IntervalTerm.java | 2 +- .../ac/tuwien/kr/alpha/common/terms/Term.java | 2 +- .../kr/alpha/common/terms/VariableTerm.java | 2 +- .../kr/alpha/grounder/NaiveGrounder.java | 1 + .../kr/alpha/grounder/NoGoodGenerator.java | 1 + .../atoms/BodyRepresentingLiteral.java | 2 +- .../kr/alpha/grounder/atoms/ChoiceAtom.java | 2 +- .../alpha/grounder/atoms/EnumerationAtom.java | 2 +- .../grounder/atoms/EnumerationLiteral.java | 2 +- .../kr/alpha/grounder/atoms/IntervalAtom.java | 2 +- .../alpha/grounder/atoms/IntervalLiteral.java | 2 +- .../kr/alpha/grounder/atoms/RuleAtom.java | 2 +- .../kr/alpha/grounder/structure/LitSet.java | 4 ++-- .../CardinalityNormalization.java | 21 ++++++++++++++----- .../transformation/SumNormalization.java | 13 +++++++++--- .../VariableEqualityRemoval.java | 9 ++++++-- .../tuwien/kr/alpha/solver/DefaultSolver.java | 2 +- .../kr/alpha/grounder/ChoiceGrounder.java | 1 + .../kr/alpha/grounder/DummyGrounder.java | 1 + .../kr/alpha/grounder/NaiveGrounderTest.java | 1 + .../alpha/grounder/NoGoodGeneratorTest.java | 1 + .../kr/alpha/grounder/SubstitutionTest.java | 1 + .../tuwien/kr/alpha/grounder/UnifierTest.java | 2 ++ .../kr/alpha/solver/AtomCounterTests.java | 2 +- 43 files changed, 102 insertions(+), 60 deletions(-) rename src/main/java/at/ac/tuwien/kr/alpha/{grounder => common}/Substitution.java (98%) rename src/main/java/at/ac/tuwien/kr/alpha/{grounder => common}/Unification.java (97%) rename src/main/java/at/ac/tuwien/kr/alpha/{grounder => common}/Unifier.java (99%) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Rule.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Rule.java index f1f8943fd..036f619dc 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Rule.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Rule.java @@ -30,7 +30,6 @@ import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Unifier; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java similarity index 98% rename from src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java rename to src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java index 5f3fac98a..6878a39d7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java @@ -25,7 +25,7 @@ * 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; +package at.ac.tuwien.kr.alpha.common; import at.ac.tuwien.kr.alpha.antlr.ASPCore2Lexer; import at.ac.tuwien.kr.alpha.antlr.ASPCore2Parser; @@ -35,6 +35,7 @@ import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; 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.Instance; import at.ac.tuwien.kr.alpha.grounder.parser.ParseTreeVisitor; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Unification.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Unification.java similarity index 97% rename from src/main/java/at/ac/tuwien/kr/alpha/grounder/Unification.java rename to src/main/java/at/ac/tuwien/kr/alpha/common/Unification.java index b448dc29f..77c81026d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Unification.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Unification.java @@ -1,19 +1,19 @@ /** * Copyright (c) 2017-2018, 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 @@ -25,7 +25,7 @@ * 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; +package at.ac.tuwien.kr.alpha.common; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; @@ -36,9 +36,6 @@ import static at.ac.tuwien.kr.alpha.Util.oops; -/** - * Copyright (c) 2017, the Alpha Team. - */ public class Unification { public static Unifier unifyAtoms(Atom left, Atom right) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Unifier.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java similarity index 99% rename from src/main/java/at/ac/tuwien/kr/alpha/grounder/Unifier.java rename to src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java index 8d65fa7ca..5048cdf15 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/Unifier.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java @@ -1,4 +1,4 @@ -package at.ac.tuwien.kr.alpha.grounder; +package at.ac.tuwien.kr.alpha.common; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java index 29ae39bff..4be37c4fe 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java @@ -29,7 +29,6 @@ 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.Substitution; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateAtom.java index f9108050e..9f453a757 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateAtom.java @@ -29,9 +29,9 @@ import at.ac.tuwien.kr.alpha.common.ComparisonOperator; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; 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.Substitution; import java.util.Collections; import java.util.LinkedList; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateLiteral.java index 4cdabd179..536828d1b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/AggregateLiteral.java @@ -1,9 +1,9 @@ package at.ac.tuwien.kr.alpha.common.atoms; import at.ac.tuwien.kr.alpha.common.ComparisonOperator; +import at.ac.tuwien.kr.alpha.common.Substitution; 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.Substitution; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Atom.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Atom.java index 5519f3404..2c85d7c78 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Atom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Atom.java @@ -28,10 +28,10 @@ package at.ac.tuwien.kr.alpha.common.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; +import at.ac.tuwien.kr.alpha.common.Unifier; 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.Unifier; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.List; import java.util.Set; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicAtom.java index 172d78587..8cc036609 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicAtom.java @@ -28,10 +28,12 @@ package at.ac.tuwien.kr.alpha.common.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.terms.Term; -import at.ac.tuwien.kr.alpha.grounder.Substitution; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; import static at.ac.tuwien.kr.alpha.Util.join; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java index c3a078fd8..d73a09aab 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java @@ -27,9 +27,9 @@ */ package at.ac.tuwien.kr.alpha.common.atoms; +import at.ac.tuwien.kr.alpha.common.Substitution; 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.Substitution; import java.util.Collections; import java.util.HashSet; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonAtom.java index b19e2a699..6df127d18 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ComparisonAtom.java @@ -29,8 +29,8 @@ import at.ac.tuwien.kr.alpha.common.ComparisonOperator; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.terms.Term; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.Arrays; import java.util.List; 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 91ac679d3..7681059f3 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 @@ -28,13 +28,17 @@ package at.ac.tuwien.kr.alpha.common.atoms; import at.ac.tuwien.kr.alpha.common.ComparisonOperator; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Substitution; -import java.util.*; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import static at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm.evaluateGroundTerm; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalAtom.java index 2c9ba2191..43cfddd69 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalAtom.java @@ -27,16 +27,16 @@ */ package at.ac.tuwien.kr.alpha.common.atoms; -import static at.ac.tuwien.kr.alpha.Util.join; +import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; +import at.ac.tuwien.kr.alpha.common.fixedinterpretations.PredicateInterpretation; +import at.ac.tuwien.kr.alpha.common.terms.Term; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import at.ac.tuwien.kr.alpha.common.Predicate; -import at.ac.tuwien.kr.alpha.common.fixedinterpretations.PredicateInterpretation; -import at.ac.tuwien.kr.alpha.common.terms.Term; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import static at.ac.tuwien.kr.alpha.Util.join; public class ExternalAtom implements Atom, VariableNormalizableAtom { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalLiteral.java index 86a943df5..dbb9e8868 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/ExternalLiteral.java @@ -27,12 +27,16 @@ */ package at.ac.tuwien.kr.alpha.common.atoms; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Substitution; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static java.util.Collections.emptyList; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/FixedInterpretationLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/FixedInterpretationLiteral.java index 2db2df800..e831835f2 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/FixedInterpretationLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/FixedInterpretationLiteral.java @@ -27,7 +27,7 @@ */ package at.ac.tuwien.kr.alpha.common.atoms; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.common.Substitution; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java index 907a38b04..e0bef6caf 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java @@ -28,9 +28,9 @@ package at.ac.tuwien.kr.alpha.common.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; 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.Substitution; import org.apache.commons.collections4.SetUtils; import java.util.List; 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 cf01f5f6e..410fc19f2 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 @@ -28,7 +28,7 @@ package at.ac.tuwien.kr.alpha.common.terms; import at.ac.tuwien.kr.alpha.common.Interner; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.common.Substitution; import com.google.common.math.IntMath; import java.util.ArrayList; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ConstantTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ConstantTerm.java index 21b98e730..7113f262c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ConstantTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/ConstantTerm.java @@ -1,7 +1,7 @@ package at.ac.tuwien.kr.alpha.common.terms; import at.ac.tuwien.kr.alpha.common.Interner; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.common.Substitution; import java.util.Collections; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/FunctionTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/FunctionTerm.java index 9e3847ac4..3ec07ed96 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/FunctionTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/FunctionTerm.java @@ -1,9 +1,13 @@ package at.ac.tuwien.kr.alpha.common.terms; import at.ac.tuwien.kr.alpha.common.Interner; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.common.Substitution; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import static at.ac.tuwien.kr.alpha.Util.join; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java index 8383b55ca..7b82db741 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java @@ -1,7 +1,7 @@ package at.ac.tuwien.kr.alpha.common.terms; import at.ac.tuwien.kr.alpha.common.Interner; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.common.Substitution; import java.util.LinkedList; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java index a619a108d..5a233146d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java @@ -1,6 +1,6 @@ package at.ac.tuwien.kr.alpha.common.terms; -import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.common.Substitution; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java index 8396b4102..eb76508e0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java @@ -1,8 +1,8 @@ package at.ac.tuwien.kr.alpha.common.terms; import at.ac.tuwien.kr.alpha.common.Interner; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.grounder.IntIdGenerator; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.Collections; import java.util.List; 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 c379e5f88..f5a93123c 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.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.Substitution; 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.ComparisonLiteral; 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 2fe6b6054..1e4e94de0 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 @@ -31,6 +31,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.FixedInterpretationLiteral; import at.ac.tuwien.kr.alpha.common.atoms.Literal; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java index cb7875cc8..b22241666 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java @@ -26,9 +26,9 @@ package at.ac.tuwien.kr.alpha.grounder.atoms; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.Collections; import java.util.Set; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/ChoiceAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/ChoiceAtom.java index bea727e50..6b369dacc 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/ChoiceAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/ChoiceAtom.java @@ -28,11 +28,11 @@ package at.ac.tuwien.kr.alpha.grounder.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.Atom; 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.grounder.Substitution; import java.util.Collections; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationAtom.java index 8848b2845..7c229e5d2 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationAtom.java @@ -1,11 +1,11 @@ package at.ac.tuwien.kr.alpha.grounder.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.HashMap; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationLiteral.java index 46e82aa2b..be662a4cd 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/EnumerationLiteral.java @@ -1,9 +1,9 @@ package at.ac.tuwien.kr.alpha.grounder.atoms; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.BasicLiteral; 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.Substitution; import java.util.Collections; import java.util.HashSet; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalAtom.java index 5832fc878..5acc36b55 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalAtom.java @@ -28,11 +28,11 @@ package at.ac.tuwien.kr.alpha.grounder.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.VariableNormalizableAtom; import at.ac.tuwien.kr.alpha.common.terms.IntervalTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalLiteral.java index 372594550..4582d6be3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/IntervalLiteral.java @@ -27,12 +27,12 @@ */ package at.ac.tuwien.kr.alpha.grounder.atoms; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.FixedInterpretationLiteral; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; import at.ac.tuwien.kr.alpha.common.terms.IntervalTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import com.google.common.collect.Sets; import java.util.ArrayList; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java index b5347887c..2d9c055b6 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/RuleAtom.java @@ -28,12 +28,12 @@ package at.ac.tuwien.kr.alpha.grounder.atoms; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/LitSet.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/LitSet.java index 2e415af08..6c57cebfe 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/LitSet.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/LitSet.java @@ -1,9 +1,9 @@ package at.ac.tuwien.kr.alpha.grounder.structure; +import at.ac.tuwien.kr.alpha.common.Unification; +import at.ac.tuwien.kr.alpha.common.Unifier; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.VariableNormalizableAtom; -import at.ac.tuwien.kr.alpha.grounder.Unifier; -import at.ac.tuwien.kr.alpha.grounder.Unification; import java.util.Collections; import java.util.HashSet; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/CardinalityNormalization.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/CardinalityNormalization.java index 21b301a5f..dc31f1766 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/CardinalityNormalization.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/CardinalityNormalization.java @@ -1,17 +1,28 @@ package at.ac.tuwien.kr.alpha.grounder.transformation; -import at.ac.tuwien.kr.alpha.common.*; -import at.ac.tuwien.kr.alpha.common.atoms.*; +import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; +import at.ac.tuwien.kr.alpha.common.Program; +import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; +import at.ac.tuwien.kr.alpha.common.Unifier; +import at.ac.tuwien.kr.alpha.common.atoms.AggregateAtom; +import at.ac.tuwien.kr.alpha.common.atoms.AggregateLiteral; +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.FunctionTerm; 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.Substitution; -import at.ac.tuwien.kr.alpha.grounder.Unifier; import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationAtom; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; /** * Copyright (c) 2017, the Alpha Team. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/SumNormalization.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/SumNormalization.java index 3f69906d7..841ad3988 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/SumNormalization.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/SumNormalization.java @@ -3,16 +3,23 @@ import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.common.Rule; -import at.ac.tuwien.kr.alpha.common.atoms.*; +import at.ac.tuwien.kr.alpha.common.Unifier; +import at.ac.tuwien.kr.alpha.common.atoms.AggregateAtom; +import at.ac.tuwien.kr.alpha.common.atoms.AggregateLiteral; +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.FunctionTerm; 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.Unifier; import at.ac.tuwien.kr.alpha.grounder.atoms.EnumerationAtom; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; import static at.ac.tuwien.kr.alpha.grounder.transformation.PredicateInternalizer.makePredicatesInternal; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/VariableEqualityRemoval.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/VariableEqualityRemoval.java index 8529f4775..82fd034e4 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/VariableEqualityRemoval.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/transformation/VariableEqualityRemoval.java @@ -30,14 +30,19 @@ import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Unifier; import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.ComparisonLiteral; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import at.ac.tuwien.kr.alpha.grounder.Unifier; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; /** * Removes variable equalities from rules by replacing one variable with the other. 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 bcab0d7db..e982d4680 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 @@ -32,6 +32,7 @@ import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; 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.ComparisonAtom; @@ -41,7 +42,6 @@ import at.ac.tuwien.kr.alpha.grounder.Grounder; import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; import at.ac.tuwien.kr.alpha.grounder.ProgramAnalyzingGrounder; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import at.ac.tuwien.kr.alpha.solver.heuristics.BranchingHeuristic; import at.ac.tuwien.kr.alpha.solver.heuristics.BranchingHeuristicFactory; 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 d9bd9442b..72c35339c 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 @@ -37,6 +37,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; 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.ChoiceAtom; 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 d3de2e5a9..cf5f70cec 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 @@ -37,6 +37,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; 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; 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 f2aeda83f..26b92c4cc 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 @@ -32,6 +32,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Program; +import at.ac.tuwien.kr.alpha.common.Substitution; 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; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java index cacf844d3..ab9867789 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/NoGoodGeneratorTest.java @@ -30,6 +30,7 @@ import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.Program; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.atoms.ChoiceAtom; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java index c037c30c1..0575ca334 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java @@ -29,6 +29,7 @@ import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.BasicLiteral; import at.ac.tuwien.kr.alpha.common.atoms.Literal; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java b/src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java index 4ea4dd92c..4ec16ab1d 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java @@ -3,6 +3,8 @@ 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.Substitution; +import at.ac.tuwien.kr.alpha.common.Unifier; 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.BasicLiteral; diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java index 51aabfb18..ad20f2352 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/AtomCounterTests.java @@ -31,13 +31,13 @@ import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.AggregateAtom; 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.terms.ConstantTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; -import at.ac.tuwien.kr.alpha.grounder.Substitution; import at.ac.tuwien.kr.alpha.grounder.atoms.ChoiceAtom; import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import org.junit.Before; From 4de090c0fcfc10bbef49be3f4dcaf60caad1b069 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 1 Apr 2020 15:15:33 +0200 Subject: [PATCH 25/55] Fix visibility of Substitution constructor and method --- .../java/at/ac/tuwien/kr/alpha/common/Substitution.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java index 6878a39d7..5d2a28ece 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2016-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -54,7 +54,7 @@ public class Substitution { protected TreeMap substitution; - public Substitution(TreeMap substitution) { // FIXME + Substitution(TreeMap substitution) { if (substitution == null) { throw oops("Substitution is null."); } @@ -69,7 +69,7 @@ public Substitution(Substitution clone) { this(new TreeMap<>(clone.substitution)); } - static Substitution unify(Literal literal, Instance instance, Substitution substitution) { + public static Substitution unify(Literal literal, Instance instance, Substitution substitution) { return unify(literal.getAtom(), instance, substitution); } From 5c24ad9d2db2fbdad4010080103fab1d0562a7b0 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 3 Apr 2020 16:00:07 +0200 Subject: [PATCH 26/55] Implement explanation-based learning of non-ground constraints --- .../tuwien/kr/alpha/common/Substitution.java | 66 ++++--- .../at/ac/tuwien/kr/alpha/common/Unifier.java | 19 ++ .../kr/alpha/common/UniqueVariableNames.java | 16 +- .../kr/alpha/grounder/NoGoodGenerator.java | 14 ++ .../ac/tuwien/kr/alpha/solver/Antecedent.java | 25 ++- .../tuwien/kr/alpha/solver/DefaultSolver.java | 6 +- .../learning/ConflictAnalysisResult.java | 31 +++ .../NonGroundConflictNoGoodLearner.java | 177 ++++++++++++++---- .../NonGroundConflictNoGoodLearnerTest.java | 169 ++++++++++++++++- 9 files changed, 447 insertions(+), 76 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java index 5d2a28ece..4fcc1057e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java @@ -92,34 +92,56 @@ public static Substitution unify(Atom atom, Instance instance, Substitution subs } /** - * Checks if the left possible non-ground term unifies with the ground term. - * @param termNonGround - * @param termGround - * @return + * Checks if the left, possibly non-ground term unifies with the right ground term. + * This substitution is modified to reflect the unification. + * @param termNonGround the left term, possibly non-ground + * @param termGround the right term, must be ground + * @return {@code true} iff the unification succeeds */ public boolean unifyTerms(Term termNonGround, Term termGround) { - if (termNonGround == termGround) { - // Both terms are either the same constant or the same variable term + return unifyTerms(termNonGround, termGround, false); + } + + /** + * Checks if the left, possibly non-ground term unifies with the right, possibly non-ground term. + * This substitution is modified to reflect the unification. + * @param termLeft the left term, possibly + * @param termRight the right term, may only be non-ground if {@code allowNonGroundTermRight} is {@code true} + * @return {@code true} iff the unification succeeds + */ + public boolean unifyTerms(Term termLeft, Term termRight, boolean allowNonGroundTermRight) { + final boolean isRightTermGround = termRight.isGround(); + if (!allowNonGroundTermRight && !isRightTermGround) { + throw new IllegalArgumentException("Term " + termRight + " is not ground."); + } + if (termLeft == termRight) { return true; - } else if (termNonGround instanceof ConstantTerm) { + } else if (isRightTermGround && termLeft instanceof ConstantTerm) { // Since right term is ground, both terms differ return false; - } else if (termNonGround instanceof VariableTerm) { - VariableTerm variableTerm = (VariableTerm)termNonGround; - // Left term is variable, bind it to the right term. - Term bound = eval(variableTerm); - - if (bound != null) { - // Variable is already bound, return true if binding is the same as the current ground term. - return termGround == bound; - } + } else if (termLeft instanceof VariableTerm) { + VariableTerm variableTermLeft = (VariableTerm)termLeft; + if (isRightTermGround) { + // Left term is variable, bind it to the right term. + Term bound = eval(variableTermLeft); - substitution.put(variableTerm, termGround); - return true; - } else if (termNonGround instanceof FunctionTerm && termGround instanceof FunctionTerm) { + if (bound != null) { + // Variable is already bound, return true if binding is the same as the current ground term. + return termRight == bound; + } + + put(variableTermLeft, termRight); + return true; + } else { + assert termRight instanceof VariableTerm; + put(variableTermLeft, termRight); // see Unifier#put + // TODO is this right, or should we check if the variable is already bound? + return true; + } + } else if (termLeft instanceof FunctionTerm && termRight instanceof FunctionTerm) { // Both terms are function terms - FunctionTerm ftNonGround = (FunctionTerm) termNonGround; - FunctionTerm ftGround = (FunctionTerm) termGround; + FunctionTerm ftNonGround = (FunctionTerm) termLeft; + FunctionTerm ftGround = (FunctionTerm) termRight; if (!(ftNonGround.getSymbol().equals(ftGround.getSymbol()))) { return false; @@ -130,7 +152,7 @@ public boolean unifyTerms(Term termNonGround, Term termGround) { // Iterate over all subterms of both function terms for (int i = 0; i < ftNonGround.getTerms().size(); i++) { - if (!unifyTerms(ftNonGround.getTerms().get(i), ftGround.getTerms().get(i))) { + if (!unifyTerms(ftNonGround.getTerms().get(i), ftGround.getTerms().get(i), allowNonGroundTermRight)) { return false; } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java index 5048cdf15..7ee5afc76 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java @@ -1,11 +1,13 @@ package at.ac.tuwien.kr.alpha.common; +import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.TreeMap; import static at.ac.tuwien.kr.alpha.Util.oops; @@ -47,7 +49,24 @@ public Unifier extendWith(Substitution extension) { return this; } + public void unify(Atom modifiableAtom, Atom unmodifiableAtom) { + if (modifiableAtom == null || unmodifiableAtom == null) { + throw new IllegalArgumentException("Atom must not be null."); + } + if (!Objects.equals(modifiableAtom.getPredicate(), unmodifiableAtom.getPredicate())) { + throw new IllegalArgumentException("Predicates of two atoms do not match."); + } + int i = 0; + for (Term modifiableTerm : modifiableAtom.getTerms()) { + if (!unifyTerms(modifiableTerm, unmodifiableAtom.getTerms().get(i), true)) { + throw new IllegalArgumentException("Atoms cannot be unified: " + modifiableAtom + ", " + unmodifiableAtom); + } + i++; + } + + } + @Override public > Term put(VariableTerm variableTerm, Term term) { // If term is not ground, store it for right-hand side reverse-lookup. if (!term.isGround()) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java index 4be37c4fe..0e24084d2 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java @@ -27,7 +27,6 @@ package at.ac.tuwien.kr.alpha.common; 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 java.util.ArrayList; @@ -35,39 +34,38 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; public class UniqueVariableNames { private final Map variablesToOccurrences = new HashMap<>(); public NonGroundNoGood makeVariableNamesUnique(NonGroundNoGood noGood) { - final Substitution substitution = renameVariablesIfAlreadyUsed(noGood.getOccurringVariables()); - if (substitution.isEmpty()) { + final Unifier unifier = renameVariablesIfAlreadyUsed(noGood.getOccurringVariables()); + if (unifier.isEmpty()) { return noGood; } final List newLiterals = new ArrayList<>(noGood.size()); for (Literal literal : noGood) { - newLiterals.add(literal.substitute(substitution)); + newLiterals.add(literal.substitute(unifier)); } return new NonGroundNoGood(noGood.getType(), newLiterals, noGood.hasHead()); } - private Substitution renameVariablesIfAlreadyUsed(Set variables) { - final TreeMap substitution = new TreeMap<>(); + private Unifier renameVariablesIfAlreadyUsed(Set variables) { + final Unifier unifier = new Unifier(); for (VariableTerm variable : variables) { if (variablesToOccurrences.containsKey(variable)) { VariableTerm newVariable; do { newVariable = VariableTerm.getInstance(variable.toString() + "_" + (variablesToOccurrences.computeIfPresent(variable, (v,o) -> o+1))); } while (variablesToOccurrences.containsKey(newVariable)); - substitution.put(variable, newVariable); + unifier.put(variable, newVariable); } else { variablesToOccurrences.put(variable, 0); } } - return new Substitution(substitution); + return unifier; } } 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 1e4e94de0..541ffad03 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 @@ -249,6 +249,20 @@ public static class CollectedLiterals { private final List skippedFixedInterpretationLiterals = new ArrayList<>(); private final List skippedFacts = new ArrayList<>(); + public CollectedLiterals() { + super(); + } + + public CollectedLiterals( + List collectedGroundLiterals, List correspondingNonGroundLiterals, + List skippedFixedInterpretationLiterals, List skippedFacts) { + this(); + this.collectedGroundLiterals.addAll(collectedGroundLiterals); + this.correspondingNonGroundLiterals.addAll(correspondingNonGroundLiterals); + this.skippedFixedInterpretationLiterals.addAll(skippedFixedInterpretationLiterals); + this.skippedFacts.addAll(skippedFacts); + } + public List getCollectedGroundLiterals() { return Collections.unmodifiableList(collectedGroundLiterals); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java index 6db35070b..0c8b907b0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/Antecedent.java @@ -29,13 +29,15 @@ import at.ac.tuwien.kr.alpha.common.NoGood; +import java.util.Iterator; + /** * An interface to reasons of implications as used internally by the solver. This is a lightweight {@link at.ac.tuwien.kr.alpha.common.NoGood} that only * provides an array of literals (in some order) and has an activity that may change. * * Copyright (c) 2019-2020, the Alpha Team. */ -public interface Antecedent { +public interface Antecedent extends Iterable { int[] getReasonLiterals(); @@ -51,4 +53,25 @@ default NoGood getOriginalNoGood() { return null; } + @Override + default Iterator iterator() { + return new Iterator() { + private int i; + private int[] literals = getReasonLiterals(); + + @Override + public boolean hasNext() { + return literals.length > i; + } + + @Override + public Integer next() { + return literals[i++]; + } + }; + } + + default int size() { + return getReasonLiterals().length; + } } 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 e982d4680..238bcb2fc 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-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -110,7 +110,7 @@ public DefaultSolver(AtomStore atomStore, Grounder grounder, NoGoodStore store, this.store = store; this.choiceManager = new ChoiceManager(assignment, store); final GroundConflictNoGoodLearner groundLearner = new GroundConflictNoGoodLearner(assignment, atomStore); - this.learner = conflictGeneralisationEnabled ? new NonGroundConflictNoGoodLearner(assignment, groundLearner) : groundLearner; + this.learner = conflictGeneralisationEnabled ? new NonGroundConflictNoGoodLearner(assignment, atomStore, groundLearner) : groundLearner; this.branchingHeuristic = chainFallbackHeuristic(grounder, assignment, random, heuristicsConfiguration); this.disableJustifications = config.isDisableJustificationSearch(); this.disableNoGoodDeletion = config.isDisableNoGoodDeletion(); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java index 2ca45e60d..9610c228c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java @@ -28,6 +28,7 @@ package at.ac.tuwien.kr.alpha.solver.learning; import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; import java.util.Collection; import java.util.Collections; @@ -45,6 +46,8 @@ public class ConflictAnalysisResult { public final int lbd; private List additionalLearnedNoGoods; + private NonGroundNoGood learnedNonGroundNoGood; + private List additionalLearnedNonGroundNoGoods; private ConflictAnalysisResult() { learnedNoGood = null; @@ -88,6 +91,34 @@ List getAdditionalLearnedNoGoods() { return additionalLearnedNoGoods == null ? Collections.emptyList() : Collections.unmodifiableList(additionalLearnedNoGoods); } + /** + * Adds non-ground nogoods that have been learned additionally to the primary learned nogood on non-first UIPs. + * @param additionalLearnedNonGroundNoGoods the non-ground nogoods learnt on non-first UIPs + */ + void addLearnedNonGroundNoGoods(List additionalLearnedNonGroundNoGoods) { + if (this.additionalLearnedNonGroundNoGoods == null) { + this.additionalLearnedNonGroundNoGoods = additionalLearnedNonGroundNoGoods; + } else { + this.additionalLearnedNonGroundNoGoods.addAll(additionalLearnedNonGroundNoGoods); + } + } + + /** + * Gets a list of non-ground nogoods that have been learned additionally to the primary learned nogood on non-first UIPs. + * @return a list of additional learned non-ground nogoods + */ + List getAdditionalLearnedNonGroundNoGoods() { + return additionalLearnedNonGroundNoGoods == null ? Collections.emptyList() : Collections.unmodifiableList(additionalLearnedNonGroundNoGoods); + } + + void setLearnedNonGroundNoGood(NonGroundNoGood learnedNonGroundNoGood) { + this.learnedNonGroundNoGood = learnedNonGroundNoGood; + } + + public NonGroundNoGood getLearnedNonGroundNoGood() { + return learnedNonGroundNoGood; + } + @Override public String toString() { if (this == UNSAT) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 2324047b2..97a769182 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -26,8 +26,12 @@ package at.ac.tuwien.kr.alpha.solver.learning; import at.ac.tuwien.kr.alpha.common.Assignment; +import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; +import at.ac.tuwien.kr.alpha.common.Unifier; +import at.ac.tuwien.kr.alpha.common.UniqueVariableNames; +import at.ac.tuwien.kr.alpha.common.atoms.Atom; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.solver.Antecedent; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; @@ -35,8 +39,10 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -44,6 +50,7 @@ import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; 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.isPositive; import static at.ac.tuwien.kr.alpha.common.Literals.negateLiteral; /** @@ -58,10 +65,12 @@ public class NonGroundConflictNoGoodLearner implements ConflictNoGoodLearner { private static final Logger LOGGER = LoggerFactory.getLogger(NonGroundConflictNoGoodLearner.class); private final Assignment assignment; + private final AtomStore atomStore; private final GroundConflictNoGoodLearner groundLearner; - public NonGroundConflictNoGoodLearner(Assignment assignment, GroundConflictNoGoodLearner groundLearner) { + public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore, GroundConflictNoGoodLearner groundLearner) { this.assignment = assignment; + this.atomStore = atomStore; this.groundLearner = groundLearner; } @@ -98,79 +107,78 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent LOGGER.trace("Conflict on decision level 0."); return ConflictAnalysisResult.UNSAT; } + final TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); NoGood firstLearnedNoGood = null; + NonGroundNoGood firstLearnedNonGroundNoGood = null; List additionalLearnedNoGoods = new ArrayList<>(); - Set currentNoGood; - Set resolvent = intArrayToLinkedHashSet(violatedNoGood.getReasonLiterals()); - do { - currentNoGood = resolvent; - resolvent = makeOneResolutionStep(currentNoGood, trailWalker); - if (resolvent != null && containsUIP(resolvent)) { - final NoGood learntNoGood = createLearntNoGood(resolvent); + List additionalLearnedNonGroundNoGoods = new ArrayList<>(); + GroundAndNonGroundNoGood currentNoGood = new GroundAndNonGroundNoGood(violatedNoGood); + while (makeOneResolutionStep(currentNoGood, trailWalker)) { + if (containsUIP(currentNoGood)) { + final NoGood learntNoGood = createLearntNoGood(currentNoGood); + final NonGroundNoGood learntNonGroundNoGood = currentNoGood.toNonGroundNoGood(); + learntNoGood.setNonGroundNoGood(learntNonGroundNoGood); if (firstLearnedNoGood == null) { firstLearnedNoGood = learntNoGood; + firstLearnedNonGroundNoGood = learntNonGroundNoGood; } else { additionalLearnedNoGoods.add(learntNoGood); + additionalLearnedNonGroundNoGoods.add(learntNonGroundNoGood); } } - } while (resolvent != null); + } final ConflictAnalysisResult groundAnalysisResultNotMinimized = groundLearner.analyzeTrailBased(violatedNoGood, false); if (!Objects.equals(groundAnalysisResultNotMinimized.learnedNoGood, firstLearnedNoGood)) { throw oops("Learned nogood is not the same as the one computed by ground analysis"); } final ConflictAnalysisResult analysisResult = groundLearner.analyzeConflictingNoGood(violatedNoGood); - // if backJumpLevel < 0, then problem is UNSAT (ground analysis result does additional checks for this) - if (!additionalLearnedNoGoods.isEmpty() && analysisResult.backjumpLevel >= 0) { - analysisResult.addLearnedNoGoods(additionalLearnedNoGoods); + if (analysisResult.backjumpLevel >= 0) { + // if backJumpLevel < 0, then problem is UNSAT (ground analysis result does additional checks for this) + analysisResult.setLearnedNonGroundNoGood(firstLearnedNonGroundNoGood); + if (!additionalLearnedNoGoods.isEmpty()) { + analysisResult.addLearnedNoGoods(additionalLearnedNoGoods); + analysisResult.addLearnedNonGroundNoGoods(additionalLearnedNonGroundNoGoods); + } } return analysisResult; } /** * Resolves the current nogood with the antecedent of one of its literals assigned on the current decision level. + * The given {@code currentNoGood} is modified in situ. * @param currentNoGood the nogood to resolve with the antecedent of one of its literals * @param trailWalker a backwards-walker on the trail - * @return the resolvent (as a set of literals), or {@code null} if no further resolution step is possible + * @return {@code true} if resolution succeeded, or {@code false} if no further resolution step is possible */ - private Set makeOneResolutionStep(Set currentNoGood, TrailAssignment.TrailBackwardsWalker trailWalker) { + private boolean makeOneResolutionStep(GroundAndNonGroundNoGood currentNoGood, TrailAssignment.TrailBackwardsWalker trailWalker) { if (!containsLiteralForResolution(currentNoGood)) { - return null; + return false; } final int literal = findNextLiteralToResolve(currentNoGood, trailWalker); - return resolve(currentNoGood, literal, intArrayToLinkedHashSet(assignment.getImpliedBy(atomOf(literal)).getReasonLiterals())); + resolve(currentNoGood, literal); + return true; } - private Integer findNextLiteralToResolve(Set noGood, TrailAssignment.TrailBackwardsWalker trailWalker) { + private Integer findNextLiteralToResolve(GroundAndNonGroundNoGood noGood, TrailAssignment.TrailBackwardsWalker trailWalker) { final int currentDecisionLevel = assignment.getDecisionLevel(); int literal; int atom; do { literal = trailWalker.getNextLowerLiteral(); atom = atomOf(literal); - } while (assignment.getWeakDecisionLevel(atom) != currentDecisionLevel || assignment.getImpliedBy(atom) == null || !noGood.contains(literal)); + } while (assignment.getWeakDecisionLevel(atom) != currentDecisionLevel || assignment.getImpliedBy(atom) == null || !noGood.groundNoGood.contains(literal)); return literal; } - private Set resolve(Set noGood1, int literalInNoGood1, Set noGood2) { - final int literalInNoGood2 = negateLiteral(literalInNoGood1); - final Set resolvent = new LinkedHashSet<>(); - for (int literal : noGood1) { - if (literal != literalInNoGood1) { - resolvent.add(literal); - } - } - for (int literal : noGood2) { - if (literal != literalInNoGood2) { - resolvent.add(literal); - } - } - return resolvent; + private void resolve(GroundAndNonGroundNoGood noGood1, int literal1) { + final Antecedent antecedentOfLiteral = assignment.getImpliedBy(atomOf(literal1)); + noGood1.resolveWith(antecedentOfLiteral, literal1); } - private boolean containsLiteralForResolution(Set noGood) { + private boolean containsLiteralForResolution(GroundAndNonGroundNoGood noGood) { final int currentDecisionLevel = assignment.getDecisionLevel(); - for (Integer literal : noGood) { + for (Integer literal : noGood.groundNoGood) { final int atom = atomOf(literal); if (assignment.getWeakDecisionLevel(atom) == currentDecisionLevel && assignment.getImpliedBy(atom) != null) { return true; @@ -179,10 +187,10 @@ private boolean containsLiteralForResolution(Set noGood) { return false; } - private boolean containsUIP(Set resolvent) { + private boolean containsUIP(GroundAndNonGroundNoGood resolvent) { final int currentDecisionLevel = assignment.getDecisionLevel(); boolean containsLiteralOnCurrentDecisionLevel = false; - for (Integer literal : resolvent) { + for (int literal : resolvent.groundNoGood) { if (assignment.getWeakDecisionLevel(atomOf(literal)) == currentDecisionLevel) { if (containsLiteralOnCurrentDecisionLevel) { return false; @@ -195,11 +203,11 @@ private boolean containsUIP(Set resolvent) { return containsLiteralOnCurrentDecisionLevel; } - private NoGood createLearntNoGood(Set resolvent) { - return NoGood.learnt(collectionToIntArray(resolvent)); + private NoGood createLearntNoGood(GroundAndNonGroundNoGood resolvent) { + return NoGood.learnt(collectionToIntArray(resolvent.groundNoGood)); } - private List getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { + private static List getAdditionalLiterals(NonGroundNoGood nonGroundNoGood, int numberOfAlreadyConsideredLiterals) { final List result = new ArrayList<>(nonGroundNoGood.size() - numberOfAlreadyConsideredLiterals); for (int i = numberOfAlreadyConsideredLiterals; i < nonGroundNoGood.size(); i++) { result.add(nonGroundNoGood.getLiteral(i)); @@ -207,4 +215,95 @@ private List getAdditionalLiterals(NonGroundNoGood nonGroundN return result; } + class GroundAndNonGroundNoGood { + + final UniqueVariableNames uniqueVariableNames = new UniqueVariableNames(); + + Set groundNoGood; + Map mapToNonGroundAtoms = new HashMap<>(); + Set additionalNonGroundLiterals = new LinkedHashSet<>(); + + boolean canLearnNonGround; + + public GroundAndNonGroundNoGood(Antecedent antecedent) { + this.groundNoGood = intArrayToLinkedHashSet(antecedent.getReasonLiterals()); + canLearnNonGround = digestOriginalNonGroundNoGood(antecedent); + } + + private boolean digestOriginalNonGroundNoGood(Antecedent antecedent) { + final NoGood originalNoGood = antecedent.getOriginalNoGood(); + if (originalNoGood == null) { + LOGGER.warn("Cannot generalise conflict because original nogood unknown for " + antecedent); + return false; + } + NonGroundNoGood nonGroundNoGood = originalNoGood.getNonGroundNoGood(); + if (nonGroundNoGood == null) { + LOGGER.warn("Cannot generalise conflict because non-ground nogood unknown for " + atomStore.noGoodToString(originalNoGood)); + return false; + } + + nonGroundNoGood = uniqueVariableNames.makeVariableNamesUnique(nonGroundNoGood); + final Unifier unifier = new Unifier(); + if (!mapToNonGroundAtoms.isEmpty()) { + // if we already have atoms stored, we need to unify conflicting non-ground atoms: + for (int literal : antecedent) { + final Literal nonGroundLiteral = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood); + final Atom existingNonGroundAtom = mapToNonGroundAtoms.get(atomOf(literal)); + if (existingNonGroundAtom != null) { + unifier.unify(nonGroundLiteral.getAtom(), existingNonGroundAtom); + } + } + } + for (int literal : antecedent) { + final Atom unifiedNonGroundAtom = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood).getAtom().substitute(unifier); + final Atom existingNonGroundAtom = mapToNonGroundAtoms.get(atomOf(literal)); + if (existingNonGroundAtom != null) { + if (!existingNonGroundAtom.equals(unifiedNonGroundAtom)) { + throw oops("Existing non-ground atom " + existingNonGroundAtom + " for " + atomOf(literal) + " does not match unified atom " + unifiedNonGroundAtom); + } + } else { + mapToNonGroundAtoms.put(atomOf(literal), unifiedNonGroundAtom); + } + } + + for (Literal additionalLiteral : getAdditionalLiterals(nonGroundNoGood, antecedent.size())) { + additionalNonGroundLiterals.add(additionalLiteral.substitute(unifier)); + } + return true; + } + + private Literal findNonGroundLiteral(int literal, NoGood originalNoGood, NonGroundNoGood nonGroundNoGood) { + // index must be looked up because literals may have different order in antecedent than in original nogood: + final int indexOfLiteralInOriginalNogood = originalNoGood.indexOf(literal); + final Literal nonGroundLiteral = nonGroundNoGood.getLiteral(indexOfLiteralInOriginalNogood); + if (!nonGroundLiteral.getPredicate().equals(atomStore.get(atomOf(literal)).getPredicate())) { + throw oops("Wrong non-ground literal assigned to ground literal"); + // TODO: execute this and other checks only if internal checks enabled (?) + } + return nonGroundLiteral; + } + + void resolveWith(Antecedent antecedent, int literalInThisNoGood) { + canLearnNonGround = canLearnNonGround && digestOriginalNonGroundNoGood(antecedent); + this.groundNoGood.remove(literalInThisNoGood); + for (int literal : antecedent.getReasonLiterals()) { + if (literal != negateLiteral(literalInThisNoGood)) { + this.groundNoGood.add(literal); + } + } + } + + NonGroundNoGood toNonGroundNoGood() { + if (!canLearnNonGround) { + return null; + } + final List literals = new ArrayList<>(); + for (int literal : groundNoGood) { + literals.add(mapToNonGroundAtoms.get(atomOf(literal)).toLiteral(isPositive(literal))); + } + literals.addAll(additionalNonGroundLiterals); + return NonGroundNoGood.learnt(literals); + } + } + } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java index cc6571e9a..2602629ae 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java @@ -27,9 +27,23 @@ import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; +import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; import at.ac.tuwien.kr.alpha.common.NoGood; +import at.ac.tuwien.kr.alpha.common.NonGroundNoGood; import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Substitution; +import at.ac.tuwien.kr.alpha.common.Unifier; +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.ComparisonAtom; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm; +import at.ac.tuwien.kr.alpha.common.terms.ConstantTerm; +import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; +import at.ac.tuwien.kr.alpha.grounder.NoGoodGenerator; +import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; +import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import at.ac.tuwien.kr.alpha.solver.Antecedent; import at.ac.tuwien.kr.alpha.solver.ConflictCause; import at.ac.tuwien.kr.alpha.solver.NoGoodStore; @@ -39,14 +53,25 @@ import org.junit.Before; import org.junit.Test; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; +import static at.ac.tuwien.kr.alpha.common.ComparisonOperator.EQ; +import static at.ac.tuwien.kr.alpha.common.ComparisonOperator.LT; import static at.ac.tuwien.kr.alpha.common.Literals.atomToLiteral; +import static at.ac.tuwien.kr.alpha.common.terms.ArithmeticTerm.ArithmeticOperator.MINUS; import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.FALSE; +import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.MBT; import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.TRUE; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -73,9 +98,9 @@ public void setUp() { * in: Armin Biere, Marijn Heule, Hans van Maaren and Toby Walsh (Eds.): Handbook of Satisfiability */ @Test - public void exampleFromSatisfiabilityHandbook() { + public void groundExampleFromSatisfiabilityHandbook() { final GroundConflictNoGoodLearner groundLearner = new GroundConflictNoGoodLearner(assignment, atomStore); - final NonGroundConflictNoGoodLearner learner = new NonGroundConflictNoGoodLearner(assignment, groundLearner); + final NonGroundConflictNoGoodLearner learner = new NonGroundConflictNoGoodLearner(assignment, atomStore, groundLearner); int x1 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x1", 0))); int x2 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x2", 0))); int x3 = atomStore.putIfAbsent(new BasicAtom(Predicate.getInstance("x3", 0))); @@ -148,4 +173,144 @@ public void exampleFromSatisfiabilityHandbook() { System.out.println(atomStore.noGoodToString(additionalLearnedNoGoods.get(0))); assertEquals(expectedLearnedNoGood, intArrayToLinkedHashSet(additionalLearnedNoGoods.get(0).asAntecedent().getReasonLiterals())); } + + /** + * Tests non-ground learning on the following example:
    + * Nogood 1: { -(col(1,g), +(_R_("0", "{X->1,Col->g}") }, non-ground: { -(col(X,Col), +(_R_("0", (X,Col)) }
    + * Nogood 2: { -(col(0,g)), +(col(1,g)) }, non-ground: { -(col(Y,C)), +(col(X,Col)), +(Y=X-1) }
    + * Nogood 3: { +(col(1,g)), +(col(0,g)) }, non-ground: { +(col(N,C)), +(col(M,C)), +(M + * When nogood 3 is violated, it is first resolved with nogood 2, learning a nogood at the first UIP, + * and then with nogood 1, learning another nogood at the second UIP. + */ + @Test + public void smallNonGroundExample() { + final GroundConflictNoGoodLearner groundLearner = new GroundConflictNoGoodLearner(assignment, atomStore); + final NonGroundConflictNoGoodLearner learner = new NonGroundConflictNoGoodLearner(assignment, atomStore, groundLearner); + + final Predicate predCol = Predicate.getInstance("col", 2); + final Predicate predUnrelated = Predicate.getInstance("unrelated", 2); + + final ConstantTerm const0 = ConstantTerm.getInstance(0); + final ConstantTerm const1 = ConstantTerm.getInstance(1); + final ConstantTerm constG = ConstantTerm.getInstance("g"); + + final int groundAtomCol0g = atomStore.putIfAbsent(new BasicAtom(predCol, const0, constG)); + final int groundAtomCol1g = atomStore.putIfAbsent(new BasicAtom(predCol, const1, constG)); + final int groundAtomUnrelated = atomStore.putIfAbsent(new BasicAtom(predUnrelated, const1, constG)); + + final VariableTerm varX = VariableTerm.getInstance("X"); + final VariableTerm varY = VariableTerm.getInstance("Y"); + final VariableTerm varC = VariableTerm.getInstance("C"); + final VariableTerm varM = VariableTerm.getInstance("M"); + final VariableTerm varN = VariableTerm.getInstance("N"); + final VariableTerm varCol = VariableTerm.getInstance("Col"); + + final Atom nonGroundAtomColXCol = new BasicAtom(predCol, varX, varCol); + final Atom nonGroundAtomColYCol = new BasicAtom(predCol, varY, varCol); + final Atom nonGroundAtomColMC = new BasicAtom(predCol, varM, varC); + final Atom nonGroundAtomColNC = new BasicAtom(predCol, varN, varC); + final Atom unrelated = new BasicAtom(predUnrelated, varX, varCol); + + final Rule rule = new Rule(new DisjunctiveHead(singletonList(nonGroundAtomColXCol)), singletonList(unrelated.toLiteral())); + final NonGroundRule nonGroundRule = NonGroundRule.constructNonGroundRule(rule); + final Substitution substitution = new Substitution(); + substitution.put(varX, const1); + substitution.put(varCol, constG); + final int groundAtomR = atomStore.putIfAbsent(RuleAtom.ground(nonGroundRule, substitution)); + final RuleAtom nonGroundAtomR = RuleAtom.nonGround(nonGroundRule); + + final NoGood ng1 = NoGood.headFirst(atomToLiteral(groundAtomCol1g, false), atomToLiteral(groundAtomR)); + final Map atomMapping1 = new HashMap<>(); + atomMapping1.put(groundAtomCol1g, nonGroundAtomColXCol); + atomMapping1.put(groundAtomR, nonGroundAtomR); + ng1.setNonGroundNoGood(NonGroundNoGood.forGroundNoGood(ng1, atomMapping1)); + + final NoGood ng2 = NoGood.learnt(atomToLiteral(groundAtomCol0g, false), atomToLiteral(groundAtomCol1g)); + final Literal nonGroundComparisonLiteralYEqualsXMinus1 = new ComparisonAtom(varY, ArithmeticTerm.getInstance(varX, MINUS, const1), EQ).toLiteral(); + final NoGoodGenerator.CollectedLiterals positiveCollectedLiterals2 = new NoGoodGenerator.CollectedLiterals( + asList(atomToLiteral(groundAtomCol0g, false), atomToLiteral(groundAtomCol1g)), + asList(nonGroundAtomColYCol.toLiteral(false), nonGroundAtomColXCol.toLiteral()), + singletonList(nonGroundComparisonLiteralYEqualsXMinus1), + emptyList() + ); + final NoGoodGenerator.CollectedLiterals negativeCollectedLiterals2 = new NoGoodGenerator.CollectedLiterals(); + ng2.setNonGroundNoGood(NonGroundNoGood.fromBody(ng2, positiveCollectedLiterals2, negativeCollectedLiterals2, positiveCollectedLiterals2.getAtomMapping())); + + final List groundLiteralsInNoGood3 = asList(atomToLiteral(groundAtomCol1g), atomToLiteral(groundAtomCol0g)); + final NoGood ng3 = NoGood.fromConstraint(groundLiteralsInNoGood3, emptyList()); + final Literal nonGroundComparisonLiteralMLessThanN = new ComparisonAtom(varM, varN, LT).toLiteral(); + final NoGoodGenerator.CollectedLiterals positiveCollectedLiterals3 = new NoGoodGenerator.CollectedLiterals( + groundLiteralsInNoGood3, + asList(nonGroundAtomColNC.toLiteral(), nonGroundAtomColMC.toLiteral()), + singletonList(nonGroundComparisonLiteralMLessThanN), + emptyList() + ); + final NoGoodGenerator.CollectedLiterals negativeCollectedLiterals3 = new NoGoodGenerator.CollectedLiterals(); + ng3.setNonGroundNoGood(NonGroundNoGood.fromBody(ng3, positiveCollectedLiterals3, negativeCollectedLiterals3, positiveCollectedLiterals3.getAtomMapping())); + + store.growForMaxAtomId(atomStore.getMaxAtomId()); + this.assignment.growForMaxAtomId(); + + store.add(1, ng1); + store.add(2, ng2); + store.add(3, ng3); + + assertEquals(0, assignment.getDecisionLevel()); + ConflictCause conflictCause = assignment.choose(groundAtomUnrelated, TRUE); + assertNull(conflictCause); + conflictCause = store.propagate(); + assertNull(conflictCause); + assertEquals(1, assignment.getDecisionLevel()); + conflictCause = assignment.choose(groundAtomR, TRUE); + assertNull(conflictCause); + conflictCause = store.propagate(); + assertEquals(2, assignment.getDecisionLevel()); + assertEquals(TRUE, assignment.get(groundAtomCol1g).getTruth()); + assertEquals(MBT, assignment.get(groundAtomCol0g).getTruth()); + + assertNotNull(conflictCause); + final Antecedent antecedent = conflictCause.getAntecedent(); + assertEquals(ng3, antecedent.getOriginalNoGood()); + + final ConflictAnalysisResult conflictAnalysisResult = learner.analyzeConflictingNoGoodAndGeneraliseConflict(antecedent); + Set expectedLearnedNoGood1 = new LinkedHashSet<>(); + expectedLearnedNoGood1.add(atomToLiteral(groundAtomCol1g, true)); + assert conflictAnalysisResult.learnedNoGood != null; + assertEquals(expectedLearnedNoGood1, intArrayToLinkedHashSet(conflictAnalysisResult.learnedNoGood.asAntecedent().getReasonLiterals())); + + final Literal nonGroundComparisonLiteralMEqualsNMinus1 = new ComparisonAtom(varM, ArithmeticTerm.getInstance(varN, MINUS, const1), EQ).toLiteral(); + + final NoGoodGenerator.CollectedLiterals positiveCollectedLiteralsLearned1 = new NoGoodGenerator.CollectedLiterals( + new ArrayList<>(expectedLearnedNoGood1), + singletonList(nonGroundAtomColNC.toLiteral()), + asList(nonGroundComparisonLiteralMLessThanN, nonGroundComparisonLiteralMEqualsNMinus1), + emptyList() + ); + final NoGoodGenerator.CollectedLiterals negativeCollectedLiteralsLearned1 = new NoGoodGenerator.CollectedLiterals(); + final NonGroundNoGood expectedLearnedNonGroundNoGood1 = NonGroundNoGood.fromBody(conflictAnalysisResult.learnedNoGood, positiveCollectedLiteralsLearned1, negativeCollectedLiteralsLearned1, positiveCollectedLiteralsLearned1.getAtomMapping()); + assertEquals(expectedLearnedNonGroundNoGood1, conflictAnalysisResult.getLearnedNonGroundNoGood()); + + assertEquals(1, conflictAnalysisResult.getAdditionalLearnedNoGoods().size()); + assertEquals(1, conflictAnalysisResult.getAdditionalLearnedNonGroundNoGoods().size()); + + Set expectedLearnedNoGood2 = new LinkedHashSet<>(); + expectedLearnedNoGood2.add(atomToLiteral(groundAtomR, true)); + final NoGood learnedNoGood2 = conflictAnalysisResult.getAdditionalLearnedNoGoods().get(0); + assertEquals(expectedLearnedNoGood2, intArrayToLinkedHashSet(learnedNoGood2.asAntecedent().getReasonLiterals())); + + final Unifier ruleUnifier = new Unifier(); + ruleUnifier.put(varX, varN); + ruleUnifier.put(varCol, varC); + final RuleAtom nonGroundAtomRenamedRule = nonGroundAtomR.substitute(ruleUnifier); + + final NoGoodGenerator.CollectedLiterals positiveCollectedLiteralsLearned2 = new NoGoodGenerator.CollectedLiterals( + new ArrayList<>(expectedLearnedNoGood2), + singletonList(nonGroundAtomRenamedRule.toLiteral()), + asList(nonGroundComparisonLiteralMLessThanN, nonGroundComparisonLiteralMEqualsNMinus1), + emptyList() + ); + final NoGoodGenerator.CollectedLiterals negativeCollectedLiteralsLearned2 = new NoGoodGenerator.CollectedLiterals(); + final NonGroundNoGood expectedLearnedNonGroundNoGood2 = NonGroundNoGood.fromBody(learnedNoGood2, positiveCollectedLiteralsLearned2, negativeCollectedLiteralsLearned2, positiveCollectedLiteralsLearned2.getAtomMapping()); + assertEquals(expectedLearnedNonGroundNoGood2, conflictAnalysisResult.getAdditionalLearnedNonGroundNoGoods().get(0)); + } } \ No newline at end of file From 498a008d71bfa6aa805c21e222b2e610a2b97afd Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 Apr 2020 09:05:35 +0200 Subject: [PATCH 27/55] Substitution: left constant / right variable is unsupported --- .../at/ac/tuwien/kr/alpha/common/Substitution.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java index 4fcc1057e..ddcffa942 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java @@ -105,7 +105,7 @@ public boolean unifyTerms(Term termNonGround, Term termGround) { /** * Checks if the left, possibly non-ground term unifies with the right, possibly non-ground term. * This substitution is modified to reflect the unification. - * @param termLeft the left term, possibly + * @param termLeft the left term, possibly non-ground * @param termRight the right term, may only be non-ground if {@code allowNonGroundTermRight} is {@code true} * @return {@code true} iff the unification succeeds */ @@ -116,9 +116,14 @@ public boolean unifyTerms(Term termLeft, Term termRight, boolean allowNonGroundT } if (termLeft == termRight) { return true; - } else if (isRightTermGround && termLeft instanceof ConstantTerm) { - // Since right term is ground, both terms differ - return false; + } else if (termLeft instanceof ConstantTerm) { + if (isRightTermGround) { + // Since right term is ground, both terms differ + return false; + } else { + throw new UnsupportedOperationException(); + // TODO: substitute right term to unify with left term + } } else if (termLeft instanceof VariableTerm) { VariableTerm variableTermLeft = (VariableTerm)termLeft; if (isRightTermGround) { From 19d1fec0133da8eef38bf1ea55b64dee6c1579ef Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 Apr 2020 09:20:53 +0200 Subject: [PATCH 28/55] Fix get...Variables in BodyRepresentingLiteral --- .../kr/alpha/common/atoms/BasicLiteral.java | 48 +---------------- .../tuwien/kr/alpha/common/atoms/Literal.java | 52 ++++++++++++++++--- .../atoms/BodyRepresentingLiteral.java | 14 ----- 3 files changed, 46 insertions(+), 68 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java index d73a09aab..ea333e3c0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/BasicLiteral.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-2018, the Alpha Team. +/* + * Copyright (c) 2017-2018, 2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -28,17 +28,9 @@ package at.ac.tuwien.kr.alpha.common.atoms; import at.ac.tuwien.kr.alpha.common.Substitution; -import at.ac.tuwien.kr.alpha.common.terms.Term; -import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; /** * Contains a potentially negated {@link BasicAtom}. - * - * Copyright (c) 2017-2018, the Alpha Team. */ public class BasicLiteral extends Literal { @@ -66,40 +58,4 @@ public BasicLiteral negate() { public BasicLiteral substitute(Substitution substitution) { return new BasicLiteral(getAtom().substitute(substitution), positive); } - - /** - * Set of all variables occurring in the Atom that are potentially binding, i.e., variables in positive atoms. - * - * @return - */ - @Override - public Set getBindingVariables() { - if (!positive) { - // Negative literal has no binding variables. - return Collections.emptySet(); - } - Set bindingVariables = new HashSet<>(); - for (Term term : atom.getTerms()) { - bindingVariables.addAll(term.getOccurringVariables()); - } - return bindingVariables; - } - - /** - * Set of all variables occurring in the Atom that are never binding, not even in positive atoms, e.g., variables in intervals or built-in atoms. - * - * @return - */ - @Override - public Set getNonBindingVariables() { - if (positive) { - // Positive literal has only binding variables. - return Collections.emptySet(); - } - Set nonbindingVariables = new HashSet<>(); - for (Term term : atom.getTerms()) { - nonbindingVariables.addAll(term.getOccurringVariables()); - } - return nonbindingVariables; - } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java index e0bef6caf..1d3d13931 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2017-2018, the Alpha Team. +/* + * Copyright (c) 2017-2018, 2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -33,13 +33,13 @@ import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import org.apache.commons.collections4.SetUtils; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; /** * A potentially negated {@link Atom} - * - * Copyright (c) 2017-2018, the Alpha Team. */ public abstract class Literal { @@ -62,10 +62,46 @@ public boolean isNegated() { public abstract Literal negate(); public abstract Literal substitute(Substitution substitution); - - public abstract Set getBindingVariables(); - - public abstract Set getNonBindingVariables(); + + /** + * Set of all variables occurring in the Atom that are potentially binding, i.e., variables in positive atoms. + * + * This is the default implementation used by {@link BasicLiteral} and {@link at.ac.tuwien.kr.alpha.grounder.atoms.BodyRepresentingLiteral} + * and may be overridden in implementing classes. + * + * @return the set of binding variables in this literal + */ + public Set getBindingVariables() { + if (!positive) { + // Negative literal has no binding variables. + return Collections.emptySet(); + } + Set bindingVariables = new HashSet<>(); + for (Term term : atom.getTerms()) { + bindingVariables.addAll(term.getOccurringVariables()); + } + return bindingVariables; + } + + /** + * Set of all variables occurring in the Atom that are never binding, not even in positive atoms, e.g., variables in intervals or built-in atoms. + * + * This is the default implementation used by {@link BasicLiteral} and {@link at.ac.tuwien.kr.alpha.grounder.atoms.BodyRepresentingLiteral} + * and may be overridden in implementing classes. + * + * @return the set of non-binding variables in this literal + */ + public Set getNonBindingVariables() { + if (positive) { + // Positive literal has only binding variables. + return Collections.emptySet(); + } + Set nonbindingVariables = new HashSet<>(); + for (Term term : atom.getTerms()) { + nonbindingVariables.addAll(term.getOccurringVariables()); + } + return nonbindingVariables; + } /** * Union of {@link #getBindingVariables()} and {@link #getNonBindingVariables()} diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java index b22241666..db1443891 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/atoms/BodyRepresentingLiteral.java @@ -28,10 +28,6 @@ import at.ac.tuwien.kr.alpha.common.Substitution; import at.ac.tuwien.kr.alpha.common.atoms.Literal; -import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; - -import java.util.Collections; -import java.util.Set; /** * A literal containing a {@link RuleAtom} (only to be used internally, e.g., in {@link at.ac.tuwien.kr.alpha.common.NonGroundNoGood}s) @@ -56,14 +52,4 @@ public BodyRepresentingLiteral negate() { public BodyRepresentingLiteral substitute(Substitution substitution) { return new BodyRepresentingLiteral(getAtom().substitute(substitution), positive); } - - @Override - public Set getBindingVariables() { - return Collections.emptySet(); - } - - @Override - public Set getNonBindingVariables() { - return Collections.emptySet(); - } } From 59b1f18e924d506bda5f39957cf900a8cad79f8a Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 Apr 2020 09:55:43 +0200 Subject: [PATCH 29/55] Logging: output learned non-ground nogoods --- .../alpha/solver/learning/NonGroundConflictNoGoodLearner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 97a769182..e3147b001 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -136,9 +136,11 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent if (analysisResult.backjumpLevel >= 0) { // if backJumpLevel < 0, then problem is UNSAT (ground analysis result does additional checks for this) analysisResult.setLearnedNonGroundNoGood(firstLearnedNonGroundNoGood); + LOGGER.info("Learned non-ground nogood for ground conflict {}: {}", violatedNoGood, firstLearnedNonGroundNoGood); if (!additionalLearnedNoGoods.isEmpty()) { analysisResult.addLearnedNoGoods(additionalLearnedNoGoods); analysisResult.addLearnedNonGroundNoGoods(additionalLearnedNonGroundNoGoods); + LOGGER.info("Additionally learned non-ground nogoods: {}", additionalLearnedNonGroundNoGoods); } } return analysisResult; From 7890cb7c3777124ad377f15e5665152a8120c570 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 Apr 2020 10:45:23 +0200 Subject: [PATCH 30/55] Output learned non-ground nogoods with counters --- .../tuwien/kr/alpha/solver/DefaultSolver.java | 7 +++ .../NonGroundConflictNoGoodLearner.java | 49 ++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) 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 238bcb2fc..26bb850a5 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 @@ -600,5 +600,12 @@ private void logStats() { AtomCounter atomCounter = atomStore.getAtomCounter(); LOGGER.debug("Number of atoms by type: {}", atomCounter.getStatsByType()); } + logLearnedNonGroundNoGoods(); + } + + private void logLearnedNonGroundNoGoods() { + if (learner instanceof NonGroundConflictNoGoodLearner) { + ((NonGroundConflictNoGoodLearner)learner).logLearnedNonGroundNoGoods(); + } } } \ No newline at end of file diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index e3147b001..01444ca5b 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -68,6 +69,11 @@ public class NonGroundConflictNoGoodLearner implements ConflictNoGoodLearner { private final AtomStore atomStore; private final GroundConflictNoGoodLearner groundLearner; + private Map nonGroundNoGoodViolationCounter = new HashMap<>(); + private Map> learnedOnFirstUIP = new HashMap<>(); + private Map> learnedOnLastUIP = new HashMap<>(); + private Map> learnedOnOtherUIPs = new HashMap<>(); + public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore, GroundConflictNoGoodLearner groundLearner) { this.assignment = assignment; this.atomStore = atomStore; @@ -124,7 +130,9 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent firstLearnedNonGroundNoGood = learntNonGroundNoGood; } else { additionalLearnedNoGoods.add(learntNoGood); - additionalLearnedNonGroundNoGoods.add(learntNonGroundNoGood); + if (learntNonGroundNoGood != null) { + additionalLearnedNonGroundNoGoods.add(learntNonGroundNoGood); + } } } } @@ -142,6 +150,7 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent analysisResult.addLearnedNonGroundNoGoods(additionalLearnedNonGroundNoGoods); LOGGER.info("Additionally learned non-ground nogoods: {}", additionalLearnedNonGroundNoGoods); } + countAndRememberLearnedNonGroundNoGoods(violatedNoGood, analysisResult); } return analysisResult; } @@ -217,6 +226,44 @@ private static List getAdditionalLiterals(NonGroundNoGood non return result; } + private void countAndRememberLearnedNonGroundNoGoods(Antecedent violatedNoGood, ConflictAnalysisResult analysisResult) { + if (violatedNoGood.getOriginalNoGood() == null) { + return; + } + final NonGroundNoGood violatedNonGroundNoGood = violatedNoGood.getOriginalNoGood().getNonGroundNoGood(); + if (violatedNonGroundNoGood == null) { + return; + } + final NonGroundNoGood learnedNonGroundNoGood = analysisResult.getLearnedNonGroundNoGood(); + final List additionalLearnedNonGroundNoGoods = analysisResult.getAdditionalLearnedNonGroundNoGoods(); + if (!nonGroundNoGoodViolationCounter.containsKey(violatedNonGroundNoGood)) { + nonGroundNoGoodViolationCounter.put(violatedNonGroundNoGood, 0); + learnedOnFirstUIP.put(violatedNonGroundNoGood, new HashSet<>()); + learnedOnLastUIP.put(violatedNonGroundNoGood, new HashSet<>()); + learnedOnOtherUIPs.put(violatedNonGroundNoGood, new HashSet<>()); + } + nonGroundNoGoodViolationCounter.computeIfPresent(violatedNonGroundNoGood, (n,c) -> c + 1); + if (learnedNonGroundNoGood != null) { + learnedOnFirstUIP.get(violatedNonGroundNoGood).add(learnedNonGroundNoGood); + } + if (!additionalLearnedNonGroundNoGoods.isEmpty()) { + learnedOnLastUIP.get(violatedNonGroundNoGood).add(additionalLearnedNonGroundNoGoods.get(additionalLearnedNonGroundNoGoods.size() - 1)); + for (int i = 0; i < additionalLearnedNonGroundNoGoods.size() - 1; i++) { + learnedOnOtherUIPs.get(violatedNonGroundNoGood).add(additionalLearnedNonGroundNoGoods.get(i)); + } + } + } + + public void logLearnedNonGroundNoGoods() { + LOGGER.info("LEARNED NON-GROUND NOGOODS:"); + for (NonGroundNoGood violatedNonGroundNoGood : nonGroundNoGoodViolationCounter.keySet()) { + LOGGER.info("Violated {} times: {}", nonGroundNoGoodViolationCounter.get(violatedNonGroundNoGood), violatedNonGroundNoGood); + LOGGER.info("Learned on first UIP: {}", learnedOnFirstUIP.get(violatedNonGroundNoGood)); + LOGGER.info("Learned on last UIP: {}", learnedOnLastUIP.get(violatedNonGroundNoGood)); + LOGGER.info("Learned on other UIPs: {}", learnedOnOtherUIPs.get(violatedNonGroundNoGood)); + } + } + class GroundAndNonGroundNoGood { final UniqueVariableNames uniqueVariableNames = new UniqueVariableNames(); From 1c1dd68d667c2fc0b859e5218fd81ee7ec3fba27 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 Apr 2020 16:41:29 +0200 Subject: [PATCH 31/55] GroundAndNonGroundNoGood#toString --- .../solver/learning/NonGroundConflictNoGoodLearner.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 01444ca5b..84f3d10e3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -353,6 +353,11 @@ NonGroundNoGood toNonGroundNoGood() { literals.addAll(additionalNonGroundLiterals); return NonGroundNoGood.learnt(literals); } + + @Override + public String toString() { + return String.valueOf(toNonGroundNoGood()); + } } } From dc663bc71b7a0b6a59cbb6b1dff663fd2a04fa70 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 7 Apr 2020 17:35:18 +0200 Subject: [PATCH 32/55] Fix unification of conflicting atoms during non-ground resolution --- .../at/ac/tuwien/kr/alpha/solver/Atoms.java | 31 +++++++++++++++++++ .../NonGroundConflictNoGoodLearner.java | 31 ++++++++++++++++--- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/Atoms.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/Atoms.java index b0d26b58b..d43a08fd9 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/Atoms.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/Atoms.java @@ -1,6 +1,37 @@ +/* + * Copyright (c) 2017, 2020, 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; public final class Atoms { + + public static final int FALSUM = 0; + public static boolean isAtom(int atom) { return atom >= 0; } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 84f3d10e3..90d269785 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -51,8 +51,10 @@ import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; 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; import static at.ac.tuwien.kr.alpha.common.Literals.isPositive; import static at.ac.tuwien.kr.alpha.common.Literals.negateLiteral; +import static at.ac.tuwien.kr.alpha.solver.Atoms.FALSUM; /** * Conflict-driven learning on ground clauses that also learns non-ground nogoods. @@ -276,10 +278,10 @@ class GroundAndNonGroundNoGood { public GroundAndNonGroundNoGood(Antecedent antecedent) { this.groundNoGood = intArrayToLinkedHashSet(antecedent.getReasonLiterals()); - canLearnNonGround = digestOriginalNonGroundNoGood(antecedent); + canLearnNonGround = digestOriginalNonGroundNoGood(antecedent, atomToLiteral(FALSUM)); } - private boolean digestOriginalNonGroundNoGood(Antecedent antecedent) { + private boolean digestOriginalNonGroundNoGood(Antecedent antecedent, int impliedLiteral) { final NoGood originalNoGood = antecedent.getOriginalNoGood(); if (originalNoGood == null) { LOGGER.warn("Cannot generalise conflict because original nogood unknown for " + antecedent); @@ -293,15 +295,34 @@ private boolean digestOriginalNonGroundNoGood(Antecedent antecedent) { nonGroundNoGood = uniqueVariableNames.makeVariableNamesUnique(nonGroundNoGood); final Unifier unifier = new Unifier(); + assert (impliedLiteral == atomToLiteral(FALSUM)) == mapToNonGroundAtoms.isEmpty(); // impliedLiteral is only FALSUM for violated nogood if (!mapToNonGroundAtoms.isEmpty()) { // if we already have atoms stored, we need to unify conflicting non-ground atoms: + final Map mapExistingAtomToConflictUnifier = new HashMap<>(); for (int literal : antecedent) { final Literal nonGroundLiteral = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood); + final Atom nonGroundAtom = nonGroundLiteral.getAtom(); final Atom existingNonGroundAtom = mapToNonGroundAtoms.get(atomOf(literal)); - if (existingNonGroundAtom != null) { - unifier.unify(nonGroundLiteral.getAtom(), existingNonGroundAtom); + if (literal == negateLiteral(impliedLiteral)) { + // this is the literal used for resolution + assert existingNonGroundAtom != null; + unifier.unify(nonGroundAtom, existingNonGroundAtom); + } else if (existingNonGroundAtom != null) { + // this is another atom that already exists in the current nogood. It may be that because of this, + // the current nogood uses two variable names for what should be the same variable. + // Therefore, additional unification may be necessary in the current nogood: + // all atoms that are substituted in the conflicting atom must also be substituted in the existing atom + final Unifier conflictUnifier = new Unifier(); + conflictUnifier.unify(existingNonGroundAtom, nonGroundAtom); + mapExistingAtomToConflictUnifier.put(atomOf(literal), conflictUnifier); } } + for (Map.Entry existingAtomToConflictUnifier : mapExistingAtomToConflictUnifier.entrySet()) { + final int existingAtom = existingAtomToConflictUnifier.getKey(); + final Unifier mergedUnifier = Unifier.mergeIntoLeft(existingAtomToConflictUnifier.getValue(), unifier); + mapToNonGroundAtoms.computeIfPresent(existingAtom, (a,n) -> n.substitute(mergedUnifier)); + // TODO: should this actually be done for ALL existing atoms ?! + } } for (int literal : antecedent) { final Atom unifiedNonGroundAtom = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood).getAtom().substitute(unifier); @@ -333,7 +354,7 @@ private Literal findNonGroundLiteral(int literal, NoGood originalNoGood, NonGrou } void resolveWith(Antecedent antecedent, int literalInThisNoGood) { - canLearnNonGround = canLearnNonGround && digestOriginalNonGroundNoGood(antecedent); + canLearnNonGround = canLearnNonGround && digestOriginalNonGroundNoGood(antecedent, literalInThisNoGood); this.groundNoGood.remove(literalInThisNoGood); for (int literal : antecedent.getReasonLiterals()) { if (literal != negateLiteral(literalInThisNoGood)) { From ae2cfc324f2e30a48772fd2eddc3f291734a53aa Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 8 Apr 2020 09:07:31 +0200 Subject: [PATCH 33/55] Satisfy Checkstyle --- .../kr/alpha/common/UniqueVariableNames.java | 2 +- .../NonGroundConflictNoGoodLearner.java | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java index 0e24084d2..9f7269c20 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java @@ -58,7 +58,7 @@ private Unifier renameVariablesIfAlreadyUsed(Set variables) { if (variablesToOccurrences.containsKey(variable)) { VariableTerm newVariable; do { - newVariable = VariableTerm.getInstance(variable.toString() + "_" + (variablesToOccurrences.computeIfPresent(variable, (v,o) -> o+1))); + newVariable = VariableTerm.getInstance(variable.toString() + "_" + (variablesToOccurrences.computeIfPresent(variable, (v, o) -> o + 1))); } while (variablesToOccurrences.containsKey(newVariable)); unifier.put(variable, newVariable); } else { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 90d269785..7ab43b53d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -58,7 +58,7 @@ /** * Conflict-driven learning on ground clauses that also learns non-ground nogoods. - * + *

    * This class builds upon an underlying {@link GroundConflictNoGoodLearner}, to which it acts as a proxy for all operations * not implemented on the non-ground level. * The non-ground learning capabilities implemented here are not designed for efficiency (yet), so this class should only @@ -71,10 +71,10 @@ public class NonGroundConflictNoGoodLearner implements ConflictNoGoodLearner { private final AtomStore atomStore; private final GroundConflictNoGoodLearner groundLearner; - private Map nonGroundNoGoodViolationCounter = new HashMap<>(); - private Map> learnedOnFirstUIP = new HashMap<>(); - private Map> learnedOnLastUIP = new HashMap<>(); - private Map> learnedOnOtherUIPs = new HashMap<>(); + private Map nonGroundNoGoodViolationCounter = new HashMap<>(); + private Map> learnedOnFirstUIP = new HashMap<>(); + private Map> learnedOnLastUIP = new HashMap<>(); + private Map> learnedOnOtherUIPs = new HashMap<>(); public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore, GroundConflictNoGoodLearner groundLearner) { this.assignment = assignment; @@ -100,7 +100,7 @@ public ConflictAnalysisResult analyzeConflictingNoGood(Antecedent violatedNoGood /** * Analyzes a conflict and learns both a ground nogood (if possible) and one or more non-ground nogoods (if possible). - * + *

    * This method also contains an implementation of first UIP learning that is redundant to the one in * {@link GroundConflictNoGoodLearner#analyzeTrailBased(Antecedent, boolean)} on purpose: * While the other implementation is designed for efficiency, this one is designed to be easily understood such that @@ -116,7 +116,7 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent return ConflictAnalysisResult.UNSAT; } - final TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment)assignment).getTrailBackwardsWalker(); + final TrailAssignment.TrailBackwardsWalker trailWalker = ((TrailAssignment) assignment).getTrailBackwardsWalker(); NoGood firstLearnedNoGood = null; NonGroundNoGood firstLearnedNonGroundNoGood = null; List additionalLearnedNoGoods = new ArrayList<>(); @@ -160,8 +160,9 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent /** * Resolves the current nogood with the antecedent of one of its literals assigned on the current decision level. * The given {@code currentNoGood} is modified in situ. + * * @param currentNoGood the nogood to resolve with the antecedent of one of its literals - * @param trailWalker a backwards-walker on the trail + * @param trailWalker a backwards-walker on the trail * @return {@code true} if resolution succeeded, or {@code false} if no further resolution step is possible */ private boolean makeOneResolutionStep(GroundAndNonGroundNoGood currentNoGood, TrailAssignment.TrailBackwardsWalker trailWalker) { @@ -244,7 +245,7 @@ private void countAndRememberLearnedNonGroundNoGoods(Antecedent violatedNoGood, learnedOnLastUIP.put(violatedNonGroundNoGood, new HashSet<>()); learnedOnOtherUIPs.put(violatedNonGroundNoGood, new HashSet<>()); } - nonGroundNoGoodViolationCounter.computeIfPresent(violatedNonGroundNoGood, (n,c) -> c + 1); + nonGroundNoGoodViolationCounter.computeIfPresent(violatedNonGroundNoGood, (n, c) -> c + 1); if (learnedNonGroundNoGood != null) { learnedOnFirstUIP.get(violatedNonGroundNoGood).add(learnedNonGroundNoGood); } @@ -298,7 +299,7 @@ private boolean digestOriginalNonGroundNoGood(Antecedent antecedent, int implied assert (impliedLiteral == atomToLiteral(FALSUM)) == mapToNonGroundAtoms.isEmpty(); // impliedLiteral is only FALSUM for violated nogood if (!mapToNonGroundAtoms.isEmpty()) { // if we already have atoms stored, we need to unify conflicting non-ground atoms: - final Map mapExistingAtomToConflictUnifier = new HashMap<>(); + final Map mapExistingAtomToConflictUnifier = new HashMap<>(); for (int literal : antecedent) { final Literal nonGroundLiteral = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood); final Atom nonGroundAtom = nonGroundLiteral.getAtom(); @@ -320,7 +321,7 @@ private boolean digestOriginalNonGroundNoGood(Antecedent antecedent, int implied for (Map.Entry existingAtomToConflictUnifier : mapExistingAtomToConflictUnifier.entrySet()) { final int existingAtom = existingAtomToConflictUnifier.getKey(); final Unifier mergedUnifier = Unifier.mergeIntoLeft(existingAtomToConflictUnifier.getValue(), unifier); - mapToNonGroundAtoms.computeIfPresent(existingAtom, (a,n) -> n.substitute(mergedUnifier)); + mapToNonGroundAtoms.computeIfPresent(existingAtom, (a, n) -> n.substitute(mergedUnifier)); // TODO: should this actually be done for ALL existing atoms ?! } } From 2c33c2168a760a1b97af6f073fa2cb5f47c84ba5 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 8 Apr 2020 09:47:46 +0200 Subject: [PATCH 34/55] Apply conflict unifier to whole non-ground nogood --- .../learning/NonGroundConflictNoGoodLearner.java | 14 +++++--------- .../NonGroundConflictNoGoodLearnerTest.java | 14 ++++++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 7ab43b53d..d0aae36b7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -46,6 +46,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import static at.ac.tuwien.kr.alpha.Util.collectionToIntArray; import static at.ac.tuwien.kr.alpha.Util.intArrayToLinkedHashSet; @@ -299,7 +300,7 @@ private boolean digestOriginalNonGroundNoGood(Antecedent antecedent, int implied assert (impliedLiteral == atomToLiteral(FALSUM)) == mapToNonGroundAtoms.isEmpty(); // impliedLiteral is only FALSUM for violated nogood if (!mapToNonGroundAtoms.isEmpty()) { // if we already have atoms stored, we need to unify conflicting non-ground atoms: - final Map mapExistingAtomToConflictUnifier = new HashMap<>(); + final Unifier conflictUnifier = new Unifier(); for (int literal : antecedent) { final Literal nonGroundLiteral = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood); final Atom nonGroundAtom = nonGroundLiteral.getAtom(); @@ -313,17 +314,12 @@ private boolean digestOriginalNonGroundNoGood(Antecedent antecedent, int implied // the current nogood uses two variable names for what should be the same variable. // Therefore, additional unification may be necessary in the current nogood: // all atoms that are substituted in the conflicting atom must also be substituted in the existing atom - final Unifier conflictUnifier = new Unifier(); conflictUnifier.unify(existingNonGroundAtom, nonGroundAtom); - mapExistingAtomToConflictUnifier.put(atomOf(literal), conflictUnifier); } } - for (Map.Entry existingAtomToConflictUnifier : mapExistingAtomToConflictUnifier.entrySet()) { - final int existingAtom = existingAtomToConflictUnifier.getKey(); - final Unifier mergedUnifier = Unifier.mergeIntoLeft(existingAtomToConflictUnifier.getValue(), unifier); - mapToNonGroundAtoms.computeIfPresent(existingAtom, (a, n) -> n.substitute(mergedUnifier)); - // TODO: should this actually be done for ALL existing atoms ?! - } + final Unifier mergedUnifier = Unifier.mergeIntoLeft(conflictUnifier, unifier); + mapToNonGroundAtoms.replaceAll((a, n) -> n.substitute(mergedUnifier)); + additionalNonGroundLiterals = additionalNonGroundLiterals.stream().map(n -> n.substitute(mergedUnifier)).collect(Collectors.toCollection(LinkedHashSet::new)); } for (int literal : antecedent) { final Atom unifiedNonGroundAtom = findNonGroundLiteral(literal, originalNoGood, nonGroundNoGood).getAtom().substitute(unifier); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java index 2602629ae..f64508396 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearnerTest.java @@ -177,7 +177,7 @@ public void groundExampleFromSatisfiabilityHandbook() { /** * Tests non-ground learning on the following example:
    * Nogood 1: { -(col(1,g), +(_R_("0", "{X->1,Col->g}") }, non-ground: { -(col(X,Col), +(_R_("0", (X,Col)) }
    - * Nogood 2: { -(col(0,g)), +(col(1,g)) }, non-ground: { -(col(Y,C)), +(col(X,Col)), +(Y=X-1) }
    + * Nogood 2: { -(col(0,g)), +(col(1,g)) }, non-ground: { -(col(Y,Col)), +(col(X,Col)), +(Y=X-1) }
    * Nogood 3: { +(col(1,g)), +(col(0,g)) }, non-ground: { +(col(N,C)), +(col(M,C)), +(M * When nogood 3 is violated, it is first resolved with nogood 2, learning a nogood at the first UIP, * and then with nogood 1, learning another nogood at the second UIP. @@ -209,6 +209,7 @@ public void smallNonGroundExample() { final Atom nonGroundAtomColYCol = new BasicAtom(predCol, varY, varCol); final Atom nonGroundAtomColMC = new BasicAtom(predCol, varM, varC); final Atom nonGroundAtomColNC = new BasicAtom(predCol, varN, varC); + final Atom nonGroundAtomColXC = new BasicAtom(predCol, varX, varC); final Atom unrelated = new BasicAtom(predUnrelated, varX, varCol); final Rule rule = new Rule(new DisjunctiveHead(singletonList(nonGroundAtomColXCol)), singletonList(unrelated.toLiteral())); @@ -278,12 +279,14 @@ public void smallNonGroundExample() { assert conflictAnalysisResult.learnedNoGood != null; assertEquals(expectedLearnedNoGood1, intArrayToLinkedHashSet(conflictAnalysisResult.learnedNoGood.asAntecedent().getReasonLiterals())); - final Literal nonGroundComparisonLiteralMEqualsNMinus1 = new ComparisonAtom(varM, ArithmeticTerm.getInstance(varN, MINUS, const1), EQ).toLiteral(); + final Literal nonGroundComparisonLiteralMLessThanX = new ComparisonAtom(varM, varX, LT).toLiteral(); + final Literal nonGroundComparisonLiteralMEqualsXMinus1 = new ComparisonAtom(varM, ArithmeticTerm.getInstance(varX, MINUS, const1), EQ).toLiteral(); final NoGoodGenerator.CollectedLiterals positiveCollectedLiteralsLearned1 = new NoGoodGenerator.CollectedLiterals( new ArrayList<>(expectedLearnedNoGood1), - singletonList(nonGroundAtomColNC.toLiteral()), - asList(nonGroundComparisonLiteralMLessThanN, nonGroundComparisonLiteralMEqualsNMinus1), + singletonList(nonGroundAtomColXC.toLiteral()), + asList(nonGroundComparisonLiteralMLessThanX, nonGroundComparisonLiteralMEqualsXMinus1), + // TODO: comparison literals could be removed by learning algorithm because they involve variables that are not used anywhere else emptyList() ); final NoGoodGenerator.CollectedLiterals negativeCollectedLiteralsLearned1 = new NoGoodGenerator.CollectedLiterals(); @@ -299,14 +302,13 @@ public void smallNonGroundExample() { assertEquals(expectedLearnedNoGood2, intArrayToLinkedHashSet(learnedNoGood2.asAntecedent().getReasonLiterals())); final Unifier ruleUnifier = new Unifier(); - ruleUnifier.put(varX, varN); ruleUnifier.put(varCol, varC); final RuleAtom nonGroundAtomRenamedRule = nonGroundAtomR.substitute(ruleUnifier); final NoGoodGenerator.CollectedLiterals positiveCollectedLiteralsLearned2 = new NoGoodGenerator.CollectedLiterals( new ArrayList<>(expectedLearnedNoGood2), singletonList(nonGroundAtomRenamedRule.toLiteral()), - asList(nonGroundComparisonLiteralMLessThanN, nonGroundComparisonLiteralMEqualsNMinus1), + asList(nonGroundComparisonLiteralMLessThanX, nonGroundComparisonLiteralMEqualsXMinus1), emptyList() ); final NoGoodGenerator.CollectedLiterals negativeCollectedLiteralsLearned2 = new NoGoodGenerator.CollectedLiterals(); From b4cdc6b72c5a48ecb8c609cccc4cb7d0e0870faa Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 8 Apr 2020 11:59:44 +0200 Subject: [PATCH 35/55] Implement NonGroundNoGood#equals() and hashCode() and compareTo --- .../kr/alpha/common/NonGroundNoGood.java | 74 +++++++++++++------ .../kr/alpha/common/UniqueVariableNames.java | 9 +-- .../tuwien/kr/alpha/common/atoms/Literal.java | 9 ++- .../kr/alpha/common/terms/IntervalTerm.java | 47 +++++++++++- .../ac/tuwien/kr/alpha/common/terms/Term.java | 4 +- .../structure/AnalyzeUnjustified.java | 61 +++++++++++++-- .../kr/alpha/common/NonGroundNoGoodTest.java | 55 ++++++++++++++ .../alpha/common/UniqueVariableNamesTest.java | 22 +++--- 8 files changed, 232 insertions(+), 49 deletions(-) create mode 100644 src/test/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGoodTest.java diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index cc00481e2..d0109087f 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -33,6 +33,7 @@ import at.ac.tuwien.kr.alpha.solver.Antecedent; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -64,56 +65,75 @@ */ public class NonGroundNoGood implements NoGoodInterface { - protected final List literals; + protected final Literal[] literals; + private final Literal[] sortedLiterals; private final boolean head; private final Type type; - public NonGroundNoGood(List literals) { + public NonGroundNoGood(Literal... literals) { this(STATIC, literals, false); } - public NonGroundNoGood(Type type, List literals) { + public NonGroundNoGood(Type type, Literal... literals) { this(type, literals, false); } - NonGroundNoGood(Type type, List literals, boolean head) { + NonGroundNoGood(Type type, Literal[] literals, boolean head) { this.type = type; this.head = head; - if (head && !literals.get(HEAD).isNegated()) { + if (head && !literals[HEAD].isNegated()) { throw oops("Head is not negative"); } - this.literals = literals; + + this.literals = Arrays.copyOf(literals, literals.length); + // note: literals are not sorted here (in contrast to ground NoGood) because of the assumption that the literals + // appear in the same order as in the corresponding ground nogood + // however, we maintain a second array of literals that is sorted for comparison purposes: + // (the following code is duplicated from the constructor of NoGood) + + Arrays.sort(literals, head ? 1 : 0, literals.length); + + int shift = 0; + for (int i = 1; i < literals.length; i++) { + if (literals[i - 1] == literals[i]) { // check for duplicate + shift++; + } + literals[i - shift] = literals[i]; // Remove duplicates in place by shifting remaining literals. + } + + // copy-shrink array if needed. + this.sortedLiterals = shift <= 0 ? literals : Arrays.copyOf(literals, literals.length - shift); } public static NonGroundNoGood forGroundNoGood(NoGood groundNoGood, Map atomMapping) { - final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); - return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); + return new NonGroundNoGood(groundNoGood.getType(), literalsForGroundNoGood(groundNoGood, atomMapping), groundNoGood.hasHead()); } public static NonGroundNoGood fromBody(NoGood groundNoGood, NoGoodGenerator.CollectedLiterals posLiterals, NoGoodGenerator.CollectedLiterals negLiterals, Map atomMapping) { - final List literals = literalsForGroundNoGood(groundNoGood, atomMapping); + final List literals = new ArrayList<>(Arrays.asList(literalsForGroundNoGood(groundNoGood, atomMapping))); literals.addAll(posLiterals.getSkippedFacts()); literals.addAll(posLiterals.getSkippedFixedInterpretationLiterals()); literals.addAll(negLiterals.getSkippedFacts().stream().map(Literal::negate).collect(Collectors.toList())); literals.addAll(negLiterals.getSkippedFixedInterpretationLiterals().stream().map(Literal::negate).collect(Collectors.toList())); - return new NonGroundNoGood(groundNoGood.getType(), literals, groundNoGood.hasHead()); + return new NonGroundNoGood(groundNoGood.getType(), literals.toArray(new Literal[]{}), groundNoGood.hasHead()); } public static NonGroundNoGood learnt(Collection literals) { - return new NonGroundNoGood(LEARNT, new ArrayList<>(literals)); + return new NonGroundNoGood(LEARNT, literals.toArray(new Literal[]{})); } - private static List literalsForGroundNoGood(NoGood groundNoGood, Map atomMapping) { - final List literals = new ArrayList<>(groundNoGood.size()); - for (int groundLiteral : groundNoGood) { - literals.add(atomMapping.get(atomOf(groundLiteral)).toLiteral(isPositive(groundLiteral))); + private static Literal[] literalsForGroundNoGood(NoGood groundNoGood, Map atomMapping) { + final Literal[] literals = new Literal[groundNoGood.size()]; + for (int i = 0; i < groundNoGood.size(); i++) { + final int groundLiteral = groundNoGood.getLiteral(i); + literals[i] = atomMapping.get(atomOf(groundLiteral)).toLiteral(isPositive(groundLiteral)); } return literals; } @Override public Literal getLiteral(int index) { - return literals.get(index); + return literals[index]; } @Override @@ -123,7 +143,7 @@ public boolean hasHead() { @Override public int size() { - return literals.size(); + return literals.length; } public Set getOccurringVariables() { @@ -146,7 +166,19 @@ public Type getType() { @Override public Iterator iterator() { - return literals.iterator(); + return new Iterator() { + private int i; + + @Override + public boolean hasNext() { + return literals.length > i; + } + + @Override + public Literal next() { + return literals[i++]; + } + }; } @Override @@ -159,18 +191,18 @@ public boolean equals(Object o) { } NonGroundNoGood that = (NonGroundNoGood) o; return head == that.head && - literals.equals(that.literals) && + Arrays.equals(sortedLiterals, that.sortedLiterals) && type == that.type; } @Override public int hashCode() { - return Objects.hash(literals, head, type); + return Objects.hash(Arrays.hashCode(sortedLiterals), head, type); } @Override public String toString() { - final List stringLiterals = new ArrayList<>(literals.size()); + final List stringLiterals = new ArrayList<>(literals.length); for (Literal literal : literals) { stringLiterals.add((literal.isNegated() ? "-" : "+") + "(" + literal.getAtom() + ")"); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java index 9f7269c20..b6ebb20f4 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNames.java @@ -29,9 +29,7 @@ import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -44,9 +42,10 @@ public NonGroundNoGood makeVariableNamesUnique(NonGroundNoGood noGood) { if (unifier.isEmpty()) { return noGood; } - final List newLiterals = new ArrayList<>(noGood.size()); - for (Literal literal : noGood) { - newLiterals.add(literal.substitute(unifier)); + final Literal[] newLiterals = new Literal[noGood.size()]; + for (int i = 0; i < noGood.size(); i++) { + final Literal literal = noGood.getLiteral(i); + newLiterals[i] = literal.substitute(unifier); } return new NonGroundNoGood(noGood.getType(), newLiterals, noGood.hasHead()); diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java index 1d3d13931..5a713c796 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/atoms/Literal.java @@ -41,7 +41,7 @@ /** * A potentially negated {@link Atom} */ -public abstract class Literal { +public abstract class Literal implements Comparable { protected final Atom atom; protected final boolean positive; @@ -155,4 +155,11 @@ public int hashCode() { return 12 * atom.hashCode() + (positive ? 1 : 0); } + @Override + public int compareTo(Literal o) { + if (this.positive == o.positive) { + return this.getAtom().compareTo(o.getAtom()); + } + return Boolean.compare(this.positive, o.positive); + } } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java index 7b82db741..0e66b75db 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/IntervalTerm.java @@ -1,3 +1,31 @@ +/* + * Copyright (c) 2017, 2018, 2020, 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; @@ -11,7 +39,6 @@ /** * An IntervalTerm represents the shorthand notation for a set of rules where all elements in this interval occur once, e.g., fact(2..5). * An IntervalTerm is a meta-term and the grounder must replace it with its corresponding set of facts or rules. - * Copyright (c) 2017, the Alpha Team. */ public class IntervalTerm extends Term { private static final Interner INTERNER = new Interner<>(); @@ -115,7 +142,23 @@ public int hashCode() { @Override public int compareTo(Term o) { - throw new UnsupportedOperationException("Intervals cannot be compared."); + if (this == o) { + return 0; + } + + if (!(o instanceof IntervalTerm)) { + return super.compareTo(o); + } + + IntervalTerm other = (IntervalTerm)o; + + int result = lowerBoundTerm.compareTo(other.lowerBoundTerm); + + if (result != 0) { + return result; + } + + return upperBoundTerm.compareTo(other.upperBoundTerm); } @Override diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java index 5a233146d..106a1d733 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/Term.java @@ -51,8 +51,10 @@ private static int priority(Term term) { return 2; } else if (clazz.equals(VariableTerm.class)) { return 3; + } else if (clazz.equals(ArithmeticTerm.class)) { + return 4; } - throw new UnsupportedOperationException("Can only compare constant term, function terms and variable terms among each other."); + throw new UnsupportedOperationException("Can only compare constant term, function terms, variable terms, and arithmetic terms among each other."); } @Override diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustified.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustified.java index 8f17cbe11..eefaa4dc2 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustified.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/structure/AnalyzeUnjustified.java @@ -1,19 +1,64 @@ +/* + * Copyright (c) 2018-2020, 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.structure; -import at.ac.tuwien.kr.alpha.common.*; -import at.ac.tuwien.kr.alpha.common.atoms.*; -import at.ac.tuwien.kr.alpha.grounder.*; +import at.ac.tuwien.kr.alpha.common.Assignment; +import at.ac.tuwien.kr.alpha.common.AtomStore; +import at.ac.tuwien.kr.alpha.common.DisjunctiveHead; +import at.ac.tuwien.kr.alpha.common.Predicate; +import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.Unification; +import at.ac.tuwien.kr.alpha.common.Unifier; +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.ComparisonLiteral; +import at.ac.tuwien.kr.alpha.common.atoms.FixedInterpretationLiteral; +import at.ac.tuwien.kr.alpha.common.atoms.Literal; +import at.ac.tuwien.kr.alpha.grounder.Instance; +import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; import static at.ac.tuwien.kr.alpha.Util.oops; -/** - * Copyright (c) 2018, the Alpha Team. - */ public class AnalyzeUnjustified { private static final Logger LOGGER = LoggerFactory.getLogger(AnalyzeUnjustified.class); private final ProgramAnalysis programAnalysis; @@ -304,7 +349,7 @@ private List rulesHeadUnifyingWith(Atom p) { List rulesWithUnifier = new ArrayList<>(); Predicate predicate = p.getPredicate(); // Check if literal is built-in with a fixed interpretation. - if (p instanceof FixedInterpretationLiteral) { + if (p.toLiteral() instanceof FixedInterpretationLiteral) { return Collections.emptyList(); } ArrayList definingRulesAndFacts = new ArrayList<>(); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGoodTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGoodTest.java new file mode 100644 index 000000000..33f7b3c23 --- /dev/null +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGoodTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 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.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.VariableTerm; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link NonGroundNoGood} + */ +public class NonGroundNoGoodTest { + + @Test + public void testEqualsIgnoreOrderOfLiterals() { + final Predicate predA = Predicate.getInstance("a", 1); + final Predicate predB = Predicate.getInstance("b", 1); + final VariableTerm varX = VariableTerm.getInstance("X"); + final VariableTerm varY = VariableTerm.getInstance("Y"); + final Literal literalAX = new BasicAtom(predA, varX).toLiteral(); + final Literal literalBY = new BasicAtom(predB, varY).toLiteral(); + final NonGroundNoGood ng1 = new NonGroundNoGood(literalAX, literalBY); + final NonGroundNoGood ng2 = new NonGroundNoGood(literalBY, literalAX); + assertEquals(ng1, ng2); + assertEquals(ng1.hashCode(), ng2.hashCode()); + } + +} diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java index 2eea8aded..abf703ed4 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/UniqueVariableNamesTest.java @@ -38,9 +38,6 @@ import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.List; - import static at.ac.tuwien.kr.alpha.common.ComparisonOperator.EQ; import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.LEARNT; import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; @@ -69,16 +66,19 @@ public void testRenameInSecondNoGood() { final VariableTerm varY2 = VariableTerm.getInstance("Y_2"); final VariableTerm varZ = VariableTerm.getInstance("Z"); - final List literals1 = new ArrayList<>(); - literals1.add(lit(true, predA, varX, varY)); - literals1.add(lit(false, predA, varX, varY1)); - literals1.add(comp(varY1, plus(varY, ConstantTerm.getInstance(1)), EQ)); + final Literal[] literals1 = new Literal[]{ + lit(true, predA, varX, varY), + lit(false, predA, varX, varY1), + comp(varY1, plus(varY, ConstantTerm.getInstance(1)), EQ) + }; - final List literals2 = new ArrayList<>(); - literals2.add(lit(true, predB, varY, varZ)); + final Literal[] literals2 = new Literal[]{ + lit(true, predB, varY, varZ) + }; - final List expectedModifiedLiterals2 = new ArrayList<>(); - expectedModifiedLiterals2.add(lit(true, predB, varY2, varZ)); + final Literal[] expectedModifiedLiterals2 = new Literal[]{ + lit(true, predB, varY2, varZ) + }; final NonGroundNoGood noGood1 = new NonGroundNoGood(LEARNT, literals1, false); final NonGroundNoGood noGood2 = new NonGroundNoGood(STATIC, literals2, false); From b27e283d4c2080e614a89e92716bc936bdb22768 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 9 Apr 2020 10:11:58 +0200 Subject: [PATCH 36/55] Enable more kinds of unifiers (esp. try in other direction if first direction fails) --- .../tuwien/kr/alpha/common/Substitution.java | 18 +++--------------- .../at/ac/tuwien/kr/alpha/common/Unifier.java | 12 +++++++++--- .../NonGroundConflictNoGoodLearner.java | 11 +++++++++-- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java index ddcffa942..027e99b89 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java @@ -117,13 +117,7 @@ public boolean unifyTerms(Term termLeft, Term termRight, boolean allowNonGroundT if (termLeft == termRight) { return true; } else if (termLeft instanceof ConstantTerm) { - if (isRightTermGround) { - // Since right term is ground, both terms differ - return false; - } else { - throw new UnsupportedOperationException(); - // TODO: substitute right term to unify with left term - } + return false; } else if (termLeft instanceof VariableTerm) { VariableTerm variableTermLeft = (VariableTerm)termLeft; if (isRightTermGround) { @@ -134,15 +128,9 @@ public boolean unifyTerms(Term termLeft, Term termRight, boolean allowNonGroundT // Variable is already bound, return true if binding is the same as the current ground term. return termRight == bound; } - - put(variableTermLeft, termRight); - return true; - } else { - assert termRight instanceof VariableTerm; - put(variableTermLeft, termRight); // see Unifier#put - // TODO is this right, or should we check if the variable is already bound? - return true; } + put(variableTermLeft, termRight); + return true; } else if (termLeft instanceof FunctionTerm && termRight instanceof FunctionTerm) { // Both terms are function terms FunctionTerm ftNonGround = (FunctionTerm) termLeft; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java index 7ee5afc76..a883e2170 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java @@ -49,7 +49,13 @@ public Unifier extendWith(Substitution extension) { return this; } - public void unify(Atom modifiableAtom, Atom unmodifiableAtom) { + /** + * Unifies the given atoms if possible. Unification will only substitute variables that occur in {@code modifiableAtom}. + * @param modifiableAtom an atom that can be modified by substitution + * @param unmodifiableAtom an atom that cannot be modified by substitution + * @return {@code true} iff unification was successful + */ + public boolean unify(Atom modifiableAtom, Atom unmodifiableAtom) { if (modifiableAtom == null || unmodifiableAtom == null) { throw new IllegalArgumentException("Atom must not be null."); } @@ -59,11 +65,11 @@ public void unify(Atom modifiableAtom, Atom unmodifiableAtom) { int i = 0; for (Term modifiableTerm : modifiableAtom.getTerms()) { if (!unifyTerms(modifiableTerm, unmodifiableAtom.getTerms().get(i), true)) { - throw new IllegalArgumentException("Atoms cannot be unified: " + modifiableAtom + ", " + unmodifiableAtom); + return false; } i++; } - + return true; } @Override diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index d0aae36b7..1e98826e7 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -308,13 +308,20 @@ private boolean digestOriginalNonGroundNoGood(Antecedent antecedent, int implied if (literal == negateLiteral(impliedLiteral)) { // this is the literal used for resolution assert existingNonGroundAtom != null; - unifier.unify(nonGroundAtom, existingNonGroundAtom); + if (!unifier.unify(nonGroundAtom, existingNonGroundAtom)) { + // try unification in other direction (this time with conflictUnifier, because this will be applied to original nogood): + if (!conflictUnifier.unify(existingNonGroundAtom, nonGroundAtom)) { + throw new IllegalArgumentException("Cannot unify existing non-ground atom " + existingNonGroundAtom + " with new atom " + nonGroundAtom); + } + } } else if (existingNonGroundAtom != null) { // this is another atom that already exists in the current nogood. It may be that because of this, // the current nogood uses two variable names for what should be the same variable. // Therefore, additional unification may be necessary in the current nogood: // all atoms that are substituted in the conflicting atom must also be substituted in the existing atom - conflictUnifier.unify(existingNonGroundAtom, nonGroundAtom); + if (!conflictUnifier.unify(existingNonGroundAtom, nonGroundAtom)) { + throw new IllegalArgumentException("Cannot unify existing non-ground atom " + existingNonGroundAtom + " with conflicting atom " + nonGroundAtom); + } } } final Unifier mergedUnifier = Unifier.mergeIntoLeft(conflictUnifier, unifier); From edc03ca039ffec8e4159e246c414be74a416b8ef Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Fri, 17 Apr 2020 17:05:34 +0200 Subject: [PATCH 37/55] stop conflict generalisation after 1000 conflicts --- .../java/at/ac/tuwien/kr/alpha/solver/DefaultSolver.java | 8 ++++++-- 1 file changed, 6 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 26bb850a5..842d8d69d 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 @@ -102,7 +102,8 @@ public class DefaultSolver extends AbstractSolver implements SolverMaintainingSt private final PerformanceLog performanceLog; private final boolean conflictGeneralisationEnabled = true; // TODO: make parameterisable - + private static final int STOP_AFTER_BACKJUMPS = 1000; // TODO: make parameterisable + public DefaultSolver(AtomStore atomStore, Grounder grounder, NoGoodStore store, WritableAssignment assignment, Random random, SystemConfig config, HeuristicsConfiguration heuristicsConfiguration) { super(atomStore, grounder); @@ -185,7 +186,10 @@ protected boolean tryAdvance(Consumer action) { // TODO: The violatedNoGood should not be necessary here, but this requires major type changes in heuristics. branchingHeuristic.violatedNoGood(violatedNoGood); if (!afterAllAtomsAssigned) { - if (!learnBackjumpAddFromConflict(conflictCause)) { + if (getNumberOfBackjumps() >= STOP_AFTER_BACKJUMPS) { + LOGGER.info("Stopping conflict generalisation after {} conflicts", STOP_AFTER_BACKJUMPS); + } + if (getNumberOfBackjumps() >= STOP_AFTER_BACKJUMPS || !learnBackjumpAddFromConflict(conflictCause)) { logStats(); return false; } From 1a5660f7e0fdaa20c6caf114b819df1a27b281b3 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 22 Apr 2020 11:13:15 +0200 Subject: [PATCH 38/55] Move SubsitutionTest to common package because tested classes are also in common package --- .../kr/alpha/{grounder => common}/SubstitutionTest.java | 5 +++-- .../at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java | 4 ++++ .../ac/tuwien/kr/alpha/{grounder => common}/UnifierTest.java | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) rename src/test/java/at/ac/tuwien/kr/alpha/{grounder => common}/SubstitutionTest.java (95%) create mode 100644 src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java rename src/test/java/at/ac/tuwien/kr/alpha/{grounder => common}/UnifierTest.java (94%) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java similarity index 95% rename from src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java rename to src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java index 89093c149..9aa6a685b 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/SubstitutionTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java @@ -29,6 +29,7 @@ import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Rule; +import at.ac.tuwien.kr.alpha.common.SubstitutionTestUtil; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.BasicLiteral; import at.ac.tuwien.kr.alpha.common.atoms.Literal; @@ -96,7 +97,7 @@ public void groundAndPrintRule() { Substitution substitution = new Substitution(); substitution.unifyTerms(X, A); substitution.unifyTerms(Y, B); - String printedString = NaiveGrounder.groundAndPrintRule(nonGroundRule, substitution); + String printedString = SubstitutionTestUtil.groundAndPrintRule(nonGroundRule, substitution); assertEquals("x :- p(a, b), not q(a, b).", printedString); } @@ -130,7 +131,7 @@ private void groundLiteralToString(boolean negated) { Substitution substitution = new Substitution(); substitution.unifyTerms(X, A); substitution.unifyTerms(Y, B); - String printedString = NaiveGrounder.groundLiteralToString(atom.toLiteral(!negated), substitution, true); + String printedString = SubstitutionTestUtil.groundLiteralToString(atom.toLiteral(!negated), substitution, true); assertEquals((negated ? "not " : "") + "p(a, b)", printedString); } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java new file mode 100644 index 000000000..83c100710 --- /dev/null +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java @@ -0,0 +1,4 @@ +package at.ac.tuwien.kr.alpha.common; + +public class SubstitutionTestUtil { +} diff --git a/src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java similarity index 94% rename from src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java rename to src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java index 4ea4dd92c..6fa24c033 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/grounder/UnifierTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java @@ -3,6 +3,7 @@ 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.SubstitutionTestUtil; 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.BasicLiteral; @@ -99,7 +100,7 @@ public void groundAndPrintRule() { Substitution substitution = new Unifier(); substitution.unifyTerms(X, A); substitution.unifyTerms(Y, B); - String printedString = NaiveGrounder.groundAndPrintRule(nonGroundRule, substitution); + String printedString = SubstitutionTestUtil.groundAndPrintRule(nonGroundRule, substitution); assertEquals("x :- p(a, b), not q(a, b).", printedString); } @@ -133,7 +134,7 @@ private void groundLiteralToString(boolean negated) { Substitution substitution = new Unifier(); substitution.unifyTerms(X, A); substitution.unifyTerms(Y, B); - String printedString = NaiveGrounder.groundLiteralToString(atom.toLiteral(!negated), substitution, true); + String printedString = SubstitutionTestUtil.groundLiteralToString(atom.toLiteral(!negated), substitution, true); assertEquals((negated ? "not " : "") + "p(a, b)", printedString); } } \ No newline at end of file From b3484a00494435f59e7f05d4af2915b64b45fc59 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 22 Apr 2020 11:15:12 +0200 Subject: [PATCH 39/55] Move SubsitutionTest to common package because tested classes are also in common package --- .../kr/alpha/grounder/NaiveGrounder.java | 25 -------- .../kr/alpha/common/SubstitutionTest.java | 22 +++---- .../kr/alpha/common/SubstitutionTestUtil.java | 58 +++++++++++++++++++ .../tuwien/kr/alpha/common/UnifierTest.java | 40 ++++++++++--- 4 files changed, 99 insertions(+), 46 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 c379e5f88..9bfa9f5d2 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 @@ -704,31 +704,6 @@ public void forgetAssignment(int[] atomIds) { throw new UnsupportedOperationException("Forgetting assignments is not implemented"); } - public static String groundAndPrintRule(NonGroundRule rule, Substitution substitution) { - StringBuilder ret = new StringBuilder(); - if (!rule.isConstraint()) { - Atom groundHead = rule.getHeadAtom().substitute(substitution); - ret.append(groundHead.toString()); - } - ret.append(" :- "); - boolean isFirst = true; - for (Atom bodyAtom : rule.getBodyAtomsPositive()) { - ret.append(groundLiteralToString(bodyAtom.toLiteral(), substitution, isFirst)); - isFirst = false; - } - for (Atom bodyAtom : rule.getBodyAtomsNegative()) { - ret.append(groundLiteralToString(bodyAtom.toLiteral(false), substitution, isFirst)); - isFirst = false; - } - ret.append("."); - return ret.toString(); - } - - static String groundLiteralToString(Literal literal, Substitution substitution, boolean isFirst) { - Literal groundLiteral = literal.substitute(substitution); - return (isFirst ? "" : ", ") + groundLiteral.toString(); - } - @Override public NonGroundRule getNonGroundRule(Integer ruleId) { return knownNonGroundRules.get(ruleId); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java index 9aa6a685b..7921966b5 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTest.java @@ -1,19 +1,19 @@ -/** +/* * Copyright (c) 2016-2018, 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 @@ -25,11 +25,8 @@ * 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; +package at.ac.tuwien.kr.alpha.common; -import at.ac.tuwien.kr.alpha.common.Predicate; -import at.ac.tuwien.kr.alpha.common.Rule; -import at.ac.tuwien.kr.alpha.common.SubstitutionTestUtil; import at.ac.tuwien.kr.alpha.common.atoms.BasicAtom; import at.ac.tuwien.kr.alpha.common.atoms.BasicLiteral; import at.ac.tuwien.kr.alpha.common.atoms.Literal; @@ -37,6 +34,8 @@ import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; +import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; +import at.ac.tuwien.kr.alpha.grounder.Substitution; import at.ac.tuwien.kr.alpha.grounder.atoms.RuleAtom; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import org.junit.Test; @@ -46,9 +45,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -/** - * Copyright (c) 2016-2018, the Alpha Team. - */ public class SubstitutionTest { static final ProgramParser PARSER = new ProgramParser(); diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java index 83c100710..8d4518182 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/SubstitutionTestUtil.java @@ -1,4 +1,62 @@ +/** + * Copyright (c) 2016-2020, 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; +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.NonGroundRule; +import at.ac.tuwien.kr.alpha.grounder.Substitution; + public class SubstitutionTestUtil { + + static String groundAndPrintRule(NonGroundRule rule, Substitution substitution) { + StringBuilder ret = new StringBuilder(); + if (!rule.isConstraint()) { + Atom groundHead = rule.getHeadAtom().substitute(substitution); + ret.append(groundHead.toString()); + } + ret.append(" :- "); + boolean isFirst = true; + for (Atom bodyAtom : rule.getBodyAtomsPositive()) { + ret.append(groundLiteralToString(bodyAtom.toLiteral(), substitution, isFirst)); + isFirst = false; + } + for (Atom bodyAtom : rule.getBodyAtomsNegative()) { + ret.append(groundLiteralToString(bodyAtom.toLiteral(false), substitution, isFirst)); + isFirst = false; + } + ret.append("."); + return ret.toString(); + } + + static String groundLiteralToString(Literal literal, Substitution substitution, boolean isFirst) { + Literal groundLiteral = literal.substitute(substitution); + return (isFirst ? "" : ", ") + groundLiteral.toString(); + } } diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java index 6fa24c033..35da92f35 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java @@ -1,9 +1,33 @@ -package at.ac.tuwien.kr.alpha.grounder; +/* + * Copyright (c) 2018, 2020, 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; -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.SubstitutionTestUtil; 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.BasicLiteral; @@ -12,6 +36,9 @@ import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; +import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; +import at.ac.tuwien.kr.alpha.grounder.Substitution; +import at.ac.tuwien.kr.alpha.grounder.Unifier; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; import org.junit.Test; @@ -19,9 +46,6 @@ import static org.junit.Assert.assertEquals; -/** - * Copyright (c) 2018, the Alpha Team. - */ public class UnifierTest extends SubstitutionTest { @Test From fafecf16f20720d0bca6793aa3d7a967674bb613 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 22 Apr 2020 11:50:52 +0200 Subject: [PATCH 40/55] move method parseTerm from Substitution to ProgramPartParser --- .../kr/alpha/grounder/Substitution.java | 30 ++------- .../grounder/parser/ProgramPartParser.java | 64 +++++++++++++++++++ 2 files changed, 70 insertions(+), 24 deletions(-) create mode 100644 src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java 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 b859cb501..cd2d0a710 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-2019, the Alpha Team. +/* + * Copyright (c) 2016-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -27,21 +27,14 @@ */ package at.ac.tuwien.kr.alpha.grounder; -import at.ac.tuwien.kr.alpha.antlr.ASPCore2Lexer; -import at.ac.tuwien.kr.alpha.antlr.ASPCore2Parser; import at.ac.tuwien.kr.alpha.common.atoms.Atom; 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.FunctionTerm; 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.ParseTreeVisitor; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.misc.ParseCancellationException; +import at.ac.tuwien.kr.alpha.grounder.parser.ProgramPartParser; -import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.TreeMap; @@ -49,7 +42,8 @@ import static at.ac.tuwien.kr.alpha.Util.oops; public class Substitution { - private static final ParseTreeVisitor VISITOR = new ParseTreeVisitor(Collections.emptyMap(), false); + + private static final ProgramPartParser PROGRAM_PART_PARSER = new ProgramPartParser(); protected TreeMap substitution; @@ -198,24 +192,12 @@ public static Substitution fromString(String substitution) { for (String assignment : assignments) { String keyVal[] = assignment.split("->"); VariableTerm variable = VariableTerm.getInstance(keyVal[0]); - Term assignedTerm = parseTerm(keyVal[1]); + Term assignedTerm = PROGRAM_PART_PARSER.parseTerm(keyVal[1]); ret.put(variable, assignedTerm); } return ret; } - private static Term parseTerm(String s) { - try { - final ASPCore2Parser parser = new ASPCore2Parser(new CommonTokenStream(new ASPCore2Lexer(CharStreams.fromString(s)))); - return (Term)VISITOR.visit(parser.term()); - } catch (RecognitionException | ParseCancellationException e) { - // If there were issues parsing the given string, we - // throw something that suggests that the input string - // is malformed. - throw new IllegalArgumentException("Could not parse term.", e); - } - } - @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java new file mode 100644 index 000000000..88e783b22 --- /dev/null +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018-2020, 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.parser; + +import at.ac.tuwien.kr.alpha.antlr.ASPCore2Lexer; +import at.ac.tuwien.kr.alpha.antlr.ASPCore2Parser; +import at.ac.tuwien.kr.alpha.common.terms.Term; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.misc.ParseCancellationException; + +import java.util.Collections; + +/** + * A parser that, in contrast to {@link ProgramParser}, does not parse full programs but only program parts like + * atoms, terms and such. + */ +public class ProgramPartParser { + private final ParseTreeVisitor visitor = new ParseTreeVisitor(Collections.emptyMap(), false); + + public Term parseTerm(String s) { + final ASPCore2Parser parser = new ASPCore2Parser(new CommonTokenStream(new ASPCore2Lexer(CharStreams.fromString(s)))); + return (Term)parse(parser.term()); + } + + private Object parse(ParserRuleContext context) { + try { + return visitor.visit(context); + } catch (RecognitionException | ParseCancellationException e) { + // If there were issues parsing the given string, we + // throw something that suggests that the input string + // is malformed. + throw new IllegalArgumentException("Could not parse term.", e); + } + } +} From 80b0cf199914d19c25e58daf36930264c0cddba0 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 22 Apr 2020 14:50:53 +0200 Subject: [PATCH 41/55] use ProgramPartParser to construct literals for unit tests --- .../grounder/parser/ProgramPartParser.java | 18 ++- .../java/at/ac/tuwien/kr/alpha/TestUtil.java | 13 +-- .../kr/alpha/grounder/NaiveGrounderTest.java | 108 +++++++++--------- .../grounder/RuleGroundingOrderTest.java | 40 ++++--- 4 files changed, 99 insertions(+), 80 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java index 88e783b22..57648a28e 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java @@ -30,6 +30,8 @@ import at.ac.tuwien.kr.alpha.antlr.ASPCore2Lexer; import at.ac.tuwien.kr.alpha.antlr.ASPCore2Parser; +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 org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -47,10 +49,24 @@ public class ProgramPartParser { private final ParseTreeVisitor visitor = new ParseTreeVisitor(Collections.emptyMap(), false); public Term parseTerm(String s) { - final ASPCore2Parser parser = new ASPCore2Parser(new CommonTokenStream(new ASPCore2Lexer(CharStreams.fromString(s)))); + final ASPCore2Parser parser = getASPCore2Parser(s); return (Term)parse(parser.term()); } + public BasicAtom parseBasicAtom(String s) { + final ASPCore2Parser parser = getASPCore2Parser(s); + return (BasicAtom)parse(parser.classical_literal()); + } + + public Literal parseLiteral(String s) { + final ASPCore2Parser parser = getASPCore2Parser(s); + return (Literal)parse(parser.naf_literal()); + } + + private ASPCore2Parser getASPCore2Parser(String s) { + return new ASPCore2Parser(new CommonTokenStream(new ASPCore2Lexer(CharStreams.fromString(s)))); + } + private Object parse(ParserRuleContext context) { try { return visitor.visit(context); 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 1fa27b4a2..ac8eb6e2a 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/TestUtil.java @@ -1,5 +1,5 @@ -/** - * Copyright (c) 2019 Siemens AG +/* + * Copyright (c) 2019-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,6 @@ 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; import at.ac.tuwien.kr.alpha.common.terms.Term; import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; @@ -65,14 +64,6 @@ public static Atom atom(String predicateName, int... termInts) { } return new BasicAtom(Predicate.getInstance(predicateName, terms.length), terms); } - - public static Literal literal(String predicateName, String... termStrings) { - return atom(predicateName, termStrings).toLiteral(); - } - - public static Literal literal(String predicateName, int... termInts) { - return atom(predicateName, termInts).toLiteral(); - } 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 f2aeda83f..be40472ef 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-2019 Siemens AG +/* + * Copyright (c) 2018-2020 Siemens AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,13 +30,12 @@ import at.ac.tuwien.kr.alpha.common.AtomStoreImpl; import at.ac.tuwien.kr.alpha.common.Literals; import at.ac.tuwien.kr.alpha.common.NoGood; -import at.ac.tuwien.kr.alpha.common.Predicate; import at.ac.tuwien.kr.alpha.common.Program; -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.grounder.parser.ProgramPartParser; import at.ac.tuwien.kr.alpha.solver.ThriceTruth; import at.ac.tuwien.kr.alpha.solver.TrailAssignment; import org.junit.Before; @@ -50,7 +49,6 @@ import java.util.Map; import static at.ac.tuwien.kr.alpha.TestUtil.atom; -import static at.ac.tuwien.kr.alpha.TestUtil.literal; import static at.ac.tuwien.kr.alpha.solver.ThriceTruth.TRUE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -65,7 +63,15 @@ * 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(); + private static final ProgramParser PROGRAM_PARSER = new ProgramParser(); + private static final ProgramPartParser PROGRAM_PART_PARSER = new ProgramPartParser(); + + final Literal litP1X = PROGRAM_PART_PARSER.parseLiteral("p1(X)"); + final Literal litP2X = PROGRAM_PART_PARSER.parseLiteral("p2(X)"); + final Literal litQ2Y = PROGRAM_PART_PARSER.parseLiteral("q2(Y)"); + final Literal litQ1Y = PROGRAM_PART_PARSER.parseLiteral("q1(Y)"); + final Literal litAX = PROGRAM_PART_PARSER.parseLiteral("a(X)"); + final Literal litA1 = PROGRAM_PART_PARSER.parseLiteral("a(1)"); @Before public void resetRuleIdGenerator() { @@ -78,15 +84,15 @@ public void resetRuleIdGenerator() { */ @Test public void groundRuleAlreadyGround() { - Program program = PARSER.parse("a :- not b. " + Program program = PROGRAM_PARSER.parse("a :- not b. " + "b :- not a. " + "c :- b."); AtomStore atomStore = new AtomStoreImpl(); Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true); Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore)); - 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)))); + int litCNeg = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("c")), false); + int litB = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("b"))); assertExistsNoGoodContaining(noGoods.values(), litCNeg); assertExistsNoGoodContaining(noGoods.values(), litB); } @@ -97,7 +103,7 @@ public void groundRuleAlreadyGround() { */ @Test public void groundRuleWithLongerBodyAlreadyGround() { - Program program = PARSER.parse("a :- not b. " + Program program = PROGRAM_PARSER.parse("a :- not b. " + "b :- not a. " + "c :- b. " + "d :- b, c. "); @@ -105,10 +111,10 @@ public void groundRuleWithLongerBodyAlreadyGround() { AtomStore atomStore = new AtomStoreImpl(); Grounder grounder = GrounderFactory.getInstance("naive", program, atomStore, true); Map noGoods = grounder.getNoGoods(new TrailAssignment(atomStore)); - 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); - int litDNeg = Literals.atomToLiteral(atomStore.get(new BasicAtom(Predicate.getInstance("d", 0))), false); + int litANeg = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("a")), false); + int litBNeg = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("b")), false); + int litCNeg = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("c")), false); + int litDNeg = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("d")), false); assertExistsNoGoodContaining(noGoods.values(), litANeg); assertExistsNoGoodContaining(noGoods.values(), litBNeg); assertExistsNoGoodContaining(noGoods.values(), litCNeg); @@ -121,42 +127,42 @@ public void groundRuleWithLongerBodyAlreadyGround() { */ @Test public void groundConstraintAlreadyGround() { - Program program = PARSER.parse("a :- not b. " + Program program = PROGRAM_PARSER.parse("a :- not b. " + "b :- not a. " + ":- b."); AtomStore atomStore = new AtomStoreImpl(); 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)))); + int litB = Literals.atomToLiteral(atomStore.get(PROGRAM_PART_PARSER.parseBasicAtom("b"))); assertTrue(noGoods.containsValue(NoGood.fromConstraint(Collections.singletonList(litB), Collections.emptyList()))); } @Test public void avoidDeadEndsWithPermissiveGrounderHeuristicForP1() { - RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), - Arrays.asList(literal("p2", "X"), literal("q2", "Y"), literal("q1", "Y")), -1, false); + RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(litP1X, + Arrays.asList(litP2X, litQ2Y, litQ1Y), -1, false); testDeadEnd("p1", groundingOrderP1, true); } @Test public void avoidDeadEndsWithPermissiveGrounderHeuristicForQ1() { - RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), - Arrays.asList(literal("q2", "Y"), literal("p2", "X"), literal("p1", "X")), -1, false); + RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(litQ1Y, + Arrays.asList(litQ2Y, litP2X, litP1X), -1, false); testDeadEnd("q1", groundingOrderQ1, true); } @Test public void noDeadEndWithPermissiveGrounderHeuristicForP1() { - RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(literal("p1", "X"), - Arrays.asList(literal("p2", "X"), literal("q1", "Y"), literal("q2", "Y")), -1, false); + RuleGroundingOrder groundingOrderP1 = new RuleGroundingOrder(litP1X, + Arrays.asList(litP2X, litQ1Y, litQ2Y), -1, false); testDeadEnd("p1", groundingOrderP1, true); } @Test public void noDeadEndWithPermissiveGrounderHeuristicForQ1() { - RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(literal("q1", "Y"), - Arrays.asList(literal("q2", "Y"), literal("p1", "X"), literal("p2", "X")), -1, false); + RuleGroundingOrder groundingOrderQ1 = new RuleGroundingOrder(litQ1Y, + Arrays.asList(litQ2Y, litP1X, litP2X), -1, false); testDeadEnd("q1", groundingOrderQ1, true); } @@ -180,7 +186,7 @@ public void noDeadEndWithPermissiveGrounderHeuristicForQ1() { * described above. */ private void testDeadEnd(String predicateNameOfStartingLiteral, RuleGroundingOrder groundingOrder, boolean expectNoGoods) { - Program program = PARSER.parse("p1(1). q1(1). " + Program program = PROGRAM_PARSER.parse("p1(1). q1(1). " + "x :- p1(X), p2(X), q1(Y), q2(Y). " + "p2(X) :- something(X). " + "q2(X) :- something(X). "); @@ -189,8 +195,8 @@ private void testDeadEnd(String predicateNameOfStartingLiteral, RuleGroundingOrd NaiveGrounder grounder = (NaiveGrounder) GrounderFactory.getInstance("naive", program, atomStore, p -> true, GrounderHeuristicsConfiguration.permissive(), true); NonGroundRule nonGroundRule = grounder.getNonGroundRule(0); - String varName = "p1".equals(predicateNameOfStartingLiteral) ? "X" : "Y"; - final Literal startingLiteral = literal("p1", varName); + String strLiteral = "p1".equals(predicateNameOfStartingLiteral) ? "p1(X)" : "p1(Y)"; + final Literal startingLiteral = PROGRAM_PART_PARSER.parseLiteral(strLiteral); nonGroundRule.groundingOrder.groundingOrders.put(startingLiteral, groundingOrder); grounder.bootstrap(); @@ -203,36 +209,36 @@ private void testDeadEnd(String predicateNameOfStartingLiteral, RuleGroundingOrd @Test public void testGroundingOfRuleSwitchedOffByFalsePositiveBody() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X). " + "b(X) :- something(X). "); - testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.FALSE, false); + testIfGrounderGroundsRule(program, 0, litAX, 1, ThriceTruth.FALSE, false); } @Test public void testGroundingOfRuleNotSwitchedOffByTruePositiveBody() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X). " + "b(X) :- something(X). "); - testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.TRUE, true); + testIfGrounderGroundsRule(program, 0, litAX, 1, ThriceTruth.TRUE, true); } @Test @Ignore("Currently, rule grounding is not switched off by a true negative body atom") public void testGroundingOfRuleSwitchedOffByTrueNegativeBody() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), not b(X). " + "b(X) :- something(X). "); - testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.TRUE, false); + testIfGrounderGroundsRule(program, 0, litAX, 1, ThriceTruth.TRUE, false); } @Test public void testGroundingOfRuleNotSwitchedOffByFalseNegativeBody() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), not b(X). " + "b(X) :- something(X). "); - testIfGrounderGroundsRule(program, 0, literal("a", "X"), 1, ThriceTruth.FALSE, true); + testIfGrounderGroundsRule(program, 0, litAX, 1, ThriceTruth.FALSE, true); } /** @@ -260,66 +266,66 @@ private void testIfGrounderGroundsRule(Program program, int ruleID, Literal star @Test public void testPermissiveGrounderHeuristicTolerance_0_reject() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X). " + "b(X) :- something(X)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 0, false, Arrays.asList(1)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 0, false, Arrays.asList(1)); } @Test public void testPermissiveGrounderHeuristicTolerance_1_accept() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X). " + "b(X) :- something(X)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 1, true, Arrays.asList(1)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 1, true, Arrays.asList(1)); } @Test public void testPermissiveGrounderHeuristicTolerance_1_reject() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X), b(X+1). " + "b(X) :- something(X)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 1, false, Arrays.asList(2)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 1, false, Arrays.asList(2)); } @Test public void testPermissiveGrounderHeuristicTolerance_2_accept() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X), b(X+1). " + "b(X) :- something(X)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 2, true, Arrays.asList(2)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 2, true, Arrays.asList(2)); } @Test public void testPermissiveGrounderHeuristicTolerance_1_accept_two_substitutions() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X,Y). " + "b(X,Y) :- something(X,Y)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 1, new ThriceTruth[] {TRUE, TRUE}, 2, true, Arrays.asList(0, 0)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 1, new ThriceTruth[] {TRUE, TRUE}, 2, true, Arrays.asList(0, 0)); } @Test public void testPermissiveGrounderHeuristicTolerance_1_accept_accept_two_substitutions_with_different_remaining_tolerances() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(1), b(X,Y). " + "b(X,Y) :- something(X,Y)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", 1), 1, 1, new ThriceTruth[] {null, null}, 2, true, Arrays.asList(1, 1)); + testPermissiveGrounderHeuristicTolerance(program, 0, litA1, 1, 1, new ThriceTruth[] {null, null}, 2, true, Arrays.asList(1, 1)); } @Test public void testPermissiveGrounderHeuristicTolerance_2_reject() { - Program program = PARSER.parse("a(1). " + Program program = PROGRAM_PARSER.parse("a(1). " + "c(X) :- a(X), b(X), b(X+1), b(X+2). " + "b(X) :- something(X)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 2, false, Arrays.asList(3)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 2, false, Arrays.asList(3)); } @Test public void testPermissiveGrounderHeuristicTolerance_2_accept_multiple_facts_of_same_variable() { - Program program = PARSER.parse("a(1). b(1). " + Program program = PROGRAM_PARSER.parse("a(1). b(1). " + "c(X) :- a(X), b(X), b(X+1), b(X+2). " + "b(X) :- something(X)."); - testPermissiveGrounderHeuristicTolerance(program, 0, literal("a", "X"), 1, 2, true, Arrays.asList(2)); + testPermissiveGrounderHeuristicTolerance(program, 0, litAX, 1, 2, true, Arrays.asList(2)); } private void testPermissiveGrounderHeuristicTolerance(Program program, int ruleID, Literal startingLiteral, int startingInstance, int tolerance, boolean expectNoGoods, List expectedNumbersOfUnassignedPositiveBodyAtoms) { 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 8c6b53c41..5d0b5f1a8 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,5 +1,5 @@ -/** - * Copyright (c) 2017-2019, the Alpha Team. +/* + * Copyright (c) 2017-2020, the Alpha Team. * All rights reserved. * * Additional changes made by Siemens. @@ -31,25 +31,27 @@ import at.ac.tuwien.kr.alpha.common.Rule; import at.ac.tuwien.kr.alpha.common.atoms.Literal; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; +import at.ac.tuwien.kr.alpha.grounder.parser.ProgramPartParser; 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.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Copyright (c) 2017-2019, the Alpha Team. */ public class RuleGroundingOrderTest { - - private final ProgramParser parser = new ProgramParser(); + private static final ProgramParser PROGRAM_PARSER = new ProgramParser(); + private static final ProgramPartParser PROGRAM_PART_PARSER = new ProgramPartParser(); @Test public void groundingOrder() throws IOException { - Program program = parser.parse("h(X,C) :- p(X,Y), q(A,B), r(Y,A), s(C)." + + Program program = PROGRAM_PARSER.parse("h(X,C) :- p(X,Y), q(A,B), r(Y,A), s(C)." + "j(A,B,X,Y) :- r1(A,B), r1(X,Y), r1(A,X), r1(B,Y), A = B." + "p(a) :- b = a."); new VariableEqualityRemoval().transform(program); @@ -74,21 +76,21 @@ public void groundingOrder() throws IOException { @Test(expected = RuntimeException.class) public void groundingOrderUnsafe() throws IOException { - Program program = parser.parse("h(X,C) :- X = Y, Y = C .. 3, C = X."); + Program program = PROGRAM_PARSER.parse("h(X,C) :- X = Y, Y = C .. 3, C = X."); new VariableEqualityRemoval().transform(program); computeGroundingOrdersForRule(program, 0); } @Test public void testPositionFromWhichAllVarsAreBound_ground() { - Program program = parser.parse("a :- b, not c."); + Program 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)."); + Program 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()) { @@ -98,7 +100,7 @@ public void testPositionFromWhichAllVarsAreBound_simpleNonGround() { @Test public void testPositionFromWhichAllVarsAreBound_longerSimpleNonGround() { - Program program = parser.parse("a(X) :- b(X), c(X), d(X), not e(X)."); + Program 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()) { @@ -108,7 +110,7 @@ public void testPositionFromWhichAllVarsAreBound_longerSimpleNonGround() { @Test public void testToString_longerSimpleNonGround() { - Program program = parser.parse("a(X) :- b(X), c(X), d(X), not e(X)."); + Program 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()) { @@ -123,16 +125,20 @@ public void testToString_longerSimpleNonGround() { @Test public void testPositionFromWhichAllVarsAreBound_joinedNonGround() { - Program program = parser.parse("a(X) :- b(X), c(X,Y), d(X,Z), not e(X)."); + Program 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(literal("b", "X")).getPositionFromWhichAllVarsAreBound()); - assertTrue(1 <= rgo0.orderStartingFrom(literal("c", "X", "Y")).getPositionFromWhichAllVarsAreBound()); - assertTrue(1 <= rgo0.orderStartingFrom(literal("d", "X", "Z")).getPositionFromWhichAllVarsAreBound()); + final Literal litBX = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal litCXY = PROGRAM_PART_PARSER.parseLiteral("c(X,Y)"); + final Literal litDXZ = PROGRAM_PART_PARSER.parseLiteral("d(X,Z)"); + assertTrue(2 <= rgo0.orderStartingFrom(litBX).getPositionFromWhichAllVarsAreBound()); + assertTrue(1 <= rgo0.orderStartingFrom(litCXY).getPositionFromWhichAllVarsAreBound()); + assertTrue(1 <= rgo0.orderStartingFrom(litDXZ).getPositionFromWhichAllVarsAreBound()); } private RuleGroundingOrders computeGroundingOrdersForRule(Program program, int ruleIndex) { Rule rule = program.getRules().get(ruleIndex); - NonGroundRule nonGroundRule = NonGroundRule.constructNonGroundRule(rule); + final NonGroundRule nonGroundRule1 = NonGroundRule.constructNonGroundRule(rule); + NonGroundRule nonGroundRule = nonGroundRule1; RuleGroundingOrders rgo = new RuleGroundingOrders(nonGroundRule); rgo.computeGroundingOrders(); return rgo; From d61a4a6d0510426ca853d97fc47c22aed9c3e6e2 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 23 Apr 2020 08:31:50 +0200 Subject: [PATCH 42/55] Fix: ProgramPartParser must accept variables --- .../ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java b/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java index 57648a28e..56593b6a3 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/grounder/parser/ProgramPartParser.java @@ -46,7 +46,7 @@ * atoms, terms and such. */ public class ProgramPartParser { - private final ParseTreeVisitor visitor = new ParseTreeVisitor(Collections.emptyMap(), false); + private final ParseTreeVisitor visitor = new ParseTreeVisitor(Collections.emptyMap(), true); public Term parseTerm(String s) { final ASPCore2Parser parser = getASPCore2Parser(s); From 9c47b90210dd0b1e27a7733a4ae2d4f3f340c66c Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 23 Apr 2020 09:58:26 +0200 Subject: [PATCH 43/55] Throw exception if variable name is invalid (i.e., does not start with _ or upper-case letter) (this is mainly to notice errors in unit tests!) --- .../kr/alpha/common/terms/VariableTerm.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java index 8396b4102..a0db894ff 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/terms/VariableTerm.java @@ -1,3 +1,31 @@ +/* + * Copyright (c) 2016-2018, 2020, 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; @@ -7,9 +35,6 @@ import java.util.Collections; import java.util.List; -/** - * Copyright (c) 2016-2017, the Alpha Team. - */ public class VariableTerm extends Term { private static final Interner INTERNER = new Interner<>(); @@ -23,6 +48,10 @@ private VariableTerm(String variableName) { } public static VariableTerm getInstance(String variableName) { + final char firstChar = variableName.charAt(0); + if (firstChar != ANONYMOUS_VARIABLE_PREFIX.charAt(0) && !Character.isUpperCase(firstChar)) { + throw new IllegalArgumentException("Variable name must start with upper-case letter or " + ANONYMOUS_VARIABLE_PREFIX); + } return INTERNER.intern(new VariableTerm(variableName)); } From 6934d801ee85e3ac43ebd5959f4fee46d512ebfd Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 23 Apr 2020 10:45:42 +0200 Subject: [PATCH 44/55] Do not learn multiple equivalent non-ground nogoods --- .../kr/alpha/common/NonGroundNoGood.java | 4 + .../tuwien/kr/alpha/common/Substitution.java | 1 + .../at/ac/tuwien/kr/alpha/common/Unifier.java | 36 ++++++++ .../NonGroundConflictNoGoodLearner.java | 23 ++++- .../tuwien/kr/alpha/common/UnifierTest.java | 83 +++++++++++++++++++ 5 files changed, 144 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index d0109087f..92be8b56c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -136,6 +136,10 @@ public Literal getLiteral(int index) { return literals[index]; } + Literal[] getSortedLiterals() { + return sortedLiterals; + } + @Override public boolean hasHead() { return head; diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java index 5ca799c55..442bc82da 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Substitution.java @@ -113,6 +113,7 @@ public boolean unifyTerms(Term termLeft, Term termRight, boolean allowNonGroundT } else if (termLeft instanceof ConstantTerm) { return false; } else if (termLeft instanceof VariableTerm) { + // TODO: what do do with anonymous variables? VariableTerm variableTermLeft = (VariableTerm)termLeft; if (isRightTermGround) { // Left term is variable, bind it to the right term. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java index a883e2170..7769188c5 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/Unifier.java @@ -1,6 +1,7 @@ package at.ac.tuwien.kr.alpha.common; import at.ac.tuwien.kr.alpha.common.atoms.Atom; +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; @@ -72,6 +73,41 @@ public boolean unify(Atom modifiableAtom, Atom unmodifiableAtom) { return true; } + /** + * Unifies the given non-ground nogoods if possible. Unification will only substitute variables that occur in {@code modifiableNonGroundNoGood}. + * @param modifiableNonGroundNoGood a non-ground nogood that can be modified by substitution + * @param unmodifiableNonGroundNoGood a non-ground nogood that cannot be modified by substitution + * @return {@code true} iff unification was successful + */ + public boolean unify(NonGroundNoGood modifiableNonGroundNoGood, NonGroundNoGood unmodifiableNonGroundNoGood) { + if (modifiableNonGroundNoGood.hasHead() != unmodifiableNonGroundNoGood.hasHead()) { + return false; + } + final Literal[] modifiableLiterals = modifiableNonGroundNoGood.getSortedLiterals(); + final Literal[] unmodifiableLiterals = unmodifiableNonGroundNoGood.getSortedLiterals(); + if (modifiableLiterals.length != unmodifiableLiterals.length) { + return false; + } + for (int i = 0; i < modifiableLiterals.length; i++) { + final Literal modifiableLiteral = modifiableLiterals[i]; + final Literal unmodifiableLiteral = unmodifiableLiterals[i]; + if (modifiableLiteral.isNegated() != unmodifiableLiteral.isNegated()) { + return false; + } + if (!Objects.equals(modifiableLiteral.getPredicate(), unmodifiableLiteral.getPredicate())) { + return false; + } + if (!unify(modifiableLiteral.getAtom(), unmodifiableLiteral.getAtom())) { + return false; + } + } + return true; + } + + public static boolean canBeUnifiedInBothDirections(NonGroundNoGood nonGroundNoGood1, NonGroundNoGood nonGroundNoGood2) { + return new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1) && new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2); + } + @Override public > Term put(VariableTerm variableTerm, Term term) { // If term is not ground, store it for right-hand side reverse-lookup. diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 1e98826e7..d9452f336 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -248,16 +248,33 @@ private void countAndRememberLearnedNonGroundNoGoods(Antecedent violatedNoGood, } nonGroundNoGoodViolationCounter.computeIfPresent(violatedNonGroundNoGood, (n, c) -> c + 1); if (learnedNonGroundNoGood != null) { - learnedOnFirstUIP.get(violatedNonGroundNoGood).add(learnedNonGroundNoGood); + rememberNonGroundNoGoodIfNotAlreadyPresent(learnedNonGroundNoGood, learnedOnFirstUIP.get(violatedNonGroundNoGood)); } if (!additionalLearnedNonGroundNoGoods.isEmpty()) { - learnedOnLastUIP.get(violatedNonGroundNoGood).add(additionalLearnedNonGroundNoGoods.get(additionalLearnedNonGroundNoGoods.size() - 1)); + rememberNonGroundNoGoodIfNotAlreadyPresent(additionalLearnedNonGroundNoGoods.get(additionalLearnedNonGroundNoGoods.size() - 1), learnedOnLastUIP.get(violatedNonGroundNoGood)); for (int i = 0; i < additionalLearnedNonGroundNoGoods.size() - 1; i++) { - learnedOnOtherUIPs.get(violatedNonGroundNoGood).add(additionalLearnedNonGroundNoGoods.get(i)); + rememberNonGroundNoGoodIfNotAlreadyPresent(additionalLearnedNonGroundNoGoods.get(i), learnedOnOtherUIPs.get(violatedNonGroundNoGood)); } } } + /** + * Adds {@code learnedNonGroundNoGood} to the set {@code nonGroundNoGoods} only if the set does not already contain + * another nogood that is equivalent to the given one under unification. + * @param learnedNonGroundNoGood the nogood to add + * @param nonGroundNoGoods the set of existing nogoods + * @return true iff the nogood is added + */ + private boolean rememberNonGroundNoGoodIfNotAlreadyPresent(NonGroundNoGood learnedNonGroundNoGood, Set nonGroundNoGoods) { + for (NonGroundNoGood existingNoGood : nonGroundNoGoods) { + if (existingNoGood.equals(learnedNonGroundNoGood) || Unifier.canBeUnifiedInBothDirections(learnedNonGroundNoGood, existingNoGood)) { + return false; + } + } + nonGroundNoGoods.add(learnedNonGroundNoGood); + return true; + } + public void logLearnedNonGroundNoGoods() { LOGGER.info("LEARNED NON-GROUND NOGOODS:"); for (NonGroundNoGood violatedNonGroundNoGood : nonGroundNoGoodViolationCounter.keySet()) { diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java index bd4daa713..651a189f5 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java @@ -38,14 +38,20 @@ import at.ac.tuwien.kr.alpha.common.terms.VariableTerm; import at.ac.tuwien.kr.alpha.grounder.NonGroundRule; import at.ac.tuwien.kr.alpha.grounder.parser.ProgramParser; +import at.ac.tuwien.kr.alpha.grounder.parser.ProgramPartParser; import org.junit.Test; import java.util.Arrays; +import static at.ac.tuwien.kr.alpha.common.NoGoodInterface.Type.STATIC; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class UnifierTest extends SubstitutionTest { + private static final ProgramPartParser PROGRAM_PART_PARSER = new ProgramPartParser(); + @Test public void extendUnifier() { VariableTerm varX = VariableTerm.getInstance("X"); @@ -159,4 +165,81 @@ private void groundLiteralToString(boolean negated) { String printedString = SubstitutionTestUtil.groundLiteralToString(atom.toLiteral(!negated), substitution, true); assertEquals((negated ? "not " : "") + "p(a, b)", printedString); } + + @Test + public void unsuccessfullyUnifyNonGroundNoGoodsMismatchingHead() { + final Literal lit1 = PROGRAM_PART_PARSER.parseLiteral("not a(X)"); + final Literal lit2 = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal[] literals = new Literal[]{lit1, lit2}; + final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(STATIC, literals, true); + final NonGroundNoGood nonGroundNoGood2 = new NonGroundNoGood(STATIC, literals, false); + assertFalse(new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2)); + assertFalse(new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1)); + } + + @Test + public void unsuccessfullyUnifyNonGroundNoGoodsMismatchingPredicate() { + final Literal lit1a = PROGRAM_PART_PARSER.parseLiteral("a(X)"); + final Literal lit1b = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal lit2 = PROGRAM_PART_PARSER.parseLiteral("not c(X)"); + final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(lit1a, lit2); + final NonGroundNoGood nonGroundNoGood2 = new NonGroundNoGood(lit1b, lit2); + assertFalse(new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2)); + assertFalse(new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1)); + } + + @Test + public void unsuccessfullyUnifyNonGroundNoGoodsAdditionalLiteral() { + final Literal lit1 = PROGRAM_PART_PARSER.parseLiteral("a(X,Y)"); + final Literal lit2 = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal lit3 = PROGRAM_PART_PARSER.parseLiteral("X < Y"); + final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(lit1, lit2); + final NonGroundNoGood nonGroundNoGood2 = new NonGroundNoGood(lit1, lit2, lit3); + assertFalse(new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2)); + assertFalse(new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1)); + } + + @Test + public void successfullyUnifyNonGroundNoGoods() { + final Literal lit1 = PROGRAM_PART_PARSER.parseLiteral("a(X,Y)"); + final Literal lit2 = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal lit3 = PROGRAM_PART_PARSER.parseLiteral("X < Y"); + final Unifier substitution = new Unifier(); + substitution.put(VariableTerm.getInstance("X"), VariableTerm.getInstance("X1")); + substitution.put(VariableTerm.getInstance("Y"), VariableTerm.getInstance("Z")); + final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(lit1, lit2, lit3); + final NonGroundNoGood nonGroundNoGood2 = new NonGroundNoGood( + lit1.substitute(substitution), lit2.substitute(substitution), lit3.substitute(substitution)); + assertTrue(new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2)); + assertTrue(new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1)); + } + + @Test + public void successfullyUnifyNonGroundNoGoodsOneIsPartiallyGround() { + final Literal lit1 = PROGRAM_PART_PARSER.parseLiteral("a(X,Y)"); + final Literal lit2 = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal lit3 = PROGRAM_PART_PARSER.parseLiteral("X < Y"); + final Unifier substitution = new Unifier(); + substitution.put(VariableTerm.getInstance("X"), VariableTerm.getInstance("X1")); + substitution.put(VariableTerm.getInstance("Y"), ConstantTerm.getInstance("y")); + final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(lit1, lit2, lit3); + final NonGroundNoGood nonGroundNoGood2 = new NonGroundNoGood( + lit1.substitute(substitution), lit2.substitute(substitution), lit3.substitute(substitution)); + assertTrue(new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2)); + assertFalse(new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1)); + } + + @Test + public void unsuccessfullyUnifyPartiallyGroundNonGroundNoGoods() { + final Literal lit1a = PROGRAM_PART_PARSER.parseLiteral("a(1,Y)"); + final Literal lit2a = PROGRAM_PART_PARSER.parseLiteral("b(1)"); + final Literal lit3a = PROGRAM_PART_PARSER.parseLiteral("1 < Y"); + final Literal lit1b = PROGRAM_PART_PARSER.parseLiteral("a(X,2)"); + final Literal lit2b = PROGRAM_PART_PARSER.parseLiteral("b(X)"); + final Literal lit3b = PROGRAM_PART_PARSER.parseLiteral("X < 2"); + final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(lit1a, lit2a, lit3a); + final NonGroundNoGood nonGroundNoGood2 = new NonGroundNoGood(lit1b, lit2b, lit3b); + assertFalse(new Unifier().unify(nonGroundNoGood1, nonGroundNoGood2)); + assertFalse(new Unifier().unify(nonGroundNoGood2, nonGroundNoGood1)); + } } \ No newline at end of file From a877134d77027da3287ab2b8830a8b569b6fd247 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 23 Apr 2020 11:07:38 +0200 Subject: [PATCH 45/55] Output most effective learned non-ground nogoods as constraints --- src/main/java/at/ac/tuwien/kr/alpha/Util.java | 6 ++++++ .../kr/alpha/common/NonGroundNoGood.java | 4 ++++ .../tuwien/kr/alpha/solver/DefaultSolver.java | 2 +- .../NonGroundConflictNoGoodLearner.java | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Util.java b/src/main/java/at/ac/tuwien/kr/alpha/Util.java index ee55dc12e..8c277cae0 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Util.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Util.java @@ -34,6 +34,8 @@ import java.nio.charset.StandardCharsets; import java.util.AbstractMap; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; @@ -55,6 +57,10 @@ public static Map.Entry entry(K key, V value) { return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue); } + public static K getKeyWithMaximumValue(Map map) { + return Collections.max(map.entrySet(), Comparator.comparingInt(Map.Entry::getValue)).getKey(); + } + public static String join(String prefix, Iterable iterable, String suffix) { return join(prefix, iterable, ", ", suffix); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 92be8b56c..51b1279fd 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -163,6 +163,10 @@ public Antecedent asAntecedent() { throw new UnsupportedOperationException("Non-ground nogood cannot be represented as an antecedent"); } + public Rule asConstraint() { + return new Rule(null, Arrays.asList(literals)); + } + @Override public Type getType() { return type; 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 842d8d69d..1b191453f 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 @@ -609,7 +609,7 @@ private void logStats() { private void logLearnedNonGroundNoGoods() { if (learner instanceof NonGroundConflictNoGoodLearner) { - ((NonGroundConflictNoGoodLearner)learner).logLearnedNonGroundNoGoods(); + ((NonGroundConflictNoGoodLearner)learner).logMostEffectiveLearnedConstraints(); } } } \ No newline at end of file diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index d9452f336..0f81d8c9d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -25,6 +25,7 @@ */ package at.ac.tuwien.kr.alpha.solver.learning; +import at.ac.tuwien.kr.alpha.Util; import at.ac.tuwien.kr.alpha.common.Assignment; import at.ac.tuwien.kr.alpha.common.AtomStore; import at.ac.tuwien.kr.alpha.common.NoGood; @@ -285,6 +286,24 @@ public void logLearnedNonGroundNoGoods() { } } + public void logMostEffectiveLearnedConstraints() { + final NonGroundNoGood noGoodViolatedMostOften = getNoGoodViolatedMostOften(); + LOGGER.info("Violated {} times: {}", nonGroundNoGoodViolationCounter.get(noGoodViolatedMostOften), noGoodViolatedMostOften); + for (NonGroundNoGood learnedNoGood : learnedOnFirstUIP.get(noGoodViolatedMostOften)) { + LOGGER.info("Learned on first UIP: {}", learnedNoGood.asConstraint()); + } + for (NonGroundNoGood learnedNoGood : learnedOnLastUIP.get(noGoodViolatedMostOften)) { + LOGGER.info("Learned on last UIP: {}", learnedNoGood.asConstraint()); + } + for (NonGroundNoGood learnedNoGood : learnedOnOtherUIPs.get(noGoodViolatedMostOften)) { + LOGGER.info("Learned on other UIPs: {}", learnedNoGood.asConstraint()); + } + } + + public NonGroundNoGood getNoGoodViolatedMostOften() { + return Util.getKeyWithMaximumValue(nonGroundNoGoodViolationCounter); + } + class GroundAndNonGroundNoGood { final UniqueVariableNames uniqueVariableNames = new UniqueVariableNames(); From 2e89d91908be5f265e64d694d014af935cb4e7a4 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 23 Apr 2020 12:28:43 +0200 Subject: [PATCH 46/55] Simplify variable names in learned nogoods --- .../kr/alpha/common/NonGroundNoGood.java | 31 +++++++++++++++++++ .../NonGroundConflictNoGoodLearner.java | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 51b1279fd..3ad9ef54d 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -172,6 +172,37 @@ public Type getType() { return type; } + /** + * Makes variable names more readable by: + *

      + *
    • Substituting variable names containing an underscore by the part before the underscore, if this part alone is not already used
    • + *
    + * @return a unifier that simplifies variable names + */ + public Unifier simplifyVariableNames() { + final Unifier unifier = new Unifier(); + final Set occurringVariables = getOccurringVariables(); + for (VariableTerm variable : occurringVariables) { + final String variableName = variable.toString(); + final int startOfPostfix = variableName.lastIndexOf('_'); + if (startOfPostfix > 0) { + final VariableTerm variableWithoutPostfix = VariableTerm.getInstance(variableName.substring(0, startOfPostfix)); + if (!occurringVariables.contains(variableWithoutPostfix) && unifier.eval(variableWithoutPostfix) == null) { + unifier.put(variable, variableWithoutPostfix); + } + } + } + return unifier; + } + + public NonGroundNoGood substitute(Substitution substitution) { + return new NonGroundNoGood( + this.type, + Arrays.stream(literals).map(l -> l.substitute(substitution)).collect(Collectors.toList()).toArray(new Literal[]{}), + this.head + ); + } + @Override public Iterator iterator() { return new Iterator() { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 0f81d8c9d..e083b0e90 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -272,7 +272,8 @@ private boolean rememberNonGroundNoGoodIfNotAlreadyPresent(NonGroundNoGood learn return false; } } - nonGroundNoGoods.add(learnedNonGroundNoGood); + final NonGroundNoGood simplifiedLearnedNoGood = learnedNonGroundNoGood.substitute(learnedNonGroundNoGood.simplifyVariableNames()); + nonGroundNoGoods.add(simplifiedLearnedNoGood); return true; } From 276b8d5759a3c707e21a8ec7302a8554276a8f26 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Thu, 23 Apr 2020 15:22:16 +0200 Subject: [PATCH 47/55] Further simplify variable names in learned nogoods --- .../ac/tuwien/kr/alpha/common/NonGroundNoGood.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 3ad9ef54d..8186b1db1 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -176,6 +176,7 @@ public Type getType() { * Makes variable names more readable by: *
      *
    • Substituting variable names containing an underscore by the part before the underscore, if this part alone is not already used
    • + *
    • Replacing different variable names with the same prefix by new variable names with the postfixes 1 and 2, e.g., H and H_1 become H1 and H2
    • *
    * @return a unifier that simplifies variable names */ @@ -186,9 +187,18 @@ public Unifier simplifyVariableNames() { final String variableName = variable.toString(); final int startOfPostfix = variableName.lastIndexOf('_'); if (startOfPostfix > 0) { - final VariableTerm variableWithoutPostfix = VariableTerm.getInstance(variableName.substring(0, startOfPostfix)); + final String variableNameWithoutPostfix = variableName.substring(0, startOfPostfix); + final VariableTerm variableWithoutPostfix = VariableTerm.getInstance(variableNameWithoutPostfix); if (!occurringVariables.contains(variableWithoutPostfix) && unifier.eval(variableWithoutPostfix) == null) { unifier.put(variable, variableWithoutPostfix); + continue; + } + final VariableTerm variableWithNewPostfix1 = VariableTerm.getInstance(variableNameWithoutPostfix + 1); + final VariableTerm variableWithNewPostfix2 = VariableTerm.getInstance(variableNameWithoutPostfix + 2); + if (!occurringVariables.contains(variableWithNewPostfix1) && unifier.eval(variableWithNewPostfix1) == null && + !occurringVariables.contains(variableWithNewPostfix2) && unifier.eval(variableWithNewPostfix2) == null) { + unifier.put(variableWithoutPostfix, variableWithNewPostfix1); + unifier.put(variable, variableWithNewPostfix2); } } } From fa045d08a41cba272a06f677be8fac42d7344baf Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Sun, 3 May 2020 17:09:29 +0200 Subject: [PATCH 48/55] Do not attempt to print violated nogoods if there are none --- src/main/java/at/ac/tuwien/kr/alpha/Util.java | 3 +++ .../alpha/solver/learning/NonGroundConflictNoGoodLearner.java | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/Util.java b/src/main/java/at/ac/tuwien/kr/alpha/Util.java index 8c277cae0..bab1de70c 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/Util.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/Util.java @@ -58,6 +58,9 @@ public static Map.Entry entry(K key, V value) { } public static K getKeyWithMaximumValue(Map map) { + if (map.isEmpty()) { + return null; + } return Collections.max(map.entrySet(), Comparator.comparingInt(Map.Entry::getValue)).getKey(); } diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index e083b0e90..790fbba25 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -289,6 +289,10 @@ public void logLearnedNonGroundNoGoods() { public void logMostEffectiveLearnedConstraints() { final NonGroundNoGood noGoodViolatedMostOften = getNoGoodViolatedMostOften(); + if (noGoodViolatedMostOften == null) { + LOGGER.info("No violated nogoods recorded."); + return; + } LOGGER.info("Violated {} times: {}", nonGroundNoGoodViolationCounter.get(noGoodViolatedMostOften), noGoodViolatedMostOften); for (NonGroundNoGood learnedNoGood : learnedOnFirstUIP.get(noGoodViolatedMostOften)) { LOGGER.info("Learned on first UIP: {}", learnedNoGood.asConstraint()); From ef7a796782198df40ef4a0d07c12c8e0afa239c0 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Sun, 12 Jul 2020 18:46:20 +0200 Subject: [PATCH 49/55] Satisfy CheckStyle --- src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java index 651a189f5..291b16c0a 100644 --- a/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java +++ b/src/test/java/at/ac/tuwien/kr/alpha/common/UnifierTest.java @@ -219,7 +219,7 @@ public void successfullyUnifyNonGroundNoGoodsOneIsPartiallyGround() { final Literal lit1 = PROGRAM_PART_PARSER.parseLiteral("a(X,Y)"); final Literal lit2 = PROGRAM_PART_PARSER.parseLiteral("b(X)"); final Literal lit3 = PROGRAM_PART_PARSER.parseLiteral("X < Y"); - final Unifier substitution = new Unifier(); + final Unifier substitution = new Unifier(); substitution.put(VariableTerm.getInstance("X"), VariableTerm.getInstance("X1")); substitution.put(VariableTerm.getInstance("Y"), ConstantTerm.getInstance("y")); final NonGroundNoGood nonGroundNoGood1 = new NonGroundNoGood(lit1, lit2, lit3); From d84305a8b96bb1aaf3bf09a51b617f493e2975cc Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Mon, 13 Jul 2020 07:48:24 +0200 Subject: [PATCH 50/55] Log all learned non-ground nogoods if ... ... none learned from nogood violated most often --- .../alpha/solver/learning/NonGroundConflictNoGoodLearner.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 790fbba25..09e0e70f4 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -294,6 +294,10 @@ public void logMostEffectiveLearnedConstraints() { return; } LOGGER.info("Violated {} times: {}", nonGroundNoGoodViolationCounter.get(noGoodViolatedMostOften), noGoodViolatedMostOften); + if (learnedOnFirstUIP.get(noGoodViolatedMostOften).isEmpty()) { + logLearnedNonGroundNoGoods(); + return; + } for (NonGroundNoGood learnedNoGood : learnedOnFirstUIP.get(noGoodViolatedMostOften)) { LOGGER.info("Learned on first UIP: {}", learnedNoGood.asConstraint()); } From 5a5f6a2723095e47fd5928176b647130ffed3636 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 15 Jul 2020 18:54:08 +0200 Subject: [PATCH 51/55] Learn non-ground nogood from non-UIP if further resolution is not possible --- .../learning/ConflictAnalysisResult.java | 9 ++++ .../NonGroundConflictNoGoodLearner.java | 43 +++++++++---------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java index 9610c228c..eab25fcea 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/ConflictAnalysisResult.java @@ -48,6 +48,7 @@ public class ConflictAnalysisResult { private List additionalLearnedNoGoods; private NonGroundNoGood learnedNonGroundNoGood; private List additionalLearnedNonGroundNoGoods; + private NonGroundNoGood exceptionallyLearnedNonGroundNoGood; private ConflictAnalysisResult() { learnedNoGood = null; @@ -119,6 +120,14 @@ public NonGroundNoGood getLearnedNonGroundNoGood() { return learnedNonGroundNoGood; } + public void setLearnedNonGroundGoodFromNonUIP(NonGroundNoGood exceptionallyLearnedNonGroundNoGood) { + this.exceptionallyLearnedNonGroundNoGood = exceptionallyLearnedNonGroundNoGood; + } + + public NonGroundNoGood getLearnedNonGroundGoodFromNonUIP() { + return exceptionallyLearnedNonGroundNoGood; + } + @Override public String toString() { if (this == UNSAT) { diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 09e0e70f4..8d9d23e74 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -77,6 +77,7 @@ public class NonGroundConflictNoGoodLearner implements ConflictNoGoodLearner { private Map> learnedOnFirstUIP = new HashMap<>(); private Map> learnedOnLastUIP = new HashMap<>(); private Map> learnedOnOtherUIPs = new HashMap<>(); + private Map> learnedOnNonUIPs = new HashMap<>(); public NonGroundConflictNoGoodLearner(Assignment assignment, AtomStore atomStore, GroundConflictNoGoodLearner groundLearner) { this.assignment = assignment; @@ -123,11 +124,20 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent NonGroundNoGood firstLearnedNonGroundNoGood = null; List additionalLearnedNoGoods = new ArrayList<>(); List additionalLearnedNonGroundNoGoods = new ArrayList<>(); + NonGroundNoGood exceptionallyLearnedNonGroundNoGood = null; GroundAndNonGroundNoGood currentNoGood = new GroundAndNonGroundNoGood(violatedNoGood); + boolean wasAbleToLearn = currentNoGood.canLearnNonGround; + NonGroundNoGood learntNonGroundNoGood = null; while (makeOneResolutionStep(currentNoGood, trailWalker)) { + if (wasAbleToLearn && !currentNoGood.canLearnNonGround) { + // learn current non-ground nogood if we stopped being able to learn with the last resolution step (e.g., because no non-ground nogood is known for the last antecedent) + assert exceptionallyLearnedNonGroundNoGood == null; + exceptionallyLearnedNonGroundNoGood = learntNonGroundNoGood; + } + wasAbleToLearn = currentNoGood.canLearnNonGround; + learntNonGroundNoGood = currentNoGood.toNonGroundNoGood(); if (containsUIP(currentNoGood)) { final NoGood learntNoGood = createLearntNoGood(currentNoGood); - final NonGroundNoGood learntNonGroundNoGood = currentNoGood.toNonGroundNoGood(); learntNoGood.setNonGroundNoGood(learntNonGroundNoGood); if (firstLearnedNoGood == null) { firstLearnedNoGood = learntNoGood; @@ -154,6 +164,10 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent analysisResult.addLearnedNonGroundNoGoods(additionalLearnedNonGroundNoGoods); LOGGER.info("Additionally learned non-ground nogoods: {}", additionalLearnedNonGroundNoGoods); } + if (exceptionallyLearnedNonGroundNoGood != null) { + analysisResult.setLearnedNonGroundGoodFromNonUIP(exceptionallyLearnedNonGroundNoGood); + LOGGER.info("Non-ground nogood learned at non-UIP: {}", exceptionallyLearnedNonGroundNoGood); + } countAndRememberLearnedNonGroundNoGoods(violatedNoGood, analysisResult); } return analysisResult; @@ -241,11 +255,13 @@ private void countAndRememberLearnedNonGroundNoGoods(Antecedent violatedNoGood, } final NonGroundNoGood learnedNonGroundNoGood = analysisResult.getLearnedNonGroundNoGood(); final List additionalLearnedNonGroundNoGoods = analysisResult.getAdditionalLearnedNonGroundNoGoods(); + final NonGroundNoGood learnedNonGroundGoodFromNonUIP = analysisResult.getLearnedNonGroundGoodFromNonUIP(); if (!nonGroundNoGoodViolationCounter.containsKey(violatedNonGroundNoGood)) { nonGroundNoGoodViolationCounter.put(violatedNonGroundNoGood, 0); learnedOnFirstUIP.put(violatedNonGroundNoGood, new HashSet<>()); learnedOnLastUIP.put(violatedNonGroundNoGood, new HashSet<>()); learnedOnOtherUIPs.put(violatedNonGroundNoGood, new HashSet<>()); + learnedOnNonUIPs.put(violatedNonGroundNoGood, new HashSet<>()); } nonGroundNoGoodViolationCounter.computeIfPresent(violatedNonGroundNoGood, (n, c) -> c + 1); if (learnedNonGroundNoGood != null) { @@ -257,6 +273,9 @@ private void countAndRememberLearnedNonGroundNoGoods(Antecedent violatedNoGood, rememberNonGroundNoGoodIfNotAlreadyPresent(additionalLearnedNonGroundNoGoods.get(i), learnedOnOtherUIPs.get(violatedNonGroundNoGood)); } } + if (learnedNonGroundGoodFromNonUIP != null) { + rememberNonGroundNoGoodIfNotAlreadyPresent(learnedNonGroundGoodFromNonUIP, learnedOnNonUIPs.get(violatedNonGroundNoGood)); + } } /** @@ -287,28 +306,6 @@ public void logLearnedNonGroundNoGoods() { } } - public void logMostEffectiveLearnedConstraints() { - final NonGroundNoGood noGoodViolatedMostOften = getNoGoodViolatedMostOften(); - if (noGoodViolatedMostOften == null) { - LOGGER.info("No violated nogoods recorded."); - return; - } - LOGGER.info("Violated {} times: {}", nonGroundNoGoodViolationCounter.get(noGoodViolatedMostOften), noGoodViolatedMostOften); - if (learnedOnFirstUIP.get(noGoodViolatedMostOften).isEmpty()) { - logLearnedNonGroundNoGoods(); - return; - } - for (NonGroundNoGood learnedNoGood : learnedOnFirstUIP.get(noGoodViolatedMostOften)) { - LOGGER.info("Learned on first UIP: {}", learnedNoGood.asConstraint()); - } - for (NonGroundNoGood learnedNoGood : learnedOnLastUIP.get(noGoodViolatedMostOften)) { - LOGGER.info("Learned on last UIP: {}", learnedNoGood.asConstraint()); - } - for (NonGroundNoGood learnedNoGood : learnedOnOtherUIPs.get(noGoodViolatedMostOften)) { - LOGGER.info("Learned on other UIPs: {}", learnedNoGood.asConstraint()); - } - } - public NonGroundNoGood getNoGoodViolatedMostOften() { return Util.getKeyWithMaximumValue(nonGroundNoGoodViolationCounter); } From cf8a47fececddb64f512c520d239c0da5f3b37cb Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 15 Jul 2020 18:55:01 +0200 Subject: [PATCH 52/55] Output all learned constraints ordered by number of violations --- .../tuwien/kr/alpha/solver/DefaultSolver.java | 2 +- .../NonGroundConflictNoGoodLearner.java | 27 ++++++++++++++----- 2 files changed, 21 insertions(+), 8 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 1b191453f..af94937a3 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 @@ -609,7 +609,7 @@ private void logStats() { private void logLearnedNonGroundNoGoods() { if (learner instanceof NonGroundConflictNoGoodLearner) { - ((NonGroundConflictNoGoodLearner)learner).logMostEffectiveLearnedConstraints(); + ((NonGroundConflictNoGoodLearner)learner).logLearnedConstraints(); } } } \ No newline at end of file diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 8d9d23e74..ced56e79a 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -296,17 +296,30 @@ private boolean rememberNonGroundNoGoodIfNotAlreadyPresent(NonGroundNoGood learn return true; } - public void logLearnedNonGroundNoGoods() { - LOGGER.info("LEARNED NON-GROUND NOGOODS:"); - for (NonGroundNoGood violatedNonGroundNoGood : nonGroundNoGoodViolationCounter.keySet()) { - LOGGER.info("Violated {} times: {}", nonGroundNoGoodViolationCounter.get(violatedNonGroundNoGood), violatedNonGroundNoGood); - LOGGER.info("Learned on first UIP: {}", learnedOnFirstUIP.get(violatedNonGroundNoGood)); - LOGGER.info("Learned on last UIP: {}", learnedOnLastUIP.get(violatedNonGroundNoGood)); - LOGGER.info("Learned on other UIPs: {}", learnedOnOtherUIPs.get(violatedNonGroundNoGood)); + public void logLearnedConstraints() { + LOGGER.info("LEARNED NON-GROUND CONSTRAINTS:"); + List> nonGroundViolationCounterEntries = new ArrayList<>(nonGroundNoGoodViolationCounter.entrySet()); + nonGroundViolationCounterEntries.sort(Map.Entry.comparingByValue().reversed()); + for (Map.Entry nonGroundViolationCounterEntry : nonGroundViolationCounterEntries) { + final NonGroundNoGood violatedNonGroundNoGood = nonGroundViolationCounterEntry.getKey(); + LOGGER.info("Violated {} times: {}", nonGroundViolationCounterEntry.getValue(), violatedNonGroundNoGood); + for (NonGroundNoGood learnedNoGood : learnedOnFirstUIP.get(violatedNonGroundNoGood)) { + LOGGER.info("Learned on first UIP: {}", learnedNoGood.asConstraint()); + } + for (NonGroundNoGood learnedNoGood : learnedOnLastUIP.get(violatedNonGroundNoGood)) { + LOGGER.info("Learned on last UIP: {}", learnedNoGood.asConstraint()); + } + for (NonGroundNoGood learnedNoGood : learnedOnOtherUIPs.get(violatedNonGroundNoGood)) { + LOGGER.info("Learned on other UIPs: {}", learnedNoGood.asConstraint()); + } + for (NonGroundNoGood learnedNoGood : learnedOnNonUIPs.get(violatedNonGroundNoGood)) { + LOGGER.info("Learned on non-UIPs: {}", learnedNoGood.asConstraint()); + } } } public NonGroundNoGood getNoGoodViolatedMostOften() { + // TODO: if more than one nogood has been violated the same maximum number of times, only one of them is returned return Util.getKeyWithMaximumValue(nonGroundNoGoodViolationCounter); } From d56eea4592dff38d6384021dbd1ae4440ad579d6 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 22 Jul 2020 09:56:50 +0200 Subject: [PATCH 53/55] Fix bug in NonGroundNoGood#simplifyVariableNames that made nogoods too small by mapping different variables to the same new variable name --- .../ac/tuwien/kr/alpha/common/NonGroundNoGood.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java index 8186b1db1..e7ba317c5 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/common/NonGroundNoGood.java @@ -182,23 +182,27 @@ public Type getType() { */ public Unifier simplifyVariableNames() { final Unifier unifier = new Unifier(); - final Set occurringVariables = getOccurringVariables(); - for (VariableTerm variable : occurringVariables) { + final Set existingVariables = getOccurringVariables(); + final Set allVariables = new HashSet<>(existingVariables); + for (VariableTerm variable : existingVariables) { final String variableName = variable.toString(); final int startOfPostfix = variableName.lastIndexOf('_'); if (startOfPostfix > 0) { final String variableNameWithoutPostfix = variableName.substring(0, startOfPostfix); final VariableTerm variableWithoutPostfix = VariableTerm.getInstance(variableNameWithoutPostfix); - if (!occurringVariables.contains(variableWithoutPostfix) && unifier.eval(variableWithoutPostfix) == null) { + if (!allVariables.contains(variableWithoutPostfix) && unifier.eval(variableWithoutPostfix) == null) { unifier.put(variable, variableWithoutPostfix); + allVariables.add(variableWithoutPostfix); continue; } final VariableTerm variableWithNewPostfix1 = VariableTerm.getInstance(variableNameWithoutPostfix + 1); final VariableTerm variableWithNewPostfix2 = VariableTerm.getInstance(variableNameWithoutPostfix + 2); - if (!occurringVariables.contains(variableWithNewPostfix1) && unifier.eval(variableWithNewPostfix1) == null && - !occurringVariables.contains(variableWithNewPostfix2) && unifier.eval(variableWithNewPostfix2) == null) { + if (!allVariables.contains(variableWithNewPostfix1) && unifier.eval(variableWithNewPostfix1) == null && + !allVariables.contains(variableWithNewPostfix2) && unifier.eval(variableWithNewPostfix2) == null) { unifier.put(variableWithoutPostfix, variableWithNewPostfix1); unifier.put(variable, variableWithNewPostfix2); + allVariables.add(variableWithNewPostfix1); + allVariables.add(variableWithNewPostfix2); } } } From f5e88248bc606751ef20560734566aeb3c3b2118 Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Wed, 22 Jul 2020 16:04:01 +0200 Subject: [PATCH 54/55] use also literals assigned at DL below current DL for resolution (to learn better nogoods in the case of 3col_crafted) --- .../solver/learning/NonGroundConflictNoGoodLearner.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index ced56e79a..6eba2cd12 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -68,6 +68,7 @@ */ public class NonGroundConflictNoGoodLearner implements ConflictNoGoodLearner { private static final Logger LOGGER = LoggerFactory.getLogger(NonGroundConflictNoGoodLearner.class); + private static final int RESOLUTION_DL_LIMIT = 1; // how many DLs below the current DL shall be used to resolve literals (originally 0, TODO: make configurable) private final Assignment assignment; private final AtomStore atomStore; @@ -152,7 +153,8 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent } final ConflictAnalysisResult groundAnalysisResultNotMinimized = groundLearner.analyzeTrailBased(violatedNoGood, false); if (!Objects.equals(groundAnalysisResultNotMinimized.learnedNoGood, firstLearnedNoGood)) { - throw oops("Learned nogood is not the same as the one computed by ground analysis"); + //throw oops("Learned nogood is not the same as the one computed by ground analysis"); + // TODO: re-introduce check } final ConflictAnalysisResult analysisResult = groundLearner.analyzeConflictingNoGood(violatedNoGood); if (analysisResult.backjumpLevel >= 0) { @@ -197,7 +199,7 @@ private Integer findNextLiteralToResolve(GroundAndNonGroundNoGood noGood, TrailA do { literal = trailWalker.getNextLowerLiteral(); atom = atomOf(literal); - } while (assignment.getWeakDecisionLevel(atom) != currentDecisionLevel || assignment.getImpliedBy(atom) == null || !noGood.groundNoGood.contains(literal)); + } while (assignment.getWeakDecisionLevel(atom) < currentDecisionLevel - RESOLUTION_DL_LIMIT || assignment.getImpliedBy(atom) == null || !noGood.groundNoGood.contains(literal)); return literal; } @@ -210,7 +212,7 @@ private boolean containsLiteralForResolution(GroundAndNonGroundNoGood noGood) { final int currentDecisionLevel = assignment.getDecisionLevel(); for (Integer literal : noGood.groundNoGood) { final int atom = atomOf(literal); - if (assignment.getWeakDecisionLevel(atom) == currentDecisionLevel && assignment.getImpliedBy(atom) != null) { + if (assignment.getWeakDecisionLevel(atom) >= currentDecisionLevel - RESOLUTION_DL_LIMIT && assignment.getImpliedBy(atom) != null) { return true; } } From aa79539dbfd65bb29f7752587968f7a0f1bcb68f Mon Sep 17 00:00:00 2001 From: Richard Taupe Date: Tue, 8 Sep 2020 16:26:49 +0200 Subject: [PATCH 55/55] Satisfy CheckStyle --- .../learning/NonGroundConflictNoGoodLearner.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java index 6eba2cd12..6a231d3c4 100644 --- a/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java +++ b/src/main/java/at/ac/tuwien/kr/alpha/solver/learning/NonGroundConflictNoGoodLearner.java @@ -45,7 +45,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -152,10 +151,10 @@ ConflictAnalysisResult analyzeConflictingNoGoodAndGeneraliseConflict(Antecedent } } final ConflictAnalysisResult groundAnalysisResultNotMinimized = groundLearner.analyzeTrailBased(violatedNoGood, false); - if (!Objects.equals(groundAnalysisResultNotMinimized.learnedNoGood, firstLearnedNoGood)) { +// if (!Objects.equals(groundAnalysisResultNotMinimized.learnedNoGood, firstLearnedNoGood)) { //throw oops("Learned nogood is not the same as the one computed by ground analysis"); // TODO: re-introduce check - } +// } final ConflictAnalysisResult analysisResult = groundLearner.analyzeConflictingNoGood(violatedNoGood); if (analysisResult.backjumpLevel >= 0) { // if backJumpLevel < 0, then problem is UNSAT (ground analysis result does additional checks for this) @@ -300,9 +299,9 @@ private boolean rememberNonGroundNoGoodIfNotAlreadyPresent(NonGroundNoGood learn public void logLearnedConstraints() { LOGGER.info("LEARNED NON-GROUND CONSTRAINTS:"); - List> nonGroundViolationCounterEntries = new ArrayList<>(nonGroundNoGoodViolationCounter.entrySet()); - nonGroundViolationCounterEntries.sort(Map.Entry.comparingByValue().reversed()); - for (Map.Entry nonGroundViolationCounterEntry : nonGroundViolationCounterEntries) { + List> nonGroundViolationCounterEntries = new ArrayList<>(nonGroundNoGoodViolationCounter.entrySet()); + nonGroundViolationCounterEntries.sort(Map.Entry.comparingByValue().reversed()); + for (Map.Entry nonGroundViolationCounterEntry : nonGroundViolationCounterEntries) { final NonGroundNoGood violatedNonGroundNoGood = nonGroundViolationCounterEntry.getKey(); LOGGER.info("Violated {} times: {}", nonGroundViolationCounterEntry.getValue(), violatedNonGroundNoGood); for (NonGroundNoGood learnedNoGood : learnedOnFirstUIP.get(violatedNonGroundNoGood)) {