aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--subprojects/frontend/src/language/problem.grammar2
-rw-r--r--subprojects/language-model/problem.aird16
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.ecore1
-rw-r--r--subprojects/language-model/src/main/resources/model/problem.genmodel1
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java91
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java27
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java49
-rw-r--r--subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java47
-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
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java8
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java4
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java18
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java20
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractCallAtom.java30
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/BasicCallKind.java26
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java66
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/CallKind.java23
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/ComparisonOperator.java22
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/ConstantAtom.java12
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/CountCallKind.java13
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/CountNotEqualsAtom.java31
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/DNFCallAtom.java40
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelation.java17
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelationAtom.java38
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/Modality.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationAtom.java32
-rw-r--r--subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java14
32 files changed, 648 insertions, 288 deletions
diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar
index 1e1ef70f..d630b9c4 100644
--- a/subprojects/frontend/src/language/problem.grammar
+++ b/subprojects/frontend/src/language/problem.grammar
@@ -153,7 +153,7 @@ sep1<separator, content> { content (separator content)* }
153 "\"" (![\\"\n] | "\\" (![\n] | "\n"))* "\"" 153 "\"" (![\\"\n] | "\\" (![\n] | "\n"))* "\""
154 } 154 }
155 155
156 ComparisonOp { ">" | ">=" | "<" | "<=" | "==" } 156 ComparisonOp { ">" | ">=" | "<" | "<=" | "==" | "!=" }
157 157
158 NotOp { "!" } 158 NotOp { "!" }
159 159
diff --git a/subprojects/language-model/problem.aird b/subprojects/language-model/problem.aird
index a02adf78..b877ac3b 100644
--- a/subprojects/language-model/problem.aird
+++ b/subprojects/language-model/problem.aird
@@ -7,7 +7,7 @@
7 <semanticResources>build/resources/main/model/problem.genmodel</semanticResources> 7 <semanticResources>build/resources/main/model/problem.genmodel</semanticResources>
8 <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg"> 8 <ownedViews xmi:type="viewpoint:DView" uid="_CsAAYKA4EeuqkpDnuik1sg">
9 <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/> 9 <viewpoint xmi:type="description:Viewpoint" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']"/>
10 <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="problem" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="289bce27-d1d3-48e4-ba46-87251d9867ca"> 10 <ownedRepresentationDescriptors xmi:type="viewpoint:DRepresentationDescriptor" uid="_CsYa4KA4EeuqkpDnuik1sg" name="problem" repPath="#_CsUwgKA4EeuqkpDnuik1sg" changeId="abc164e8-7868-44c9-a3f3-3aee2fd8009a">
11 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/> 11 <description xmi:type="description_1:DiagramDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']"/>
12 <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/> 12 <target xmi:type="ecore:EPackage" href="src/main/resources/model/problem.ecore#/"/>
13 </ownedRepresentationDescriptors> 13 </ownedRepresentationDescriptors>
@@ -601,11 +601,15 @@
601 <styles xmi:type="notation:FontStyle" xmi:id="_RN1AMRg0Ee2_erjsEmF9GQ" fontName="Noto Sans" fontHeight="8"/> 601 <styles xmi:type="notation:FontStyle" xmi:id="_RN1AMRg0Ee2_erjsEmF9GQ" fontName="Noto Sans" fontHeight="8"/>
602 <layoutConstraint xmi:type="notation:Location" xmi:id="_RN1AMhg0Ee2_erjsEmF9GQ"/> 602 <layoutConstraint xmi:type="notation:Location" xmi:id="_RN1AMhg0Ee2_erjsEmF9GQ"/>
603 </children> 603 </children>
604 <children xmi:type="notation:Node" xmi:id="_iSdpcFCpEe20ctwQhs06ZQ" type="3010" element="_iRojAFCpEe20ctwQhs06ZQ">
605 <styles xmi:type="notation:FontStyle" xmi:id="_iSdpcVCpEe20ctwQhs06ZQ" fontName="Noto Sans" fontHeight="8"/>
606 <layoutConstraint xmi:type="notation:Location" xmi:id="_iSdpclCpEe20ctwQhs06ZQ"/>
607 </children>
604 <styles xmi:type="notation:SortingStyle" xmi:id="_LinJ1Rg0Ee2_erjsEmF9GQ"/> 608 <styles xmi:type="notation:SortingStyle" xmi:id="_LinJ1Rg0Ee2_erjsEmF9GQ"/>
605 <styles xmi:type="notation:FilteringStyle" xmi:id="_LinJ1hg0Ee2_erjsEmF9GQ"/> 609 <styles xmi:type="notation:FilteringStyle" xmi:id="_LinJ1hg0Ee2_erjsEmF9GQ"/>
606 </children> 610 </children>
607 <styles xmi:type="notation:ShapeStyle" xmi:id="_LinJ0Rg0Ee2_erjsEmF9GQ" fontName="Noto Sans" fontHeight="8"/> 611 <styles xmi:type="notation:ShapeStyle" xmi:id="_LinJ0Rg0Ee2_erjsEmF9GQ" fontName="Noto Sans" fontHeight="8"/>
608 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_LinJ0hg0Ee2_erjsEmF9GQ" x="2232" y="60" width="120" height="111"/> 612 <layoutConstraint xmi:type="notation:Bounds" xmi:id="_LinJ0hg0Ee2_erjsEmF9GQ" x="2232" y="60" width="120" height="135"/>
609 </children> 613 </children>
610 <children xmi:type="notation:Node" xmi:id="_xp1icDNlEe2fD4dIhR_vzA" type="2003" element="_xpDfUDNlEe2fD4dIhR_vzA"> 614 <children xmi:type="notation:Node" xmi:id="_xp1icDNlEe2fD4dIhR_vzA" type="2003" element="_xpDfUDNlEe2fD4dIhR_vzA">
611 <children xmi:type="notation:Node" xmi:id="_xp2JgDNlEe2fD4dIhR_vzA" type="5007"/> 615 <children xmi:type="notation:Node" xmi:id="_xp2JgDNlEe2fD4dIhR_vzA" type="5007"/>
@@ -3754,6 +3758,14 @@
3754 </ownedStyle> 3758 </ownedStyle>
3755 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/> 3759 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
3756 </ownedElements> 3760 </ownedElements>
3761 <ownedElements xmi:type="diagram:DNodeListElement" uid="_iRojAFCpEe20ctwQhs06ZQ" name="NOT_EQ" tooltipText="">
3762 <target xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//ComparisonOp/NOT_EQ"/>
3763 <semanticElements xmi:type="ecore:EEnumLiteral" href="src/main/resources/model/problem.ecore#//ComparisonOp/NOT_EQ"/>
3764 <ownedStyle xmi:type="diagram:BundledImage" uid="_iRqYMFCpEe20ctwQhs06ZQ" labelAlignment="LEFT">
3765 <description xmi:type="style:BundledImageDescription" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']/@style"/>
3766 </ownedStyle>
3767 <actualMapping xmi:type="description_1:NodeMapping" href="platform:/plugin/org.eclipse.emf.ecoretools.design/description/ecore.odesign#//@ownedViewpoints[name='Design']/@ownedRepresentations[name='Entities']/@defaultLayer/@containerMappings[name='EC%20EEnum']/@subNodeMappings[name='EC%20EEnumLiteral']"/>
3768 </ownedElements>
3757 </ownedDiagramElements> 3769 </ownedDiagramElements>
3758 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_xpDfUDNlEe2fD4dIhR_vzA" name="ReferenceKind" tooltipText="" width="12" height="10"> 3770 <ownedDiagramElements xmi:type="diagram:DNodeList" uid="_xpDfUDNlEe2fD4dIhR_vzA" name="ReferenceKind" tooltipText="" width="12" height="10">
3759 <target xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//ReferenceKind"/> 3771 <target xmi:type="ecore:EEnum" href="src/main/resources/model/problem.ecore#//ReferenceKind"/>
diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore b/subprojects/language-model/src/main/resources/model/problem.ecore
index 0106dd61..4ef023c1 100644
--- a/subprojects/language-model/src/main/resources/model/problem.ecore
+++ b/subprojects/language-model/src/main/resources/model/problem.ecore
@@ -191,6 +191,7 @@
191 <eLiterals name="GREATER" value="2"/> 191 <eLiterals name="GREATER" value="2"/>
192 <eLiterals name="GREATER_EQ" value="3"/> 192 <eLiterals name="GREATER_EQ" value="3"/>
193 <eLiterals name="EQ" value="4"/> 193 <eLiterals name="EQ" value="4"/>
194 <eLiterals name="NOT_EQ" value="5"/>
194 </eClassifiers> 195 </eClassifiers>
195 <eClassifiers xsi:type="ecore:EEnum" name="ReferenceKind"> 196 <eClassifiers xsi:type="ecore:EEnum" name="ReferenceKind">
196 <eLiterals name="DEFAULT"/> 197 <eLiterals name="DEFAULT"/>
diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel b/subprojects/language-model/src/main/resources/model/problem.genmodel
index da70f922..9ff4989b 100644
--- a/subprojects/language-model/src/main/resources/model/problem.genmodel
+++ b/subprojects/language-model/src/main/resources/model/problem.genmodel
@@ -28,6 +28,7 @@
28 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/GREATER"/> 28 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/GREATER"/>
29 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/GREATER_EQ"/> 29 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/GREATER_EQ"/>
30 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/EQ"/> 30 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/EQ"/>
31 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ComparisonOp/NOT_EQ"/>
31 </genEnums> 32 </genEnums>
32 <genEnums typeSafeEnumCompatible="false" ecoreEnum="problem.ecore#//ReferenceKind"> 33 <genEnums typeSafeEnumCompatible="false" ecoreEnum="problem.ecore#//ReferenceKind">
33 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ReferenceKind/DEFAULT"/> 34 <genEnumLiterals ecoreEnumLiteral="problem.ecore#//ReferenceKind/DEFAULT"/>
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java
index 830d4a2c..0378983c 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/ModelInitializer.java
@@ -1,9 +1,96 @@
1package tools.refinery.language.semantics.model; 1package tools.refinery.language.semantics.model;
2 2
3import tools.refinery.language.utils.CollectedSymbols; 3import com.google.inject.Inject;
4import org.eclipse.collections.api.factory.primitive.ObjectIntMaps;
5import org.eclipse.collections.api.map.primitive.MutableObjectIntMap;
6import tools.refinery.language.model.problem.*;
7import tools.refinery.language.semantics.model.internal.DecisionTree;
8import tools.refinery.language.utils.ProblemDesugarer;
9import tools.refinery.language.utils.RelationInfo;
10import tools.refinery.store.model.representation.Relation;
11import tools.refinery.store.model.representation.TruthValue;
12import tools.refinery.store.tuple.Tuple;
13
14import java.util.HashMap;
15import java.util.Map;
4 16
5public class ModelInitializer { 17public class ModelInitializer {
6 public void createModel(CollectedSymbols symbols) { 18 @Inject
19 private ProblemDesugarer desugarer;
20
21 private final MutableObjectIntMap<Node> nodeTrace = ObjectIntMaps.mutable.empty();
22
23 private final Map<tools.refinery.language.model.problem.Relation, Relation<TruthValue>> relationTrace =
24 new HashMap<>();
25
26 private int nodeCount = 0;
27
28 public void createModel(Problem problem) {
29 var builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalArgumentException(
30 "Problem has no builtin library"));
31 var collectedSymbols = desugarer.collectSymbols(problem);
32 for (var node : collectedSymbols.nodes().keySet()) {
33 nodeTrace.put(node, nodeCount);
34 nodeCount += 1;
35 }
36 for (var pair : collectedSymbols.relations().entrySet()) {
37 var relation = pair.getKey();
38 var relationInfo = pair.getValue();
39 var isEqualsRelation = relation == builtinSymbols.equals();
40 var decisionTree = mergeAssertions(relationInfo, isEqualsRelation);
41 var defaultValue = isEqualsRelation ? TruthValue.FALSE : TruthValue.UNKNOWN;
42 relationTrace.put(relation, new Relation<>(relationInfo.name(), relationInfo.arity(), defaultValue));
43 }
44 }
45
46 private DecisionTree mergeAssertions(RelationInfo relationInfo, boolean isEqualsRelation) {
47 var arity = relationInfo.arity();
48 var defaultAssertions = new DecisionTree(arity, isEqualsRelation ? null : TruthValue.UNKNOWN);
49 var assertions = new DecisionTree(arity);
50 for (var assertion : relationInfo.assertions()) {
51 var tuple = getTuple(assertion);
52 var value = getTruthValue(assertion.getValue());
53 if (assertion.isDefault()) {
54 defaultAssertions.mergeValue(tuple, value);
55 } else {
56 assertions.mergeValue(tuple, value);
57 }
58 }
59 defaultAssertions.overwriteValues(assertions);
60 if (isEqualsRelation) {
61 for (int i = 0; i < nodeCount; i++) {
62 defaultAssertions.setIfMissing(Tuple.of(i, i), TruthValue.TRUE);
63 }
64 defaultAssertions.setAllMissing(TruthValue.FALSE);
65 }
66 return defaultAssertions;
67 }
68
69 private Tuple getTuple(Assertion assertion) {
70 var arguments = assertion.getArguments();
71 int arity = arguments.size();
72 var nodes = new int[arity];
73 for (int i = 0; i < arity; i++) {
74 var argument = arguments.get(i);
75 if (argument instanceof NodeAssertionArgument nodeArgument) {
76 nodes[i] = nodeTrace.getOrThrow(nodeArgument.getNode());
77 } else if (argument instanceof ConstantAssertionArgument constantArgument) {
78 nodes[i] = nodeTrace.getOrThrow(constantArgument.getNode());
79 } else if (argument instanceof WildcardAssertionArgument) {
80 nodes[i] = -1;
81 } else {
82 throw new IllegalArgumentException("Unknown assertion argument: " + argument);
83 }
84 }
85 return Tuple.of(nodes);
86 }
7 87
88 private static TruthValue getTruthValue(LogicValue value) {
89 return switch (value) {
90 case TRUE -> TruthValue.TRUE;
91 case FALSE -> TruthValue.FALSE;
92 case UNKNOWN -> TruthValue.UNKNOWN;
93 case ERROR -> TruthValue.ERROR;
94 };
8 } 95 }
9} 96}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
index f514e96c..2a8429a3 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -72,7 +72,7 @@ NegativeLiteral:
72 modality=Modality? "!" atom=Atom; 72 modality=Modality? "!" atom=Atom;
73 73
74enum ComparisonOp: 74enum ComparisonOp:
75 LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="=="; 75 LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!=";
76 76
77CountLiteral: 77CountLiteral:
78 modality=Modality? "count" atom=Atom op=ComparisonOp threshold=INT; 78 modality=Modality? "count" atom=Atom op=ComparisonOp threshold=INT;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java b/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java
index ae56e3a5..2253d257 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/RelationInfo.java
@@ -1,27 +1,24 @@
1package tools.refinery.language.utils; 1package tools.refinery.language.utils;
2 2
3import tools.refinery.language.model.problem.*;
4
3import java.util.ArrayList; 5import java.util.ArrayList;
4import java.util.Collection; 6import java.util.Collection;
5import java.util.List; 7import java.util.List;
6 8
7import tools.refinery.language.model.problem.Assertion; 9public record RelationInfo(String name, ContainmentRole containmentRole, List<Parameter> parameters,
8import tools.refinery.language.model.problem.Conjunction; 10 Multiplicity multiplicity, Relation opposite, Collection<Conjunction> bodies,
9import tools.refinery.language.model.problem.Multiplicity; 11 Collection<Assertion> assertions, Collection<TypeScope> typeScopes) {
10import tools.refinery.language.model.problem.Parameter; 12 public RelationInfo(String name, ContainmentRole containmentRole, List<Parameter> parameters,
11import tools.refinery.language.model.problem.PredicateKind; 13 Multiplicity multiplicity, Relation opposite, Collection<Conjunction> bodies) {
12import tools.refinery.language.model.problem.Relation; 14 this(name, containmentRole, parameters, multiplicity, opposite, bodies, new ArrayList<>(), new ArrayList<>());
13import tools.refinery.language.model.problem.TypeScope;
14
15public record RelationInfo(String name, PredicateKind kind, List<Parameter> parameters, Multiplicity multiplicity,
16 Relation opposite, Collection<Conjunction> bodies, Collection<Assertion> defaultAssertions,
17 Collection<Assertion> assertions, Collection<TypeScope> typeScopes) {
18 public RelationInfo(String name, PredicateKind kind, List<Parameter> parameters, Multiplicity multiplicity,
19 Relation opposite, Collection<Conjunction> bodies) {
20 this(name, kind, parameters, multiplicity, opposite, bodies, new ArrayList<>(), new ArrayList<>(),
21 new ArrayList<>());
22 } 15 }
23 16
24 public boolean hasDefinition() { 17 public boolean hasDefinition() {
25 return bodies != null && !bodies.isEmpty(); 18 return bodies != null && !bodies.isEmpty();
26 } 19 }
20
21 public int arity() {
22 return parameters.size();
23 }
27} 24}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java b/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java
index a386db7f..210e96ab 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/utils/SymbolCollector.java
@@ -19,10 +19,12 @@ class SymbolCollector {
19 private IQualifiedNameConverter qualifiedNameConverter; 19 private IQualifiedNameConverter qualifiedNameConverter;
20 20
21 @Inject 21 @Inject
22 ProblemDesugarer desugarer; 22 private ProblemDesugarer desugarer;
23 23
24 private BuiltinSymbols builtinSymbols; 24 private BuiltinSymbols builtinSymbols;
25
25 private final Map<Node, NodeInfo> nodes = new LinkedHashMap<>(); 26 private final Map<Node, NodeInfo> nodes = new LinkedHashMap<>();
27
26 private final Map<Relation, RelationInfo> relations = new LinkedHashMap<>(); 28 private final Map<Relation, RelationInfo> relations = new LinkedHashMap<>();
27 29
28 public CollectedSymbols collectSymbols(Problem problem) { 30 public CollectedSymbols collectSymbols(Problem problem) {
@@ -54,9 +56,10 @@ class SymbolCollector {
54 } 56 }
55 57
56 private void collectPredicate(PredicateDefinition predicateDefinition) { 58 private void collectPredicate(PredicateDefinition predicateDefinition) {
59 var predicateKind = predicateDefinition.getKind();
57 var info = new RelationInfo(getQualifiedNameString(predicateDefinition), 60 var info = new RelationInfo(getQualifiedNameString(predicateDefinition),
58 predicateDefinition.getKind(), 61 ContainmentRole.fromPredicateKind(predicateKind), predicateDefinition.getParameters(), null, null,
59 predicateDefinition.getParameters(), null, null, predicateDefinition.getBodies()); 62 predicateDefinition.getBodies());
60 relations.put(predicateDefinition, info); 63 relations.put(predicateDefinition, info);
61 } 64 }
62 65
@@ -65,10 +68,10 @@ class SymbolCollector {
65 // contained, including data types. 68 // contained, including data types.
66 var contained = 69 var contained =
67 classDeclaration != builtinSymbols.node() && classDeclaration != builtinSymbols.domain(); 70 classDeclaration != builtinSymbols.node() && classDeclaration != builtinSymbols.domain();
68 var classKind = contained ? PredicateKind.CONTAINED : PredicateKind.DEFAULT; 71 var containmentRole = contained ? ContainmentRole.CONTAINED : ContainmentRole.NONE;
69 var instanceParameter = ProblemFactory.eINSTANCE.createParameter(); 72 var instanceParameter = ProblemFactory.eINSTANCE.createParameter();
70 instanceParameter.setName("instance"); 73 instanceParameter.setName("instance");
71 var classInfo = new RelationInfo(getQualifiedNameString(classDeclaration), classKind, 74 var classInfo = new RelationInfo(getQualifiedNameString(classDeclaration), containmentRole,
72 List.of(instanceParameter), null, null, List.of()); 75 List.of(instanceParameter), null, null, List.of());
73 relations.put(classDeclaration, classInfo); 76 relations.put(classDeclaration, classInfo);
74 collectReferences(classDeclaration); 77 collectReferences(classDeclaration);
@@ -76,9 +79,9 @@ class SymbolCollector {
76 79
77 private void collectReferences(ClassDeclaration classDeclaration) { 80 private void collectReferences(ClassDeclaration classDeclaration) {
78 for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) { 81 for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) {
79 var referenceKind = desugarer.isContainmentReference(referenceDeclaration) ? 82 var referenceRole = desugarer.isContainmentReference(referenceDeclaration) ?
80 PredicateKind.CONTAINMENT : 83 ContainmentRole.CONTAINMENT :
81 PredicateKind.DEFAULT; 84 ContainmentRole.NONE;
82 var sourceParameter = ProblemFactory.eINSTANCE.createParameter(); 85 var sourceParameter = ProblemFactory.eINSTANCE.createParameter();
83 sourceParameter.setName("source"); 86 sourceParameter.setName("source");
84 sourceParameter.setParameterType(classDeclaration); 87 sourceParameter.setParameterType(classDeclaration);
@@ -91,7 +94,7 @@ class SymbolCollector {
91 multiplicity = exactMultiplicity; 94 multiplicity = exactMultiplicity;
92 } 95 }
93 targetParameter.setParameterType(referenceDeclaration.getReferenceType()); 96 targetParameter.setParameterType(referenceDeclaration.getReferenceType());
94 var referenceInfo = new RelationInfo(getQualifiedNameString(referenceDeclaration), referenceKind, 97 var referenceInfo = new RelationInfo(getQualifiedNameString(referenceDeclaration), referenceRole,
95 List.of(sourceParameter, targetParameter), multiplicity, referenceDeclaration.getOpposite(), 98 List.of(sourceParameter, targetParameter), multiplicity, referenceDeclaration.getOpposite(),
96 List.of()); 99 List.of());
97 this.relations.put(referenceDeclaration, referenceInfo); 100 this.relations.put(referenceDeclaration, referenceInfo);
@@ -101,7 +104,7 @@ class SymbolCollector {
101 private void collectEnum(EnumDeclaration enumDeclaration) { 104 private void collectEnum(EnumDeclaration enumDeclaration) {
102 var instanceParameter = ProblemFactory.eINSTANCE.createParameter(); 105 var instanceParameter = ProblemFactory.eINSTANCE.createParameter();
103 instanceParameter.setName("instance"); 106 instanceParameter.setName("instance");
104 var info = new RelationInfo(getQualifiedNameString(enumDeclaration), PredicateKind.DEFAULT, 107 var info = new RelationInfo(getQualifiedNameString(enumDeclaration), ContainmentRole.NONE,
105 List.of(instanceParameter), null, null, List.of()); 108 List.of(instanceParameter), null, null, List.of());
106 this.relations.put(enumDeclaration, info); 109 this.relations.put(enumDeclaration, info);
107 } 110 }
@@ -172,6 +175,8 @@ class SymbolCollector {
172 collectAssertion(assertion); 175 collectAssertion(assertion);
173 } else if (statement instanceof NodeValueAssertion nodeValueAssertion) { 176 } else if (statement instanceof NodeValueAssertion nodeValueAssertion) {
174 collectNodeValueAssertion(nodeValueAssertion); 177 collectNodeValueAssertion(nodeValueAssertion);
178 } else if (statement instanceof PredicateDefinition predicateDefinition) {
179 collectPredicateAssertion(predicateDefinition);
175 } else if (statement instanceof ClassDeclaration classDeclaration) { 180 } else if (statement instanceof ClassDeclaration classDeclaration) {
176 collectClassAssertion(classDeclaration); 181 collectClassAssertion(classDeclaration);
177 } else if (statement instanceof EnumDeclaration enumDeclaration) { 182 } else if (statement instanceof EnumDeclaration enumDeclaration) {
@@ -190,11 +195,7 @@ class SymbolCollector {
190 // Problem during editing. The errors can still be detected by the Problem validator. 195 // Problem during editing. The errors can still be detected by the Problem validator.
191 return; 196 return;
192 } 197 }
193 if (assertion.isDefault()) { 198 relationInfo.assertions().add(assertion);
194 relationInfo.defaultAssertions().add(assertion);
195 } else {
196 relationInfo.assertions().add(assertion);
197 }
198 for (var argument : assertion.getArguments()) { 199 for (var argument : assertion.getArguments()) {
199 if (argument instanceof ConstantAssertionArgument constantAssertionArgument) { 200 if (argument instanceof ConstantAssertionArgument constantAssertionArgument) {
200 var constantNode = constantAssertionArgument.getNode(); 201 var constantNode = constantAssertionArgument.getNode();
@@ -239,6 +240,14 @@ class SymbolCollector {
239 addAssertion(dataType, LogicValue.TRUE, node); 240 addAssertion(dataType, LogicValue.TRUE, node);
240 } 241 }
241 242
243 private void collectPredicateAssertion(PredicateDefinition predicateDefinition) {
244 if (predicateDefinition.getKind() != PredicateKind.ERROR) {
245 return;
246 }
247 int arity = predicateDefinition.getParameters().size();
248 addAssertion(predicateDefinition, LogicValue.FALSE, new Node[arity]);
249 }
250
242 private void collectClassAssertion(ClassDeclaration classDeclaration) { 251 private void collectClassAssertion(ClassDeclaration classDeclaration) {
243 var node = classDeclaration.getNewNode(); 252 var node = classDeclaration.getNewNode();
244 if (node == null) { 253 if (node == null) {
@@ -259,8 +268,14 @@ class SymbolCollector {
259 var assertion = ProblemFactory.eINSTANCE.createAssertion(); 268 var assertion = ProblemFactory.eINSTANCE.createAssertion();
260 assertion.setRelation(relation); 269 assertion.setRelation(relation);
261 for (var node : nodes) { 270 for (var node : nodes) {
262 var argument = ProblemFactory.eINSTANCE.createNodeAssertionArgument(); 271 AssertionArgument argument;
263 argument.setNode(node); 272 if (node == null) {
273 argument = ProblemFactory.eINSTANCE.createWildcardAssertionArgument();
274 } else {
275 var nodeArgument = ProblemFactory.eINSTANCE.createNodeAssertionArgument();
276 nodeArgument.setNode(node);
277 argument = nodeArgument;
278 }
264 assertion.getArguments().add(argument); 279 assertion.getArguments().add(argument);
265 } 280 }
266 assertion.setValue(logicValue); 281 assertion.setValue(logicValue);
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java
index e2e3218c..a05f3335 100644
--- a/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java
+++ b/subprojects/language/src/test/java/tools/refinery/language/tests/utils/SymbolCollectorTest.java
@@ -12,6 +12,7 @@ import org.junit.jupiter.params.provider.MethodSource;
12import tools.refinery.language.model.problem.*; 12import tools.refinery.language.model.problem.*;
13import tools.refinery.language.model.tests.utils.ProblemParseHelper; 13import tools.refinery.language.model.tests.utils.ProblemParseHelper;
14import tools.refinery.language.tests.ProblemInjectorProvider; 14import tools.refinery.language.tests.ProblemInjectorProvider;
15import tools.refinery.language.utils.ContainmentRole;
15import tools.refinery.language.utils.ProblemDesugarer; 16import tools.refinery.language.utils.ProblemDesugarer;
16 17
17import java.util.stream.Stream; 18import java.util.stream.Stream;
@@ -61,7 +62,7 @@ class SymbolCollectorTest {
61 assertThat(collectedSymbols.relations(), hasKey(classDeclaration)); 62 assertThat(collectedSymbols.relations(), hasKey(classDeclaration));
62 var classInfo = collectedSymbols.relations().get(classDeclaration); 63 var classInfo = collectedSymbols.relations().get(classDeclaration);
63 assertThat(classInfo.parameters(), hasSize(1)); 64 assertThat(classInfo.parameters(), hasSize(1));
64 assertThat(classInfo.kind(), is(PredicateKind.CONTAINED)); 65 assertThat(classInfo.containmentRole(), is(ContainmentRole.CONTAINED));
65 assertThat(classInfo.hasDefinition(), is(false)); 66 assertThat(classInfo.hasDefinition(), is(false));
66 var newNode = classDeclaration.getNewNode(); 67 var newNode = classDeclaration.getNewNode();
67 assertThat(collectedSymbols.nodes(), hasKey(newNode)); 68 assertThat(collectedSymbols.nodes(), hasKey(newNode));
@@ -96,11 +97,11 @@ class SymbolCollectorTest {
96 var barInfo = collectedSymbols.relations().get(barReference); 97 var barInfo = collectedSymbols.relations().get(barReference);
97 var quuxReference = fooClass.reference("quux"); 98 var quuxReference = fooClass.reference("quux");
98 var quuxInfo = collectedSymbols.relations().get(quuxReference); 99 var quuxInfo = collectedSymbols.relations().get(quuxReference);
99 assertThat(barInfo.kind(), is(PredicateKind.DEFAULT)); 100 assertThat(barInfo.containmentRole(), is(ContainmentRole.NONE));
100 assertThat(barInfo.opposite(), is(quuxReference)); 101 assertThat(barInfo.opposite(), is(quuxReference));
101 assertThat(barInfo.multiplicity(), is(instanceOf(UnboundedMultiplicity.class))); 102 assertThat(barInfo.multiplicity(), is(instanceOf(UnboundedMultiplicity.class)));
102 assertThat(barInfo.hasDefinition(), is(false)); 103 assertThat(barInfo.hasDefinition(), is(false));
103 assertThat(quuxInfo.kind(), is(PredicateKind.DEFAULT)); 104 assertThat(quuxInfo.containmentRole(), is(ContainmentRole.NONE));
104 assertThat(quuxInfo.opposite(), is(barReference)); 105 assertThat(quuxInfo.opposite(), is(barReference));
105 assertThat(quuxInfo.multiplicity(), is(instanceOf(ExactMultiplicity.class))); 106 assertThat(quuxInfo.multiplicity(), is(instanceOf(ExactMultiplicity.class)));
106 assertThat(quuxInfo.multiplicity(), hasProperty("exactValue", is(1))); 107 assertThat(quuxInfo.multiplicity(), hasProperty("exactValue", is(1)));
@@ -115,8 +116,8 @@ class SymbolCollectorTest {
115 } 116 }
116 """); 117 """);
117 var collectedSymbols = desugarer.collectSymbols(problem.get()); 118 var collectedSymbols = desugarer.collectSymbols(problem.get());
118 assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).kind(), 119 assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).containmentRole(),
119 is(PredicateKind.CONTAINMENT)); 120 is(ContainmentRole.CONTAINMENT));
120 } 121 }
121 122
122 @Test 123 @Test
@@ -127,8 +128,8 @@ class SymbolCollectorTest {
127 } 128 }
128 """); 129 """);
129 var collectedSymbols = desugarer.collectSymbols(problem.get()); 130 var collectedSymbols = desugarer.collectSymbols(problem.get());
130 assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).kind(), 131 assertThat(collectedSymbols.relations().get(problem.findClass("Foo").reference("bar")).containmentRole(),
131 is(PredicateKind.CONTAINMENT)); 132 is(ContainmentRole.CONTAINMENT));
132 } 133 }
133 134
134 @Test 135 @Test
@@ -141,27 +142,27 @@ class SymbolCollectorTest {
141 var collectedSymbols = desugarer.collectSymbols(problem.get()); 142 var collectedSymbols = desugarer.collectSymbols(problem.get());
142 var enumDeclaration = problem.findEnum("Foo"); 143 var enumDeclaration = problem.findEnum("Foo");
143 var enumInfo = collectedSymbols.relations().get(enumDeclaration.get()); 144 var enumInfo = collectedSymbols.relations().get(enumDeclaration.get());
144 assertThat(enumInfo.kind(), is(PredicateKind.DEFAULT)); 145 assertThat(enumInfo.containmentRole(), is(ContainmentRole.NONE));
145 assertThat(enumInfo.assertions(), assertsNode(enumDeclaration.literal("bar"), LogicValue.TRUE)); 146 assertThat(enumInfo.assertions(), assertsNode(enumDeclaration.literal("bar"), LogicValue.TRUE));
146 assertThat(enumInfo.assertions(), assertsNode(enumDeclaration.literal("quux"), LogicValue.TRUE)); 147 assertThat(enumInfo.assertions(), assertsNode(enumDeclaration.literal("quux"), LogicValue.TRUE));
147 } 148 }
148 149
149 @ParameterizedTest 150 @ParameterizedTest
150 @MethodSource 151 @MethodSource
151 void predicateTest(String keyword, PredicateKind kind) { 152 void predicateTest(String keyword, ContainmentRole containmentRole) {
152 var problem = parseHelper.parse(keyword + " foo(node x) <-> domain(x); data(x)."); 153 var problem = parseHelper.parse(keyword + " foo(node x) <-> domain(x); data(x).");
153 var collectedSymbols = desugarer.collectSymbols(problem.get()); 154 var collectedSymbols = desugarer.collectSymbols(problem.get());
154 var predicateInfo = collectedSymbols.relations().get(problem.pred("foo").get()); 155 var predicateInfo = collectedSymbols.relations().get(problem.pred("foo").get());
155 assertThat(predicateInfo.kind(), is(kind)); 156 assertThat(predicateInfo.containmentRole(), is(containmentRole));
156 assertThat(predicateInfo.parameters(), hasSize(1)); 157 assertThat(predicateInfo.parameters(), hasSize(1));
157 assertThat(predicateInfo.bodies(), hasSize(2)); 158 assertThat(predicateInfo.bodies(), hasSize(2));
158 assertThat(predicateInfo.hasDefinition(), is(true)); 159 assertThat(predicateInfo.hasDefinition(), is(true));
159 } 160 }
160 161
161 static Stream<Arguments> predicateTest() { 162 static Stream<Arguments> predicateTest() {
162 return Stream.of(Arguments.of("pred", PredicateKind.DEFAULT), Arguments.of("error", PredicateKind.ERROR), 163 return Stream.of(Arguments.of("pred", ContainmentRole.NONE), Arguments.of("error", ContainmentRole.NONE),
163 Arguments.of("contained", PredicateKind.CONTAINED), Arguments.of("containment", 164 Arguments.of("contained", ContainmentRole.CONTAINED), Arguments.of("containment",
164 PredicateKind.CONTAINMENT)); 165 ContainmentRole.CONTAINMENT));
165 } 166 }
166 167
167 @ParameterizedTest 168 @ParameterizedTest
@@ -184,7 +185,7 @@ class SymbolCollectorTest {
184 default foo(a): %s. 185 default foo(a): %s.
185 """.formatted(keyword)); 186 """.formatted(keyword));
186 var collectedSymbols = desugarer.collectSymbols(problem.get()); 187 var collectedSymbols = desugarer.collectSymbols(problem.get());
187 assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).defaultAssertions(), 188 assertThat(collectedSymbols.relations().get(problem.pred("foo").get()).assertions(),
188 assertsNode(problem.node("a"), value)); 189 assertsNode(problem.node("a"), value));
189 } 190 }
190 191
@@ -254,6 +255,10 @@ class SymbolCollectorTest {
254 LogicValue.UNKNOWN)); 255 LogicValue.UNKNOWN));
255 } 256 }
256 257
258 static Stream<Arguments> valueTypes() {
259 return Stream.of(Arguments.of("3", "int"), Arguments.of("3.14", "real"), Arguments.of("\"foo\"", "string"));
260 }
261
257 @Test 262 @Test
258 void invalidProblemTest() { 263 void invalidProblemTest() {
259 var problem = parseHelper.parse(""" 264 var problem = parseHelper.parse("""
@@ -265,8 +270,18 @@ class SymbolCollectorTest {
265 assertDoesNotThrow(() -> desugarer.collectSymbols(problem)); 270 assertDoesNotThrow(() -> desugarer.collectSymbols(problem));
266 } 271 }
267 272
268 static Stream<Arguments> valueTypes() { 273 @Test
269 return Stream.of(Arguments.of("3", "int"), Arguments.of("3.14", "real"), Arguments.of("\"foo\"", "string")); 274 void errorAssertionTest() {
275 var problem = parseHelper.parse("""
276 error foo(node a, node b) <-> equals(a, b).
277 """);
278 var collectedSymbols = desugarer.collectSymbols(problem.get());
279 var fooInfo = collectedSymbols.relations().get(problem.pred("foo").get());
280 assertThat(fooInfo.assertions(), hasSize(1));
281 var assertion = fooInfo.assertions().stream().findFirst().orElseThrow();
282 assertThat(assertion.getValue(), is(LogicValue.FALSE));
283 assertThat(assertion.getArguments(), hasSize(2));
284 assertThat(assertion.getArguments(), everyItem(instanceOf(WildcardAssertionArgument.class)));
270 } 285 }
271 286
272 private static Matcher<Iterable<? super Assertion>> assertsNode(Node node, LogicValue value) { 287 private static Matcher<Iterable<? super Assertion>> assertsNode(Node node, LogicValue value) {
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();
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java
index f68734ab..9397dede 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java
@@ -35,7 +35,7 @@ public class ImmutableNode<K, V> extends Node<K, V> {
35 35
36 /** 36 /**
37 * Constructor that copies a mutable node to an immutable. 37 * Constructor that copies a mutable node to an immutable.
38 * 38 *
39 * @param node A mutable node. 39 * @param node A mutable node.
40 * @param cache A cache of existing immutable nodes. It can be used to search 40 * @param cache A cache of existing immutable nodes. It can be used to search
41 * and place reference immutable nodes. It can be null, if no cache 41 * and place reference immutable nodes. It can be null, if no cache
@@ -151,7 +151,7 @@ public class ImmutableNode<K, V> extends Node<K, V> {
151 oldValue.setOldValue(value); 151 oldValue.setOldValue(value);
152 return this; 152 return this;
153 } else { 153 } else {
154 // update existing value 154 // update existing nodeId
155 MutableNode<K, V> mutable = this.toMutable(); 155 MutableNode<K, V> mutable = this.toMutable();
156 return mutable.updateValue(value, oldValue, selectedHashFragment); 156 return mutable.updateValue(value, oldValue, selectedHashFragment);
157 } 157 }
@@ -161,7 +161,7 @@ public class ImmutableNode<K, V> extends Node<K, V> {
161 oldValue.setOldValue(defaultValue); 161 oldValue.setOldValue(defaultValue);
162 return this; 162 return this;
163 } else { 163 } else {
164 // add new key + value 164 // add new key + nodeId
165 MutableNode<K, V> mutable = this.toMutable(); 165 MutableNode<K, V> mutable = this.toMutable();
166 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); 166 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth);
167 } 167 }
@@ -182,7 +182,7 @@ public class ImmutableNode<K, V> extends Node<K, V> {
182 return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue)); 182 return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue));
183 } 183 }
184 } else { 184 } else {
185 // add new key + value 185 // add new key + nodeId
186 MutableNode<K, V> mutable = this.toMutable(); 186 MutableNode<K, V> mutable = this.toMutable();
187 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); 187 return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth);
188 } 188 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java
index 42333635..a4ca813c 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java
@@ -10,13 +10,13 @@ import tools.refinery.store.map.VersionedMap;
10 10
11/** 11/**
12 * A cursor representing the difference between two states of a map. 12 * A cursor representing the difference between two states of a map.
13 * 13 *
14 * @author Oszkar Semerath 14 * @author Oszkar Semerath
15 * 15 *
16 */ 16 */
17public class MapDiffCursor<K, V> implements DiffCursor<K, V>, Cursor<K, V> { 17public class MapDiffCursor<K, V> implements DiffCursor<K, V>, Cursor<K, V> {
18 /** 18 /**
19 * Default value representing missing elements. 19 * Default nodeId representing missing elements.
20 */ 20 */
21 private V defaultValue; 21 private V defaultValue;
22 private MapCursor<K, V> cursor1; 22 private MapCursor<K, V> cursor1;
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
index 54853010..7c3cf7e8 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
@@ -31,7 +31,7 @@ public class MutableNode<K, V> extends Node<K, V> {
31 31
32 /** 32 /**
33 * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode} 33 * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode}
34 * 34 *
35 * @param node 35 * @param node
36 */ 36 */
37 protected MutableNode(ImmutableNode<K, V> node) { 37 protected MutableNode(ImmutableNode<K, V> node) {
@@ -107,18 +107,18 @@ public class MutableNode<K, V> extends Node<K, V> {
107 } 107 }
108 } 108 }
109 } else { 109 } else {
110 // If it does not have key, check for value 110 // If it does not have key, check for nodeId
111 @SuppressWarnings("unchecked") 111 @SuppressWarnings("unchecked")
112 var nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1]; 112 var nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1];
113 if (nodeCandidate != null) { 113 if (nodeCandidate != null) {
114 // If it has value, it is a subnode -> upate that 114 // If it has nodeId, it is a subnode -> upate that
115 var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, 115 var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue,
116 newHash(hashProvider, key, hash, depth + 1), depth + 1); 116 newHash(hashProvider, key, hash, depth + 1), depth + 1);
117 return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue)); 117 return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue));
118 } else { 118 } else {
119 // If it does not have value, put it in the empty place 119 // If it does not have nodeId, put it in the empty place
120 if (value == defaultValue) { 120 if (value == defaultValue) {
121 // dont need to add new key-value pair 121 // dont need to add new key-nodeId pair
122 oldValueBox.setOldValue(defaultValue); 122 oldValueBox.setOldValue(defaultValue);
123 return this; 123 return this;
124 } else { 124 } else {
@@ -138,8 +138,8 @@ public class MutableNode<K, V> extends Node<K, V> {
138 } 138 }
139 139
140 /** 140 /**
141 * Updates an entry in a selected hash-fragment to a non-default value. 141 * Updates an entry in a selected hash-fragment to a non-default nodeId.
142 * 142 *
143 * @param value 143 * @param value
144 * @param selectedHashFragment 144 * @param selectedHashFragment
145 * @return 145 * @return
@@ -153,7 +153,7 @@ public class MutableNode<K, V> extends Node<K, V> {
153 } 153 }
154 154
155 /** 155 /**
156 * 156 *
157 * @param selectedHashFragment 157 * @param selectedHashFragment
158 * @param newNode 158 * @param newNode
159 * @return 159 * @return
@@ -400,7 +400,7 @@ public class MutableNode<K, V> extends Node<K, V> {
400 V value = (V) this.content[2 * i + 1]; 400 V value = (V) this.content[2 * i + 1];
401 401
402 if (value == defaultValue) { 402 if (value == defaultValue) {
403 throw new IllegalStateException("Node contains default value!"); 403 throw new IllegalStateException("Node contains default nodeId!");
404 } 404 }
405 int hashCode = hashProvider.getHash(key, hashDepth(depth)); 405 int hashCode = hashProvider.getHash(key, hashDepth(depth));
406 int shiftDepth = shiftDepth(depth); 406 int shiftDepth = shiftDepth(depth);
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java
index 234a4ff3..2260cd5b 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java
@@ -10,7 +10,7 @@ public abstract class Node<K,V>{
10 protected static final int NUMBER_OF_FACTORS = Integer.SIZE / BRANCHING_FACTOR_BITS; 10 protected static final int NUMBER_OF_FACTORS = Integer.SIZE / BRANCHING_FACTOR_BITS;
11 protected static final int FACTOR_MASK = FACTOR-1; 11 protected static final int FACTOR_MASK = FACTOR-1;
12 public static final int EFFECTIVE_BITS = BRANCHING_FACTOR_BITS * NUMBER_OF_FACTORS; 12 public static final int EFFECTIVE_BITS = BRANCHING_FACTOR_BITS * NUMBER_OF_FACTORS;
13 13
14 /** 14 /**
15 * Calculates the index for the continuous hash. 15 * Calculates the index for the continuous hash.
16 * @param depth The depth of the node in the tree. 16 * @param depth The depth of the node in the tree.
@@ -19,10 +19,10 @@ public abstract class Node<K,V>{
19 protected static int hashDepth(int depth) { 19 protected static int hashDepth(int depth) {
20 return depth/NUMBER_OF_FACTORS; 20 return depth/NUMBER_OF_FACTORS;
21 } 21 }
22 22
23 /** 23 /**
24 * Calculates the which segment of a single hash should be used. 24 * Calculates the which segment of a single hash should be used.
25 * @param depth The depth of the node in the tree. 25 * @param depth The depth of the node in the tree.
26 * @return The segment of a hash code. 26 * @return The segment of a hash code.
27 */ 27 */
28 protected static int shiftDepth(int depth) { 28 protected static int shiftDepth(int depth) {
@@ -38,7 +38,7 @@ public abstract class Node<K,V>{
38 if(shiftDepth<0 || Node.NUMBER_OF_FACTORS<shiftDepth) throw new IllegalArgumentException("Invalid shift depth! valid intervall=[0;5], input="+shiftDepth); 38 if(shiftDepth<0 || Node.NUMBER_OF_FACTORS<shiftDepth) throw new IllegalArgumentException("Invalid shift depth! valid intervall=[0;5], input="+shiftDepth);
39 return (hash >>> shiftDepth*BRANCHING_FACTOR_BITS) & FACTOR_MASK; 39 return (hash >>> shiftDepth*BRANCHING_FACTOR_BITS) & FACTOR_MASK;
40 } 40 }
41 41
42 /** 42 /**
43 * Returns the hash code for a given depth. It may calculate new hash code, or reuse a hash code calculated for depth-1. 43 * Returns the hash code for a given depth. It may calculate new hash code, or reuse a hash code calculated for depth-1.
44 * @param key The key. 44 * @param key The key.
@@ -55,12 +55,12 @@ public abstract class Node<K,V>{
55 hashProvider.getHash(key, hashDepth) : 55 hashProvider.getHash(key, hashDepth) :
56 hash; 56 hash;
57 } 57 }
58 58
59 59
60 public abstract V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth); 60 public abstract V getValue(K key, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth);
61 public abstract Node<K,V> putValue(K key, V value, OldValueBox<V> old, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth); 61 public abstract Node<K,V> putValue(K key, V value, OldValueBox<V> old, ContinousHashProvider<? super K> hashProvider, V defaultValue, int hash, int depth);
62 public abstract long getSize(); 62 public abstract long getSize();
63 63
64 abstract MutableNode<K, V> toMutable(); 64 abstract MutableNode<K, V> toMutable();
65 public abstract ImmutableNode<K, V> toImmutable( 65 public abstract ImmutableNode<K, V> toImmutable(
66 Map<Node<K, V>,ImmutableNode<K, V>> cache); 66 Map<Node<K, V>,ImmutableNode<K, V>> cache);
@@ -68,10 +68,10 @@ public abstract class Node<K,V>{
68 /** 68 /**
69 * Moves a {@link MapCursor} to its next position. 69 * Moves a {@link MapCursor} to its next position.
70 * @param cursor the cursor 70 * @param cursor the cursor
71 * @return Whether there was a next value to move on. 71 * @return Whether there was a next nodeId to move on.
72 */ 72 */
73 abstract boolean moveToNext(MapCursor<K,V> cursor); 73 abstract boolean moveToNext(MapCursor<K,V> cursor);
74 74
75 ///////// FOR printing 75 ///////// FOR printing
76 public abstract void prettyPrint(StringBuilder builder, int depth, int code); 76 public abstract void prettyPrint(StringBuilder builder, int depth, int code);
77 @Override 77 @Override
@@ -81,5 +81,5 @@ public abstract class Node<K,V>{
81 return stringBuilder.toString(); 81 return stringBuilder.toString();
82 } 82 }
83 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {} 83 public void checkIntegrity(ContinousHashProvider<? super K> hashProvider, V defaultValue, int depth) {}
84 84
85} 85}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractCallAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractCallAtom.java
deleted file mode 100644
index fb8d7432..00000000
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/AbstractCallAtom.java
+++ /dev/null
@@ -1,30 +0,0 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.model.RelationLike;
4import tools.refinery.store.query.Variable;
5
6import java.util.List;
7import java.util.Set;
8
9public abstract class AbstractCallAtom<T extends RelationLike> extends AbstractSubstitutionAtom<T> {
10 private final CallKind kind;
11
12 protected AbstractCallAtom(CallKind kind, T target, List<Variable> substitution) {
13 super(target, substitution);
14 if (kind.isTransitive() && target.getArity() != 2) {
15 throw new IllegalArgumentException("Transitive closures can only take binary relations");
16 }
17 this.kind = kind;
18 }
19
20 public CallKind getKind() {
21 return kind;
22 }
23
24 @Override
25 public void collectAllVariables(Set<Variable> variables) {
26 if (kind.isPositive()) {
27 super.collectAllVariables(variables);
28 }
29 }
30}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/BasicCallKind.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/BasicCallKind.java
new file mode 100644
index 00000000..cf2ffc07
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/BasicCallKind.java
@@ -0,0 +1,26 @@
1package tools.refinery.store.query.atom;
2
3public enum BasicCallKind implements CallKind {
4 POSITIVE(true, false),
5 NEGATIVE(false, false),
6 TRANSITIVE(true, true);
7
8 private final boolean positive;
9
10 private final boolean transitive;
11
12 BasicCallKind(boolean positive, boolean transitive) {
13 this.positive = positive;
14 this.transitive = transitive;
15 }
16
17 @Override
18 public boolean isPositive() {
19 return positive;
20 }
21
22 @Override
23 public boolean isTransitive() {
24 return transitive;
25 }
26}
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
new file mode 100644
index 00000000..e52e33ac
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallAtom.java
@@ -0,0 +1,66 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.model.RelationLike;
4import tools.refinery.store.query.Variable;
5
6import java.util.List;
7import java.util.Objects;
8import java.util.Set;
9
10public final class CallAtom<T extends RelationLike> extends AbstractSubstitutionAtom<T> {
11 private final CallKind kind;
12
13 public CallAtom(CallKind kind, T target, List<Variable> substitution) {
14 super(target, substitution);
15 if (kind.isTransitive() && target.getArity() != 2) {
16 throw new IllegalArgumentException("Transitive closures can only take binary relations");
17 }
18 this.kind = kind;
19 }
20
21 public CallAtom(CallKind kind, T target, Variable... substitution) {
22 this(kind, target, List.of(substitution));
23 }
24
25 public CallAtom(boolean positive, T target, List<Variable> substitution) {
26 this(CallKind.fromBoolean(positive), target, substitution);
27 }
28
29 public CallAtom(boolean positive, T target, Variable... substitution) {
30 this(positive, target, List.of(substitution));
31 }
32
33 public CallAtom(T target, List<Variable> substitution) {
34 this(true, target, substitution);
35 }
36
37 public CallAtom(T target, Variable... substitution) {
38 this(target, List.of(substitution));
39 }
40
41 public CallKind getKind() {
42 return kind;
43 }
44
45 @Override
46 public void collectAllVariables(Set<Variable> variables) {
47 if (kind.isPositive()) {
48 super.collectAllVariables(variables);
49 }
50 }
51
52 @Override
53 public boolean equals(Object o) {
54 if (this == o) return true;
55 if (o == null || getClass() != o.getClass()) return false;
56 CallAtom<?> that = (CallAtom<?>) o;
57 return Objects.equals(kind, that.kind)
58 && Objects.equals(getTarget(), that.getTarget())
59 && Objects.equals(getSubstitution(), that.getSubstitution());
60 }
61
62 @Override
63 public int hashCode() {
64 return Objects.hash(kind, getTarget(), getSubstitution());
65 }
66}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallKind.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallKind.java
index c7cbc955..86066b8e 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallKind.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CallKind.java
@@ -1,24 +1,11 @@
1package tools.refinery.store.query.atom; 1package tools.refinery.store.query.atom;
2 2
3public enum CallKind { 3public sealed interface CallKind permits BasicCallKind, CountCallKind {
4 POSITIVE(true, false), 4 boolean isPositive();
5 NEGATIVE(false, false),
6 TRANSITIVE(true, true);
7 5
8 private final boolean positive; 6 boolean isTransitive();
9 7
10 private final boolean transitive; 8 static CallKind fromBoolean(boolean positive) {
11 9 return positive ? BasicCallKind.POSITIVE : BasicCallKind.NEGATIVE;
12 CallKind(boolean positive, boolean transitive) {
13 this.positive = positive;
14 this.transitive = transitive;
15 }
16
17 public boolean isPositive() {
18 return positive;
19 }
20
21 public boolean isTransitive() {
22 return transitive;
23 } 10 }
24} 11}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/ComparisonOperator.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ComparisonOperator.java
new file mode 100644
index 00000000..ca113181
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ComparisonOperator.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.query.atom;
2
3public enum ComparisonOperator {
4 EQUALS,
5 NOT_EQUALS,
6 LESS,
7 LESS_EQUALS,
8 GREATER,
9 GREATER_EQUALS;
10
11 @Override
12 public String toString() {
13 return switch (this) {
14 case EQUALS -> "==";
15 case NOT_EQUALS -> "!=";
16 case LESS -> "<";
17 case LESS_EQUALS -> "<=";
18 case GREATER -> ">";
19 case GREATER_EQUALS -> ">=";
20 };
21 }
22}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/ConstantAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ConstantAtom.java
new file mode 100644
index 00000000..13dae7d0
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ConstantAtom.java
@@ -0,0 +1,12 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.query.Variable;
4
5import java.util.Set;
6
7public record ConstantAtom(Variable variable, int nodeId) implements DNFAtom {
8 @Override
9 public void collectAllVariables(Set<Variable> variables) {
10 variables.add(variable);
11 }
12}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/CountCallKind.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CountCallKind.java
new file mode 100644
index 00000000..2c85cb4f
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CountCallKind.java
@@ -0,0 +1,13 @@
1package tools.refinery.store.query.atom;
2
3public record CountCallKind(ComparisonOperator operator, int threshold) implements CallKind {
4 @Override
5 public boolean isPositive() {
6 return false;
7 }
8
9 @Override
10 public boolean isTransitive() {
11 return false;
12 }
13}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/CountNotEqualsAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CountNotEqualsAtom.java
new file mode 100644
index 00000000..312e5fb8
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/CountNotEqualsAtom.java
@@ -0,0 +1,31 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.model.RelationLike;
4import tools.refinery.store.query.Variable;
5
6import java.util.List;
7import java.util.Set;
8
9public record CountNotEqualsAtom<T extends RelationLike>(boolean must, int threshold, T mayTarget, T mustTarget,
10 List<Variable> substitution) implements DNFAtom {
11 public CountNotEqualsAtom {
12 if (substitution.size() != mayTarget.getArity()) {
13 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(mayTarget.getName(),
14 mayTarget.getArity(), substitution.size()));
15 }
16 if (substitution.size() != mustTarget.getArity()) {
17 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(mustTarget.getName(),
18 mustTarget.getArity(), substitution.size()));
19 }
20 }
21
22 public CountNotEqualsAtom(boolean must, int threshold, T mayTarget, T mustTarget, Variable... substitution) {
23 this(must, threshold, mayTarget, mustTarget, List.of(substitution));
24 }
25
26 @Override
27 public void collectAllVariables(Set<Variable> variables) {
28 // No variables to collect, because all variables should either appear in other clauses,
29 // or are quantified by this clause.
30 }
31}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/DNFCallAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/DNFCallAtom.java
deleted file mode 100644
index 9d2efa53..00000000
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/DNFCallAtom.java
+++ /dev/null
@@ -1,40 +0,0 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.query.DNF;
4import tools.refinery.store.query.Variable;
5
6import java.util.List;
7import java.util.Objects;
8
9public final class DNFCallAtom extends AbstractCallAtom<DNF> {
10 public DNFCallAtom(CallKind kind, DNF target, List<Variable> substitution) {
11 super(kind, target, substitution);
12 }
13
14 public DNFCallAtom(CallKind kind, DNF target, Variable... substitution) {
15 super(kind, target, List.of(substitution));
16 }
17
18 public DNFCallAtom(DNF target, List<Variable> substitution) {
19 this(CallKind.POSITIVE, target, substitution);
20 }
21
22 public DNFCallAtom(DNF target, Variable... substitution) {
23 this(target, List.of(substitution));
24 }
25
26 @Override
27 public boolean equals(Object o) {
28 if (this == o) return true;
29 if (o == null || getClass() != o.getClass()) return false;
30 DNFCallAtom dnfCallAtom = (DNFCallAtom) o;
31 return Objects.equals(getKind(), dnfCallAtom.getKind())
32 && Objects.equals(getTarget(), dnfCallAtom.getTarget())
33 && Objects.equals(getSubstitution(), dnfCallAtom.getSubstitution());
34 }
35
36 @Override
37 public int hashCode() {
38 return Objects.hash(getKind(), getTarget(), getSubstitution());
39 }
40}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelation.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelation.java
new file mode 100644
index 00000000..1e4f8f55
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelation.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.model.RelationLike;
4import tools.refinery.store.model.representation.Relation;
5import tools.refinery.store.model.representation.TruthValue;
6
7public record ModalRelation(Modality modality, Relation<TruthValue> relation) implements RelationLike {
8 @Override
9 public String getName() {
10 return "%s %s".formatted(modality, relation);
11 }
12
13 @Override
14 public int getArity() {
15 return relation.getArity();
16 }
17}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelationAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelationAtom.java
deleted file mode 100644
index 2480e82e..00000000
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/ModalRelationAtom.java
+++ /dev/null
@@ -1,38 +0,0 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.model.representation.Relation;
4import tools.refinery.store.model.representation.TruthValue;
5import tools.refinery.store.query.Variable;
6
7import java.util.List;
8import java.util.Objects;
9
10public final class ModalRelationAtom extends AbstractCallAtom<Relation<TruthValue>> {
11 private final Modality modality;
12
13 public ModalRelationAtom(CallKind kind, Modality modality, Relation<TruthValue> target,
14 List<Variable> substitution) {
15 super(kind, target, substitution);
16 this.modality = modality;
17 }
18
19 public ModalRelationAtom(Modality modality, Relation<TruthValue> target, List<Variable> substitution) {
20 this(CallKind.POSITIVE, modality, target, substitution);
21 }
22
23 @Override
24 public boolean equals(Object o) {
25 if (this == o) return true;
26 if (o == null || getClass() != o.getClass()) return false;
27 ModalRelationAtom modalRelationAtom = (ModalRelationAtom) o;
28 return Objects.equals(getKind(), modalRelationAtom.getKind())
29 && modality == modalRelationAtom.modality
30 && Objects.equals(getTarget(), modalRelationAtom.getTarget())
31 && Objects.equals(getSubstitution(), modalRelationAtom.getSubstitution());
32 }
33
34 @Override
35 public int hashCode() {
36 return Objects.hash(getKind(), modality, getTarget(), getSubstitution());
37 }
38}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/Modality.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/Modality.java
index 9344a9d3..bc107b76 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/Modality.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/query/atom/Modality.java
@@ -1,7 +1,14 @@
1package tools.refinery.store.query.atom; 1package tools.refinery.store.query.atom;
2 2
3import java.util.Locale;
4
3public enum Modality { 5public enum Modality {
4 MUST, 6 MUST,
5 MAY, 7 MAY,
6 CURRENT; 8 CURRENT;
9
10 @Override
11 public String toString() {
12 return name().toLowerCase(Locale.ROOT);
13 }
7} 14}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationAtom.java b/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationAtom.java
deleted file mode 100644
index 06098139..00000000
--- a/subprojects/store/src/main/java/tools/refinery/store/query/atom/RelationAtom.java
+++ /dev/null
@@ -1,32 +0,0 @@
1package tools.refinery.store.query.atom;
2
3import tools.refinery.store.model.representation.Relation;
4import tools.refinery.store.query.Variable;
5
6import java.util.List;
7import java.util.Objects;
8
9public final class RelationAtom extends AbstractCallAtom<Relation<?>> {
10 public RelationAtom(CallKind kind, Relation<?> target, List<Variable> substitution) {
11 super(kind, target, substitution);
12 }
13
14 public RelationAtom(Relation<?> target, List<Variable> substitution) {
15 this(CallKind.POSITIVE, target, substitution);
16 }
17
18 @Override
19 public boolean equals(Object o) {
20 if (this == o) return true;
21 if (o == null || getClass() != o.getClass()) return false;
22 RelationAtom relationAtom = (RelationAtom) o;
23 return Objects.equals(getKind(), relationAtom.getKind())
24 && Objects.equals(getTarget(), relationAtom.getTarget())
25 && Objects.equals(getSubstitution(), relationAtom.getSubstitution());
26 }
27
28 @Override
29 public int hashCode() {
30 return Objects.hash(getKind(), getTarget(), getSubstitution());
31 }
32}
diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java
index 991b4f51..a4ba7441 100644
--- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java
+++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java
@@ -56,7 +56,7 @@ public class MapTestEnvironment<K, V> {
56 } 56 }
57 57
58 } 58 }
59 59
60 public static <K, V> void compareTwoMaps(String title, VersionedMapImpl<K, V> map1, 60 public static <K, V> void compareTwoMaps(String title, VersionedMapImpl<K, V> map1,
61 VersionedMapImpl<K, V> map2) { 61 VersionedMapImpl<K, V> map2) {
62 compareTwoMaps(title, map1, map2, null); 62 compareTwoMaps(title, map1, map2, null);
@@ -112,7 +112,7 @@ public class MapTestEnvironment<K, V> {
112 oldOracleValue = oracle.remove(key); 112 oldOracleValue = oracle.remove(key);
113 } 113 }
114 if(oldSutValue == sut.getDefaultValue() && oldOracleValue != null) { 114 if(oldSutValue == sut.getDefaultValue() && oldOracleValue != null) {
115 fail("After put, SUT old value was default, but oracle old walue was " + oldOracleValue); 115 fail("After put, SUT old nodeId was default, but oracle old walue was " + oldOracleValue);
116 } 116 }
117 if(oldSutValue != sut.getDefaultValue()) { 117 if(oldSutValue != sut.getDefaultValue()) {
118 assertEquals(oldOracleValue, oldSutValue); 118 assertEquals(oldOracleValue, oldSutValue);
@@ -127,8 +127,8 @@ public class MapTestEnvironment<K, V> {
127 fail(title + ": " + e.getMessage()); 127 fail(title + ": " + e.getMessage());
128 } 128 }
129 129
130 // 1. Checking: if Reference contains <key,value> pair, then SUT contains 130 // 1. Checking: if Reference contains <key,nodeId> pair, then SUT contains
131 // <key,value> pair. 131 // <key,nodeId> pair.
132 // Tests get functions 132 // Tests get functions
133 for (Entry<K, V> entry : oracle.entrySet()) { 133 for (Entry<K, V> entry : oracle.entrySet()) {
134 V sutValue = sut.get(entry.getKey()); 134 V sutValue = sut.get(entry.getKey());
@@ -140,8 +140,8 @@ public class MapTestEnvironment<K, V> {
140 } 140 }
141 } 141 }
142 142
143 // 2. Checking: if SUT contains <key,value> pair, then Reference contains 143 // 2. Checking: if SUT contains <key,nodeId> pair, then Reference contains
144 // <key,value> pair. 144 // <key,nodeId> pair.
145 // Tests iterators 145 // Tests iterators
146 int elementsInSutEntrySet = 0; 146 int elementsInSutEntrySet = 0;
147 Cursor<K, V> cursor = sut.getAll(); 147 Cursor<K, V> cursor = sut.getAll();
@@ -160,7 +160,7 @@ public class MapTestEnvironment<K, V> {
160 } 160 }
161 161
162 // 3. Checking sizes 162 // 3. Checking sizes
163 // Counting of non-default value pairs. 163 // Counting of non-default nodeId pairs.
164 int oracleSize = oracle.entrySet().size(); 164 int oracleSize = oracle.entrySet().size();
165 long sutSize = sut.getSize(); 165 long sutSize = sut.getSize();
166 if (oracleSize != sutSize || oracleSize != elementsInSutEntrySet) { 166 if (oracleSize != sutSize || oracleSize != elementsInSutEntrySet) {