aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-01 02:15:50 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-01 11:10:39 +0100
commitda5d898b3bad6f55efd6619f0abee0ec8aa1de4b (patch)
treed752bb975189a52aaaa5211e64dec42ea0b152fe
parentchore(deps): bump dependencies (diff)
downloadrefinery-da5d898b3bad6f55efd6619f0abee0ec8aa1de4b.tar.gz
refinery-da5d898b3bad6f55efd6619f0abee0ec8aa1de4b.tar.zst
refinery-da5d898b3bad6f55efd6619f0abee0ec8aa1de4b.zip
refactor: more direct access to VIATRA result set
Accessing VIATRA result sets through IQueryResultProvider requires a lot of indirection, allocations, and locking. We use reflection instead to have direct access to the underlying Indexer instead. Unfortunately, projection to arbitrary tuple masks (with wildcard entries in a tuple) is completely broken in VIATRA when RETE update propagation is delayed. While a new, ad-hoc projection indexer gets added to the RETE net immediately, it is not populated with the projection results until updates are flushed in the query engine. Therefore, when encountering an ad-hoc projection for the first time, the projection results will always be empty (thus usually out of date). While declaring the desired projections ahead of time would be a possible solution, for now, we completely remove ad-hoc projection support. If projections are needed on the ModelQuery level, we should create an API for declaring projections for each registered Dnf.
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java29
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java48
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java127
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java7
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java29
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java16
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java (renamed from subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Seed.java)2
7 files changed, 151 insertions, 107 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
index e0341598..37700413 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
@@ -21,32 +21,37 @@ import java.util.Map;
21 21
22public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { 22public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
23 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery"; 23 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery";
24 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE;
24 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends"; 25 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends";
26 private static final MethodHandle GET_QUERY_BACKENDS_HANDLE;
25 27
26 private final Model model; 28 private final Model model;
27 private final ViatraModelQueryStoreAdapterImpl storeAdapter; 29 private final ViatraModelQueryStoreAdapterImpl storeAdapter;
28 private final ViatraQueryEngineImpl queryEngine; 30 private final ViatraQueryEngineImpl queryEngine;
29 private final MethodHandle setUpdatePropagationDelayedHandle; 31
30 private final MethodHandle getQueryBackendsHandle;
31 private final Map<Dnf, ResultSet> resultSets; 32 private final Map<Dnf, ResultSet> resultSets;
32 private boolean pendingChanges; 33 private boolean pendingChanges;
33 34
34 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { 35 static {
35 this.model = model;
36 this.storeAdapter = storeAdapter;
37 var scope = new RelationalScope(this);
38 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope);
39
40 try { 36 try {
41 var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup()); 37 var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup());
42 setUpdatePropagationDelayedHandle = lookup.findSetter(ViatraQueryEngineImpl.class, 38 SET_UPDATE_PROPAGATION_DELAYED_HANDLE = lookup.findSetter(ViatraQueryEngineImpl.class,
43 DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE); 39 DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE);
44 getQueryBackendsHandle = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME, 40 GET_QUERY_BACKENDS_HANDLE = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME,
45 Map.class); 41 Map.class);
46 } catch (IllegalAccessException | NoSuchFieldException e) { 42 } catch (IllegalAccessException | NoSuchFieldException e) {
47 throw new IllegalStateException("Cannot access private members of %s" 43 throw new IllegalStateException("Cannot access private members of %s"
48 .formatted(ViatraQueryEngineImpl.class.getName()), e); 44 .formatted(ViatraQueryEngineImpl.class.getName()), e);
49 } 45 }
46 }
47
48 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) {
49 this.model = model;
50 this.storeAdapter = storeAdapter;
51 var scope = new RelationalScope(this);
52 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope);
53
54
50 55
51 var querySpecifications = storeAdapter.getQuerySpecifications(); 56 var querySpecifications = storeAdapter.getQuerySpecifications();
52 GenericQueryGroup.of( 57 GenericQueryGroup.of(
@@ -67,7 +72,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
67 72
68 private void setUpdatePropagationDelayed(boolean value) { 73 private void setUpdatePropagationDelayed(boolean value) {
69 try { 74 try {
70 setUpdatePropagationDelayedHandle.invokeExact(queryEngine, value); 75 SET_UPDATE_PROPAGATION_DELAYED_HANDLE.invokeExact(queryEngine, value);
71 } catch (Error e) { 76 } catch (Error e) {
72 // Fatal JVM errors should not be wrapped. 77 // Fatal JVM errors should not be wrapped.
73 throw e; 78 throw e;
@@ -79,7 +84,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
79 private Collection<IQueryBackend> getQueryBackends() { 84 private Collection<IQueryBackend> getQueryBackends() {
80 try { 85 try {
81 @SuppressWarnings("unchecked") 86 @SuppressWarnings("unchecked")
82 var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) getQueryBackendsHandle.invokeExact(queryEngine); 87 var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) GET_QUERY_BACKENDS_HANDLE.invokeExact(queryEngine);
83 return backendMap.values(); 88 return backendMap.values();
84 } catch (Error e) { 89 } catch (Error e) {
85 // Fatal JVM errors should not be wrapped. 90 // Fatal JVM errors should not be wrapped.
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java
new file mode 100644
index 00000000..75588b81
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
4import org.eclipse.viatra.query.runtime.rete.index.Indexer;
5import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine;
6import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
7import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo;
8
9import java.lang.invoke.MethodHandle;
10import java.lang.invoke.MethodHandles;
11import java.lang.invoke.MethodType;
12
13final class IndexerUtils {
14 private static final MethodHandle GET_ENGINE_HANDLE;
15 private static final MethodHandle GET_PRODUCTION_NODE_TRACE_HANDLE;
16 private static final MethodHandle ACCESS_PROJECTION_HANDLE;
17
18 static {
19 try {
20 var lookup = MethodHandles.privateLookupIn(RetePatternMatcher.class, MethodHandles.lookup());
21 GET_ENGINE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "engine", ReteEngine.class);
22 GET_PRODUCTION_NODE_TRACE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "productionNodeTrace",
23 RecipeTraceInfo.class);
24 ACCESS_PROJECTION_HANDLE = lookup.findVirtual(ReteEngine.class, "accessProjection",
25 MethodType.methodType(Indexer.class, RecipeTraceInfo.class, TupleMask.class));
26 } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) {
27 throw new IllegalStateException("Cannot access private members of %s"
28 .formatted(RetePatternMatcher.class.getPackageName()), e);
29 }
30 }
31
32 private IndexerUtils() {
33 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
34 }
35
36 public static Indexer getIndexer(RetePatternMatcher backend, TupleMask mask) {
37 try {
38 var engine = (ReteEngine) GET_ENGINE_HANDLE.invokeExact(backend);
39 var trace = (RecipeTraceInfo) GET_PRODUCTION_NODE_TRACE_HANDLE.invokeExact(backend);
40 return (Indexer) ACCESS_PROJECTION_HANDLE.invokeExact(engine, trace, mask);
41 } catch (Error e) {
42 // Fatal JVM errors should not be wrapped.
43 throw e;
44 } catch (Throwable e) {
45 throw new IllegalStateException("Cannot access matcher for mask " + mask, e);
46 }
47 }
48}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
index e944e873..8f56586e 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
@@ -2,71 +2,92 @@ package tools.refinery.store.query.viatra.internal.pquery;
2 2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher; 3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
6import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
10import org.eclipse.viatra.query.runtime.rete.index.Indexer;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
5import tools.refinery.store.query.ResultSet; 12import tools.refinery.store.query.ResultSet;
6import tools.refinery.store.query.viatra.ViatraTupleLike; 13import tools.refinery.store.query.viatra.ViatraTupleLike;
7import tools.refinery.store.tuple.Tuple; 14import tools.refinery.store.tuple.Tuple;
8import tools.refinery.store.tuple.TupleLike; 15import tools.refinery.store.tuple.TupleLike;
9 16
10import java.util.Optional;
11import java.util.stream.Stream; 17import java.util.stream.Stream;
12 18
19/**
20 * Directly access the tuples inside a VIATRA pattern matcher.<p>
21 * This class neglects calling
22 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
23 * and
24 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
25 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
26 * implementation for these methods.
27 * Using this class with any other runtime context may lead to undefined behavior.
28 */
13public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet { 29public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet {
14 protected final Object[] empty; 30 private final Object[] empty;
31 private final TupleMask identityMask;
32 private Indexer emptyMaskIndexer;
15 33
16 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) { 34 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
17 super(specification); 35 super(specification);
18 empty = new Object[specification.getParameterNames().size()]; 36 var arity = specification.getParameterNames().size();
19 } 37 empty = new Object[arity];
38 identityMask = TupleMask.identity(arity);
39 }
20 40
21 @Override 41 @Override
22 public boolean hasResult() { 42 protected void setBackend(ViatraQueryEngine engine, IQueryResultProvider resultProvider,
23 return backend.hasMatch(empty); 43 IMatcherCapability capabilities) {
24 } 44 super.setBackend(engine, resultProvider, capabilities);
45 if (resultProvider instanceof RetePatternMatcher reteBackend) {
46 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, TupleMask.empty(identityMask.sourceWidth));
47 }
48 }
25 49
26 @Override 50 @Override
27 public boolean hasResult(Tuple parameters) { 51 public boolean hasResult(TupleLike parameters) {
28 return backend.hasMatch(toParametersArray(parameters)); 52 org.eclipse.viatra.query.runtime.matchers.tuple.Tuple tuple;
29 } 53 if (parameters instanceof ViatraTupleLike viatraTupleLike) {
54 tuple = viatraTupleLike.wrappedTuple().toImmutable();
55 } else {
56 var parametersArray = toParametersArray(parameters);
57 tuple = Tuples.flatTupleOf(parametersArray);
58 }
59 if (emptyMaskIndexer == null) {
60 return backend.hasMatch(identityMask, tuple);
61 }
62 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
63 return matches != null && matches.contains(tuple);
64 }
30 65
31 @Override 66 @Override
32 public Optional<TupleLike> oneResult() { 67 public Stream<TupleLike> allResults() {
33 return backend.getOneArbitraryMatch(empty).map(ViatraTupleLike::new); 68 if (emptyMaskIndexer == null) {
34 } 69 return backend.getAllMatches(empty).map(ViatraTupleLike::new);
70 }
71 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
72 return matches == null ? Stream.of() : matches.stream().map(ViatraTupleLike::new);
73 }
35 74
36 @Override 75 @Override
37 public Optional<TupleLike> oneResult(Tuple parameters) { 76 public int countResults() {
38 return backend.getOneArbitraryMatch(toParametersArray(parameters)).map(ViatraTupleLike::new); 77 if (emptyMaskIndexer == null) {
39 } 78 return backend.countMatches(empty);
79 }
80 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
81 return matches == null ? 0 : matches.size();
82 }
40 83
41 @Override 84 private Object[] toParametersArray(TupleLike tuple) {
42 public Stream<TupleLike> allResults() { 85 int size = tuple.getSize();
43 return backend.getAllMatches(empty).map(ViatraTupleLike::new); 86 var array = new Object[size];
44 } 87 for (int i = 0; i < size; i++) {
45 88 var value = tuple.get(i);
46 @Override 89 array[i] = Tuple.of(value);
47 public Stream<TupleLike> allResults(Tuple parameters) { 90 }
48 return backend.getAllMatches(toParametersArray(parameters)).map(ViatraTupleLike::new); 91 return array;
49 } 92 }
50
51 @Override
52 public int countResults() {
53 return backend.countMatches(empty);
54 }
55
56 @Override
57 public int countResults(Tuple parameters) {
58 return backend.countMatches(toParametersArray(parameters));
59 }
60
61 private Object[] toParametersArray(Tuple tuple) {
62 int size = tuple.getSize();
63 var array = new Object[tuple.getSize()];
64 for (int i = 0; i < size; i++) {
65 var value = tuple.get(i);
66 if (value >= 0) {
67 array[i] = Tuple.of(value);
68 }
69 }
70 return array;
71 }
72} 93}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
index 54ae70c3..3dd517c4 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
@@ -16,8 +16,7 @@ import java.util.HashSet;
16import java.util.Set; 16import java.util.Set;
17import java.util.stream.Stream; 17import java.util.stream.Stream;
18 18
19import static org.junit.jupiter.api.Assertions.assertEquals; 19import static org.junit.jupiter.api.Assertions.*;
20import static org.junit.jupiter.api.Assertions.assertThrows;
21import static tools.refinery.store.query.literal.Literals.not; 20import static tools.refinery.store.query.literal.Literals.not;
22 21
23class QueryTest { 22class QueryTest {
@@ -97,10 +96,14 @@ class QueryTest {
97 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 96 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
98 97
99 assertEquals(0, predicateResultSet.countResults()); 98 assertEquals(0, predicateResultSet.countResults());
99 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 1)));
100 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2)));
100 101
101 queryEngine.flushChanges(); 102 queryEngine.flushChanges();
102 assertEquals(3, predicateResultSet.countResults()); 103 assertEquals(3, predicateResultSet.countResults());
103 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2))); 104 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2)));
105 assertTrue(predicateResultSet.hasResult(Tuple.of(0, 1)));
106 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2)));
104 } 107 }
105 108
106 @Test 109 @Test
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java
index a01a5a2f..0c2e07d6 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java
@@ -1,49 +1,22 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.tuple.Tuple;
4import tools.refinery.store.tuple.TupleLike; 3import tools.refinery.store.tuple.TupleLike;
5 4
6import java.util.Optional;
7import java.util.stream.Stream; 5import java.util.stream.Stream;
8 6
9public class EmptyResultSet implements ResultSet { 7public class EmptyResultSet implements ResultSet {
10 @Override 8 @Override
11 public boolean hasResult() { 9 public boolean hasResult(TupleLike parameters) {
12 return false; 10 return false;
13 } 11 }
14 12
15 @Override 13 @Override
16 public boolean hasResult(Tuple parameters) {
17 return false;
18 }
19
20 @Override
21 public Optional<TupleLike> oneResult() {
22 return Optional.empty();
23 }
24
25 @Override
26 public Optional<TupleLike> oneResult(Tuple parameters) {
27 return Optional.empty();
28 }
29
30 @Override
31 public Stream<TupleLike> allResults() { 14 public Stream<TupleLike> allResults() {
32 return Stream.of(); 15 return Stream.of();
33 } 16 }
34 17
35 @Override 18 @Override
36 public Stream<TupleLike> allResults(Tuple parameters) {
37 return Stream.of();
38 }
39
40 @Override
41 public int countResults() { 19 public int countResults() {
42 return 0; 20 return 0;
43 } 21 }
44
45 @Override
46 public int countResults(Tuple parameters) {
47 return 0;
48 }
49} 22}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java
index 3542e252..407cf075 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java
@@ -1,25 +1,17 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.tuple.Tuple;
4import tools.refinery.store.tuple.TupleLike; 3import tools.refinery.store.tuple.TupleLike;
5 4
6import java.util.Optional;
7import java.util.stream.Stream; 5import java.util.stream.Stream;
8 6
9public interface ResultSet { 7public interface ResultSet {
10 boolean hasResult(); 8 default boolean hasResult() {
9 return countResults() > 0;
10 }
11 11
12 boolean hasResult(Tuple parameters); 12 boolean hasResult(TupleLike parameters);
13
14 Optional<TupleLike> oneResult();
15
16 Optional<TupleLike> oneResult(Tuple parameters);
17 13
18 Stream<TupleLike> allResults(); 14 Stream<TupleLike> allResults();
19 15
20 Stream<TupleLike> allResults(Tuple parameters);
21
22 int countResults(); 16 int countResults();
23
24 int countResults(Tuple parameters);
25} 17}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Seed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java
index 779eadbe..042c2636 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Seed.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java
@@ -6,6 +6,8 @@ import tools.refinery.store.tuple.Tuple;
6public interface Seed<T> { 6public interface Seed<T> {
7 int arity(); 7 int arity();
8 8
9 T getReducedValue();
10
9 T get(Tuple key); 11 T get(Tuple key);
10 12
11 Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount); 13 Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount);