aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store/src/main/java/tools/refinery/store
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store/src/main/java/tools/refinery/store')
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java64
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java44
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/Model.java4
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java11
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java102
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java48
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java16
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java34
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java15
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java12
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java27
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java48
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java39
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java5
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java17
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java29
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java68
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java6
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java25
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java31
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java19
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java3
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java9
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java42
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java22
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java27
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java33
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java7
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java82
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java57
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java6
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java4
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java53
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java5
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java8
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java9
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java10
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java11
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java10
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java13
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java4
53 files changed, 924 insertions, 199 deletions
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java b/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java
index 0a94d449..5e69e7af 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java
@@ -5,6 +5,9 @@
5 */ 5 */
6package tools.refinery.store.map; 6package tools.refinery.store.map;
7 7
8import java.util.Iterator;
9import java.util.Map;
10
8public final class Cursors { 11public final class Cursors {
9 private Cursors() { 12 private Cursors() {
10 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 13 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
@@ -14,6 +17,18 @@ public final class Cursors {
14 return new Empty<>(); 17 return new Empty<>();
15 } 18 }
16 19
20 public static <K, V> Cursor<K, V> singleton(K key, V value) {
21 return new Singleton<>(key, value);
22 }
23
24 public static <K, V> Cursor<K, V> of(Iterator<Map.Entry<K, V>> iterator) {
25 return new IteratorBasedCursor<>(iterator);
26 }
27
28 public static <K, V> Cursor<K, V> of(Map<K, V> map) {
29 return of(map.entrySet().iterator());
30 }
31
17 private static class Empty<K, V> implements Cursor<K, V> { 32 private static class Empty<K, V> implements Cursor<K, V> {
18 private boolean terminated = false; 33 private boolean terminated = false;
19 34
@@ -38,4 +53,53 @@ public final class Cursors {
38 return false; 53 return false;
39 } 54 }
40 } 55 }
56
57 private static class Singleton<K, V> implements Cursor<K, V> {
58 private State state = State.INITIAL;
59 private final K key;
60 private final V value;
61
62 public Singleton(K key, V value) {
63 this.key = key;
64 this.value = value;
65 }
66
67 @Override
68 public K getKey() {
69 if (state == State.STARTED) {
70 return key;
71 }
72 return null;
73 }
74
75 @Override
76 public V getValue() {
77 if (state == State.STARTED) {
78 return value;
79 }
80 return null;
81 }
82
83 @Override
84 public boolean isTerminated() {
85 return state == State.TERMINATED;
86 }
87
88 @Override
89 public boolean move() {
90 if (state == State.INITIAL) {
91 state = State.STARTED;
92 return true;
93 }
94 state = State.TERMINATED;
95 return false;
96 }
97
98
99 private enum State {
100 INITIAL,
101 STARTED,
102 TERMINATED
103 }
104 }
41} 105}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java
new file mode 100644
index 00000000..0ed9b730
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/IteratorBasedCursor.java
@@ -0,0 +1,44 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.map;
7
8import java.util.Iterator;
9import java.util.Map;
10
11public class IteratorBasedCursor<K, V> implements Cursor<K, V> {
12 private final Iterator<Map.Entry<K, V>> iterator;
13 private Map.Entry<K, V> entry;
14 private boolean terminated;
15
16 public IteratorBasedCursor(Iterator<Map.Entry<K, V>> iterator) {
17 this.iterator = iterator;
18 }
19
20 @Override
21 public K getKey() {
22 return entry.getKey();
23 }
24
25 @Override
26 public V getValue() {
27 return entry.getValue();
28 }
29
30 @Override
31 public boolean isTerminated() {
32 return terminated;
33 }
34
35 @Override
36 public boolean move() {
37 if (!terminated && iterator.hasNext()) {
38 entry = iterator.next();
39 return true;
40 }
41 terminated = true;
42 return false;
43 }
44}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java
index c19cc817..8be42712 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java
@@ -5,10 +5,9 @@
5 */ 5 */
6package tools.refinery.store.map.internal.delta; 6package tools.refinery.store.map.internal.delta;
7 7
8import java.util.*;
9
10import tools.refinery.store.map.*; 8import tools.refinery.store.map.*;
11import tools.refinery.store.map.IteratorAsCursor; 9
10import java.util.*;
12 11
13public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> { 12public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> {
14 protected final VersionedMapStoreDeltaImpl<K, V> store; 13 protected final VersionedMapStoreDeltaImpl<K, V> store;
@@ -24,7 +23,7 @@ public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> {
24 this.store = store; 23 this.store = store;
25 this.defaultValue = defaultValue; 24 this.defaultValue = defaultValue;
26 25
27 current = new HashMap<>(); 26 current = new LinkedHashMap<>();
28 if (summarizeChanges) { 27 if (summarizeChanges) {
29 this.uncommittedStore = new UncommittedDeltaMapStore<>(this); 28 this.uncommittedStore = new UncommittedDeltaMapStore<>(this);
30 } else { 29 } else {
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java
index f906b48a..d650bd06 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java
@@ -13,4 +13,6 @@ public sealed interface AnyInterpretation permits Interpretation {
13 AnySymbol getSymbol(); 13 AnySymbol getSymbol();
14 14
15 long getSize(); 15 long getSize();
16
17 int getAdjacentSize(int slot, int node);
16} 18}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java
index 72f188d3..1b15e4cf 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java
@@ -19,6 +19,8 @@ public non-sealed interface Interpretation<T> extends AnyInterpretation {
19 19
20 Cursor<Tuple, T> getAll(); 20 Cursor<Tuple, T> getAll();
21 21
22 Cursor<Tuple, T> getAdjacent(int slot, int node);
23
22 T put(Tuple key, T value); 24 T put(Tuple key, T value);
23 25
24 void putAll(Cursor<Tuple, T> cursor); 26 void putAll(Cursor<Tuple, T> cursor);
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
index e2ab72e7..c4ce5207 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java
@@ -8,11 +8,9 @@ package tools.refinery.store.model;
8import tools.refinery.store.adapter.ModelAdapter; 8import tools.refinery.store.adapter.ModelAdapter;
9import tools.refinery.store.map.Version; 9import tools.refinery.store.map.Version;
10import tools.refinery.store.map.Versioned; 10import tools.refinery.store.map.Versioned;
11import tools.refinery.store.model.internal.VersionedInterpretation;
12import tools.refinery.store.representation.AnySymbol; 11import tools.refinery.store.representation.AnySymbol;
13import tools.refinery.store.representation.Symbol; 12import tools.refinery.store.representation.Symbol;
14 13
15import java.util.Map;
16import java.util.Optional; 14import java.util.Optional;
17 15
18public interface Model extends Versioned { 16public interface Model extends Versioned {
@@ -38,4 +36,6 @@ public interface Model extends Versioned {
38 void addListener(ModelListener listener); 36 void addListener(ModelListener listener);
39 37
40 void removeListener(ModelListener listener); 38 void removeListener(ModelListener listener);
39
40 void checkCancelled();
41} 41}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java
index 89382b3a..61abf126 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java
@@ -26,6 +26,8 @@ public interface ModelStore {
26 26
27 <T extends ModelStoreAdapter> T getAdapter(Class<T> adapterType); 27 <T extends ModelStoreAdapter> T getAdapter(Class<T> adapterType);
28 28
29 void checkCancelled();
30
29 static ModelStoreBuilder builder() { 31 static ModelStoreBuilder builder() {
30 return new ModelStoreBuilderImpl(); 32 return new ModelStoreBuilderImpl();
31 } 33 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java
index 3a4024b5..9b2b38c3 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreBuilder.java
@@ -8,12 +8,15 @@ package tools.refinery.store.model;
8import tools.refinery.store.adapter.ModelAdapterBuilder; 8import tools.refinery.store.adapter.ModelAdapterBuilder;
9import tools.refinery.store.representation.AnySymbol; 9import tools.refinery.store.representation.AnySymbol;
10import tools.refinery.store.representation.Symbol; 10import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.util.CancellationToken;
11 12
12import java.util.Collection; 13import java.util.Collection;
13import java.util.List; 14import java.util.List;
14import java.util.Optional; 15import java.util.Optional;
15 16
16public interface ModelStoreBuilder { 17public interface ModelStoreBuilder {
18 ModelStoreBuilder cancellationToken(CancellationToken cancellationToken);
19
17 default ModelStoreBuilder symbols(AnySymbol... symbols) { 20 default ModelStoreBuilder symbols(AnySymbol... symbols) {
18 return symbols(List.of(symbols)); 21 return symbols(List.of(symbols));
19 } 22 }
@@ -29,7 +32,9 @@ public interface ModelStoreBuilder {
29 32
30 <T> ModelStoreBuilder symbol(Symbol<T> symbol); 33 <T> ModelStoreBuilder symbol(Symbol<T> symbol);
31 34
32 <T extends ModelAdapterBuilder> ModelStoreBuilder with(T adapterBuilder); 35 ModelStoreBuilder with(ModelAdapterBuilder adapterBuilder);
36
37 ModelStoreBuilder with(ModelStoreConfiguration configuration);
33 38
34 <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType); 39 <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType);
35 40
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java
new file mode 100644
index 00000000..e94af5f8
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStoreConfiguration.java
@@ -0,0 +1,11 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model;
7
8@FunctionalInterface
9public interface ModelStoreConfiguration {
10 void apply(ModelStoreBuilder storeBuilder);
11}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java
new file mode 100644
index 00000000..3d7f59d7
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java
@@ -0,0 +1,102 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import org.eclipse.collections.api.factory.Maps;
9import org.eclipse.collections.api.factory.primitive.IntObjectMaps;
10import org.eclipse.collections.api.map.MutableMap;
11import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
12import tools.refinery.store.map.*;
13import tools.refinery.store.tuple.Tuple;
14
15import java.util.Set;
16
17class BaseIndexer<T> {
18 private final MutableIntObjectMap<MutableMap<Tuple, T>>[] maps;
19 private final VersionedMap<Tuple, T> versionedMap;
20
21 public BaseIndexer(int arity, VersionedMap<Tuple, T> map) {
22 if (arity < 2) {
23 throw new IllegalArgumentException("Only arity >= 2 symbols need to be indexed");
24 }
25 // There is no way in Java to create a generic array in a checked way.
26 @SuppressWarnings({"unchecked", "squid:S1905"})
27 var uncheckedMaps = (MutableIntObjectMap<MutableMap<Tuple, T>>[]) new MutableIntObjectMap[arity];
28 maps = uncheckedMaps;
29 for (int i = 0; i < arity; i++) {
30 maps[i] = IntObjectMaps.mutable.empty();
31 }
32 this.versionedMap = map;
33 if (map != null) {
34 var cursor = map.getAll();
35 while (cursor.move()) {
36 put(cursor.getKey(), cursor.getValue());
37 }
38 }
39 }
40
41 public void put(Tuple key, T value) {
42 for (int i = 0; i < maps.length; i++) {
43 var map = maps[i];
44 int element = key.get(i);
45 var adjacentTuples = map.getIfAbsentPut(element, Maps.mutable::empty);
46 adjacentTuples.put(key, value);
47 }
48 }
49
50 public void remove(Tuple key) {
51 for (int i = 0; i < maps.length; i++) {
52 var map = maps[i];
53 int element = key.get(i);
54 var adjacentTuples = map.get(element);
55 if (adjacentTuples == null) {
56 continue;
57 }
58 adjacentTuples.remove(key);
59 if (adjacentTuples.isEmpty()) {
60 map.remove(element);
61 }
62 }
63 }
64
65 private MutableMap<Tuple, T> getAdjacentMap(int slot, int node) {
66 if (slot < 0 || slot >= maps.length) {
67 throw new IllegalArgumentException("Invalid index: " + slot);
68 }
69 var map = maps[slot];
70 return map.get(node);
71 }
72
73 public int getAdjacentSize(int slot, int node) {
74 var adjacentTuples = getAdjacentMap(slot, node);
75 if (adjacentTuples == null) {
76 return 0;
77 }
78 return adjacentTuples.size();
79 }
80
81 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
82 var adjacentTuples = getAdjacentMap(slot, node);
83 if (adjacentTuples == null) {
84 return Cursors.empty();
85 }
86 return new IndexCursor<>(adjacentTuples, versionedMap);
87 }
88
89 private static class IndexCursor<T> extends IteratorBasedCursor<Tuple, T> {
90 private final Set<AnyVersionedMap> dependingMaps;
91
92 public IndexCursor(MutableMap<Tuple, T> map, VersionedMap<Tuple, T> versionedMap) {
93 super(map.entrySet().iterator());
94 dependingMaps = versionedMap == null ? Set.of() : Set.of(versionedMap);
95 }
96
97 @Override
98 public Set<AnyVersionedMap> getDependingMaps() {
99 return dependingMaps;
100 }
101 }
102}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java
new file mode 100644
index 00000000..0be16f77
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java
@@ -0,0 +1,48 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.VersionedMap;
10import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.tuple.Tuple;
12
13import java.util.Objects;
14
15class IndexedVersionedInterpretation<T> extends VersionedInterpretation<T> {
16 private final BaseIndexer<T> indexer;
17
18 public IndexedVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) {
19 super(model, symbol, map);
20 indexer = new BaseIndexer<>(symbol.arity(), map);
21 }
22
23 @Override
24 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
25 return indexer.getAdjacent(slot, node);
26 }
27
28 @Override
29 public int getAdjacentSize(int slot, int node) {
30 return indexer.getAdjacentSize(slot, node);
31 }
32
33 @Override
34 protected boolean shouldNotifyRestoreListeners() {
35 // Always call the {@code valueChanged} method to update the index.
36 return true;
37 }
38
39 @Override
40 protected void valueChanged(Tuple key, T fromValue, T toValue, boolean restoring) {
41 if (Objects.equals(toValue, getSymbol().defaultValue())) {
42 indexer.remove(key);
43 } else {
44 indexer.put(key, toValue);
45 }
46 super.valueChanged(key, fromValue, toValue, restoring);
47 }
48}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
index 2b12d5a6..d11e431b 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java
@@ -13,23 +13,26 @@ import tools.refinery.store.model.*;
13import tools.refinery.store.representation.AnySymbol; 13import tools.refinery.store.representation.AnySymbol;
14import tools.refinery.store.representation.Symbol; 14import tools.refinery.store.representation.Symbol;
15import tools.refinery.store.tuple.Tuple; 15import tools.refinery.store.tuple.Tuple;
16import tools.refinery.store.util.CancellationToken;
16 17
17import java.util.*; 18import java.util.*;
18 19
19public class ModelImpl implements Model { 20public class ModelImpl implements Model {
20 private final ModelStore store; 21 private final ModelStoreImpl store;
21 private Version state; 22 private Version state;
22 private LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations; 23 private LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations;
23 private final List<ModelAdapter> adapters; 24 private final List<ModelAdapter> adapters;
24 private final List<ModelListener> listeners = new ArrayList<>(); 25 private final List<ModelListener> listeners = new ArrayList<>();
26 private final CancellationToken cancellationToken;
25 private boolean uncommittedChanges; 27 private boolean uncommittedChanges;
26 private ModelAction pendingAction = ModelAction.NONE; 28 private ModelAction pendingAction = ModelAction.NONE;
27 private Version restoringToState = null; 29 private Version restoringToState = null;
28 30
29 ModelImpl(ModelStore store, Version state, int adapterCount) { 31 ModelImpl(ModelStoreImpl store, Version state, int adapterCount) {
30 this.store = store; 32 this.store = store;
31 this.state = state; 33 this.state = state;
32 adapters = new ArrayList<>(adapterCount); 34 adapters = new ArrayList<>(adapterCount);
35 cancellationToken = store.getCancellationToken();
33 } 36 }
34 37
35 void setInterpretations(LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations) { 38 void setInterpretations(LinkedHashMap<? extends AnySymbol, ? extends VersionedInterpretation<?>> interpretations) {
@@ -88,6 +91,7 @@ public class ModelImpl implements Model {
88 91
89 @Override 92 @Override
90 public Version commit() { 93 public Version commit() {
94 checkCancelled();
91 if (hasPendingAction()) { 95 if (hasPendingAction()) {
92 throw pendingActionError("commit"); 96 throw pendingActionError("commit");
93 } 97 }
@@ -106,6 +110,7 @@ public class ModelImpl implements Model {
106 Version[] interpretationVersions = new Version[interpretations.size()]; 110 Version[] interpretationVersions = new Version[interpretations.size()];
107 int j = 0; 111 int j = 0;
108 for (var interpretationEntry : interpretations.entrySet()) { 112 for (var interpretationEntry : interpretations.entrySet()) {
113 checkCancelled();
109 interpretationVersions[j++] = interpretationEntry.getValue().commit(); 114 interpretationVersions[j++] = interpretationEntry.getValue().commit();
110 } 115 }
111 ModelVersion modelVersion = new ModelVersion(interpretationVersions); 116 ModelVersion modelVersion = new ModelVersion(interpretationVersions);
@@ -125,6 +130,7 @@ public class ModelImpl implements Model {
125 130
126 @Override 131 @Override
127 public void restore(Version version) { 132 public void restore(Version version) {
133 checkCancelled();
128 if (hasPendingAction()) { 134 if (hasPendingAction()) {
129 throw pendingActionError("restore to %s".formatted(version)); 135 throw pendingActionError("restore to %s".formatted(version));
130 } 136 }
@@ -140,6 +146,7 @@ public class ModelImpl implements Model {
140 } 146 }
141 int j = 0; 147 int j = 0;
142 for (var interpretation : interpretations.values()) { 148 for (var interpretation : interpretations.values()) {
149 checkCancelled();
143 interpretation.restore(ModelVersion.getInternalVersion(version, j++)); 150 interpretation.restore(ModelVersion.getInternalVersion(version, j++));
144 } 151 }
145 152
@@ -187,4 +194,9 @@ public class ModelImpl implements Model {
187 public void removeListener(ModelListener listener) { 194 public void removeListener(ModelListener listener) {
188 listeners.remove(listener); 195 listeners.remove(listener);
189 } 196 }
197
198 @Override
199 public void checkCancelled() {
200 cancellationToken.checkCancelled();
201 }
190} 202}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java
index 2bd187a8..53ecde5e 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java
@@ -12,21 +12,37 @@ import tools.refinery.store.map.VersionedMapStoreFactory;
12import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; 12import tools.refinery.store.map.VersionedMapStoreFactoryBuilder;
13import tools.refinery.store.model.ModelStore; 13import tools.refinery.store.model.ModelStore;
14import tools.refinery.store.model.ModelStoreBuilder; 14import tools.refinery.store.model.ModelStoreBuilder;
15import tools.refinery.store.model.ModelStoreConfiguration;
15import tools.refinery.store.representation.AnySymbol; 16import tools.refinery.store.representation.AnySymbol;
16import tools.refinery.store.representation.Symbol; 17import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple; 18import tools.refinery.store.tuple.Tuple;
19import tools.refinery.store.util.CancellationToken;
18 20
19import java.util.*; 21import java.util.*;
20 22
21public class ModelStoreBuilderImpl implements ModelStoreBuilder { 23public class ModelStoreBuilderImpl implements ModelStoreBuilder {
24 private CancellationToken cancellationToken;
22 private final LinkedHashSet<AnySymbol> allSymbols = new LinkedHashSet<>(); 25 private final LinkedHashSet<AnySymbol> allSymbols = new LinkedHashSet<>();
23 private final LinkedHashMap<SymbolEquivalenceClass<?>, List<AnySymbol>> equivalenceClasses = new LinkedHashMap<>(); 26 private final LinkedHashMap<SymbolEquivalenceClass<?>, List<AnySymbol>> equivalenceClasses = new LinkedHashMap<>();
24 private final List<ModelAdapterBuilder> adapters = new ArrayList<>(); 27 private final List<ModelAdapterBuilder> adapters = new ArrayList<>();
25 28
26 @Override 29 @Override
30 public ModelStoreBuilder cancellationToken(CancellationToken cancellationToken) {
31 if (this.cancellationToken != null) {
32 throw new IllegalStateException("Cancellation token was already set");
33 }
34 if (cancellationToken == null) {
35 throw new IllegalStateException("Cancellation token must not be null");
36 }
37 this.cancellationToken = cancellationToken;
38 return this;
39 }
40
41 @Override
27 public <T> ModelStoreBuilder symbol(Symbol<T> symbol) { 42 public <T> ModelStoreBuilder symbol(Symbol<T> symbol) {
28 if (!allSymbols.add(symbol)) { 43 if (!allSymbols.add(symbol)) {
29 throw new IllegalArgumentException("Symbol %s already added".formatted(symbol)); 44 // No need to add symbol twice.
45 return this;
30 } 46 }
31 var equivalenceClass = new SymbolEquivalenceClass<>(symbol); 47 var equivalenceClass = new SymbolEquivalenceClass<>(symbol);
32 var symbolsInEquivalenceClass = equivalenceClasses.computeIfAbsent(equivalenceClass, 48 var symbolsInEquivalenceClass = equivalenceClasses.computeIfAbsent(equivalenceClass,
@@ -36,7 +52,7 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder {
36 } 52 }
37 53
38 @Override 54 @Override
39 public <T extends ModelAdapterBuilder> ModelStoreBuilder with(T adapterBuilder) { 55 public ModelStoreBuilder with(ModelAdapterBuilder adapterBuilder) {
40 for (var existingAdapter : adapters) { 56 for (var existingAdapter : adapters) {
41 if (existingAdapter.getClass().equals(adapterBuilder.getClass())) { 57 if (existingAdapter.getClass().equals(adapterBuilder.getClass())) {
42 throw new IllegalArgumentException("%s adapter was already configured for store builder" 58 throw new IllegalArgumentException("%s adapter was already configured for store builder"
@@ -48,6 +64,12 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder {
48 } 64 }
49 65
50 @Override 66 @Override
67 public ModelStoreBuilder with(ModelStoreConfiguration configuration) {
68 configuration.apply(this);
69 return this;
70 }
71
72 @Override
51 public <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType) { 73 public <T extends ModelAdapterBuilder> Optional<T> tryGetAdapter(Class<? extends T> adapterType) {
52 return AdapterUtils.tryGetAdapter(adapters, adapterType); 74 return AdapterUtils.tryGetAdapter(adapters, adapterType);
53 } 75 }
@@ -59,6 +81,7 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder {
59 81
60 @Override 82 @Override
61 public ModelStore build() { 83 public ModelStore build() {
84 // First configure adapters and let them register any symbols we don't know about yet.
62 for (int i = adapters.size() - 1; i >= 0; i--) { 85 for (int i = adapters.size() - 1; i >= 0; i--) {
63 adapters.get(i).configure(this); 86 adapters.get(i).configure(this);
64 } 87 }
@@ -66,7 +89,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder {
66 for (var entry : equivalenceClasses.entrySet()) { 89 for (var entry : equivalenceClasses.entrySet()) {
67 createStores(stores, entry.getKey(), entry.getValue()); 90 createStores(stores, entry.getKey(), entry.getValue());
68 } 91 }
69 var modelStore = new ModelStoreImpl(stores, adapters.size()); 92 var modelStore = new ModelStoreImpl(stores, adapters.size(), cancellationToken == null ?
93 CancellationToken.NONE : cancellationToken);
70 for (var adapterBuilder : adapters) { 94 for (var adapterBuilder : adapters) {
71 var storeAdapter = adapterBuilder.build(modelStore); 95 var storeAdapter = adapterBuilder.build(modelStore);
72 modelStore.addAdapter(storeAdapter); 96 modelStore.addAdapter(storeAdapter);
@@ -77,8 +101,8 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder {
77 private <T> void createStores(Map<AnySymbol, VersionedMapStore<Tuple, ?>> stores, 101 private <T> void createStores(Map<AnySymbol, VersionedMapStore<Tuple, ?>> stores,
78 SymbolEquivalenceClass<T> equivalenceClass, List<AnySymbol> symbols) { 102 SymbolEquivalenceClass<T> equivalenceClass, List<AnySymbol> symbols) {
79 int size = symbols.size(); 103 int size = symbols.size();
80 VersionedMapStoreFactory<Tuple,T> mapFactory = VersionedMapStore 104 VersionedMapStoreFactory<Tuple, T> mapFactory = VersionedMapStore
81 .<Tuple,T>builder() 105 .<Tuple, T>builder()
82 .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA) 106 .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA)
83 .defaultValue(equivalenceClass.defaultValue()) 107 .defaultValue(equivalenceClass.defaultValue())
84 .build(); 108 .build();
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java
index a320a618..fd4cc160 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java
@@ -14,16 +14,20 @@ import tools.refinery.store.model.ModelDiffCursor;
14import tools.refinery.store.model.ModelStore; 14import tools.refinery.store.model.ModelStore;
15import tools.refinery.store.representation.AnySymbol; 15import tools.refinery.store.representation.AnySymbol;
16import tools.refinery.store.tuple.Tuple; 16import tools.refinery.store.tuple.Tuple;
17import tools.refinery.store.util.CancellationToken;
17 18
18import java.util.*; 19import java.util.*;
19 20
20public class ModelStoreImpl implements ModelStore { 21public class ModelStoreImpl implements ModelStore {
21 private final LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores; 22 private final LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores;
22 private final List<ModelStoreAdapter> adapters; 23 private final List<ModelStoreAdapter> adapters;
24 private final CancellationToken cancellationToken;
23 25
24 ModelStoreImpl(LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores, int adapterCount) { 26 ModelStoreImpl(LinkedHashMap<? extends AnySymbol, ? extends VersionedMapStore<Tuple, ?>> stores, int adapterCount,
27 CancellationToken cancellationToken) {
25 this.stores = stores; 28 this.stores = stores;
26 adapters = new ArrayList<>(adapterCount); 29 adapters = new ArrayList<>(adapterCount);
30 this.cancellationToken = cancellationToken;
27 } 31 }
28 32
29 @Override 33 @Override
@@ -100,4 +104,13 @@ public class ModelStoreImpl implements ModelStore {
100 void addAdapter(ModelStoreAdapter adapter) { 104 void addAdapter(ModelStoreAdapter adapter) {
101 adapters.add(adapter); 105 adapters.add(adapter);
102 } 106 }
107
108 @Override
109 public void checkCancelled() {
110 cancellationToken.checkCancelled();
111 }
112
113 CancellationToken getCancellationToken() {
114 return cancellationToken;
115 }
103} 116}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java
index f81386f1..c3e52084 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java
@@ -11,11 +11,9 @@ import java.util.Arrays;
11 11
12public class ModelVersion implements Version { 12public class ModelVersion implements Version {
13 final Version[] mapVersions; 13 final Version[] mapVersions;
14 final int hash;
15 14
16 public ModelVersion(Version[] mapVersions) { 15 public ModelVersion(Version[] mapVersions) {
17 this.mapVersions = mapVersions; 16 this.mapVersions = mapVersions;
18 this.hash = Arrays.hashCode(mapVersions);
19 } 17 }
20 18
21 public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { 19 public static Version getInternalVersion(Version modelVersion, int interpretationIndex) {
@@ -23,16 +21,6 @@ public class ModelVersion implements Version {
23 } 21 }
24 22
25 @Override 23 @Override
26 public int hashCode() {
27 return hash;
28 }
29
30 @Override
31 public boolean equals(Object obj) {
32 return super.equals(obj);
33 }
34
35 @Override
36 public String toString() { 24 public String toString() {
37 return "ModelVersion{" + 25 return "ModelVersion{" +
38 "mapVersions=" + Arrays.toString(mapVersions) + 26 "mapVersions=" + Arrays.toString(mapVersions) +
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java
new file mode 100644
index 00000000..4a8e6752
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java
@@ -0,0 +1,27 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.VersionedMap;
10import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.tuple.Tuple;
12
13class NullaryVersionedInterpretation<T> extends VersionedInterpretation<T> {
14 public NullaryVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) {
15 super(model, symbol, map);
16 }
17
18 @Override
19 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
20 throw new IllegalArgumentException("Invalid index: " + slot);
21 }
22
23 @Override
24 public int getAdjacentSize(int slot, int node) {
25 throw new IllegalArgumentException("Invalid index: " + slot);
26 }
27}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java
new file mode 100644
index 00000000..75946680
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java
@@ -0,0 +1,48 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.Cursors;
10import tools.refinery.store.map.VersionedMap;
11import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.tuple.Tuple;
13
14import java.util.Objects;
15
16class UnaryVersionedInterpretation<T> extends VersionedInterpretation<T> {
17 public UnaryVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) {
18 super(model, symbol, map);
19 }
20
21 private void validateSlot(int slot) {
22 if (slot != 0) {
23 throw new IllegalArgumentException("Invalid index: " + slot);
24 }
25 }
26
27 @Override
28 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
29 validateSlot(slot);
30 var key = Tuple.of(node);
31 var value = get(key);
32 if (Objects.equals(value, getSymbol().defaultValue())) {
33 return Cursors.empty();
34 }
35 return Cursors.singleton(key, value);
36 }
37
38 @Override
39 public int getAdjacentSize(int slot, int node) {
40 validateSlot(slot);
41 var key = Tuple.of(node);
42 var value = get(key);
43 if (Objects.equals(value, getSymbol().defaultValue())) {
44 return 0;
45 }
46 return 1;
47 }
48}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java
index 76e3baea..dcf0ad08 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java
@@ -16,14 +16,14 @@ import tools.refinery.store.tuple.Tuple;
16import java.util.ArrayList; 16import java.util.ArrayList;
17import java.util.List; 17import java.util.List;
18 18
19public class VersionedInterpretation<T> implements Interpretation<T> { 19public abstract class VersionedInterpretation<T> implements Interpretation<T> {
20 private final ModelImpl model; 20 private final ModelImpl model;
21 private final Symbol<T> symbol; 21 private final Symbol<T> symbol;
22 private final VersionedMap<Tuple, T> map; 22 private final VersionedMap<Tuple, T> map;
23 private final List<InterpretationListener<T>> listeners = new ArrayList<>(); 23 private final List<InterpretationListener<T>> listeners = new ArrayList<>();
24 private final List<InterpretationListener<T>> restoreListeners = new ArrayList<>(); 24 private final List<InterpretationListener<T>> restoreListeners = new ArrayList<>();
25 25
26 private VersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) { 26 protected VersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMap<Tuple, T> map) {
27 this.model = model; 27 this.model = model;
28 this.symbol = symbol; 28 this.symbol = symbol;
29 this.map = map; 29 this.map = map;
@@ -50,6 +50,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
50 .formatted(symbol, symbol.arity())); 50 .formatted(symbol, symbol.arity()));
51 } 51 }
52 } 52 }
53
53 @Override 54 @Override
54 public T get(Tuple key) { 55 public T get(Tuple key) {
55 checkKey(key); 56 checkKey(key);
@@ -61,7 +62,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
61 return map.getAll(); 62 return map.getAll();
62 } 63 }
63 64
64 private void notifyListeners(Tuple key, T fromValue, T toValue, boolean restoring) { 65 protected void valueChanged(Tuple key, T fromValue, T toValue, boolean restoring) {
65 var listenerList = restoring ? restoreListeners : listeners; 66 var listenerList = restoring ? restoreListeners : listeners;
66 int listenerCount = listenerList.size(); 67 int listenerCount = listenerList.size();
67 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead. 68 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead.
@@ -74,23 +75,21 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
74 @Override 75 @Override
75 public T put(Tuple key, T value) { 76 public T put(Tuple key, T value) {
76 checkKey(key); 77 checkKey(key);
78 model.checkCancelled();
77 model.markAsChanged(); 79 model.markAsChanged();
78 var oldValue = map.put(key, value); 80 var oldValue = map.put(key, value);
79 notifyListeners(key, oldValue, value, false); 81 valueChanged(key, oldValue, value, false);
80 return oldValue; 82 return oldValue;
81 } 83 }
82 84
83 @Override 85 @Override
84 public void putAll(Cursor<Tuple, T> cursor) { 86 public void putAll(Cursor<Tuple, T> cursor) {
85 if (listeners.isEmpty()) {
86 map.putAll(cursor);
87 return;
88 }
89 model.markAsChanged(); 87 model.markAsChanged();
90 if (cursor.getDependingMaps().contains(map)) { 88 if (cursor.getDependingMaps().contains(map)) {
91 List<Tuple> keys = new ArrayList<>(); 89 List<Tuple> keys = new ArrayList<>();
92 List<T> values = new ArrayList<>(); 90 List<T> values = new ArrayList<>();
93 while (cursor.move()) { 91 while (cursor.move()) {
92 model.checkCancelled();
94 keys.add(cursor.getKey()); 93 keys.add(cursor.getKey());
95 values.add(cursor.getValue()); 94 values.add(cursor.getValue());
96 } 95 }
@@ -114,11 +113,16 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
114 Version commit() { 113 Version commit() {
115 return map.commit(); 114 return map.commit();
116 } 115 }
117 void restore(Version state) { 116
118 if (!restoreListeners.isEmpty()) { 117 protected boolean shouldNotifyRestoreListeners() {
118 return !restoreListeners.isEmpty();
119 }
120
121 public void restore(Version state) {
122 if (shouldNotifyRestoreListeners()) {
119 var diffCursor = getDiffCursor(state); 123 var diffCursor = getDiffCursor(state);
120 while (diffCursor.move()) { 124 while (diffCursor.move()) {
121 notifyListeners(diffCursor.getKey(), diffCursor.getFromValue(), diffCursor.getToValue(), true); 125 valueChanged(diffCursor.getKey(), diffCursor.getFromValue(), diffCursor.getToValue(), true);
122 } 126 }
123 } 127 }
124 map.restore(state); 128 map.restore(state);
@@ -142,7 +146,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
142 @SuppressWarnings("unchecked") 146 @SuppressWarnings("unchecked")
143 var typedSymbol = (Symbol<T>) symbol; 147 var typedSymbol = (Symbol<T>) symbol;
144 var map = store.createMap(); 148 var map = store.createMap();
145 return new VersionedInterpretation<>(model, typedSymbol, map); 149 return of(model, typedSymbol, map);
146 } 150 }
147 151
148 static <T> VersionedInterpretation<T> of(ModelImpl model, AnySymbol symbol, VersionedMapStore<Tuple, T> store, 152 static <T> VersionedInterpretation<T> of(ModelImpl model, AnySymbol symbol, VersionedMapStore<Tuple, T> store,
@@ -150,6 +154,15 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
150 @SuppressWarnings("unchecked") 154 @SuppressWarnings("unchecked")
151 var typedSymbol = (Symbol<T>) symbol; 155 var typedSymbol = (Symbol<T>) symbol;
152 var map = store.createMap(state); 156 var map = store.createMap(state);
153 return new VersionedInterpretation<>(model, typedSymbol, map); 157 return of(model, typedSymbol, map);
158 }
159
160 private static <T> VersionedInterpretation<T> of(ModelImpl model, Symbol<T> typedSymbol,
161 VersionedMap<Tuple, T> map) {
162 return switch (typedSymbol.arity()) {
163 case 0 -> new NullaryVersionedInterpretation<>(model, typedSymbol, map);
164 case 1 -> new UnaryVersionedInterpretation<>(model, typedSymbol, map);
165 default -> new IndexedVersionedInterpretation<>(model, typedSymbol, map);
166 };
154 } 167 }
155} 168}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java b/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java
index 52c740e8..dfdb43bd 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/AbstractDomain.java
@@ -5,6 +5,7 @@
5 */ 5 */
6package tools.refinery.store.representation; 6package tools.refinery.store.representation;
7 7
8import java.util.Objects;
8import java.util.Optional; 9import java.util.Optional;
9 10
10public non-sealed interface AbstractDomain<A, C> extends AnyAbstractDomain { 11public non-sealed interface AbstractDomain<A, C> extends AnyAbstractDomain {
@@ -22,7 +23,9 @@ public non-sealed interface AbstractDomain<A, C> extends AnyAbstractDomain {
22 return toConcrete(abstractValue).isPresent(); 23 return toConcrete(abstractValue).isPresent();
23 } 24 }
24 25
25 boolean isRefinement(A originalValue, A refinedValue); 26 default boolean isRefinement(A originalValue, A refinedValue) {
27 return Objects.equals(commonRefinement(originalValue, refinedValue), refinedValue);
28 }
26 29
27 A commonRefinement(A leftValue, A rightValue); 30 A commonRefinement(A leftValue, A rightValue);
28 31
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java
index 40baf9a5..f81ee9a4 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValue.java
@@ -36,6 +36,10 @@ public enum TruthValue {
36 return this != UNKNOWN; 36 return this != UNKNOWN;
37 } 37 }
38 38
39 public boolean isConcrete() {
40 return this == TRUE || this == FALSE;
41 }
42
39 public boolean must() { 43 public boolean must() {
40 return this == TRUE || this == ERROR; 44 return this == TRUE || this == ERROR;
41 } 45 }
@@ -55,9 +59,18 @@ public enum TruthValue {
55 public TruthValue merge(TruthValue other) { 59 public TruthValue merge(TruthValue other) {
56 return switch (this) { 60 return switch (this) {
57 case TRUE -> other == UNKNOWN || other == TRUE ? TRUE : ERROR; 61 case TRUE -> other == UNKNOWN || other == TRUE ? TRUE : ERROR;
58 case FALSE -> other == TruthValue.UNKNOWN || other == TruthValue.FALSE ? FALSE : ERROR; 62 case FALSE -> other == UNKNOWN || other == FALSE ? FALSE : ERROR;
59 case UNKNOWN -> other; 63 case UNKNOWN -> other;
60 default -> ERROR; 64 case ERROR -> ERROR;
65 };
66 }
67
68 public TruthValue join(TruthValue other) {
69 return switch (this) {
70 case TRUE -> other == ERROR || other == TRUE ? TRUE : UNKNOWN;
71 case FALSE -> other == ERROR || other == FALSE ? FALSE : UNKNOWN;
72 case UNKNOWN -> UNKNOWN;
73 case ERROR -> other;
61 }; 74 };
62 } 75 }
63} 76}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java
index 89f8dd19..61696dca 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/TruthValueDomain.java
@@ -7,6 +7,8 @@ package tools.refinery.store.representation;
7 7
8import java.util.Optional; 8import java.util.Optional;
9 9
10// Singleton pattern, because there is only one domain for truth values.
11@SuppressWarnings("squid:S6548")
10public final class TruthValueDomain implements AbstractDomain<TruthValue, Boolean> { 12public final class TruthValueDomain implements AbstractDomain<TruthValue, Boolean> {
11 public static final TruthValueDomain INSTANCE = new TruthValueDomain(); 13 public static final TruthValueDomain INSTANCE = new TruthValueDomain();
12 14
@@ -15,51 +17,50 @@ public final class TruthValueDomain implements AbstractDomain<TruthValue, Boolea
15 17
16 @Override 18 @Override
17 public Class<TruthValue> abstractType() { 19 public Class<TruthValue> abstractType() {
18 return null; 20 return TruthValue.class;
19 } 21 }
20 22
21 @Override 23 @Override
22 public Class<Boolean> concreteType() { 24 public Class<Boolean> concreteType() {
23 return null; 25 return Boolean.class;
24 } 26 }
25 27
26 @Override 28 @Override
27 public TruthValue toAbstract(Boolean concreteValue) { 29 public TruthValue toAbstract(Boolean concreteValue) {
28 return null; 30 return TruthValue.toTruthValue(concreteValue);
29 } 31 }
30 32
31 @Override 33 @Override
32 public Optional<Boolean> toConcrete(TruthValue abstractValue) { 34 public Optional<Boolean> toConcrete(TruthValue abstractValue) {
33 return Optional.empty(); 35 return switch (abstractValue) {
36 case TRUE -> Optional.of(true);
37 case FALSE -> Optional.of(false);
38 default -> Optional.empty();
39 };
34 } 40 }
35 41
36 @Override 42 @Override
37 public boolean isConcrete(TruthValue abstractValue) { 43 public boolean isConcrete(TruthValue abstractValue) {
38 return AbstractDomain.super.isConcrete(abstractValue); 44 return abstractValue.isConcrete();
39 }
40
41 @Override
42 public boolean isRefinement(TruthValue originalValue, TruthValue refinedValue) {
43 return false;
44 } 45 }
45 46
46 @Override 47 @Override
47 public TruthValue commonRefinement(TruthValue leftValue, TruthValue rightValue) { 48 public TruthValue commonRefinement(TruthValue leftValue, TruthValue rightValue) {
48 return null; 49 return leftValue.merge(rightValue);
49 } 50 }
50 51
51 @Override 52 @Override
52 public TruthValue commonAncestor(TruthValue leftValue, TruthValue rightValue) { 53 public TruthValue commonAncestor(TruthValue leftValue, TruthValue rightValue) {
53 return null; 54 return leftValue.join(rightValue);
54 } 55 }
55 56
56 @Override 57 @Override
57 public TruthValue unknown() { 58 public TruthValue unknown() {
58 return null; 59 return TruthValue.UNKNOWN;
59 } 60 }
60 61
61 @Override 62 @Override
62 public boolean isError(TruthValue abstractValue) { 63 public boolean isError(TruthValue abstractValue) {
63 return false; 64 return !abstractValue.isConsistent();
64 } 65 }
65} 66}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java
new file mode 100644
index 00000000..7ae2d935
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityDomain.java
@@ -0,0 +1,68 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.representation.cardinality;
7
8import tools.refinery.store.representation.AbstractDomain;
9
10import java.util.Optional;
11
12// Singleton pattern, because there is only one domain for truth values.
13@SuppressWarnings("squid:S6548")
14public class CardinalityDomain implements AbstractDomain<CardinalityInterval, Integer> {
15 public static final CardinalityDomain INSTANCE = new CardinalityDomain();
16
17 private CardinalityDomain() {
18 }
19
20 @Override
21 public Class<CardinalityInterval> abstractType() {
22 return CardinalityInterval.class;
23 }
24
25 @Override
26 public Class<Integer> concreteType() {
27 return Integer.class;
28 }
29
30 @Override
31 public CardinalityInterval toAbstract(Integer concreteValue) {
32 return CardinalityIntervals.exactly(concreteValue);
33 }
34
35 @Override
36 public Optional<Integer> toConcrete(CardinalityInterval abstractValue) {
37 return isConcrete(abstractValue) ? Optional.of(abstractValue.lowerBound()) : Optional.empty();
38 }
39
40 @Override
41 public boolean isConcrete(CardinalityInterval abstractValue) {
42 if (!(abstractValue instanceof NonEmptyCardinalityInterval nonEmptyValue) ||
43 !((nonEmptyValue.upperBound()) instanceof FiniteUpperCardinality finiteUpperCardinality)) {
44 return false;
45 }
46 return nonEmptyValue.lowerBound() == finiteUpperCardinality.finiteUpperBound();
47 }
48
49 @Override
50 public CardinalityInterval commonRefinement(CardinalityInterval leftValue, CardinalityInterval rightValue) {
51 return leftValue.meet(rightValue);
52 }
53
54 @Override
55 public CardinalityInterval commonAncestor(CardinalityInterval leftValue, CardinalityInterval rightValue) {
56 return leftValue.join(rightValue);
57 }
58
59 @Override
60 public CardinalityInterval unknown() {
61 return CardinalityIntervals.SET;
62 }
63
64 @Override
65 public boolean isError(CardinalityInterval abstractValue) {
66 return abstractValue.isEmpty();
67 }
68}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java
index 704ca2fc..b20c685a 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityInterval.java
@@ -18,6 +18,8 @@ public sealed interface CardinalityInterval permits NonEmptyCardinalityInterval,
18 18
19 CardinalityInterval add(CardinalityInterval other); 19 CardinalityInterval add(CardinalityInterval other);
20 20
21 CardinalityInterval take(int count);
22
21 CardinalityInterval multiply(CardinalityInterval other); 23 CardinalityInterval multiply(CardinalityInterval other);
22 24
23 CardinalityInterval meet(CardinalityInterval other); 25 CardinalityInterval meet(CardinalityInterval other);
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java
index ad16a3e8..855fd248 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/CardinalityIntervals.java
@@ -30,7 +30,7 @@ public final class CardinalityIntervals {
30 } 30 }
31 31
32 public static CardinalityInterval between(int lowerBound, int upperBound) { 32 public static CardinalityInterval between(int lowerBound, int upperBound) {
33 return between(lowerBound, UpperCardinalities.valueOf(upperBound)); 33 return between(lowerBound, UpperCardinalities.atMost(upperBound));
34 } 34 }
35 35
36 public static CardinalityInterval atMost(UpperCardinality upperBound) { 36 public static CardinalityInterval atMost(UpperCardinality upperBound) {
@@ -38,7 +38,7 @@ public final class CardinalityIntervals {
38 } 38 }
39 39
40 public static CardinalityInterval atMost(int upperBound) { 40 public static CardinalityInterval atMost(int upperBound) {
41 return atMost(UpperCardinalities.valueOf(upperBound)); 41 return atMost(UpperCardinalities.atMost(upperBound));
42 } 42 }
43 43
44 public static CardinalityInterval atLeast(int lowerBound) { 44 public static CardinalityInterval atLeast(int lowerBound) {
@@ -46,6 +46,6 @@ public final class CardinalityIntervals {
46 } 46 }
47 47
48 public static CardinalityInterval exactly(int lowerBound) { 48 public static CardinalityInterval exactly(int lowerBound) {
49 return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.valueOf(lowerBound)); 49 return new NonEmptyCardinalityInterval(lowerBound, UpperCardinalities.atMost(lowerBound));
50 } 50 }
51} 51}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java
index 49911c29..9e371e21 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/EmptyCardinalityInterval.java
@@ -5,6 +5,8 @@
5 */ 5 */
6package tools.refinery.store.representation.cardinality; 6package tools.refinery.store.representation.cardinality;
7 7
8// Singleton implementation, because there is only a single empty interval.
9@SuppressWarnings("squid:S6548")
8public final class EmptyCardinalityInterval implements CardinalityInterval { 10public final class EmptyCardinalityInterval implements CardinalityInterval {
9 static final EmptyCardinalityInterval INSTANCE = new EmptyCardinalityInterval(); 11 static final EmptyCardinalityInterval INSTANCE = new EmptyCardinalityInterval();
10 12
@@ -43,6 +45,11 @@ public final class EmptyCardinalityInterval implements CardinalityInterval {
43 } 45 }
44 46
45 @Override 47 @Override
48 public CardinalityInterval take(int count) {
49 return this;
50 }
51
52 @Override
46 public CardinalityInterval multiply(CardinalityInterval other) { 53 public CardinalityInterval multiply(CardinalityInterval other) {
47 return this; 54 return this;
48 } 55 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java
index 82afdbbc..b63a8637 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/FiniteUpperCardinality.java
@@ -6,6 +6,7 @@
6package tools.refinery.store.representation.cardinality; 6package tools.refinery.store.representation.cardinality;
7 7
8import org.jetbrains.annotations.NotNull; 8import org.jetbrains.annotations.NotNull;
9import org.jetbrains.annotations.Nullable;
9 10
10import java.util.function.IntBinaryOperator; 11import java.util.function.IntBinaryOperator;
11 12
@@ -22,6 +23,15 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi
22 } 23 }
23 24
24 @Override 25 @Override
26 @Nullable
27 public UpperCardinality take(int count) {
28 if (finiteUpperBound < count) {
29 return null;
30 }
31 return new FiniteUpperCardinality(finiteUpperBound - count);
32 }
33
34 @Override
25 public UpperCardinality multiply(UpperCardinality other) { 35 public UpperCardinality multiply(UpperCardinality other) {
26 return lift(other, (a, b) -> a * b); 36 return lift(other, (a, b) -> a * b);
27 } 37 }
@@ -49,7 +59,7 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi
49 59
50 private UpperCardinality lift(@NotNull UpperCardinality other, IntBinaryOperator operator) { 60 private UpperCardinality lift(@NotNull UpperCardinality other, IntBinaryOperator operator) {
51 if (other instanceof FiniteUpperCardinality finiteUpperCardinality) { 61 if (other instanceof FiniteUpperCardinality finiteUpperCardinality) {
52 return UpperCardinalities.valueOf(operator.applyAsInt(finiteUpperBound, 62 return UpperCardinalities.atMost(operator.applyAsInt(finiteUpperBound,
53 finiteUpperCardinality.finiteUpperBound)); 63 finiteUpperCardinality.finiteUpperBound));
54 } 64 }
55 if (other instanceof UnboundedUpperCardinality) { 65 if (other instanceof UnboundedUpperCardinality) {
@@ -57,4 +67,17 @@ public record FiniteUpperCardinality(int finiteUpperBound) implements UpperCardi
57 } 67 }
58 throw new IllegalArgumentException("Unknown UpperCardinality: " + other); 68 throw new IllegalArgumentException("Unknown UpperCardinality: " + other);
59 } 69 }
70
71 @Override
72 public boolean equals(Object o) {
73 if (this == o) return true;
74 if (o == null || getClass() != o.getClass()) return false;
75 FiniteUpperCardinality that = (FiniteUpperCardinality) o;
76 return finiteUpperBound == that.finiteUpperBound;
77 }
78
79 @Override
80 public int hashCode() {
81 return finiteUpperBound;
82 }
60} 83}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java
index 38bd53bf..6bd66df7 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/NonEmptyCardinalityInterval.java
@@ -5,6 +5,7 @@
5 */ 5 */
6package tools.refinery.store.representation.cardinality; 6package tools.refinery.store.representation.cardinality;
7 7
8import java.util.Objects;
8import java.util.function.BinaryOperator; 9import java.util.function.BinaryOperator;
9import java.util.function.IntBinaryOperator; 10import java.util.function.IntBinaryOperator;
10 11
@@ -53,6 +54,16 @@ public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upper
53 return lift(other, Math::min, UpperCardinality::max, this); 54 return lift(other, Math::min, UpperCardinality::max, this);
54 } 55 }
55 56
57 @Override
58 public CardinalityInterval take(int count) {
59 int newLowerBound = Math.max(lowerBound - count, 0);
60 var newUpperBound = upperBound.take(count);
61 if (newUpperBound == null) {
62 return CardinalityIntervals.ERROR;
63 }
64 return CardinalityIntervals.between(newLowerBound, newUpperBound);
65 }
66
56 private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator, 67 private CardinalityInterval lift(CardinalityInterval other, IntBinaryOperator lowerOperator,
57 BinaryOperator<UpperCardinality> upperOperator, 68 BinaryOperator<UpperCardinality> upperOperator,
58 CardinalityInterval whenEmpty) { 69 CardinalityInterval whenEmpty) {
@@ -73,7 +84,23 @@ public record NonEmptyCardinalityInterval(int lowerBound, UpperCardinality upper
73 84
74 @Override 85 @Override
75 public String toString() { 86 public String toString() {
76 var closeBracket = upperBound instanceof UnboundedUpperCardinality ? ")" : "]"; 87 if (upperBound instanceof FiniteUpperCardinality finiteUpperCardinality &&
77 return "[%d..%s%s".formatted(lowerBound, upperBound, closeBracket); 88 finiteUpperCardinality.finiteUpperBound() == lowerBound) {
89 return "[%d]".formatted(lowerBound);
90 }
91 return "[%d..%s]".formatted(lowerBound, upperBound);
92 }
93
94 @Override
95 public boolean equals(Object o) {
96 if (this == o) return true;
97 if (o == null || getClass() != o.getClass()) return false;
98 NonEmptyCardinalityInterval that = (NonEmptyCardinalityInterval) o;
99 return lowerBound == that.lowerBound && Objects.equals(upperBound, that.upperBound);
100 }
101
102 @Override
103 public int hashCode() {
104 return lowerBound * 31 + upperBound.hashCode();
78 } 105 }
79} 106}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java
index a5634020..03c701ae 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UnboundedUpperCardinality.java
@@ -7,6 +7,8 @@ package tools.refinery.store.representation.cardinality;
7 7
8import org.jetbrains.annotations.NotNull; 8import org.jetbrains.annotations.NotNull;
9 9
10// Singleton implementation, because there is only a single countable infinity.
11@SuppressWarnings("squid:S6548")
10public final class UnboundedUpperCardinality implements UpperCardinality { 12public final class UnboundedUpperCardinality implements UpperCardinality {
11 static final UnboundedUpperCardinality INSTANCE = new UnboundedUpperCardinality(); 13 static final UnboundedUpperCardinality INSTANCE = new UnboundedUpperCardinality();
12 14
@@ -20,10 +22,17 @@ public final class UnboundedUpperCardinality implements UpperCardinality {
20 } 22 }
21 23
22 @Override 24 @Override
25 public UpperCardinality take(int count) {
26 return this;
27 }
28
29 @Override
23 public UpperCardinality multiply(UpperCardinality other) { 30 public UpperCardinality multiply(UpperCardinality other) {
24 return this; 31 return this;
25 } 32 }
26 33
34 // This should always be greater than any finite cardinality.
35 @SuppressWarnings("ComparatorMethodParameterNotUsed")
27 @Override 36 @Override
28 public int compareTo(@NotNull UpperCardinality upperCardinality) { 37 public int compareTo(@NotNull UpperCardinality upperCardinality) {
29 if (upperCardinality instanceof FiniteUpperCardinality) { 38 if (upperCardinality instanceof FiniteUpperCardinality) {
@@ -44,4 +53,14 @@ public final class UnboundedUpperCardinality implements UpperCardinality {
44 public String toString() { 53 public String toString() {
45 return "*"; 54 return "*";
46 } 55 }
56
57 @Override
58 public boolean equals(Object obj) {
59 return this == obj || (obj != null && getClass() == obj.getClass());
60 }
61
62 @Override
63 public int hashCode() {
64 return -1;
65 }
47} 66}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java
index 1e18dde0..17d1b292 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinalities.java
@@ -26,7 +26,7 @@ public final class UpperCardinalities {
26 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 26 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
27 } 27 }
28 28
29 public static UpperCardinality valueOf(int upperBound) { 29 public static UpperCardinality atMost(int upperBound) {
30 if (upperBound < 0) { 30 if (upperBound < 0) {
31 return UNBOUNDED; 31 return UNBOUNDED;
32 } 32 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java
index 5dbaa922..3f0db028 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/representation/cardinality/UpperCardinality.java
@@ -5,6 +5,8 @@
5 */ 5 */
6package tools.refinery.store.representation.cardinality; 6package tools.refinery.store.representation.cardinality;
7 7
8import org.jetbrains.annotations.Nullable;
9
8public sealed interface UpperCardinality extends Comparable<UpperCardinality> permits FiniteUpperCardinality, 10public sealed interface UpperCardinality extends Comparable<UpperCardinality> permits FiniteUpperCardinality,
9 UnboundedUpperCardinality { 11 UnboundedUpperCardinality {
10 default UpperCardinality min(UpperCardinality other) { 12 default UpperCardinality min(UpperCardinality other) {
@@ -17,11 +19,14 @@ public sealed interface UpperCardinality extends Comparable<UpperCardinality> pe
17 19
18 UpperCardinality add(UpperCardinality other); 20 UpperCardinality add(UpperCardinality other);
19 21
22 @Nullable
23 UpperCardinality take(int count);
24
20 UpperCardinality multiply(UpperCardinality other); 25 UpperCardinality multiply(UpperCardinality other);
21 26
22 int compareToInt(int value); 27 int compareToInt(int value);
23 28
24 static UpperCardinality of(int upperBound) { 29 static UpperCardinality of(int upperBound) {
25 return UpperCardinalities.valueOf(upperBound); 30 return UpperCardinalities.atMost(upperBound);
26 } 31 }
27} 32}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java
index 04e17a13..809205e4 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java
@@ -7,9 +7,10 @@ package tools.refinery.store.statecoding;
7 7
8import org.eclipse.collections.api.set.primitive.IntSet; 8import org.eclipse.collections.api.set.primitive.IntSet;
9import tools.refinery.store.model.Interpretation; 9import tools.refinery.store.model.Interpretation;
10import tools.refinery.store.model.Model;
10 11
11import java.util.List; 12import java.util.List;
12 13
13public interface StateCodeCalculatorFactory { 14public interface StateCodeCalculatorFactory {
14 StateCodeCalculator create(List<? extends Interpretation<?>> interpretations, IntSet individuals); 15 StateCodeCalculator create(Model model, List<? extends Interpretation<?>> interpretations, IntSet individuals);
15} 16}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java
index 8cfd24d5..cb73d27e 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java
@@ -17,7 +17,7 @@ public interface StateCoderAdapter extends ModelAdapter {
17 return calculateStateCode().objectCode(); 17 return calculateStateCode().objectCode();
18 } 18 }
19 19
20 static StateCoderBuilderImpl builder() { 20 static StateCoderBuilder builder() {
21 return new StateCoderBuilderImpl(); 21 return new StateCoderBuilderImpl();
22 } 22 }
23} 23}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java
index 3fd8c8d8..0f0023ed 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java
@@ -6,7 +6,7 @@
6package tools.refinery.store.statecoding; 6package tools.refinery.store.statecoding;
7 7
8import org.eclipse.collections.api.set.primitive.IntSet; 8import org.eclipse.collections.api.set.primitive.IntSet;
9import tools.refinery.store.model.Interpretation; 9import tools.refinery.store.model.AnyInterpretation;
10 10
11import java.util.List; 11import java.util.List;
12 12
@@ -16,9 +16,6 @@ public interface StateEquivalenceChecker {
16 } 16 }
17 17
18 EquivalenceResult constructMorphism( 18 EquivalenceResult constructMorphism(
19 IntSet individuals, 19 IntSet individuals, List<? extends AnyInterpretation> interpretations1, ObjectCode code1,
20 List<? extends Interpretation<?>> interpretations1, 20 List<? extends AnyInterpretation> interpretations2, ObjectCode code2);
21 ObjectCode code1, List<?
22 extends Interpretation<?>> interpretations2,
23 ObjectCode code2);
24} 21}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java
index 05b47c52..eed591e7 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java
@@ -5,61 +5,61 @@
5 */ 5 */
6package tools.refinery.store.statecoding.internal; 6package tools.refinery.store.statecoding.internal;
7 7
8import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; 8import org.eclipse.collections.api.factory.primitive.IntSets;
9import org.eclipse.collections.api.set.primitive.MutableIntSet;
10import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
9import tools.refinery.store.model.ModelStore; 11import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.model.ModelStoreBuilder;
11import tools.refinery.store.representation.AnySymbol; 12import tools.refinery.store.representation.AnySymbol;
12import tools.refinery.store.representation.Symbol; 13import tools.refinery.store.representation.Symbol;
13import tools.refinery.store.statecoding.*; 14import tools.refinery.store.statecoding.StateCodeCalculatorFactory;
15import tools.refinery.store.statecoding.StateCoderBuilder;
16import tools.refinery.store.statecoding.StateCoderStoreAdapter;
17import tools.refinery.store.statecoding.StateEquivalenceChecker;
14import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; 18import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator;
15import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; 19import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl;
16import tools.refinery.store.tuple.Tuple1; 20import tools.refinery.store.tuple.Tuple1;
17 21
18import java.util.*; 22import java.util.HashSet;
23import java.util.LinkedHashSet;
24import java.util.Set;
19 25
20public class StateCoderBuilderImpl implements StateCoderBuilder { 26public class StateCoderBuilderImpl extends AbstractModelAdapterBuilder<StateCoderStoreAdapter>
21 Set<AnySymbol> excluded = new HashSet<>(); 27 implements StateCoderBuilder {
22 IntHashSet individuals = new IntHashSet(); 28 private final Set<AnySymbol> excluded = new HashSet<>();
23 29 private final MutableIntSet individuals = IntSets.mutable.empty();
24 StateCodeCalculatorFactory calculator = NeighbourhoodCalculator::new; 30 private StateCodeCalculatorFactory calculator = NeighbourhoodCalculator::new;
25 StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl(); 31 private StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl();
26 32
27 @Override 33 @Override
28 public StateCoderBuilder exclude(AnySymbol symbol) { 34 public StateCoderBuilder exclude(AnySymbol symbol) {
35 checkNotConfigured();
29 excluded.add(symbol); 36 excluded.add(symbol);
30 return this; 37 return this;
31 } 38 }
32 39
33 @Override 40 @Override
34 public StateCoderBuilder individual(Tuple1 tuple) { 41 public StateCoderBuilder individual(Tuple1 tuple) {
42 checkNotConfigured();
35 individuals.add(tuple.get(0)); 43 individuals.add(tuple.get(0));
36 return this; 44 return this;
37 } 45 }
38 46
39 @Override 47 @Override
40 public StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker) { 48 public StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker) {
49 checkNotConfigured();
41 this.checker = stateEquivalenceChecker; 50 this.checker = stateEquivalenceChecker;
42 return this; 51 return this;
43 } 52 }
44 53
45 @Override 54 @Override
46 public StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory) { 55 public StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory) {
56 checkNotConfigured();
47 this.calculator = codeCalculatorFactory; 57 this.calculator = codeCalculatorFactory;
48 return this; 58 return this;
49 } 59 }
50 60
51 @Override 61 @Override
52 public boolean isConfigured() { 62 protected StateCoderStoreAdapter doBuild(ModelStore store) {
53 return true;
54 }
55
56 @Override
57 public void configure(ModelStoreBuilder storeBuilder) {
58 // It does not modify the build process
59 }
60
61 @Override
62 public StateCoderStoreAdapter build(ModelStore store) {
63 Set<Symbol<?>> symbols = new LinkedHashSet<>(); 63 Set<Symbol<?>> symbols = new LinkedHashSet<>();
64 for (AnySymbol symbol : store.getSymbols()) { 64 for (AnySymbol symbol : store.getSymbols()) {
65 if (!excluded.contains(symbol) && (symbol instanceof Symbol<?> typed)) { 65 if (!excluded.contains(symbol) && (symbol instanceof Symbol<?> typed)) {
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java
index 89586bfb..2cb6f3c1 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java
@@ -68,7 +68,7 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter {
68 @Override 68 @Override
69 public StateCoderAdapter createModelAdapter(Model model) { 69 public StateCoderAdapter createModelAdapter(Model model) {
70 var interpretations = symbols.stream().map(model::getInterpretation).toList(); 70 var interpretations = symbols.stream().map(model::getInterpretation).toList();
71 var coder = codeCalculatorFactory.create(interpretations, individuals); 71 var coder = codeCalculatorFactory.create(model, interpretations, individuals);
72 return new StateCoderAdapterImpl(this, coder, model); 72 return new StateCoderAdapterImpl(this, coder, model);
73 } 73 }
74} 74}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java
index 0a40a19d..4cef6786 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java
@@ -5,9 +5,12 @@
5 */ 5 */
6package tools.refinery.store.statecoding.neighbourhood; 6package tools.refinery.store.statecoding.neighbourhood;
7 7
8import org.eclipse.collections.api.factory.primitive.IntLongMaps;
9import org.eclipse.collections.api.map.primitive.MutableIntLongMap;
8import org.eclipse.collections.api.set.primitive.IntSet; 10import org.eclipse.collections.api.set.primitive.IntSet;
9import org.eclipse.collections.impl.map.mutable.primitive.IntLongHashMap; 11import tools.refinery.store.model.AnyInterpretation;
10import tools.refinery.store.model.Interpretation; 12import tools.refinery.store.model.Interpretation;
13import tools.refinery.store.model.Model;
11import tools.refinery.store.statecoding.ObjectCode; 14import tools.refinery.store.statecoding.ObjectCode;
12import tools.refinery.store.tuple.Tuple; 15import tools.refinery.store.tuple.Tuple;
13import tools.refinery.store.tuple.Tuple0; 16import tools.refinery.store.tuple.Tuple0;
@@ -15,25 +18,28 @@ import tools.refinery.store.tuple.Tuple0;
15import java.util.*; 18import java.util.*;
16 19
17public abstract class AbstractNeighbourhoodCalculator { 20public abstract class AbstractNeighbourhoodCalculator {
18 protected final List<Interpretation<?>> nullImpactValues; 21 protected final Model model;
19 protected final LinkedHashMap<Interpretation<?>, long[]> impactValues; 22 protected final List<AnyInterpretation> nullImpactValues;
20 protected final IntLongHashMap individualHashValues; 23 protected final LinkedHashMap<AnyInterpretation, long[]> impactValues;
24 protected final MutableIntLongMap individualHashValues = IntLongMaps.mutable.empty();
21 25
22 protected static final long PRIME = 31; 26 protected static final long PRIME = 31;
23 27
24 protected AbstractNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { 28 protected AbstractNeighbourhoodCalculator(Model model, List<? extends AnyInterpretation> interpretations,
29 IntSet individuals) {
30 this.model = model;
25 this.nullImpactValues = new ArrayList<>(); 31 this.nullImpactValues = new ArrayList<>();
26 this.impactValues = new LinkedHashMap<>(); 32 this.impactValues = new LinkedHashMap<>();
33 // Random isn't used for cryptographical purposes but just to assign distinguishable identifiers to symbols.
27 @SuppressWarnings("squid:S2245") 34 @SuppressWarnings("squid:S2245")
28 Random random = new Random(1); 35 Random random = new Random(1);
29 36
30 individualHashValues = new IntLongHashMap();
31 var individualsInOrder = individuals.toSortedList(Integer::compare); 37 var individualsInOrder = individuals.toSortedList(Integer::compare);
32 for(int i = 0; i<individualsInOrder.size(); i++) { 38 for(int i = 0; i<individualsInOrder.size(); i++) {
33 individualHashValues.put(individualsInOrder.get(i), random.nextLong()); 39 individualHashValues.put(individualsInOrder.get(i), random.nextLong());
34 } 40 }
35 41
36 for (Interpretation<?> interpretation : interpretations) { 42 for (AnyInterpretation interpretation : interpretations) {
37 int arity = interpretation.getSymbol().arity(); 43 int arity = interpretation.getSymbol().arity();
38 if (arity == 0) { 44 if (arity == 0) {
39 nullImpactValues.add(interpretation); 45 nullImpactValues.add(interpretation);
@@ -86,7 +92,7 @@ public abstract class AbstractNeighbourhoodCalculator {
86 protected long calculateModelCode(long lastSum) { 92 protected long calculateModelCode(long lastSum) {
87 long result = 0; 93 long result = 0;
88 for (var nullImpactValue : nullImpactValues) { 94 for (var nullImpactValue : nullImpactValues) {
89 result = result * PRIME + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); 95 result = result * PRIME + Objects.hashCode(((Interpretation<?>) nullImpactValue).get(Tuple0.INSTANCE));
90 } 96 }
91 result += lastSum; 97 result += lastSum;
92 return result; 98 return result;
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java
index 2ffbef5e..04335141 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java
@@ -5,11 +5,14 @@
5 */ 5 */
6package tools.refinery.store.statecoding.neighbourhood; 6package tools.refinery.store.statecoding.neighbourhood;
7 7
8import org.eclipse.collections.api.factory.primitive.LongIntMaps;
8import org.eclipse.collections.api.map.primitive.LongIntMap; 9import org.eclipse.collections.api.map.primitive.LongIntMap;
10import org.eclipse.collections.api.map.primitive.MutableLongIntMap;
9import org.eclipse.collections.api.set.primitive.IntSet; 11import org.eclipse.collections.api.set.primitive.IntSet;
10import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap;
11import tools.refinery.store.map.Cursor; 12import tools.refinery.store.map.Cursor;
13import tools.refinery.store.model.AnyInterpretation;
12import tools.refinery.store.model.Interpretation; 14import tools.refinery.store.model.Interpretation;
15import tools.refinery.store.model.Model;
13import tools.refinery.store.statecoding.StateCodeCalculator; 16import tools.refinery.store.statecoding.StateCodeCalculator;
14import tools.refinery.store.statecoding.StateCoderResult; 17import tools.refinery.store.statecoding.StateCoderResult;
15import tools.refinery.store.tuple.Tuple; 18import tools.refinery.store.tuple.Tuple;
@@ -17,13 +20,14 @@ import tools.refinery.store.tuple.Tuple;
17import java.util.List; 20import java.util.List;
18 21
19public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { 22public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator {
20 public LazyNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { 23 public LazyNeighbourhoodCalculator(Model model, List<? extends AnyInterpretation> interpretations,
21 super(interpretations, individuals); 24 IntSet individuals) {
25 super(model, interpretations, individuals);
22 } 26 }
23 27
24 public StateCoderResult calculateCodes() { 28 public StateCoderResult calculateCodes() {
25 ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); 29 ObjectCodeImpl previousObjectCode = new ObjectCodeImpl();
26 LongIntHashMap prevHash2Amount = new LongIntHashMap(); 30 MutableLongIntMap prevHash2Amount = LongIntMaps.mutable.empty();
27 31
28 long lastSum; 32 long lastSum;
29 // All hash code is 0, except to the individuals. 33 // All hash code is 0, except to the individuals.
@@ -42,7 +46,7 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator
42 } 46 }
43 constructNextObjectCodes(previousObjectCode, nextObjectCode, prevHash2Amount); 47 constructNextObjectCodes(previousObjectCode, nextObjectCode, prevHash2Amount);
44 48
45 LongIntHashMap nextHash2Amount = new LongIntHashMap(); 49 MutableLongIntMap nextHash2Amount = LongIntMaps.mutable.empty();
46 lastSum = calculateLastSum(previousObjectCode, nextObjectCode, prevHash2Amount, nextHash2Amount); 50 lastSum = calculateLastSum(previousObjectCode, nextObjectCode, prevHash2Amount, nextHash2Amount);
47 51
48 int nextSize = nextHash2Amount.size(); 52 int nextSize = nextHash2Amount.size();
@@ -60,7 +64,7 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator
60 } 64 }
61 65
62 private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, 66 private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount,
63 LongIntHashMap nextHash2Amount) { 67 MutableLongIntMap nextHash2Amount) {
64 long lastSum = 0; 68 long lastSum = 0;
65 for (int i = 0; i < next.getSize(); i++) { 69 for (int i = 0; i < next.getSize(); i++) {
66 final long hash; 70 final long hash;
@@ -84,7 +88,7 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator
84 88
85 private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount) { 89 private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount) {
86 for (var impactValueEntry : this.impactValues.entrySet()) { 90 for (var impactValueEntry : this.impactValues.entrySet()) {
87 Interpretation<?> interpretation = impactValueEntry.getKey(); 91 Interpretation<?> interpretation = (Interpretation<?>) impactValueEntry.getKey();
88 var cursor = interpretation.getAll(); 92 var cursor = interpretation.getAll();
89 int arity = interpretation.getSymbol().arity(); 93 int arity = interpretation.getSymbol().arity();
90 long[] impactValue = impactValueEntry.getValue(); 94 long[] impactValue = impactValueEntry.getValue();
@@ -114,7 +118,8 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator
114 return amount == 1; 118 return amount == 1;
115 } 119 }
116 120
117 private void lazyImpactCalculation1(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { 121 private void lazyImpactCalculation1(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next,
122 long[] impactValues, Cursor<Tuple, ?> cursor) {
118 123
119 Tuple tuple = cursor.getKey(); 124 Tuple tuple = cursor.getKey();
120 int o = tuple.get(0); 125 int o = tuple.get(0);
@@ -129,7 +134,8 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator
129 } 134 }
130 } 135 }
131 136
132 private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { 137 private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next,
138 long[] impactValues, Cursor<Tuple, ?> cursor) {
133 final Tuple tuple = cursor.getKey(); 139 final Tuple tuple = cursor.getKey();
134 final int o1 = tuple.get(0); 140 final int o1 = tuple.get(0);
135 final int o2 = tuple.get(1); 141 final int o2 = tuple.get(1);
@@ -155,7 +161,8 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator
155 } 161 }
156 } 162 }
157 163
158 private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { 164 private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next,
165 long[] impactValues, Cursor<Tuple, ?> cursor) {
159 final Tuple tuple = cursor.getKey(); 166 final Tuple tuple = cursor.getKey();
160 167
161 final boolean[] uniques = new boolean[tuple.getSize()]; 168 final boolean[] uniques = new boolean[tuple.getSize()];
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java
index 5b3e5ea3..5e6de53b 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java
@@ -8,6 +8,7 @@ package tools.refinery.store.statecoding.neighbourhood;
8import org.eclipse.collections.api.set.primitive.IntSet; 8import org.eclipse.collections.api.set.primitive.IntSet;
9import tools.refinery.store.map.Cursor; 9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.model.Interpretation; 10import tools.refinery.store.model.Interpretation;
11import tools.refinery.store.model.Model;
11import tools.refinery.store.statecoding.ObjectCode; 12import tools.refinery.store.statecoding.ObjectCode;
12import tools.refinery.store.statecoding.StateCodeCalculator; 13import tools.refinery.store.statecoding.StateCodeCalculator;
13import tools.refinery.store.statecoding.StateCoderResult; 14import tools.refinery.store.statecoding.StateCoderResult;
@@ -18,21 +19,27 @@ import java.util.List;
18import java.util.Objects; 19import java.util.Objects;
19 20
20public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { 21public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator {
21 public NeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { 22 private ObjectCodeImpl previousObjectCode = new ObjectCodeImpl();
22 super(interpretations, individuals); 23 private ObjectCodeImpl nextObjectCode = new ObjectCodeImpl();
24
25 public NeighbourhoodCalculator(Model model, List<? extends Interpretation<?>> interpretations, IntSet individuals) {
26 super(model, interpretations, individuals);
23 } 27 }
24 28
25 public StateCoderResult calculateCodes() { 29 public StateCoderResult calculateCodes() {
26 ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); 30 model.checkCancelled();
31 previousObjectCode.clear();
32 nextObjectCode.clear();
27 initializeWithIndividuals(previousObjectCode); 33 initializeWithIndividuals(previousObjectCode);
28 34
29 int rounds = 0; 35 int rounds = 0;
30 do { 36 do {
31 final ObjectCodeImpl nextObjectCode = rounds == 0 ? new ObjectCodeImpl() : 37 model.checkCancelled();
32 new ObjectCodeImpl(previousObjectCode.getSize());
33
34 constructNextObjectCodes(previousObjectCode, nextObjectCode); 38 constructNextObjectCodes(previousObjectCode, nextObjectCode);
39 var tempObjectCode = previousObjectCode;
35 previousObjectCode = nextObjectCode; 40 previousObjectCode = nextObjectCode;
41 nextObjectCode = tempObjectCode;
42 nextObjectCode.clear();
36 rounds++; 43 rounds++;
37 } while (rounds <= 7 && rounds <= previousObjectCode.getEffectiveSize()); 44 } while (rounds <= 7 && rounds <= previousObjectCode.getEffectiveSize());
38 45
@@ -43,7 +50,7 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp
43 private long calculateLastSum(ObjectCode codes) { 50 private long calculateLastSum(ObjectCode codes) {
44 long result = 0; 51 long result = 0;
45 for (var nullImpactValue : nullImpactValues) { 52 for (var nullImpactValue : nullImpactValues) {
46 result = result * 31 + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); 53 result = result * PRIME + Objects.hashCode(((Interpretation<?>) nullImpactValue).get(Tuple0.INSTANCE));
47 } 54 }
48 55
49 for (int i = 0; i < codes.getSize(); i++) { 56 for (int i = 0; i < codes.getSize(); i++) {
@@ -56,7 +63,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp
56 63
57 private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { 64 private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) {
58 for (var impactValueEntry : this.impactValues.entrySet()) { 65 for (var impactValueEntry : this.impactValues.entrySet()) {
59 Interpretation<?> interpretation = impactValueEntry.getKey(); 66 model.checkCancelled();
67 Interpretation<?> interpretation = (Interpretation<?>) impactValueEntry.getKey();
60 var cursor = interpretation.getAll(); 68 var cursor = interpretation.getAll();
61 int arity = interpretation.getSymbol().arity(); 69 int arity = interpretation.getSymbol().arity();
62 long[] impactValue = impactValueEntry.getValue(); 70 long[] impactValue = impactValueEntry.getValue();
@@ -78,7 +86,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp
78 } 86 }
79 87
80 88
81 private void impactCalculation1(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { 89 private void impactCalculation1(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues,
90 Cursor<Tuple, ?> cursor) {
82 91
83 Tuple tuple = cursor.getKey(); 92 Tuple tuple = cursor.getKey();
84 int o = tuple.get(0); 93 int o = tuple.get(0);
@@ -87,7 +96,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp
87 addHash(next, o, impactValues[0], tupleHash); 96 addHash(next, o, impactValues[0], tupleHash);
88 } 97 }
89 98
90 private void impactCalculation2(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { 99 private void impactCalculation2(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues,
100 Cursor<Tuple, ?> cursor) {
91 final Tuple tuple = cursor.getKey(); 101 final Tuple tuple = cursor.getKey();
92 final int o1 = tuple.get(0); 102 final int o1 = tuple.get(0);
93 final int o2 = tuple.get(1); 103 final int o2 = tuple.get(1);
@@ -99,7 +109,8 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp
99 addHash(next, o2, impactValues[1], tupleHash); 109 addHash(next, o2, impactValues[1], tupleHash);
100 } 110 }
101 111
102 private void impactCalculationN(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { 112 private void impactCalculationN(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues,
113 Cursor<Tuple, ?> cursor) {
103 final Tuple tuple = cursor.getKey(); 114 final Tuple tuple = cursor.getKey();
104 115
105 Object value = cursor.getValue(); 116 Object value = cursor.getValue();
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java
index 0cd7ff58..422e1d73 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java
@@ -32,6 +32,13 @@ public class ObjectCodeImpl implements ObjectCode {
32 effectiveSize = copy.effectiveSize; 32 effectiveSize = copy.effectiveSize;
33 } 33 }
34 34
35 public void clear() {
36 effectiveSize = 0;
37 for (int i = 0; i < size; i++) {
38 vector[i] = 0;
39 }
40 }
41
35 public void ensureSize(int object) { 42 public void ensureSize(int object) {
36 if (object >= size) { 43 if (object >= size) {
37 size = object + 1; 44 size = object + 1;
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java
index decff1d5..0682e1a4 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java
@@ -8,18 +8,14 @@ package tools.refinery.store.statecoding.stateequivalence;
8import org.eclipse.collections.api.factory.primitive.IntIntMaps; 8import org.eclipse.collections.api.factory.primitive.IntIntMaps;
9import org.eclipse.collections.api.map.primitive.IntIntMap; 9import org.eclipse.collections.api.map.primitive.IntIntMap;
10import org.eclipse.collections.api.set.primitive.IntSet; 10import org.eclipse.collections.api.set.primitive.IntSet;
11import org.eclipse.collections.impl.list.Interval;
12import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet;
13 11
14import java.util.ArrayList; 12import java.util.*;
15import java.util.Arrays;
16import java.util.List;
17 13
18public class CombinationNodePairing implements NodePairing { 14public class CombinationNodePairing implements NodePairing {
19 private final int[] left; 15 private final int[] left;
20 private final int[] right; 16 private final int[] right;
21 17
22 CombinationNodePairing(IntSet left, IntHashSet right) { 18 CombinationNodePairing(IntSet left, IntSet right) {
23 this.left = left.toArray(); 19 this.left = left.toArray();
24 this.right = right.toArray(); 20 this.right = right.toArray();
25 21
@@ -32,59 +28,33 @@ public class CombinationNodePairing implements NodePairing {
32 return left.length; 28 return left.length;
33 } 29 }
34 30
35 static final int LIMIT = 5; 31 private static final int LIMIT = 5;
36 static final List<List<int[]>> permutations = new ArrayList<>(); 32 // Enum-based singleton used to delay generating all permutations until they are first needed.
37 33 @SuppressWarnings("squid:S6548")
38 /** 34 private enum PermutationsHolder {
39 * Generates and stores permutations up to a given size. If the number would be more than a limit, it provides a 35 INSTANCE;
40 * single permutation only. 36
41 * 37 final CombinationNodePairingPermutations permutations = new CombinationNodePairingPermutations(LIMIT);
42 * @param max The max number in the permutation
43 * @return A complete list of permutations of numbers 0...max, or a single permutation.
44 */
45 public static List<int[]> getPermutations(int max) {
46 if (max < permutations.size()) {
47 return permutations.get(max);
48 }
49 if (max == 0) {
50 List<int[]> result = new ArrayList<>();
51 result.add(new int[1]);
52 permutations.add(result);
53 return result;
54 }
55 List<int[]> result = new ArrayList<>();
56 List<int[]> previousPermutations = getPermutations(max - 1);
57 for (var permutation : previousPermutations) {
58 for (int pos = 0; pos <= max; pos++) {
59 int[] newPermutation = new int[max + 1];
60 System.arraycopy(permutation, 0, newPermutation, 0, pos);
61 newPermutation[pos] = max;
62 if (max - (pos + 1) >= 0)
63 System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1));
64 result.add(newPermutation);
65 }
66 }
67 permutations.add(result);
68 return result;
69 } 38 }
70 39
71 @Override 40 @Override
72 public List<IntIntMap> permutations() { 41 public List<IntIntMap> permutations() {
73 final var interval = Interval.zeroTo(this.size() - 1); 42 int limit = this.size();
43 Iterable<Integer> interval = () -> new IntervalIterator(limit);
74 44
75 if (isComplete()) { 45 if (isComplete()) {
76 final List<int[]> p = getPermutations(this.size() - 1); 46 final List<int[]> p = PermutationsHolder.INSTANCE.permutations.getPermutations(this.size() - 1);
77 return p.stream().map(x -> constructPermutationMap(interval, x)).toList(); 47 return p.stream().map(x -> constructPermutationMap(interval, x)).toList();
78 } else { 48 } else {
79 return List.of(constructTrivialMap(interval)); 49 return List.of(constructTrivialMap(interval));
80 } 50 }
81 } 51 }
82 52
83 private IntIntMap constructTrivialMap(Interval interval) { 53 private IntIntMap constructTrivialMap(Iterable<Integer> interval) {
84 return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[r]); 54 return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[r]);
85 } 55 }
86 56
87 private IntIntMap constructPermutationMap(Interval interval, int[] permutation) { 57 private IntIntMap constructPermutationMap(Iterable<Integer> interval, int[] permutation) {
88 return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[permutation[r]]); 58 return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[permutation[r]]);
89 } 59 }
90 60
@@ -92,4 +62,28 @@ public class CombinationNodePairing implements NodePairing {
92 public boolean isComplete() { 62 public boolean isComplete() {
93 return this.size() <= LIMIT; 63 return this.size() <= LIMIT;
94 } 64 }
65
66 private static class IntervalIterator implements Iterator<Integer> {
67 private final int limit;
68 private int value = 0;
69
70 private IntervalIterator(int max) {
71 this.limit = max;
72 }
73
74 @Override
75 public boolean hasNext() {
76 return value < limit;
77 }
78
79 @Override
80 public Integer next() {
81 if (value >= limit) {
82 throw new NoSuchElementException("End of interval");
83 }
84 int next = value;
85 value++;
86 return next;
87 }
88 }
95} 89}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java
new file mode 100644
index 00000000..eacd3a2a
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairingPermutations.java
@@ -0,0 +1,57 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.statecoding.stateequivalence;
7
8import java.util.ArrayList;
9import java.util.List;
10
11class CombinationNodePairingPermutations {
12 private final List<List<int[]>> permutations = new ArrayList<>();
13
14 public CombinationNodePairingPermutations(int max) {
15 initializePermutations(max);
16 }
17
18 public List<int[]> getPermutations(int max) {
19 if (max >= permutations.size()) {
20 throw new IllegalArgumentException("Only permutations up to %d elements are supported".formatted(max));
21 }
22 return permutations.get(max);
23 }
24
25 /**
26 * Generates and stores permutations up to a given size. If the number would be more than a limit, it provides a
27 * single permutation only.
28 *
29 * @param max The max number in the permutation
30 * @return A complete list of permutations of numbers 0...max, or a single permutation.
31 */
32 private List<int[]> initializePermutations(int max) {
33 if (max < permutations.size()) {
34 return permutations.get(max);
35 }
36 if (max == 0) {
37 List<int[]> result = new ArrayList<>();
38 result.add(new int[1]);
39 permutations.add(result);
40 return result;
41 }
42 List<int[]> result = new ArrayList<>();
43 List<int[]> previousPermutations = initializePermutations(max - 1);
44 for (var permutation : previousPermutations) {
45 for (int pos = 0; pos <= max; pos++) {
46 int[] newPermutation = new int[max + 1];
47 System.arraycopy(permutation, 0, newPermutation, 0, pos);
48 newPermutation[pos] = max;
49 if (max - (pos + 1) >= 0)
50 System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1));
51 result.add(newPermutation);
52 }
53 }
54 permutations.add(result);
55 return result;
56 }
57}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java
index 7e5db7a3..f45f0d2e 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java
@@ -6,18 +6,18 @@
6package tools.refinery.store.statecoding.stateequivalence; 6package tools.refinery.store.statecoding.stateequivalence;
7 7
8import org.eclipse.collections.api.map.primitive.IntIntMap; 8import org.eclipse.collections.api.map.primitive.IntIntMap;
9import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; 9import org.eclipse.collections.api.set.primitive.IntSet;
10 10
11import java.util.List; 11import java.util.List;
12 12
13public interface NodePairing { 13public interface NodePairing {
14
15 int size(); 14 int size();
15
16 List<IntIntMap> permutations(); 16 List<IntIntMap> permutations();
17 17
18 boolean isComplete(); 18 boolean isComplete();
19 19
20 static NodePairing constructNodePairing(IntHashSet left, IntHashSet right){ 20 static NodePairing constructNodePairing(IntSet left, IntSet right){
21 if(left.size() != right.size()) { 21 if(left.size() != right.size()) {
22 return null; 22 return null;
23 } 23 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java
index 6be0f3e7..bc4d723a 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java
@@ -12,12 +12,12 @@ import java.util.List;
12 12
13public class PermutationMorphism implements Morphism { 13public class PermutationMorphism implements Morphism {
14 private final IntIntMap object2PermutationGroup; 14 private final IntIntMap object2PermutationGroup;
15 private final List<List<IntIntMap>> permutationsGroups; 15 private final List<? extends List<? extends IntIntMap>> permutationsGroups;
16 private final int[] selection; 16 private final int[] selection;
17 private boolean hasNext; 17 private boolean hasNext;
18 18
19 PermutationMorphism(IntIntMap object2PermutationGroup, 19 PermutationMorphism(IntIntMap object2PermutationGroup,
20 List<List<IntIntMap>> permutationsGroups) { 20 List<? extends List<? extends IntIntMap>> permutationsGroups) {
21 this.object2PermutationGroup = object2PermutationGroup; 21 this.object2PermutationGroup = object2PermutationGroup;
22 this.permutationsGroups = permutationsGroups; 22 this.permutationsGroups = permutationsGroups;
23 23
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java
index ef0d76a7..5a62d8a0 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java
@@ -6,11 +6,14 @@
6package tools.refinery.store.statecoding.stateequivalence; 6package tools.refinery.store.statecoding.stateequivalence;
7 7
8import org.eclipse.collections.api.factory.primitive.IntIntMaps; 8import org.eclipse.collections.api.factory.primitive.IntIntMaps;
9import org.eclipse.collections.api.factory.primitive.IntSets;
10import org.eclipse.collections.api.factory.primitive.LongObjectMaps;
9import org.eclipse.collections.api.map.primitive.IntIntMap; 11import org.eclipse.collections.api.map.primitive.IntIntMap;
12import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
13import org.eclipse.collections.api.map.primitive.MutableLongObjectMap;
10import org.eclipse.collections.api.set.primitive.IntSet; 14import org.eclipse.collections.api.set.primitive.IntSet;
11import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; 15import org.eclipse.collections.api.set.primitive.MutableIntSet;
12import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; 16import tools.refinery.store.model.AnyInterpretation;
13import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet;
14import tools.refinery.store.model.Interpretation; 17import tools.refinery.store.model.Interpretation;
15import tools.refinery.store.statecoding.Morphism; 18import tools.refinery.store.statecoding.Morphism;
16import tools.refinery.store.statecoding.ObjectCode; 19import tools.refinery.store.statecoding.ObjectCode;
@@ -26,12 +29,11 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker {
26 29
27 @Override 30 @Override
28 public EquivalenceResult constructMorphism(IntSet individuals, 31 public EquivalenceResult constructMorphism(IntSet individuals,
29 List<? extends Interpretation<?>> interpretations1, 32 List<? extends AnyInterpretation> interpretations1,
30 ObjectCode code1, 33 ObjectCode code1,
31 List<? extends Interpretation<?>> interpretations2, 34 List<? extends AnyInterpretation> interpretations2,
32 ObjectCode code2) 35 ObjectCode code2) {
33 { 36 MutableIntIntMap object2PermutationGroup = IntIntMaps.mutable.empty();
34 IntIntHashMap object2PermutationGroup = new IntIntHashMap();
35 List<List<IntIntMap>> permutationsGroups = new ArrayList<>(); 37 List<List<IntIntMap>> permutationsGroups = new ArrayList<>();
36 38
37 final EquivalenceResult permutations = constructPermutationNavigation(individuals, 39 final EquivalenceResult permutations = constructPermutationNavigation(individuals,
@@ -50,7 +52,7 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker {
50 return permutations; 52 return permutations;
51 } 53 }
52 54
53 if(tried >= LIMIT) { 55 if (tried >= LIMIT) {
54 return EquivalenceResult.UNKNOWN; 56 return EquivalenceResult.UNKNOWN;
55 } 57 }
56 58
@@ -58,22 +60,22 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker {
58 tried++; 60 tried++;
59 } while (hasNext); 61 } while (hasNext);
60 62
61 if(permutations == EquivalenceResult.UNKNOWN) { 63 if (permutations == EquivalenceResult.UNKNOWN) {
62 return EquivalenceResult.UNKNOWN; 64 return EquivalenceResult.UNKNOWN;
63 } else { 65 } else {
64 return EquivalenceResult.DIFFERENT; 66 return EquivalenceResult.DIFFERENT;
65 } 67 }
66 } 68 }
67 69
68 private LongObjectHashMap<IntHashSet> indexByHash(ObjectCode code, IntSet individuals) { 70 private MutableLongObjectMap<MutableIntSet> indexByHash(ObjectCode code, IntSet individuals) {
69 LongObjectHashMap<IntHashSet> result = new LongObjectHashMap<>(); 71 MutableLongObjectMap<MutableIntSet> result = LongObjectMaps.mutable.empty();
70 for (int o = 0; o < code.getSize(); o++) { 72 for (int o = 0; o < code.getSize(); o++) {
71 if(! individuals.contains(o)){ 73 if (!individuals.contains(o)) {
72 long hash = code.get(o); 74 long hash = code.get(o);
73 if(hash != 0) { 75 if (hash != 0) {
74 var equivalenceClass = result.get(hash); 76 var equivalenceClass = result.get(hash);
75 if (equivalenceClass == null) { 77 if (equivalenceClass == null) {
76 equivalenceClass = new IntHashSet(); 78 equivalenceClass = IntSets.mutable.empty();
77 result.put(hash, equivalenceClass); 79 result.put(hash, equivalenceClass);
78 } 80 }
79 equivalenceClass.add(o); 81 equivalenceClass.add(o);
@@ -83,11 +85,9 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker {
83 return result; 85 return result;
84 } 86 }
85 87
86 private EquivalenceResult constructPermutationNavigation(IntSet individuals, 88 private EquivalenceResult constructPermutationNavigation(
87 LongObjectHashMap<IntHashSet> map1, 89 IntSet individuals, MutableLongObjectMap<MutableIntSet> map1, MutableLongObjectMap<MutableIntSet> map2,
88 LongObjectHashMap<IntHashSet> map2, 90 MutableIntIntMap object2OptionIndex, List<List<IntIntMap>> listOfOptions) {
89 IntIntHashMap object2OptionIndex,
90 List<List<IntIntMap>> listOfOptions) {
91 if (map1.size() != map2.size()) { 91 if (map1.size() != map2.size()) {
92 return EquivalenceResult.DIFFERENT; 92 return EquivalenceResult.DIFFERENT;
93 } 93 }
@@ -116,27 +116,28 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker {
116 listOfOptions.add(pairing.permutations()); 116 listOfOptions.add(pairing.permutations());
117 } 117 }
118 118
119 individuals.forEach(o -> listOfOptions.add(o,List.of(IntIntMaps.immutable.of(o,o)))); 119 individuals.forEach(o -> listOfOptions.add(o, List.of(IntIntMaps.immutable.of(o, o))));
120 120
121 if(allComplete) { 121 if (allComplete) {
122 return EquivalenceResult.ISOMORPHIC; 122 return EquivalenceResult.ISOMORPHIC;
123 } else { 123 } else {
124 return EquivalenceResult.UNKNOWN; 124 return EquivalenceResult.UNKNOWN;
125 } 125 }
126 } 126 }
127 127
128 private boolean testMorphism(List<? extends Interpretation<?>> s, List<? extends Interpretation<?>> t, Morphism m) { 128 private boolean testMorphism(List<? extends AnyInterpretation> s, List<? extends AnyInterpretation> t,
129 Morphism m) {
129 for (int interpretationIndex = 0; interpretationIndex < s.size(); interpretationIndex++) { 130 for (int interpretationIndex = 0; interpretationIndex < s.size(); interpretationIndex++) {
130 var sI = s.get(interpretationIndex); 131 var sI = s.get(interpretationIndex);
131 var tI = t.get(interpretationIndex); 132 var tI = t.get(interpretationIndex);
132 133
133 var cursor = sI.getAll(); 134 var cursor = ((Interpretation<?>) sI).getAll();
134 while (cursor.move()) { 135 while (cursor.move()) {
135 final Tuple sTuple = cursor.getKey(); 136 final Tuple sTuple = cursor.getKey();
136 final Object sValue = cursor.getValue(); 137 final Object sValue = cursor.getValue();
137 138
138 final Tuple tTuple = apply(sTuple, m); 139 final Tuple tTuple = apply(sTuple, m);
139 final Object tValue = tI.get(tTuple); 140 final Object tValue = ((Interpretation<?>) tI).get(tTuple);
140 141
141 if (!Objects.equals(sValue, tValue)) { 142 if (!Objects.equals(sValue, tValue)) {
142 return false; 143 return false;
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java
index aae7b344..e9761763 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple.java
@@ -12,6 +12,8 @@ public sealed interface Tuple extends Comparable<Tuple> permits Tuple0, Tuple1,
12 12
13 int get(int element); 13 int get(int element);
14 14
15 Tuple set(int element, int value);
16
15 @Override 17 @Override
16 default int compareTo(@NotNull Tuple other) { 18 default int compareTo(@NotNull Tuple other) {
17 int size = getSize(); 19 int size = getSize();
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java
index a9aa9bf2..5f525798 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple0.java
@@ -29,6 +29,11 @@ public final class Tuple0 implements Tuple {
29 } 29 }
30 30
31 @Override 31 @Override
32 public Tuple set(int element, int value) {
33 throw new IndexOutOfBoundsException(element);
34 }
35
36 @Override
32 public String toString() { 37 public String toString() {
33 return TUPLE_BEGIN + TUPLE_END; 38 return TUPLE_BEGIN + TUPLE_END;
34 } 39 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java
index 388ee3a9..fb9497d2 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple1.java
@@ -38,6 +38,14 @@ public final class Tuple1 implements Tuple {
38 } 38 }
39 39
40 @Override 40 @Override
41 public Tuple set(int element, int value) {
42 if (element == 0) {
43 return Tuple.of(value);
44 }
45 throw new IndexOutOfBoundsException(element);
46 }
47
48 @Override
41 public String toString() { 49 public String toString() {
42 return TUPLE_BEGIN + value0 + TUPLE_END; 50 return TUPLE_BEGIN + value0 + TUPLE_END;
43 } 51 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java
index 6d886fd3..2213df97 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple2.java
@@ -25,6 +25,15 @@ public record Tuple2(int value0, int value1) implements Tuple {
25 } 25 }
26 26
27 @Override 27 @Override
28 public Tuple set(int element, int value) {
29 return switch (element) {
30 case 0 -> Tuple.of(value, value1);
31 case 1 -> Tuple.of(value0, value);
32 default -> throw new ArrayIndexOutOfBoundsException(element);
33 };
34 }
35
36 @Override
28 public String toString() { 37 public String toString() {
29 return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_END; 38 return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_END;
30 } 39 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java
index 734e45c2..417770e8 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple3.java
@@ -26,6 +26,16 @@ public record Tuple3(int value0, int value1, int value2) implements Tuple {
26 } 26 }
27 27
28 @Override 28 @Override
29 public Tuple set(int element, int value) {
30 return switch (element) {
31 case 0 -> Tuple.of(value, value1, value2);
32 case 1 -> Tuple.of(value0, value, value2);
33 case 2 -> Tuple.of(value0, value1, value);
34 default -> throw new ArrayIndexOutOfBoundsException(element);
35 };
36 }
37
38 @Override
29 public String toString() { 39 public String toString() {
30 return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_END; 40 return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_END;
31 } 41 }
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java
index e1b93e7b..c4915198 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/Tuple4.java
@@ -27,6 +27,17 @@ public record Tuple4(int value0, int value1, int value2, int value3) implements
27 } 27 }
28 28
29 @Override 29 @Override
30 public Tuple set(int element, int value) {
31 return switch (element) {
32 case 0 -> Tuple.of(value, value1, value2, value3);
33 case 1 -> Tuple.of(value0, value, value2, value3);
34 case 2 -> Tuple.of(value0, value1, value, value3);
35 case 3 -> Tuple.of(value0, value1, value2, value);
36 default -> throw new ArrayIndexOutOfBoundsException(element);
37 };
38 }
39
40 @Override
30 public String toString() { 41 public String toString() {
31 return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_SEPARATOR + value3 + 42 return TUPLE_BEGIN + value0 + TUPLE_SEPARATOR + value1 + TUPLE_SEPARATOR + value2 + TUPLE_SEPARATOR + value3 +
32 TUPLE_END; 43 TUPLE_END;
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java
index b66af491..b42b4b6a 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java
@@ -32,6 +32,16 @@ public final class TupleN implements Tuple {
32 } 32 }
33 33
34 @Override 34 @Override
35 public Tuple set(int element, int value) {
36 int size = getSize();
37 var newValues = new int[size];
38 for (int i = 0; i < size; i++) {
39 newValues[i] = element == i ? value : values[i];
40 }
41 return Tuple.of(newValues);
42 }
43
44 @Override
35 public String toString() { 45 public String toString() {
36 var valuesString = Arrays.stream(values) 46 var valuesString = Arrays.stream(values)
37 .mapToObj(Integer::toString) 47 .mapToObj(Integer::toString)
diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java
new file mode 100644
index 00000000..be294013
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/util/CancellationToken.java
@@ -0,0 +1,13 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.util;
7
8@FunctionalInterface
9public interface CancellationToken {
10 CancellationToken NONE = () -> {};
11
12 void checkCancelled();
13}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java b/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java
index 78ad2ad7..2e302663 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/util/CycleDetectingMapper.java
@@ -20,6 +20,10 @@ public class CycleDetectingMapper<T, R> {
20 20
21 private final Map<T, R> results = new HashMap<>(); 21 private final Map<T, R> results = new HashMap<>();
22 22
23 public CycleDetectingMapper(Function<T, R> doMap) {
24 this(Objects::toString, doMap);
25 }
26
23 public CycleDetectingMapper(Function<T, String> getName, Function<T, R> doMap) { 27 public CycleDetectingMapper(Function<T, String> getName, Function<T, R> doMap) {
24 this.getName = getName; 28 this.getName = getName;
25 this.doMap = doMap; 29 this.doMap = doMap;