diff options
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 @@ | |||
1 | package tools.refinery.language.semantics.model; | 1 | package tools.refinery.language.semantics.model; |
2 | 2 | ||
3 | import tools.refinery.language.utils.CollectedSymbols; | 3 | import com.google.inject.Inject; |
4 | import org.eclipse.collections.api.factory.primitive.ObjectIntMaps; | ||
5 | import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; | ||
6 | import tools.refinery.language.model.problem.*; | ||
7 | import tools.refinery.language.semantics.model.internal.DecisionTree; | ||
8 | import tools.refinery.language.utils.ProblemDesugarer; | ||
9 | import tools.refinery.language.utils.RelationInfo; | ||
10 | import tools.refinery.store.model.representation.Relation; | ||
11 | import tools.refinery.store.model.representation.TruthValue; | ||
12 | import tools.refinery.store.tuple.Tuple; | ||
13 | |||
14 | import java.util.HashMap; | ||
15 | import java.util.Map; | ||
4 | 16 | ||
5 | public class ModelInitializer { | 17 | public 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 | ||
74 | enum ComparisonOp: | 74 | enum ComparisonOp: |
75 | LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="=="; | 75 | LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!="; |
76 | 76 | ||
77 | CountLiteral: | 77 | CountLiteral: |
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 @@ | |||
1 | package tools.refinery.language.utils; | 1 | package tools.refinery.language.utils; |
2 | 2 | ||
3 | import tools.refinery.language.model.problem.*; | ||
4 | |||
3 | import java.util.ArrayList; | 5 | import java.util.ArrayList; |
4 | import java.util.Collection; | 6 | import java.util.Collection; |
5 | import java.util.List; | 7 | import java.util.List; |
6 | 8 | ||
7 | import tools.refinery.language.model.problem.Assertion; | 9 | public record RelationInfo(String name, ContainmentRole containmentRole, List<Parameter> parameters, |
8 | import tools.refinery.language.model.problem.Conjunction; | 10 | Multiplicity multiplicity, Relation opposite, Collection<Conjunction> bodies, |
9 | import tools.refinery.language.model.problem.Multiplicity; | 11 | Collection<Assertion> assertions, Collection<TypeScope> typeScopes) { |
10 | import tools.refinery.language.model.problem.Parameter; | 12 | public RelationInfo(String name, ContainmentRole containmentRole, List<Parameter> parameters, |
11 | import tools.refinery.language.model.problem.PredicateKind; | 13 | Multiplicity multiplicity, Relation opposite, Collection<Conjunction> bodies) { |
12 | import tools.refinery.language.model.problem.Relation; | 14 | this(name, containmentRole, parameters, multiplicity, opposite, bodies, new ArrayList<>(), new ArrayList<>()); |
13 | import tools.refinery.language.model.problem.TypeScope; | ||
14 | |||
15 | public 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; | |||
12 | import tools.refinery.language.model.problem.*; | 12 | import tools.refinery.language.model.problem.*; |
13 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | 13 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; |
14 | import tools.refinery.language.tests.ProblemInjectorProvider; | 14 | import tools.refinery.language.tests.ProblemInjectorProvider; |
15 | import tools.refinery.language.utils.ContainmentRole; | ||
15 | import tools.refinery.language.utils.ProblemDesugarer; | 16 | import tools.refinery.language.utils.ProblemDesugarer; |
16 | 17 | ||
17 | import java.util.stream.Stream; | 18 | import 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; | |||
4 | import tools.refinery.store.model.ModelDiffCursor; | 4 | import tools.refinery.store.model.ModelDiffCursor; |
5 | import tools.refinery.store.model.ModelStore; | 5 | import tools.refinery.store.model.ModelStore; |
6 | import tools.refinery.store.model.ModelStoreImpl; | 6 | import tools.refinery.store.model.ModelStoreImpl; |
7 | import tools.refinery.store.model.RelationLike; | ||
7 | import tools.refinery.store.model.representation.DataRepresentation; | 8 | import tools.refinery.store.model.representation.DataRepresentation; |
8 | import tools.refinery.store.query.*; | 9 | import tools.refinery.store.query.*; |
9 | import tools.refinery.store.query.atom.DNFAtom; | 10 | import tools.refinery.store.query.atom.*; |
10 | import tools.refinery.store.query.atom.DNFCallAtom; | ||
11 | import tools.refinery.store.query.atom.EquivalenceAtom; | ||
12 | import tools.refinery.store.query.atom.RelationViewAtom; | ||
13 | import tools.refinery.store.query.viatra.internal.RawPatternMatcher; | 11 | import tools.refinery.store.query.viatra.internal.RawPatternMatcher; |
14 | import tools.refinery.store.query.viatra.internal.ViatraQueryableModel; | 12 | import tools.refinery.store.query.viatra.internal.ViatraQueryableModel; |
15 | import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery; | 13 | import 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 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.pquery; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator; | ||
4 | import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider; | ||
5 | import tools.refinery.store.query.atom.ComparisonOperator; | ||
6 | import tools.refinery.store.query.atom.CountCallKind; | ||
7 | |||
8 | import java.util.List; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.pquery; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator; | ||
4 | import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider; | ||
5 | |||
6 | import java.util.List; | ||
7 | |||
8 | public 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 | ||
3 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | 3 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; |
4 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; | 4 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; |
5 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; | 5 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.*; |
6 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality; | ||
8 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | 6 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; |
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
10 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | 8 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; |
11 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; | 9 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; |
12 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; | 10 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; |
11 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; | ||
13 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | 12 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; |
14 | import tools.refinery.store.query.*; | 13 | import tools.refinery.store.query.*; |
15 | import tools.refinery.store.query.atom.DNFAtom; | 14 | import tools.refinery.store.query.atom.*; |
16 | import tools.refinery.store.query.atom.EquivalenceAtom; | ||
17 | import tools.refinery.store.query.atom.DNFCallAtom; | ||
18 | import tools.refinery.store.query.atom.RelationViewAtom; | ||
19 | import tools.refinery.store.query.view.RelationView; | 15 | import tools.refinery.store.query.view.RelationView; |
20 | 16 | ||
21 | import java.util.*; | 17 | import 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; | |||
4 | import tools.refinery.store.model.representation.Relation; | 4 | import tools.refinery.store.model.representation.Relation; |
5 | import tools.refinery.store.model.representation.TruthValue; | 5 | import tools.refinery.store.model.representation.TruthValue; |
6 | import tools.refinery.store.query.*; | 6 | import tools.refinery.store.query.*; |
7 | import tools.refinery.store.query.atom.CallKind; | 7 | import tools.refinery.store.query.atom.*; |
8 | import tools.refinery.store.query.atom.EquivalenceAtom; | ||
9 | import tools.refinery.store.query.atom.DNFCallAtom; | ||
10 | import tools.refinery.store.query.atom.RelationViewAtom; | ||
11 | import tools.refinery.store.query.viatra.ViatraQueryableModelStore; | 8 | import tools.refinery.store.query.viatra.ViatraQueryableModelStore; |
12 | import tools.refinery.store.query.view.FilteredRelationView; | 9 | import tools.refinery.store.query.view.FilteredRelationView; |
13 | import tools.refinery.store.query.view.KeyOnlyRelationView; | 10 | import 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 | */ |
17 | public class MapDiffCursor<K, V> implements DiffCursor<K, V>, Cursor<K, V> { | 17 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.model.RelationLike; | ||
4 | import tools.refinery.store.query.Variable; | ||
5 | |||
6 | import java.util.List; | ||
7 | import java.util.Set; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.model.RelationLike; | ||
4 | import tools.refinery.store.query.Variable; | ||
5 | |||
6 | import java.util.List; | ||
7 | import java.util.Objects; | ||
8 | import java.util.Set; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | 1 | package tools.refinery.store.query.atom; |
2 | 2 | ||
3 | public enum CallKind { | 3 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.query.Variable; | ||
4 | |||
5 | import java.util.Set; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.model.RelationLike; | ||
4 | import tools.refinery.store.query.Variable; | ||
5 | |||
6 | import java.util.List; | ||
7 | import java.util.Set; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.query.DNF; | ||
4 | import tools.refinery.store.query.Variable; | ||
5 | |||
6 | import java.util.List; | ||
7 | import java.util.Objects; | ||
8 | |||
9 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.model.RelationLike; | ||
4 | import tools.refinery.store.model.representation.Relation; | ||
5 | import tools.refinery.store.model.representation.TruthValue; | ||
6 | |||
7 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.model.representation.Relation; | ||
4 | import tools.refinery.store.model.representation.TruthValue; | ||
5 | import tools.refinery.store.query.Variable; | ||
6 | |||
7 | import java.util.List; | ||
8 | import java.util.Objects; | ||
9 | |||
10 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | 1 | package tools.refinery.store.query.atom; |
2 | 2 | ||
3 | import java.util.Locale; | ||
4 | |||
3 | public enum Modality { | 5 | public 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 @@ | |||
1 | package tools.refinery.store.query.atom; | ||
2 | |||
3 | import tools.refinery.store.model.representation.Relation; | ||
4 | import tools.refinery.store.query.Variable; | ||
5 | |||
6 | import java.util.List; | ||
7 | import java.util.Objects; | ||
8 | |||
9 | public 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) { |