-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AnswerSetQuery: API feature to query answer sets in a fluent way #287
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
123e432
added API feature to query answer sets in a fluent way
madmike200590 8d23f54
fix checkstyle issues
madmike200590 de41589
Update src/main/java/at/ac/tuwien/kr/alpha/api/query/AnswerSetQuery.java
madmike200590 e109b3c
Update src/main/java/at/ac/tuwien/kr/alpha/api/query/AnswerSetQuery.java
madmike200590 914175c
improve tests
madmike200590 5420880
add test case for term matching
madmike200590 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
183 changes: 183 additions & 0 deletions
183
src/main/java/at/ac/tuwien/kr/alpha/api/query/AnswerSetQuery.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,183 @@ | ||||||
package at.ac.tuwien.kr.alpha.api.query; | ||||||
|
||||||
import java.util.Collections; | ||||||
import java.util.HashMap; | ||||||
import java.util.List; | ||||||
import java.util.Map; | ||||||
import java.util.stream.Collectors; | ||||||
|
||||||
import at.ac.tuwien.kr.alpha.common.AnswerSet; | ||||||
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; | ||||||
|
||||||
/** | ||||||
* A query for ASP atoms matching a set of filter predicates. | ||||||
*/ | ||||||
public final class AnswerSetQuery implements java.util.function.Predicate<Atom> { | ||||||
|
||||||
private final Predicate predicate; | ||||||
private Map<Integer, java.util.function.Predicate<Term>> filters = new HashMap<>(); | ||||||
|
||||||
private AnswerSetQuery(Predicate pred) { | ||||||
this.predicate = pred; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Creates a new AnswerSetQuery that will match all atoms that are instances of the given {@link Predicate}. | ||||||
* | ||||||
* @param predicate the predicate to match against | ||||||
* @return a new AnswerSetQuery matching against the given predicate | ||||||
*/ | ||||||
public static AnswerSetQuery forPredicate(Predicate predicate) { | ||||||
return new AnswerSetQuery(predicate); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Adds a new filter to this AnswerSetQuery. | ||||||
* For an atom <code>a(t1, ..., tn)</code>, the term at index <code>termIdx</code> will be tested against the given filter predicate. | ||||||
* | ||||||
* @param termIdx the index of the term to test | ||||||
* @param filter the test predicate to use on terms | ||||||
* @return this AnswerSetQuery with the additional filter added | ||||||
*/ | ||||||
public AnswerSetQuery withFilter(int termIdx, java.util.function.Predicate<Term> filter) { | ||||||
if (termIdx >= this.predicate.getArity()) { | ||||||
throw new IndexOutOfBoundsException( | ||||||
"Predicate " + this.predicate.getName() + " has arity " + this.predicate.getArity() + ", term index " + termIdx + " is invalid!"); | ||||||
} | ||||||
if (this.filters.containsKey(termIdx)) { | ||||||
java.util.function.Predicate<Term> currFilter = this.filters.get(termIdx); | ||||||
this.filters.put(termIdx, currFilter.and(filter)); | ||||||
} else { | ||||||
this.filters.put(termIdx, filter); | ||||||
} | ||||||
return this; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Convenience method - adds a filter to match names of symbolic constants against a string. | ||||||
* | ||||||
* @param termIdx | ||||||
* @param str | ||||||
* @return | ||||||
*/ | ||||||
public AnswerSetQuery withConstantEquals(int termIdx, String str) { | ||||||
return this.withFilter(termIdx, AnswerSetQuery.constantTermEquals(str)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method
Suggested change
|
||||||
} | ||||||
|
||||||
/** | ||||||
* Convenience method - adds a filter to match values of constant terms against a string. | ||||||
* | ||||||
* @param termIdx | ||||||
* @param str | ||||||
* @return | ||||||
*/ | ||||||
public AnswerSetQuery withStringEquals(int termIdx, String str) { | ||||||
return this.withFilter(termIdx, (term) -> { | ||||||
if (!(term instanceof ConstantTerm<?>)) { | ||||||
return false; | ||||||
} | ||||||
if (((ConstantTerm<?>) term).isSymbolic()) { | ||||||
return false; | ||||||
} | ||||||
return ((ConstantTerm<?>) term).getObject().equals(str); | ||||||
}); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Convenience method - adds a filter to check for function terms with a given function symbol and arity. | ||||||
* | ||||||
* @param termIdx | ||||||
* @param funcSymbol | ||||||
* @param funcArity | ||||||
* @return | ||||||
*/ | ||||||
public AnswerSetQuery withFunctionTerm(int termIdx, String funcSymbol, int funcArity) { | ||||||
java.util.function.Predicate<Term> isFunction = (term) -> { | ||||||
if (!(term instanceof FunctionTerm)) { | ||||||
return false; | ||||||
} | ||||||
FunctionTerm funcTerm = (FunctionTerm) term; | ||||||
if (!funcTerm.getSymbol().equals(funcSymbol)) { | ||||||
return false; | ||||||
} | ||||||
if (funcTerm.getTerms().size() != funcArity) { | ||||||
return false; | ||||||
} | ||||||
return true; | ||||||
}; | ||||||
return this.withFilter(termIdx, isFunction); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Convenience method - adds a filter to check whether a term is equal to a given term. | ||||||
* | ||||||
* @param termIdx | ||||||
* @param otherTerm | ||||||
* @return | ||||||
*/ | ||||||
public AnswerSetQuery withTermEquals(int termIdx, Term otherTerm) { | ||||||
AntoniusW marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
java.util.function.Predicate<Term> isEqual = (term) -> { | ||||||
return term.equals(otherTerm); | ||||||
}; | ||||||
return this.withFilter(termIdx, isEqual); | ||||||
} | ||||||
|
||||||
/** | ||||||
* Applies this query to an atom. Filters are worked off in | ||||||
* order of ascending term index in a conjunctive fashion, i.e. for an atom | ||||||
* to match the query, all of its terms must satisfy all filters on these | ||||||
* terms | ||||||
* | ||||||
* @param atom the atom to which to apply the query | ||||||
* @return true iff the atom satisfies the query | ||||||
*/ | ||||||
@Override | ||||||
public boolean test(Atom atom) { | ||||||
if (!atom.getPredicate().equals(predicate)) { | ||||||
return false; | ||||||
} | ||||||
for (int i = 0; i < atom.getTerms().size(); i++) { | ||||||
Term ithTerm = atom.getTerms().get(i); | ||||||
java.util.function.Predicate<Term> ithFilter = filters.get(i); | ||||||
if (ithFilter != null && !ithFilter.test(ithTerm)) { | ||||||
return false; | ||||||
} | ||||||
} | ||||||
return true; | ||||||
} | ||||||
|
||||||
/** | ||||||
* Applies this query to an {@link AnswerSet}. | ||||||
* | ||||||
* @param as | ||||||
* @return | ||||||
*/ | ||||||
public List<Atom> applyTo(AnswerSet as) { | ||||||
if (!as.getPredicates().contains(this.predicate)) { | ||||||
return Collections.emptyList(); | ||||||
} | ||||||
return as.getPredicateInstances(this.predicate).stream().filter(this).collect(Collectors.toList()); | ||||||
} | ||||||
|
||||||
private static java.util.function.Predicate<Term> constantTermEquals(final String str) { | ||||||
java.util.function.Predicate<Term> equalsGivenString = (t) -> { | ||||||
return AnswerSetQuery.constantTermEquals(t, str); | ||||||
}; | ||||||
return equalsGivenString; | ||||||
} | ||||||
Comment on lines
+166
to
+171
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method can be deleted if the above suggestion to inline it is accepted. |
||||||
|
||||||
private static boolean constantTermEquals(Term term, String str) { | ||||||
if (!(term instanceof ConstantTerm<?>)) { | ||||||
return false; | ||||||
} | ||||||
if (!((ConstantTerm<?>) term).isSymbolic()) { | ||||||
return false; | ||||||
} | ||||||
return ((ConstantTerm<?>) term).getObject().toString().equals(str); | ||||||
} | ||||||
|
||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 134 additions & 0 deletions
134
src/test/java/at/ac/tuwien/kr/alpha/api/query/AnswerSetQueryTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package at.ac.tuwien.kr.alpha.api.query; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.SortedSet; | ||
import java.util.TreeSet; | ||
|
||
import org.junit.Assert; | ||
import org.junit.Test; | ||
|
||
import at.ac.tuwien.kr.alpha.common.AnswerSet; | ||
import at.ac.tuwien.kr.alpha.common.AnswerSetBuilder; | ||
import at.ac.tuwien.kr.alpha.common.BasicAnswerSet; | ||
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.terms.ConstantTerm; | ||
import at.ac.tuwien.kr.alpha.common.terms.FunctionTerm; | ||
import at.ac.tuwien.kr.alpha.common.terms.Term; | ||
|
||
public class AnswerSetQueryTest { | ||
|
||
@Test | ||
public void matchPredicate() { | ||
AnswerSetBuilder bld = new AnswerSetBuilder(); | ||
//@formatter:off | ||
bld.predicate("p") | ||
.symbolicInstance("a") | ||
.symbolicInstance("b") | ||
.predicate("q") | ||
.symbolicInstance("x"); | ||
//@formatter:on | ||
AnswerSet as = bld.build(); | ||
List<Atom> queryResult = as.query(AnswerSetQuery.forPredicate(Predicate.getInstance("p", 1))); | ||
Assert.assertEquals(2, queryResult.size()); | ||
AntoniusW marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (Atom a : queryResult) { | ||
Assert.assertTrue(a.getPredicate().equals(Predicate.getInstance("p", 1))); | ||
} | ||
} | ||
|
||
@Test | ||
public void matchSymbolicConstant() { | ||
AnswerSetBuilder bld = new AnswerSetBuilder(); | ||
bld.predicate("p") | ||
.symbolicInstance("a") | ||
.instance("a"); | ||
AnswerSet as = bld.build(); | ||
AnswerSetQuery constantQuery = AnswerSetQuery | ||
.forPredicate(Predicate.getInstance("p", 1)) | ||
.withConstantEquals(0, "a"); | ||
List<Atom> queryResult = as.query(constantQuery); | ||
Assert.assertEquals(1, queryResult.size()); | ||
} | ||
|
||
@Test | ||
public void matchString() { | ||
AnswerSetBuilder bld = new AnswerSetBuilder(); | ||
bld.predicate("p") | ||
.symbolicInstance("a") | ||
.instance("a"); | ||
AnswerSet as = bld.build(); | ||
AnswerSetQuery stringQuery = AnswerSetQuery | ||
.forPredicate(Predicate.getInstance("p", 1)) | ||
.withStringEquals(0, "a"); | ||
List<Atom> queryResult = as.query(stringQuery); | ||
Assert.assertEquals(1, queryResult.size()); | ||
} | ||
|
||
@Test | ||
public void matchEvenIntegers() { | ||
AnswerSetBuilder bld = new AnswerSetBuilder(); | ||
bld.predicate("p") | ||
.instance(1).instance(2).instance(3).instance(4).instance(5) | ||
.instance("bla").symbolicInstance("blubb"); | ||
AnswerSet as = bld.build(); | ||
java.util.function.Predicate<Term> isInteger = (term) -> { | ||
if (!(term instanceof ConstantTerm<?>)) { | ||
return false; | ||
} | ||
String strValue = ((ConstantTerm<?>) term).getObject().toString(); | ||
return strValue.matches("[0-9]+"); | ||
}; | ||
AnswerSetQuery evenIntegers = AnswerSetQuery.forPredicate(Predicate.getInstance("p", 1)) | ||
.withFilter(0, isInteger.and( | ||
(term) -> Integer.valueOf(((ConstantTerm<?>) term).getObject().toString()) % 2 == 0)); | ||
List<Atom> queryResult = as.query(evenIntegers); | ||
Assert.assertEquals(2, queryResult.size()); | ||
for (Atom atom : queryResult) { | ||
ConstantTerm<?> term = (ConstantTerm<?>) atom.getTerms().get(0); | ||
Assert.assertTrue(Integer.valueOf(term.getObject().toString()) % 2 == 0); | ||
} | ||
} | ||
|
||
@Test | ||
public void matchXWithFuncTerm() { | ||
Predicate p = Predicate.getInstance("p", 2); | ||
Atom a1 = new BasicAtom(p, ConstantTerm.getSymbolicInstance("x"), FunctionTerm.getInstance("f", ConstantTerm.getSymbolicInstance("x"))); | ||
Atom a2 = new BasicAtom(p, ConstantTerm.getSymbolicInstance("y"), FunctionTerm.getInstance("f", ConstantTerm.getSymbolicInstance("y"))); | ||
Atom a3 = new BasicAtom(p, ConstantTerm.getSymbolicInstance("y"), FunctionTerm.getInstance("f", ConstantTerm.getSymbolicInstance("x"))); | ||
Atom a4 = new BasicAtom(p, ConstantTerm.getSymbolicInstance("x"), FunctionTerm.getInstance("f", ConstantTerm.getSymbolicInstance("y"))); | ||
Atom a5 = new BasicAtom(p, ConstantTerm.getSymbolicInstance("x"), ConstantTerm.getSymbolicInstance("f")); | ||
SortedSet<Predicate> predicates = new TreeSet<>(); | ||
predicates.add(p); | ||
Map<Predicate, SortedSet<Atom>> instances = new HashMap<>(); | ||
SortedSet<Atom> ps = new TreeSet<>(); | ||
ps.add(a1); | ||
ps.add(a2); | ||
ps.add(a3); | ||
ps.add(a4); | ||
ps.add(a5); | ||
instances.put(p, ps); | ||
AnswerSet as = new BasicAnswerSet(predicates, instances); | ||
AntoniusW marked this conversation as resolved.
Show resolved
Hide resolved
|
||
AnswerSetQuery query = AnswerSetQuery.forPredicate(Predicate.getInstance("p", 2)).withConstantEquals(0, "x").withFunctionTerm(1, "f", 1); | ||
List<Atom> queryResult = as.query(query); | ||
Assert.assertEquals(2, queryResult.size()); | ||
} | ||
|
||
@Test | ||
public void matchTerm() { | ||
AnswerSetBuilder bld = new AnswerSetBuilder(); | ||
bld.predicate("p") | ||
.instance(1).instance(2).instance(3).instance(4).instance(5) | ||
.instance("bla").symbolicInstance("blubb"); | ||
AnswerSet as = bld.build(); | ||
|
||
AnswerSetQuery equalTerm = AnswerSetQuery.forPredicate(Predicate.getInstance("p", 1)).withTermEquals(0, ConstantTerm.getInstance(1)); | ||
List<Atom> queryResult = as.query(equalTerm); | ||
Assert.assertEquals(1, queryResult.size()); | ||
Atom retrievedAtom = queryResult.get(0); | ||
Assert.assertTrue(retrievedAtom.getTerms().get(0).equals(ConstantTerm.getInstance(1))); | ||
} | ||
|
||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A general thought on this: our IDEs create these javadoc parameter descriptions automatically, but as long as we do not fill them in, we might as well delete the javadoc parameters.