aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-01-29 02:06:57 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-01-29 02:06:57 +0100
commit1d87633ef1dcf924f27949ff4dc2fedb4a8b67ef (patch)
treeee5cfb52db47c10de7e86bb44651e815526acb0b
parentrefactor: Model store and query API (diff)
downloadrefinery-1d87633ef1dcf924f27949ff4dc2fedb4a8b67ef.tar.gz
refinery-1d87633ef1dcf924f27949ff4dc2fedb4a8b67ef.tar.zst
refinery-1d87633ef1dcf924f27949ff4dc2fedb4a8b67ef.zip
feat: negative and transitive RelationViewAtom
Use PVisibility.EMBEDDED helper patterns to avoid superfluous production nodes in the Rete net.
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java47
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java8
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java124
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractSubstitutionAtom.java35
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java32
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationViewAtom.java33
6 files changed, 210 insertions, 69 deletions
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 2b5618d2..eb815588 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
@@ -12,10 +12,12 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.Consta
12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
15import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 17import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
17import tools.refinery.store.query.DNF; 18import tools.refinery.store.query.DNF;
18import tools.refinery.store.query.DNFAnd; 19import tools.refinery.store.query.DNFAnd;
20import tools.refinery.store.query.DNFUtils;
19import tools.refinery.store.query.Variable; 21import tools.refinery.store.query.Variable;
20import tools.refinery.store.query.atom.*; 22import tools.refinery.store.query.atom.*;
21import tools.refinery.store.query.view.AnyRelationView; 23import tools.refinery.store.query.view.AnyRelationView;
@@ -124,8 +126,19 @@ public class DNF2PQuery {
124 } 126 }
125 127
126 private void translateRelationViewAtom(RelationViewAtom relationViewAtom, PBody body) { 128 private void translateRelationViewAtom(RelationViewAtom relationViewAtom, PBody body) {
127 new TypeConstraint(body, translateSubstitution(relationViewAtom.getSubstitution(), body), 129 var substitution = translateSubstitution(relationViewAtom.getSubstitution(), body);
128 wrapView(relationViewAtom.getTarget())); 130 var polarity = relationViewAtom.getPolarity();
131 var relationView = relationViewAtom.getTarget();
132 if (polarity == CallPolarity.POSITIVE) {
133 new TypeConstraint(body, substitution, wrapView(relationView));
134 } else {
135 var embeddedPQuery = translateEmbeddedRelationViewPQuery(relationView);
136 switch (polarity) {
137 case TRANSITIVE -> new BinaryTransitiveClosure(body, substitution, embeddedPQuery);
138 case NEGATIVE -> new NegativePatternCall(body, substitution, embeddedPQuery);
139 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
140 }
141 }
129 } 142 }
130 143
131 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) { 144 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) {
@@ -138,16 +151,36 @@ public class DNF2PQuery {
138 return Tuples.flatTupleOf(variables); 151 return Tuples.flatTupleOf(variables);
139 } 152 }
140 153
154 private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) {
155 var embeddedPQuery = new RawPQuery(DNFUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED);
156 var body = new PBody(embeddedPQuery);
157 int arity = relationView.arity();
158 var parameters = new ArrayList<PParameter>(arity);
159 var arguments = new Object[arity];
160 var symbolicParameters = new ArrayList<ExportedParameter>(arity);
161 for (int i = 0; i < arity; i++) {
162 var parameterName = "p" + i;
163 var parameter = new PParameter(parameterName);
164 parameters.add(parameter);
165 var variable = body.getOrCreateVariableByName(parameterName);
166 arguments[i] = variable;
167 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
168 }
169 embeddedPQuery.setParameters(parameters);
170 body.setSymbolicParameters(symbolicParameters);
171 var argumentTuple = Tuples.flatTupleOf(arguments);
172 new TypeConstraint(body, argumentTuple, wrapView(relationView));
173 embeddedPQuery.addBody(body);
174 return embeddedPQuery;
175 }
176
141 private RelationViewWrapper wrapView(AnyRelationView relationView) { 177 private RelationViewWrapper wrapView(AnyRelationView relationView) {
142 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new); 178 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new);
143 } 179 }
144 180
145 private void translateCallAtom(CallAtom<?> callAtom, PBody body) { 181 private void translateCallAtom(DNFCallAtom callAtom, PBody body) {
146 if (!(callAtom.getTarget() instanceof DNF target)) {
147 throw new IllegalArgumentException("Only calls to DNF are supported");
148 }
149 var variablesTuple = translateSubstitution(callAtom.getSubstitution(), body); 182 var variablesTuple = translateSubstitution(callAtom.getSubstitution(), body);
150 var translatedReferred = translate(target); 183 var translatedReferred = translate(callAtom.getTarget());
151 var polarity = callAtom.getPolarity(); 184 var polarity = callAtom.getPolarity();
152 switch (polarity) { 185 switch (polarity) {
153 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred); 186 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred);
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
index 5d0b9e82..830495b2 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
@@ -18,11 +18,15 @@ public class RawPQuery extends BasePQuery {
18 private List<PParameter> parameters; 18 private List<PParameter> parameters;
19 private final LinkedHashSet<PBody> bodies = new LinkedHashSet<>(); 19 private final LinkedHashSet<PBody> bodies = new LinkedHashSet<>();
20 20
21 public RawPQuery(String name) { 21 public RawPQuery(String name, PVisibility visibility) {
22 super(PVisibility.PUBLIC); 22 super(visibility);
23 fullyQualifiedName = name; 23 fullyQualifiedName = name;
24 } 24 }
25 25
26 public RawPQuery(String name) {
27 this(name, PVisibility.PUBLIC);
28 }
29
26 @Override 30 @Override
27 public String getFullyQualifiedName() { 31 public String getFullyQualifiedName() {
28 return fullyQualifiedName; 32 return fullyQualifiedName;
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
index 72e8d7e5..ba0abca0 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
@@ -391,6 +391,48 @@ class QueryTest {
391 } 391 }
392 392
393 @Test 393 @Test
394 void negativeRelationViewTest() {
395 var person = new Symbol<>("Person", 1, Boolean.class, false);
396 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
397 var personView = new KeyOnlyRelationView<>(person);
398 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
399
400 var p1 = new Variable("p1");
401 var p2 = new Variable("p2");
402 var predicate = DNF.builder("NegativePatternCall")
403 .parameters(p1, p2)
404 .clause(
405 new RelationViewAtom(personView, p1),
406 new RelationViewAtom(personView, p2),
407 new RelationViewAtom(false, friendMustView, p1, p2)
408 )
409 .build();
410
411 var store = ModelStore.builder()
412 .symbols(person, friend)
413 .with(ViatraModelQuery.ADAPTER)
414 .queries(predicate)
415 .build();
416
417 var model = store.createModel();
418 var personInterpretation = model.getInterpretation(person);
419 var friendInterpretation = model.getInterpretation(friend);
420 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
421 var predicateResultSet = queryEngine.getResultSet(predicate);
422
423 personInterpretation.put(Tuple.of(0), true);
424 personInterpretation.put(Tuple.of(1), true);
425 personInterpretation.put(Tuple.of(2), true);
426
427 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
428 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
429 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
430
431 queryEngine.flushChanges();
432 assertEquals(6, predicateResultSet.countResults());
433 }
434
435 @Test
394 void negativePatternCallTest() { 436 void negativePatternCallTest() {
395 var person = new Symbol<>("Person", 1, Boolean.class, false); 437 var person = new Symbol<>("Person", 1, Boolean.class, false);
396 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 438 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
@@ -444,6 +486,47 @@ class QueryTest {
444 } 486 }
445 487
446 @Test 488 @Test
489 void negativeRelationViewWithQuantificationTest() {
490 var person = new Symbol<>("Person", 1, Boolean.class, false);
491 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
492 var personView = new KeyOnlyRelationView<>(person);
493 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
494
495 var p1 = new Variable("p1");
496 var p2 = new Variable("p2");
497
498 var predicate = DNF.builder("Count")
499 .parameters(p1)
500 .clause(
501 new RelationViewAtom(personView, p1),
502 new RelationViewAtom(false, friendMustView, p1, p2)
503 )
504 .build();
505
506 var store = ModelStore.builder()
507 .symbols(person, friend)
508 .with(ViatraModelQuery.ADAPTER)
509 .queries(predicate)
510 .build();
511
512 var model = store.createModel();
513 var personInterpretation = model.getInterpretation(person);
514 var friendInterpretation = model.getInterpretation(friend);
515 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
516 var predicateResultSet = queryEngine.getResultSet(predicate);
517
518 personInterpretation.put(Tuple.of(0), true);
519 personInterpretation.put(Tuple.of(1), true);
520 personInterpretation.put(Tuple.of(2), true);
521
522 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
523 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
524
525 queryEngine.flushChanges();
526 assertEquals(2, predicateResultSet.countResults());
527 }
528
529 @Test
447 void negativeWithQuantificationTest() { 530 void negativeWithQuantificationTest() {
448 var person = new Symbol<>("Person", 1, Boolean.class, false); 531 var person = new Symbol<>("Person", 1, Boolean.class, false);
449 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 532 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
@@ -494,6 +577,47 @@ class QueryTest {
494 } 577 }
495 578
496 @Test 579 @Test
580 void transitiveRelationViewTest() {
581 var person = new Symbol<>("Person", 1, Boolean.class, false);
582 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
583 var personView = new KeyOnlyRelationView<>(person);
584 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
585
586 var p1 = new Variable("p1");
587 var p2 = new Variable("p2");
588 var predicate = DNF.builder("TransitivePatternCall")
589 .parameters(p1, p2)
590 .clause(
591 new RelationViewAtom(personView, p1),
592 new RelationViewAtom(personView, p2),
593 new RelationViewAtom(CallPolarity.TRANSITIVE, friendMustView, p1, p2)
594 )
595 .build();
596
597 var store = ModelStore.builder()
598 .symbols(person, friend)
599 .with(ViatraModelQuery.ADAPTER)
600 .queries(predicate)
601 .build();
602
603 var model = store.createModel();
604 var personInterpretation = model.getInterpretation(person);
605 var friendInterpretation = model.getInterpretation(friend);
606 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
607 var predicateResultSet = queryEngine.getResultSet(predicate);
608
609 personInterpretation.put(Tuple.of(0), true);
610 personInterpretation.put(Tuple.of(1), true);
611 personInterpretation.put(Tuple.of(2), true);
612
613 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
614 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
615
616 queryEngine.flushChanges();
617 assertEquals(3, predicateResultSet.countResults());
618 }
619
620 @Test
497 void transitivePatternCallTest() { 621 void transitivePatternCallTest() {
498 var person = new Symbol<>("Person", 1, Boolean.class, false); 622 var person = new Symbol<>("Person", 1, Boolean.class, false);
499 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 623 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractSubstitutionAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractSubstitutionAtom.java
deleted file mode 100644
index af6a8f5a..00000000
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractSubstitutionAtom.java
+++ /dev/null
@@ -1,35 +0,0 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.representation.SymbolLike;
4import tools.refinery.store.query.Variable;
5
6import java.util.List;
7import java.util.Set;
8
9public abstract class AbstractSubstitutionAtom<T extends SymbolLike> implements DNFAtom {
10 private final T target;
11
12 private final List<Variable> substitution;
13
14 protected AbstractSubstitutionAtom(T target, List<Variable> substitution) {
15 if (substitution.size() != target.arity()) {
16 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(target.name(),
17 target.arity(), substitution.size()));
18 }
19 this.target = target;
20 this.substitution = substitution;
21 }
22
23 public T getTarget() {
24 return target;
25 }
26
27 public List<Variable> getSubstitution() {
28 return substitution;
29 }
30
31 @Override
32 public void collectAllVariables(Set<Variable> variables) {
33 variables.addAll(substitution);
34 }
35}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java
index 2e855246..6de5cd08 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java
@@ -1,21 +1,28 @@
1package tools.refinery.store.query.atom; 1package tools.refinery.store.query.atom;
2 2
3import tools.refinery.store.representation.SymbolLike;
4import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.Variable;
4import tools.refinery.store.representation.SymbolLike;
5 5
6import java.util.List; 6import java.util.List;
7import java.util.Objects; 7import java.util.Objects;
8import java.util.Set; 8import java.util.Set;
9 9
10public abstract class CallAtom<T extends SymbolLike> extends AbstractSubstitutionAtom<T> { 10public abstract class CallAtom<T extends SymbolLike> implements DNFAtom {
11 private final CallPolarity polarity; 11 private final CallPolarity polarity;
12 private final T target;
13 private final List<Variable> substitution;
12 14
13 protected CallAtom(CallPolarity polarity, T target, List<Variable> substitution) { 15 protected CallAtom(CallPolarity polarity, T target, List<Variable> substitution) {
14 super(target, substitution); 16 if (substitution.size() != target.arity()) {
17 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(target.name(),
18 target.arity(), substitution.size()));
19 }
15 if (polarity.isTransitive() && target.arity() != 2) { 20 if (polarity.isTransitive() && target.arity() != 2) {
16 throw new IllegalArgumentException("Transitive closures can only take binary relations"); 21 throw new IllegalArgumentException("Transitive closures can only take binary relations");
17 } 22 }
18 this.polarity = polarity; 23 this.polarity = polarity;
24 this.target = target;
25 this.substitution = substitution;
19 } 26 }
20 27
21 protected CallAtom(CallPolarity polarity, T target, Variable... substitution) { 28 protected CallAtom(CallPolarity polarity, T target, Variable... substitution) {
@@ -42,10 +49,18 @@ public abstract class CallAtom<T extends SymbolLike> extends AbstractSubstitutio
42 return polarity; 49 return polarity;
43 } 50 }
44 51
52 public T getTarget() {
53 return target;
54 }
55
56 public List<Variable> getSubstitution() {
57 return substitution;
58 }
59
45 @Override 60 @Override
46 public void collectAllVariables(Set<Variable> variables) { 61 public void collectAllVariables(Set<Variable> variables) {
47 if (polarity.isPositive()) { 62 if (polarity.isPositive()) {
48 super.collectAllVariables(variables); 63 variables.addAll(substitution);
49 } 64 }
50 } 65 }
51 66
@@ -53,14 +68,13 @@ public abstract class CallAtom<T extends SymbolLike> extends AbstractSubstitutio
53 public boolean equals(Object o) { 68 public boolean equals(Object o) {
54 if (this == o) return true; 69 if (this == o) return true;
55 if (o == null || getClass() != o.getClass()) return false; 70 if (o == null || getClass() != o.getClass()) return false;
56 CallAtom<?> that = (CallAtom<?>) o; 71 CallAtom<?> callAtom = (CallAtom<?>) o;
57 return Objects.equals(polarity, that.polarity) 72 return polarity == callAtom.polarity && Objects.equals(target, callAtom.target) && Objects.equals(substitution
58 && Objects.equals(getTarget(), that.getTarget()) 73 , callAtom.substitution);
59 && Objects.equals(getSubstitution(), that.getSubstitution());
60 } 74 }
61 75
62 @Override 76 @Override
63 public int hashCode() { 77 public int hashCode() {
64 return Objects.hash(polarity, getTarget(), getSubstitution()); 78 return Objects.hash(polarity, target, substitution);
65 } 79 }
66} 80}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationViewAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationViewAtom.java
index cf836541..a2b176c4 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationViewAtom.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationViewAtom.java
@@ -4,28 +4,29 @@ import tools.refinery.store.query.Variable;
4import tools.refinery.store.query.view.AnyRelationView; 4import tools.refinery.store.query.view.AnyRelationView;
5 5
6import java.util.List; 6import java.util.List;
7import java.util.Objects;
8 7
9public final class RelationViewAtom extends AbstractSubstitutionAtom<AnyRelationView> { 8public final class RelationViewAtom extends CallAtom<AnyRelationView> {
10 public RelationViewAtom(AnyRelationView target, List<Variable> substitution) { 9 public RelationViewAtom(CallPolarity polarity, AnyRelationView target, List<Variable> substitution) {
11 super(target, substitution); 10 super(polarity, target, substitution);
12 } 11 }
13 12
14 public RelationViewAtom(AnyRelationView target, Variable... substitution) { 13 public RelationViewAtom(CallPolarity polarity, AnyRelationView target, Variable... substitution) {
15 this(target, List.of(substitution)); 14 super(polarity, target, substitution);
16 } 15 }
17 16
18 @Override 17 public RelationViewAtom(boolean positive, AnyRelationView target, List<Variable> substitution) {
19 public boolean equals(Object o) { 18 super(positive, target, substitution);
20 if (this == o) return true;
21 if (o == null || getClass() != o.getClass()) return false;
22 RelationViewAtom relationViewAtom = (RelationViewAtom) o;
23 return Objects.equals(getTarget(), relationViewAtom.getTarget())
24 && Objects.equals(getSubstitution(), relationViewAtom.getSubstitution());
25 } 19 }
26 20
27 @Override 21 public RelationViewAtom(boolean positive, AnyRelationView target, Variable... substitution) {
28 public int hashCode() { 22 super(positive, target, substitution);
29 return Objects.hash(getTarget(), getSubstitution()); 23 }
24
25 public RelationViewAtom(AnyRelationView target, List<Variable> substitution) {
26 super(target, substitution);
27 }
28
29 public RelationViewAtom(AnyRelationView target, Variable... substitution) {
30 super(target, substitution);
30 } 31 }
31} 32}