aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language-semantics
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-09-29 17:54:03 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-10-03 20:06:52 +0200
commit7218308c9be553c3e0d2af0dabde853d0dab27cd (patch)
tree44ba305c993689235c6599aa0d40d4d883e6f687 /subprojects/language-semantics
parentfix: make Tuple1 cache thread safe (diff)
downloadrefinery-7218308c9be553c3e0d2af0dabde853d0dab27cd.tar.gz
refinery-7218308c9be553c3e0d2af0dabde853d0dab27cd.tar.zst
refinery-7218308c9be553c3e0d2af0dabde853d0dab27cd.zip
feat: data structure for default assertions
Diffstat (limited to 'subprojects/language-semantics')
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java26
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeNode.java18
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeValue.java8
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/IntermediateNode.java44
-rw-r--r--subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/TerminalNode.java50
-rw-r--r--subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/tests/DecisionTreeTests.java89
6 files changed, 204 insertions, 31 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java
index d743ee2b..3893f396 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTree.java
@@ -20,21 +20,41 @@ public class DecisionTree {
20 root = node; 20 root = node;
21 } 21 }
22 22
23 public DecisionTree(int levels) {
24 this(levels, null);
25 }
26
23 public TruthValue get(Tuple tuple) { 27 public TruthValue get(Tuple tuple) {
24 return root.getValue(levels - 1, tuple).getTruthValue(); 28 return root.getValue(levels - 1, tuple).getTruthValue();
25 } 29 }
26 30
27 public void mergeValue(Tuple tuple, TruthValue truthValue) { 31 public void mergeValue(Tuple tuple, TruthValue truthValue) {
28 if (truthValue == null) { 32 if (truthValue != null) {
29 return; 33 root.mergeValue(levels - 1, tuple, truthValue);
34 }
35 }
36
37 public void setIfMissing(Tuple tuple, TruthValue truthValue) {
38 if (truthValue != null) {
39 root.setIfMissing(levels - 1, tuple, truthValue);
40 }
41 }
42
43 public void setAllMissing(TruthValue truthValue) {
44 if (truthValue != null) {
45 root.setAllMissing(truthValue);
30 } 46 }
31 root.mergeValue(levels - 1, tuple, truthValue);
32 } 47 }
33 48
34 public void overwriteValues(DecisionTree values) { 49 public void overwriteValues(DecisionTree values) {
35 root.overwriteValues(values.root); 50 root.overwriteValues(values.root);
36 } 51 }
37 52
53 public TruthValue getReducedValue() {
54 var reducedValue = root.getReducedValue();
55 return reducedValue == null ? null : reducedValue.getTruthValue();
56 }
57
38 public Cursor<Tuple, TruthValue> getCursor(TruthValue defaultValue, int nodeCount) { 58 public Cursor<Tuple, TruthValue> getCursor(TruthValue defaultValue, int nodeCount) {
39 return new DecisionTreeCursor(levels, defaultValue, nodeCount, root); 59 return new DecisionTreeCursor(levels, defaultValue, nodeCount, root);
40 } 60 }
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeNode.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeNode.java
index 7f38fc79..8ca54969 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeNode.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeNode.java
@@ -33,6 +33,24 @@ abstract class DecisionTreeNode {
33 return copy; 33 return copy;
34 } 34 }
35 35
36 public void setIfMissing(int level, Tuple tuple, TruthValue value) {
37 var key = tuple.get(level);
38 if (key < 0) {
39 throw new IllegalArgumentException("Not allowed set a missing wildcard");
40 }
41 doSetIfMissing(key, level - 1, tuple, value);
42 }
43
44 protected abstract void doSetIfMissing(int key, int nextLevel, Tuple tuple, TruthValue value);
45
46 public DecisionTreeNode withValueSetIfMissing(int level, Tuple tuple, TruthValue value) {
47 var copy = deepCopy();
48 copy.setIfMissing(level, tuple, value);
49 return copy;
50 }
51
52 public abstract void setAllMissing(TruthValue value);
53
36 public abstract void overwriteValues(DecisionTreeNode values); 54 public abstract void overwriteValues(DecisionTreeNode values);
37 55
38 public DecisionTreeNode withOverwrittenValues(DecisionTreeNode values) { 56 public DecisionTreeNode withOverwrittenValues(DecisionTreeNode values) {
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeValue.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeValue.java
index 1bf3c8b8..993987f5 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeValue.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/DecisionTreeValue.java
@@ -19,14 +19,18 @@ public enum DecisionTreeValue {
19 return truthValue; 19 return truthValue;
20 } 20 }
21 21
22 public DecisionTreeValue merge(TruthValue other) { 22 public TruthValue merge(TruthValue other) {
23 return truthValue == null ? fromTruthValue(other) : fromTruthValue(truthValue.merge(other)); 23 return truthValue == null ? other : truthValue.merge(other);
24 } 24 }
25 25
26 public DecisionTreeValue overwrite(DecisionTreeValue other) { 26 public DecisionTreeValue overwrite(DecisionTreeValue other) {
27 return other == UNSET ? this : other; 27 return other == UNSET ? this : other;
28 } 28 }
29 29
30 public TruthValue getTruthValueOrElse(TruthValue other) {
31 return this == UNSET ? other : truthValue;
32 }
33
30 public static DecisionTreeValue fromTruthValue(TruthValue truthValue) { 34 public static DecisionTreeValue fromTruthValue(TruthValue truthValue) {
31 if (truthValue == null) { 35 if (truthValue == null) {
32 return DecisionTreeValue.UNSET; 36 return DecisionTreeValue.UNSET;
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/IntermediateNode.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/IntermediateNode.java
index 7165197c..a7486ecb 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/IntermediateNode.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/IntermediateNode.java
@@ -62,19 +62,43 @@ final class IntermediateNode extends DecisionTreeNode {
62 } 62 }
63 63
64 @Override 64 @Override
65 public void overwriteValues(DecisionTreeNode values) { 65 protected void doSetIfMissing(int key, int nextLevel, Tuple tuple, TruthValue value) {
66 if (values instanceof IntermediateNode intermediateValues) { 66 var child = children.get(key);
67 otherwise.overwriteValues(intermediateValues.otherwise); 67 if (child == null) {
68 for (var pair : children.keyValuesView()) { 68 var otherwiseReducedValue = getOtherwiseReducedValue();
69 pair.getTwo().overwriteValues(intermediateValues.getChild(pair.getOne())); 69 if (otherwiseReducedValue != null && otherwiseReducedValue != DecisionTreeValue.UNSET) {
70 } 70 // Value already set.
71 for (var pair : intermediateValues.children.keyValuesView()) { 71 return;
72 children.getIfAbsentPut(pair.getOne(), () -> otherwise.withOverwrittenValues(pair.getTwo()));
73 } 72 }
74 reduceChildren(); 73 var newChild = otherwise.withValueSetIfMissing(nextLevel, tuple, value);
75 } else { 74 children.put(key, newChild);
75 return;
76 }
77 child.setIfMissing(nextLevel, tuple, value);
78 }
79
80 @Override
81 public void setAllMissing(TruthValue value) {
82 otherwise.setAllMissing(value);
83 for (var child : children) {
84 child.setAllMissing(value);
85 }
86 reduceChildren();
87 }
88
89 @Override
90 public void overwriteValues(DecisionTreeNode values) {
91 if (!(values instanceof IntermediateNode intermediateValues)) {
76 throw new IllegalArgumentException("Level mismatch"); 92 throw new IllegalArgumentException("Level mismatch");
77 } 93 }
94 otherwise.overwriteValues(intermediateValues.otherwise);
95 for (var pair : children.keyValuesView()) {
96 pair.getTwo().overwriteValues(intermediateValues.getChild(pair.getOne()));
97 }
98 for (var pair : intermediateValues.children.keyValuesView()) {
99 children.getIfAbsentPut(pair.getOne(), () -> otherwise.withOverwrittenValues(pair.getTwo()));
100 }
101 reduceChildren();
78 } 102 }
79 103
80 private void reduceChildren() { 104 private void reduceChildren() {
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/TerminalNode.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/TerminalNode.java
index f3adea61..c0197e89 100644
--- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/TerminalNode.java
+++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/model/internal/TerminalNode.java
@@ -8,18 +8,18 @@ import tools.refinery.store.tuple.Tuple;
8import tools.refinery.store.model.representation.TruthValue; 8import tools.refinery.store.model.representation.TruthValue;
9 9
10class TerminalNode extends DecisionTreeNode { 10class TerminalNode extends DecisionTreeNode {
11 private MutableIntObjectMap<DecisionTreeValue> children; 11 private MutableIntObjectMap<TruthValue> children;
12 12
13 private DecisionTreeValue otherwise; 13 private DecisionTreeValue otherwise;
14 14
15 TerminalNode(MutableIntObjectMap<DecisionTreeValue> children, DecisionTreeValue otherwise) { 15 TerminalNode(MutableIntObjectMap<TruthValue> children, DecisionTreeValue otherwise) {
16 this.children = children; 16 this.children = children;
17 this.otherwise = otherwise; 17 this.otherwise = otherwise;
18 } 18 }
19 19
20 private DecisionTreeValue getChild(int index) { 20 private DecisionTreeValue getChild(int index) {
21 var child = children.get(index); 21 var child = children.get(index);
22 return child == null ? otherwise : child; 22 return child == null ? otherwise : DecisionTreeValue.fromTruthValue(child);
23 } 23 }
24 24
25 @Override 25 @Override
@@ -41,7 +41,7 @@ class TerminalNode extends DecisionTreeNode {
41 41
42 @Override 42 @Override
43 protected void mergeAllValues(int nextLevel, Tuple tuple, TruthValue value) { 43 protected void mergeAllValues(int nextLevel, Tuple tuple, TruthValue value) {
44 otherwise = otherwise.merge(value); 44 otherwise = DecisionTreeValue.fromTruthValue(otherwise.merge(value));
45 children = IntObjectMaps.mutable.from(children.keyValuesView(), IntObjectPair::getOne, 45 children = IntObjectMaps.mutable.from(children.keyValuesView(), IntObjectPair::getOne,
46 pair -> pair.getTwo().merge(value)); 46 pair -> pair.getTwo().merge(value));
47 reduceChildren(); 47 reduceChildren();
@@ -50,7 +50,7 @@ class TerminalNode extends DecisionTreeNode {
50 @Override 50 @Override
51 protected void mergeSingleValue(int key, int nextLevel, Tuple tuple, TruthValue value) { 51 protected void mergeSingleValue(int key, int nextLevel, Tuple tuple, TruthValue value) {
52 var newChild = getChild(key).merge(value); 52 var newChild = getChild(key).merge(value);
53 if (newChild == otherwise) { 53 if (otherwise.getTruthValue() == newChild) {
54 children.remove(key); 54 children.remove(key);
55 } else { 55 } else {
56 children.put(key, newChild); 56 children.put(key, newChild);
@@ -58,25 +58,45 @@ class TerminalNode extends DecisionTreeNode {
58 } 58 }
59 59
60 @Override 60 @Override
61 public void overwriteValues(DecisionTreeNode values) { 61 public void setIfMissing(int level, Tuple tuple, TruthValue value) {
62 if (values instanceof TerminalNode terminalValues) { 62 assertLevel(level);
63 otherwise = otherwise.overwrite(terminalValues.otherwise); 63 super.setIfMissing(level, tuple, value);
64 children = IntObjectMaps.mutable.from(children.keyValuesView(), IntObjectPair::getOne, 64 }
65 pair -> pair.getTwo().overwrite(terminalValues.getChild(pair.getOne()))); 65
66 for (var pair : terminalValues.children.keyValuesView()) { 66 @Override
67 children.getIfAbsentPut(pair.getOne(), otherwise.overwrite(pair.getTwo())); 67 protected void doSetIfMissing(int key, int nextLevel, Tuple tuple, TruthValue value) {
68 } 68 if (otherwise == DecisionTreeValue.UNSET) {
69 children.getIfAbsentPut(key, value);
70 }
71 }
72
73 @Override
74 public void setAllMissing(TruthValue value) {
75 if (otherwise == DecisionTreeValue.UNSET) {
76 otherwise = DecisionTreeValue.fromTruthValue(value);
69 reduceChildren(); 77 reduceChildren();
70 } else { 78 }
79 }
80
81 @Override
82 public void overwriteValues(DecisionTreeNode values) {
83 if (!(values instanceof TerminalNode terminalValues)) {
71 throw new IllegalArgumentException("Level mismatch"); 84 throw new IllegalArgumentException("Level mismatch");
72 } 85 }
86 otherwise = otherwise.overwrite(terminalValues.otherwise);
87 children = IntObjectMaps.mutable.from(children.keyValuesView(), IntObjectPair::getOne,
88 pair -> terminalValues.getChild(pair.getOne()).getTruthValueOrElse(pair.getTwo()));
89 for (var pair : terminalValues.children.keyValuesView()) {
90 children.getIfAbsentPut(pair.getOne(), pair.getTwo());
91 }
92 reduceChildren();
73 } 93 }
74 94
75 private void reduceChildren() { 95 private void reduceChildren() {
76 var iterator = children.iterator(); 96 var iterator = children.iterator();
77 while (iterator.hasNext()) { 97 while (iterator.hasNext()) {
78 var child = iterator.next(); 98 var child = iterator.next();
79 if (child == otherwise) { 99 if (otherwise.getTruthValue() == child) {
80 iterator.remove(); 100 iterator.remove();
81 } 101 }
82 } 102 }
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/tests/DecisionTreeTests.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/tests/DecisionTreeTests.java
index 5268eb8a..f171e5c7 100644
--- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/tests/DecisionTreeTests.java
+++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/model/tests/DecisionTreeTests.java
@@ -2,14 +2,15 @@ package tools.refinery.language.semantics.model.tests;
2 2
3import org.junit.jupiter.api.Test; 3import org.junit.jupiter.api.Test;
4import tools.refinery.language.semantics.model.internal.DecisionTree; 4import tools.refinery.language.semantics.model.internal.DecisionTree;
5import tools.refinery.store.tuple.Tuple;
6import tools.refinery.store.model.representation.TruthValue; 5import tools.refinery.store.model.representation.TruthValue;
6import tools.refinery.store.tuple.Tuple;
7 7
8import java.util.LinkedHashMap; 8import java.util.LinkedHashMap;
9import java.util.Map; 9import java.util.Map;
10 10
11import static org.hamcrest.MatcherAssert.assertThat; 11import static org.hamcrest.MatcherAssert.assertThat;
12import static org.hamcrest.Matchers.*; 12import static org.hamcrest.Matchers.*;
13import static org.junit.jupiter.api.Assertions.assertThrows;
13 14
14class DecisionTreeTests { 15class DecisionTreeTests {
15 @Test 16 @Test
@@ -165,6 +166,25 @@ class DecisionTreeTests {
165 } 166 }
166 167
167 @Test 168 @Test
169 void reducedValueEmptyTest() {
170 var sut = new DecisionTree(2, TruthValue.TRUE);
171 assertThat(sut.getReducedValue(), is(TruthValue.TRUE));
172 }
173
174 @Test
175 void reducedValueUnsetTest() {
176 var sut = new DecisionTree(2);
177 assertThat(sut.getReducedValue(), is(nullValue()));
178 }
179
180 @Test
181 void reducedValueNonEmptyTest() {
182 var sut = new DecisionTree(2, TruthValue.UNKNOWN);
183 sut.mergeValue(Tuple.of(1, 2), TruthValue.TRUE);
184 assertThat(sut.getReducedValue(), is(nullValue()));
185 }
186
187 @Test
168 void removeIntermediateChildTest() { 188 void removeIntermediateChildTest() {
169 var sut = new DecisionTree(3, TruthValue.TRUE); 189 var sut = new DecisionTree(3, TruthValue.TRUE);
170 var values = new DecisionTree(3, null); 190 var values = new DecisionTree(3, null);
@@ -172,6 +192,73 @@ class DecisionTreeTests {
172 sut.overwriteValues(values); 192 sut.overwriteValues(values);
173 sut.mergeValue(Tuple.of(1, 1, 1), TruthValue.TRUE); 193 sut.mergeValue(Tuple.of(1, 1, 1), TruthValue.TRUE);
174 assertThat(sut.get(Tuple.of(1, 1, 1)), is(TruthValue.TRUE)); 194 assertThat(sut.get(Tuple.of(1, 1, 1)), is(TruthValue.TRUE));
195 assertThat(sut.getReducedValue(), is(TruthValue.TRUE));
196 }
197
198 @Test
199 void setMissingValueTest() {
200 var sut = new DecisionTree(2);
201 sut.setIfMissing(Tuple.of(0, 0), TruthValue.FALSE);
202 assertThat(sut.get(Tuple.of(0, 0)), is(TruthValue.FALSE));
203 }
204
205 @Test
206 void setNotMissingValueTest() {
207 var sut = new DecisionTree(2);
208 sut.mergeValue(Tuple.of(0, 0), TruthValue.TRUE);
209 sut.setIfMissing(Tuple.of(0, 0), TruthValue.FALSE);
210 assertThat(sut.get(Tuple.of(0, 0)), is(TruthValue.TRUE));
211 }
212
213 @Test
214 void setNotMissingDefaultValueTest() {
215 var sut = new DecisionTree(2, TruthValue.TRUE);
216 sut.setIfMissing(Tuple.of(0, 0), TruthValue.FALSE);
217 assertThat(sut.get(Tuple.of(0, 0)), is(TruthValue.TRUE));
218 }
219
220 @Test
221 void setMissingValueWildcardTest() {
222 var sut = new DecisionTree(2);
223 sut.mergeValue(Tuple.of(-1, 0), TruthValue.TRUE);
224 sut.mergeValue(Tuple.of(1, -1), TruthValue.TRUE);
225 sut.setIfMissing(Tuple.of(0, 0), TruthValue.FALSE);
226 sut.setIfMissing(Tuple.of(1, 1), TruthValue.FALSE);
227 sut.setIfMissing(Tuple.of(2, 2), TruthValue.FALSE);
228 assertThat(sut.get(Tuple.of(0, 0)), is(TruthValue.TRUE));
229 assertThat(sut.get(Tuple.of(1, 1)), is(TruthValue.TRUE));
230 assertThat(sut.get(Tuple.of(2, 2)), is(TruthValue.FALSE));
231 assertThat(sut.get(Tuple.of(2, 3)), is(nullValue()));
232 }
233
234 @Test
235 void setMissingValueInvalidTupleTest() {
236 var sut = new DecisionTree(2);
237 var tuple = Tuple.of(-1, -1);
238 assertThrows(IllegalArgumentException.class, () -> sut.setIfMissing(tuple, TruthValue.TRUE));
239 }
240
241 @Test
242 void setAllMissingTest() {
243 var sut = new DecisionTree(2);
244 sut.mergeValue(Tuple.of(-1, 0), TruthValue.TRUE);
245 sut.mergeValue(Tuple.of(1, -1), TruthValue.TRUE);
246 sut.mergeValue(Tuple.of(2, 2), TruthValue.TRUE);
247 sut.setAllMissing(TruthValue.FALSE);
248 assertThat(sut.get(Tuple.of(0, 0)), is(TruthValue.TRUE));
249 assertThat(sut.get(Tuple.of(2, 0)), is(TruthValue.TRUE));
250 assertThat(sut.get(Tuple.of(1, 1)), is(TruthValue.TRUE));
251 assertThat(sut.get(Tuple.of(1, 2)), is(TruthValue.TRUE));
252 assertThat(sut.get(Tuple.of(2, 2)), is(TruthValue.TRUE));
253 assertThat(sut.get(Tuple.of(2, 3)), is(TruthValue.FALSE));
254 assertThat(sut.get(Tuple.of(3, 2)), is(TruthValue.FALSE));
255 }
256
257 @Test
258 void setAllMissingEmptyTest() {
259 var sut = new DecisionTree(2);
260 sut.setAllMissing(TruthValue.TRUE);
261 assertThat(sut.getReducedValue(), is(TruthValue.TRUE));
175 } 262 }
176 263
177 private Map<Tuple, TruthValue> iterateAll(DecisionTree sut, TruthValue defaultValue, int nodeCount) { 264 private Map<Tuple, TruthValue> iterateAll(DecisionTree sut, TruthValue defaultValue, int nodeCount) {