aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-10-30 19:27:34 -0400
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-11-05 19:41:17 +0100
commit960af83c7c1cb871da03b9ac4ec6f44c94e78a1d (patch)
tree7d37ee007ee2d3b031d62ca892920d326758f438 /subprojects/store-query-viatra
parentrefactor: DNF query builder (diff)
downloadrefinery-960af83c7c1cb871da03b9ac4ec6f44c94e78a1d.tar.gz
refinery-960af83c7c1cb871da03b9ac4ec6f44c94e78a1d.tar.zst
refinery-960af83c7c1cb871da03b9ac4ec6f44c94e78a1d.zip
refactor: DNF atoms
Restore count != capability. Still needs semantics and tests for count atoms over partial models.
Diffstat (limited to 'subprojects/store-query-viatra')
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java30
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountExpressionEvaluator.java38
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountNotEqualsExpressionEvaluator.java30
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java84
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java97
5 files changed, 235 insertions, 44 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
index 702eb659..59fb1171 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
@@ -4,12 +4,10 @@ import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
4import tools.refinery.store.model.ModelDiffCursor; 4import tools.refinery.store.model.ModelDiffCursor;
5import tools.refinery.store.model.ModelStore; 5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.model.ModelStoreImpl; 6import tools.refinery.store.model.ModelStoreImpl;
7import tools.refinery.store.model.RelationLike;
7import tools.refinery.store.model.representation.DataRepresentation; 8import tools.refinery.store.model.representation.DataRepresentation;
8import tools.refinery.store.query.*; 9import tools.refinery.store.query.*;
9import tools.refinery.store.query.atom.DNFAtom; 10import tools.refinery.store.query.atom.*;
10import tools.refinery.store.query.atom.DNFCallAtom;
11import tools.refinery.store.query.atom.EquivalenceAtom;
12import tools.refinery.store.query.atom.RelationViewAtom;
13import tools.refinery.store.query.viatra.internal.RawPatternMatcher; 11import tools.refinery.store.query.viatra.internal.RawPatternMatcher;
14import tools.refinery.store.query.viatra.internal.ViatraQueryableModel; 12import tools.refinery.store.query.viatra.internal.ViatraQueryableModel;
15import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery; 13import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery;
@@ -57,9 +55,11 @@ public class ViatraQueryableModelStore implements QueryableModelStore {
57 for (DNFAtom atom : clause.constraints()) { 55 for (DNFAtom atom : clause.constraints()) {
58 if (atom instanceof RelationViewAtom relationViewAtom) { 56 if (atom instanceof RelationViewAtom relationViewAtom) {
59 validateRelationAtom(relationViews, dnfPredicate, relationViewAtom); 57 validateRelationAtom(relationViews, dnfPredicate, relationViewAtom);
60 } else if (atom instanceof DNFCallAtom queryCallAtom) { 58 } else if (atom instanceof CallAtom<?> queryCallAtom) {
61 validatePredicateAtom(predicates, dnfPredicate, queryCallAtom); 59 validatePredicateAtom(predicates, dnfPredicate, queryCallAtom);
62 } else if (!(atom instanceof EquivalenceAtom)) { 60 } else if (atom instanceof CountNotEqualsAtom<?> countNotEqualsAtom) {
61 validateCountNotEqualsAtom(predicates, dnfPredicate, countNotEqualsAtom);
62 } else if (!(atom instanceof EquivalenceAtom || atom instanceof ConstantAtom)) {
63 throw new IllegalArgumentException("Unknown constraint: " + atom.toString()); 63 throw new IllegalArgumentException("Unknown constraint: " + atom.toString());
64 } 64 }
65 } 65 }
@@ -77,16 +77,24 @@ public class ViatraQueryableModelStore implements QueryableModelStore {
77 } 77 }
78 } 78 }
79 79
80 private void validatePredicateAtom(Set<DNF> predicates, DNF dnfPredicate, 80 private void validatePredicateReference(Set<DNF> predicates, DNF dnfPredicate, RelationLike target) {
81 DNFCallAtom queryCallAtom) { 81 if (!(target instanceof DNF dnfTarget) || !predicates.contains(dnfTarget)) {
82 if (!predicates.contains(queryCallAtom.getTarget())) {
83 throw new IllegalArgumentException( 82 throw new IllegalArgumentException(
84 "%s %s contains reference to a predicate %s that is not in the model.".formatted( 83 "%s %s contains reference to a predicate %s that is not in the model.".formatted(
85 DNF.class.getSimpleName(), dnfPredicate.getUniqueName(), 84 DNF.class.getSimpleName(), dnfPredicate.getUniqueName(), target.getName()));
86 queryCallAtom.getTarget().getName()));
87 } 85 }
88 } 86 }
89 87
88 private void validatePredicateAtom(Set<DNF> predicates, DNF dnfPredicate, CallAtom<?> queryCallAtom) {
89 validatePredicateReference(predicates, dnfPredicate, queryCallAtom.getTarget());
90 }
91
92 private void validateCountNotEqualsAtom(Set<DNF> predicates, DNF dnfPredicate,
93 CountNotEqualsAtom<?> countNotEqualsAtom) {
94 validatePredicateReference(predicates, dnfPredicate, countNotEqualsAtom.mayTarget());
95 validatePredicateReference(predicates, dnfPredicate, countNotEqualsAtom.mustTarget());
96 }
97
90 private Map<DNF, GenericQuerySpecification<RawPatternMatcher>> initPredicates(Set<DNF> predicates) { 98 private Map<DNF, GenericQuerySpecification<RawPatternMatcher>> initPredicates(Set<DNF> predicates) {
91 Map<DNF, GenericQuerySpecification<RawPatternMatcher>> result = new HashMap<>(); 99 Map<DNF, GenericQuerySpecification<RawPatternMatcher>> result = new HashMap<>();
92 var dnf2PQuery = new DNF2PQuery(); 100 var dnf2PQuery = new DNF2PQuery();
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountExpressionEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountExpressionEvaluator.java
new file mode 100644
index 00000000..6fc96c05
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountExpressionEvaluator.java
@@ -0,0 +1,38 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator;
4import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
5import tools.refinery.store.query.atom.ComparisonOperator;
6import tools.refinery.store.query.atom.CountCallKind;
7
8import java.util.List;
9
10public record CountExpressionEvaluator(String variableName, ComparisonOperator operator,
11 int threshold) implements IExpressionEvaluator {
12 public CountExpressionEvaluator(String variableName, CountCallKind callKind) {
13 this(variableName, callKind.operator(), callKind.threshold());
14 }
15
16 @Override
17 public String getShortDescription() {
18 return "%s %s %d".formatted(variableName, operator, threshold);
19 }
20
21 @Override
22 public Iterable<String> getInputParameterNames() {
23 return List.of(variableName);
24 }
25
26 @Override
27 public Object evaluateExpression(IValueProvider provider) {
28 int value = (Integer) provider.getValue(variableName);
29 return switch (operator) {
30 case EQUALS -> value == threshold;
31 case NOT_EQUALS -> value != threshold;
32 case LESS -> value < threshold;
33 case LESS_EQUALS -> value <= threshold;
34 case GREATER -> value > threshold;
35 case GREATER_EQUALS -> value >= threshold;
36 };
37 }
38}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountNotEqualsExpressionEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountNotEqualsExpressionEvaluator.java
new file mode 100644
index 00000000..6f333a06
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/CountNotEqualsExpressionEvaluator.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator;
4import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
5
6import java.util.List;
7
8public record CountNotEqualsExpressionEvaluator(boolean must, int threshold, String mayVariableName,
9 String mustVariableName) implements IExpressionEvaluator {
10 @Override
11 public String getShortDescription() {
12 return "%d %s not in [%s; %s]".formatted(threshold, must ? "must" : "may", mustVariableName, mayVariableName);
13 }
14
15 @Override
16 public Iterable<String> getInputParameterNames() {
17 return List.of(mayVariableName, mustVariableName);
18 }
19
20 @Override
21 public Object evaluateExpression(IValueProvider provider) throws Exception {
22 int mayCount = (Integer) provider.getValue(mayVariableName);
23 int mustCount = (Integer) provider.getValue(mustVariableName);
24 if (must) {
25 return mayCount < threshold || mustCount > threshold;
26 } else {
27 return mayCount > threshold || mustCount < threshold;
28 }
29 }
30}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
index e3c586a0..61b984ae 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
@@ -2,20 +2,16 @@ package tools.refinery.store.query.viatra.internal.pquery;
2 2
3import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; 3import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
4import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; 4import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
5import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; 5import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.*;
6import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
7import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
9import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; 6import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
7import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 8import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 9import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
12import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
11import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
13import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
14import tools.refinery.store.query.*; 13import tools.refinery.store.query.*;
15import tools.refinery.store.query.atom.DNFAtom; 14import tools.refinery.store.query.atom.*;
16import tools.refinery.store.query.atom.EquivalenceAtom;
17import tools.refinery.store.query.atom.DNFCallAtom;
18import tools.refinery.store.query.atom.RelationViewAtom;
19import tools.refinery.store.query.view.RelationView; 15import tools.refinery.store.query.view.RelationView;
20 16
21import java.util.*; 17import java.util.*;
@@ -85,8 +81,12 @@ public class DNF2PQuery {
85 translateEquivalenceAtom(equivalenceAtom, body); 81 translateEquivalenceAtom(equivalenceAtom, body);
86 } else if (constraint instanceof RelationViewAtom relationViewAtom) { 82 } else if (constraint instanceof RelationViewAtom relationViewAtom) {
87 translateRelationViewAtom(relationViewAtom, body); 83 translateRelationViewAtom(relationViewAtom, body);
88 } else if (constraint instanceof DNFCallAtom dnfCallAtom) { 84 } else if (constraint instanceof CallAtom<?> callAtom) {
89 translateDNFCallAtom(dnfCallAtom, body); 85 translateCallAtom(callAtom, body);
86 } else if (constraint instanceof ConstantAtom constantAtom) {
87 translateConstantAtom(constantAtom, body);
88 } else if (constraint instanceof CountNotEqualsAtom<?> countNotEqualsAtom) {
89 translateCountNotEqualsAtom(countNotEqualsAtom, body);
90 } else { 90 } else {
91 throw new IllegalArgumentException("Unknown constraint: " + constraint.toString()); 91 throw new IllegalArgumentException("Unknown constraint: " + constraint.toString());
92 } 92 }
@@ -103,32 +103,66 @@ public class DNF2PQuery {
103 } 103 }
104 104
105 private void translateRelationViewAtom(RelationViewAtom relationViewAtom, PBody body) { 105 private void translateRelationViewAtom(RelationViewAtom relationViewAtom, PBody body) {
106 int arity = relationViewAtom.getSubstitution().size(); 106 new TypeConstraint(body, translateSubstitution(relationViewAtom.getSubstitution(), body),
107 wrapView(relationViewAtom.getTarget()));
108 }
109
110 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) {
111 int arity = substitution.size();
107 Object[] variables = new Object[arity]; 112 Object[] variables = new Object[arity];
108 for (int i = 0; i < arity; i++) { 113 for (int i = 0; i < arity; i++) {
109 var variable = relationViewAtom.getSubstitution().get(i); 114 var variable = substitution.get(i);
110 variables[i] = body.getOrCreateVariableByName(variable.getUniqueName()); 115 variables[i] = body.getOrCreateVariableByName(variable.getUniqueName());
111 } 116 }
112 new TypeConstraint(body, Tuples.flatTupleOf(variables), wrapView(relationViewAtom.getTarget())); 117 return Tuples.flatTupleOf(variables);
113 } 118 }
114 119
115 private RelationViewWrapper wrapView(RelationView<?> relationView) { 120 private RelationViewWrapper wrapView(RelationView<?> relationView) {
116 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new); 121 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new);
117 } 122 }
118 123
119 private void translateDNFCallAtom(DNFCallAtom queryCallAtom, PBody body) { 124 private void translateCallAtom(CallAtom<?> callAtom, PBody body) {
120 int arity = queryCallAtom.getSubstitution().size(); 125 if (!(callAtom.getTarget() instanceof DNF target)) {
121 Object[] variables = new Object[arity]; 126 throw new IllegalArgumentException("Only calls to DNF are supported");
122 for (int i = 0; i < arity; i++) { 127 }
123 var variable = queryCallAtom.getSubstitution().get(i); 128 var variablesTuple = translateSubstitution(callAtom.getSubstitution(), body);
124 variables[i] = body.getOrCreateVariableByName(variable.getUniqueName()); 129 var translatedReferred = translate(target);
130 var callKind = callAtom.getKind();
131 if (callKind instanceof BasicCallKind basicCallKind) {
132 switch (basicCallKind) {
133 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred);
134 case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred);
135 case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred);
136 default -> throw new IllegalArgumentException("Unknown BasicCallKind: " + basicCallKind);
137 }
138 } else if (callKind instanceof CountCallKind countCallKind) {
139 var countVariableName = DNFUtils.generateUniqueName("count");
140 var countPVariable = body.getOrCreateVariableByName(countVariableName);
141 new PatternMatchCounter(body, variablesTuple, translatedReferred, countPVariable);
142 new ExpressionEvaluation(body, new CountExpressionEvaluator(countVariableName, countCallKind), null);
143 } else {
144 throw new IllegalArgumentException("Unknown CallKind: " + callKind);
125 } 145 }
126 var variablesTuple = Tuples.flatTupleOf(variables); 146 }
127 var translatedReferred = translate(queryCallAtom.getTarget()); 147
128 switch (queryCallAtom.getKind()) { 148 private void translateConstantAtom(ConstantAtom constantAtom, PBody body) {
129 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred); 149 var variable = body.getOrCreateVariableByName(constantAtom.variable().getUniqueName());
130 case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred); 150 new ConstantValue(body, variable, constantAtom.nodeId());
131 case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred); 151 }
152
153 private void translateCountNotEqualsAtom(CountNotEqualsAtom<?> countNotEqualsAtom, PBody body) {
154 if (!(countNotEqualsAtom.mayTarget() instanceof DNF mayTarget) ||
155 !(countNotEqualsAtom.mustTarget() instanceof DNF mustTarget)) {
156 throw new IllegalArgumentException("Only calls to DNF are supported");
132 } 157 }
158 var variablesTuple = translateSubstitution(countNotEqualsAtom.substitution(), body);
159 var mayCountName = DNFUtils.generateUniqueName("countMay");
160 var mayCountVariable = body.getOrCreateVariableByName(mayCountName);
161 new PatternMatchCounter(body, variablesTuple, translate(mayTarget), mayCountVariable);
162 var mustCountName = DNFUtils.generateUniqueName("countMust");
163 var mustCountVariable = body.getOrCreateVariableByName(mustCountName);
164 new PatternMatchCounter(body, variablesTuple, translate(mustTarget), mustCountVariable);
165 new ExpressionEvaluation(body, new CountNotEqualsExpressionEvaluator(countNotEqualsAtom.must(),
166 countNotEqualsAtom.threshold(), mayCountName, mustCountName), null);
133 } 167 }
134} 168}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java
index 8984cb2c..224df362 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java
@@ -4,10 +4,7 @@ import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.representation.Relation; 4import tools.refinery.store.model.representation.Relation;
5import tools.refinery.store.model.representation.TruthValue; 5import tools.refinery.store.model.representation.TruthValue;
6import tools.refinery.store.query.*; 6import tools.refinery.store.query.*;
7import tools.refinery.store.query.atom.CallKind; 7import tools.refinery.store.query.atom.*;
8import tools.refinery.store.query.atom.EquivalenceAtom;
9import tools.refinery.store.query.atom.DNFCallAtom;
10import tools.refinery.store.query.atom.RelationViewAtom;
11import tools.refinery.store.query.viatra.ViatraQueryableModelStore; 8import tools.refinery.store.query.viatra.ViatraQueryableModelStore;
12import tools.refinery.store.query.view.FilteredRelationView; 9import tools.refinery.store.query.view.FilteredRelationView;
13import tools.refinery.store.query.view.KeyOnlyRelationView; 10import tools.refinery.store.query.view.KeyOnlyRelationView;
@@ -61,7 +58,7 @@ class QueryTest {
61 .parameters(p1, p2) 58 .parameters(p1, p2)
62 .clause( 59 .clause(
63 new RelationViewAtom(personView, p1), 60 new RelationViewAtom(personView, p1),
64 new RelationViewAtom(personView, p1), 61 new RelationViewAtom(personView, p2),
65 new RelationViewAtom(friendMustView, p1, p2) 62 new RelationViewAtom(friendMustView, p1, p2)
66 ) 63 )
67 .build(); 64 .build();
@@ -308,7 +305,7 @@ class QueryTest {
308 .clause( 305 .clause(
309 new RelationViewAtom(personView, p3), 306 new RelationViewAtom(personView, p3),
310 new RelationViewAtom(personView, p4), 307 new RelationViewAtom(personView, p4),
311 new DNFCallAtom(friendPredicate, p3, p4) 308 new CallAtom<>(friendPredicate, p3, p4)
312 ) 309 )
313 .build(); 310 .build();
314 311
@@ -354,7 +351,7 @@ class QueryTest {
354 .clause( 351 .clause(
355 new RelationViewAtom(personView, p3), 352 new RelationViewAtom(personView, p3),
356 new RelationViewAtom(personView, p4), 353 new RelationViewAtom(personView, p4),
357 new DNFCallAtom(CallKind.NEGATIVE, friendPredicate, p3, p4) 354 new CallAtom<>(false, friendPredicate, p3, p4)
358 ) 355 )
359 .build(); 356 .build();
360 357
@@ -374,6 +371,48 @@ class QueryTest {
374 } 371 }
375 372
376 @Test 373 @Test
374 void negativeWithQuantificationTest() {
375 Relation<Boolean> person = new Relation<>("Person", 1, false);
376 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
377 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
378 RelationView<TruthValue> friendMustView = new FilteredRelationView<>(friend, "must",
379 TruthValue::must);
380
381 Variable p1 = new Variable("p1");
382 Variable p2 = new Variable("p2");
383
384 DNF called = DNF.builder("Called")
385 .parameters(p1, p2)
386 .clause(
387 new RelationViewAtom(personView, p1),
388 new RelationViewAtom(personView, p2),
389 new RelationViewAtom(friendMustView, p1, p2)
390 )
391 .build();
392
393 DNF predicate = DNF.builder("Count")
394 .parameters(p1)
395 .clause(
396 new RelationViewAtom(personView, p1),
397 new CallAtom<>(false, called, p1, p2)
398 )
399 .build();
400
401 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
402 Set.of(personView, friendMustView), Set.of(called, predicate));
403 QueryableModel model = store.createModel();
404
405 model.put(person, Tuple.of(0), true);
406 model.put(person, Tuple.of(1), true);
407 model.put(person, Tuple.of(2), true);
408 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
409 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
410
411 model.flushChanges();
412 assertEquals(2, model.countResults(predicate));
413 }
414
415 @Test
377 void transitivePatternCallTest() { 416 void transitivePatternCallTest() {
378 Relation<Boolean> person = new Relation<>("Person", 1, false); 417 Relation<Boolean> person = new Relation<>("Person", 1, false);
379 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 418 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
@@ -399,7 +438,7 @@ class QueryTest {
399 .clause( 438 .clause(
400 new RelationViewAtom(personView, p3), 439 new RelationViewAtom(personView, p3),
401 new RelationViewAtom(personView, p4), 440 new RelationViewAtom(personView, p4),
402 new DNFCallAtom(CallKind.TRANSITIVE, friendPredicate, p3, p4) 441 new CallAtom<>(BasicCallKind.TRANSITIVE, friendPredicate, p3, p4)
403 ) 442 )
404 .build(); 443 .build();
405 444
@@ -417,6 +456,48 @@ class QueryTest {
417 assertEquals(3, model.countResults(predicate)); 456 assertEquals(3, model.countResults(predicate));
418 } 457 }
419 458
459 @Test
460 void countMatchTest() {
461 Relation<Boolean> person = new Relation<>("Person", 1, false);
462 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
463 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
464 RelationView<TruthValue> friendMustView = new FilteredRelationView<>(friend, "must",
465 TruthValue::must);
466
467 Variable p1 = new Variable("p1");
468 Variable p2 = new Variable("p2");
469
470 DNF called = DNF.builder("Called")
471 .parameters(p1, p2)
472 .clause(
473 new RelationViewAtom(personView, p1),
474 new RelationViewAtom(personView, p2),
475 new RelationViewAtom(friendMustView, p1, p2)
476 )
477 .build();
478
479 DNF predicate = DNF.builder("Count")
480 .parameters(p1)
481 .clause(
482 new RelationViewAtom(personView, p1),
483 new CallAtom<>(new CountCallKind(ComparisonOperator.EQUALS, 2), called, p1, p2)
484 )
485 .build();
486
487 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
488 Set.of(personView, friendMustView), Set.of(called, predicate));
489 QueryableModel model = store.createModel();
490
491 model.put(person, Tuple.of(0), true);
492 model.put(person, Tuple.of(1), true);
493 model.put(person, Tuple.of(2), true);
494 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
495 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
496
497 model.flushChanges();
498 assertEquals(1, model.countResults(predicate));
499 }
500
420 static void compareMatchSets(Stream<TupleLike> matchSet, Set<Tuple> expected) { 501 static void compareMatchSets(Stream<TupleLike> matchSet, Set<Tuple> expected) {
421 Set<Tuple> translatedMatchSet = new HashSet<>(); 502 Set<Tuple> translatedMatchSet = new HashSet<>();
422 var iterator = matchSet.iterator(); 503 var iterator = matchSet.iterator();